Search code examples
assemblypseudocode

I am trying to convert this pseudo code into hack language saved as an .asm file


I have been stuck trying to figure out how to translate pseudo code into hack language formatted as .asm, that will run successfully using the CPUEmulator.sh in the Nand2Tetris, with attached .tst & .cmp files.

I keep receiving a 'Comparison Failure at Line: 2' Error on the Emulator.

The pseudo code below.

n=r0
m=r1
result = 0
i = 1
LOOP:
    if i > n goto STOP
    result = result + m
    i = i + 1
    goto LOOP
STOP:
R2 = result

My understanding is that I need to initlalize '@n', '@m', '@result', '@i'.

  1. n the multiplier and 'm' the multiplicand from register R0 & R1.

  2. Initialize 'result' to 0 and 'i' to 1.

  3. Enter a loop:

  • If i < n, exit the loop
  • Otherwise, add 'm' to 'result'
  • increment i by 1
  • Repeat the loop.
  1. Store the result in reg R2

Here is what I have written so far:

// Initialize variables
@R0        // Load the value of R0 (n) into the D register
D=M
@n         // Address for variable n
M=D        // Store the value of R0 in n

@R1        // Load the value of R1 (m) into the D register
D=M
@m         // Address for variable m
M=D        // Store the value of R1 in m

@result    // Address for variable result
M=0        // Initialize result to 0

@i         // Address for variable i
M=1        // Initialize i to 1

// LOOP
(LOOP)
    @i        // Load the value of i into the D register
    D=M
    @n        // Load the value of n into the A register
    D=D-M     // Subtract n from i and store the result in D
    @STOP     // Address for the STOP label
    D;JGT     // If i > n, jump to STOP

    @result   // Load the value of result into the D register
    D=M
    @m        // Load the value of m into the A register
    D=D+M
    @result   // Address for variable result
    M=D       // Store the new result back in result

    @i        // Load the value of i into the D register
    M=M+1     // Increment i by 1

    @LOOP     // Address for the LOOP label
    0;JMP     // Jump to LOOP

// STOP
(STOP)
    @result   // Load the value of result into the D register
    D=M
    @R2       // Address for register R2
    M=D       // Store the result in R2

// End of program
(END)
    @END      // Address for the END label
    0;JMP     // Infinite loop to end the program

I'm unsure if I am missing something or misunderstanding how to initialize result and i. I'm at a loss.

Here is the additional test file.

// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/04/mult/Mult.tst

load Mult.asm,
output-file Mult.out,
compare-to Mult.cmp,
output-list RAM[0]%D2.6.2 RAM[1]%D2.6.2 RAM[2]%D2.6.2;

set RAM[0] 0,   // Set test arguments
set RAM[1] 0,
set RAM[2] -1;  // Test that program initialized product to 0
repeat 20 {
  ticktock;
}
set RAM[0] 0,   // Restore arguments in case program used them as loop counter
set RAM[1] 0,
output;

set PC 0,
set RAM[0] 1,   // Set test arguments
set RAM[1] 0,
set RAM[2] -1;  // Ensure that program initialized product to 0
repeat 50 {
  ticktock;
}
set RAM[0] 1,   // Restore arguments in case program used them as loop counter
set RAM[1] 0,
output;

set PC 0,
set RAM[0] 0,   // Set test arguments
set RAM[1] 2,
set RAM[2] -1;  // Ensure that program initialized product to 0
repeat 80 {
  ticktock;
}
set RAM[0] 0,   // Restore arguments in case program used them as loop counter
set RAM[1] 2,
output;

set PC 0,
set RAM[0] 3,   // Set test arguments
set RAM[1] 1,
set RAM[2] -1;  // Ensure that program initialized product to 0
repeat 120 {
  ticktock;
}
set RAM[0] 3,   // Restore arguments in case program used them as loop counter
set RAM[1] 1,
output;

set PC 0,
set RAM[0] 2,   // Set test arguments
set RAM[1] 4,
set RAM[2] -1;  // Ensure that program initialized product to 0
repeat 150 {
  ticktock;
}
set RAM[0] 2,   // Restore arguments in case program used them as loop counter
set RAM[1] 4,
output;

set PC 0,
set RAM[0] 6,   // Set test arguments
set RAM[1] 7,
set RAM[2] -1;  // Ensure that program initialized product to 0
repeat 210 {
  ticktock;
}
set RAM[0] 6,   // Restore arguments in case program used them as loop counter
set RAM[1] 7,
output;

And the component file:

|  RAM[0]  |  RAM[1]  |  RAM[2]  |
|       0  |       0  |       0  |
|       1  |       0  |       0  |
|       0  |       2  |       0  |
|       3  |       1  |       3  |
|       2  |       4  |       8  |
|       6  |       7  |      42  |

EDIT: Thank you too Tange Perp for pointing out the code posting policy. I apologize for the screenshot and most importantly for the kitten, this is my first time, I shouldn't have rushed. :(.


Solution

  • Your code looks ok; however, it looks like the computation for 0×0 will take 22+ ticktock's and the Mult.tst test runner file only allows for 20 ticktock's for the first test.

    If that's the case, then the last part of your R2=result will not be executed, so R2 will still hold -1.

    Try setting the repeat in Mult.tst to 25 (instead of 20) for that first test.

    The repeat's of 50 and greater for the subsequent tests would seem sufficient for your implementation.


    The standard multiplication Mult.asm does not use data variables at all:

    • it multiplies R0 by R1 accumulating the result in R2
    • it counts down R1 decrementing to 0

    Here's the pseudo code:

    R2=0
    while(R1 != 0)
       R2=R2+R0
       R1--
    

    It is much shorter, as it forgoes variable initialization, so 20 ticktock's for the first test will suffice for that reference implementation.


    Regarding variables vs. numbered registers, I would say it is harder to read the numbered registers — useful names are valued by programmers.  The use of variables represents only a small overhead, as once they're established in initialization, then the loop is just as efficient as using the Rx's "registers".

    (The execution overhead in initialization is one or two instructions per variable to copy/initialize values and there is also space overhead for their storage, but to be clear, their names are strictly a compile/assemble time concept so naming incurs no runtime overhead: so go ahead and use the most descriptive names you can think of.)

    The count down to 0 is an algorithmic choice that is often made for efficiency when the actual value of the loop counter is otherwise unused since compare with 0 is easier than compare with n.  However, this destroys the R1 value — but Mult.tst is aware of that and resets both R0 & R1 to the test input values after running the hack code and before outputing answer lines.


    TL;DR the standard Mult.tst is expecting a particular implementation of Mult.asm, shorter, and also input destroying.  The pseudo code you're starting from is longer and doesn't quite match the expectations of Mult.tst, but I'd say the flaw is in Mult.tst not being sufficiently general purpose.