汇编

汇编

困难

说明

在本练习中,你要为一门受 x86-64 汇编语言启发的小型语言定义语法和执行模型。

目标是形式化地定义代码在受限环境中如何书写、解析和执行。

执行模型

你的汇编代码要写在下面这个特殊结构里:

let program := assemble!(
    -- assembly code here
)

这个结构必须产出一个程序,它模拟一个函数:执行汇编代码并返回一个值。 然后用下面的语法调用这个程序,其中 a、b、c 等是实参:

let result := program(a, b, c, ...)

所有实参和返回值都是64 位有符号整数。 最多允许6 个实参。

寄存器

返回值和所有实参都通过寄存器传递。 你的语言必须支持以下寄存器:

寄存器 作用
rdi 第 1 个实参
rsi 第 2 个实参
rdx 第 3 个实参
rcx 第 4 个实参
r8 第 5 个实参
r9 第 6 个实参
rax 返回值

寄存器的初始值默认为 0,除非它们被用来向函数传递实参。

例如,在下面的程序中,所有寄存器的值都是 0,只有 rdi 和 rsi 例外,前者初始化为 10,后者初始化为 -20:

program(10, -20)

程序的返回值始终存放在 rax 中。

Note

寄存器名不区分大小写。 这意味着 rax、RAX 和 rAx 指的是同一个寄存器。

汇编代码

汇编代码中的每一行都可以是下面两种形式之一:

  • 标签用于标记程序中某些指令会用到的地方。
  • 指令代表程序执行的操作。

除非被某条指令改变,程序的执行流程都是线性推进的。 也就是说,上一行会在下一行之前完整执行。

标签

标签的语法如下:

<label>:

除了标记代码中的特定位置以供指令使用之外,标签不会改变任何寄存器的值,也不会对程序产生其他影响。

Note

标签区分大小写。 这意味着 Start、start 和 sTart 都是不同的标签。

双操作数指令

大多数指令的语法如下:

<opcode> <destination>, <source>

opcode 表示要执行的操作,它会用到 destination 和 source 两个操作数的值。 操作的结果存放在 destination 操作数中。

例如,add 指令把 destination 和 source 两个操作数中的值相加,并把结果存放在 destination 操作数中。

destination 操作数始终是寄存器,而 source 操作数可以是寄存器,也可以是字面量数字。

你的程序必须支持以下指令:

opcode operation performed
mov destination := source
add destination := destination + source
sub destination := destination - source
mul destination := destination * source
div destination := destination / source
xor destination := destination ^^^ source
and destination := destination &&& source
or destination := destination ||| source
shl destination := destination <<< source
shr destination := destination >>> source

除此之外,你的程序还必须支持双操作数的 cmp 指令。

这条指令不会修改任何寄存器,而是比较 destination 和 source 两个操作数,并把程序的某个内部状态设为以下三种之一:

  • 大于,如果 destination > source
  • 等于,如果 destination == source
  • 小于,如果 destination < source

内部如何记录这个状态由你决定。

单操作数指令

有些指令会改变程序的流程,把执行转移到代码的另一个位置。 这类指令叫做跳转指令。

它们都只接受一个操作数,即一个标签,用来指出跳转的目标,也就是执行将继续进行的那段代码位置:

<opcode> <label>

jmp 指令总是跳转到目标标签。

其他跳转指令只有在程序的内部状态被设为特定值时才跳转。 因此它们前面通常会有一条 cmp 指令。

你的程序必须支持以下跳转指令:

instruction operation performed
jmp unconditional jump. The jump is always performed
je jumps if the internal state is equal
jl jumps if the internal state is less than
jg jumps if the internal state is greater than
Note

指令的操作码,无论是双操作数还是单操作数,都不区分大小写。 这意味着 add、ADD 和 aDd 指的是同一条指令。

通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Lean Exercism

准备好开始 汇编 了吗?

注册 Exercism,借助 100 个练习 和真人导师指导,学习并掌握 Lean,全部免费。