1. ホーム
  2. assembly

[解決済み] LC3で不正なベクター番号でトラップが実行されました。

2022-03-14 16:48:53

質問

LC3で与えられた数値を異なる塩基に変換しなければならない課題をやっています。そのためには割り算をする必要があるのですが、これと同じことをする課題があったのですが、今回の割り算のアルゴリズムが違います。

この課題では、拡張除算を使うことが期待されています。基本的なアルゴリズムは

32ビットの数値が与えられたとき、最初の16ビットをあるメモリアドレスに格納し(上位分子と呼ぶ)、最後の16ビット(下位分子)を別のメモリアドレスに格納し、さらに16ビットの数値である除数を別のメモリアドレスに格納します。つまり、サブルーチンでは、メモリから数値を別のレジスタに取り込み、以下のようにアルゴリズムが動作することになります。

  • 高分子と低分子のレジスタを一旦左にシフトします(低分子の高ビット(左端)が1であれば、高分子の低ビットに渡されます(シフトしているのであらかじめ0になっているはず)。
  • 高分子は除数と比較されます。以上であれば、そこから除数を引き、低分子の低ビットを設定します。等しくなければ、何もしません(シフトを繰り返すうちに、最終的には大きくなります)。
  • シフトとチェックを16回繰り返し、分子の下位ビット(右端)をセットする
  • 最後に、分子の高い方のレジスタに除算の余りが、分子の低い方のレジスタに商が格納されます。

簡単のために、講師は高い分子に0を使わせています。そこで、このアルゴリズムを実現するために、次のような手順を思いつきました。

  • まず、ARGLISTという名前の5つの.BLKWを宣言し、そこにすべての引数が格納されます。
  • PRINTサブルーチンです。次に、ARGLISTのアドレスをレジスタにロードし、そこに低分子を格納し、インクリメントして高分子(0)と除数を格納します; 次に、BUFFERとDIGITSのポインタにロードして、指定したベースに変換した結果を格納します。そして、DIVIDEサブルーチンが呼ばれます。
  • DIVIDEサブルーチン。ARGLISTのアドレスをレジスタにロードし(代入命令でR6)、必要なレジスタにロード、インクリメントして値を用意し、分子をシフトするループを16回行い(分子の下位ビットを得るためにx0080でマスクも行う)、DVRが上位分子以下となるたびに減算し、を繰り返しています。そして商と余りがR6が指す場所に格納され(もちろん次を得るためにインクリメントされる)、リターンします。
  • PRINTに戻ると、R7が指す値からR2とR3(課題2からできるだけ多くのコードを再利用するために同じにしておきます)にロードし、それはまた今入力されるべきARGLISTを指すので、それから課題2と同じようにサブルーチンを続けます。
  • バッファへの保存がすべて完了したら、ARGLIST の先頭に戻って低い方の分子を更新し、次の反復(次の除算)に備えるために商を指します。

以下は、私がこれまでに作成したコードの全容です。

    .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

アルゴリズムはうまくいっているはずなのですが、LC3で実行すると、次のようなエラーが発生します。

A trap was executed with an illegal vector number

そして、プロセッサは停止しています。なぜか、LC3で自分のコードをチェックすると、この行のすぐ後で消されてしまうことに気づきました。

ADD R0, R2, #0

これは、DIVIDEサブルーチンを再度呼び出すために分岐する直前の状態です。

なぜこのようなことが起こるのでしょうか?私が知る限り、レジスタの保存と復元はきちんと行っているのですが(前回、これと似たような問題が発生し、同じ.BLKWに保存していたためにエラーが発生したと指摘されました)、このエラーは続いているのです。どなたか誤動作に心当たりのある方、また必要な情報がありましたら教えてください。よろしくお願いします。

解決方法は?

解決しました。主なエラーは2つでした。

1) マスクを使用する必要がなかった。どちらかというと、レジスタが16ビットの値を保持するため、マスク自体が間違っていた。もしマスクを使いたいのであれば、より適切な値は次のようになります。 x8000 のオペコードなので、やはり使えません。 RTI . あとは、分子の少ないレジスタであるR5を分岐計算に入れるようにすればよかったのです。もし、それが negative ということは、上位ビットは明らかに「1」です。

2) これがプログラムを壊していた原因です。注意すべきは PRINT 関数を使っていましたが LEAR7 . これは、このレジスタが引数を指し示す責任を負っていたことを意味します。しかし、この後の数行で私は JSR :

JSR DIVIDE

このようなジャンプが発生した場合、通常のプログラム実行に戻るために必要なPCの値は、内部でレジスタに格納されていることを認識していませんでした。どこですか?R7. ということは、たとえ DIVIDE があったのですが STLD に戻ると、R7の値が復元されます。 PRINT であったはずです。 次の命令のアドレス へのポインタではなく ARGLIST . このプログラムを正しく実行するために必要なことは、単純な LEA を追加し、商と余りを指すように3を追加し、ポインタの操作を少し下に修正して、更新された低い分子を格納するようにしました。要するに DIVIDE の呼び出しは、次のようになります。

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