8086 汇编学习记录
8086 PC 机内存地址空间分配
- 00000~9FFFF: 主存储器地址空间(RAM)
- A0000~BFFFF: 显存地址空间
- C0000~FFFFF: 各类ROM地址空间
段寄存器
8086CPU 不支持将数据直接送入段寄存器的操作,ds 是一个段寄存器,所以 mov ds,1000H
这条指令是非法的。
要将 1000H 送入 ds,只好用一个寄存器来进行中转,即先将 1000H 送入一个一般的寄存器,如 bx,再将 bx 中的内容送入 ds。
关于 SS,SP
8086CPU 中,有两个寄存器,段寄存器 SS 和寄存器 SP,栈顶的段地址存放在 SS 中,偏移地址存放在 SP 中。
任意时刻,SS:SP 指向栈顶元素。push 指令和 pop 指令执行时,CPU 从 SS 和 SP 中得到栈顶的地址。
伪指令
在汇编语言源程序中,包含两种指令,一种是汇编指令,一种是伪指令。
汇编指令是有对应的机器码的指令,可以被编译为机器指令,最终为 CPU 所执行。
而伪指令没有对应的机器指令,最终不被 CPU 所执行。那么谁来执行伪指令呢?
伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。
寻址方式
- 立即寻址:
mov ax, 1234H
ormov ax, VARW
ormov ax, [VARW]
(VARW 是内存字变量) - 直接寻址:
mov ax, [1234H]
- 寄存器直接寻址:
mov ax, bx
- 寄存器间接寻址:
mov ax, [bx]
- 寄存器相对寻址:
mov ax, [bx+1]
- 基址+变址寻址:
mov ax, [bx+si]
ormov ax, [bx][si]
- 基址+变址+相对寻址:
mov ax, [bx+si+1]
注:在汇编源程序中,数据不能以字母开头,所以要在前面加 0。比如,9138h 在汇编源程序中可以直接写为 9138h,而 A000h 在汇编源程序中要写为 0A000h。
Debug
- R命令:查看、改变 CPU 寄存器的内容;
- D命令:查看内存中的内容;
- E命令:改写内存中的内容;
- U命令:将内存中的机器指令翻译成汇编指令;
- T命令:执行一条机器指令;
- A命令:以汇编指令的格式在内存中写入一条机器指令;
- P命令:可用于快速结束一段LOOP,遇到loop时使用;
- G命令:可以让指令直接执行到某个地址处,如
-g 0016
执行到0016处代码。
实模式和保护模式
实模式是 Intel 80286 和之后的 x86 兼容 CPU 操作模式。
实模式的特性是一个 20 位元的区段存储器地址空间(意思为只有1MB的存储器可以被寻址),软件可以直接访问 BIOS 例程以及周边硬件,没有任何硬件等级的存储器保护观念或多任务。
保护模式是一种 80286 系列和之后的 x86 兼容CPU的运行模式。
保护模式有一些新的特性,如存储器保护,标签页系统以及硬件支持的虚拟内存,能够增强多任务处理和系统稳定度。
现今大部分的 x86 操作系统都在保护模式下运行,包含 Linux、FreeBSD、以及微软 Windows 2.0 和之后版本。
在纯 DOS 方式(实模式)下,可以不理会 DOS,直接用汇编语言去操作真实的硬件。
因为运行在 CPU 实模式下的 DOS,没有能力对硬件系统进行全面、严格的管理。
但在 Windows 2000、Unix 这些运行于 CPU 保护模式下的操作系统中,不理会操作系统,用汇编语言去操作真实的硬件,是根本不可能的。硬件已被这些操作系统利用 CPU 保护模式所提供的功能全面而严格地管理了。
Mul 与 Div
- mul bl ; al * bl => ax
- mul bx ; ax * bx => dx(H) ax(L)
- div bl ; ax / bl => ah(余) al(商)
- div bx ; dx(H) ax(L) / bx => dx(余) ax(商)
有符号和无符号比较
cmp:
无符号比较:
JA JB JNAE: above | below | equal | not
有符号比较:
JG JL JNGE: great | low | equal | not
如果是立即数的比较,cmp 不会识别是 8 位还是 16 位。如果通过 word ptr
或 byte ptr
进行指定:
1cmp byte ptr[di], 55h
标志位
- OF overflow,溢出标志,针对有符号数,1 => 溢出
- DF direction,方向标志,控制数据串操作指令的步进方向,1 => 递减
- IF interupt,中断允许标志,1 => 开中断
- TF trap,陷阱标志,1 => CPU 单步执行指令(程序调试)
- SF sign,符号标志,最高位为 0/1
- ZF zero,零标志,1 => 结果为零
- AF auxiliary carry,辅助进位标志,供 BCD 码使用,1 => D3 位出现进位或借位
- PF parity,奇偶标志,1 => 有偶个“1”出现
- CF carry,进位标志,针对无符号数,1 => 进位
In 和 Out
在 in 和 out 指令中,只能使用 ax 或 al 来存放从端口中读入的数据或要发送到端口中的数据。
访问 8 位端口时用al,访问 16 位端口时用 ax。
对 0~255 以内的端口进行读写时:
1in al, 20h ; 从 20h 端口读入一个字节
2out 20h, al ; 往 20h 端口写入一个字节
对 256~65535 的端口进行读写时,端口号放在 dx 中:(超出 8 位的接口地址必须用 dx 提供)
1mov dx, 3f8h ; 将端口号 3f8h 送入dx
2in al, dx ; 从 3f8h 端口读入一个字节
3out dx, al ; 向 3f8h 端口写入一个字节
问题
向内存 0:2000:23F 依次传送 063(3FH):
注意 0:2000:23F 等同于 0020:00020:3f,它们描述的是同一内存单元。
1assume cs:code
2code segment
3
4 mov bx, 20h
5 mov ds, bx
6 mov bx, 0
7 mov cx, 40h
8 s:mov [bx], bx
9 inc bx
10 loop s
11
12 mov ax, 4c00h
13 int 21h
14
15code ends
16end
将 mov ax, 4c00h
之前的指令复制到内存 0:200 处:
CX寄存器在debug调试一个可执行程序时,初始值为该程序的字节尺寸大小,要复制 mov ax, 4c00h
之前的指令,需要减去 mov ax,4c00h
和 int 21h
包含的 5 个字节。而由于程序指令的起始地址由 CS:IP 指定,所以将 ds 赋值为 cs。
1assume cs:code
2code segment
3 mov ax, cs ; 程序指令的起始地址由CS:IP指定
4 mov ds, ax
5 mov ax, 0020h
6 mov es, ax
7 mov bx, 0
8 sub cx, 5 ; 减去5个字节 mov ax, 4c00h 和 int 21h
9 s:mov al, [bx]
10 mov es:[bx], al
11 inc bx
12 loop s
13
14 mov ax, 4c00h
15 int 21h
16code ends
17end
参考:
- 维基百科
- 王爽《汇编语言》