8086 汇编学习记录

9 minute

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 or mov ax, VARW or mov ax, [VARW] (VARW 是内存字变量)
  • 直接寻址:mov ax, [1234H]
  • 寄存器直接寻址:mov ax, bx
  • 寄存器间接寻址:mov ax, [bx]
  • 寄存器相对寻址:mov ax, [bx+1]
  • 基址+变址寻址:mov ax, [bx+si] or mov 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 ptrbyte 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,4c00hint 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

参考:

  • 维基百科
  • 王爽《汇编语言》