《汇编语言》(王爽)补充笔记,第 1 ~ 9 章

第一章

● CPU 地址总线决定寻址能力,数据总线决定数据传输时一次数据传送量,控制总决定对系统中其他设备的控制能力

第二章 寄存器(以 16 位 8086 CPU 进行讨论)

● 内部总线用于链接 CPU 内各器件,外部总线用于连接 CPU 与其他器件

● 8086 CPU 的寄存器:AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW

● 物理地址 = 段地址 *16 + 偏移地址,即(十六进制)段地址左移一位,加上偏移地址,记作 “段地址 : 偏移地址” 或 “SA : EA”。这决定了每个内存段起点地址一定是 16 的倍数(十进制视角),切每个段的最大长度为 64 KB(偏移地址 4 位,寻址 2^16 Byte)。由于段地址在和偏移地址均有 4 位,每个物理地址可以由不同的段地址和偏移地址组合而成。

● CS 是 代码段寄存器,IP 是指令指针寄存器,任意时刻,CPU 将从 CS *16 + IP 的地址读取下一条指令来执行,用 CS:IP 来表示。

● 当新指令被加载到指令缓冲期中时,IP 的值根据新指令的长度(1 ~ 7 Byte)自增相应字节,自动指向下一条指令。

● 8086 CPU 加电启动或复位后,CS 初始化为 FFFFh,IP 初始化为 0000h。

● 能够改变 CS 或 IP 的指令称为转移指令。可以用 “jmp 段地址 : 偏移地址” 来一次性修改 CS 和 IP,也可以用 “jmp reg” 来单修改 IP。

● Debug 模式中的命令:

  R:查看、改变寄存器的内容

  D:查看内存内容

  E:改写内存内容

  U:将内存中的机器指令翻译为汇编指令

  T:执行一条机器指令

  A:以汇编指令的格式在内存中写入一条机器指令

  P:自动重复执行循环直到 cx ==0

第三章 内存访问

● 字单元,即存放一个字类型数据的内存单元,分为高位字节和地位字节

● 指令 “mov al, [imm]” 或 “mov [imm], al” 中内存单元是段地址 ds,偏移地址 imm

● 8086 CPU 不支持直接将数据送入段寄存器 ds,只能用一个通用寄存器作中介

● 8086 CPU 栈操作以字为单位(与通用寄存器等宽),任何时刻,SS : SP 指向栈顶元素,进出栈用 SP ±2h 来移动,一个栈段容量最大为 64 KB(SP = 0000h ~ FFFFh)

● 系统不自动检测栈操作越界行为,即不记录当前栈空间大小

● push 和 pop 指令也可以对段寄存器和内存单元使用

第三章 第一个程序

● 代码,示例程序,使用教材自带的 masm.exe 和 link.exe 进行编译和连接,由操作系统程序 command.com 加载,最后由 CPU 逐条执行。

 1 ; tt.asm
 2 assume cs: codesg  ; 伪指令 assume 做段标记,程序加载时寄存器指向位置,见第六章说明
 3 
 4 codesg segment    ; 名为 codesg 的段
 5     mov ax, 0123h
 6     mov bx, 0456h
 7     add ax, bx
 8     add ax, ax
 9 
10     mov ax, 4C00h  ; 与程序返回值相关
11     int 21h
12 
13 codesg ends
14 
15 end

■ dosbox 中的操作

■ 编译结果(tt_masm.lstw 文件部分)

 1                                                              Page     1-1
 2 
 3 
 4                 assume cs: codesg 
 5                  
 6  0000                codesg segment 
 7  0000  B8 0123                mov ax, 0123h  ; 依次为指令偏移量,机器指令,汇编指令
 8  0003  BB 0456                mov bx, 0456h 
 9  0006  03 C3                add ax, bx 
10  0008  03 C0                add ax, ax 
11                  
12  000A  B8 4C00                mov ax, 4C00h 
13  000D  CD 21                int 21h 
14                  
15  000F                codesg ends 
16                  
17                 end 

■ 使用 debug 查看。CX为程序长度;DS : 0000h 指向程序内存地址,该区前 256 字节存放程序段前缀数据区(PSP)用于与 DOS 操作系统通信。CS = DS + 10h,CS : IP 指向程序首指令的内存地址。

第五章 [ ] 与 loop 指令

● [ ] 的用法

  mov al,[imm]           单独中括号中的立即数,被解释为立即数本身

  mov al, reg : [imm]  有段地址前缀中括号中的立即数,被解释为内存 reg : imm 处的值。这里 reg 称为段前缀

  mov al, [reg]            单独中括号中的寄存器,解释为内存中段地址 ds,偏移地址 reg 处的值

● DOS 与其他合法程序一般不使用 0 : 0200h ~ 0 : 02FFh 这 256 Byte 的内存空间,若该范围内容全为 0,则表示没有程序在使用这段内存。

第六章 包含多个段的程序

● 程序开头使用 dw 来定义一些字类型的数据(dw 是 define word 的意思,而不是 double word),在程序加载后以 CS : 0000h 指向,范例代码:

assume cs:code

code segment
    dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh, 0cbah, 0987h

start:        ; 标记程序入口点
    mov bx, 0
    mov ax, 0
    mov cx, 8
s:
    add ax, cs:[bx]
    add bx, 2
    loop s
    
    mov ax, 4c00h
    int 21h
code ends

end start      ; 指定程序入口点

■ debug 中的结果,第一行 CS : 0000 为数据,第二行 CS : IP 开始为指令

● 代码,手工申请和维护一段内存空间用作栈

 1 assume cs:codesg
 2 
 3 codesg segment
 4     dw 0123H, 0456H, 0789H, 0abcH, 0defH, 0fedH, 0cbaH, 0987H
 5     dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0   ; 16个字的空间用作栈             
 6 
 7 start:                  ; 数据和指令在一个内存段中,没有分开
 8     mov ax, cs       
 9     mov ss, ax      ; 注意 debug -t 时该指令会与下一条指令同时执行
10     mov sp, 30h       
11 
12     mov bx, 0       ; 当前数据偏移地址
13     mov cx, 8
14 s0:
15     push cs:[bx]    ; 取数据压栈
16     add bx, 2       ; 指向下一个数据
17     loop s0         ; 以上将代码段0~15单元中的8个字型数据依次入栈
18 
19     mov bx, 0
20     mov cx, 8
21 s1:
22     pop cs:[bx]     ; 依次出栈
23     add bx, 2
24     loop s1
25     
26     mov ax, 4c00h
27     int 21h
28 codesg ends
29 
30 end start

● 代码,程序中自定义多个内存段

 1 assume cs:code, ds:data, ss:stack   ; 程序加载时三个寄存器分别指向各个内存段
 2 
 3 data segment
 4   dw 0123H, 0456H, 0789H, 0abcH, 0defH, 0fedH, 0cbaH, 0987H
 5 data ends
 6 
 7 stack segment
 8   dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 9 stack ends
10 
11 code segment
12 start:
13     mov ax, stack   ; 直接使用内存段的名字作为其地址
14     mov ss, ax
15     mov sp, 20h
16     
17     mov ax, data
18     mov ds, ax        
19     
20 ;   ...             ; 中间过程,省略
21 
22     mov ax, 4c00h
23     int 21h
24 code ends
25 
26 end start

第七章 更灵活的定位内存地址的方法

● 书中 [reg1 + reg2 / imm] 表示内存地址 (ds) * 16 + (reg1) + (reg2) / imm 处的值

● ASCII 大写改小写:or al, 11111111b 或 or al, FFh;小写改大写:and al, 11011111b 或 and al, DFh;

● 寄存器 SI 和 DI 不能拆分为高低字节来使用

第八章 数据处理的两个基本问题

● 8086 CPU 寄存器只有 bx,si,di 和 bp 可以用于 [...] 寻址;以单个或 bx / bp + si / di 的形式出现,不能出现诸如 [bx + bp]、[si + di] 等;bx 寻址默认段地址为 ds,bp 寻址默认段地址为 ss

● 机器指令层面仅关心志林执行前一刻数据所在的位置,包括 CPU内部、内存和端口

● 没有寄存器参与的内存访问和运算指令中,用 PTR 指定数据类型很有必要,除非指令已经先定了操作数类型(如 push 以字类型执行)

第九章 转移指令的原理

● 8086 CPU 转移指令分类为:无条件转移,条件转移,循环,过程,中断

● 只修改 IP 的转移指令称为段内转移(分短转移 -128 ~ +127 和近转移 -32768 ~ +32767),同时修改 CS 和 IP 的转移指令称为段间转移。

● jmp short mark 实现段内短转移,执行本质是 IP += (mark 地址) - (jmp 指令最后字节地址),编译时计算

● jmp near PTR mark 实现段内近转移,本质同上,将范围扩大了而已

● jmp 指令执行时没有记录目的地址,而记录跳转偏移量,并以此调整 IP,达到跳转目的,如下程序 jmp 执行成 EB03 和 EB06

 1 assume cs:code
 2 
 3 code segment
 4 start:
 5     mov ax, 5h    
 6     jmp short s
 7     add ax, 1h
 8     ;mov bx, 6h      ; 第一个程序注释掉,第二个程序再恢复
 9 s:
10     sub ax, 1h
11 
12     mov ax, 4c00h
13     int 21h
14 code ends
15 
16 end start

● jmp far PTR mark 实现段间转移(远转移),执行本质是 CS = (mark 段地址),IP = (mark 偏移地址),就是精确地跳转到 mark 所在地址(不用额外计算任何数值),如下程序 jmp 执行成新地址 076A : 0108

 1 assume cs:code
 2 
 3 code segment
 4 start:
 5     mov ax, 5h    
 6     jmp far ptr s
 7     db 256 dup(0)   ; 插入远大于段内跳转最大长度的指令    
 8 s:
 9     sub ax, 1h
10 
11     mov ax, 4c00h
12     int 21h
13 code ends
14 
15 end start

● jmp word PTR [addr] 实现段内转移,addr 为各种寻址方式的内存地址,用作跳转的偏移地址

● jmp dword PTR [addr] 实现段间转移,addr 的高字用作跳转段地址(CS = [addr + 2]),低字用作跳转偏移地址(IP = [addr])

● 指令 jcxz(cx 等于零跳转)和 loop 都是利用寄存器 cx 的值进行条件跳转

● 形如 jmp imm1 : imm2 的指令是在 debug 程序中手动跳转内存显示用的, 不能用在源程序中

猜你喜欢

转载自www.cnblogs.com/cuancuancuanhao/p/9655247.html