IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 8位二进制cpu的设计和制作(三) -> 正文阅读

[嵌入式]8位二进制cpu的设计和制作(三)

六、8位二进制CPU的设计和实现3

1、取指令微程序

任务描述:将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!!!')

2、MOV指令

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
在这里插入图片描述

3、汇编编译器

1
python中class的定义及使用

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

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 19:22:30  更:2022-08-19 19:25:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 16:02:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码