基础汇编


一、基础知识

基础:

注意事项:

  • 小端模式:高地址高字节,低地址低字节
  • 数据不能以字母开头,所以:0FFFFH
  • 安全空间:0:200H - 0:2FFH

二、寄存器

1 普通寄存器

通用寄存器(AX,BX,CX,DX( 分高低位:AH,AL)):

  • AX:累加寄存器。
  • BX:基址寄存器,用于内存寻址。( ds:[bx]
  • CX:计数寄存器,用于 loop 循环。
  • DX:数据寄存器。

段寄存器(CS,DS,SS,ES(IP,SP)):

  • 特点:数据不能直接送入段寄存器,需要用寄存器送入段寄存器。

  • CS:代码段寄存器,IP:指令指针寄存器,CS:IP(CS*10H + IP)寻址。

  • DS:数据段寄存器,[p] 的地址寻址默认是 DS:[ p ]

  • SS:堆栈段寄存器,SP:堆栈指针寄存器,SS:SP指向栈顶元素。(SS被修改后下一条指令也会被执行。所以mov ss,ax; mov sp,10; 会一次性被执行)

  • ES:附加段寄存器。

其他:

  • SI:源变址寄存器(无法分高低位)
  • DI:目的变址寄存器(无法分高低位)
  • BP:基址指针寄存器(ss:bp 指向内存)
  • PSW:标志寄存器(如下)

2 标志寄存器位

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
/ / / / OF DF IF TF SF ZF / AF / PF / CF

原文

标志寄存器PSW是一个16为的寄存器。它反映了CPU运算的状态特征并且存放某些控制标志。8086使用了16位中的9位,包括6个状态标志位和3个控制标志位。

标志寄存器:

  • CF(Carry flag):进位标志位。无符号计算在最高位产生了进位或借位时,CF=1;反之,CF=0。
  • ZF(Zero flag):零标志位。当运算结果全零时,ZF=1;反之,ZF=0。

  • SF(Sign flag):符号标志位。当运算结果为正数,即结果的最高位为0时,SF=0;反之,即结果的最高位为1时,SF=1。

  • OF(Over flow flag):溢出标志位。带符号数运算结果超出了补码所能表示的范围,OF=1;反之,OF=0。OF=最高位进位与次高位进位的异或。

  • PF(Parity flag):奇偶标志。算术逻辑运算的结果中低8位“1”的个数为偶数个时,PF=1;为奇数时,PF=0。

  • AF(Auxiliary carry flag):辅助进位标志。在运算过程中,第四位向第五位有进位或借位时,AF=1;反之,AF=0。

CF判断否是溢出的方法是:进行二进制运算时,最高位的进位与次高位的进位值进行异或运算若运算结果为1则表示溢出OF=1 ,否则OF=0。

溢出三条原则:

  1. 同号相加和异号相减才会发生溢出。
  2. 同号相加结果的符号与参与运算的符号不同就溢出。
  3. 异号相减结果的符号位与被减数的符号位不同就产生溢出。

另外还有三个控制标志位用来控制CPU的操作,可以由程序进行置位和复位:

  • TF(Trap Flag):跟踪标志。当 TF=1 时,CPU进入单步工作方式;当 TF=0 时,正常执行程序。

  • IF(Interrupt flag):中断标志位。当 IF=1 时,CPU可以响应外部可屏蔽中断请求;当 IF=0 时,CPU不响应外部可屏蔽中断请求。但 IF 对不可屏蔽中断或内部中断没有影响。

  • DF(Direction flag):方向标志。当 DF=0 时,si,di递增;当 DF=1 时,si,di递减。

标志位 值1 值0
CF(Carry flag) CY(1) carry NC(0) no carry
ZF(Zero flag) ZR(1) zero NZ(0) no zero
SF(Sign flag) NG(1) negative PL(0) plus
OF(Over flow flag) OV(1) overflow NV(0) no overflow
PF(Parity flag) PE(1) parity even PO(0) parity odd
AF(Auxiliary carry flag) AC(1) auxiliary carry NA(0) no auxiliary
TF(Trap Flag) 1 0
IF(Interrupt flag) EI(1) enable interrupt DI(0) disable interrupt
DF(Direction flag) DN(1) down UP(0) up

3 内存寻址

;默认:
ds:[bx]
ds:[bx+idata]
ds:[bx+si] == ds:[bx][si]
ds:[bx+si+idata] == ds:idata[bx][si] == ds:[bx].idata[si] == ds:[bx][si].idata
;正确寻址配合:bx + si/di,bp + si/di

mov byte ptr ;改一个字节
mov word ptr ;改一个字

三、程序编译

1 普通汇编指令

cmp

注:() 表示寄存器值

1.1 移动指令

; 将 (bx) 送入 ax
mov ax,bx

; (ax) 送入栈(以字为单位)(入栈:sp-=2,送入ax)
push ax
; 出栈送入 ax(以字为单位)(出栈:取出ax,sp+=2)
pop ax

; 占一个字节
nop
; 标志寄存器压栈
pushf
; 标志寄存器出栈
popf

1.2 运算指令

; (ax) = (ax) + (bx)
add ax,bx
; (ax) = (ax) - (bx)
sub ax,bx

; (bx) += 1
inc bx
; (bx) -= 1
dec bx

; (ax) = (ax) + (bx) + CF(带进位加法,高位可以加 CF 获取低位的进位)
adc ax,bx
; (ax) = (ax) - (bx) - (CF)(带错位减法,同理高位借位)
sbb ax,bx

1.3 逻辑指令

; (al) = (al) && <num>
and al,<num>
; 小写变大写
and al,11011111B

; (al) = (al) || <num>
or al,<num>
; 大写变小写
or al,00100000B

; 逻辑移位(移动大于1位需要用cl存数)
shl al,1
shr al,1
mov cl,3
shl al,cl

1.4 处理机控制指令

sti ;设置IF=1
cli ;设置IF=0

2 复杂汇编指令

2.1 div

内存寻址可以用 byte ptr 或 word ptr 指明大小。

除数:8b 或 16b,在 [] 或 reg 中。

被除数:除数8b,被除数16b,放在ax;除数16b,被除数32b,dx放高16b,ax放低16b。

商:除数8b,商在 al 中,余数在 ah 中;除数16b,商在 ax中,余数在 dx 中。

格式:div [bx] / reg

2.2 mul

内存寻址可以用 byte ptr 或 word ptr 指明大小。

乘数:都 8b,一个在 al,一个是 reg 或 [];都 16b,一个在 ax,一个是 reg 或 []。

结果:8b 在 ax,16b 高位 dx,低位 ax。

2.3 cmp

; 比较
cmp ax,bx

; 等于转移
je label
; 不等于转移
jne label
; 低于转移(below)
jb label
; 不低于转移
jnb label
; 高于转移(above)
ja label
; 不高于转移
jna label

2.4 串操作

; 把 1B / 2B数据从 ds:si 送到 es:di,次数依照 cx
rep movsb
rep movsw
;相当于
s : movsw
loop s

cld ; DF = 0(递增)
std ; DF = 1(递减)

3 Debug

debug file.exe:进入debug

R:看寄存器:

  • -r:查看CPU寄存器内容
  • -r ax:改寄存器值

D:看机器码:

  • -d cs:ip:查看指定地址的机器码
  • -d + -d:连续使用 -d 自动查看下一段地址
  • -d cs:ip ip:查看指定 ip 段的地址

E:改机器码:

  • -e cs:ip:改机器码
  • -e cs:ip 'a' / "a+b":写入字符或字符串

U:反汇编:

  • -u cs:ip:反编译(机器码变汇编语言)

T:执行指令:

  • -t:执行一条指令

A:写汇编:

  • -a cs:ip:写入汇编语句

other:

  • -g ip:一次性执行到 ip 处
  • -p:int 21H要用 p 命令执行
  • -quit:退出debug

4 程序产生

编译过程:

  • 编译:masm file[.asm](非 asm 需要后缀,最后加上 ; 直接一次性编译,不用填信息)
  • 链接:link file[.obj](非 obj 需要后缀,可加 ; )
    数据段和代码段之间会存在一个 PSP(256 B)
; 代码内容


; 注释符:;
assume cs:codesg,ds:data,ss:stack ; 取别名
data segment
...
data ends

stack segment
...
stack ends

codesg segment ; 代码段
start:
mov ax,4c00H
int 21H
codesg ends

end
; 定义大小


db ; 字节
dw ; 字
dd ; 双字

; 循环注入字符
db <num> dup ('char')
; 案例
db 3 dup(0)
db 3 dup(0,1,2)
db 3 dup('abc','ABC')

; 数据段一段字符串结束处设置 dw 0,用于程序找到字符串尾。

5 特殊内存空间

彩色缓冲区:

  • B8000H ~ BFFFFH 的内存空间会显示在显示器上。

  • 通常是 B80000H ~ B8F9FH 中的 4000 个字节显示在显示器上。

  • 一行 80 个字符 160 个字节,一页 25 行。
  • 一个字低位是内容,高位是颜色,含义从(7(BL)闪烁,654(RGB)背景,3(I)高亮,210(RGB)前景)

四、转移指令

1 跳转

定义:可以修改 ip ,或同时修改 cs 和 ip 的指令称为跳转指令。

8086CPU分类:无条件转移指令(jmp),条件转移指令,循环指令(如:loop),过程,中断。

分类:

  • 段内转移:只修改 ip,例:jmp ax。
  • 段间转移:修改 cs 和 ip。例:jmp 1000:0。
  • 短转移:ip 变化 -128 ~ 127
  • 近转移:ip 变化 -32768 ~ 32767

jmp:jmp 的机器码记录的不是 jmp 的目的地址,而是 jmp 的下一条指令距离 jmp 终点的距离。

; 跳转指令

; 获得 label 的 ip(偏移地址)
offset label
; 循环((cx) -= 1,不为0跳转到标签处,短转移)
loop label
; 跳转(cx = 0 进行短转移,否则向下执行)
jcxz label
; jmp跳转

; 跳转,相当于 mov IP,ax(-128 ~ 127)
jmp ax:
; 短转移
jmp short label
; 近转移
jmp near ptr label
; 远转移
jmp far ptr label
; 段内转移(单字,是新 ip)
jmp word ptr ds:[bx]
; 段间转移(两个字,高字是新 cs,低字是新 ip)
jmp dword ptr ds:[bx]

2 call 和 ret

call label / reg / word ptr ds:[bx]

  1. (sp) = (sp) - 2

    ((ss)*16 + (sp)) = (ip)

  2. (ip) = (ip) + 16位位移

ret

  1. (ip) = ((ss)*16 + (sp))
  2. (sp) = (sp) + 2

call far ptr / dword ptr ds:[bx]

  1. (sp) = (sp) - 2

    ((ss)*16 + (sp)) = (cs)

    (sp) = (sp) - 2

    ((ss)*16 + (sp)) = (ip)

  2. (cs) = label.cs

    (ip) = label.ip

retf

  1. (ip) = ((ss)*16 + (sp))
  2. (sp) = (sp) + 2
  3. (cs) = ((ss)*16 + (sp))
  4. (sp) = (sp) + 2

五、中断

1 内中断

中断向量表:

  • 8086PC机,中断向量表指定放在内存地址 0 处,从内存 0000:0000 到 0000:03FF 的 1024 个单元存放中断向量表,放着256个中断源所对应的中断处理程序的入口。

  • 一般情况下,0000:0200到0000:02FF的256个字节空间所对应的中断向量表项都是空的。

安装中断

设置中断

; do0

assume cs:code

code segment
start:
mov ax,cs
mov ds,ax
mov si,offset do0 ;设置ds:si指向的源地址
mov ax,0
mov es,ax
mov di,200h ;设置es:di指向目的地址
mov cx,offset do0end-offset do0 ; 设置cx为传输长度
cld ;设置传输方向为正
rep movsb
; 设置中断向量表
mov ax,0
mov es,ax
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0

mov ax,4c00h
int 21h
do0:
jmp short do0start
db "overflow!"
do0start:
mov ax,cs
mov ds,ax
mov si,202h ;设置ds:si指向字符串

mov ax,0b800h
mov es,ax
mov di,12*160+36*2 ;设置es:di指向显存空间的中间位置

mov cx,9 ;设置cx为字符串长度
s: mov al,[si]
mov es:[di],al
inc si
add di,2
loop s

mov ax,4c00h
int 21h
do0end:nop
code ends
end start

2 int指令

int指令(int n,n 为中断向量码):

  1. 取中断类型码 n
  2. 标志寄存器入栈,IF = 0,TF = 0
  3. CS,IP 入栈
  4. IP = ( n 4 ),CS = ( n 4 + 2 )

int 0H:除法中断,0:[0*4]和0:[0*4+2]

int 9H:向键盘缓冲区写入,键盘输入到达60H端口后同时引发的中断。

; int 10H:屏幕输出中断


;在屏幕的5行12列显示3个红底高亮闪烁的绿色的'a'
assume cs:code
code segment
;放置光标
mov ah,2 ;置光标
mov bh,0 ;第0页
mov dh,5 ;dh放行号
mov dl,12 ;dl放列号
int 10H

;显示字符
mov ah,9 ;在光标处显示字符
mov al,'a' ;字符
mov bl,11001010b ;颜色属性(同显存显示格式)
mov bh,0 ;第0页
mov cx,3 ;字符重复个数
int 10H
code ends
end
; int 13H:磁盘读写(软盘结构:面——磁道——扇区——字节)


;(ah)=int 13H的功能号(2读扇区,3写扇区)
;(al)=读取的扇区数
;(ch)=磁道号
;(cl)=扇区号
;(dh)=磁头号(软盘即面号,因为一个面用一个磁头来读写)
;(dl)=驱动器号(软驱0开始,0:软驱A,1:软驱B;硬盘从80H开始,80H:C盘,81H:D盘)

mov ax,0
mov es,ax
mov bx,200H ;把es:bx的内容写入磁盘或读取磁盘内容到es:bx
... ;上述设置
int 13H

;成功:(ah)=0,(al)=读入的扇区数
;失败:(ah)=出错代码
; int 16H:键盘读入


;进行一次键盘读入
mov ah,0
int 16H
;结果(ah)=扫描码,(al)=ASCII码
; int 21H:调用子程序


;程序返回
mov ah,4cH ;程序返回
mov al,0 ;返回值
int 21H

;显示内容(以$作为结束符)
mov ah,9 ;功能号9,表示在光标位置显示字符串
ds:dx ;内容地址
int 21H

3 端口

端口访问:in 和 out 读写数据只能用 al 和 ax。al 是 8位,ax 是 16位。

CMOS RAM芯片:

  • 该芯片有一个实时时钟和一个有128个存储单元的RAM存储器。(早期的计算机为64个字节)

  • 128个字节的RAM中,内部实时钟占用0~0DH单元来保存时间信息,其余大部分单元保存系统配置信息,供系统启动时BIOS程序读取。

  • 70H为端口地址,存放要访问的CMOS RAM单元的地址。

  • 71H为数据端口,存放从选定的CMOS RAM单元中读取的数据。

时间存放是BCD码存放(BCD码4b,所以8b即可存两位数),时间存放单元如下:

存放单元 秒:0 分:2 时:4 日:7 月:8 年:9
; 端口

in al,60H ;从60H端口读入一个字节
out 60H,al ;向60H端口写入一个字节
;读2号单元数据
mov al,2 ;将2送入al中;因为只能通过ax,al与端口通信
out 70h,al ;向70h端口中写入al的值
in al,71h ;从71端口中读出数据放到al中

;写2号单元数据
mov al,2 ;先将要写入的地址送入al
out 70h,al ;将al的数据送入70h端口
mov al,97 ;再将要写入的数据送入al
out 71h,al ;最后往71号端口写数据

;读取月份
assume cs:code,ds:data

data segment
db ' '
data ends

code segment
start:
mov al,8
out 70H,al
in al,71H

mov ah,al ;al为读出数据
mov cl,4
shr ah,cl ;ah中存十位
and al,00001111b ;al中存个位

add ah,30H ;BCD码变成ASCII码
add al,30H

mov bx,data
mov ds,bx

mov ds:[0],ah
mov ds:[1],al
mov byte ptr ds:[2],'$'
mov ah,9
mov al,0
mov dx,0
int 21H

mov ax,4c00H
int 21H
code ends
end start

4 外中断

外中断信息:

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

  • 键盘输入的扫描码在60h端口产生,同时引发int 9中断(向键盘缓冲区写入)。

  • 在BIOS键盘缓冲区中,一个键盘用一个字存储,高位放扫描码,低位放字符码。键盘缓冲区最多可以存15个键盘输入。

  • 按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。断码=通码+80H。

键盘输入:

5 直接定址表

利用内存空间做个字典

6 BIOS键盘输入和磁盘读写

自启动:

  • CPU一加电初始化 (CS)=0FFFFH,(IP)=0。该处有一个跳转执行,CPU执行后会去执行BIOS中的硬件检测和初始化程序。
  • 硬件检测完后会调用 int 19H 进行操作系统引导。
  • 键盘读取:int 16H(缓冲区写入:int 9H)
  • 磁盘读写:int 13H

六、masm32汇编

text::masm32汇编