【Educoder实训作业】冯·诺依曼体系结构模拟机 TOY2

任务描述

本关任务:利用 Python 语言模拟 TOY2 冯·诺依曼体系结构计算机。

相关知识

从实训简介中可以看出,TOY2 计算机和 TOY 计算机类似,其区别在于 TOY2 计算机执行的是十进制的机器指令,而不是汇编指令。TOY2 计算机指令集如表 1 所示。

表 1 TOY2 计算机的指令集

如图 1 所示, TOY2 指令的长度为 6 位,前 2 位为操作码,第 3 位和第 4 ~ 6 位为操作数,全部采用十进制数表示。

图1


图 1 TOY2 的指令格式

冯·诺依曼体系结构计算机的 CPU 工作过程是自动逐条执行指令的过程。当程序需要执行时,CPU 首先需要将程序代码从外存装载到主存,然后在控制单元的控制下,精确地、一步一步地完成指令的执行。现代计算机一直使用冯·诺依曼体系结构, CPU 执行一条指令的过程通常可分为 4 步,经过这样一些步骤完成一条指令的执行所需的时间称为指令周期,其中的每一步称为一个节拍:

  1. 取指令:指令装载到主存后,CPU 通过程序计数器获得要执行的指令存储地址。根据这个地址,CPU 将指令从主存中读入,并保存在指令寄存器中,同时对程序计数器内容进行“增 1”操作,即指向下一个内存单元。
  2. 译码:由指令译码器对指令进行解码,分析出指令的操作码,所需操作数和操作数的存放位置等信息。
  3. 执行:将译码后的操作码分解成一组相关的控制信号序列,以完成指令动作,包括从寄存器读数据、输入到运算器ALU进行算术或逻辑运算等。
  4. 写结果:若指令执行后产生了结果,则将结果写回到指定位置,通常是寄存器。如果必要,将产生的条件反馈给控制单元。

在最后一个节拍完成后,控制单元复位指令周期,从取指令节拍重新开始运行,此时,程序计数器的内容已被自动修改,指向下一条指令所在的主存地址。需要注意的是,运算指令和数据移动指令的执行不会主动修改程序计数器的值,程序计数器将会自动指向程序顺序上的下一条指令;而控制指令,如跳转指令的执行将会主动改变程序计数器的值,使得程序的执行不再是顺序的。

本关卡在对 TOY2 计算机进行建模与模拟时,共有以下几个函数和全局变量:

  • mem用于模拟主存(共 1000 个存储单元),reg用于模拟通用寄存器组(共 10 个存储单元),pReg用于模拟程序计数器,iReg 用于模拟指令寄存器,address用于模拟程序装载到主存时的起始物理地址。

  • loadProgram函数,用于加载程序,将程序从外存装入到主存,要求程序第一行指令放在address对应的主存单元中,然后根据每一行指令前面的逻辑地址,将每条指令放入主存(mem)对应的单元中。同时,将第 1 条指令的主存单元地址存入pReg,以便执行程序。

  • cycle函数,用于模拟 CPU 指令周期,实现上完全按照指令周期的步骤进行,分为取指令、译码、执行和写结果四个阶段。注意cycle函数的返回值,如果碰到00指令,返回 0 ,run函数结束;如果是其他指令,则返回 1 ,使run函数执行下一条指令。

  • run函数,其主要功能是从外存加载程序到主存并执行。

编程要求

仔细阅读右侧编辑区给出的程序代码框架,在指定的 Begin-End 区间补充完善loadProgram函数和cycle函数,使其能够正确执行使用 TOY2 指令集编写的程序。

测试说明

平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。

本关卡共有 3 个测试集,测试集的输入数据中,第 1 个是使用 TOY2 计算机指令集编写的程序代码文件,分别命名为 add.toy2、sum100.toy2、div.toy2;第 2 个是给定的主存地址address;若 .toy2 中的程序代码需要输入数据,则排在第 3 个位置。评测输出为 .toy 中程序代码的输出内容,即11指令的执行结果。

add.toy2、sum100.toy2、div.toy2 的代码指令如下所示。

 
  1. #add.toy2
  2. 000 031012
  3. 001 032013
  4. 002 041002
  5. 003 111000
  6. 004 000000
  7. #sum100.toy2
  8. 000 031000
  9. 001 032001
  10. 002 033001
  11. 003 041002
  12. 004 042003
  13. 005 034101
  14. 006 054002
  15. 007 094009
  16. 008 080003
  17. 009 111000
  18. 010 000000
  19. #div.toy2
  20. 000 100000
  21. 001 031034
  22. 002 071000
  23. 003 021999
  24. 004 013999
  25. 005 113000
  26. 006 000000

测试样例如下所示: 测试输入:add.toy2 2 预期输出:25

测试输入:div.toy2 20 17 预期输出:2


开始你的任务吧,祝你成功!

参考答案

  1. mem = [0]*1000 #主存
  2. reg = [0]*10 #通用寄存器
  3. pReg = 0 #程序计数器
  4. iReg = 0 #指令寄存器
  5. #loadProgram函数:加载程序,将file文件中的程序代码顺序存放到主存,程序第一条指令放入address地址对应的主存单元中,设置程序计数器pReg的值
  6. def loadProgram(file):
  7. global pReg, iReg, reg, mem, address #全局变量声明
  8. ########## Begin ##########
  9. pReg = address
  10. fil = open(file, 'r') #打开文件
  11. first = True #用于标识是否为第1条指令
  12. while True: #每循环一次加载一条指令
  13. line = fil.readline() #读1行
  14. if line == '': #若读取完毕,则结束循环
  15. break
  16. flds = line.split() #将1行拆分为若干部分
  17. address2 = int(flds[0]) #第0部分为地址
  18. instruc = int(flds[1]) #第1部分为指令
  19. mem[address2 + address] = instruc #将指令加载到主存单元
  20. fil.close() #关闭文件
  21. ########## End ##########
  22. # cycle函数:执行一条 TOY2 指令,包括取指令、指令译码、执行和写结果 4 个步骤
  23. def cycle():
  24. global pReg, iReg, reg, mem, address #全局变量声明
  25. ########## Begin ##########
  26. #取指令
  27. iReg = mem[pReg] #根据pReg的值,将指令从mem取到iReg
  28. pReg = pReg + 1 #pReg加1,指向下一条指令
  29. #译码
  30. opcode = iReg//10000 #操作码
  31. op1 = (iReg//1000)%10 #操作数1
  32. op2 = iReg%1000 #操作数2
  33. #执行和写结果
  34. if opcode==0: #停止指令
  35. return False
  36. elif opcode==1: #数据移动指令:寄存器←主存
  37. reg[op1] = mem[op2]
  38. elif opcode==2: #数据移动指令:主存←寄存器
  39. mem[op2] = reg[op1]
  40. elif opcode==3: #数据移动指令:寄存器←数字
  41. reg[op1] = op2
  42. elif opcode==4: #加法指令
  43. reg[op1] = reg[op1]+reg[op2]
  44. elif opcode==5: #减法指令
  45. reg[op1] = reg[op1]-reg[op2]
  46. elif opcode==6: #乘法指令
  47. reg[op1] = reg[op1]*reg[op2]
  48. elif opcode==7: #整除指令
  49. reg[op1] = reg[op1]//reg[op2]
  50. elif opcode==8: #无条件跳转指令
  51. pReg = op2 + address
  52. elif opcode==9: #条件跳转指令
  53. if reg[op1]==0:
  54. pReg = op2 + address
  55. elif opcode==10: #输入指令
  56. reg[op1] = int(input())
  57. elif opcode==11: #输出指令
  58. print(reg[op1])
  59. return True
  60. ########## End ##########
  61. # run函数:加载程序并执行程序
  62. def run(file):
  63. global pReg, iReg, reg, mem
  64. loadProgram(file) #加载TOY2程序
  65. while True: #每循环一次,执行一条指令
  66. hasNextInstruc = cycle() #执行一条TOY2指令
  67. if hasNextInstruc==False: #若执行的是停机指令
  68. break #则跳出循环
  69. fil = input() #获取待执行程序所在的文件名
  70. address = int(input()) #获取程序装载到主存的起始物理地址
  71. run(fil) #加载程序并执行
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值