This post was updated on .
It might not just be a possible but an actual bug.
I did this in my test.asm because breaking things is a specialty of mine.
@1 //set register A to 1
@2 //set register A to 2
@R01 //this registers as a symbolic instruction like @address
M = M - 1 //sets the value of the memory pointed at by R01 to -1
M = M - 1 //sets the value of the memory pointed at by R01 to -2
in the assembler it sets @R01 to the address 10000 in binary, I don't know if you'll get different.
but what happens if I do a bunch of things and then set D to some value, let us assume 2012, then have to store it in memory 16 (10000 in binary!) because I've run out of memory registers for whatever it is I was doing.
M = D
I've just overwritten -2 with 2012
if I do a jump command to @R01
D;JGT //assume we will jump at this point.
then do some calculation like
D = M + D
then instead of getting some value decreased by 2, it will instead increase by 2012.
Here below are 2 screenshots of this in action
If you guys get this please make a fix because this could be a bug that might if not already has broken some hack code.
On what basis to you claim that your first M=M-1 instruction sets the memory pointed to by R01 to -1. This assumes that it was previously equal to zero and I don't see anything that is making that happen.
This is exactly what it should be set to unless you have a previously used variable. The assembler is supposed to assign variables to RAM addresses beginning with address 16 (which is 10000 in binary).
Well, what would you expect or want to have happen?
If you are going to explicitly store something in RAM, it is YOUR responsibility to ensure that nothing else is using that memory location. Since you KNOW that the assembler IS going to assign the variables it finds to memory locations starting at 16, then this means that you can't have ANY variables in your program.
Why would you want to jump to ROM address 16 just because the variable R01 is associated with RAM address 16? That makes no sense whatsoever.
Why would you expect it to do anything else?
It is doing EXACTLY what the language specification requires. It is YOUR responsibility to write code that behaves correctly in light of what the language specification requires.
@ABC can have a few meanings:
- it could be predefined, like @THIS or @R1
- it could be a label defined in your program with (ABC)
- if it's none of the above, then it is assumed as a new "variable". The assembler allocates these from address 16.
The @R01 in your program is from this last category.
The memory registers 0 - 15 have mnemonic names: R0 - R15 (plus additional names, like THIS, THAT, LCL, etc, which will make more sense in later chapters). Memory addresses from 16-255 (if I remember correctly) are used for symbols of the third kind.
In the HACK architecture the memory up 16383 is just RAM. It doesn't have any special meaning. But because the CPU has just 2 internal registers, the authors named the initial 16 addresses as additional registers. (They are still registers in the RAM and not internally in the CPU.) This does not mean that your program cannot use the rest of the memory. It just means that you need to be aware of the architecture and of how the assembler works.
First of all, I don't see this jump command in the screenshot below.
Second, in HACK there is ROM where the program is stored and RAM where data is stored. They both have addresses starting from 0. The difference is, that the CPU will read the instructions only from ROM and will read data only from RAM.
When you write @R01 this will be treated as label from the third kind and will get an address 16 (assuming it's the first such label in your program). So @R01 is compiled as @16. The next jump will jump to address 16 in ROM, which from the screenshot is empty. That is, the ROM there is undefined (probably 0?).
You can write things like:
@R1 0;JMP // Jump to the address 1 (second instruction in your program)or
@R01 0;JMP // Jump to address 16, assuming R01 is from aboveor
@123 0;JMPbut all these are bad practice, because they will confuse you, the programmer and may introduce subtle and hard to find bugs.
The first 2 forms should be used to access RAM addresses, while the @123 form should be used to store immediate values. If you want to jump to some address in your program, you should define it with a label like this:
... (LOOP) ... @LOOP 0;JMPor
... @LABEL D;JGT ... (LABEL) ...Again, technically there's nothing stopping you from doing something like:
@LABEL M=Dbut it's a bad practice.
A piece of advice: always start by assuming the bug is in your code. Next, check your colleague's code, then library code, compiler/interpreter, OS and last, the hardware.
This is the order from the least tested to the most tested.
Sorry for replying so late. Was at a friend's house for the past week.
I've learned my lesson, please don't hurt me. Lol.
I realized that I was doing the same thing in my assembler. I also assign values to registers starting from register 16 because 0-15 are used by specific keywords which also means that register 16384 and register 24576 are being used.
I was going to assign the new labels to random memory registers but then I realized I had no control over which register it would overwrite and that could lead to a huge mess down the line so doing it sequentially starting at 16 is favourable.
I'm sorry I jumped to conclusions regarding the given assembler. Looks like I have much more to learn. Thank you for pointing out my ineptitude.
There is no need to apologize. It is perfectly possible to find bugs on any level. Just look at SSH heartbleed or Intel's Meltdown and Spectre bugs for some recent high-profile problems in places, one normally don't expect to see.
I think in this case you didn't have a good enough model of how the assembler works and so you had a different expectations.
I doubt you did anything that pretty much all of us didn't do at about the same stage in our learning. We think that we know how our code should behave (usually based on knowing how we want it to behave and not how it should actually behave) and we can't find any errors in our code, so it is natural to conclude that the error must be somewhere else.
At first we are frustrated by all of the bugs in the compiler that we have to find ways to "work around" -- often by learning how to write code properly, even if we don't realize that's what we are doing. Eventually we learn enough -- and have gone down that path enough -- that our first instinct becomes to assume that the problem is still with our code and to explore the life out of it; that is almost always the correct thing to do as the problem is almost always with our code.
Still, we know that bugs do exist in virtually any piece of software, so we can't help but feel the excitement when we become convinced that we really are onto a bug in the compiler or whatever. Hopefully that is now tempered by the realization that we will have to have our ducks in a row to convince anyone else, and so we test and document and, in the process, almost always find what it was we did wrong in the first place -- but that's okay because we've long since adopted the attitude that whether we discover a bug in the compiler and get to report it or learn something new about the fine print in how the language behaves, we have come out a winner. In fact, by this point learning something new about the language is the preferred outcome.
|Free forum by Nabble||Edit this page|