栈
本节介绍栈的基本结构及其三种用途:
1)中断驱动I/O; 2)一种算数运算机制; 3)二进制补码与ASCII字符串之间的转换算法
栈的基本结构
内存中的实现
常见方式为一段连续内存空间和一个寄存器(栈指针)组成。栈指针,是一个寄存器,它的内容是一个地址值,始终指向栈的顶部(最近被压入的元素)。 压入:每压入一个值到栈中,栈指针先递减,然后将数值存入它的地址位置。
PUSH ADD R6,R6,#-1 STR R0,R6,#0
R6为栈指针,将R0内容存入R6对应内容的值 弹出:每弹出一个元素,需要先通过栈指针提供的地址读取该数值,然后递增栈指针。
POP LDR R0,R6,#0 ADD R6,R6,#1
R6为栈指针,取出R6对应内容的值存入R0 下溢出:栈内元素不足以弹出。常用方法是,子程序将执行成功或失败的信息记录在某个寄存器中。R5为0标识成功,否则失败
POP LD R1,EMPTY
ADD R2,R6,R1
BRz Failure
LDR R0,R6,#0
ADD R6,R6,#1
AND R5,R5,#0
RET
Failure AND R5,R5,#0
ADD R5,R5,#1
RET
EMPTY .FILL xC000 ; EMPTY <-- -x4000
上溢出:占空间已全部被占用。同POP程序一样,记录程序执行状态,并返回调用程序再处理。
PUSH LD R1,MAX
ADD R2,R6,R1
BRz Failure
ADD R6,R6,#-1
STR R0,R6,#0
AND R5,R5,#0
RET
Failure AND R5,R5,#0
ADD R5,R5,#1
RET
MAX .FILL xC005 ; MAX <-- -x3FFB
小结:通过子程序POP和PUSH,我们可以将5个内存位置x3FFF~x3FFB,设计为一个栈。如果压入程序,只需,将数据存入R0,执行JSR PUSH,如果弹出程序,只需JSR POP。如果希望改变栈大小,修改BASE和MAX的值即可 最终代码如下
POP ST R2,Save2
ST R1,Save1
LD R1,BASE
ADD R1,R1,#-1
ADD R2,R6,R1
BRz fail_exit
LDR R0,R6,#0
ADD R6,R6,#1
BRnzp success_exit
PUSH ST R2,Save2
ST R1,Save1
LD R1,MAX
ADD R2,R6,R1
BRz fail_exit
ADD R6,R6,#-1
STR R0,R6,#0
success_exitLD R1,Save1
LD R2,Save2
AND R5,R5,#0
RET
fail_exit LD R1,Save1
LD R2,Save2
AND R5,R5,#0
ADD R5,R5,#1
RET
BASE .FILL xC001
MAX .FILL xC005
Save1 .FILL x0000
Save2 .FILL x0000
中断驱动I/O(第二部分)
I/O数据传输的管理能力,传输机制
中断服务程序的启动 中断服务程序的执行 中断服务程序的返回
启动和执行
当一个I/O设备的优先级高于当前执行程序时,他在发出INT信号时将启动中断。从处理器角度,每执行完一条指令之后,都将检查是否存在INT。如果没有,则继续下一条指令的执行,如果检测到INT,则暂停下一条指令的读取 此时,开始中断执行的准备工作,1)保存当前执行程序的状态; 2)装载中断服务程序的工作状态,开始中断请求的服务。
程序状态
程序运行所涉及资源的快照。包括该程序的内存空间和通用寄存器的内容,以及两个重要的寄存器:PC和PSR寄存器(处理器状态寄存器)。
PSR[15]代表的是该程序的运行模式,特权模式或非特权模式。
PSR[10:8]代表正在执行程序的优先级别(priority level),PL0~PL7 PSR[2:0]代表条件码,PSR[2]=N,PSR[1]=Z,PSR[0]=P
被中断程序的状态保存
中断启动的第一个任务就是保存正在运行程序的状态,以使得I/O设备服务完成后,程序可以继续运行。
保存PC:可获取中断返回后的下一条待执行指令 保存PSR:条件码后续程序可能依赖它;优先级提供被中断程序与其他程序相比的迫切程度;特权级别表明了被中断程序可以访问的资源范围 LC-3将这些内容保存在一个特殊的栈空间中,称为"超级用户栈",该栈空间进攻特权模式下的程序使用。所有程序都通过R6(栈指针)访问栈空间,超级用户栈也不例外。因此,内部寄存器Saved.SSP和Save.USP分别用于保护两个栈指针的内容。 即在中断服务程序开始之前,R6已装入超级用户栈指针的内容。而PC寄存器和被中断程序的PSR寄存器内容将被压入超级用户栈
中断服务程序的状态装入
大多数处理器采用"矢量中断"的工作方式。在中断情况下,I/O设备在发出中断时,将向处理器传递一个8-bit的矢量值。 在多个设备同时请求中断处理的情况下,优先级最高被选中并传递给处理器,称为INTV。 INTV拓展为16-bit的地址,对应"中断矢量表"的某个表项地址。中断矢量表的内存位置是x0100~x01FF,每个表项包含一个中断服务程序的起始地址。 处理器将扩展后中断矢量INTV的地址内容装入PC PSR寄存器装入过程:PSR[2:0]内容初始化为0;PSR[15]设置为0,特权模式;PSR[10:8]设置为中断请求者(设备)的优先级别。 至此,完成中断服务的启动阶段,之后可以运行中断服务程序。
中断服务
此时,PC包含的是中断服务程序的起始地址,所以下一个开始执行的就是中断服务程序,即I/O设备的请求开始被服务。
中断返回
中断服务程序的最后,是中断返回指令RTI。当处理器遇到RTI指令时,意味着I/O设备的请求已经完成。
RTI(1000):将PSR和PC的内容弹出超级用户栈,然后将它们填入处理器中正确的位置。
基于栈的算术运算
“栈式机"或"零地址机器”:硬件默认时将栈顶部的两个元素当做源操作数,并将它们弹出,送给ALU,之后的相加结果又压回栈顶。 (ADD) 在栈式机中,程序员只需告诉计算机执行ADD,而微结构(microarchitecture)将负责所有的具体操作 加法运算
OpADD JSR POP ;first operand
ADD R5,R5,#0 ;successful?
BRp Exit ;if unsuccessful
ADD R1,R0,#0 ;make room for next
JSR POP ;next operand
ADD R5,R5,#0 ;successful?
BRp Restore1 ;Resotre pointer
ADD R0,R0,R1 ;Add
JSR RangeCheck;chenk result
BRp Restore2 ;Restore pointer
JSR PUSH ;push on the stack
RET ;next task
Restore2 ADD R6,R6,#-1
Restore1 ADD R6,R6,#-1
Exit RET
RangeCheck LD R5,Neg999
ADD R4,R0,R5
BRp BadRange
LD R5,Pos999
ADD R4,R0,R5
BRn BadRange
AND R5,R5,#0
RET
BadRange ST R7,Save
LEA R0,RangeErrorMsg
TRAP x22
LD R7,Save
AND R5,R5,#0
ADD R5,R5,#1
RET
Neg999 .FILL #-999
Pos999 .FILL #999
Save .FILL x0000
RangeErrorMsg .FILL x000A
.STRINGZ "Number is illegal"
OpMult AND R3,R3,#0
JSR POP
ADD R5,R5,#0
BRp Exit
ADD R1,R0,#0
JSR POP
ADD R5,R5,#0
BRp Restore1
ADD R2,R0,#0
BRzp PosMultiplier
ADD R3,R3,#1
NOT R2,R2
ADD R2,R2,#1
PosMultiplier AND R0,R0,#0
ADD R2,R2,#0
BRz PushMult
MultLoop ADD R0,R0,R1
ADD R2,R2,#-1
BRp MultLoop
JSR RangeCheck
ADD R5,R5,#0
BRp Restore2
ADD R3,R3,#0
BRz PushMult
NOT R0,R0
ADD R0,R0,#1
PushMult JSR PUSH
RET
Restore2 ADD R6,R6,#-1
Restore1 ADD R6,R6,#-1
Exit RET
OpNeg JSR POP
ADD R5,R5,#0
BRp Exit
NOT R0,R0
ADD R0,R0,#1
JSR PUSH
Exit RET
数据类型转换
ASCII->二进制
按ASCII字符串方式,将数组存放在LC-3内存中标识ASCIIBUFF起始的三个连续地址中,R1记录起始内存中的有效位数。 依次处理每位数字,对于每位数字,只取其二进制值的最低4位,然后以该值索引对应的数值查找表(不同的数位有不同的表),每个表项的内容是10个数字对应的数值。然后,将每位数值累加入R0。
ASCIItoBinary AND R0,R0,#0 ;result
ADD R1,R1,#0 ;test digits
BRz DoneAtoB ;no digits
LD R3,NegASCIIOffset ;-x0030
LEA R2,ASCIIBUFF
ADD R2,R2,R1
ADD R2,R2,#-1
LDR R4,R2,#0 ;R4<--ones digit
ADD R4,R4,R3
ADD R0,R0,R4
ADD R1,R1,#-1
BRz DoneAtoB
ADD R2,R2,#-1
LDR R4,R2,#0 ;R4<--tens digit
ADD R4,R4,R3
LEA R5,LookUp10
ADD R5,R5,R4; R5 pointer
LDR R4,R5,#0
ADD R0,R0,R4
ADD R1,R1,#-1
BRz DoneAtoB
ADD R2,R2,#-1
LDR R4,R2,#0 ;R4<--hudreds
ADD R4,R4,R3
LEA R5,LookUp100
ADD R5,R5,R4; R5 pointer
LDR R4,R5,#0
ADD R0,R0,R4
DoneAtoB RET
NegASCIIOffset .FILL xFFD0
ASCIIBUFF .BLKW 4
LookUp10 .FILL #0
.FILL #10
.FILL #20
.FILL #30
.FILL #40
.FILL #50
.FILL #60
.FILL #70
.FILL #80
.FILL #90
LookUp100 .FILL #0
.FILL #100
.FILL #200
.FILL #300
.FILL #400
.FILL #500
.FILL #600
.FILL #700
.FILL #800
.FILL #900
二进制->ASCII
R0数值范围为-999~+999,存放带转换的补码整数,转换后的ASCII字符串存放在ASCIIBUFF起始的4个连续内存位置
BinarytoASCII LEA R1,ASCIIBUFF
ADD R0,R0,#0
BRn NegSign
LD R2,ASCIIplus
STR R2,R1,#0
BRnzp Begin100
NegSign LD R2,ASCIIminus
STR R2,R1,#0
NOT R0,R0
ADD R0,R0,#1
Begin100 LD R2,ASCIIoffset
LD R3,Neg100
Loop100 ADD R0,R0,R3
BRn End100
ADD R2,R2,#1
BRnzp Loop100
End100 STR R2,R1,#1
LD R3,Pos100
ADD R0,R0,R3
LD R2,ASCIIoffset
Begin10 LD R3,Neg10
Loop10 ADD R0,R0,R3
BRn End10
ADD R2,R2,#1
BRnzp Loop10
End10 STR R2,R1,#2
ADD R0,R0,#10
Begin1 LD R2,ASCIIoffset
ADD R2,R2,R0
STR R2,R1,#3
RET
ASCIIplus .FILL x002B
ASCIImius .FILL x002D
ASCIIoffset .FILL x0030
Neg100 .FILL xFF9C
Pos100 .FILL x0064
Neg10 .FILL xFFF6