如何为Hack Assembly语言编写'eq'解释器?

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编辑]我看到的目标平台摘要

  • 16位冯·诺依曼架构(地址为15位,16位字访问)
  • 数据存储器32KW(读/写)
  • 指令(程序)存储器32KW(只读)
  • 本机16位寄存器A,D
  • 通用16位寄存器R0-R15映射到0x0000 - 0x000F的数据存储器
  • 这些最有可能也用于: SP(R0),LCL(R1),ARG(R2),This(R3),That(R4)
  • 屏幕映射到数据存储器0x4000-0x5FFF(512x256 B/W像素8KW)
  • 键盘映射到0x6000的数据存储器(如果是最后一个键,则为ASCII码?)

在此输入图像描述

Ira*_*ter 9

看来还有另一章更明确地定义了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)