0%

微原与汇编

微机原理与汇编语言难舍难分,某乎上建议先把汇编过一遍的同时学习微原效果非常好,再加上本人的确有深入理解计算机系统的需求,所以开了这篇博客。

本篇博客随着学校进度更新。

1 前言

基于8086/8088的汇编语言+微机原理

参考教材:

①《微型计算机原理及技术接口第三版》-裘雪红 车向泉 西电出版社

②《汇编语言第四版》-王爽 清华大学出版社

③《CS:APP》-机械工业出版社

2 Intel单核处理器(8086-16位)

时代进步很快,RISC,CISC已经非常流行,但是为什么要学8086?

个人认为单核是多核的基础,从8086 CPU开始,Intel CPU设计采用了向后兼容(backward compatibility,也称作向下兼容Downward Compatibility)的特性。8086是Intel CPU的基石,非常经典,8086的设计思想不可或缺。

8086是16位结构,16根数据线,一次最多传1个字,用mov指令给出16位寄存器就可以进行16位数据传送。

2.1.1 8086/8088功能特性

①CPU内部有1416位的程序员可见的寄存器

②支持多种寻址方式;不支持浮点数运算。

③指令的操作数可以是16位的,也可以是8位的。(类似32位的程序也可以在64位上跑)

④独立编址(*RISC是统一编址)

⑤内存、接口按字节(Byte)编址。

⑥内存地址空间分段,可寻址1MB;接口地址空间不分段,可寻址64KB。

2.1.2 8086功能特性(与上面区分,算是上面的一个子集)

①CPU内部有1416位的程序员可见的寄存器

②支持多种寻址方式(24种);不支持浮点数运算。

③操作数类型:位(bit)、字节(byte)、字(word)和块(block)。

8、16位无符号和带符号二进制或十进制运算,包括乘法和除法。

⑤体系结构是针对强大的汇编语言和有效的高级语言设计的。

⑥直接主存寻址能力1MB。

8086与8088区别

8086(标准16位微处理器) 8088(准16位微处理器)
内部数据总线 16位 16位
外部数据总线 16位 8位
推出时间 1978年 1978年
地址位数 20位 20位
寻址空间 1MB 1MB
指令就绪队列 6 字节 4 字节

2.1.3 8086体系结构

CPU对内存读写

㈠读:

①CPU通过地址总线向内存发出地址信息

②CPU通过控制总线向内存发出命令,选中内存某个芯片,告诉他:我CPU要读你的数据

③内存将地址信息里面的数据通过数据总线传给CPU

㈡写:

①CPU通过地址总线向内存发出地址信息

②CPU通过控制总线向内存发出命令,选中内存某个芯片,告诉他:我CPU要给你写数据

③CPU将数据通过数据总线传给内存对应地址的存储单元

注意

CPU访问存储器时,地址总线AB上送出的是物理地址,编程时采用的则是逻辑地址

微处理器执行程序的步骤

  1. 从内存储器(Cache)中取出一条指令,分析指令操作码
  2. 从内存储器(Cache)或寄存器中获取数据操作数
  3. 执行指令
  4. 将结果存入内存储器(Cache)或寄存器中

微处理器在取操作码、存取操作数时要占用总线,而分析操作码和执行指令时不占用总线,因此在执行指令的过程中,总线会出现空闲时间,而不能被充分利用。

为了能充分利用总线,8086/8088微处理器的内部结构从功能上被设计为两个独立的功能部件:总线接口单元BIU执行单元EU

内部组成1-总线接口单元BIU(Bus Interface Unit,取指令)

负责与存储器、I/O接口传递数据,具体完成:

①从内存取指令,送到指令队列

②配合EU从指定的内存单元或IO端口取数据

③将EU的操作结果送到指定的内存单元或IO端口

常见IO接口

VGA(Video Graphics Array即视频图形阵列),USB,标准视频输入接口,音频输入输出接口。

内部组成2-执行单元EU(Execute Unit,执行指令)

负责指令的执行,(算术、逻辑、移位运算,有效地址计算,控制命令、……)

工作原理

①BIU取指令,EU执行指令,二者相互独立相互配合

②指令队列有2+空字节,BIU自动取指 → 队列

③EU总是从队列前部取指令去执行

④指令需要访问M或I/O,EU会请求BIU去完成

8086寄存器结构

内存中字的存储

CPU用16位1个字,高8位高字节,低8位低字节;内存中一个内存单元就是一个字节,占8位,所以一个字要两个内存单元。

N地址字单元:N和N+1内存单元组成的地址,注意:高地址在前

数据寄存器

8086有4个16位的数据寄存器:AX,BX,CX,DX。每个寄存器又可以分为高、低字节独立的2个8位寄存器:高8位AH(High),低8位AL(Low)。

位bit,字节byte=8bit,字word=2byte

同时这4个数据寄存器有特殊用途:

寄存器 特殊用途
AX 字节乘(乘积),字节除(被除数),字IO,字乘法(乘数/乘积L),字除法(被除数L/商)
AL 字节乘(乘数),字节除(商),字节IO,十进制运算校正
BX 段内偏移地址
CX 串操作计数,循环计数(loop),存储程序字节数(王爽实验4-3)
CL 移位次数
DX 字乘法(乘积H),字除法(被除数H),IO地址

A:Accumulator,又叫累加器

B:Base,基址寄存器,可当作指针,可以用来寄存器间接寻址:[BX],后面汇编语言中如果写MOV CL,[BX]是对的,但是如果写MOV CL,[AX]或者CX,DX都不行!

C:Count,计数/移位。移位次数大于1的默认存在CX中。

指针寄存器2个

SP:堆栈指针寄存器,存放堆栈区偏移地址,指示堆栈的当前操作位置

【注意】堆栈主要用于 保护断点和现场
①暂存数据
②为程序保存提供返回地址:子程序调用CALL指令用堆栈保存程序返回地址,子程序返回指令RET从堆栈取出返回地址。
③对于堆栈区域中存储单元的寻址是一种特殊的【存储器操作数】的寻址方式,它利用两个寄存器访问:堆栈指针SP和堆栈段寄存器SS;

BP:基数指针寄存器,存放主存基本偏移地址

变址寄存器2个

SI:源变址寄存器,指向源操作数

DI:目的变址寄存器,指向目的操作数

SI,DI可自动修改内容

访问数据段能当指针的有:SI,DI,BX

控制寄存器2个

IP:指令指针寄存器,指示当前指令所在存储单元的段内偏移地址。每当8086依据CS和IP从主存取得一个指令字节后,IP自动+1,指向下一个要读取的指令字节。

PSW(背):程序状态字,也叫状态寄存器或标志寄存器,共16位,暂定义9个标志位,存放8086CPU在工作中的状态信息。清零和置1要记住

会使用和编程即可,FLAGS/PSW可以不用记

(记法:CPAZ,STIDO)

FLAGS/PSW 含义
AF(8086) 辅助进位标志位:低4位向高4位如果有进位,AF置1,做BCD加法运算时判断是否修正
CF 进位/借位标志位:无符号数加减有进位或借位时置1
PF 奇偶标志位
SF 符号标志位:对应运算结果最高位
ZF 0标志位:运算结果为0置1
DF(8088)
IF
OF 溢出标志位
TF

段寄存器

通用用途:

8086CPU在访问内存时由这4个段寄存器提供内存单元的段地址不同种类的段寄存器存储不同属性段的段地址,与段内偏移地址一起确定主存的物理地址(后面会讲)。

通过四个段寄存器,CPU每次可同时对4个段进行寻址,即可同时提供容量达64KB的程序区(CS),64KB的堆栈区(SS)、128KB的数据区(DS & ES)。

CS:指示程序区,存储段高16位地址,剩下4位默认为0

DS:指示数据区

ES:指示数据区(附加段)

SS:指示堆栈区,8086栈顶对应小地址

8086不支持将数据直接送入段寄存器,比如你直接写MOV DS 1000H是非法的,要想将数据送入DS,只能先将数据送入一个一般的寄存器比如BX,再通过BX把内容送入DS。至于说为什么这样干,我们不必深究,知道有这么一回事就行。

👋拓展:DS和[address]

DS:存放要访问的数据的段地址

比如我们要访问10000H单元的内容,可以这样:

1
2
3
mov bx,1000H
mov ds,bx
mov al,[0]

第三条指令作用:将内存单元中的内容送入寄存器中

[]表示一个内存单元,表示内存单元的偏移地址,段地址怎么办?8086CPU自动取DS中的数据作为段地址。

[BX]:偏移地址BX中的内容

1
2
3
DS<=====>BX,SI,DI
CS<=====>IP
SS<=====>SP,BP

8086主存储器和IO结构

🧐系统讲解此部分知识前,先弄懂几个概念

①16位结构的CPU(16位机,字长为16位)

运算器一次最多处理16位数据;寄存器最大宽度16位;寄存器和运算器之间通路是16位。

内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂存。

注意:8086是16位CPU不代表传送地址是16位!

②8086给地址的方法

8086CPU有20位地址总线,即寻址能力为。但是在其内部一次性处理、传输、暂存的地址是16位(寄存器只有16位),如果将地址从内部简单发出,那就是16位的地址,此时表现的寻址能力为。所以8086有一种给出20位地址的方法。

这种寻址方式的本质是:物理地址=基础地址+偏移地址的一种实现。

段基址:段寄存器保存的高16位(段的首地址的16位地址)

段内偏移地址:段中某存储单元 “相对于段基址的偏移量”

段地址由16位寄存器提供,段内地址偏移由IP或EU确定的有效地址(EA)提供。

是二进制数左移4位,16进制数左移一位,末位补0。具体的计组都学过,不再阐述。

③段的概念

🤡这个概念挺会混淆视听的,内存压根没有分段这一说,如果在开始有这样的错误认知,会影响后续的学习。

✍段的划分实际上来源于CPU。比如:我们可以认为地址10000H-100FFH的内存单元算一个段,段起始地址为10000H,段地址为1000H,段的大小为100H;当然我们也可以认为地址10000H-1007FH、10080H-100FFH的内存单元是两个段,段的大小都是80H。编程时根据需要,将若干地址连续看成一个段,就是逻辑段段地址作为段的起始地址,所以段的起始地址一定是16的倍数(后4位是0);偏移地址定位段中的内存单元,为16位,所以段长度最大为64KB,也可以说:仅用偏移地址来寻址最多寻64KB个内存单元,范围是:0-FFFFH

8086系统将1MB存储空间分为若干存储段,每段固定大小64KB,正是8086这样的设计,才有主存储器的IO空间的特殊结构。8086支持20位地址,总共1MB内存,但只能使用640KB,因为其余的必须保留,供系统使用。(见下图)

8086的RESET叫复位电路,高电平有效,通电后给它大于4个时钟周期的正脉冲,将CS初始化为FFFFH,IP初始化为0000H,所以第一条指令从FFFF0开始取,此处放的是跳转指令,跳到BIOS,然后从小地址到大地之执行。

《汇编语言》实验 查看CPU和内存,用机器指令和汇编指令编程-总结

1
2
3
4
5
6
7
8
9
10
11
12
#预备知识
debug的使用
#实验要用指令#
r:查看CPU中寄存器的内容
rcs:查看并修改cs内容
rip:查看并修改ip内容
d:查看内存的内容
e cs:ip xx xx xx ......:修改内存中的内容
a cs:ip :以汇编指令的形式向内存写入指令
u:将内存中的内容解释为机器指令和对应的汇编指令
p:遇到loop命令时一口气走完循环
g 地址:直接执行到CS:地址处

1.主存和IO地址空间大小与分体结构

偶地址空间对应低字节的访问,来选择

奇地址空间对应高字节的访问,来选择

🧐为何不用代替?

🧑因为8086这两个体可能同时要读地址,即可能需要同时有效(低电平),一个负责低8位,一个负责高8位。

✋✋8086分段存储非常重要,贯穿全书

2.主存分段与段寄存器的使用

分段结构优势:

①代码、数据量不大 → 使其处于同一段内(64KB范围内)→ 可减少指令长度、提高运行速度。

②内存分段为程序的浮动分配创造了条件。

③各个分段之间可以重叠。

下面结合例题说明

第一问,CS与IP配合做PC的功能,即:任意时刻CPU将CS:IP指向的内容当作指令执行。FFFF0H单元中的指令是8086开机执行的第一条指令。其余略。

第二问,“由该物理地址决定的逻辑地址是什么?”,这句话就让你求CS和IP(段地址+偏移量),在JMP的前一个指令执行完成后,逻辑地址加载到CS和IP中,例如选择逻辑地址为2000H:0100H,则JMP执行后,CS=2000H,IP=0100H。JMP指令执行前后CS发生改变,故该程序实现了段间转移。一般来说,大约有0x1000(4096)对不同的CS:IP地址组合能指向同一个内存地址。

显示缓冲区就是个例子

老师上课时候提到了JMP指令,并且拓展延申,这里就直接学了(以后也得学):

拓展:转移指令(王爽汇编第9章)

转移指令是:修改CS和IP的指令,也可以说是控制CPU执行内存中某处代码的指令。

offset

取得标号的偏移地址

1
2
start:mov ax,offset start ;相当于mov ax,0
s:mov ax,offset s ;相当于mov ax,3
依据位移进行转移的jmp(修改IP)
1
2
3
4
jmp short xxx(段内短转移)8位:-128~127:最多向高地址移动127位,或向低地址移动128位
IP = IP + 8
jmp near ptr xxx(段内近转移)16位:-32768~32767
IP = IP + 16

这两条指令对应的机器指令并没有转移的目的地址,而是相对于当前IP的转移位移。

转移的目的地址在指令中的jmp(修改CS和IP)
1
jmp far ptr xxx(段间转移;远转移)
转移地址在寄存器中的jmp
1
2
jmp 16位REG
jmp ax <=====> mov IP,ax
转移地址在内存中的jmp
1
2
3
4
5
6
7
jmp word ptr 内存地址单元 (======>段内转移)
#从内存单元地址处存放一个字,是转移的目的偏移地址
jmp dword ptr 内存地址单元(======>段间转移)
#从内存单元地址处存放两个字
#高地址是转移的目的段地址
#低地址是转移的段内偏移地址
#即:CS=内存单元地址+2;IP=内存单元地址
jcxz指令

Jump if CX equals Zero

1
if((cx)==0) jmp short xxx
loop指令
1
2
(cx)--;#所以在汇编程序里面一定有一条和inc cx或dec cx相关的指令
if((cx)!=0) jmp short xxx;

在任何模式下,LOOPD指令都使用ECX作为循环计数器;LOOPW都使用CX作为循环计数器。

例程1:整数数组求和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; This program sums an array of words. (SumArray.asm)
.386
.MODEL flat, stdcall
.STACK 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
intarray WORD 100h,200h,300h,400h
.code
main PROC
mov edi,OFFSET intarray ; address of intarray
mov ecx,LENGTHOF intarray ; loop counter
mov ax,0 ; zero the accumulator
L1: add ax,[edi] ; add an integer
add edi,TYPE intarray ; point to next integer
loop L1 ;repeat until ECX = 0
INVOKE ExitProcess,0
main ENDP
END main
实验8

分析下面的程序,在运行前仔细思考:这个程序可以正确的返回吗?运行后在思考:为什么是这样的结果?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
assueme cs:codesg
codesg segment
mov ax,4c00h
int 21h
start: mov ax,0
s: nop
nop

mov di,offset s
mov si,offset s2
mov ax,cs:[si]
mov cs:[di],ax

s0: jmp short s

s1: mov ax,0
int 21h
mov ax,0

s2: jmp short s1
nop
codesg ends
end start

先来看一下程序的执行流程

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
 1.start: mov ax,0
2.s:nop; nop标号语句,在运行时在代码段中分配一个字节的空间,
3.nop; 这个字节(空间)的值为90h。
; 操作符 `offset` 的功能是取得标号的偏移地址。
4.mov di,offset s ; 将s的偏移地址存到di寄存器中
5.mov si,offset s2; 将s2的偏移地址存到si寄存器中
6.mov ax,cs:[si];此行是将cs:[si]内存中的机器码存到ax寄存器中,这个机器码是由编译器将s2标号字段中的指令编译而成。
7.mov cs:[di],ax;将ax中的s2标号字段的机器码存放到s标号字段中,覆盖掉2个NOP。

8.s0: jmp short s ; 跳转到 s 标号字段处执行代码,首先执行s起始处的EBF6,程序往前跳三条指令,依次执行mov ax,4c00h int 21h,程序正常结束,退出。

9.s: jmp short s1 ; 根据我们之前的分析, 指令是用相对偏移来表示的
; 因此执行的操作并不是真的跳转到 s1 这个标号,
; 而是跳转编译时确定的 该指令到 s1 标号的偏移量。
; 所以我们要分析接下来程序的流程的话 , 就必须先编译程序 ,
; 通过查看这条指令的机器代码,才知道偏移量是多少。
; 然后再根据这个偏移量确定程序下一步应该执行哪里的指令。
; 根据编译结果,可以发现 ,
; jmp short s1 在编译后得到的指令是 : EB F6
; 由上可知,偏移量是:F6
; 偏移量是由 补码 来表示的
; 我们可以算出 F6对应的有符号十进制数为 -10。
; 从这里,我们可以知道,这条指令是将 ip 的值加上 -10。
; 那么,我们再看看 ip - 10 指向的地址是哪里呢 ?
; 由编译结果,我们可以知道,
; 它指向的刚好就是 code segment 开始的位置.

10. mov ax,4c00h
11. int 21h ;看到这两句,大家就知道,程序是可以正常返回了

注意👋使用debug的u命令时要加参数:

1
-u 0

否则默认反汇编从start标号处开始。

红线就是jmp short s1对应的机器码,偏移量是F6,对应有符号10进制数就是-10,所以要找到就是ip-10,指向的地址是codesg。

2.1.4 8086芯片引脚(背,内容很多,具体看书P29,30,31)

(1)两种模式下共用信号

(2)仅在最小模式下使用的信号

(3)仅在最大模式下使用的信号

2.1.5 工作时序

时钟周期:CPU处理动作的最小时间单位,是 CPU 主频的倒数。

计组学过指令周期:取指+执行用的时间,但是8086取指和执行是重叠进行的,所以引出总线周期

总线周期:CPU通过系统总线进行存储器读写、I/O读写、中断响应所需时间。8086执行程序的过程就是若干个总线周期实现过程,许多操作都要用到系统总线,所以也叫总线操作。

正常情况下8086CPU一个总线周期由4个时钟周期组成,CPU速度高于主存或IO设备时,要在正常的的总线周期中插入等待时间

必须掌握典型8086/8088时序分析

T1:送地址地址锁存(ALE高电平)要访问主存(IO/读主存(DT/

:CPU读取数据

:低电平有效,传输数据

T3:纯等待,主存从给出的地址选中单元需要时间,判断Ready

Ready:由慢速内存或接口或控制电路产生

T4:读数据,上升沿获取数据

2.1.6 系统总线形成(要会做题)

使用器件:

1.单向总线

单向三态门驱动器74LS244

2.双向总线

①8286

②8287(带反向)

③74LS245

3.带有三态输出的锁存器

①8282

②8283(带反向)

③74LS373、74LS374

驱动器/缓冲器:增加驱动能力、减轻负载。

💪扇出能力大

👍延时小

驱动的输入与输出是一致的,只有当总电流大于各个支路电流之和才能起到驱动的效果,即:

微融合与宏融合

微融合(Micro-Fusion):将来自同一指令的多个微操作融合为单一的复杂微操作。如果不是被微融合,这些微操作会被多次发送到乱序执行核中。

宏融合(Macro-Fusion):让处理器在译码的同时,将同类的指令融合为单一的指令,以减少处理的指令数量,让处理器在更短的时间内处理更多的指令。

拓展:虚拟化技术

👦用虚拟机那么长时间一直都没了解虚拟化技术,好惭愧,先挖个坑,后续打完国赛补上,多少得懂一点。

==更新==

已经在写了

第3章前置技能-汇编程序🤛

源程序从写出到执行

1.编写

产生了存储源程序的文本文件

2.编译连接

编译:对源程序文件中的源程序编译目标文件

连接:目标文件在OS中可以直接运行的可执行文件

可执行文件包含2部分:

①程序(从源程序中的汇编指令翻译过来的机器码)和数据

②相关描述信息(eg:程序占了多大内存空间等等)

3.执行可执行文件中的程序

在OS中开始执行,将可执行文件中的机器码和数据加载内存,并进行相关初始化,然后由CPU来执行。

源程序

以👇这个为例

1
2
3
4
5
6
7
8
9
10
11
12
13
assume cs:codesg

codesg segment
mov ax,0123H
mov bx,0456H
add ax,bx
add ax,ax

mov ax,4c00H
int 21H
codesg ends

end

1.伪指令

汇编语言包含2种指令:

①汇编指令:有对应机器码的指令,可以被编译为机器指令,最终被CPU执行

②伪指令:没有对应机器码指令,是被编译器执行的指令

👆的程序出现了3种伪指令:

1.assume

作用:将有特定用途的段和相应的段寄存器关联起来

2.segment-ends

成对使用,segment段的开始,ends段的结束

3.end

汇编语言结束标记,遇见end结束对源程序的编译

注意👋ends与end区分

2.源程序中的“程序”

程序是源程序中最终由计算机执行、处理的指令或数据。

3.标号

一个标号指代一个地址,比如codesg在segment前面,作为一个段的名称,这个段的名称最终被编译、连接程序处理为一个段的段地址。

4.程序结构

源程序是由一个一个一个组成的

5.程序返回

我们在DOS(一个单任务操作系统)的基础上讲解。

一个程序P2在可执行文件中必须有个正在运行的程序P1

​ ①将P2从可执行文件中加载进内存后;

​ ②将CPU控制权交给P2,P2才开始运行,此时P1暂停;

​ ③P2运行完,将CPU控制权交给P1,P1继续运行。

所以✍程序返回:一个程序结束后,将CPU的控制权交还给使得它运行的程序

1
2
mov ax,4c00H
int 21H

这两条指令实现的功能就是程序返回,一共占5字节。

实战编写运行汇编语言

1.实战环境

ubuntu20.04 DOSBOX

自动挂载虚拟硬盘(搜索引擎有)

2.步骤

①编辑源程序

在挂载硬盘的文件夹查看文件是否齐全:dir命令;然后在具有MASM,ASM,File的文件夹的终端gedit hello.asm,输入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data segment ;数据段
string db 'Hello,World!$'
data ends
code segment ;代码段
assume cs:code,ds:data
start:
mov ax,data ;获取段基址
mov ds,ax ;将段基址送入寄存器
mov dx,offset string
mov ah,9
int 21h
mov ah,4ch
int 21h
code ends
end start

②编译:masm hello.asm+3个回车

③连接:link hello.obj+3个回车

④运行:hello.exe

DOS中有一个程序叫command.com,就是DOS系统的shell

3.程序执行过程的跟踪

1
debug filename.exe

这里讲解一下exe文件中程序的加载过程

🧐🤨SA+10H是怎么来的?

👋✍PSP占256字节,程序开始地址为:SA16+256+0(SA+16)16+0,16转为10H,就得到了SA+10H

DOSBOX加载1.exe时顺序:

①command加载debug

②debug加载1.exe

返回时顺序:

①1.exedebug

②debugcommand

寄存器组的环境

4.汇编基础

(1)操作数

立即操作数(immediate)

imm:8、16或32位立即数

imm8:8位立即数(字节)

imm16:16位立即数(字)eg:short

imm32:32位立即数(双字) eg:int

寄存器操作数

reg:任意的通用寄存器

sreg:16位段寄存器CS、DS、SS、ES、FS、GS

r8:AH、AL、BH、BL、CH、CL、DH、DL

r16:AX、BX、CX、DX、SI、DI、SP、BP

r32:EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP

内存操作数

mem:8、16或32位

其他约定

r/m8:8位操作数(8位通用寄存器或内存字节)

r/m16:16位操作数(16位通用寄存器或内存字)

r/m32:32位操作数(32位通用寄存器或内存双字)

(2)整数表达式
(3)字符、字符串常量

用’ ‘括起来

(4)保留字

有特殊含义,只能用于正确的上下文环境中

例如@data编译时返回整数常量值

(5)标识符

尽量避免用“@”和“_”作为第一个字符,因为它们即用于汇编器,也用于高级语言编译器。

(6)定义数据

(7)符号常量

不占用任何实际存储空间

但是下面的例子要懂:

1
2
3
4
5
6
7
8
9
list1 BYTE 10,20,30,40
ListqSize = ($ - list1)
;结构如下
+--------+
10 <===1000,list1
20 <===1001
30 <===1002
40 <===1003
?? <===1004,$所在
1
2
list2 WORD 1000h,2000h,3000h,4000h
List2Size = ($ - list2)/2;结果是4
(8)例程1——加减法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.MODEL small,stdcall
.386
DumpRegs PROTO
.STACK 4096
.data
val1 DWORD 10000h
val2 DWORD 40000h
val3 DWORD 20000h
finalVal DWORD ?
.code
main PROC
mov ax,@data ; data seg address
mov ds,ax ; copy to DS
mov es,ax ; copy to ES
mov eax,val1 ; EAX = 10000h
add eax,val2 ; EAX = 50000h
sub eax,val3 ; EAX = 30000h
mov finalVal,eax ; store the result
call DumpRegs ; display registers
mov ah,4Ch ; exit process
mov al,0 ; return code = 0
int 21h ; call MS-DOS function
main ENDP
END main

中断

中断分为内中断和外中断,内中断来源于CPU内部,外中断来源于CPU外部。

如何理解中断

任何一个通用的CPU,比如8086,都具备这样一种能力:可以在执行完当前正在执行的指令后,检测到从CPU外部发送过来的或内部的一种特殊信息,并且可以立即对所接收到的信息进行处理。

这样特殊的信息我们起个名,叫中断

中断就是:CPU不再接着刚执行完的指令向下执行,而是转去处理这个特殊信息。

举个例子:你作业写到一半,你妈妈叫你吃饭,然后你就放下作业去吃饭,吃完饭后继续写作业,这个就是外中断;你作业写到一半,饿了,自己点了外卖,就是内中断。

8086用中断类型码的数据来标识中断信息的来源,中断信息码是一个字节型数据(byte),一共可以表示256种中断信息来源,所以我们将产生中断信息的事件,即中断信息的来源,简称为中断源

内中断(异常、例外、陷阱)

当 8086CPU 内部有下面的情况发生的时候,将会产生相应的中断信息。

  • 除法错误,比如,执行 div 指令产生的除法溢出;==>0
  • 单步执行;==>1
  • 执行 into 指令;==>4
  • 执行 int 指令,格式为 int n,指令中 n 为字节型立即数,即为中断类型码。

外中断(中断)

PC系统的接口卡和主板上,装有各种接口芯片,这些外设接口芯片内部有若干寄存器,CPU将这些寄存器当作端口来访问。

外设的输入通过端口送入相关接口芯片的端口中;CPU向外设的输入也是先送入端口中,再由对应的芯片送入外设。

即:CPU通过端口和外部设备进行联系。

✍一般CPU设有两个中断请求输入引脚:可屏蔽中断请求输入引脚和不可屏蔽中断请求输入引脚。

中断处理过程

  1. 中断请求
  2. 中断判优
  3. 中断响应
  4. 中断服务
  5. 中断返回
1.中断请求

内中断或外中断,每个中断源都有一个中断请求触发器,锁存自己的中断请求信号,并保持到CPU响应这个中断请求后才将其清除。

CPU内部还有一个中断允许触发器,置1时允许CPU响应中断称为开中断,置0时屏蔽中断,即不允许中断,被称为关中断

2.中断判优

多个中断源同时向CPU提出中断请求时,CPU必须找出中断优先级最高的中断源,这个过程叫中断判优,可以采用硬件方法或者软件方法。

3.中断响应

中断响应时,CPU向中断源发出中断响应信号

4.中断服务

①保护现场:在中断服务程序的起始部分安排若干条入栈指令,将各寄存器的内容压入堆栈保存。

②开中断:在中断服务程序执行期间允许级别更高的中断请求中断现 行的中断服务程序,实现中断嵌套。

③中断服务:完成中断源的具体要求。

④恢复现场:通常是将保存在堆栈中的现场信息弹出到原来的寄存器中。

⑤中断返回:返回到原程序的断点处,继续执行原程序。

5.中断返回

返回到原程序的断点处,恢复硬件现场,继续执行原程序。

3 Intel指令系统与程序设计

16位寻址方式

8086中,只有这四个寄存器可以用在”[…]”中来进行内存单元的寻址。

这4个寄存器可单独行动,或以4种组合出现:

①bx+si(+idata)

②bx+di(+idata)

③bp+si(+idata)

④bp+di(+idata)

西电计科微原讲的寻址方式如下:

数据传送指令

1.MOV

1
mov destination,source

✍注意事项

两个操作数的尺寸必须一致。

两个操作数不能同时为内存操作数

目的操作数不能是CS,EIP和IP

立即数不能直接送至段寄存器

2.MOVZX

整数的零/符号拓展:

move with zero-extened

move with sigh-extened

1
2
3
4
5
6
7
格式:
movzx r32,r/m8
movzx r32,r/m16
movzx r16,r/m8
movsx r32,r/m8
movsx r32,r/m16
movsx r16,r/m8

3.XCHG指令

exchange data——交换两个操作数内容

1
2
3
4
格式
xchg reg,reg
xchg reg,mem
xchg mem,reg

交换两个内存操作数:用寄存器,MOV和XCHG结合使用

1
2
3
mov ax,val1
xchg ax,val2
mov val1,ax

4.直接偏移操作数(直接寻址)

1
2
3
4
5
6
7
8
9
.data
arrayB BYTE 10h,20h,30h,40h,50h
.code
mov ax,@data
mov ds,ax ; 16位汇编
mov al,arrayB ; AL=10h
mov al,[arrayB+1] ; AL=20h
mov al,[arrayB+2] ; AL=30h
mov al,[arrayB+20] ; AL=??

👋注意:

MASM并不要求一定要使用方括号。

MASM对有效地址没有内建的范围检查模块。

加法和减法指令

1.INC和DEC指令

这两条指令不影响进位标志

1
2
3
4
5
6
.data
myWord WORD 1000h
.code
inc myWord ; 1001h
mov bx,myWord
dec bx ; 1000h

2.ADD指令

注意同尺寸即可,不改变源操作数

3.SUB指令

不改变源操作数

影响的标志位有:

CF、ZF、SF、OF、AF、PF

4.NEG指令(negate)

将操作数按位取反,末位加1,即求补码

eg:

1
2
neg 0D8h
;结果是28h

影响的标志位有:

CF、ZF、SF、OF、AF、PF

5.NOT指令

将操作数按位取反,即求反码,不影响任何状态标志

例程—实现算数表达式

1
2
3
4
5
6
7
8
9
10
11
12
.data
Rval SDWORD ?
Xval SDWORD 26
Yval SDWORD 30
Zval SDWORD 40
.code
mov eax,Xval
neg eax ; EAX = -26
mov ebx,Yval
sub ebx,Zval ; EBX = -10
add eax,ebx
mov Rval,eax ; -36

和数据相关操作符和伪指令

1.OFFSET操作符

是由编译器处理的符号,它的功能是取得标号的偏移地址。

2.PTR操作符

用来重载操作数的默认尺寸。

3.TYPE操作数

返回按字节计算的变量的单个元素的大小。

4.LENGTHOF操作符

计算数组中元素的个数。

👋与sizeof区分

拓展✍C语言中sizeof详解:

sizeof是操作符operator而不是函数,它以字节形式给出了其操作数的存储大小

1
2
3
4
5
6
int i;  
sizeof(int); //值为4
sizeof(i); //值为4,等价于sizeof(int)
sizeof i; //值为4
sizeof(2); //值为4,等价于sizeof(int),因为2的类型为int
sizeof(2 + 3.14); //值为8,等价于sizeof(double),因为此表达式的结果的类型为double

5.SIZEOF操作符

本质就是存储数据的大小

call和ret指令

call

主体思想:(不是朝鲜那个!🤣🤣)

①将call指令的下一条指令的CS和IP入栈(段间CS和IP,段内IP)

②操作与call对应的jmp指令

✍要运用push和pop的原理!

根据位移转移的call

1
2
3
4
call 标号
<=======>
push IP
jmp near ptr 标号

检测点10.2

1
2
3
4
5
内存地址        机器码         汇编指令
1000:0 b8 00 00 mov ax, 0
1000:3 e8 01 00 call s
1000:6 40 inc ax
1000:7 58 s: pop ax

1.push IP:

IP计算方式:

①call指令的下一条指令的IP 或者

②call指令的IP是3,call指令占3个字节,3+3=6,就是IP

这两种计算方式都可以

2.jmp near ptr s

将栈的元素弹出,是IP,也就是6,所以ax=6

注意:跳过去不能再跳回来!有同学还加了1,我不说是谁🤡

转移地址在指令中的call

1
2
3
4
5
call far ptr(段间转移)
<=======>
push CS
push IP
jmp far ptr 标号

检测点:ax值是多少?1010h

1
2
3
4
5
6
7
8
内存地址       机器码              汇编指令
1000:0 b8 00 00 mov ax, 0
1000:3 9A 09 00 00 10 call far ptr s ; 1) push cs, push ip; 2) (cs) = 1000h, (ip) = 8
1000:8 40 inc ax
1000:9 58 s: pop ax ; (ax) = 8
add ax, ax ; (ax) = 10h
pop bx ; (bx) = 1000h
add ax, bx ; (ax) = 1010h

转移地址在寄存器中的call

1
2
3
4
call 16位reg
<=======>
push IP
jmp 16位 reg

检测点:ax值?0Bh

1
2
3
4
5
6
内存地址       机器码              汇编指令
1000:0 b8 06 00 mov ax, 6 ; (ax) = 6
1000:3 ff d0 call ax ; 1) push ip ---> ((ss):[(sp)-2]) = 5 ; 2) (ip) = 5
1000:5 40 inc ax
1000:6 mov bp, sp
add ax, [bp] ; (ax) = (ax) + (ip) = 6 + 5 = 0Bh

转移地址在内存中的call

1
2
3
4
call word ptr 内存单元地址
<=======>
push IP
jmp word ptr 内存单元地址

例题:

1
2
3
4
5
mov sp,10h            ;sp = 10h
mov ax,0123h ;ax = 0123h
mov ds:[0],ax ;ds:[0]内存地址内容 = 0123h
call word ptr ds:[0] ;1.push IP,怎么算IP?-->push原理: ss:[sp-2] = ss:0Eh = call指令的下一条指令的字节
;2.IP = ds:[0] = 0123h

IP = 0123h,sp = 0Eh

检测点:ax值?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code

stack segment
dw 8 dup(0)
stack ends

code segment
start: mov ax, stack
mov ss, ax
mov sp, 16
mov ds, ax
mov ax, 0
call word ptr ds:[0Eh] ; 1) push IP ---> ((ss):[0Eh]) = call指令后的第一个字节的地址;
; 2) (IP) = ((ds):[0Eh]) = ((ss):[0Eh]) = call指令后的第一个字节的地址.
inc ax ; (ax) = 1
inc ax ; (ax) = 2
inc ax ; (ax) = 3
mov ax, 4c00h
int 21h
code ends

end start

ret

用栈中的数据修改IP中的内容,实现近转移

1
2
IP = SS:SP
SP = SP+2

相当于

1
POP IP

retf

用栈中的数据修改CS和IP的内容,实现远转移

1
2
3
4
IP = SS:SP
SP = SP+2
CS = SS:SP
SP = SP+2

相当于

1
2
POP IP
POP CS
1
2
3
4
5
6
7
8
low  |_____________|
| |_____________|
| |_____________|
| |_____________|
| |_____________|
| |_____________|
V |_____CS______|
high |_____IP______|<===SP

乘法与除法指令

mul:无符号数乘法

32 位模式下,MUL(无符号数乘法)指令有三种类型:

  • 第一种执行 8 位操作数与 AL 寄存器的乘法;
  • 第二种执行 16 位操作数与 AX 寄存器的乘法;
  • 第三种执行 32 位操作数与 EAX 寄存器的乘法。

乘数和被乘数的大小必须保持一致,这三种类型都可以使用寄存器和内存操作数,但不能使用立即数。MUL指令不会发生溢出。

被乘数 乘数 乘积
AL reg/mem8 AX
AX reg/mem16 DX:AX
EAX reg/mem32 EDX:EAX

👋如果 DX或EDX 不等于零,则进位标志位置 1,这就意味着隐含的目的操作数的低半部分容纳不了整个乘积。

有个很好的理由要求在执行 MUL 后检查进位标志位,即,确认:忽略乘积的高半部分是否安全。

div:无符号数除法

32 位模式下,DIV(无符号数除法)指令有三种类型:

  • 第一种执行 8 位无符号数除法;
  • 第二种执行 16 位无符号数除法;
  • 第三种执行 32 位无符号数除法。
被除数 除数 余数
AX reg/mem8 AL AH
DX:AX reg/mem16 AX DX
EDX:EAX reg/mem32 EAX EDX

示例:

1.8位无符号除法

2.16位无符号除法

DX 包含的是被除数的高位部分,因此在执行 DIV 指令之前,必须将其清零

1
2
3
4
mov dx,0    ;清除被除数高16位
mov ax,8003h;被除数的低16位
mov cx,100h ;除数
div cx ;AX = 0080h, DX = 0003h

3.32位无符号除法

除数为内存操作数

1
2
3
4
5
6
7
.data
dividend QWORD 0000000800300020h
divisor DWORD 00000100h
.code
mov edx, DWORD PTR dividend + 4 ; 高双字
mov eax, DWORD PTR dividend ; 低双字
div divisor ; EAX = 08003000h, EDX = 00000020h

IMUL指令:有符号乘法

如果积的高半部分不是低半部分的符号扩展,则设置CF和OF。

IDIV指令:有符号除法

当执行8位数的除法指令之前,必须将被除数符号扩展到AH中(可用CBW指令)。

16位除法要求AX被符号扩展到DX中。

32位除法要求将EAX符号扩展到EDX中。

布尔和比较指令

AND指令(按位与

1
AND destination,source

结果保存在目的操作数

1
2
3
4
5
AND reg,reg
AND reg,mem
AND mem,reg
AND reg,imm
AND mem,imm

总是清除OFCF

用途举例:

1.特定位清0

2.小写转大写

例程:字符转换为大写

1
2
3
4
5
6
7
8
9
.data
array BYTE 50 DUP(?)
.code
mov ecx,LENGTHOF array
mov esi,OFFSET array
L1:
and byte ptr [esi],11011111b
inc esi
loop L1

OR指令(按位或

格式与AND相同

用途:

1.特定位置1

2.大写转小写

要掌握:

1
2
3
4
5
mov dl,5  ;二进制数
or dl,30h ;转换为ASCII码
;也可以写为
add dl,30h 或者
add dl,'0'

XOR指令(按位异或

用途:

1.对某些位取反,同时不影响其它位

2.判断16位或32位的奇偶性

1
2
mov ax , 64C1h
xor ah,al

3.简单数据加密

TEST指令

两操作数按位与,根据结果设置标志位但不回送结果,即不修改目的操作数。

用途:测试操作数某一位是0还是1

例子:想指定AL中第0位和第三位是否同时为0

1
test AL,00001001b

清除 OF、CF;修改 SF、ZF、PF。

CMP指令

1
cmp destination,source

无符号数比较:

有符号数的比较:

设置和清除单个CPU标志

1
2
3
4
5
6
7
8
9
10
11
12
stc	;设置进位标志
clc ;清除进位标志

and al,0 ;设置零标志
or al,1 ;清除零标志

or al,80h ;设置符号标志
and al,7Fh ;清除符号标志

mov al,7Fh ;AL=+127
inc al ;AL=80h(即-128),OF=1
or eax,0 ;清除溢出标志

条件跳转

按照以下条件可将跳转指令分成4组:

·根据特定的标志值。

·根据操作数之间是否相等,或根据(E)CX的值。

·根据无符号操作数的比较结果。A: Above B: Below

·根据有符号操作数的比较结果。G: Greater L: Less

例程1:比较 V1、V2、V3 三个无符号变量的值,将最小值送入AX寄存器。

1
2
3
4
5
6
7
8
9
10
11
12
13
.data
V1 WORD 23
V2 WORD 0
V3 WORD -1
.code
mov ax,V1 ;assume V1 is smallest
cmp ax,V2 ;if ax <= V2 then
jbe L1 ; jump to L1
mov ax,V2 ;else move V2 to ax
L1: cmp ax,V3 ;if ax <= V3 then
jbe L2 ; jump to L2
mov ax,V3 ;else move V3 to ax
L2: ……

移位和循环移位指令

逻辑移位和算数移位

SHL/R:逻辑左/右移

快速乘/除法

1
2
3
4
mov dl,5
shl dl,1 ;5*2 = 10
mov dl,10
shl dl,2 ;10*4 = 40
1
2
3
4
mov dl,32
shr dl,1 ;32/2 = 16
mov dl,01000000b ;AL = 64
shr dl,3 ;64/8 = 8

SAL/R:算数左/右移

1
2
3
mov al,0F0h		; AL = 11110000b (-16)
sar al,1 ; AL = 11111000b (-8)
; CF = 0
1
2
mov dl,-128		; DL = 10000000b
sar dl,3 ; DL = 11110000b

ROL:循环左移

特点:不丢失任何数据位

例:将一个字节的低4位和高4位交换

1
2
mov al,26h
rol al,4

ROR:循环右移

1
2
3
mov al,01h	;00000001
ror al,1 ;10000000, CF=1
ror al,1 ;01000000, CF=0

RCL/R(带进位的循环左/右移

例子:从进位标志中恢复一个位

1
2
3
4
5
6
.data
testval BYTE 01101010b
.code
shr testval,1 ;shift LSB into Carry flag
jc quit ;exit if Carry flag set
rcl testval,1 ;else restore the number

SHLD/SHRD

要求至少是I386处理器

SHLD:双精度左移

1
SHLD 目的操作数,源操作数,移位位数

SHRD:双精度右移

1
SHRD 目的操作数,源操作数,移位位数

间接寻址

1.间接操作数

可以是任何用方括号括起来的32位通用寄存器(EAX,EBX,ECX,EDX,ESI,EDI,EBP、ESP)。

实地址模式下只能用SI,DI,BX,BP。通常尽量避免使用BP(BP常用来寻址堆栈而不是数据段)。

✍实模式(real model):

又叫实地址模式(real address mode),是所有x86兼容CPU的一种操作模式,采用和8086相同的16位段和偏移量,最大寻址空间1MB,是CPU启动时的模式,相当于一个速度超快的8086。

在这个实模式下,可以运行DOS操作系统。著名的MSDOS是微软的DOS操作系统,微软用它赚了第一桶金。

用户写的代码中CS与IP寄存器组合成的地址即为实际的物理内存地址。这种方式非常的不安全,并且不方便实现多任务系统。在设计80286的时候要解决的主要就是这个问题。

✍保护模式(protected mode):

又叫保护地址模式(protected address mode),从80286开始,有了存储器分段管理部件,支持操作系统实现一种新的存储器管理模式,保护模式。

在80286中,段式访存得到的改进,原来段寄存器+IP得到的地址不在是实际的物理地址,而是要经过一个转换层转换才变成一个物理地址。CS里面的内存不再是20位物理地址的高16位,而变成了段描述符表中的索引。

2.数组

例程1:32位三个双字相加

1
2
3
4
5
6
7
8
9
.data
arrayD DWORD 10000h,20000h,30000h
.code
mov esi,OFFSET arrayD
mov eax,[esi]
add esi,4
add eax,[esi]
add esi,4
add eax,[esi]

例程2:16位三个双字相加

1
2
3
4
5
6
7
8
9
10
11
.data
arrayW WORD 1000h,2000h,3000h
.code
mov ax,@data
mov ds,ax
mov si,OFFSET arrayW
mov ax,[si]
add si,2
add ax,[si]
add si,2
add ax,[si]

3.变址操作数(寄存器相对寻址)

例程

1
2
3
4
5
6
7
8
9
10
.data
arrayB BYTE 10h,20h,30h
.code
mov esi,0
mov al,[arrayB + esi] ; AL = 10h
mov al,arrayB[esi] ; 同上,另一种格式
mov esi,OFFSET arrayB
mov al,[esi] ; AL = 10h
mov al,[esi+1] ; AL = 20h
mov al,[esi+2] ; AL = 30h

过程论

随着程序规模的增长,到了需要对程序进行逻辑划分并将其分为过程的时候了。

一.与外部库链接

二.堆栈操作

1
PUSH   POP

以32位为例

堆栈用途:

📒过程内的局部变量在堆栈上创建,过程结束时,这些变量被丢弃。

📓CALL指令执行时,CPU用堆栈保存当前过程的返回地址。

📚调用过程时,可通过堆栈传递参数。

📖寄存器在用作多种用途的时候,堆栈可方便地作为其临时保存区域;当寄存器使用完毕后,可从堆栈中恢复其原始值。

三.过程定义与使用

1.PROC伪指令

①过程定义

过程用PROC和ENDP伪指令来声明,另外还必须给过程起一个名字(一个有效的标识符,下面的就有main和sample)。

②例程:三个整数之和

1
2
3
4
5
SumOf PROC
add eax,ebx
add eax,ecx
ret
SumOf ENDP

③为过程添加文档

几点建议:

  • [ ] 过程完成的所有任务的描述。
  • [ ] 输入参数的清单及使用方法。
  • [ ] 过程返回值的描述。
  • [ ] 列出特殊要求,即调用过程之前必须满足的条件。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
;--------------------------------------------------------
SumOf PROC
;
; Calculates and returns the sum of three 32-bit integers.
; Receives: EAX, EBX, ECX, the three integers. May be
; signed or unsigned.
; Returns: EAX = sum, and the status flags (Carry,
; Overflow, etc.) are changed.
;--------------------------------------------------------
add eax,ebx
add eax,ecx
ret
SumOf ENDP
2.CALL和RET指令
1
2
3
4
5
6
main PROC
.
call MySub;00000020
mov eax,ebx;00000025
.
main ENDP
1
2
3
4
5
6
MySub PROC
mov eax,edx;00000040
.
.
ret
MySub ENDP

向过程传递寄存器参数例程:

1
2
3
4
5
6
7
8
9
.data
theSum DWORD ?
.code
main PROC
mov eax,10000h ; argument
mov ebx,20000h ; argument
mov ecx,30000h ; argument
call SumOf ; EAX=(EAX+EBX+ECX)
mov theSum,eax ; save the sum

例程—对整数数组求和并调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;-----------------------------------------------------
ArraySum PROC
;
; Calculates the sum of an array of 32-bit integers.
; Receives: ESI points to the array, ECX = array size
; Returns: EAX = sum of the array elements
;-----------------------------------------------------
push esi ; save ESI, ECX
push ecx
mov eax,0 ; set the sum to zero
L1:
add eax,[esi] ; add each integer to sum
add esi,4 ; point to next integer
loop L1 ; repeat for array size

pop ecx ; restore ECX, ESI
pop esi
ret ; sum is in EAX
ArraySum ENDP
1
2
3
4
5
6
7
8
9
10
.data
array DWORD 10000h,20000h,30000h,40000h,50000h
theSum DWORD ?
.code
main PROC
mov esi,OFFSET array ; ESI points to array
mov ecx,LENGTHOF array ; ECX = array count
call ArraySum ; calculate the sum
mov theSum,eax ; returned in EAX
......

四.使用过程进行程序设计

五.高级过程

堆栈参数

需要的参数由调用程序压入堆栈。

·PROTO伪指令

函数声明,为一个已存在的过程创建原型。原型声明了过程的名字和参数列表,允许一个过程在被定义之前就可以在其它地方被调用。

·ADDR运算符

可在使用INVOKE调用过程时传递指针(地址参数)。

·内存模式

①.MODEL伪指令

1
.MODEL 内存模式 [,模式选项]

例:.MODEL flat,stdcall保护模式程序使用平坦内存模式

②STDCALL关键字

指定过程按照从右到左的顺序压入参数。

👋STDCALL要求过程在RET指令后提供常量操作数。

寄存器参数

寄存器内容在装入参数前必须保存。

INVOKE指令

自动在堆栈上压入参数并调用程序。

使用invoke伪指令会帮你完成参数校检和压参操作,直接和高级语言一样直接调用函数即可。

👋几乎所有高级语言都使用堆栈参数

堆栈框架
1
LEA reg,mem

计算并装入内存操作数的偏移。

LOCAL伪指令在过程中声明一个或多个局部变量。

六.结构与宏

结构(Structure)

结构:逻辑上相互关联的一组变量的模板或模式。

结构中单个变量称为域(field)。

域的初始化:

·未定义:使用“?”

·字符串:引号引起的字符初始化字符串域

·整数:整数常量、整数表达式

·数组:用DUP操作符

宏(Macro)

命名的汇编语句块。调用宏的时候,汇编语句块的一份拷贝直接插入到程序中。

宏和子程序区别

调用通过宏指令名进行的;汇编时,由汇编程序展开,调用多少次就展开多少次,不能缩短目标程序。子程序调用是在程序执行期间执行CALL指令进行的,子程序的代码只在目标程序中出现一次。

调用时的参数由汇编程序通过实参替换形参的方式传递;子程序调用时的参数通过寄存器、堆栈或约定的内存单元传递。

调用是在汇编时完成,运行无需额外开销;子程序调用和返回均需时间,且涉及堆栈,速度慢

硬件I/O

1.内存映射

程序直接向特定的内存地址写入数据,数据则自动传送至设备。

例如:视频显示。

2.基于端口

要求使用 OUTIN 指令向“端口”写入或读取数据。

IN:从端口输入一个字节、字 或 双字。

OUT:向端口输出一个字节、字 或 双字。

32/64位处理器拓展指令

4.总线技术

概述

总线驱动和控制

负载

驱动(重点是板子的设计

常用芯片

①单向驱动器(三态输出)

②双向驱动器(三态输出)

例题

某电路板上驱动电路如图所示。

1)编程:从端口读100个16位数据写入起始的主存空间中。

2)画出:的波形变化

解:

1)

1
2
3
4
5
6
7
8
9
	mov ax,0D100H
mov ds,ax ;初始化段寄存器
mov bx,0000H ;访问数据时作为指针,bx/si/di与ds搭配
mov cx,100 ;循环次数100次
mov dx,0A080H ;0-FFFFh之间的值送到DX寄存器,0-FFh直接写常量即可
flag:in ax,dx
mov [bx],ax
add ax,2 ;加2是因为字型数据在8086/8088要2个内存单元
loop flag

2)波形的题已经10年没有考过了

对于这个总线对应情况的解答:

CXQ老师:比如你设计了一个基于ISA总线的网卡,插在计算机主板的ISA总线扩展插槽上即可使用。

在你的网卡内部,要对ISA总线过来的信号进行驱动,比如数据线,用74LS245驱动,A边连ISA总线信号(即计算机主板过来的信号),B边连网卡内部电路,则74LS245的方向控制可以用ISA总线上的/MEMR或者/IOR信号。

如果74LS245的B边接ISA总线、A边接网卡内部电路,那么方向控制信号就要用写信号(/MEMW或/IOW)了。

可以记为:A主板读,A网卡写。

附录1 英文术语

全称 缩写 全文
【1】大规模集成电路 LSI Large Scale Integration
【2】个人电脑 PC Private Computer
【3】输入/输出操作 I/O Input/Output
【4】数据总线 DB Data Bus
【5】地址总线 AB Address Bus
【6】控制总线 CB Control Bus
【7】中央处理器 CPU Central Processing Unit
【8】微处理器 MPU Microprocessor Unit
【9】累加器 ACC Accumulator
【10】标志寄存器 FR Flag Registers
【11】暂存器 TMP Temporary
【12】算术逻辑单元 ALU Arithmetic Logic Unit
【13】指令寄存器 IR Instruction Register
【14】指令译码器 ID Instruction Decoder
【15】操作控制电路 OC Operate Control
【16】程序计数器 PC Program Counter
【17】堆栈指示器 SP Stack Pointer
【18】二进制编码的十进制数 BCD Binary-Coded Decimal
【19】总线接口单元 BIU Bus Interface Unit
【20】执行单元 EU Execution Unit
【21】指令单元 IU Instruction Unit
【22】地址单元 AU Address Unit
【23】存储器管理部件 MMU Memory Management Unit
【24】指令预取单元 IPU Instruction Prefetch Unit
【25】指令译码单元 IDU Instruction Decode Unit
【26】进位标志 CF Carry Flag
【27】奇偶标志 PF Parity Flag
【28】辅助进位标志 AF Accessary Carry Flag
【29】零标志 ZF Zero Flag
【30】符号标志 SF Sign Flag
【31】溢出标志 OF Overflow Flag
【32】方向标志 DF Direction Flag
【33】陷阱标志 TF Trap Flag
【34】中断允许标志 IF Interrupt Enable Flag
【35】I/O特权级标志 IOPL I/O Priority Level
【36】任务嵌套标志 NT Nested Task Flag
【37】恢复标志 RF Resume Flag
【38】虚拟8086模式 VM Virtual 8086 Mode
【39】对准检查标志 AC Alignment Check
【40】浮点运算单元 FPU Float Point Unit
【41】引脚网络阵列 PGA Pin-Grid Array
【42】总线单元 BU BU Bus Unit
【43】寻址方式 ———————— Addressing Mode
【44】立即寻址 ———————— Immediate Addressing
【45】寄存器寻址 ———————— Register Addressing
【46】存储器操作数寻址 ———————— Memory Operator Addressing
【47】有效地址(偏移地址) EA Effective Address
【48】直接寻址 ———————— Direct Addressing
【49】寄存器间接寻址 ———————— Register Indirect Addressing
【50】寄存器相对寻址方式 ———————— Register Relative Addressing
【51】基址变址寻址方式 ———————— Based Indexed Addressing
【52】相对基址变址寻址 ———————— Relative Based Indexed Addressing
【53】比例变址寻址 ———————— Scaled Indexed Addressing
【54】基址比例变址寻址 ———————— Based Scaled Indexed Addressing
【55】相对基址比例变址寻址 ———————— Relative Based Scaled Indexed Addressing

-------------本文结束感谢您的阅读-------------
请作者喝一杯蜜雪冰城吧!