I am reading and studying The Elements of Computing Systems but I am stuck at one point. Sample chapter skip the next 5 instruction s can be found here.
Anyway, I am trying to implement a Virtual Machine (or a byte code to assembly translator) but I am stuck at skip the next 5 instruction one point.
You can find the assembly notation here.
The goal is to implement a translator that will translate a specific byte code to this assembly code.
An example I have done successfully is for the byte code
push constant 5
which is translated to:
@5
D=A
@256
M=D
As I said, the assembly language for Hack is found in the link I provided but basically:
@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]
Well this was pretty straight forward. By definition memory location 256 is the top of the stack. So
push constant 5
push constant 98
will be translated to:
@5
D=A
@256
M=D
@98
D=A
@257
M=D
which is all fine..
I also want to give one more example:
push constant 5
push constant 98
add
is translated to:
@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
I think it is pretty clear.
However I have no idea how I can translate the byte code
eq
to Assembly. Definition for eq is as follows:
Three of the commands (eq, gt, lt) return Boolean values. The VM represents true and false as -1 (minus one, 0xFFFF) and 0 (zero, 0x0000), respectively.
So I need to pop two values to registers A and D respectively, which is quite easy. But how am I supposed to create an Assembly code that will check against the values and push 1 if the result is true or 0 if the result is false?
The assembly code supported for Hack Computer is as follows:
I can do something like:
push constant 5
push constant 6
sub
which will hold the value 0 if 2 values pushed to the stack are equal or !0 if not but how does that help? I tried using D&A or D&M but that did not help much either..
I can also introduce a conditional jump but how am I supposed to know what instruction to jump to? Hack Assembly code does not have something like "skip the next 5 instructions" or etc..
[edit by Spektre] target platform summary as I see it
SP(R0),LCL(R1),ARG(R2),This(R3),That(R4)
It appears there is another chapter which more definitively defines the Hack CPU. It says:
The Hack CPU consists of the ALU specified in chapter 2 and three registers called data register (D), address register (A), and program counter (PC). D and A are general-purpose 16-bit registers that can be manipulated by arithmetic and logical instructions like A=D-1 , D=D|A , and so on, following the Hack machine language specified in chapter 4. While the D-register is used solely to store data values, the contents of the A-register can be interpreted in three different ways, depending on the instruction’s context: as a data value, as a RAM address, or as a ROM address
So apparently "M" accesses are to RAM locations controlled by A. There's the indirect addressing I was missing. Now everything clicks.
With that confusion cleared up, now we can handle OP's question (a lot more easily).
Let's start with implementing subroutine calls with the stack.
; 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 ****
The "push constant" instruction can easily be translated to store into a dynamic location in the stack:
@<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
If we wanted to make a subroutine to push constants:
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
And to call the "push constant" routine:
@<constant>
D=A
@R2
M=D
@returnaddress ; sets the A register
D=A
@pushR2
0 ; jmp
returnaddress:
To push a variable value X:
@X
D=M
@R2
M=D
@returnaddress ; sets the A register
D=A
@pushR2
0 ; jmp
returnaddress:
A subroutine to pop a value from the stack into the D register:
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
Now, to do the "EQ" computation that was OP's original request:
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
Putting it all together:
@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:
At this point, OP can generate code to push D onto the stack:
@R2 ; push D onto stack
M=D
@returnaddress4
D=A
@pushR2
0 ; jmp
returnaddress4:
or he can generate code to branch on the value of D:
@jmptarget
EQ ; jmp
As I wrote in last comment there is a branch less way so you need to compute the return value from operands directly
Lets take the easy operation like eq
for now
eq a,d
is something like a=(a==d)
0xFFFF
and false is 0x0000
So this if a==d
then a-d==0
this can be used directly
a=a-d
compute OR
cascade of all bits of a
0-OR_Cascade(a)
the OR
cascade
a+a
instead of a<<1
So when I summarize this eq a,d
could look like this:
a=a-d;
a=(a|(a>>1)|(a>>2)|...|(a>>15))&1
a=0-a;
a=a-d;
a=(a|(a<<1)|(a<<2)|...|(a<<15))&0x8000
a=0-(a>>15);
the lower and greater comparison are much more complicated
a=a-d
will became:sub al,dl
sbc ah,dh
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With