用C语言编制一个模拟器,能够模拟简单计算机执行汇编程序生成的目标代码,得到运行结果。
1383题中使用的汇编指令系统如下:
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求反,即将逻辑真变为逻辑假,将逻
(33) 字节数据定义伪指令:BYTE symbol[n] = {...} 蓝色字体部分为可选项
或:BYTE symbol[n] = "..." 蓝色字体部分为可选项
功能:定义长度为1字节的字节型数据或数据块,字节型数据块类似于C的字符数组。
(34) 字数据定义伪指令:WORD symbol[n] = {...} 蓝色字体部分为可选项
功能:定义长度为2字节的双字节型数据或数据块,双字节型数据块类似于C的整型数组(16位系统)。
模拟器实现建议,先向系统申请动态存储区,模拟内存,然后定位好CS、DS、SS、ES的初始地址,其中CS段的地址为模拟内存的初始地址,输入目标代码,加载到模拟内存,指令计数器也随着改变,此时SS段的地址则是PC的地址,在通过上面所述的变量总共占的字节数,计算出DS此时的地址,ES的地址则是模拟内存的末端减去一个ES的长度(ES的长度为变量结构的长度)。接下来就可以让PC重新指向MEM顶端的第一条指令,然后模拟处理器去执行命令,最后一次正确写出32个函数和两个伪指令的解码方法。