汇编语言学习笔记(六)

0x00 指令系统总结

8086CPU提供以下几大类指令。

数据传送指令

这些指令实现寄存器和内存、寄存器之间的单个数据传送。典型指令如mov、push、pop、pushf、popf、xchg等。

算数运算指令

这些指令实现寄存器和内存中数据的算术运算,它们的执行结果影响标志寄存器的sf、zf、of、cf、pf、af等位。典型指令如add、sub、adc、sbb、inc、dec、cmp、imul、idiv、aaa等。

逻辑指令

除not指令外,其他逻辑指令的执行结果均影响标志寄存器的相关标志位。典型指令如and、or、not、xor、test、shl、shr、sal、sar、rol、ror、rcl、rcr等。

转移指令

可以修改IP或同时修改IP和CS的指令统称为转移指令。转移指令可分为以下几类。

  • 无条件转移指令,如jmp;
  • 条件转移指令,如jcxz、je、jb、ja、jnb、jna等;
  • 循环指令,如loop;
  • 过程,如call、ret、retf;
  • 中断,如int、iret。

处理机控制指令

这些指令对标志寄存器或其他处理机状态进行设置。典型指令如cld、std、cli、sti、nop、clc、cmc、stc、hlt、wait、esc、lock等。

串处理指令

这些指令对内存中的批量数据进行处理,典型指令如movsb、movsw、cmps、scas、lods、stos等。此类指令可以和rep、repe、repne等前缀指令配合使用,以实现批量数据处理。

0x01 直接定址表

特殊标号

可以在代码段中使用标号来标记指令、数据和段地起始地址。同时,引入一种新的特殊标号,不但表示内存单元地地址,还表示内存单元的长度(字节单元/字单元/双字单元)。以如下程序为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
assume cs:code
code segment
a db 1,2,3,4,5,6,7,8
b dw 0
start: mov si,0
mov cx,8
s: mov al,a[si]
mov ah,0
add b,ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start

code段中使用的标号a、b后面没有”:”,它们同时描述内存地址和单元长度的标号。标号a描述了地址code:0,且该地址后的内存单元均为字节单元;标号b描述了地址code:8,且该地址后的内存单元均为字单元。

在其他段中同样可以使用数据标号来描述存储数据的单元地址和长度。注意,后面加有”:”的地址标号只能在代码段中使用,不能在其他段中使用。如果想在代码段中直接使用数据标号访问数据,则需要使用伪指令assume将标号所在的段和一个段寄存器联系起来。

可以将标号当作数据来定义,此时编译器将标号所表示的地址当作数据的值。

直接定址表

通过依据数据,直接计算出所要找的元素位置的表,称为直接定址表。使用根据功能号查找地址表的方法,程序的结构清晰,便于扩充。若加入一个新的功能子程序,则只需在地址表中加入它的入口地址即可。

0x02 键盘输入&磁盘读写

int9h中断例程处理键盘输入

一般的键盘输入,在CPU执行完int 9中断例程后,都放入键盘缓冲区中。键盘缓冲区中有16个字单元,可以存储15个按键的扫描码和对应的ASCII码。缓冲区的字单元中,高位字节存储扫描码低位字节存储ASCII码

int16h中断例程读取键盘缓冲区

BIOS提供int 16h中断例程,该例程的0号功能为从键盘缓冲区中读取一个键盘输入。

int 16h中断引发后,若缓冲区中不存在任何数据,则保持循环等待状态,一直等到int 9h中断发生,将数据送入缓冲区即停止等待。该过程执行IF=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
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
charstack:	jmp short charstart
table dw charpush,charpop,charshow ;定义输入,删除和回显三个功能
top dw 0 ;栈顶

charstart: push bx
push dx
push di
push es

cmp ah,2 ;提供0,1,2三号功能,若ah大于2则结束程序
ja sret
mov bl,ah
mov bh,0
add bx,bx ;address=func_index*2
jmp word ptr table[bx] ;采用直接定址表调用函数

charpush: mov bx,top ;字符入栈
mov [si][bx],al
inc top
jmp sret

charpop: cmp top,0 ;比较字符栈中是否只剩下结尾标识0
je sret
dec top
mov bx,top
mov al,[si][bx]
jmp sret

charshow: mov bx,0b800h
mov es,bx
mov al,160
mov ah,0
mul dh
mov di,ax
add dl,dl ;设置显示位置等参数
mov dh,0
add di,dx

mov bx,0

charshows: cmp bx,top ;若为结尾,显示'',进入结束阶段
jne noempty
mov byte ptr es:[di],''
jmp sret
noempty: mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2],''
inc bx
add di,2
jmp charshows

sret: pop es
pop di
pop dx
pop bx
ret

完整接收字符串输入子程序如下:

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
getstr:		push ax

getstrs: ;输入字符并显示在屏幕上
mov ah,0
int 16h
cmp al,20h
jb nochar ;ASCII码小于20h,说明不是字符
mov ah,0
call charstack ;字符入栈
mov ah,2
call charstack ;显示栈中字符
jmp getstrs

nochar: ;输入非字符,分退格和Enter两种情况处理
cmp ah,0eh ;退格的扫描码
je backspace
cmp ah,1ch ;Enter键扫描码
je enter
jmp getstrs

backspace: ;删除字符并回显
mov ah,1
call charstack ;字符出栈
mov ah,2
call charstack ;显示栈中字符
jmp getstrs

enter: ;输入完毕,显示完整字符串
mov al,0
mov ah,0
call charstack ;0入栈
mov ah,2
call charstack ;显示栈中的字符
pop ax
ret

int13h中断例程读写磁盘

3.5英寸软盘分为上下两面,每面有80个磁道,每个磁道分为18个扇区,每个扇区的大小为512字节,总存储空间约为1.44MB。

磁盘的实际访问由磁盘控制器进行,只能以扇区为单位对磁盘进行读写。读写扇区时需要给出面号、磁道号和扇区号,面号和磁道号从0开始,扇区号从1开始。

注意,直接向磁盘扇区写入数据有可能覆盖掉重要信息。若向软盘的0面0道1扇区中写入数据,则要使软盘在现有操作系统下可以使用,则必须重新格式化。

对位于不同磁道、面上的所有扇区进行统一编号,生成逻辑扇区编号。逻辑扇区号=(面号*80+磁道号)*18+扇区号-1

0x03 Intel系列微处理器的三种工作模式

微机中常用的Intel系列微处理器的主要发展过程是:8080,8086、8088,80186,80286,80386,80486,Pentium,Pentium II,Pentium III,Pentium 4。

8086/8088不具备实现一个完善的多任务操作系统的功能,为此Intel研发了80286,该处理器具备对多任务系统的支持。随后,Intel开发了80386微处理器,在以下三个模式下工作:

  • 实模式:工作方式相当于一个8086。
  • 保护模式:提供支持多任务环境的工作方式,建立保护机制。
  • 虚拟8086模式:可从保护模式切换至其中的一种8086工作方式。该方式的提供使用户可以方便地在保护模式下运行一个或多个原8086程序。
请作者吃个小鱼饼干吧