微机原理课本外知识点补充
微机原理补充
- 整理了PDF版本,内容跟下面差别不大,好处是可以打印而且不会出现渲染问题
- 在这里推一个98帖子:
- 【E电园】徐习东老师DSP_电子笔记及一些资料分享 https://www.cc98.org/topic/4751962 复制本链接,打开【CC98】微信小程序,直接查看本帖!
CPU模块
CPU寄存器
2812芯片大体上分为:中央处理单元(CPU),存储器和外设。其中的CPU寄存器如下(大致):
CPU寄存器 |- 读写改集成的累加器模块 ALU |- 加法器 ACC(32) = AH(16) + AL(16) |- 32位乘法器模块 |- 乘数寄存器(暂存) : XT |- 乘法寄存器(结果) : P(32) = PH(16) + PL(16) |- 辅助寄存器模块 ARAU(32) : 地址寄存器算术单元 |- 辅助寄存器 AR(8个×32位) : XARn (n=0~7) = 高16位 + ARn (低16位) |- 数据页指针 DP(16) : 直接寻址(22,4M) = DP(16) + 偏移量(6) |- 堆栈指针 SP(16) : 偏移量小于64, 只能访问[0,0xffffH]低地址空间单元, 复位0x0400 |- 程序计数器 PC : 总是包含到达D2阶段指令的地址 |- 指令计数器 IC : 装入下一条指令的地址, 保持到下个D2阶段 |- 状态寄存器 ST0/ST1 |- 溢出位 V (Overflow): 1为有溢出, 用于判断有符号运算是否出错 溢出一旦置位不会被下一次运算清除,会一直保留 比较运算CMP不会溢出(因为溢出的要求是“存入寄存器”) |- 进位/借位位 C (Carry): 用于判断无符号数高低, 对有符号数而言不重要 |- 0 : 无进位/有借位 |- 1 : 有进位/无借位 |- 负标志位 N (Negative): 1为有负数产生,用于有符号数判断大小 CMP比较时,看计算结果的真实值(不怕溢出) SUB减法时,看计算机操作、存储的结果,默认读最高位 |- 零标志位 Z (Zero): 1为有0产生 |- 符号扩展模式位 SXM : 0-无扩展, 1-符号扩展 |- (ST1) - 辅助寄存器指针 ARP |- 其他 (如中断控制寄存器IFR,IER等)
总线
2812的存储器空间被分为了两大块:程序空间(P)和数据空间(D)。
访问任一空间,都需要两种总线配合:地址总线(A)和数据总线(D),前者终于传送存储单元的地址,后者用于传送存储单元的具体内容。
- 下面对英文缩写做一点简单的说明:
P : Program 程序(空间) A : Address 地址(总线) D : Data 数据(空间或总线) R/W : Read/Write 读取/写入 省略表示两者均可 B : Base 总线![](\image\微机01.png) * 下面就是2812CPU对存储空间内数据的调用的过程
存储空间 程序空间P 数据空间D | |-----|-----| | | | 地址总线A PAB(22) DWAB(32) DRAB(32) |-------| | | | |------| | 数据总线D PRDB(32) D/PWDB(32) DRDB(32)
可以看到:
- 一共有6跟总线,三根数据总线,3根地址总线
- 地址总线A少一根的原因是程序空间P的读写共用一根总线(因此程序的读写不能同时进行)
- 数据总线D少一根的原因是程序、数据空间共用一根总线进行数据的写入
指令流水线
2812的指令流水线大体上遵循一下几点规则:
- 一条指令最多分8步完成
- 每一步都要一个时间间隔完成
- 同一时间最多可能有8条指令在执行
- 步与步之间可能会插入间隔(如当此步需要用到上一步的结果,就会等待)
流水线的8步分别为:
获取指令地址 ——> 获取指令内容 ——> 对指令进行解码 ——> 解析操作数地址 ——> 锁定操作数地址 ——> 获取操作数 ——> CPU执行'real work' ——> 将结果存入到内存
2812指令系统——寻址
寻址的基本操作
2812采用增强型哈佛总线结构,能够并行的访问地址和数据存储空间,其寻址的范围为 [0,0x3fffff]
(即2^22^=4M)。介绍寻址之前,大致先了解一下 MOV
指令,大意就是:MOV A B
近似于把B放到A里面,这也是最重要的汇编语言之一。
寻址方式大致上的分类有以下3种:
寻址方式 | 含义 | 写法 |
---|---|---|
立即寻址 | 直接对应数字本身 | # |
直接寻址 | 某一个单元格内对应的数字 | @ |
间接寻址 | 指向某个单元的指针对应的单元中的数字 | * |
下面介绍一些常用的寻址方法及其写法(下文中 loc16
表示任一16位的地址/数据存储单元):
立即数寻址(把数据送到寄存器)
- 数据不能太大,一般为16bit,只有XAR可以接受22bit的数据
- ACC不能直接接受更多位的立即数
1 | MOVL XARn, #22bit |
偏移量直接寻址(DP的使用,好用的)
1
2MOVW DP, #0x3F9000 >> 6 // 把3F9000“赋值”给DP
MOV @4, #16bit // 偏移量=4,把某个数16bit“赋值”给3F9004单元因为DP寻址方式是用16位DP“并上”6位偏移量来实现的,因此为了避免繁杂的计算,在给DP输入时要把
0x70D4
右移6位,直接移到偏移量的位置上,就可以把“并”换成简单的加法。堆栈间接寻址(SP的使用)
1
2MOV SP, #0x70DF // 把70DF“赋值”给SP
MOV *-SP[5], #16bit // 偏移量=-5,*表示C语言的指针,把某个数16bit“赋值”给70DA单元SP就是只能用负的偏移量,也就是
-SP[n]
;其中
n=#6bit
,即 $n\le2^6-1=63$寄存器间接寻址(AR/XAR的使用)
1
2MOVL XAR6, #0x70D0 // 把70D0“赋值”给XAR6
MOV *+XAR6[4], #16bit // 偏移量=4,把某个数16bit“赋值”给70D4单元AR/XAR就是只能用正的偏移量,也就是
+XARn[m]
;其中
m=#3bit
,即 $m\le2^3-1=7$上述方法2/3/4的第二行都属于
MOV loc16 #16bit
的形式寄存器直接寻址(说实话我看不太懂)
1
2
3MOVL ACC, @XAR2 // MOV AX, loc16
MOVL @6, ACC // MOV loc16, AX
MOVL T, @AL // MOV loc16, AXACC(32)=AH(16)+AL(16),这里用AX指代AH或AL中的任意一个,是汇编里面比较特殊的一个量
AL的目的之一就是作为中转站,来代替被禁止的
MOV loc16 loc16
空间立即寻址
1
2MOV *(0:16bit),loc16 // 这个是通式
MOV *(0:0x70D4), @AX // 这个是例子
- 以下是两种被禁止的寻址写法:
MOV loc16 loc16
,代表为:MOV DP XAR
,但是ACC(AH/AL)
除外MOC *(0:0x16bit), #16bit
,代表为:MOV *(0:0x70D4), 0xFF00
- 一般对于
MOV A B
而言,若A/B表示寄存器(DP,XAR等)时,A中不加@,B中加@(大部分情况下)
寻址例程
1 | // Gpfmux=0xF0FF (Gpfmux地址为0x70D4) |
汇编语言
常用汇编语言
传送指令
1
MOV / MOVL (32bit) / MOVW (专用于DP) / MOVU (高位0扩展,常用于ACC)
简单指令集(AX=AH或AL)
1
2
3
4
5
6
7/* 加法 */ ADD AX, loc16 // AX = AX + loc16
/* 减法 */ SUB AX, loc16 // AX = AX - loc16
/* 比较 */ CMP AX, loc16 // 根据比较结果置位,但不会改动数值
// 如置零位ZF,符号位SF
/* 与 */ AND AX, loc16 // AX = AX & loc16
/* 或 */ OR AX, loc16 // AX = AX | loc16
/* 取反 */ NEG AX, loc16 // AX = -AX上面的指令后面多一个B表示短指令,如
ANDB AX #8bit
移位指令(可以代替以2为倍数的乘除法)
1
2
3
4LSL AX, #16bit // 逻辑左移(unsigned)
LSR AX, #16bit // 逻辑右移(unsigned)
ASR AX, #16bit // 算数右移(signed),保留符号位
// 例: LSR AL, #4 表示 AL = AL / 16重复执行(常用于除法,要背除法的代码段)
1
2RPT #N-1 // 重复下一行N次
|| .... // 只能写简单的代码,比如加、减等乘法(写法很多,只介绍其中一种)
1
2MPYB P,T,#8bit // P (signed 32) = T (signed 16) * 8bit (unsigned 8)
// P (32) = PH (16) + PL (16)条件减法(常用于除法,要背除法的代码段)
1
SUBCU ACC, loc16 // ACC = ACC 条件减 loc16,具体原理不想写了
条件指令(好多啊,这咋记得住啊)
1
2
3B CODE, LOGIC // B代表条件指令,SB短跳8bit,LB长跳22bit
// CODE是代码块的名称,可以理解为C或Py的函数
// LOGIC表示条件,满足跳转,不满足不跳条件列表: 中文名 条件名 翻译 具体判断标准 不等于 NEQ Not Equal To Z = 0 等于 EQ Equal To Z = 1 大于 GT Greater Then Z = 0 AND N = 0 大于等于 GEQ Greater Then Or Equal To N = 0 小于 LT Less Then N = 1 小于等于 LEQ Less Then Or Equal To Z = 1 OR N = 1 高于 HI Higher C = 1 AND Z = 0 高于等于 HIS,C Higher Or Same, Carry Set C = 1 低于 LO,NC Lower, Carry Clear C = 0 低于等于 LOS Lower Or Same C = 0 OR Z = 1 未溢出 NOV No Overflow V = 0 溢出 OV Overflow V = 1 我不到啊 NTC Test Bit Not Set TC = 0 我不到啊 TC Test Bit Set TC = 1
自增(i++,这个不用背)
1
INC loc16 // 如 INC @2
汇编代码块
除法(需要记)
1
2
3
4
5
6
7
8
9
10
11/* 无符号数除法 */
/*
Num ÷ Den = Quot ... Rem
商: Quot = Num / Den
余数: Rem = Num % Den
*/
MOVU ACC, @Num // AH = 0, AL = Num,
RPT #15 // 16bit, Repeat 15 times
||SUBCU ACC, @Den // 条件减法
MOV @Rem, AH // 余数存在高位AH,移到Rem里
MOV @Quot, AL // 商存在低位AL,移到Quot里代码块的调用
1
2
3
4
5
6
7/* 代码块的调用——类似于C与Py的函数 */
TODO // PRE CODE
LC NAME // 调用, LC = CALL
TODO // POST CODE
NAME:
TODO // FUNCTION考试的代码块要求
1
2
3
4
5
6
7
8
9/* 考试的代码块要求 */
/*
1. 有标号,或者“函数名”
2. 以LRET结束
*/
/* 示例 */
TEST:
TODO // Code Here
LRET
汇编实例
例1
1 | /* |
例2
1 | /* |
例3
1 | /* |
分析:
*-SP[10]
给单元*(0x416)
赋值0x10
- 减法SUB得到
0x10 - 0x12 = -0x2 = 0xFFFE
,因此答案为:
- V保持不变,原来是1就是1,原来是0还是0
- 零位Z=0(因为结果不是0)
- 负位N=1,因为结果是负数(最高位是1就是负数)
- 进位/借位位C=0,减法C=0表示有借位
- 最终结果为
0xFEFF
,存放在0x416
的地址中
例4
1 | /* |
例5
1 | /* |
例6(这个太难了考试不会考的)
1 | /* |
连接命令文件CMD
连接文件的内容:把软件安排到硬件中去,用于控制程序文件中代码和数据输出段在存储器区域中的定位
MEMORY
- 划分程序页、数据页,一页可分为若干段
- 与存储器映射有关
- 调试程序放在RAM中(也可用FLASH,没学),有RESET(复位向量)项
1 | MEMORY{ |
SECTION
- 把程序中的段定位到硬件的段
- 程序段 .reset .text .cinit
- 数据段 .bss .ebss .stack
- 数据段根据要求增加
- 要求掌握安排变量到固定地址中,如GPIOF
.text:初始化段、所有可以执行的代码和常量、存储类型:ROM或RAM(FLASH)、Page0
- 常量例如define PI=3.14(也可以用立即数赋值,但不常用)
- .cinit:初始化段、全局变量和静态变量的C初始化记录、存储类型:ROM或RAM(FLASH)、Page0
- .stack:非初始化段、为系统堆栈保留的空间,主要用于和函数传递变量或位局部变量分配空间、存储类型:ROM或RAM(FLASH)、Page1
- .bss:非初始化段、为全局变量和局部变量保留的空间、存储类型:ROM或RAM(FLASH)、Page1,在程序上电时.cinit空间中的数据复制出来并存 储在.bss空间中。分配范围被限制在低64K 16位数据区 位
- .ebss:为使用大寄存器模式时的全局变量和静态变量预留的空间。分配范围为4M 22位数据区 位 (分配范围不同于寻址方式相关)
- .cinit、.bss、.ebss:三者只与C相关,用汇编时不需要
1 | SECTIONS{ |
一个简单的例子
1 | MEMORY{ |
流程
1 | /* .c */ |
示例:
1 | /* .c */ |