|
|
SO I'm about to start on my vm translator for chapter 7. It seems to me that much of the proposed program is a repeat of the exercise from chapter 6. Would it therefore be a good idea to start with the previous code and adapt it or am I missing something and it would be better to write it from the ground up?
Am I wrong in my understanding that we are just making another assembler? Our output will be machine language, just like chapter 6, right? The difference being that instead of assembling .hack files, we will assemble .vm files.
Jason
|
Administrator
|
Jason wrote
SO I'm about to start on my vm translator for chapter 7. It seems to me that much of the proposed program is a repeat of the exercise from chapter 6. Would it therefore be a good idea to start with the previous code and adapt it or am I missing something and it would be better to write it from the ground up?
Am I wrong in my understanding that we are just making another assembler? Our output will be machine language, just like chapter 6, right? The difference being that instead of assembling .hack files, we will assemble .vm files.
Jason
I think that you should use the techniques but not the actual code from chapter 6.
Instead of just one input file, the VM translator needs to be able to read an entire directory full of .vm files and process them into a single output file. The output file is a .asm source file that your assembler will translate into a .hack file.
The parser is simpler; it only needs to parse commands consisting of 1, 2 or 3 fields separated by spaces. There is no need for a symbol table. The tricky bit is the CodeWriter module, which is entirely new code.
Cut and paste useful chunks of code, of course. In particular, the higher levels of your assembler's parser may be easily adaptable.
Debugging hint: it can be quite useful to write the VM source statements in the output .asm file as comments. I made this optional in my VM translator, controlled by a -d command line option.
--Mark
|
|
cadet1620 wrote
I think that you should use the techniques but not the actual code from chapter 6.
Instead of just one input file, the VM translator needs to be able to read an entire directory full of .vm files and process them into a single output file. The output file is a .asm source file that your assembler will translate into a .hack file.
The parser is simpler; it only needs to parse commands consisting of 1, 2 or 3 fields separated by spaces. There is no need for a symbol table. The tricky bit is the CodeWriter module, which is entirely new code.
Cut and paste useful chunks of code, of course. In particular, the higher levels of your assembler's parser may be easily adaptable.
Debugging hint: it can be quite useful to write the VM source statements in the output .asm file as comments. I made this optional in my VM translator, controlled by a -d command line option.
--Mark
Ah, ok, you have cleared some things up for me. I was under the impression that the VM translator should output binary .hack files, but it should output .asm files. Probably would have caught that as I started working and re-read the chapter.
I'm rather confused, why is the vm written in vm code, then translated to asm, then translated to hack? why not write it in asm? why have asm at all? why not go from vm to hack? Is this a practical implementation or done this way for educational purposes?
Jason
|
Administrator
|
Jason wrote
I'm rather confused, why is the vm written in vm code, then translated to asm, then translated to hack? why not write it in asm? why have asm at all? why not go from vm to hack? Is this a practical implementation or done this way for educational purposes?
I'd say it's a bit of both, leaning towards educational purposes.
A modern compiler still has to do all these steps even if it is a single program. It would not, however, use a human friendly format for the data passed between the internal phases. There was one set of compilers I was using that had separate executables that were automatically run in sequence by a wrapper program: running "cc myprog.c" would generate a command line like "xyzcc myprog.c | xyzc2 | xyzi86 myprog.obj" and run it in a sub-shell. The FORTRAN compiler did the same thing but used xyzftn instead of xyzcc. Different target processors would use a different code generator instead of xyzi86.
Breaking the compiler up into three separate tools as the book has done makes it much easier to write each piece and gives the student smaller, achievable, goals.
I think that if I were writing the book (20/20 hindsight is great 8-) I would put Chapter 9 between Chapters 6 and 7 so that the student would understand the structure and use of the entire tool chain when they started on the VM translator.
--Mark
|
|
Running into a new problem now. What it boils down to is that I really don't understand the Hack assembly language. I thought I'd get a start on the code portion of the translator by looking at the .vm file and figuring out what the asm file should be and building from there. I started with SimpleAdd.vm I used previous examples of addition and I can get my asm to add 7 + 8, however, I have no idea how to insert the answer into the memory spot indicated in R0 (ram[256]). I have no idea how to get the M value from R0 and use that as my A value. If I do:
@0
A=M
@A
M=D
I end up with my answer in ram[16]
How does one assign a specific ram spot to a variable? As far as I know, if I used something like @sum, it will be randomly assigned a ram spot. Right?
Jason
|
Administrator
|
Jason wrote
...
however, I have no idea how to insert the answer into the memory spot indicated in R0 (ram[256]). I have no idea how to get the M value from R0 and use that as my A value. If I do:
@0
A=M
@A
M=D
I end up with my answer in ram[16]
How does one assign a specific ram spot to a variable? As far as I know, if I used something like @sum, it will be randomly assigned a ram spot. Right?
Yes, 'sum' will be assigned to the next available RAM location.
You want to use R0 as a pointer to store the current value of the D register.
Assuming RAM[0]=256:
@0 // A = 0
A=M // A = RAM[A] i.e.: A = 256)
M=D // RAM[A] = D i.e: RAM[256] = D
(It would be better to use @SP rather than @0 for assembled VM code since it indicates that this is a stack access.)
--Mark
|
|
Ok, that makes sense now. Here's my asm for SimpleAdd
@7
D=A
@8
D=D+A
@SP
A=M
M=D
@SP
M=M+1
Now, I think my last question on this. At the end, I increment SP. I did that to match the expected output, but I'm assuming that this is done so that SP points to the next available ram spot. So, the question is, when should this be done? Every time something is written to the memory location indicated by SP?
Oh wait, I think I just made a realization. SP is "Stack Pointer" so a push will increment and a pop will decrement, right?
Ok, so I'm revising the asm to:
@SP // A = 0
A=M // A = RAM[256] i.e.: A = 256)
M=7 // RAM[256] = 7
@SP // A = 0
M=M+1 // 257 = 256 +1
A=M // A = RAM[257]
M=8 // RAM[256] = 8
I'm not editing this in case someone else ends up in the same boat I was in and needs the nudge. So, am I now on the right track?
Jason
need to finish by adding the arithmetic, but I'll need to get some sleep before I work that out.
|
Administrator
|
Yes, you are on the right track. You need to update the SP when doing stack operations push/pop/add/etc.
Keep in mind that you are writing a program that parses VM code and writes the corresponding assembly code. For SimpleAdd, your program will end up calling
CodeWriter.WritePushPop (C_PUSH, "constant", 7);
CodeWriter.WritePushPop (C_PUSH, "constant", 8);
CodeWriter.Arithmetic("add");
so the code for the two pushes will be identical, and the SP will be 258 after the second push.
The add needs to behave as if it popped the arguments and pushed the sum, so after the add, the SP will be 257.
--Mark
|
|
Sleep be damned, I think I got it:
//Push constant 7 into ram[256]
@7
D=A
@SP
A=M
M=D
@SP
M=M+1 // increment sp to 257
//push constant 8 into ram[257]
@8
D=A
@SP
A=M
M=D
@SP
M=M+1 // increment sp to 258
//add
//start with pop current SP -1
@SP
M=M-1 // sp is now 257
A=M
D=M // D now = 8
//pop again
@SP
M=M-1 // sp is now 256
A=M
D=D+M //do addition
//push result to SP ram[256]
@SP
A=M
M=D
@SP
M=M+1 //increment after push Sp now = 257
Now I need to code up my translator and see how it does on other functions.
Thank you!!!
Jason
|
|
Hi,
Is it necessary to use 2 Push operations to store the two operands on stack? Or we could just push one operand on stack, store the second one in D register and use this value in D register to do addition with top value on stack and push the result back on stack.
Also is it necessary to increment the SP after a Pop operation? Is it not so that the address of popped value is where the next result will be stored?
Can it be like
1-go one address below the current address
2-Pop the value [one or two values]
3-perform the required operation on popped value(s)
4- Push the result back on stack at address from where the last popped value was.
Thanks.
|
Administrator
|
kraftwerk1611 wrote
Is it necessary to use 2 Push operations to store the two operands on stack? Or we could just push one operand on stack, store the second one in D register and use this value in D register to do addition with top value on stack and push the result back on stack.
The two Push operations are not part of the add VM command. They come from the translation of the two push VM commands that came before the add. The thing to realize is that the two top values on the stack may have come from anywhere, not necessarily two push commands. For example
// f(x) + g(y)
push local 1
call f 1
push local 2
call g
add
Also is it necessary to increment the SP after a Pop operation? Is it not so that the address of popped value is where the next result will be stored?
Can it be like
1-go one address below the current address
2-Pop the value [one or two values]
3-perform the required operation on popped value(s)
4- Push the result back on stack at address from where the last popped value was.
Yes, you can do it this way to avoid extra SP increments and decrements.
It is clearer if you only use "push" and "pop" to refer to those operations that do change the SP. I would write this as:
Pop y value
Read x from [SP-1]
Write x op y to [SP-1]
--Mark
|
|
Thanks for the reply.
Can you please elaborate on this statement
"The thing to realize is that the two top values on the stack may have come from anywhere, not necessarily two push commands."
What I understand so far is that there are only two possible operations on stack segment; push and pop.
Sorry, if the answer is obvious but I am not sure what you meant.
|
Administrator
|
I think this is only a language issue. By my definition, most of the VM commands are operations on the stack. The add operator we have been discussing changes the SP and a value on the stack so it qualifies as a stack operation.
The add stack operation is sometimes described as
pop y
pop x
push x+y
and a VM translator could write ASM that does exactly that using two of R13-R15 for x and y. (R13-R15 are reserved for the generated ASM code to use for any purpose.) The generated code will be rather big, though, 15-20 commands.
It is much better for the translator to write ASM code that just does the required work. This is what you have described -- only decrementing SP once, then directly manipulating the values at [SP] and [SP-1].
--Mark
|
|