根据给出的指令集架构ISA编程实现一个汇编程序,能够使用汇编程序将由给定指令集和指令格式编写的汇编源程序翻译成目标机器程序,得到正确结果。
其中汇编源程序的功能要求为:求1+2+3+„+100,并输出运算结果。
源文件如下:
#求1+2+3+„+100,并输出运算结果
#先求出A的值,即5050
LOADI A 0 # A初始值为0
LOADI B 1 # B初始值为1
LOADI C 100 # B的上限即C为100
LOADI D 10 # 除数D为10
LOADI E 30 # E的值为30 again1: ADD A A B # A = A + B
ADDI B 1 # B的值加1
LTE B C # 比较运算B的值小于等于100
CJMP again1 # 如果比较为真则转至标号again1,否则往下执行
#现在输出A的值
loop1: DIV E A D # E = A / D,E中为C除以10的商,整数除,切掉C的个位数字
MUL F D E # F = D * E
SUB F A F # F = A - F,得到C除以10的余数
PUSH F # 将余数F入栈
ADDI G 1 # G用来为余数压栈次数进行计数
ADD A E Z # 将商E存入C ADD
LT Z A # 关系运算 Z < A
CJMP loop1 # 为真表示商大于0,转至loop1,继续求余数
loop2: POP A # 余数出栈,存入C
ADDI A 48 # A = A + 48,将数字转为数字字符
OUT A 15 # 输出C中的数字字符
SUBI G 1 # 余数计数器减1
LT Z G # 关系运算符 Z < G
CJMP loop2 # 为真表明入栈的余数还未取完,转loop2继续取余数并转换为数字字符输出
HLT # 结束
32条指令以及伪指令和它们的功能如下:
(1) 停机指令:HLT
功能:终止程序运行。
(2) 无条件转移指令:JMP label
功能:将控制转移至标号label处,执行标号label后的指令。
(3) 比较运算转移指令:CJMP label
功能:如果程序状态字中比较标志位c的值为1(即关系运算的结果为真),则将控制转移至标号label处,执行标号label后的指令;否则,顺序往下执行。
(4) 溢出转移指令:OJMP
功能:如果程序状态字中比较标志位o的值为1(即算术运算的结果发生溢出),则将控制转移至标号label处,执行标号label后的指令;否则,顺序往下执行。
(5) 调用子程序指令:CALL label
功能:将通用寄存器A~G、程序状态字PSW、程序计数器PC中的值保存到ES,然后调用以标号label开始的子程序,将控制转移至标号label处,执行标
(6) 子程序返回指令:RET
功能:将ES中保存的通用寄存器A~Z、程序状态字PSW和程序字数器PC的值恢复,控制转移到子程序被调用的地方,执行调用指令的下一条指令。
(7) 入栈指令:PUSH reg0
功能:将通用寄存器reg0的值压入堆栈SS,reg0可以是A~G和Z八个通用寄存器之一。
(8) 出栈指令:POP reg0
功能:从堆栈SS中将数据出栈到寄存器reg0,reg0可以是A~G七个通用寄存器之一,但不能是通用寄存器Z。
(9) 取字节数据指令:LOADB reg0 symbol
功能:从字节数据或字节数据块symbol中取一个字节的数据存入寄存器reg0,所取的字节数据在数据块symbol中的位置由寄存器G的值决定。用C的语法可将此指令的功能描述为:
reg0 = symbol[G]
例如,假设用伪指令定义了以下字节数据块num:
BYTE num[10] = {5,3,2,8,6,9,1,7,4,0}
如果要将字节数据块num中第5个单元的值(即下标为4的元素)取到寄存器C,指令如下:
LOADI G 5
LOADB C num
后面的指令LOADW、STOREB和STOREW在操作上与此指令类似。
(10) 取双字节数据指令:LOADW reg0 symbol
功能:从双字节数据或双字节数据块symbol中取一个双字节的数据存入寄存器reg0,所取的双字节数据在数据块symbol中的位置由寄存器G的值决定。
(11) 存字节数据指令:STOREB reg0 symbol
功能:将寄存器reg0的值存入字节数据或字节数据块symbol中的某个单元,存入单元的位置由寄存器G的值决定。用C的语法可将此指令的功能描述为:
symbol[G] = reg0
(12) 存双字节数据指令:STOREW reg0 symbol
功能:将寄存器reg0的值存入双字节数据或双字节数据块symbol中的某个单元,存入单元的位置由寄存器G的值决定。
(13) 取立即数指令:LOADI reg0 immediate
功能:将指令中的立即数immediate存入寄存器reg0。立即数被当作16位
有符号数,超出16位的高位部分被截掉。例如:
LOADI B 65535
寄存器B的值为-1。
LOADI B 65537
寄存器B的值为1。
(14) 空操作指令:NOP
功能:不执行任何操作,但耗用一个指令执行周期。
(15) 控制台输入指令:IN reg0 0
功能:从输入端口(即键盘输入缓冲区)取一个字符数据,存入寄存器reg0。
(16) 控制台输出指令:OUT reg0 15
功能:将寄存器reg0的低字节作为字符数据输出到输出端口(即显示器)。
(17) 加运算指令:ADD reg0 reg1 reg2
功能:将寄存器reg1的值加上reg2的值,结果存入寄存器reg0。如果结
果超过16位有符号数的表示范围,将发生溢出,使程序状态字的溢出标志位o置为1;如果未发生溢出,则使程序状态字的溢出标志位o置为0。
(18) 加立即数指令:ADDI reg0 immediate
功能:将寄存器reg0的值加上立即数immediate,结果仍存入寄存器reg0。如果结果超过16位有符号数的表示范围,将发生溢出,使程序状态字的溢出标
志位o置为1;如果未发生溢出,则使程序状态字的溢出标志位o置为0。
(19) 减运算指令:SUB reg0 reg1 reg2
功能:将寄存器reg1的值减去reg2的值,结果存入寄存器reg0。如果结
果超过16位有符号数的表示范围,将发生溢出,使程序状态字的溢出标志位o置为1;如果未发生溢出,则使程序状态字的溢出标志位o置为0。
(20) 减立即数指令:SUBI reg0 immediate
功能:将寄存器reg0的值减去立即数immediate,结果仍存入寄存器reg0。如果结果超过16位有符号数的表示范围,将发生溢出,使程序状态字的溢出标
志位o置为1;如果未发生溢出,则使程序状态字的溢出标志位o置为0。
(21) 乘运算指令:MUL reg0 reg1 reg2
功能:将寄存器reg1的值乘以reg2的值,结果存入寄存器reg0。
如果结果超过16位有符号数的表示范围,将发生溢出,使程序状态字的溢出标志位置为1;如果未发生溢出,则使程序状态字的溢出标志位o置为0。
(22) 除运算指令:DIV reg0 reg1 reg2
功能:将寄存器reg1的值除以reg2的值,结果存入寄存器reg0,这里进
行的是整数除运算。如果寄存器reg2的值为零,将发生除零错。
(23) 按位与运算指令:AND reg0 reg1 reg2
功能:将寄存器reg1的值与reg2的值进行按位与运算,结果存入寄存器
reg0。
(24) 按位或运算指令:OR reg0 reg1 reg2
功能:将寄存器reg1的值与reg2的值进行按位或运算,结果存入寄存器
reg0。
(25) 按位异或运算指令:NOR reg0 reg1 reg2
功能:将寄存器reg1的值与reg2的值进行按位异或(按位加)运算,结果存
入寄存器reg0。
(26) 按位取反运算指令:NOTB reg0 reg1
功能:将寄存器reg1的值按位取反后,结果存入寄存器reg0。
(27) 算术左移运算指令:SAL reg0 reg1 reg2
功能:将寄存器reg1的值算术左移reg2位,结果存入寄存器reg0。在进
行算术左移时,低位空位用0填充。
(28) 算术右移运算指令:SAR reg0 reg1 reg2
功能:将寄存器reg1的值算术右移reg2位,结果存入寄存器reg0。在进
行算术右移时,高位空位用符号位填充。
(29) 相等关系运算指令:EQU reg0 reg1
功能:将两个寄存器reg0和reg1的值进行相等比较关系运算:reg0 == reg1,
关系运算的结果为逻辑真或逻辑假,存入程序状态字中的比较标志位c。
(30) 小于关系运算指令:LT reg0 reg1
功能:将两个寄存器reg0和reg1的值进行小于关系运算:reg0 < reg1,
关系运算的结果为逻辑真或逻辑假,存入程序状态字中的比较标志位c。
(31) 小于等于关系运算指令:LTE reg0 reg1
功能:将两个寄存器reg0和reg1的值进行小于等于关系运算:reg0 <= reg1,
关系运算的结果为逻辑真或逻辑假,存入程序状态字中的比较标志位c。
(32) 比较标志位取反指令:NOTC
功能:将程序状态字中的比较标志位c求反,即将逻辑真变为逻辑假,将逻
请编写编译器,主程序中从文件读入汇编源程序,输出目标机器代码(十六进制显示)