Is "pop static 19" inefficient by nature?

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Is "pop static 19" inefficient by nature?

burge91
Can't figure out how to do this without doing A=A+1 19 times. Need D for the location addition (static location + 19) but also to acquire the value from the stack. Or, very possibly, I'm being very dumb? Am I?
Reply | Threaded
Open this post in threaded view
|

Re: Is "pop static 19" inefficient by nature?

dolomiti7
Unlike locals, arguments and fields, static variables are accessed with absolute addressing. It refers to a reserved area in the memory between offset 16 and 255, allowing 240 static variables in total. Which one of the 240 available addresses is allocated to a specific variable will be decided by the assembler and not the VM translator. The only thing you have to do is to converting the number of the static variable which is unique inside the class/VM file into a symbol that is unique inside the program. According to the convention this is done by appending the number to the class name separated by a dot (I.e. Output.19 if you are referencing static 19 in class Output). There is no need to calculate the actual memory address at this point, you can just load the address directly into A with @Output.19 in this example.

Though it would be possible to calculate and allocate all the addresses of static variables inside the VM translator instead of the Assembler, this is not the convention of nand2tetris. One reason to do the mapping/allocation to fixed memory addresses at a later stage is that in more complex toolchains you may have several libraries being translated separately and linked together at a later point of time. In such scenarios it is impossible to know the absolute address at translation time.

Regarding your general concern about inefficiency, this can become an issue for the variables with indirect addressing (local, argument, this). For these variables it is possible to follow your approach and concatenate multiple A=A+1 to maintain the contents of D. In fact, for small indexes this is the most efficient approach. However, the generic case requires to use one of the internal registers R13-R15 which can be used by the VM translator to temporarily store the address. This is shorter and faster for high indexes and works in general for all indexes. A pop local 19 would for sure be better using one of the R13-R15 registers.
Reply | Threaded
Open this post in threaded view
|

Re: Is "pop static 19" inefficient by nature?

burge91
Thanks. I'm not sure what you mean by the .19 part. I solved the static problem almost identically to all the other labels. Just by going to its offset. Thanks for your reply. I knew the solution entailed saving it to RAM but I wasn't 100% sure so didn't approach it with confidence. But you saying that means I could. I now have a more optimised VM translator, since before for a command like pop argument 100, there'd be around 100 lines of ASM code due to the A=A+1 method, which is ridiculous.
Reply | Threaded
Open this post in threaded view
|

Re: Is "pop static 19" inefficient by nature?

dolomiti7
Assume you have 2 classes which both have 1 static variable. Let's say class Output and class Math. In both classes the VM files will contain commands like push/pop static 0. How do you distinguish between the two variables in your generated ASM code? The VM translator should not care (or know) about the offsets of the two variables. Instead it should generate unique symbol for both. This can follow any logic that results in unique names that can never overlap, therefore embedding the class name in the symbol is the most straightforward way. The VM translator could name the variables for example Output0 and Math0 or Output$0 and Math$0, however the proposed convention would be Output.0 and Math.0.

Assuming further that these are the only 2 static variables in the whole program they would be allocated to the offsets 16 and 17. But this allocation should be done by the Assembler, not the VM translator. The generated ASM code should work independently from the fact which of the two variables will be at offset 16 and which one at offset 17.

As mentioned in my previous post, the VM translator could in theory keep track of all static variables across all translated classes and do the mapping directly and thus generate something like @16 instead of @Output.0, but this is not good practice. Use symbolic names for the static variables as described above.

To make this transparent, it may be helpful to add an option to your Assembler so that it will print out the symbol table. When you translate the Pong.asm file from project 06 with your Assembler you should see all kinds of static variables being allocated to addresses from 16 upwards, similar to this:
   16   ponggame.0
   17   math.1
   18   math.0
   19   memory.0
   20   output.4
   21   output.2
   22   output.1
   23   output.0
   24   output.3
   25   output.5
   26   output.6
   27   screen.1
   28   screen.2
   29   screen.0
The addresses don't necessarily need to be the same, though they likely should since the easiest way is to map the static variables in the order of their appearance in the ASM code.

So for example the VM translator just converted all occurrences of "static 3" in class output to a symbol output.3 which it addresses with @output.3. You can find this symbol multiple times inside the Pong.asm file. Only the Assembler then maps this symbol to address 24 when converting it into Hack machine code.

P.S. it is better practice to maintain the upper- and lowercase of the class name - I was just using lowercase in the above example because it has been converted like that in the Pong.asm file from project 06.
Reply | Threaded
Open this post in threaded view
|

Re: Is "pop static 19" inefficient by nature?

WBahn
Administrator
In reply to this post by burge91
It's important to recognize that the static memory segment isn't like the other memory segments, so you can't generate code for it that is like other memory segments.

In fact, there are four different kinds of memory segments and each kind has a different type of code needed to implement it.

In one kind, the index isn't an index at all, but rather the actual value.

In another kind, the index is an offset relative to a fixed and known base.

In yet another kind, the index is an offset relative to a base that is unknown, but what is known is where the value of that base is stored.

In the final kind, there is no base at all, but the code needs to behave as if there were. This is the static segment. The approach that the text recommends to deal with this is to note that all that is important is that each static variable map to a location in RAM that is guaranteed to be the same whenever that static variable is referred to in either a push or a pop instruction AND that it is guaranteed that no other variable is going to use that same address.

The VM Emulator is free to create a static memory segment for each class and have the index refer to offsets relative to the base of the corresponding segment. In fact, that is what is should do if it is rigorously implementing the VM language model. But the VM Translator is not bound by the VM language model except to the extent that it has to generate ASM code that has the specified behavior -- how it achieves that behavior is completely up to it.

So the text suggests using the behavior of the assembler with regards to variable names to create a single static memory segment, located at base address 16, that has all of the static variables from all of the classes stored somewhere within it, but instead of trying to map them to indexes that are mathematically tied to the class name and index within the segment, let the assembler map them naturally by creating a unique name for each static variable that consists of the class name and the index. The numerical value of the index is meaningless in this approach, it merely becomes part of the name of the variable as far as the assembler is concerned.