assemblydivisionlc3

A trap was executed with an illegal vector number in LC3


I have been working on an assignment where I must convert a given number into different bases in the LC3. To do this, I need to use division, and while I have had an assignment where I did this same thing, the division algorithm for this one is different.

I am expected to use extended division in this assignment. The basic algorithm is:

Given a 32 bit number, store the first 16 bits into a memory address (called the high numerator), the last 16 bits (the low numerator) into another one, and the divisor, another 16 bit number, into another one. So, in the subroutine, the numbers would be fetched from memory into different registers and the algorithm would work as follows:

For simplicity, our instructor is letting us use 0 as the high numerator. So, in order to fulfill the algorithm, I came up with the following procedure:

Below is the full code I have so far:

    .ORIG   x3000
    LEA R0, MPROMPT
    TRAP    x22
LOOP
    JSR GETDEC      ;Input an unsigned (decimal) integer
    ADD R0, R0, #0  ;Exit if the input integer is 0
    BRZ EXIT

    JSR NEWLN       ;Move cursor to the start of a new line
    AND R1, R1, #0  ;R1 = 10 (output base 10 DECIMAL)
    ADD R1, R1, #10 
    JSR PRINT       ;Print the integer in decimal

    JSR NEWLN       ;Move cursor to the start of a new line
    AND R1, R1, #0  ;R1 = 2 (output base 2 BINARY)
    ADD R1, R1, #2
    JSR PRINT       ;Print the integer in binary

    JSR NEWLN       ;Move cursor to the start of a new line
    AND R1, R1, #0  ;R1 = 8 (output base 8 OCTAL)
    ADD R1, R1, #8
    JSR PRINT       ;Print the integer in octal

    JSR NEWLN       ;Move cursor to the start of a new line
    AND R1, R1, #0  ;R1 = 16 (output base 16 HEXADECIMAL)
    ADD R1, R1, #15
    ADD R1, R1, #1
    JSR PRINT       ;Print the integer in hexadecimal

    JSR NEWLN       ;Move cursor to the start of a new line

    BRNZP   LOOP
EXIT                ;Loop exit point
    TRAP    x25     ;HALT program execution
MPROMPT .STRINGZ "Enter a sequence of unsigned integer values\nEnter 0 To Quit\n"

;Subroutine NEWLN*************************************************************
;Advances the console cursor to the start of a new line
NEWLN   
    ST  R7, NEW7    ;Save working registers
    ST  R0, NEW0

    LD  R0, NL      ;Output a newline character
    TRAP    x21

    LD  R0, NEW0    ;Restore working registers
    LD  R7, NEW7
    RET         ;Return
;Data
NL  .FILL   x000A   ;Newline character
NEW0    .BLKW   1   ;Save area - R0
NEW7    .BLKW   1   ;Save area - R7

;Subroutine GETDEC************************************************************
;Inputs an unsigned integer typed at the console in decimal format
;The input value is returned in R0
GETDEC
    ;Save Values of ALL Registers that don't need to change
    ST  R1, GET1
    ST  R2, GET2
    ST  R3, GET3
    ST  R4, GET4
    ST  R5, GET5
    ST  R6, GET6
    ST  R7, GET7    ;Save R7 to be able to return to main

    AND R3, R3, #0  ;Prepare a value
    LEA R0, GPROMPT
    TRAP    x22
GETDKEY
    TRAP    x20     ;To input a keystroke   
    ADD R2, R0, #-10    ;Subtract new keystroke by 10 (new line, or enter)
    BRZ GFINIS      ;If R2 is 0 we just pressed enter, so we break out
    TRAP    x21     ;Echo keystroke     
    ADD R2, R0, #0  ;Get the number back
    AND R2, R2, #15 ;We mask to only get the first four bits
    ADD R3, R3, R3  ;1st addition: x + x = 2x
    ADD R4, R3, #0  ;Store the 2x
    ADD R3, R3, R3  ;2nd addition: 2x + 2x = 4x
    ADD R3, R3, R3  ;3rd addition: 4x + 4x = 8x
    ADD R3, R3, R4  ;4th addition: 8x + 2x = 10x
    ADD R3, R3, R2  ;10x + new number
    BRNZP   GETDKEY     ;Go back to get next number
GFINIS
    ADD R0, R3, #0  ;Put number into R0

    ;Restore the values of registers that don't need change
    LD  R1, GET1
    LD  R2, GET2
    LD  R3, GET3
    LD  R4, GET4
    LD  R5, GET5
    LD  R6, GET6
    LD  R7, GET7    ;Get back the value of R7
    RET
;Data
GPROMPT .STRINGZ "Enter an unsigned integer> "  ;Input Prompt
GET1    .BLKW   1
GET2    .BLKW   1
GET3    .BLKW   1
GET4    .BLKW   1
GET5    .BLKW   1
GET6    .BLKW   1
GET7    .BLKW   1

;Subroutine PRINT*************************************************************
;Displays an unsigned integer in any base up to 16, e.g. binary, octal, decimal
;Parameters - R0: the integer - R1: the base
PRINT
    ST  R0, PR0
    ST  R1, PR1
    ST  R2, PR2
    ST  R3, PR3
    ST  R4, PR4
    ST  R5, PR5
    ST  R6, PR6
    ST  R7, PR7 

    LEA R7, ARGLIST ;Point to ARGLIST to start populating the args
    STR R0, R7, #0  ;Put R0 to be the low numerator
    AND R0, R0, #0  ;R0 is not needed after this, so it is cleared

    ADD R7, R7, #1  ;Go down on pointer once to store next arg
    STR R0, R7, #0  ;Put R0, now x0000, to be the high numerator

    ADD R7, R7, #1
    STR R1, R7, #0  ;Store the base, R1, to be the DVR
    ADD R7, R7, #1  ;Have R7 point to Quotient

    LEA R6, BUFFER
    AND R5, R5, #0  ;To make sure R5 is cleared
    LEA R4, DIGITS  ;Prepare pointer to the digits  
SDIV
    JSR DIVIDE

    LDR R2, R7, #0  ;Since the addresses should now be ready, 
                ;R7 would point to quotient, passed to R2
    LDR R3, R7, #1  ;Pass remainder to R3

    ADD R3, R3, R4
    LDR R5, R3, #0
    ADD R6, R6, #-1 ;Decrement to get to the next one
    STR R5, R6, #0  ;Store number into buffer

    LEA R7, ARGLIST ;Go back to ARGLIST to store new numerator
    STR R2, R7, #0
    ADD R7, R7, #3  ;Point to quotient

    ADD R0, R2, #0  ;Pass quotient into R0
    BRP SDIV        ;If number is at least 1, then divide again

    ADD R0, R6, #0  ;If we got here, then we should have the converted number
    TRAP    x22     ;Last but not least we display

    LD  R0, PR0
    LD  R1, PR1
    LD  R2, PR2
    LD  R3, PR3
    LD  R4, PR4
    LD  R5, PR5
    LD  R6, PR6
    LD  R7, PR7
    RET
;Data
DIGITS  .STRINGZ "0123456789ABCDEF" ;Digits
        .BLKW   18          ;Output Buffer
BUFFER  .FILL   x0000           ;Null
PR0 .BLKW   1
PR1 .BLKW   1
PR2 .BLKW   1
PR3 .BLKW   1
PR4 .BLKW   1
PR5 .BLKW   1
PR6 .BLKW   1
PR7 .BLKW   1

;Subroutine DIVIDE************************************************************
;Extended division is done here
DIVIDE  
    ST  R1, DIV1
    ST  R2, DIV2
    ST  R3, DIV3
    ST  R4, DIV4
    ST  R5, DIV5
    ST  R6, DIV6
    ST  R7, DIV7

    LEA R6, ARGLIST ;Point straight to where arguments start

    LDR R5, R6, #0  ;Load NUMLOW into R5
    ADD R6, R6, #1  ;Go down one step in pointer

    LDR R4, R6, #0  ;Load NUMHIGH into R4
    ADD R6, R6, #1  ;Go down once more

    LDR R3, R6, #0  ;Load DVR into R3
    ADD R6, R6, #1  ;Go down yet once more to be ready when we 
                ;store results

    LD  R1, M8BIT   ;Make R1 be equal to x0080 to get last bit 
                ;from NUMLOW

    AND R2, R2, #0  ;Set R2 as counter to decrement with each
    ADD R2, R2, #10 ;iteration
    ADD R2, R2, #6

ITER    ADD R4, R4, R4  ;Shift NUMHIGH to the left once
    AND R7, R5, R1  ;Masking. If R5[7] == 1, then R7 == x0080               
    BRNZ    NOADD       ;(or positive)

    ADD R4, R4, #1  ;If this happens then R5[7] was set, so we're 
                ;adding the bit that would be lost if R5 
                ;shifts left

NOADD   ADD R5, R5, R5  ;Shift NUMLOW to the left once

    NOT R7, R3      ;Subtract NUMHIGH and DVR. If 0 or positive 
    ADD R7, R7, #1  ;then subtract and increment NUMLOW
    ADD R7, R4, R7
    BRN DECCNT      ;If negative then DVR is greater than NUMHIGH,
                ;go straight to decrement the loop count 
    ADD R4, R7, #0
    ADD R5, R5, #1

DECCNT  ADD R2, R2, #-1
    BRP ITER        ;If count is positive, go back to ITER

    STR R5, R6, #0  ;Store NUMLOW for the Quotient Address
    ADD R6, R6, #1  ;Increment to store in the next one
    STR R4, R6, #0  ;Store NUMHIGH for the Remainder Address

    LD  R1, DIV1
    LD  R2, DIV2
    LD  R3, DIV3
    LD  R4, DIV4
    LD  R5, DIV5
    LD  R6, DIV6
    LD  R7, DIV7

    RET         ;Return
;Data
ARGLIST .BLKW   5       ;Here we will put the LOWNUM, HIGHNUM,
                ;DVR, QUO, and REM
M8BIT   .FILL   x0080               
DIV1    .BLKW   1
DIV2    .BLKW   1
DIV3    .BLKW   1
DIV4    .BLKW   1
DIV5    .BLKW   1
DIV6    .BLKW   1
DIV7    .BLKW   1

    .END

I think the algorithm should be working, but as I run it on the LC3, I get the following error:

A trap was executed with an illegal vector number

And the processor is halted. For some reason, I noticed that when I checked my code in the LC3 it gets erased right after this line:

ADD R0, R2, #0

Which would be right before branching to call the DIVIDE subroutine again.

Why would this be happening? As far as I am able to tell, I have been saving and restoring the registers properly (last time I had a similar issue to this, and someone pointed out I was saving to the same .BLKW which caused an error), but the error persists. Please let me know if anyone has any idea of what could be malfunctioning, or if there's any more information you need. Thank you for any help in advance


Solution

  • Solved. There were two main errors:

    1) There wasn't a need to use a mask. If anything, the mask was wrong in itself, as the registers hold 16 bit values. If I had wanted to use a mask, a more appropriate value would have been x8000, which still cannot be used as this is actually the opcode for RTI. All I had to do was make sure to get R5, the register with the low numerator, into the branch calculation. If it was negative, then the high bit obviously is a 1!

    2) This is what was breaking the program. Note that on the PRINT function, I was using a LEA with R7. This means I was holding this register responsible for pointing to the arguments. But some lines after this I call a JSR:

    JSR DIVIDE

    I failed to realize that internally when such a jump happens the PC value needed to be able to return to normal program execution is stored inside a register. Where? R7. So even if on the beginning and end of DIVIDE I had an ST and an LD to restore the value of R7, upon returning to PRINT it would have been the address of the next instruction, not the pointer to ARGLIST. All it took for the program to run properly was to add a simple LEA, add 3 to point to the quotient and remainder, and modify the manipulation of the pointer a bit farther down to store the updated low numerator. In all, the bit of code right after the DIVIDE call would look like this:

    SDIV
        JSR DIVIDE
    
        LEA R7, ARGLIST
        ADD R7, R7, #3
    
        LDR R2, R7, #0  ;Since the addresses should now be ready, 
                        ;R7 would point to quotient, passed to R2
        LDR R3, R7, #1  ;Pass remainder to R3
    
        ADD R3, R3, R4
        LDR R5, R3, #0
        ADD R6, R6, #-1 ;Decrement to get to the next one
        STR R5, R6, #0  ;Store number into buffer
    
        LEA R7, ARGLIST ;Go back to ARGLIST to store new numerator
        STR R2, R7, #0
    
        ADD R0, R2, #0  ;Pass quotient into R0
        BRP SDIV        ;If number is at least 1, then divide again
    
        ADD R0, R6, #0  ;If we got here, then we should have the converted number
        TRAP    x22     ;Last but not least we display