Logical operations

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

Re: Logical operations

ybakos
Signed, with no overflow checking. A liberty we have with the Hack platform.
dlk
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

dlk
Strictly by the sign of the subtraction result it is, then.
Thank you!
dlk
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

dlk
Hi,

Actually, the provided VMEmulator.sh does appear to implement 'lt' to handle signed 16-bit integer comparisons correctly, i.e., it does not just subtract and look at the sign of the result, it handles overflow.

For instance, the following VM program

push constant 1
push constant 2
lt                   // is 1 < 2
push constant 32767
neg                  // -32767
push constant 10
lt                   // is -32767 < 10
push constant 32767
neg
push constant 10
sub                  // resutlt of -32767 - 10, i.e. +32759
push constant 32767
not                  // -32768
push constant 32767
lt                   // is -32768 < 32767 ?
push constant 1
neg
push constant 0
lt                   // is -1 < 0 ?
push constant 32767
push constant 32767
not
lt                   // is 32767 < -32768
push constant 32767
push constant 32767
not
gt                   // is 32767 > -32768

leaves

-1
-1
32759
-1
-1
0
-1

on the stack.

If the VM didn't handle 'lt' and 'gt' correctly for signed 16-bit integers, then the compiler would have to handle it (or else the Jack programmer would have to know about it and compensate, which wouldn't reflect well on a 'high level' language).  I think it makes sense to handle it in the VM.  Whether by design or just incidentally because VMEmulator.sh happened to be implemented in a high-level
language, that program seems to agree ;-)


Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

krinnert
Hm. I was plowing through your numbers. Aren't  your results exactly what you would expect from ybakos's post?

I might have got that wrong, maybe you just wanted to confirm his post?

Anyway, there is no carry bit in the ALU, so there is no way to get fancy with overflows.

I know, in principle there is, using the memory... but I really don't think this is intended.

So I'd say it's gonna be fine with a 'naive' implementation.

Someone correct me if I'm wrong.


Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

sakumar
This post was updated on .
In reply to this post by culchie
culchie wrote
After changing this mine takes the same number of steps as yours but the code is quite different which I found interesting. Your code uses 2 labels while mine uses 1.
So I looked at it some more and now I'm fairly sure that it can be done in 17 steps.


PS It's great to see someone else's code, looking at yours gave me a fresh perspective however I think we're not supposed to post code here.
My revised code is a bit of a cheat but seems to work. If you're interested I'll email it to you.
(I know -- Holy Smokes this is an ancient thread!)
I implemented conditionals in just 12 asm instructions for each (including the label). It passes the test suite, so I don't think I overlooked anything. I didn't use any special tricks, except perhaps that I took advantage of the fact that 0 and -1 are built in commands so there is no need to load these constants into the A register first.
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

sankar.lp.gym
This post was updated on .
In reply to this post by cadet1620
What # represents


[advertising link deleted. --admin]
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

cadet1620
Administrator
I don't see "#" used in the book, but generally in the context of programming languages, it is another way to express not equals using the ASCII character set. You may see a#b, a!=b or a<>b all used at various times.

I've also seen a/=b used in old papers, which can be confusing since in modern usage it means a=a/b.

--Mark
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

dannyrose42
In reply to this post by culchie
So in completing the Ch.7 project are we expected to devise a method for generating globally unique labels?

From reading this thread it appears as if it has been decided that it is impossible to implement the logical operations on the HACK asm without the use of globally unique labels. However, as stated before, the book makes no mention (in chapter 7) of this. It sounds like culchie went ahead an implented such a method with his counter but I'd love a response from an admin as to whether or not this is the chapter's intention.
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

ybakos
Labels at the assembly level need to be globally unique, hence the convention of using class names, method names, and an "internal" number for branches within a function (different if statements should result in different labels).
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

dannyrose42
Thanks for the reply. That is indeed what I did. Its just such an important consideration and I was surprised that the book made no mention of it that I can see (someone correct me if I'm wrong here).
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

yangmillstheory
I generated unique jump labels for each translation of lt, gt, and eq, as well.

The tests didn't pass when the same labels are used - a jump to label L will always jump to the last declared instance of label L. I think more advanced assemblers allow for re-using existing labels, see this answer on SO.

I was wondering if I could get some help with some confusion I'm going through.

In principle, won't each instance of lt, gt, and eq in VM code take up memory, starting from RAM address 16 onward, since they constitute user-defined variables?

When I run the tests in the CPU Emulator (they're all passing), I don't see any such stored addresses. So I think my confusion is that the translation process of the assembly code to binary code will take more memory - storing each unique name in the "SymbolTable" - but those label addresses won't actually be stored anywhere in RAM, so the runtime memory of the actual program isn't increased.
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

cadet1620
Administrator
yangmillstheory wrote
I generated unique jump labels for each translation of lt, gt, and eq, as well.

The tests didn't pass when the same labels are used - a jump to label L will always jump to the last declared instance of label L. I think more advanced assemblers allow for re-using existing labels, see this answer on SO.
Yes, as you saw each instance of lt, etc., needs its own unique labels. I wrote my assembler to complain if a label was defined more than once:
[D:/TECS/projects/test]
% cat foo.asm
(foo)
@foo
0;JMP
(foo)
@foo
0;JMP
(foo)
@foo
0;JMP
[D:/TECS/projects/test]
% hasm foo.asm
foo.asm(4): (foo)
     Multiple definitions for symbol foo

foo.asm(7): (foo)
     Multiple definitions for symbol foo

Code size =     6 (0x0006)
Data size =    16 (0x0010)
In principle, won't each instance of lt, gt, and eq in VM code take up memory, starting from RAM address 16 onward, since they constitute user-defined variables?

When I run the tests in the CPU Emulator (they're all passing), I don't see any such stored addresses. So I think my confusion is that the translation process of the assembly code to binary code will take more memory - storing each unique name in the "SymbolTable" - but those label addresses won't actually be stored anywhere in RAM, so the runtime memory of the actual program isn't increased.
That's right. Labels only take up space in the Assembler's memory. They don't require any RAM or ROM in the generated program.

That said, each instance of lt, etc., code does require ROM for its instructions. This isn't too bad because there aren't that many comparisons in typical programs. It's a bigger problem for call and return which are both much longer code and occur more frequently in programs.

There's a forum post that shows how to share one comparison routine throughout the code.

You can do something similar for call and return.

Note that you don't need to worry about the size of generated code for the chapter 7 and 8 tests.

--Mark


Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

yangmillstheory
Thanks Mark.

Do you know if modern assemblers try to optimize sharing common routines, like comparison (or other?) routines?

Victor
Reply | Threaded
Open this post in threaded view
|

Re: Logical operations

cadet1620
Administrator
The assemblers that I've used don't do any major optimizations like common code abstraction. Some of them will do instruction selection, for example automatically choosing between short and long jump instructions if they can determine that a short jump will suffice.

I usually turn even small optimizations like this off because when I need to write in assembly language it's because I need to write code sequences that have very tight timing requirements, and changing a single instruction in a loop can cause problems.

The C compilers that I use in embedded systems programming generally have optimizers that are quite good; that's where all the magic is.

--Mark
12