C语言能实现汇编语言的大部分功能,能进行位运算,可以直接对硬件进行操作,例如可以允许直接访问内存或端口的物理地址。因此,学习C语言的人掌握一定的汇编语言基础是必要的。
一、80x86系列CPU的编程结构
寄存器在汇编语言中的地位类似于变量。寄存器变量的访问时间远小于内存变量的访问时间。在汇编语言中大量的使用寄存器而不是直接访问内存。
1 寄存器堆
8086CPU是Intel系列的16位微处理器,有16根数据线和20根地址线,直接寻址空间为2^20即1MB。8088CPU的对外数据总线为8位,称为准16位微处理器。
8086/8088的内部寄存器(register)共有14个,如下:
(1)通用寄存器:8个,包括数据寄存器、地址指针寄存器、变址寄存器。
数据寄存器4个:AX BX CX DX,它们又可作为8个8位的寄存器使用,即AH BH CH DH AL BL CL DL
AX称为累加器,I/O指令均使用该寄存器,访问外部硬件和接口。
BX称为基址寄存器,在访问内存时用于存放基地址。
CX称为计数寄存器,用于循环、字符串的循环控制。
DX称为数据寄存器,在寄存器间接寻址的i/o指令中存放i/o地址,在作双字运算时[DX][AX]构成一个双字。
地址指针寄存器2个:SP BP
SP称为堆栈指针寄存器,BP称为基址指针寄存器,在作数组和字符串运算时,用于存放内存的偏移地址。
变址寄存器2个:SI DI
SI称为源变址寄存器,DI称为目的变址寄存器,用于数据块操作的内存寻址。
(2)段寄存器4个:CS DS ES SS
CS代码段寄存器,DS数据段寄存器,ES附加段寄存器,SS堆栈段寄存器
用于存放段地址(段基址)
(3)指令指针IP:始终指向将要执行的指令。用户不能直接访问和编程。
(4)标志寄存器FLAGS:16位寄存器,8086/8088仅使用了九个标志位。
2 标志寄存器
CF:进位标志位
PF:奇偶标志位
AF:辅助进位位
ZF:零标志位
SF:符号标志位
OF:溢出标志位
TF:跟踪标志位:单步标志
IF:中断标志位
DF:方向标志位
其中前六个为状态标志位,也叫条件码,用作条件转移指令中的判断条件。
后三个为控制标志位,对相关的操作起控制作用。
14个寄存器的内容,将要执行的指令,将要处理的数据,被称作CPU的“现场”,用debug的r命令可以清楚地看到“现场”。
二、内存的分段组织
计算机的基本存储单位是字节,由8个二进制位组成,8个位捆绑使用。可用一个两位16进制数表示其内容。16位CPU一次可以处理两个字节。
为了正确访问内存,每一个存储器单位即字节必须给出一个地址。地址编号从0开始,依次加1,被称为线性编址。
8086的地址线有20根,(详述)能够直接访问的地址空间为2^20即1MB。即内存的地址编号可以从0编到1M。用16进制数表示内存的物理地址,其地址范围为00000H~FFFFFH,为5位16进制数。每一个内存单元都有一个确定的20位物理地址。
但是,16位CPU的字长为16位,一次只能访问2^16=64k内存,如何访问1M的内存空间呢,在8086CPU中采用了地址分段的办法。即每一个存储单元的物理地址都有段地址和偏移地址两部分构成。
规定:(详述)只有地址为16的整数倍的物理地址可以作为段地址。这样,1MB的内存空间被分为了1M/16=64K个段。段地址的特征为xxxx0H。
我们知道了段地址和相对于段地址的段内偏移量(偏移地址)后就可以确定一个内存单元的物理地址了。所谓的偏移地址等于内存单元的物理地址减去段地址,不得超过一段(即64k)。
段地址可以不用20位表示,而用16位表示,即xxxx0H=xxxxH*10H表示为xxxxH,用4位16进制数表示。
物理地址的计算公式为:
物理地址=段地址*16+偏移地址
或者,物理地址=段地址*10H+偏移地址
乘以16相当于左移4位。即段地址左移4位和偏移地址相加。按十六进制数描述为,段地址左移一位和偏移地址相加。通常表示为
物理地址=段地址:偏移地址
例如:02002=0200:0002
可以看出,实际上偏移地址也是16位的,每一段的最大空间为2^16=64K,这样,不同的段之间有重叠。也即意味着物理地址可以有不同的表示方法。或者说不同的表示方法可以表示同一个物理地址。
例如:02020=0200:0020=0100:1020=0000:2020=0202:0000=......
举例说明:摩天大楼。
注意:实际上每个段并不一定占用64k的最大空间。
总结:如此麻烦的做法带来的好处是扩大了内存的表示空间,更重要的是,原本很麻烦的程序的再定位工作变得异常简单,实际上一般的程序员以及高级语言并不关心段地址,段地址的分配工作交给操作系统了。
在高级语言中,变量有两个含义:首先表示的是内存的偏移地址,对于占用两个以上存储单元的变量,其地址是低地址,一般为偶数。其次,表示存储的内容,对于字数据(两个字节),其高位存入高地址,低位存入低地址,如
xxxx:0200 2b ...var
xxxx:0201 01
xxxx:0202 00
xxxx:0203 01
对于整型变量 var,地址为0200,内容为01H*256+2BH=01H*100H+2BH=256+32+11
若为双字长整型变量var,则地址一般为4的整数倍。var的地址为0200,其内容为01H*1000000H+01H*100H+2BH=4096+256+32+11。
640K~1M 的内存称为 UMB (upper memory block)
它分为a000H,b000H,c000H,d000H,e000H,f000H六个段,f000H段为ROM。存放的是ROM-BIOS(加电自检程序、固化子程序库、硬件参数等)。
加电时,尽管主机板厂家可以不同,计算机总是从 ffff:0000开始运行,其中存放的总是jmp指令,指向加电自检程序(post)真正的起始处。
ffff段除了前16个内存单元(物理地址<1M)外,还可以访问地址超过1M的部分内存,这部分内存称为HMA。
三、寻址方式(略)
取得操作数地址的方式称为寻址方式。
(1)数据寻址
立即寻址:mov al,5
寄存器寻址:mov ax,bx
直接寻址:mov ax,[2000H]
寄存器间接寻址:mov ax,[bx]
寄存器相对寻址:mov ax,offset[si]
基址变址寻址:mov ax,[bx][di]
相对基址变址寻址:mov offset[bx][si]
(2)指令寻址
段内直接寻址:jmp near ptr label1 //near ptr|short
段内间接寻址:jmp word ptr [offset][bp]
段间直接寻址:jmp far ptr label2
段间间接寻址:jmp dword ptr [offset][bx]
(3)端口寻址:
四、指令系统(略)
(一) 指令的执行时间
若时钟周期为T,则指令的基本执行时间如下(最佳寻址方式):
传送mov, 2T
加法add, 3T
整数乘法imul, 128T~154T
整数除法idiv, 165T~184T
移位(即乘以2或除以2), 2T
无条件转移, 15T
条件转移, 不转移 4T 转移 16T
采用不同方式寻址的加法指令执行时间如下:
寄存器到寄存器3T
存储器到寄存器9T+EA
寄存器到存储器16T+EA(访问两次存储器)
立即数到寄存器4T
立即数到存储器17T+EA(访问两次存储器)
不同寻址方式计算有效地址EA所需时间:
直接寻址 6T
寄存器间接寻址 5T
寄存器相对寻址 9T
基址寻址 7T~8T
相对基址变址寻址 11T~12T
总结:从指令执行时间上看,应尽量采用加法,避免乘法,尽量用移位不用乘法
尽量使用寄存器,少用存储器。尽量用简单的寻址方式,少用复杂的寻址方式。
(二) 指令系统
1.1 mov push pop xchg
1.2 in out xlat
1.3 lea lds les
1.4 lahf sahf pushf popf
注意:mov 等传送指令相当于赋值语句。in/out为基本的端口输入和输出
2.1 add adc inc
2.2 sub sbb dec neg cmp
2.3 mul imul
2.4 div idiv cbw cwd
2.5a daa das
2.5b aaa aas aam aad
3.1 and or not xor test
3.2 shl sal shr sal rol
ror rcl rcr
4 movs cmps scas lods stos... ...rep repe|repz
repne|repnz
5.1 jmp
5.2 jz|je jnz|jne js jns jo jno jp|jpe jnp|jpo
jb|jnae|jc jnb|jae|jnc
... ...jb|jnae|jc jnb|jae|jnc jbe|jna jnbe|ja
jl|jnge jnl|jge jle|jng jnle|jg
... ...jcxz
5.3 loop loopz|loope loopnz|loopne
5.4 call ret
5.5 int into iret
6.1 clc cmc stc cld std cli sti
6.2 nop hlt wait esc lock
五、汇编程序的格式
(1)汇编语言的语句种类与格式
1 指令语句
标号:指令助记符 操作数1,操作数2;注释
2 伪指令语句
名字 伪指令 参数1,参数2,...;注释
符号定义语句:equ =
数据块定义语句:db dw dd dq dt dup(?)
标号及其属性:
分析符type length size offset seg
标号类型label byte word dword near far
合成符ptr this
3 宏指令
4 段定义
segment ends
定位类型:para bye word page
组合类型:public common stack memory at
类别:code data stack
5 过程定义
proc endp
6 其他伪定义
assume org end
name title
even
radix
short high low
+ - * / mod
and or xor not
eq ne lt gt le ge
(2) com 文件格式(略)
code segment public 'code'
org 100H
assume cs:code,ds:data,es:data
main proc near
jmp start
message db 'How are u?$'
start:mov ah,9
mov dx,offse message
int 21H
int 20H
main endp
code ends
end main
(3) exe 文件格式(略)
stack segment stack 'stack'
db 256dup(?)
stack ends
data segment public 'data'
......
data ends
code segment public 'code'
assume cs:code,ds:data,es:data,ss:stack
main proc far
push ds ;保护psp前缀
xor ax,ax
push ax ;保护偏移0地址
mov ax,data
mov ds,ax
mov es,ax
......
ret
main endp
code ends
end main
六、BIOS中断和DOS功能调用(略)
相当于高级语言中的库函数或者系统子程序。
七、debug和汇编语言上机(略)