文章目录
前言
本文描述的为8086CPU. 在CPU中, 寄存器暂存一到二个字节的数据, 交于运算器进行信息处理, 值得注意的是, 真正存储数据的是内存, 寄存器起快速转移的作用.
1. 寄存器概述
8086CPU有14个寄存器, 都为16位寄存器, 分别为:
- 通用寄存器: AX, BX, CX, DX
- AX(accumulator 累加寄存器), 用于一般的运算, 可分为两个8位寄存器AH(High 高八位), AL(low 低八位), 同时这两个寄存器也涉及中断指令的操作
- BX(base 基址寄存器), 主要用于涉及内存地址和数组的运算, 可分为BH, BL两个8位寄存器
- CX(count 计数寄存器), 是循环计数器, 也涉及位运算的使用, 可分为CH, CL两个8位寄存器
- DX(data segment数据寄存器), 涉及内存地址的运算和指定I/O端口号, 可分为DH, DL两个8位寄存器
- 段寄存器: CS, DS, SS, ES
- CS(code segment 代码段寄存器), 存储代码段的段地址, 和IP联合使用, 指明下一条执行的指令的位置
- DS(data segment 数据段寄存器), 存储数据段的段地址
- SS(stack segment 堆栈段寄存器), 存储堆栈段的段地址
- ES(extra segment 附加段寄存器), 存储附加段的段地址, 附加段为一个额外的段, 配合其他段寄存器使用
- 指令指针寄存器: IP
- IP(instruction pointer 指令指针寄存器), 存储代码段的偏移地址
- 指针和变址寄存器: SI, DI, SP, BP
- SI(source index 源索引寄存器), 主要用于存储源数据的偏移地址
- DI(destination index 目的索引寄存器), 主要用于存储目的数据的偏移地址
- SP(stack pointer 堆栈指针寄存器), 主要用于指示堆栈的栈顶位置, 它存储的是偏移地址
- BP(Base Pointer 基址指针寄存器), 主要用于配合SS访问堆栈不同位置, 而无需修改SP, 它存储的是偏移地址
- 标志寄存器: FLAGS
- FLAGS(flags 标志寄存器), 包含9个标志位, 7个无意义位置, 即9 + 7 = 16位寄存器, 标志位用于条件的判定
以上主要为规范, 而非规则, CS和IP除外
2. 数据的存储
一个字节由8个bit组成, 可以存储在8位寄存器中; 一个字由两个字节组成, 这两个字节分别称为这个字的高位字节和低位字节. 自然而然地, 若一个16位数据存在AX中, 其高八位存放在AH中, 低八位存放在AL中, 其他通用寄存器亦然
一个内存单元可放8位数据, 寄存器又可放8或16位数据, 故用十六进制可直观看出某个数据有哪些八位数据构成; 如20000可写出4E20, 将其存放在AX中, 自然AH中为4E, AL中为20
小心的是, 寄存器中数据最高位若是进位, 则不能保存, 无论是8位寄存器还是16寄存器
另外, 值得注意的是, 二进制数据后加B, 十进制数据其后不加, 十六进制后加H
3. 16位结构CPU
16位CPU具有以下结构特性:
- 运算器一次最多可以处理16位数据
- 寄存器的最大宽度为16位
- 寄存器和运算器之间的通路为16位
对于16位CPU而言, 能一次性处理、传输、暂时存储16位的地址
4. 物理地址
CPU访问内存单元时, 需给出内存单元的地址, 这个唯一的地址称为物理地址。CPU向地址总线发出物理地址前, 必须要在内部先形成这个物理地址。
8086CPU有20位地址总线, 达到 2 20 2^{20} 220B, 即1MB的寻址能力, 但8086CPU又是16位结构, 表现的寻址能力只有 2 16 2^{16} 216B, 即64KB。故其通过左移4位的方式,获得20位地址, 以下进行说明:
- CPU中的相关部件提供两个16位地址, 一个称为段地址, 另一个称为偏移地址
- 段地址和偏移地址通过内部总线送入地址加法器
- 地址加法器将段地址左移4位, 再将其和偏移地址相加, 合成为一个20位的物理地址
- 地址加法器通过内部总线将20位的物理地址送入输入输出控制电路
- 输入输出控制电路将20位物理地址送入地址总线
- 地址总线将20位物理地址送入存储器, 即内存
地址加法器采用段地址 × \times × 2 4 2^4 24 + 偏移地址 = 物理地址的方式合成物理地址,
以图说明
基于这种寻址方式, 我们采用分段的方式管理内存, 以段地址为一个段的起始地址, 以偏移地址定位段中的内存单元。值得注意的是,段地址由于需乘以16,故起始地址必然为16的倍数;同时偏移地址为16位数据, 故大小范围为0~ 2 16 2^{16} 216 - 1, 所以一个段的长度最大为 2 16 2^{16} 216B, 即64KB
5. 段寄存器
存放段地址的寄存器称为段寄存器
5.1 CS和IP
CS和IP是8086CPU中两个最关键的寄存器, 它们指明了CPU当前读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器
任何时刻,若设CS中的内容为M,IP中的内容为N,则CPU将从内存M × 16 + N单元开始,读取指令并执行
5.2 修改CS、IP的指令
CS和IP不能直接修改,必须使用jmp(jump 跳跃)指令, jmp指令分为:
- jmp 段地址 : 偏移地址, 用指令给出的段地址修改CS, 同时用给出的偏移地址修改IP
- jmp 寄存器, 只用给出的数据修改IP
值得注意的是, 每执行一条指令, IP会自加一
5.3 代码段
内存中存放运行代码的段,称为代码段
值得注意的是,CPU只认被CS:IP指向的内容作为指令,所以需要将其指向代码段的第一行指令
6. debug调试
debug是DOS、Windows 都提供的实模式程序的调试工具。使用它,可以查看CPU各种寄存器中的内容、内存的情况
常用的debug功能:
- R命令,查看、修改CPU寄存器的内容
- D命令,查看内存中的内容
- E命令,改写内存中的内容
- U命令,将内存中的机器指令翻译为汇编指令
- T命令,单步执行一条机器指令
- A命令,其后添加汇编指令时,将写入到内存
以下进行说明
使用 r 命令
r命令是 register 的缩写。这个命令用于显示和修改 CPU 寄存器的内容。
-
显示寄存器
输入 r后按回车键,可以显示当前所有的 CPU 寄存器及其内容。输出通常包括通用寄存器(如 AX、BX、CX、DX 等)、段寄存器(如 CS、DS、ES 等)、指令指针(IP)和标志寄存器(FLAGS)等。示例:
-r AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=1234 ES=5678 SS=9ABC CS=DEF0 IP=0100 NV UP EI PL NZ NA PO NC
-
修改寄存器
输入 r 后跟上寄存器名称,可以查看或修改该特定寄存器的值。例如,输入 r ax会提示你输入新的 AX 寄存器值。示例:
-r ax AX 0000 :1234
上述例子中,输入 1234 将 AX 寄存器的值修改为 1234
使用 d 命令
d 命令是 dump 的缩写。这个命令用于显示指定内存地址范围的内容。
-
显示内存内容
输入 d 后跟上一个起始地址,可以查看从该地址开始的内存内容。默认情况下,如果不指定地址,则从当前的指令指针(IP)所在位置开始显示。
示例:
-d 0100 0A2B:0100 4D 5A 90 00 03 00 00 00-04 00 00 00 FF FF 00 00 MZ.............. 0A2B:0110 B8 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0A2B:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
上述命令会显示从内存地址 0100 开始的内容,按 16 字节一行进行输出。
-
连续显示内存内容
每次执行 d 命令后,可以通过再次输入 d 而不带参数,继续从上次显示结束的位置开始显示内存内容。
示例:
-d 0A2B:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
使用 e 命令
e 命令是 enter 的缩写。这个命令用于在指定的内存地址处写入数据。
-
写入数据到指定内存地址
你可以使用 e 命令加上一个起始地址,然后依次输入你要写入的数据。数据可以是字符或十六进制数。
示例:
-e 0100 0A2B:0100 00.41 00.42 00.43 00.44 00.45 00.46 00.47
上述命令将数据 41 42 43 44 45 46 47(分别对应 ASCII 字符 A B C D E F G)写入从内存地址 0100 开始的地方。
使用 u 命令
u 命令是 unassemble 的缩写。这个命令用于将机器语言指令转换成汇编语言指令。
-
反汇编指定内存地址的内容
输入 u 后跟上一个起始地址和一个结束地址,可以将这段内存中的机器语言指令转换成汇编语言指令。
示例:
-u 0100 0109 0A2B:0100 B8 00 00 00 00 00 00 00-00 00 ......... 0A2B:010A CD 20 .
上述命令会将从内存地址 0100 到 0109 的内容转换为相应的汇编语言指令。
-
连续反汇编指令
每次执行 u 命令后,可以通过再次输入 u 而不带参数,继续从上次转换结束的位置开始进行反汇编。
示例:
-u 0A2B:0110 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0A2B:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
上述命令会从上次 u 命令结束的位置开始继续反汇编内存内容。
使用 t 命令
t 命令是 trace 的缩写。这个命令用于单步执行一条机器指令,方便用户逐条检查程序的执行情况。
-
单步执行
输入 t 命令后,debug 工具会执行当前指令,并显示下一条将要执行的指令。
示例:
-t AX=0000 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 DS=0A2B ES=0A2B SS=0A2B CS=0A2B IP=0103 NV UP EI PL NZ NA PO NC 0A2B:0103 CD 20 INT 20
-
连续单步执行
可以多次输入 t 命令来逐条执行程序中的每一条指令。每次执行 t 后,debug 会更新寄存器和指令指针的状态,让你看到下一条指令。
示例:
-t AX=0201 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 DS=0A2B ES=0A2B SS=0A2B CS=0A2B IP=0105 NV UP EI PL NZ NA PO NC 0A2B:0105 <next instruction>
执行完 t 命令后可以继续查看下一条指令及其影响。
使用 a 命令
a 命令是 assemble 的缩写。这个命令用于将汇编语言指令转换成机器语言指令,即将人类可读的汇编代码转换为计算机可以执行的二进制指令。
-
汇编指令
输入 a 后跟上要汇编的一行汇编指令,debug 工具会将它转换为机器语言指令并写入内存。
示例:
-a 0100 MOV AX, 0201
上述命令将汇编代码 MOV AX, 0201 转换成相应的机器语言指令,并将其写入内存地址 0100 处。
-
连续汇编指令
可以多次输入 a 命令来连续汇编多行汇编指令。每次执行 a 后,debug 会将汇编指令转换为机器语言指令并写入内存的下一个地址。
示例:
-a 0100 MOV AX, 0201 -a INT 20
在这个例子中,第一个 a 命令将汇编指令 MOV AX, 0201 转换成机器语言指令并写入内存地址 0100。接下来的 a 命令将汇编指令 INT 20 转换成机器语言指令并写入内存的下一个地址。
总结
- CPU可以用不同的段地址和偏移地址形成同一个物理地址
- 若给定一个段地址, 仅通过变化偏移地址来寻址, 可寻址64KB
- CS:IP指定执行中的指令
- 地址加法器采用段地址 × \times × 2 4 2^4 24 + 偏移地址 = 物理地址的方式,合成物理地址
还请大家动动发财的小手, 给我点点赞, 蟹蟹