15.4 翻译 Pcode 中的算术命令、 push/pop 命令以及 jmp/jz 命令
算术命令(add / sub / mul / div / mod / cmpeq / cmpne / cmpgt / cmplt / cmpge / cmple / and / or / not / neg 命令)、 push/pop 命令以及 jmp/jz 命令的操作很简单,因此将其翻译成 x86 指令也很简单,结合第 3 、 4 章中介绍的这些命令对栈的操作步骤,典型的宏定义如下:
- %MACRO add 0
- POP EAX
- ADD DWORD [ESP], EAX
- %ENDMACRO
- %MACRO sub 0
- POP EAX
- SUB DWORD [ESP], EAX
- %ENDMACRO
- %MACRO cmpeq 0
- MOV EAX, [ESP+4]
- CMP EAX, [ESP]
- PUSHF
- POP EAX
- SHR EAX, 6
- AND EAX, 0X1
- ADD ESP, 4
- MOV [ESP], EAX
- %ENDMACRO
- %MACRO not 0
- MOV EAX, [ESP]
- OR EAX, EAX
- PUSHF
- POP EAX
- SHR EAX, 6
- AND EAX, 0X1
- MOV [ESP], EAX
- %ENDMACRO
- %MACRO jz 1
- POP EAX
- OR EAX, EAX
- JZ %1
- %ENDMACRO
- %MACRO jmp 1
- JMP %1
- %ENDMACRO
- %MACRO push 1
- PUSH DWORD %1
- %ENDMACRO
- %MACRO pop 0-1
- %IFIDN %0, 0
- ADD ESP, 4
- %ELSE
- POP DWORD %1
- %ENDIF
- %ENDMACRO
所有宏定义见 macro.inc 。
测试文件 test.nasm :
- MOV EBP, ESP
- SUB ESP, 8
- %define a [EBP-4]
- %define b [EBP-8]
- ; a = readint("Please input an number `a`: ")
- readint "Please input an number `a`: "
- pop a ; ==> POP DWORD [EBP-4]
- ; b = readint("Please input another number `b`: ")
- readint "Please input another number `b`: "
- pop b ; ==> POP DWORD [EBP-8]
- ; print("a = %d", a)
- push a ; ==> PUSH DWORD [EBP-4]
- print "a = %d"
- ; print("b = %d", b)
- push b ; ==> PUSH DWORD [EBP-8]
- print "b = %d"
- ; print("a - b = %d", a - b)
- push a
- push b
- sub
- print "a - b = %d"
- ; if (a > b) { print("a > b"); } else { print("a <= b") }
- push a
- push b
- cmpgt
- jz _LESSEQUAL
- print "a > b"
- jmp _EXIT
- _LESSEQUAL:
- print "a <= b"
- _EXIT:
- exit 0
库函数文件( tio.c )和 makefile 文件,和上一节是一样的。
将以上四个文件下载下来放到用一个目录,输入 make run 即可编译并运行测试代码。运行过程如下:
- $ make run
- ...
- ./test
- Please input an number `a`: 46
- Please input another number `b`: 48
- a = 46
- b = 48
- a - b = -2
- a <= b
test.nasm 的开头在栈上分配了 8 个字节的空间(也就是 2 个 int ),并定义了 a 和 b 代表这储存在这两个 int 型空间中的数值:
- MOV EBP, ESP
- SUB ESP, 8
- %define a [EBP-4]
- %define b [EBP-8]
在 macro.inc 中定义了 pop 和 push 宏, test.nasm 中的 “pop a” 和 “push a” 将会被展开为: “POP DWORD [EBP-4]” 和 “PUSH DWORD [EBP-8]”。