任务描述:将RAM中0地址 1地址 2地址数据分别取出放入IR DST SRC寄存器中。
pin.PC_OUT | pin.MAR_IN 程序计数器PC的数送到存储器地址寄存器MAR
pin.RAM_OUT | pin.IR_IN | pin.PC_INC 存储器地址寄存器MAR指向RAM地址的数据送到指令寄存器IR,程序计数器PC+1
pin.PC_OUT | pin.MAR_IN 程序计数器PC的数送到存储器地址寄存器MAR
pin.RAM_OUT | pin.DST_IN | pin.PC_INC存储器地址寄存器MAR指向RAM地址的数据送到目的数寄存器DST,程序计数器PC+1
pin.PC_OUT | pin.MAR_IN 程序计数器PC的数送到存储器地址寄存器MAR
pin.RAM_OUT | pin.SRC_IN | pin.PC_INC存储器地址寄存器MAR指向RAM地址的数据送到目的数寄存器SRC,程序计数器PC+1
七条指令
00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B)
0001C0A4(H):0000 0000 0000 0001 1100 0000 1010 0100(B)
00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B)
0001C0C4(H):0000 0000 0000 0001 1100 0000 1100 0100(B)
00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B)
0001C0E4(H):0000 0000 0000 0001 1100 0000 1110 0100(B)
80000000(H):1000 0000 0000 0000 0000 0000 0000 0000(B) 第32位是1,时钟关闭,不输出一个周期的时钟,程序执行结束
指令解析
分析指令:00008040(H):0000 0000 0000 0000 1000 0000 0100 0000(B) 32
的0是打开时钟输出一个周期的时钟
31的0是内存控制器的PC的EN等于111(B)就是开启计数器的当前计数值,处于关闭读写模式,计数器的加1计算器开启,即加1计算器可以用时钟触发加1,control unit的A端输出:指令寄存器的38位输出+恒为0位,溢出位,奇偶校验位,奇偶标志位+程序计数器的低4位到内存
26~30暂时没有控制任何东西,保留 21~24的0000是不用控制–接收算术逻辑单元的与,或,异或,非的四选一的结果
18~20的000是不用控制–进行选择,算术逻辑单元用于与,或,异或,非的四选一
15~17的010是磁盘的PCEN等于010(B)就是开启计数器的当前计数值,处于读模式,计数器的加1计算器开启,即加1计算器可以用时钟触发加1,计数器的当前计数值输出到总线
11~14的00000是指令的1 ~ 5 位控制写什么寄存器,指令的6 ~ 10 位控制读什么寄存器,
6~10的00010是用于输入寄存器读写控制器的R端,用五三十二译码器等等处理后,可知寄存器读写控制位的低位只有MAR为1,MAR可写
1~5的000000是用于输入寄存器读写控制器的W端,用五三十二译码器等等处理后,可知寄存器读写控制位的高位都为0,都不可读
1~5 控制--写寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
6~10 控制--读寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
11,13控制--选择用目的操作数寄存器,源操作数寄存器,指令的1~5 中的其一控制写寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
12,14控制--选择用目的操作数寄存器,源操作数寄存器,指令的1~5 中的其一控制读寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
15~17控制--控制--磁盘RAM的程序计数器--是开关用程序计数器,还是**读写**程序计数器
18~20控制--进行选择,算术逻辑单元用于与,或,异或,非的四选一
21~24控制--接收算术逻辑单元的与,或,异或,非的四选一的结果
26~30暂时没有控制任何东西,保留
31 控制--内存ROM的程序计数器--是开关用程序计数器,还是**读写**程序计数器
32 控制--开关,是否输出一个周期的时钟
两位控制寄存器读写原理
两位控制读写原理:寄存器中CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC这些的两位读写控制,低位是写端值,高位时读与写异或后的值。
这两位读写控制位输送给一字节的寄存器读写模式控制端IO
一字节的寄存器读写模式控制端IO将2位数据,低位输送给WE,高位输送给CS
WE:一字节的寄存器读写模式的选择(信号)按钮
CS:cs信号等于1时,一字节的寄存器能被触发;cs信号等于0时,一字节的寄存器不能被触发
指令设计pin.py
# coding=utf-8
#正数的原码=反码=补码。
#指令的低5~ 9位控制写端,写端位作低位,
#指令的低0~ 4位与指令的低5~ 9位相异或的值作高位
#使用了22个引脚控制寄存器MSR,MAR,MDR,MC,IR,DST,SRC;A,B,C,D,DI,SI,SP,BP;CS,DS,SS,ES,VEC,T1,T2
#如下是控制寄存器的22个引脚的引脚号
#OUT表示读寄存器马上将寄存器的数据送到总线,IN指写寄存器,W处于高五位,R处于低五位
MSR = 1#存储器段寄存器 #memory segment register
MAR = 2#存储器地址寄存器 #memory address register
MDR = 3#存储器数据寄存器 #memory data register
RAM = 4#内存 #Random Access Memory随机存取存储器
IR = 5 #指令寄存器 #instruction register
DST = 6#目的操作数寄存器 #destination operand register
SRC = 7#源操作数寄存器 #source operand register
A = 8 #A寄存器
B = 9 #B寄存器
C = 10 #C寄存器
D = 11 #D寄存器
DI = 12#目的编址寄存器 #destination index register
SI = 13#源编址寄存器 #source index register
SP = 14#堆栈指令寄存器 # stack instruction register
BP = 15#基址寄存器 #base address register
CS = 16#代码段寄存器 #code segment register
DS = 17#数据段寄存器 #data segment register
SS = 18#堆栈段寄存器 #stack segment register
ES = 19#附加寄存器 #additional register
VEC = 20#
T1 = 21#临时寄存器1 #temporary register 1
T2 = 22#临时寄存器2 #temporary register 2
#OUT表示读寄存器,就是将寄存器的数据送到总线,指令的5~ 9位控制写端,是两位读写控制位的低位。指令的低0~ 4位与指令的低5~ 9位相异或的值作两位读写控制位的高位
MSR_OUT = MSR#0000 0000 0000 0000 0000 0000 0000 0001(B)#指令的5~ 9位表示两位读写控制位的低位值是0,0与指令的低0~ 4位表示的值1相异或的值等于1。得到MSR的两位读写控制位10,是读
MAR_OUT = MAR#0000 0000 0000 0000 0000 0000 0000 0010(B)
MDR_OUT = MDR#0000 0000 0000 0000 0000 0000 0000 0011(B)
RAM_OUT = RAM#0000 0000 0000 0000 0000 0000 0000 0100(B)
IR_OUT = IR #0000 0000 0000 0000 0000 0000 0000 0101(B)
DST_OUT = DST#0000 0000 0000 0000 0000 0000 0000 0110(B)
SRC_OUT = SRC#0000 0000 0000 0000 0000 0000 0000 0111(B)
A_OUT = A #0000 0000 0000 0000 0000 0000 0000 1000(B)
B_OUT = B #0000 0000 0000 0000 0000 0000 0000 1001(B)
C_OUT = C #0000 0000 0000 0000 0000 0000 0000 1010(B)
D_OUT = D #0000 0000 0000 0000 0000 0000 0000 1011(B)
DI_OUT = DI #0000 0000 0000 0000 0000 0000 0000 1100(B)
SI_OUT = SI #0000 0000 0000 0000 0000 0000 0000 1101(B)
SP_OUT = SP #0000 0000 0000 0000 0000 0000 0000 1110(B)
BP_OUT = BP #0000 0000 0000 0000 0000 0000 0000 1111(B)
CS_OUT = CS #0000 0000 0000 0000 0000 0000 0001 0000(B)
DS_OUT = DS #0000 0000 0000 0000 0000 0000 0001 0001(B)
SS_OUT = SS #0000 0000 0000 0000 0000 0000 0001 0010(B)
ES_OUT = ES #0000 0000 0000 0000 0000 0000 0001 0011(B)
VEC_OUT = VEC#0000 0000 0000 0000 0000 0000 0001 0100(B)
T1_OUT = T1 #0000 0000 0000 0000 0000 0000 0001 0101(B)
T2_OUT = T2 #0000 0000 0000 0000 0000 0000 0001 0110(B)
_DST_SHIFT = 5
#IN指写寄存器 #左移动运算符:运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
MSR_IN = MSR << _DST_SHIFT#00001 00000(B)#指令的5~ 9位表示两位读写控制位的低位值是1,1与指令的低0~ 4位表示的值0相异或的值等于1。得到MSR的两位读写控制位11,是写
MAR_IN = MAR << _DST_SHIFT
MDR_IN = MDR << _DST_SHIFT
RAM_IN = RAM << _DST_SHIFT
IR_IN = IR << _DST_SHIFT
DST_IN = DST << _DST_SHIFT
SRC_IN = SRC << _DST_SHIFT
A_IN = A << _DST_SHIFT
B_IN = B << _DST_SHIFT
C_IN = C << _DST_SHIFT
D_IN = D << _DST_SHIFT
DI_IN = DI << _DST_SHIFT
SI_IN = SI << _DST_SHIFT
SP_IN = SP << _DST_SHIFT
BP_IN = BP << _DST_SHIFT
CS_IN = CS << _DST_SHIFT
DS_IN = DS << _DST_SHIFT
SS_IN = SS << _DST_SHIFT
ES_IN = ES << _DST_SHIFT
VEC_IN = VEC << _DST_SHIFT
T1_IN = T1 << _DST_SHIFT
T2_IN = T2 << _DST_SHIFT
#11,13控制--选择用目的操作数寄存器,源操作数寄存器,指令的1~5中的其一控制写寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
#12,14控制--选择用目的操作数寄存器,源操作数寄存器,指令的5~10中的其一控制读寄存器CS,DS,SS,ES,VEC,T1,T2,A,B,C,D,DI,SI,SP,BP,MSR,MAR,MDR,MC,IR,DST,SRC
SRC_R = 2 ** 10#0000 0000 0000 0000 0000 0100 0000 0000(B) #SRC_R即SR,寄存器的读控制,选择用SRC源操作数寄存器 #幂运算2的10次方等于1024
SRC_W = 2 ** 11#0000 0000 0000 0000 0000 1000 0000 0000(B) #SRC_W即SW,寄存器的写控制,选择用SRC源操作数寄存器
DST_R = 2 ** 12#0000 0000 0000 0000 0001 0000 0000 0000(B) #DST_R即DR,寄存器的写控制,选择用DST目的操作数寄存器
DST_W = 2 ** 13#0000 0000 0000 0000 0010 0000 0000 0000(B) #DST_W即DW,寄存器的读控制,选择用DST目的操作数寄存器
#磁盘RAM计数器的读写模式选择和开启关闭循环累加
PC_WE = 2 ** 14#0000 0000 0000 0000 0100 0000 0000 0000(B)
PC_CS = 2 ** 15#0000 0000 0000 0000 1000 0000 0000 0000(B)
PC_EN = 2 ** 16#0000 0000 0000 0001 0000 0000 0000 0000(B)
PC_OUT = PC_CS #磁盘RAM计数器EN端的三位输入位为010,高位0代表计数器关闭循环累加,低2位10代表计数器读写模式选择写,低2位是PC的寄存器的两位读写控制位(11写,10读),高1位是选择PC输入数据到PC寄存器,还是选择PC加法器输出数据到PC寄存器(1输出,0输入)。
PC_IN = PC_CS | PC_WE #磁盘RAM计数器EN端的三位输入位为011,高位0代表计数器关闭循环累加,低2位11代表计数器读写模式选择读
PC_INC = PC_CS | PC_WE | PC_EN #磁盘RAM计数器EN端的三位输入位为111,高位1代表计数器开启循环累加,低2位11代表计数器读写模式选择读
HLT = 2 ** 31#1000 0000 0000 0000 0000 0000 0000 0000(B) #控制时钟信号的打开和关闭
程序设计
import pin
FETCH = [
pin.PC_OUT | pin.MAR_IN, #(将PC里的计数值放到总线上)读PC计数寄存器 | (偏移地址寄存器MAR接收计数值)写寄存器MAR
pin.RAM_OUT | pin.IR_IN | pin.PC_INC, #(输出内存RAM数据(作用更像磁盘))读RAM | 写IR寄存器 | 开启RAM的PC的计数功能(就是计数值加一)
pin.PC_OUT | pin.MAR_IN, #读RAM的PC | 写偏移地址寄存器MAR
pin.RAM_OUT | pin.DST_IN | pin.PC_INC, #读RAM | 写目的操作数寄存器DST | 开启RAM的PC的计数功能(就是计数值加一)
pin.PC_OUT | pin.MAR_IN, #读RAM的PC | 写偏移地址寄存器MAR
pin.RAM_OUT | pin.SRC_IN | pin.PC_INC, #读RAM | 写源操作数寄存器SRC | 开启RAM的PC的计数功能(就是计数值加一)
]
编译下载
# coding=utf-8
#with open(filename, 'wb') as file意思以二进制格式打开一个文件filename,只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。#当with as代码块结束时,程序自动关闭打开的文件
#to_bytes()
#int.to_bytes(length, byteorder, *,signed=False)返回一个整数的字节数组,是Python3.1新增加的功能,即int型数组转化成字节数组
#参数含义:
#length: 整数字节数,如果不能用给定的字节数来表示则会引发OverflowError
#byteorder: 确定用于表示整数的字节顺序,byteorder为’big’表示最高位字节放在字节位开头。byteorder为’little’表示最高位字节放在字节数组的末尾。
#signed: 是否使用二进制补码来表示整数,如果signed为False并且给出的是负整数,则会引发OverflowError。默认值为False
#for i in range(5): 也是成立的,这代表着代码将会生成0,1,2,3,4的int整数序列,这里默认range( )第一个参数为0,;如果代码为for i in range(10,15): 那么代码会生成10,11,12,13,14的int整数序列。
#64位系统int是4字节的。
#Len(text):得到字符串的长度。
#for 变量名 in 原列表
#m = [1 for y in range(3)]
#for x in m :
# print(x)
#[Running] python -u "c:\Users\李杰\Desktop\computer-main\computer-main\cpu\26 取指令微程序\cpu\controller.py"
#1
#1
#1
#Compile micro instruction finish!!!
#
#[Done] exited with code=0 in 0.094 seconds
import os
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'micro.bin')
micro = [pin.HLT for _ in range(0x10000)] # 在ROM里写满HLT指令 ,列表micro的每个元素都是2147483648=0x8000 0000=2 ** 31
for addr in range(0x10000): # 循环0x10000=65,536(D)次
ir = addr >> 8 # 从地址中取出IR即指令信息
psw = (addr >> 4) & 0xf # 从地址中取出PSW即状态字信息
cyc = addr & 0xf # 从地址中取出系统时钟周期信息 #与上0xf是保留低4位,清零其余位,cyc的值是0到1111(B)
if cyc < len(ASM.FETCH): # 如果cyc小于指令的长度,cyc是CPU控制器的程序计数器计数值的低4位值,len(ASM.FETCH)=6
micro[addr] = ASM.FETCH[cyc]#用列表FETCH给列表micro赋值,cyc的值是0到5,FETCH元素序号满足cyc值为7到15的,不赋到micro对应位置.因此FETCH中有16分之6要赋给micro
with open(filename, 'wb') as file: # 转换成二进制
for var in micro:
value = var.to_bytes(4, byteorder='little')
file.write(value) #向文件写入数据value
print('Compile micro instruction finish!!!')
1
强调一下几点概念 我们做的事是在对指令进行编码,如MOV,我们编码为1000 xxxx,
MOV 寄存器,立即数 我们编码为1000 0100,这样的编码是为了方便将指令放在RAM中(同时在ROM指令集里寻找到相应的微指令),
我们将汇编语言如:MOV 寄存器,立即数翻译成1000 0100称作编译,但是编译出来的1000 0100这样的机器语言仍然只是编码。
真正的指令是存放在CPU内部的ROM指令集里的,我们需要用这个机器语言去找到对应的微指令。而这个微指令才是真正控制CPU里的所有资源的指令。
2
# coding=utf-8
MSR = 1
MAR = 2
MDR = 3
RAM = 4
IR = 5
DST = 6
SRC = 7
A = 8
B = 9
C = 10
D = 11
DI = 12
SI = 13
SP = 14
BP = 15
CS = 16
DS = 17
SS = 18
ES = 19
VEC = 20
T1 = 21
T2 = 22
MSR_OUT = MSR
MAR_OUT = MAR
MDR_OUT = MDR
RAM_OUT = RAM
IR_OUT = IR
DST_OUT = DST
SRC_OUT = SRC
A_OUT = A
B_OUT = B
C_OUT = C
D_OUT = D
DI_OUT = DI
SI_OUT = SI
SP_OUT = SP
BP_OUT = BP
CS_OUT = CS
DS_OUT = DS
SS_OUT = SS
ES_OUT = ES
VEC_OUT = VEC
T1_OUT = T1
T2_OUT = T2
_DST_SHIFT = 5
MSR_IN = MSR << _DST_SHIFT
MAR_IN = MAR << _DST_SHIFT
MDR_IN = MDR << _DST_SHIFT
RAM_IN = RAM << _DST_SHIFT
IR_IN = IR << _DST_SHIFT
DST_IN = DST << _DST_SHIFT
SRC_IN = SRC << _DST_SHIFT
A_IN = A << _DST_SHIFT
B_IN = B << _DST_SHIFT
C_IN = C << _DST_SHIFT
D_IN = D << _DST_SHIFT
DI_IN = DI << _DST_SHIFT
SI_IN = SI << _DST_SHIFT
SP_IN = SP << _DST_SHIFT
BP_IN = BP << _DST_SHIFT
CS_IN = CS << _DST_SHIFT
DS_IN = DS << _DST_SHIFT
SS_IN = SS << _DST_SHIFT
ES_IN = ES << _DST_SHIFT
VEC_IN = VEC << _DST_SHIFT
T1_IN = T1 << _DST_SHIFT
T2_IN = T2 << _DST_SHIFT
SRC_R = 2 ** 10
SRC_W = 2 ** 11
DST_R = 2 ** 12
DST_W = 2 ** 13
PC_WE = 2 ** 14
PC_CS = 2 ** 15
PC_EN = 2 ** 16
PC_OUT = PC_CS
PC_IN = PC_CS | PC_WE
PC_INC = PC_CS | PC_WE | PC_EN
CYC = 2 ** 30
HLT = 2 ** 31
## 指令长度定义
ADDR2 = 1 << 7 # 二地址指令 1xxx xxxx
ADDR1 = 1 << 6 # 一地址指令 01xx xxxx
ADDR2_SHIFT = 4 # 二地址指令偏移
ADDR1_SHIFT = 2 # 一地址指令偏移
## 寻址方式定义
AM_INS = 0 # 立即寻址编号
AM_REG = 1 # 寄存器寻址编号
AM_DIR = 2 # 直接寻址编号
AM_RAM = 3 # 寄存器间接寻址编号
# coding=utf-8
#编译器预定义程序
#预编译
#在预编译过程,编辑器首先会删掉代码中的注释,因为注释是给程序使用者看的,代码文件不需要这些。
#删除注释之后,编辑器会将程序包含的头文件的声明的内容导入到该程序中,然后将预定义的符号完成替换,
#如将 #define中的内容替换,这些工作完成之后,就会生成一个后缀为 .obj 的文件
#集合使用大括号 { },元组使用()
import pin
FETCH = [
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.IR_IN | pin.PC_INC,
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.DST_IN | pin.PC_INC,
pin.PC_OUT | pin.MAR_IN,
pin.RAM_OUT | pin.SRC_IN | pin.PC_INC,
]
MOV = 0 | pin.ADDR2 # MOV指令定义位1000 xxxx, MOV = 0 | pin.ADDR2 中的0代表MOV是二操作数指令列表的第一个指令
ADD = (1 << pin.ADDR2_SHIFT) | pin.ADDR2 # ADD指令定义为 1001 xxxx,(1 << pin.ADDR2_SHIFT)中的1代表ADD是二操作数指令列表的第二个指令
NOP = 0 # NOP指令定义为 0000 0000
HLT = 0x3f # HLT指令定义为 0011 1111
#各类操作集合INSTRUCTIONS={ 2: {128: {(1, 0): [8199]}}, 1: {}, 0: {0: [1073741824], 63: [2147483648]} }
INSTRUCTIONS = {
# 二操作数指令列表
2: {
MOV: { # MOV指令寻址方式列表
(pin.AM_REG, pin.AM_INS): [ # (寄存器寻址,立即寻址) #AM_REG=1代表寄存器寻址,AM_INS=0代表立即寻址
pin.DST_W | pin.SRC_OUT, # 微指令:DST目的操作数寄存器写,SRC源操作数寄存器读。实现操作读SRC的数据,写到DST。这里DST是控制寄存器写
]
}
},
# 一操作数指令列表
1: {},
# 零操作数指令列表
0: {
NOP: [
pin.CYC, # 让指令周期清零,跳过这次指令
],
HLT: [
pin.HLT, # 指令停止
]
}
}
print(INSTRUCTIONS)
print(bin(MOV))
# coding=utf-8
# 生成指令集的程序 ---用列表micro制作micro.bin指令集,导入ROM
#python中的def语句是define的意思,用来定义函数
#例子1
#x = 1
#
#def func():
# x = 2
##
#func()
#print(x)
#输出:1
#在func函数中并未在x前面加global,所以func函数无法将x赋为2,无法改变x的值
#
#例子2
#x = 1
#
#def func():
# global x
# x = 2
#
#func()
#print(x)
#输出:2
#加了global,则可以在函数内部对函数外的对象进行操作了,也可以改变它的值了
import os
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'micro.bin')
micro = [pin.HLT for _ in range(0x10000)] # 将程序停止指令放满整个指令集
def compile_addr2(addr, ir, psw, index): # 处理二操作数的指令
global micro
op = ir & 0xf0 # 取出操作指令
amd = (ir >> 2) & 3 # 取出目标操作数的寻址方式
ams = ir & 3 # 取出源操作数的寻址方式
INST = ASM.INSTRUCTIONS[2] # 取出二操作数的所有指令的列表
if op not in INST: # 遍历二操作数的所有指令看存不存在,如果不存在
micro[addr] = pin.CYC # 跳过该指令
return
am = (amd, ams) # 目的操作数和源操作数合起来
if am not in INST[op]: # 遍历该指令下的所有寻址方式,如果不存在
micro[addr] = pin.CYC # 跳过该指令
return
EXEC = INST[op][am] # 假设指令和寻址方式都找到了,则拷贝出对应的微指令,即取出2: { MOV: { (pin.AM_REG, pin.AM_INS): [ pin.DST_W | pin.SRC_OUT, ] }中的列表 [pin.DST_W | pin.SRC_OUT]
if index < len(EXEC): # 把指令补到后面(因为ASM.FETCH已经有6个指令)
micro[addr] = EXEC[index] #即micro[addr] = [ pin.DST_W | pin.SRC_OUT ]
else:
micro[addr] = pin.CYC
def compile_addr1(addr, ir, psw, index): # 处理一操作数的指令
pass
def compile_addr0(addr, ir, psw, index): # 处理零操作数的指令
global micro
op = ir # 取出操作指令
INST = ASM.INSTRUCTIONS[0] # 取出零操作数的所有指令的列表
if op not in INST: # 遍历二操作数的所有指令看存不存在,如果不存在
micro[addr] = pin.CYC # 跳过该指令
return
EXEC = INST[op] # 假设指令找到了,则拷贝出对应的微指令
if index < len(EXEC): # 把指令补到后面
micro[addr] = EXEC[index]
else:
micro[addr] = pin.CYC
for addr in range(0x10000): # 对整个指令集依次处理
ir = addr >> 8 # 取出表示指令的一段
psw = (addr >> 4) & 0xf # 取出表示状态字的一段
cyc = addr & 0xf # 取出微指令周期的一段
if cyc < len(ASM.FETCH): # 这里是将一段取值微程序放到所有指令中,共6条指令,6条指令实现:将RAM中0地址 1地址 2地址数据分别取出放入IR指令寄存器 DST目的操作数寄存器 SRC源操作数寄存器中。
micro[addr] = ASM.FETCH[cyc]
continue
addr2 = ir & (1 << 7) # 取出表示二操作数指令的位
addr1 = ir & (1 << 6) # 取出表示一操作数指令的位
index = cyc - len(ASM.FETCH) # ASM.FETCH已经有6个指令
if addr2: # 对操作数不同的指令分情况处理
compile_addr2(addr, ir, psw, index)
elif addr1:
compile_addr1(addr, ir, psw, index)
else:
compile_addr0(addr, ir, psw, index)
with open(filename, 'wb') as file:
for var in micro: #用列表micro制作micro.bin指令集,导入ROM
value = var.to_bytes(4, byteorder='little')
file.write(value)
print('Compile micro instruction finish!!!')#编译微指令完成
3
2
3
# coding=utf-8
#第一是作为脚本直接执行,第二是 import 到其他的 python 脚本中被调用(模块重用)执行。
# 因此 if __name__ == 'main': 的作用就是控制这两种情况执行代码的过程,
# 在 if __name__ == 'main': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而 import 到其他脚本中是不会被执行的。
import os
import re
import pin
import assembly as ASM
dirname = os.path.dirname(__file__)
inputfile = os.path.join(dirname, 'program.asm') # 读入汇编文件program.asm
outputfile = os.path.join(dirname, 'program.bin')
annotation = re.compile(r"(.*?);.*") # 正则匹配 #将字符串以;为中断分为几部分,(.*)因为是贪婪的,所以返回的是所有满足条件的内容,(.*?)为非贪婪模式,所以返回第一个满足要求的内容或没有匹配成功
codes = []
OP2 = { # 二操作数指令列表
'MOV': ASM.MOV #MOV = 0 | ADDR2 = 1 << 7=1000 0000=0x80=128(D)
}
OP1 = { # 一操作数指令列表
}
OP0 = { # 零操作数指令列表
'NOP': ASM.NOP,
'HLT': ASM.HLT,
}
OP2SET = set(OP2.values()) #python内置的values()函数返回一个字典中所有的值,set() 函数创建一个无序不重复元素集,不重复就是达到命名或标记的作用
OP1SET = set(OP1.values())
OP0SET = set(OP0.values())
REGISTERS = { # 可操作寄存器
"A": pin.A,
"B": pin.B,
"C": pin.C,
"D": pin.D,
}
###########################################定义对象Code,类Code的属性:numer行号,source源代码,op 操作指令,dst目的操作数寄存器,src源操作数寄存器,prepare_source()调用预处理源代码
###########################################允许对象进行操作的方法:get_op(self) 获取指令,get_am(self, addr) 获取目的 操作数和源操作数,prepare_source(self)预处理源代码,compile_code(self),__repr__(self)打印的时候显示
class Code(object): # Code对象
def __init__(self, number, source):
self.numer = number # 行号
self.source = source.upper() # 源代码 #MOV A, 5 #Python upper() 方法将字符串中的小写字母转为大写字母。
self.op = None #操作指令
self.dst = None #目的操作数寄存器
self.src = None #源操作数寄存器
self.prepare_source() # 调用预处理源代码
def get_op(self): # 获取指令如MOV
if self.op in OP2:
return OP2[self.op]
if self.op in OP1:
return OP1[self.op]
if self.op in OP0:
return OP0[self.op]
raise SyntaxError(self)
def get_am(self, addr): # 获取目的操作数和源操作数 (目的操作数肯定是寄存器)如pin.A,int(5)
if not addr: # 如果啥都没有,返回0
return 0, 0
if addr in REGISTERS: # 如果是寄存器,列表中存在返回寄存器编码
return pin.AM_REG, REGISTERS[addr]
if re.match(r'^[0-9]+$', addr): # 如果是数字,返回立即数
return pin.AM_INS, int(addr)
if re.match(r'^0X[0-9A-F]+$', addr): # 如果是十六进制数,返回十六进制立即数
return pin.AM_INS, int(addr, 16)
raise SyntaxError(self)
def prepare_source(self): # 预处理源代码
tup = self.source.split(',') # 用逗号分割代码
if len(tup) > 2: # 如果分割出来长度大于2 说明语法错误
raise SyntaxError(self)
if len(tup) == 2: # 如果分割出来等于二
self.src = tup[1].strip() # 把逗号后面的分配给源操作数
tup = re.split(r" +", tup[0]) # 用正则空格来分割
if len(tup) > 2: # 如果分割出来长度大于2 说明语法错误
raise SyntaxError(self)
if len(tup) == 2: # 如果等于二
self.dst = tup[1].strip() # 将后面的分配给目的操作数
self.op = tup[0].strip() # 前面的分配给指令
def compile_code(self):
op = self.get_op() # 获取指令
amd, dst = self.get_am(self.dst) # 获取目的操作数编码
ams, src = self.get_am(self.src) # 获取源操作数编码
if op in OP2SET: # 获取指令编码
ir = op | (amd << 2) | ams #MOV A, 5; 寄存器寻址,立即寻址;ir=1000 0000 | (amd << 2) | ams =1000 0000 | 0100 | 0
elif op in OP1SET:
ir = op | amd
else:
ir = op
return [ir, dst, src]
def __repr__(self): # 打印的时候显示
return f'[{self.numer}] - {self.source}' # 显示行号+源代码
###########################################类的名称:SyntaxError;类的属性:类code;允许对对象进行操作的方法:
class SyntaxError(Exception): # 语法错误
def __init__(self, code: Code, *args, **kwargs):
super().__init__(*args, **kwargs) #super() 函数是用于调用父类(超类)的一个方法。
self.code = code
###########################################index是以0开始的索引序列值, line是汇编文件program.asm的第index行字符串, source是line去掉两端的空格后的字符串
def compile_program():
with open(inputfile, encoding='utf8') as file: # 读入汇编文件program.asm
lines = file.readlines() # 记录行号 #readlines()用于读取所有行,并返回列表,在这里readlines()操作对象是汇编文件program.asm
for index, line in enumerate(lines): #enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,所以index是以0开始的索引序列值, line是汇编文件program.asm的第index行字符串
source = line.strip() # 将两端的空格去掉 #strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
if ';' in source: # 将;后面的去掉
match = annotation.match(source) #函数 match() 的功能是在字符串中匹配正则表达式,如果匹配成功,则返回 MatchObject 对象实例。
source = match.group(1) #group(1) 列出第一个括号匹配部分(正则表达式中的三组括号把匹配结果分成三组)
if not source: # 如果没有代码跳过
continue
code = Code(index + 1, source) # 生成Code类对源代码进行处理 #即自动执行宏定义函数__init__(self, number, source)
codes.append(code) #append() 方法用于在列表末尾添加新的对象。
with open(outputfile, 'wb') as file: #以二进制格式打开或创建文件program.bin,当with as代码块结束时,程序自动关闭打开的文件program.bin
for code in codes:#循环在program.bin文件中打印列表codes的元素
values = code.compile_code() # 获得 编码 # 函数compile_code()返回[ir, dst, src]
for value in values:#有时values=[84,8,5]
result = value.to_bytes(1, byteorder='little')#to_byte函数返回一个整数数组,且此元素的数组有一个元素
file.write(result) #将整数数组写入文件program.bin
def main():
compile_program()
# try:
# compile_program()
# except SyntaxError as e:
# print(f'Syntax error at {e.code}')
# return
print('compile program.asm finished!!!')
if __name__ == '__main__':
main()
引脚标识的一般含义
A: 8位二进制加法器的8位输入
B: 8位二进制加法器的8位输入
O:8位二进制加法器的8位结果输出端(需要使能位使能)
S:8位二进制加法器的8位结果输出端(不需要使能位使能)
CO:8位二进制加法器的输出进位值
CI:8位二进制加法器的输入进位值
OP:取反器的使能(信号)按钮
CP:D边沿触发器的上升沿触发(信号)按钮
DI:一字节存储器的数据输入端
DO:一字节存储器的数据输出端
Clear:一字节存储器的清零(信号)按钮
Pre:一字节存储器的预设输入端
DO:一字节存储器的的数据输出端
EN:一字节存储器的允许触发(信号)按钮
CL:一字节的寄存器的清零(信号)按钮
W:一字节的寄存器的读模式设置(信号)按钮
R:一字节的寄存器的数据输出使能(信号)按钮(前提已经按动了寄存器的读模式(信号)按钮)
WE:一字节的寄存器读写模式的选择(信号)按钮
CS:cs信号等于1时,一字节的寄存器能被触发;cs信号等于0时,一字节的寄存器不能被触发
IO:一字节的寄存器读写模式控制端,将2位数据,一位输送给WE,另一位输送给CS,
-----最终低位为1时,寄存器为写模式;此时高位必须为1,以允许时钟触发写入数据
-----------------为0时,寄存器为读模式;此时高位必须为1,以允许输出数据
即11写,10读
W:16位的高位交叉编址存储器的数据输入使能(信号)按钮
R:16位的高位交叉编址存储器的数据输出使能(信号)按钮
A:16位的高位交叉编址存储器的地址总线
ALU:算术逻辑单元的与,或,异或,非的四选一的结果输出端
PWS:算术逻辑单元计算前后溢出位,奇偶校验位,奇偶标志位的值的输出端,共4位最高位恒为0
OP:算术逻辑单元用于与,或,异或,非的四选一的输入数据端
CL:清零算术逻辑单元计算前后溢出位,奇偶校验位,奇偶标志位的值
CP:触发更新算术逻辑单元计算前后溢出位,奇偶校验位,奇偶标志位的值
PC或EN:程序计数器的3位输入,这3位输入可以决定读写模式选择和开启关闭循环累加:
-----------1)是否启动用程序计数器,程序计数器会用DI端的值更新计数器的当前计数值,再每个时钟加1(2位)
-----------2)控制对当前计数值的读写(0~1位)
例子:a、PC或EN=111(B)就是开启计数器的当前计数值,处于写模式,计数器的加1计算器开启,即加1计算器可以用时钟触发加1
b、
磁盘RAM计数器EN端的三位输入位为010,高位0代表计数器关闭循环累加,低2位10代表计数器读写模式选择读,低2位是PC的寄存器的两位读写控制位(11写,10读),高1位是选择PC输入数据到PC寄存器,还是选择PC加法器输出数据到PC寄存器(1输出,0输入)。
磁盘RAM计数器EN端的三位输入位为011,高位0代表计数器关闭循环累加,低2位11代表计数器读写模式选择写
磁盘RAM计数器EN端的三位输入位为111,高位1代表计数器开启循环累加,低2位11代表计数器读写模式选择写
I1:CPU控制器的–输入:指令寄存器的8位输出
I2:CPU控制器的–输入:目的操作寄存器的8位输出
I3:CPU控制器的–输入:源操作寄存器的8位输出
SYC:CPU控制器的32位微指令中的4位地址位
A:CPU控制器的–输出:输出:指令寄存器的8位输出+恒为0位,溢出位,奇偶校验位,奇偶标志位+程序计数器的低4位
D:CPU控制器的–输入:内存的某个32位存储单元
HLT:CPU控制器的–输出:控制时钟信号的打开和关闭
8位二进制CPU的设计和实现CPU基本电路的实现1
8位二进制CPU的设计和实现CPU微机架构的实现2
8位二进制CPU的设计和实现3