ICS计算系统概论LC3汇编实验Lab5—中断递归解决汉诺塔问题

Posted 之墨_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ICS计算系统概论LC3汇编实验Lab5—中断递归解决汉诺塔问题相关的知识,希望对你有一定的参考价值。

Lab

Purpose

  1. 完成用户程序的编写。
  2. 编写下面描述的键盘中断服务例程。

condition:
用户程序:
汉诺塔的参数,记录为N,将用xFFFF初始化并存储在X3FFF内存中。 您的用户程序从 x3000 开始,将持续(即无限循环)打印您的学生证,如下所示: PB12345678 PB12345678 PB12345678 PB12345678 PB12345678 PB12345678 ······ 并等待参数 N 成为有效值。当 N 成为有效值时,程序停止等待并调用 HANOI 子例程来解决问题。HANOI 子例程解决问题后,您的结果应以十进制格式显示在控制台上。 为了确保屏幕上的输出不会太快而无法用肉眼看到,用户程序应包含一段代码,该代码将在屏幕上输出每个单词后从2500(或任何其他数字)开始倒计时
终端服务例程:
从 x1000 开始的键盘中断服务例程将检查键入的键以查看它是否为十进制数字。如果键入的字符不是十进制数字,中断服务例程将从屏幕上的新行开始打印“<输入字符>不是十进制数字”。然后,服务例程会将换行符 (x0A) 打印到屏幕上,最后以 RTI 终止。 如果键入的字符是十进制数字,则中断服务例程将从屏幕上的新行开始打印“是十进制数字”。然后,您应该将此十进制数保存到x3FFF。 例如,如果输入键为“K”,则中断服务例程将打印:K 不是十进制数字。如果输入键为“4”,则中断服务例程将打印:4 是十进制数字。并将 x3FFF 的值更改为 4。 然后,服务例程会将换行符 (x0A) 打印到屏幕上,最后以 RTI 终止。 提示:不要忘记保存和恢复中断服务例程中使用的任何寄存器。

several examples:

Principles

Key issues:

The user program
用户程序完成循环输出学号的功能,只要程序没有被中断、没有结束那么就不停的输出学号,并且为了让输出更慢一些,需要加上一个数字循环减来控制输出速度

The keyboard interrupt service routine

  • 中断程序里面完成的功能有:
    1. 汉诺塔的计算:使用递归算法解决
    2. 输入数字的判断:数字相减取acsii码
    3. 十进制转换acsii码:最多可到3位数,因此对个十百位都需要转换再依次输出

Procedure

实验代码使用框架完成,中断向量表的控制在模板中已有

        .ORIG x800
        ; (1) Initialize interrupt vector table.
        LD R0, VEC
        LD R1, ISR
        STR R1, R0, #0

        ; (2) Set bit 14 of KBSR.
        LDI R0, KBSR
        LD R1, MASK
        NOT R1, R1
        AND R0, R0, R1
        NOT R1, R1
        ADD R0, R0, R1
        STI R0, KBSR

        ; (3) Set up system stack to enter user space.
        LD R0, PSR
        ADD R6, R6, #-1
        STR R0, R6, #0
        LD R0, PC
        ADD R6, R6, #-1
        STR R0, R6, #0
        ; Enter user space.
        RTI
        
VEC     .FILL x0180
ISR     .FILL x1000
KBSR    .FILL xFE00
MASK    .FILL x4000
PSR     .FILL x8002
PC      .FILL x3000
        .END

用户程序如下,使用LEA取字符串至寄存器中,再使用PUTS输出,延迟输出用一个数字循环减实现

        .ORIG x3000
        
        ; *** Begin user program code here ***
LOOP	LEA R0, STUID		; 打印字符串1
		PUTS ;TRAP x22
		AND R0, R0, #0
		LD R0, DELAY
SLEEP	ADD R0, R0, #-1
		BRp SLEEP
		
		BRnzp LOOP


DELAY	.FILL #25000
STUID	.STRINGZ "JL22110003 "
        ; *** End user program code here ***
        .END

中断服务例程如下,首先是获取输入的数字,使用GETC获取,ASCIIZ存储’0’的acsii码负值,ASCIIN存储’9’的acsii码负值,将获取的值与之相减,正确的数字ascii码应该在’0’-'9’之间,如果不符合则跳转至ERROR,输出其不是十进制数并执行RTI返回用户程序,正确执行MAIN即汉诺塔递归部分

  ; *** Begin interrupt service routine code here ***
LD R6,STACK;压栈
LD R0, NEWLINE
OUT
;输入汉诺塔阶数
GETC ;input n
;从键盘读入n
OUT
ADD R5, R0, #0
LD  R3, ASCIIZ
ADD R0, R0, R3    ;n-'0'
ADD R4, R0, #0
BRn ERROR
LD  R3, ASCIIN     
ADD R0, R5, R3    ;n-'9'
BRp ERROR
LEA R0, OUTPUT2
PUTS
LD R0, NEWLINE
OUT
ADD R0, R4, #0
BR MAIN
ERROR  LEA R0, OUTPUT1
       PUTS
FINISH LD R0, NEWLINE
       OUT
       RTI

以下是调用HANOI递归程序以及处理计算结果的部分代码,R6是栈指针,首先将正确处理后的n即R0压栈,作为HANOI的参数,经过HANOI递归后,结果会存储在R1中,随后输出字符串以及结果的acsii码,个十百位分别处理完后直接输出

MAIN ADD R6,R6,#-1
STR R0,R6,#0
JSR HANOI
;output
LEA R0,OUTPUT3
PUTS
LD R0, OFF
LD R2, NEGH
LOOPH   ADD R1, R2, R1
        BRn ENDH
        ADD R0, R0, #1
        BRnzp LOOPH
ENDH    OUT     ; 百位
        LD R3, POSH
        ADD R1, R1, R3
        LD R0, OFF
        LD R2, NEGS
LOOPS  ADD R1, R2, R1
        BRn ENDS
        ADD R0, R0, #1
        BRnzp LOOPS
ENDS    OUT     ; 十位
        ADD R1, R1, #10
        LD R0, OFF
        ADD R0, R0, R1
        OUT     ; 个位
LEA R0,OUTPUT4
PUTS
HALT

以下是汉诺塔的递归部分程序,使用栈完成递归功能,计算递推式,联系到书本上斐波那契的递归计算过程,此过程与之十分相似,首先将调用者的返回地址压栈,再将参数压栈,最后压入一个参数用来检验n = 0,1时的基本情况,如果计算到了基本情况,说明递归已到最大深度,此时跳转至DONE,否则还需要继续计算下一层n-1,使用R1寄存器保存H(n)计算结果,每次计算都是对其加上一定值,当递归完成后得到的结果就是保存在R1中的最终结果
H ( 0 ) = 0 H ( n ) = 2 H ( n − 1 ) + 1 H(0) = 0\\\\H(n) = 2H(n-1) + 1 H(0)=0H(n)=2H(n1)+1

HANOI ADD R6, R6, #-1
STR R7, R6, #0 ; Push R7, the return linkage
ADD R6, R6, #-1
STR R0, R6, #0 ; Push R0, the value of n
ADD R6, R6, #-1
STR R2, R6, #0 ; Push R2, which is needed in the subroutine
; Check for base case
AND R2, R0, #-2
BRnp SKIP ; H(n)=n if n=0,1
ADD R1, R0, #0 ; R0=n is the answer
BRnzp DONE
; Not a base case, do the recursion
SKIP ADD R0, R0, #-1  ; R0 = n-1
JSR HANOI ; R1 = H(n-1)
ADD R1, R1, R1; R1 = 2H(n-1)
ADD R1, R1, #1 ; R1 = 2H(n-1) + 1
; Restore registers and return
DONE LDR R2, R6, #0

ADD R6, R6, #1
LDR R0, R6, #0
ADD R6, R6, #1
LDR R7, R6, #0
ADD R6, R6, #1
RET

;
ASCIIZ .FILL xFFD0
ASCIIN .FILL xFFC7 
OFF .FILL   x0030
STACK .FILL x6000
NEWLINE .FILL   x000A
OUTPUT1 .STRINGZ " is not a decimal digit. "
OUTPUT2 .STRINGZ " is a decimal digit. "
OUTPUT3 .STRINGZ "Tower of hanoi needs "
OUTPUT4 .STRINGZ " moves."
NEGH  .FILL	xFF9C
POSH  .FILL	x0064
NEGS   .FILL	xFFF6

Result

Debug:

过程中遇到的主要问题有:
输出的结果不正确,有时出现输出乱码的情况,因此想到应该是acsii码处理有问题,所以检查发现在键盘读入时有问题

Test:
此处原本没有使用R5,R6寄存器保存R0的值,这样就会导致R0最后保存的值是n-'0’处的值,从而出现错误,因此我在这里使用R5保存输入的n的初始的acsii码值,R4保存n的实际十进制值,R5用于后续判断是不是字母,而R4作为后面的HANOI的参数进行计算

在本地 LC3Tool \\textLC3Tool LC3Tool调试完毕后,将代码整理如下:

; Unfortunately we have not YET installed Windows or Linux on the LC-3,
; so we are going to have to write some operating system code to enable
; keyboard interrupts. The OS code does three things:
;
;    (1) Initializes the interrupt vector table with the starting
;        address of the interrupt service routine. The keyboard
;        interrupt vector is x80 and the interrupt vector table begins
;        at memory location x0100. The keyboard interrupt service routine
;        begins at x1000. Therefore, we must initialize memory location
;        x0180 with the value x1000.
;    (2) Sets bit 14 of the KBSR to enable interrupts.
;    (3) Pushes a PSR and PC to the system stack so that it can jump
;        to the user program at x3000 using an RTI instruction.

        .ORIG x800
        ; (1) Initialize interrupt vector table.
        LD R0, VEC
        LD R1, ISR
        STR R1, R0, #0

        ; (2) Set bit 14 of KBSR.
        LDI R0, KBSR
        LD R1, MASK
        NOT R1, R1
        AND R0, R0, R1
        NOT R1, R1
        ADD R0, R0, R1
        STI R0, KBSR

        ; (3) Set up system stack to enter user space.
        LD R0, PSR
        ADD R6, R6, #-1
        STR R0, R6, #0
        LD R0, PC
        ADD R6, R6, #-1
        STR R0, R6, #0
        ; Enter user space.
        RTI
        
VEC     .FILL x0180
ISR     .FILL x1000
KBSR    .FILL xFE00
MASK    .FILL x4000
PSR     .FILL x8002
PC      .FILL x3000
        .END

        .ORIG x3000
        
        ; *** Begin user program code here ***
LOOP	LEA R0, STUID		; 打印字符串1
		PUTS
		AND R0, R0, #0
		LD R0, DELAY
SLEEP	ADD R0, R0, #-1
		BRp SLEEP
		
		BRnzp LOOP


DELAY	.FILL #25000
STUID	.STRINGZ "JL22110003 "
        ; *** End user program code here ***
        .END

        .ORIG x3FFF
        ; *** Begin honoi data here ***
HONOI_N .FILL xFFFF
        ; *** End honoi data here ***
        .END

        .ORIG x1000
        ; *** Begin interrupt service routine code here ***
LD R6,STACK;压栈

LD R0, NEWLINE
OUT
;输入汉诺塔阶数

GETC ;input n
;从键盘读入n
OUT
ADD R5, R0, #0
LD  R3, ASCIIZ
ADD R0, R0, R3    ;n-'0'
ADD R4, R0, #0
BRn ERROR
LD  R3, ASCIIN     
ADD R0, R5, R3    ;n-'9'
BRp ERROR
LEA R0, OUTPUT2
PUTS
LD R0, NEWLINE
OUT
ADD R0, R4, #0
BR MAIN
ERROR  LEA R0, OUTPUT1
       PUTS
FINISH LD R0, NEWLINE
       OUT
       RTI
       
MAIN ADD R6,R6,#-1
STR R0,R6,#0
JSR HANOI
;output
LEA R0,OUTPUT3
PUTS
LD R0, OFF
LD R2, NEGH
LOOPH   ADD R1, R2, R1
        BRn ENDH
        ADD R0, R0, #1
        BRnzp LOOPH
ENDH    OUT     ; 百位
        LD R3, POSH
        ADD R1, R1, R3
        LD R0, OFF
        LD R2, NEGS
LOOPS  ADD R1, R2, R1
        BRn ENDS
        ADD R0, R0, #1
        BRnzp LOOPS
ENDS    OUT     ; 十位
        ADD R1, R1, #10
        LD R0, OFF
        ADD R0, R0, R1
        OUT     ; 个位
LEA R0,OUTPUT4
PUTS
HALT

HANOI ADD R6, R6, #-1
STR R7, R6, #0 ; Push R7, the return linkage
ADD R6, R6, #-1
STR R0, R6, #0 ; Push R0, the value of n
ADD R6, R6, #-1
STR R2, R6, #0 ; Push R2, which is needed in the subroutine
; Check for base case
AND R2, R0, #-2
BRnp SKIP ; H(n)=n if n=0,1
ADD R1, R0, #0 ; R0=n is the answer
BRnzp DONE
; Not a base case, do the recursion
SKIP ADD R0, R0, #-1  ; R0 = n-1
JSR HANOI ; R1 = H(n-1)
ADD R1, R1, R1; R1 = 2H(n-1)
ADD R1, R1, #1 ; R1 = 2H(n-1) + 1
; Restore registers and return
DONE LDR R2, R6, #0

ADD R6, R6, #1
LDR R0, R6, #0
ADD R6, R6, #1
LDR R7, R6, #0
ADD R6, R6, #1
RET

;
ASCIIZ .FILL xFFD0
ASCIIN .FILL xFFC7 
OFF .FILL   x0030
STACK .FILL x6000
NEWLINE .FILL   x000A
OUTPUT1 .STRINGZ " is not a decimal digit. "
OUTPUT2 .STRINGZ " is a decimal digit. "
OUTPUT3 .STRINGZ "Tower of hanoi needs "
OUTPUT4 .STRINGZ " moves."
NEGH  .FILL	xFF9C
POSH  .FILL	x0064
NEGS   .FILL	xFFF6
; *** End interrupt service routine code here ***
        .END
      


输出结果如下,正常运行,输出正确。

测试用例主要对大小写字母都进行测试,而数字则对0-9均测试,输出均正确

以上是关于ICS计算系统概论LC3汇编实验Lab5—中断递归解决汉诺塔问题的主要内容,如果未能解决你的问题,请参考以下文章

ICS计算系统概论LC3汇编实验Lab4—排序和计数 Sort and Count

ICS计算系统概论LC3汇编实验Lab4—排序和计数 Sort and Count

ICS计算系统概论实验3—LC3汇编代码实现最长重复子字符串Longest-duplicate-substring

ICS计算系统概论实验—Lab2-LC3实现斐波那契数列(变型式)

数字设计

计算系统概论实验Lab 6——C重写Lab1-5