• 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 控制--开关,是否输出一个周期的时钟
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    两位控制寄存器读写原理
    两位控制读写原理:寄存器中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) #控制时钟信号的打开和关闭
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    程序设计

    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的计数功能(就是计数值加一)
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    编译下载

    # 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    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 # 寄存器间接寻址编号
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    # 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))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    # 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!!!')#编译微指令完成
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161

    七、笔记

    在这里插入图片描述

    引脚标识的一般含义
    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代表计数器读写模式选择写
    
    • 1
    • 2
    • 3
    • 4

    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

  • 相关阅读:
    WebSocket消息推送
    CompletableFuture异步编程详解
    6-2 分治法求解金块问题
    36.【C/C++ 重载运算符,(全干的无水分)】
    自研系统加入license授权(附源码)
    关于vue封装form表单单向流数据问题
    基于SSM(SpringBoot+Mybatis+MySql+Layui)实现的健身房管理系统
    国家/行业标准查询及下载全流程
    Java · List的使用 · List的常用方法 · ArrayList顺序表 · LinkedList链表 · 打牌小程序 · 杨辉三角List实现
    从头开始使用 KNN 进行 KNN 和 MNIST 手写数字识别的初学者指南
  • 原文地址:https://blog.csdn.net/weixin_55255438/article/details/126304405