《汇编语言》- 读书笔记 - 第8章 - 数据处理的两个基本问题(阶段总结)
- 8.1 bx、si、di 和 bp (可用于内存寻址)
- 8.2 机器指令处理的数据在什么地方
- 8.3 汇编语言中数据位置的表达
- 1. 立即数(idata)
- 2. 寄存器
- 3. 段地址(SA)和偏移地址(EA)
- 8.4 寻址方式
- 8.5 指令要处理的数据有多长(如何定义)
- 1. 通过:寄存器名
- 2. 通过:操作符 word ptr 或 byte ptr
- 3. 其它方法
- 8.6 寻址方式的综合应用
- 8.7 指令 div
- 8.8 伪指令 db、dw、dd
- 问题 8.1
- 解
- 1. 完整代码
- 2. 执行后在 debug 中反编译看下
- 3. 执行一下查看内存中数据段
- 8.9 操作符 dup
- 实验 7 寻址方式在结构化数据访问中的应用
8.1 bx、si、di 和 bp (可用于内存寻址)
- 8086CPU 中,只有
这4个
寄存器可以用在[...]
中来进行内存单元的寻址
。
1.1.这4个
寄存器都可以单独使用。
1.2. 通常是单独使用bx
或bp
。在实现更复杂的内存寻址时,用si
或di
与bx
或bp
组合。
1.3. 还可以加立即数
。 - 允许的组合方式如下表:(
bx
与bp
,si
与di
不能直接组合 )
寄存器 | 单独使用 | 与si 组合 | 与 di 组合 | 与立即数 组合 |
---|---|---|---|---|
bx | [ bx ] | [ bx + si ] | [ bx + di ] | [ bx + idata ] [ bx + si + idata ] |
pb | [ pb ] | [ pb + si ] | [ pb + di ] | [ pb + idata ] [ pb + si + idata ] |
- 使用
bx
时默认的段地址在ds
,如:mov ax, [bx]
;mov ax, [bx+si]
;mov ax, [bx+si+idata]
使用bp
时默认的段地址在ss
,如:mov ax, [bp]
;mov ax, [bp+si]
;mov ax, [bp+si+idata]
3.1. 如果需要在其他段中寻址,则需要显式地指定相应的段前缀,如:
mov ax, es:[bx]
;mov ax, es:[bx+si]
;mov ax, es:[bx+si+idata]
mov ax, cs:[bx]
;mov ax, cs:[bx+si]
;mov ax, cs:[bx+si+idata]
。
8.2 机器指令处理的数据在什么地方
3 个地方:CPU内部
、内存
、接口
(将在后面的课程中进行讨论)
- 表 8.1 指令举例
机器码 | 汇编指令 | 指令执行前数据的位置 |
---|---|---|
8E1E0000 | mov bx,[0] | 内存,ds:0 单元 |
89C3 | mov bx,ax | CPU 内部,ax 寄存器 |
BB0100 | mov bx,1 | CPU 内部,1 在指令缓冲器中 |
8.3 汇编语言中数据位置的表达
汇编语言中用 3 个概念来表达数据的位置
1. 立即数(idata)
直接写在代码
中的数据,在汇编语言中称为: 立即数
。(它们执行前在 CPU 的指令缓冲器中)
可以简单的理解为汇编语言的字面量
(只是类似,不是同相)。
mov ax,1
add bx,2000h
or bx,00010000b
mov al,'a'
2. 寄存器
指令要处理的数据
在寄存器中
,在汇编指令中给出相应的寄存器名
mov ax,bx
mov ds,ax
push bx
mov ds:[0],bx
push ds
mov ss,ax
mov sp,ax
3. 段地址(SA)和偏移地址(EA)
要处理的数据在内存
时,汇编指令中可用[X]
的格式指出 EA、SA
在某个段寄存器中。
8.4 寻址方式
计算
内存单元偏移地址
的方法
称为寻址方式
。
8.5 指令要处理的数据有多长(如何定义)
8086CPU指令,可以处理 byte
(8位) 和 word
(16位) 两种尺寸的数据。
1. 通过:寄存器名
- 通过
寄存器名
知晓数据的尺寸。
使用AX、BX、CX、DX
这些16位寄存器,说明数据尺寸为字
,如:
mov ax,1
mov bx,ds:[0]
mov ds,ax
mov ds:[0],ax
inc ax
add ax,1000
使用ah、al、bh、bl、ch、cl、dh、dl
这些8位寄存器,说明数据尺寸为字节
,如:
mov al,1
mov al,bl
mov al,ds:[0]
mov ds:[0],al
inc al
add al,100
2. 通过:操作符 word ptr 或 byte ptr
在没有寄存器名存在的情况下,可以用操作符 X ptr
指明内存单元的长度,X
在汇编指令中可以为 word
或 byte
。
# 用 word ptr 指明访问的内存单元是一个字单元
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2
# 用 byte ptr 指明访问的内存单元是一个字节单元
mov byte ptr ds:[0],1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx],2
- 书上的例子:
假设我们用 Debug 查看内存的结果如下:
2000:1000 FF FF FF FF FF FF ......
使用byte ptr
修改 2000:1000 位置结果为
2000:1000 01 FF FF FF FF FF ......
使用word ptr
修改 2000:1000 位置结果为
2000:1000 01 00 FF FF FF FF ......
3. 其它方法
有些指令默认了访问的是字
单元还是字节
单元,
比如:push [1000H]
无需指明访问数据的尺寸,因为 push
指令只进行字
操作。
8.6 寻址方式的综合应用
8086CPU提供了[bx+si+idata]
的寻址方式,便于结构化数据
的处理。
bx
:定位整个结构体,idata
:定位结构体中的某一个数据项si
:定位数组项中的每个元素。
且汇编语言提供了更为贴切的书写方式,如:[bx].idata
、[bx].idata [si]
。
对应C语言中的变量.数据项[字符]
。如书中的C代码示例声明了这样一个结构体:
struct company { /*声明一个公司记录的结构体*/
char cn[3]; /*公司名称*/
char hn[9]; /*总裁姓名*/
int pm; /*排名*/
int sr; /*收入*/
char cp[3]; /*著名产品*/
}
/*定义一个公司记录的变量,内存中将存有一条公司的记录*/
struct company dec={ "DEC", "Ken Olsen", 137, 40, "PDP" };
8.7 指令 div
- 除数:可以是
8
或16
位。可放存在寄存器
或内存
。 - 被除数:可以是
16
或32
位。
2.1. 如果是16位
,放在AX
中。如果32位
,高16位
在DX
。
2.2. 在DIV
操作中被除数
是用16
还是32
取决于除数
用多少位来运算:
除数用8位
则被除数16位
,
除数用16位
则被除数32位
。
被除数 | 位数 | AX | DX |
---|---|---|---|
2 | 16位 | 00000000 00000010 | |
65536 | 32位 | 00000000 00000000 | 00000000 00000001 |
- 结果:也取决于
除数
用多少位来运算。
3.1. 除数用8位
则。AL
存商
,AH
存余数
。
3.2. 除数用16位
则。AX
存商
,DX
存余数
。
除数 | 商 | 余数 |
---|---|---|
8位 | AL | AH |
16位 | AX | DX |
8.8 伪指令 db、dw、dd
缩写 | 全称 | 含义 | 位数 |
---|---|---|---|
db | define byte 或 data byte | 定义字节(1个字节)的数据 | 8 |
dw | define word 或 data word | 定义字(2个字节)的数据 | 16 |
dd | define doubleword 或 data doubleword | 定义双字(4个字节)的数据 | 32 |
问题 8.1
用 div
计算 data
段中第一个数据除以第二个数据后的结果,商存在第三个数据的存储单元中。
data segment
dd 100001 ; 被除数 dword(双字)型 32 位
dw 100 ; 除数 word ( 字 )型 16 位
dw 0 ; 存商的位置
data ends
- 分析
data
段中dd
定义是被除数,做除法之前:
- 将
data:0
字单元中的低 16 位存储在ax
中 - 将
data:2
字单元中的高 16 位存储在dx
中
mov ax,data
mov ds ,ax
mov ax,ds:[0] ; ds:0 双字单元中的低 16 位存储在 ax 中
mov dx,ds:[2] ; ds:2 双字单元中的高 16 位存储在 dx 中
div word ptr ds:[4] ; 用 dx:ax 中的 32 位数据除以 ds:4 字单元中的数据
mov ds :[6],ax ; 将商存储在 ds:6 字单元中
解
1. 完整代码
assume cs:codesg, ds:data
data segment
dd 100001 ; 被除数 dword(双字)型 32 位
dw 100 ; 除数 word ( 字 )型 16 位
dw 0 ; 存商的位置
data ends
codesg segment
mov ax,data ; 设置代码段位置。先用 ax 中转一手,才能装入 ds
mov ds,ax
; 读取 32位 双字节 被除数存入寄存器 dx:ax
mov ax,ds:[0] ; 从 ds:0 处开始读取低 16 位 '86A1' 存入 ax 中
mov dx,ds:[2] ; 从 ds:2 处开始读取高 16 位 '0001' 存入 dx 中
div word ptr ds:[4] ; 用 dx:ax 中的 32 位数据除以 ds:4 字单元中的数据
mov ds:[6],ax ; 将商存储在 ds:6 字单元中
codesg ends
end
2. 执行后在 debug 中反编译看下
u cs:0 002F
查看从程序开始到 002F
部分反编译结果。
- 我当前的学习环境,应该是没有任何额外的引导或启动代码的,所以程序通常从
CS:0000H
处开始执行。 -u
命令忘记了的话要可以回顾一下:Debug的使用- 使用
-u
命令查看反汇编结果时,可以看到数据段
部分也显示了对应的汇编代码
,但这只是debug
自作多情罢了。因为这段内容只会被当作数据
读取。虽然可以一根筋的把它翻译成汇编代码,但是然并卵,它只是一段数据。
3. 执行一下查看内存中数据段
- 我们分析一下
dd 100001
另外两个同理。
10进制100001
转16进制后是186A1
由于声明的是双字节有32位,所以高位要补零,得到:00 01 86 A1
然后先低后高保到内存里就是A1 86 01 00
8.9 操作符 dup
dup
操作符在汇编语言中是一个用于简化定义重复数据
的操作符。
它通常与数据定义伪指令(如 db、dw、dd 等)结合使用,以指定重复特定模式的数据块的次数
指令 | 描述 | 生成的数据 |
---|---|---|
db 5 dup(0) | 定义5个字节,每个字节的值都为0 | 00 00 00 00 00 |
dw 3 dup(0x1234) | 定义6个字节(因为dw 是定义字,占两个字节),每个字的值都是0x1234 | 34 12 34 12 34 12 |
dd 2 dup(0x12345678) | 定义8个字节(因为dd 是定义双字,占四个字节),每个双字的值都是0x12345678 | 78 56 34 12 78 56 34 12 |
db 4 dup(1, 2, 3) | 定义12个字节,按照序列1、2、3重复四次 | 01 02 03 01 02 03 01 02 03 01 02 03 |
———————————— |
实验 7 寻址方式在结构化数据访问中的应用
《汇编语言》- 读书笔记 - 实验 7 寻址方式在结构化数据访问中的应用