|
|
Good day,
I'm trying to fix multiplication problem but I keep getting a comparison failure at line 2. I know I'm not getting it right. But I needed to be shown the right direction.
@R0
D=M
@6
M=A
D=M
@R1
D=M
@2
M=A
D=M
(LOOP)
@END
D;JLE // IF R0 <=0 END PROGRAM
@R0
D=M
@R1
D=D-A
@R0
M=D
@LOOP
0;JMP
(END)
0;JMP
|
|
I'm assuming your solving the multiplication problem in Lesson 4. The problem states that the registers R0 and R1 are to hold the multiplier and multiplicand and the product is to be saved in R2. In your program, R2 is not used. In fact, the product (the sum of R0 copies of R1) is not saved anywhere.
I entered your program into the CPU Emulator. It assembled without error and executed without error. There is a slight problem with the way you terminate execution using an END label. It's not quite right.
A good way to proceed on problems of this type is to write a high-level language version of the algorithm you are going to use and translate it into Hack assembly. For simple programs (without function calls) there is generally a straightforward translation. Suppose you wanted to multiply 8 times 3. In Python, for example:
R0 = 8 # multiplicand
R1 = 3 # multiplier
R2 = 0 # product accumulator
while R1 > 0:
R2 = R2 + R0 # add the multiplicand to product
R1 = R1 - 1 # decrement the multiplier
The first line in Hack assembly could be written as:
@8
D=A
@R1
M=D
and so on. Hope this helps.
|
Administrator
|
In addition to the good advice already given, here's some more general steps to use.
What did your program produce for Line 2 in the .out file and what does the .cmp file say it should produce?
Do you agree that the .cmp file is correct and that the .out file is wrong? Often this is enough to steer you in the right direction.
Walk through your program step by step, tracking what you think the values should be if what you had in mind was happening and look for where they start deviating. Then focus on why the actual values are what they are instead of what you intended.
In looking over your code briefly, I see that you have @6 and @2. Why? Are these meant to be register locations? In which case, use @R6 and @R2 to make your code more readable. If they are simply values (i.e., the number six and the number two), what is there purpose in your approach?
|
|
Thanks for the reply. It clarifies some of my confusion. And sorry my late response, sometimes life gets in the way.
I have been able correct some of the errors in my program and I could see the progress as my .cmp file no longer conflicting with my .out file at line 2 but at line 5 now. My initial understanding was that I am suppose to declare the values and put them in the right memory for the multiplication operation, but I realized that the values are set in the script file. At line 5, .cmp file expects 3*1=3 but my .out file is producing 3*1=0.
I think I need to do a bit of modification but I am not sure what to do next.
@3 //
D=A
@R0
M=D
@1
D=A
@R1
M=D
@R2
M=0
(LOOP)
@R2
D=M
@R0
D=D+M
@R1
M=M-1
@2
D=A
@R0
M=D
@4
D=A
@R1
M=D
@R2
D=M
@R0
D=D+M
@R1
M=M-1
@LOOP
D;JGT // IF R0 >0 RUN THE ABOVE PROGRAMS
(END)
@END
0;JMP
|
|
This post was updated on .
Completely understand "life gets in the way." Substantial progress has been made. I note that with the statement D=D+M you add the multiplicand (R0 = 3) to the current value of the product accumulator (stored in R2). As I mentioned in my first reply, you don't seem to be saving this "running product" result anywhere. I would start fixing there.
I had no looked at the rest script until now. The script initializes the multiplicand and multiplier, so, contrary to my pervious advice, your program should initialize them. As the script comments indicate, your program must still handle initialization of the product accumulator.
After you correctly decrement the multiplier (stored in R1) with the statement M=M-1, what it the intent of the following code down to @LOOP?
Still a problem with the loop condition. What's in D when you execute "D;JGT"? What happens in the case where the multiplier (R1) starts at zero? There may be a logic problem here: "while" loop versus "do...while" loop (in C language).
Your termination "infinite loop" now looks good.
Hope this helps. You are really close to a correct solution.
|
|
I think I need more clarification. I have modified the program several times, but I'm still stuck in the same place. For instance, I thought by the statement "D=D+M" I'm saving the result of the computation in the D register.
Concerning the loop, I only intend to iterate until the "M=M-1" condition is met.
According to the lesson, I think D;JGT is suppose to jump to execute @LOOP which is a reference to (LOOP). My understanding may be deficient.
|
|
I am looking at the last version of your program that was posted. It would be really helpful if you would include a comment with each line to explain what you intend the statement to accomplish.
You are right about the statement D=D+M. However, remember the problem specification requires that the product end up in R2. Also, registers D and A are used for temporary operands or temporary storage of results. In the apparent algorithm you are using to compute the product, which employs a loop construct, the D register does not contain the product until the very last iteration. You must do something with the "partial" product during each iteration of the loop.
I'm not exactly sure how to respond to your comment concerning the loop. Not knowing the level of your programming experience, I apologize in advance if the following is below that level.
High-level languages generally provide a syntax for three main kinds of looping constructs. A pre-condition conditional loop (while<condition==True>{...}), a post condition conditional loop (do{...}while<condition==True>), and a counting loop (for<N times>{...}) Which kind do you have?
In the Hack assembly language, there is only one kind of looping statement, an arithmetic conditional loop with 6 different condition variations. Fortunately, any of the types of loops available in high-level languages can be constructed with this kind of looping statement. Placement of the loop statement in the program and the arithmetic condition evaluated determines this.
The hardest thing in constructing assembly language loops, for me anyway, is insuring that the loop is iterated the correct number of times for every possible starting condition. I will call the block of statements in the loop that are executed in each iteration the body of the loop. One thing you need to decide at the start is whether there are conditions for which the loop should not execute the body even one time or whether the loop should always execute the body at least once. Which case do you have for this problem? Is there a starting condition for which the loop body should never execute or must we always execute the body at least once? Your program appears to currently be using a post-condition type of conditional loop, so the loop body will always execute at least one time.
In reading your code, it seems you are using R0 as the multiplicand and R1 as the multiplier; that is, the product (R2) is R0 + R0 + ... + R0, where the number of terms is equal to R1. You need to look at what is in the D register when the conditional statement executes. Is it what you want?
The test script sets various values for R0 and R1. You don't need to set values for R0 and R1 in your program. Look at the values that will be tested by the script. This will help you determine which kind of loop you need to use.
|
|
Thanks for the detailed explanation. Although I am very familiar with the loop concept you mentioned but refreshing ones' mind doesn't harm in anyway. Actually, my determination was that anytime I am going to reply to this message, it will be to give you a glad tiding that I did it. But I've been on it all this while and I couldn't seem to be getting it right. Although progress has been made.
It appears that the line:
@R2
M=M+D
is not adding things up. D is holding the value contained in R0 and I intend to add it to R2, but I keep getting comparison failure at line 5 and .out file is producing R2=0 where R0=3 and R1=1.
Here is the new version of my program. THANKS.
@i //variable i refers to some memory location
D=M // initialize i to the value of RAM[0] set in the test file
@R0
M=D // R0 = i
@j //variable j refers to some memory location
D=M // initialize j to the value of RAM[1] set in the test file
@R1
M=D // R1 = j
@R2
M=0 // R2 = 0
(LOOP) // LABEL DECLARATION
@R0
D=M // D = 3
@R2
M=M+D // R2=R2+3
@R1
M=M-1 // R1=R1-1
D=M // D = R1
@LOOP //Jump to (LOOP)
D;JGT // IF R1 >0 GOTO (LOOP)
(LOOP)
@i
D=M
@R0
M=D
D=M
@R2
M=D+M
@j
D=M
@R1
M=D
M=M-1
D=M
@LOOP
D;JGT
(LOOP)
@i
D=M
@R0
M=D
D=M
@R2
M=D+M
@j
D=M
@R1
M=D
M=M-1
@LOOP
D;JGT
(LOOP)
@i
D=M
@R0
M=D
D=M
@R2
M=D+M
@j
D=M
@R1
M=D
M=M-1
@LOOP
D;JGT
(LOOP)
@i
D=M
@R0
M=D
D=M
@R2
M=D+M
@j
D=M
@R1
M=D
M=M-1
@LOOP
D;JGT
(LOOP)
@i
D=M
@R0
M=D
D=M
@R2
M=D+M
@j
D=M
@R1
M=D
M=M-1
@LOOP
D;JGT
(END)
@END
0;JMP
|
Administrator
|
You've got the (LOOP) label declared in multiple places.
You can jump to a label from many places, but you can only declare a label once.
|
|
WBahn's response is correct. Although the Hack assembler built into the front end of the CPU emulator does not seem to flag multiple label declarations as a error, it most certainly is. Your program loads into the CPU emulator without error. The label "LOOP" is converted to ROM address 76. This appears to be the location where the LOOP label is last defined in the program. The rule for loop declarations in Hack assembly language is: define only once, use many times (as necessary).
This is unfortunately not your only problem. The length of your program has steadily increased from the first version, and I'm not sure why. It's reached the point now where I'm having trouble following the logic. Perhaps it's time to start fresh.
However, lets walk through the program a bit, line by line, and see what we can figure out. I'm not counting blank lines.
Line 1: @i //variable i refers to some memory location While your comment is a true statement, it does not convey any information regarding why you are declaring the variable i - what's its intended use in the program. After this statement is executed, the variable i is equivalent to 16, the first available variable address as assigned by the assembler at assembly-time. That address is loaded into the A register by the statement @i. As a side effect of the statement the pseudo-register M = RAM[A] = RAM[16].
Line 2: D=M // initialize i to the value of RAM[0] set in the test file From the previous statement, we know that M now represents the value contained in RAM[16]. The statement loads the current value of RAM[16] into the D register.
Line 3: @R0 The predefined symbol "R0" is assigned the value 0 by the assembler. This statement loads that value (0) into the A register, and the M pseudo-register now references RAM[0].
Line 4: M=D // R0 = i Completely correct, but I'm not sure what you are trying to do. Initialize R0 to 0? The test script does this for each test case with the statements "set RAM[0] 0, // Set test arguments" or "set RAM[0] 3, // Set test arguments". The problem statement specifies that the register R0 is to contain either the multiplier or the multiplicand. For each test, the test script initializes the multiplier and multiplicand test values, so there's no need for your code to do this.
Line 5: @j //variable j refers to some memory location Same comment as Line 1, except the variable j is set to 17, and after the statement executes, M references RAM[17].
Line 6: D=M // initialize j to the value of RAM[1] set in the test file Same comment as Line 2
Line 7: @R1
Line 8: M=D // R1 = j Similar to Line 3 and 4. The test script handles initializing the register R1 for each test case.
Line 8: @R2
Line 9: M=0 // R2 = 0 These lines correctly initialize the specified product accumulator (R2) to 0. This is probably where your program should start.
Line 10: (LOOP) // LABEL DECLARATION Remember WBhan's comment. You have now defined the label "LOOP". You shouldn't define it again. As we previously discussed, I'm assuming that you're going to find the product by successive addition. This is the entry into the loop that performs the multiplication by successive additions. Each time you enter this loop, R0 and R1 should contain the multiplier and multiplicand and the product accumulator (R2) should contain 0. The test script set the test values for R0 and R1. You should look at the test script and determine what the test cases are. For example, the first case is R0 = 0 and R1 = 0 (a boundary case).
Line 11: @R0
Line 12: D=M // D = 3 Puts the value currently stores in R0 in register D. The comment indicates its the value 3. For the first test case it's not. The test script assigned the value 0. So these two statements load the current value of R0 into D. This value may change for every test case. However, this seems reasonable.
Line 13: @R2
Line 14: M=M+D // R2=R2+3 The comment is incorrect. What you're doing in Line 14 is adding the contents of the D register (which was set in Lines 11 and 12 to the value of R0) to the contents of R2 and saving the result back in R2; that is, R2 = R2 + R0 This tells me that you are treating R0 as the multiplicand in the program. I would say that this looks good.
Line 15: @R1
Line 16: M=M-1 // R1=R1-1 These two lines correctly decrement the current value of R1, which originally contained the value of the multiplier. Remember though, you are losing the original value of the multiplier (R1) with this algorithm. The problem specification is silent on this, so I guess it's OK. In fact, the test script restores the R0 and R1 values at the end of each test "in case program used them as loop counter." In another program context it might be necessary to retain the multiplier value, so this program would have to be changed.
Line 17: D=M // D = R1 Perfect! Set up register D as the loop test value.
Line 18: @LOOP //Jump to (LOOP)
Line 19: D;JGT // IF R1 >0 GOTO (LOOP) Again, this looks OK. Except...what happens in the cases where the multiplier is set by the test script to an initial value of 0 and the multiplicand is non-zero? This is in fact one of the test cases.
Also, don't forget to add the terminating "infinite loop" code at the very end. An important habit to develop for Hack assembly language programming.
I'm not sure what the rest of the code was intended to do. Rhetorical question: Do you really need it? This only problem you seem to now have is the special case mentioned above. It has to do with the kind of loop you are using, which I believe I mentioned earlier. You are using a "do...while" type of loop. It always executes at least one time. Is this really the kind of loop you should be using? Do you have a test case where you never need to add anything to the "zeroed" product accumulator?
Keep working; you're nearly there.
|
|
I finally got it! It indeed worked! Thanks for your help I really appreciate it.
I finally decided to write the program in java and see things clearer.
It worked in java with a few lines of code but getting it to work in the hack assembly language became a very difficult problem to solve.
After dissecting how the CPUEmulator was running it, I realised that the line "repeat 120" is causing the Emulator to continue to run the program until the time elapses. The side effect of it was that it run some other portion of the program which will eventually set R2 = O.
Following your guide and that of WBahn, I eradicate the multiple loop blocks, I set thecondition not only check if R0>0 but also to check if R0=0 and then run the appropriate block of code. And then included the infinite loop so that it will keep doing that for the time remaining. As for the loop, while loop type of loop did the trick for me.
Thanks for your help I really appreciate it.
|
|