|
|
It's apparent to me that I have a problem with my translator somewhere. All of the chapter 8 tests pass completely, but when I run Square through the supplied jack compiler then through my translator, it does not work properly. I'm attaching a screenshot because trying to explain the issue would be pretty tough. It seems that rather than running as it's supposed to, it just continues incrementing SP for some reason until it runs out of memory space.
Not even sure where to look, and since the compiled asm is so large, I'm hoping to not have to go step by step through it.
Ant ideas?
Jason
|
Administrator
|
You may have a bug in your return code that is leaving the SP incremented every time you return from a function. Check that the SP value after a function returns is the same as it was before the function was called.
Another thing to do is put a write breakpoint on a RAM address 100 or so words into the stack, 350 might be good. See what you can tell by looking at the stack contents when it hits the breakpoint.
--Mark
|
|
But look at how cool that pattern is!
|
|
Cool pattern indeed. With the repetition of pattern, I wonder if I stumbled on some sort of fractal generation :-)
So, the original problem was that I had not included the OS code when I compiled. Whoops! So I added the code to the directory and re-ran the translation. I still end up with a non functioning program, but the strange pattern went away. I watched the program flow and realized something was causing it to loop back to ram[0] and restart. Further tracking leads me to believe my push and pop code needs work. It's fine for constants and most memory segments, but I think I need to get some clarification about pointer and temp. I've looked at all the diagrams and text regarding these segments, but am still a bit confused.
Here's my understanding so far:
Pointer[0] is ram[3] also called THIS
pointer[1] is ram[4] also called THAT
so
POP POINTER 0
and
POP THIS 0
are the same, but
POP POINTER 1
is not the same as
POP THIS 1
rather, it is the same as
POP THAT 0
So if ram[3].M=300 then POP POINTER 0 takes the top entry on the stack and places it in ram[300], correct?
And what about temp? I get the idea that TEMP 0 = R5, but in this case
POP TEMP 0
is not the same as
POP R5 0 (not even sure this is syntactically correct)
So POP TEMP 0 would take the top entry in the stack and store it in R5
Even as I'm typing this, I'm second guessing everything. Please, set me straight on these memory segments.
Jason
|
Administrator
|
Jason wrote
Here's my understanding so far:
Pointer[0] is ram[3] also called THIS
pointer[1] is ram[4] also called THAT
so
POP POINTER 0
and
POP THIS 0
are the same, but
POP POINTER 1
is not the same as
POP THIS 1
rather, it is the same as
POP THAT 0
So if ram[3].M=300 then POP POINTER 0 takes the top entry on the stack and places it in ram[300], correct?
And what about temp? I get the idea that TEMP 0 = R5, but in this case
POP TEMP 0
is not the same as
POP R5 0 (not even sure this is syntactically correct)
So POP TEMP 0 would take the top entry in the stack and store it in R5
"pop pointer 0" and "pop this 0" are not the same. "pop pointer 0" sets the this pointer; "pop this 0" uses the this pointer and accesses memory somewhere in the heap.
It's the difference between "this = something;" and "this.foo = bar;"
"pointer n" refers to RAM[3+ n]
"this n" refers to RAM[RAM[3]+ n]
"that n" refers to RAM[RAM[4]+ n]
"temp n" refers to RAM[5+ n]
--Mark
|
|
This post was updated on .
YES!!!! I just made that connection about memory segment "THIS" and java's this keyword as I was re-reading 7.2 about Array and Object handling and it was one of those head-smacker moments. I never made that connection before for some reason and it all make so much more sense now.
Am I correct though that to handle pointer and temp, we must translate @POINTER into @R3 and @TEMP into @R5 (as in, there are no mnemonics for those segments?) And also, when using THIS, does the mnemonic THIS refer to ram[3] or ram[R3]?
And how does one set LCL and ARG? Is it POP LCL (with no index?) Because, to me, if LCL contains, say 300, then POP LCL 0 will put the top stack value into ram[300]
I have a feeling I will be writing something up about memory segments in the near future to try to help anyone else struggling with the concept.
Jason
|
Administrator
|
Jason wrote
Am I correct though that to handle pointer and temp, we must translate @POINTER into @R3 and @TEMP into @R5 (as in, there are no mnemonics for those segments?) And also, when using THIS, does the mnemonic THIS refer to ram[3] or ram[R3]?
And how does one set LCL and ARG? Is it POP LCL (with no index?) Because, to me, if LCL contains, say 300, then POP LCL 0 will put the top stack value into ram[300]
For "pointer 0" I use @3, for "pointer 1" I use @4, for "temp 0", @5 for "temp 1". @6 etc.
You are correct about how "pop local 0" works.
SP, LCL, ARG, THIS and THAT are assembly language symbols that have numeric values 0, 1, 2, 3 and 4, respectively. Writing @THIS has exactly the same effect as writing @3.
--Mark
|
|
So then is there any way in vm language to put a value in LCL? or is that function reserved for our assembly code?
It's too bad there aren't more test programs that fall between the chapter 8 files and something like square for complexity
Jason
|
Administrator
|
Jason wrote
So then is there any way in vm language to put a value in LCL? or is that function reserved for our assembly code?
The thing to realize is that the VM language manipulates the Virtual Machine as described in 7.2.
The VM language has nothing to do with the target assembly language. The VM Translator could be writing Motorola 68K assembly language, in which there are 16 A and D registers and many addressing modes available. The generated code for "pop local 0" might be something like "POP D0; MOV D0, 4[A14]".
There's no portable way for the VM language deal with the underlying hardware. It's possible to cheat using OS functions, but then your VM code won't run on some other target processor. Given the OS specification, you could use Memory.poke() to directly access RAM[LCL]:
push 1
push 1234
call Memory.poke 2
pop temp 0
--Mark
|
|
This all makes much more sense now. I have re-written mu push and pop so that they work correctly when faced with pointer and temp.
I'm still getting weird patterns, but they are different patterns, so that's progress.
I checked call and return, push and pop, but there's still a problem somewhere that's not showing up until I get to the complex program. This debugging of multi-thousand line assembly code really kinda sucks.
Jason
|
|
Jason wrote
I'm still getting weird patterns, but they are different patterns, so that's progress.
Well, not necessarily! Don't give up. If you email me a summary of the output of each command, I can take a look.
|
Administrator
|
Another thing that can cause crashes that loop is if you have a jump to a label that does not exist. The assembler will assign a RAM address to that symbol and it will be a small number since RAM variable allocation begins at 16.
This is what was probably happening when you didn't have the OS .vm files. Your bootstrap code wrote a call to Sys.init() which didn't exist. Since this was the first undefined symbol it was assigned address 16. The call code pushed the return IP and state, and then jumped to ROM address 16 which was probably in the early part of the code for the call to Sys.init(). This makes an infinite loop with unbounded stack growth.
My assembler, chasm1.zip, creates a listing file that includes a symbol map. You can spot suspicious data symbols in the map. If you are running Windows, run this from the command prompt.
chasm1 -l Square.asm (that's a lower-case L)
and look at Square.lst. If you see anything that looks like a code label in the "Data Symbols" section, that's an undefined code label.
Data Symbols
24576 D KBD
21 D Keyboard.keyPressed
1790 2087
17 D Memory.alloc
180 1614
18 D Memory.deAlloc
256 1705
16384 D SCREEN
20 D Screen.drawRectangle
345 436 821 914 1032 1141 1265 1358 1476 1585
19 D Screen.setColor
286 377 751 860 978 1071 1195 1304 1422 1515
16 D Sys.init
12
22 D Sys.wait
2327
Code Symbols
61 C _1
18
266 C _10
262
...
This example is from building Square without the OS .vm files to you see all the OS calls that Square makes got RAM addresses.
If you are not running Windows, email me your generated Square.asm and I can let you know if I see any undefined code labels.
--Mark
|
|
So I ran this and examined the list and at first I wasn't sure what I was looking at, then it dawned on me, I'm looking at just about every label in the code!!!!! It seems that my label index code is not adequate. I was just incrementing index with each call, well, that won't work, so I'll rewrite that part tomorrow. Not sure how I will keep track of what goes where, but I thought it seemed to easy when I wrote it.
Thank you Mark, that assembler is pretty nifty. What are the numbers on the second line of each entry?
Jason
|
Administrator
|
Jason wrote
So I ran this and examined the list and at first I wasn't sure what I was looking at, then it dawned on me, I'm looking at just about every label in the code!!!!! It seems that my label index code is not adequate. I was just incrementing index with each call, well, that won't work, so I'll rewrite that part tomorrow. Not sure how I will keep track of what goes where, but I thought it seemed to easy when I wrote it.
If roughly half your unique labels are showing up in the "Data Symbols" section, and half in the "Code Symbols" section, I'd guess that there is a problem with the code that writes the assembly language labels -- the (label) commands.
Thank you Mark, that assembler is pretty nifty. What are the numbers on the second line of each entry?
That's a reference list showing the ROM addresses where the symbol is used.
--Mark
|
|