Kor*_*gay 15 algorithm assembly interpreter bytecode nand2tetris
我正在阅读和研究计算系统的元素,但我一度陷入困境.示例章节跳过下面的5条指令可以在这里找到.
无论如何,我正在尝试实现一个虚拟机(或一个字节代码到汇编转换器),但我坚持跳过下一个5指令一点.
您可以在此处找到装配符号.
目标是实现将特定字节代码转换为此汇编代码的转换器.
我成功完成的一个例子是字节码
push constant 5
Run Code Online (Sandbox Code Playgroud)
这被翻译成:
@5
D=A
@256
M=D
Run Code Online (Sandbox Code Playgroud)
正如我所说,Hack的汇编语言可以在我提供的链接中找到,但基本上是:
@5 // Load constant 5 to Register A
D=A // Assign the value in Reg A to Reg D
@256// Load constant 256 to Register A
M=D // Store the value found in Register D to Memory Location[A]
Run Code Online (Sandbox Code Playgroud)
嗯这很直接.根据定义,存储器位置256是堆栈的顶部.所以
push constant 5
push constant 98
Run Code Online (Sandbox Code Playgroud)
将被翻译为:
@5
D=A
@256
M=D
@98
D=A
@257
M=D
Run Code Online (Sandbox Code Playgroud)
这一切都很好..
我还想再举一个例子:
push constant 5
push constant 98
add
Run Code Online (Sandbox Code Playgroud)
被翻译成:
@5
D=A
@256
M=D
@98
D=A
@257
M=D
@257 // Here starts the translation for 'add' // Load top of stack to A
D=M // D = M[A]
@256 // Load top of stack to A
A=M // A = M[A]
D=D+A
@256
M=D
Run Code Online (Sandbox Code Playgroud)
我认为很清楚.
但是我不知道如何翻译字节码
eq
Run Code Online (Sandbox Code Playgroud)
到大会.eq的定义如下:
其中三个命令(eq,gt,lt)返回布尔值.VM表示真和假,分别为-1(减1,0xFFFF)和0(0,0x0000).
所以我需要分别向寄存器A和D弹出两个值,这很容易.但是我应该如何创建一个将检查值的汇编代码,如果结果为true则按1,如果结果为false则按0?
Hack Computer支持的汇编代码如下:

我可以这样做:
push constant 5
push constant 6
sub
Run Code Online (Sandbox Code Playgroud)
如果推送到堆栈的2个值相等,它将保持值0;如果不是,则保持为0,但这有何帮助?我尝试使用D&A或D&M,但这也没有多大帮助..
我也可以引入一个条件跳转,但我怎么知道要跳转到什么指令?Hack汇编代码没有"跳过下5条指令"等等.
[Spektre编辑]我看到的目标平台摘要
SP(R0),LCL(R1),ARG(R2),This(R3),That(R4)
看来还有另一章更明确地定义了Hack CPU.它说:
Hack CPU由第2章中指定的ALU和称为数据寄存器(D),地址寄存器(A)和程序计数器(PC)的三个寄存器组成.D和A是通用的16位寄存器,可以通过算法和逻辑指令操作,如A = D-1,D = D | A等,遵循第4章中规定的Hack机器语言. -register仅用于存储数据值,A寄存器的内容可以用三种不同的方式解释,具体取决于指令的上下文:作为数据值,作为RAM地址或作为ROM地址
显然"M"访问是由A控制的RAM位置. 我缺少间接寻址. 现在一切都点击了
随着这种混乱被清除,现在我们可以处理OP的问题(更容易).
让我们从使用堆栈实现子程序调用开始.
; subroutine calling sequence
@returnaddress ; sets the A register
D=A
@subroutine
0 ; jmp
returnaddress:
...
subroutine: ; D contains return address
; all parameters must be passed in memory locations, e.g, R1-R15
; ***** subroutine entry code *****
@STK
AM=M+1 ; bump stack pointer; also set A to new SP value
M=D ; write the return address into the stack
; **** subroutine entry code end ***
<do subroutine work using any or all registers>
; **** subroutine exit code ****
@STK
AM=M-1 ; move stack pointer back
A=M ; fetch entry from stack
0; jmp ; jmp to return address
; **** subroutine exit code end ****
Run Code Online (Sandbox Code Playgroud)
"推送常量"指令可以轻松转换为存储到堆栈中的动态位置:
@<constant> ; sets A register
D=A ; save the constant someplace safe
@STK
AM=M+1 ; bump stack pointer; also set A to new SP value
M=D ; write the constant into the stack
Run Code Online (Sandbox Code Playgroud)
如果我们想要一个子程序来推动常量:
pushR2: ; value to push in R2
@R15 ; save return address in R15
M=D ; we can't really use the stack,...
@R2 ; because we are pushing on it
D=M
@STK
AM=M+1 ; bump stack pointer; also set A to new SP value
M=D ; write the return address into the stack
@R15
A=M
0 ; jmp
Run Code Online (Sandbox Code Playgroud)
并称为"推动常量"例程:
@<constant>
D=A
@R2
M=D
@returnaddress ; sets the A register
D=A
@pushR2
0 ; jmp
returnaddress:
Run Code Online (Sandbox Code Playgroud)
要推送变量值X:
@X
D=M
@R2
M=D
@returnaddress ; sets the A register
D=A
@pushR2
0 ; jmp
returnaddress:
Run Code Online (Sandbox Code Playgroud)
一个子程序,用于将值从堆栈弹出到D寄存器:
popD:
@R15 ; save return address in R15
M=D ; we can't really use the stack,...
@STK
AM=M-1 ; decrement stack pointer; also set A to new SP value
D=M ; fetch the popped value
@R15
A=M
0 ; jmp
Run Code Online (Sandbox Code Playgroud)
现在,要执行OP的原始请求的"EQ"计算:
EQ: ; compare values on top of stack, return boolean in D
@R15 ; save return address
M=D
@EQReturn1
D=A
@PopD
0; jmp
@EQReturn1:
@R2
M=D ; save first popped value
@EQReturn2
D=A
@PopD
0; jmp
@EQReturn2:
; here D has 2nd popped value, R2 has first
@R2
D=D-M
@EQDone
equal; jmp
@AddressOfXFFFF
D=M
EQDone: ; D contains 0 or FFFF here
@R15
A=M ; fetch return address
0; jmp
Run Code Online (Sandbox Code Playgroud)
把它们放在一起:
@5 ; push constant 5
D=A
@R2
M=D
@returnaddress1
D=A
@pushR2
0 ; jmp
returnaddress1:
@X ; now push X
D=M
@R2
M=D
@returnaddress2
D=A
@pushR2
0 ; jmp
returnaddress2:
@returnaddress3 ; pop and compare the values
D=A
@EQ
0 ; jmp
returnaddress3:
Run Code Online (Sandbox Code Playgroud)
此时,OP可以生成将D推入堆栈的代码:
@R2 ; push D onto stack
M=D
@returnaddress4
D=A
@pushR2
0 ; jmp
returnaddress4:
Run Code Online (Sandbox Code Playgroud)
或者他可以生成代码来分支D的值:
@jmptarget
EQ ; jmp
Run Code Online (Sandbox Code Playgroud)