我正在尝试为 Intel Atom 系统编译基于 6502 的模拟器,但是我收到了此文件的以下类型的错误:https : //github.com/littlefluffytoys/Beebdroid/blob/master/app/src/main/ jni/6502asm_x86.S
jni/6502asm_x86.S:163:5: error: invalid instruction mnemonic 'movb'
movb ch, [ ebp+9] # ch = r10 = S
^~~~
jni/6502asm_x86.S:181:2: error: invalid instruction mnemonic 'pushw'
pushw 0xfffa
^~~~~
Run Code Online (Sandbox Code Playgroud)
这是 32/64 位的问题吗?我熟悉汇编,但不熟悉 x86 或 x86_64,而且我发现很难追踪正在发生的事情。我知道 movq 在 32 位上不可用,但我想不出为什么 byte 根本不可用。
我不得不从文件中删除所有 % 符号 - 似乎我的 cc (4.8.4) 版本不喜欢它们 - 但后来遇到了这个 mov 问题。
特别令人困惑的是 movw 和 movb 的早期实例不会产生错误,例如
movw di, [ ebp+4] # di = r6 = PC
movb cl, [ ebp+6] # cl = r7 = A
Run Code Online (Sandbox Code Playgroud)
(虽然我注意到这些在宏定义中,所以也许它们还没有被解析)
我在一些英特尔文档中读到 mov 有时看起来如下,但我对这种格式的了解不够,无法尝试重写数十个错误:
MOV ECX, dword ptr table[RBX][RDI]
Run Code Online (Sandbox Code Playgroud)
任何帮助,将不胜感激!
汇编程序宏是纯文本替换。如果您不使用宏,则其内容不必有效。而且如果用了,只在用的地方组装。(它不像一个内联函数,它像一个 C 预处理器宏)。
原始文件的用途.intel_syntax noprefix上方,但随后充满了像疯狂的代码
mov %ebx, [%ebx + %eax*4],并movb %al,[%esi+%edi]仍与装饰的名字注册%,尽管noprefix,更重要的是仍然使用AT&T风格的操作数大小后缀。
它是 Intel 和 AT&T 语法的突变混合体,难怪一些汇编程序拒绝它。
参见https://stackoverflow.com/tags/intel-syntax/info与https://stackoverflow.com/tags/att/info
在我的 Linux 桌面上,原始文件与as我调用的GNU Binutils 组合得很好gcc -m32 -c 6502asm_x86.S。(我在 Linux 上,所以这是真正的 GCC,具体gcc --version说gcc (GCC) 9.1.0 Copyright (C) 2019 Free Software Foundation, Inc.等。它使用as. as --version说“GNU 汇编程序(GNU Binutils)2.32”)
我怀疑您使用的是带有 Apple Clang 的 Mac。您的“cc (4.8.4)”看起来更像是 gcc 版本号,但 GCC 不包含汇编程序。它总是使用外部的。在 Mac 上,它可能仍然是 Clang/LLVM,而不是 GNU Binutils。
在我的 Linux 桌面上,clang 8.0.1 拒绝了这个文件。在英特尔模式下不接受 AT&T-isms 更加严格,并且根本不支持.intel_syntax prefix,仅支持intel noprefix或att prefix. 删除%文件中的所有字符后,clang -m32 -c 6502asm_x86.S给出与您显示的相同的错误消息:
6502asm_x86.S:121:5: error: invalid instruction mnemonic 'movw'
movw di, [ebp+4] # di = r6 = PC
^~~~
Run Code Online (Sandbox Code Playgroud)
如果可能,请使用GNU binutils 中的asaka gas。但是 IDK 如果它支持 MachO 对象文件,那么它可能不适合您。(更新:显然您在 Linux 上尝试使用 Android 工具链。这也是叮当声,但可能正在创建 ELF 对象。所以您可能只需要as手动使用。)
要实际修复源代码,请删除所有操作数大小的后缀,并让寄存器操作数暗示大小。
在两个操作数都不是寄存器的.intel_syntax情况下,该文件确实正确使用了 GAS操作数大小覆盖,mov dword ptr [ebp+20], 0因此它需要dword ptr.
但是你不能只删除每个助记符的最后一个字符:一些指令已经省略了它。(看起来该文件对 dword 操作数大小是这样做的,但为使用字节或字操作数大小的每条指令冗余地指定它。)
有一些指令仍然可以使用(有时需要)英特尔语法中的大小后缀,例如pushw immediate. 一些像 NASM 这样的汇编器使用push word 123,但 GAS.intel_syntax noprefix使用pushw 123. 但是,如果有寄存器或内存操作数,则可能暗示大小。egpush di是一个词push,pop word ptr [ecx]是一个词pop。您还有“字符串”指令的后缀,例如movsb/w/d/lodsb/w/d等。
例如
do_interrupt:
PUSHWORD di # push(cpu->pc)
movzx eax, byte ptr [ebp+10]
or eax, 0x20 # uint8_t temp = cpu->p | 0x20;
PUSH_BYTE al # push(temp);
popw ax
movw di, [esi+eax] # cpu->pc=*(uint16_t*)&(cpu->mem[0xfffe]);
or byte ptr [ebp+10], 4 # cpu->p |= FLAG_I;
movw [ebp+4],di # Remove when C-only
movb [ebp+9],ch # Remove when C-only
pop eax
add eax,7 # c += 7;
push eax
Run Code Online (Sandbox Code Playgroud)
变成
do_interrupt:
PUSHWORD di # push(cpu->pc)
movzx eax, byte ptr [ebp+10]
or eax, 0x20 # uint8_t temp = cpu->p | 0x20;
PUSH_BYTE al # push(temp);
pop ax
mov di, [esi+eax] # cpu->pc=*(uint16_t*)&(cpu->mem[0xfffe]);
or byte ptr [ebp+10], 4 # cpu->p |= FLAG_I;
mov [ebp+4],di # Remove when C-only
mov [ebp+9],ch # Remove when C-only
# pop eax; add eax,7 ; push eax # optimize into one instruction:
add dword ptr [esp], 7 # c += 7;
# or address it relative to EBP if we know where ESP is relative to EBP
Run Code Online (Sandbox Code Playgroud)
显然,您还需要查看宏定义。
这看起来不像是有史以来最高效的代码;可以在寄存器中做更多事情。但这不是重点。我只看到了一个小的窥视孔优化 pop/add/push into a memory-destination add,没有尝试优化其余部分。
还有其他明显的东西,比如
movb %dl, [%ebp+7] # dl = r8 = X
movb %dh, [%ebp+8] # dh = r9 = Y
Run Code Online (Sandbox Code Playgroud)
这可能是单个字加载到 DX = DH:DL(x86 是小端字节序并且具有非常有效的未对齐加载,如果这恰好是未对齐的)。
所以我不建议使用此代码作为学习 x86 的示例!
| 归档时间: |
|
| 查看次数: |
159 次 |
| 最近记录: |