简体   繁体   中英

Stumped on Extended Multiplication for LC-3 Assembly

I'm trying to get Extended Multiplication to work on the LC-3. Relevant excerpts from my code:

    .ORIG   x3000

; Calculate AB = A x B
    LEA R6, ARGS
    LD  R0, B       
    STR R0, R6, #0  ; Store B into Multiplier address of ARGS
    JSR PRINT       
    LD  R0, A       
    STR R0, R6, #1  ; Store A into Multiplicand address of ARGS
    JSR PRINT       
    LEA R0, AB      ; R0 <- starting address of AB
    STR R0, R6, #2  ; Store starting address of AB into Product word of ARGS

    JSR XMULT

    ; DID WE LOAD THE PRODUCT PROPERLY?

    ; THIS SHOULD PRINT THE LEAST SIGNIFICANT BITS OF PRODUCT

    LDR R0, R0, #0
    JSR PRINT

    ; THIS SHOULD PRINT THE MOST SIGNIFICANT BITS OF PRODUCT

    LEA R0, AB
    ADD R0, R0, #1
    LDR R0, R0, #0
    JSR PRINT

; Calculate XY = X * Y


    TRAP    x25
;   Test Data
A   .FILL   x0010
B   .FILL   x00AB
X   .FILL   x1234
Y   .FILL   xABCD
AB  .BLKW   2
XY  .BLKW   2

;   Argument List
ARGS    .BLKW   1   ;Multiplier   (value)
    .BLKW   1   ;Multiplicand (value)
    .BLKW   1   ;Product      (address)
;**********************************************************


 XMULT  ;Extended Multiplication
    ;Arguments located by R6
    ;   multiplier (value)
    ;   multiplicand (value)
    ;   product (address)

    ST  R7, XMU7    ;Save Registers
    ST  R0, XMU0    ; TEMP register (for storing temp results and passing to RightShift subroutine)
    ST  R1, XMU1    ; Will store MPR (Multiplier)
    ST  R2, XMU2    ; Will store MND (Multiplicand)
    ST  R3, XMU3    ; Will store ACC (Accumulator)
    ST  R4, XMU4    ; Will serve as a COUNTER for loop
    ST  R5, XMU5    ; Will store BITMASK for testing
    ST  R6, XMU6    ; Argument list


    LDR R1, R6, #0  ; Store MPR into R1 (Multiplier is first item in the argument list pointed to by R6)
    LDR R2, R6, #1  ; Store MND into R2 (Multiplicand is second item)
    AND R3, R3, #0  ; ACC <- 0
    LD  R4, COUNTER ; Set counter
    LD  R5, BITMASK ; Set R5 to 0000 0000 0000 0001, the bitmask needed to test MPR[0]

    ; Counter and operands ready - now we can start the loop

MULOOP  ; MUltiplication LOOP

    AND R0, R5, R1  ; Test MPR[0]   
    BRz ELSE    ; Branch if MPR[0] isn't set
    ADD R3, R3, R2  ; ACC <- ACC + MND  

ELSE
    AND R0, R0, #0  ; Clear R0
    ADD R0, R3, #0  ; R0 <- ACC
    JSR SHIFT       ; ShiftRight(ACC)
    ADD R3, R0, #0  ; R3 <- Right-shifted ACC
    ADD R0, R1, #0  ; R0 <- MPR 
    JSR SHIFT       ; ShiftRight(MPR)
    ADD R1, R0, #0  ; R1 <- Right-shifted MPR
    ADD R4, R4, #-1 ; Decrement Counter

    BRp MULOOP      ; If Counter > 0, branch to beginning of loop   

MULOOPEND   ; MUltiplication LOOP ends here

; Write results to memory addresses (OUT-parameter segment)

    LDR R0, R6, #2  ; R0 <- Product(address), least significant digit
    STR R1, R0, #0  ; Right-shifted MPR goes in the lower address word
    STR R3, R0, #1  ; Right-shifted ACC goes in the higher address word

    LD  R7, XMU7    ; Restore Registers
    LD  R0, XMU0
    LD  R1, XMU1    
    LD  R2, XMU2
    LD  R3, XMU3
    LD  R4, XMU4
    LD  R5, XMU5
    LD  R6, XMU6
    RET

XMU0    .BLKW   1
XMU1    .BLKW   1
XMU2    .BLKW   1
XMU3    .BLKW   1
XMU4    .BLKW   1
XMU5    .BLKW   1
XMU6    .BLKW   1
XMU7    .BLKW   1

; Data

COUNTER .FILL   x0010
BITMASK .FILL   x0001

Please note that the subroutines PRINT and SHIFT simply print the contents of R0 to the console in bit form and execute a right-shift on the contents of R0, respectively. Please assume these are working correctly (I've tested them multiple times and they are).

So, the code is supposed to calculate the double-word product of two N-bit unsigned integers. Of course, the product is stored in two contiguous words, with the "least significant" bits being stored at the lower-address word.

In the XMULT subroutine, I use R3 (ACC for ACCumulator) and R1 (MPR for MultiPlieR) to store the "most significant" and "least significant" parts of the product, respectively. These are calculated using the standard General Multiplication Algorithm

MPR <- Multiplier
MND <- Multiplicand
ACC <- 0

for (int k = 1; k <= N; k++)
{
if (MPR[0])    // Test MPR[0]
  ACC <- ACC + MND

ShiftRight(ACC:MPR)
}

So that by the end of the loop, the double-word product is available in ACC:MPR.

ACC appears to store the correct value when the loop terminates, but MPR doesn't. For example, taking the values X and Y, consulting a bit multiplication calculator shows that x1234 * xABCD = xC374FA4

Now, if I run my code and multiply X and Y, once the multiplication loop terminates, ACC (the most significant apart of the product) stores b0000 1100 0011 0111 = x0C37 , so this part seems to be correct. However, MPR stores zero (b0000 0000 0000 0000 = x0000).

I've been using breakpoints and the Step Into function on my LC-3 simulator to go through my code step by step for the past few hours to try and figure out why this is happening, and the only thing I've noticed is that the Logical right shifts that occur during the multiplication loop are reducing MPR to 0 before the loop terminates (ACC gets the right value, though).

As I said, all the subroutines (SHIFT for logical right shift is the most important of them) are working correctly, and it SEEMS like MULOOP implements the General Multiplication Algorithm properly, so why is MPR getting zeroed out?

What's even more confusing is that I tried multiplying x0100 and x0200 (just to try two simple numbers), and I got the correct answer: x0002 was stored in ACC and x0000 was stored in MPR, making the product x20000 (since ACC is the most significant part of the product and MPR is the least significant).

I don't know what's wrong. I've been spinning my wheels trying all sorts of things for hours now:

  • I changed the order of the operands (swapped multiplier and multiplicand), which of course doesn't matter for multiplication, but I was desperate

  • I tried a totally different logical right SHIFT implementation that was also correct. As expected, the results were exactly the same as with the original one

  • I've changed the value of the loop Counter, trying to see if less iterations of the loop result in MPR NOT geting zeroed out before the loop terminates. Answer: it does, but as expected, this results in ACC not storing the correct value at loop termination anymore. Also, MPR doesn't store the correct value either - it just doesn't end up being zero (if I use a smaller counter).

I'm legitimately stumped. Is it a problem with my Multiplication loop's implementation, or is there something else wrong? I don't even know where to look for errors anymore.

Quick question:
Does the PRINT procedure preserve R6 ? If not you would need to reload it for the 2nd and 3rd arguments with extra LEA R6, ARGS instructions.


You don't actually ShiftRight(ACC:MPR)

You ShiftRight(ACC) ShiftRight(MPR)

 ADD R0, R3, #0 ; R0 <- ACC JSR SHIFT ; ShiftRight(ACC) ADD R3, R0, #0 ; R3 <- Right-shifted ACC ADD R0, R1, #0 ; R0 <- MPR JSR SHIFT ; ShiftRight(MPR) ADD R1, R0, #0 ; R1 <- Right-shifted MPR 

In this part of your program you perform 2 completely independant operations. You shift the word in R3 (ACC) to the right and you shift the word in R1 (MPR) to the right.
You're forgetting however that ACC:MPR is supposed to be a 32-bit quantity. The bit that got shifted out on the right side of the ACC must get shifted in on the left side of MPR. Since this is not happening, you end up with an empty MPR.

What you should do:

If Bit(ACC,0) = 0
    ShiftRight(ACC)
    ShiftRight(MPR)
Else
    ShiftRight(ACC)
    ShiftRight(MPR)
    Add(MPR,32768)
Endif

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM