程序6.4 : 实现数据的逆序存放
data segment
dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20H
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8
s0: pop [bx]
add bx,2
loop s0
mov ax,4c00H
int 21H
code ends
end start
- 段名code data stack 代表了段地址
-
start
在 code段 内,这样CPU就将code段中的内容当做指令来执行
实验5
问题(1)
assume cs:code,ds:data,ss:stack
data segment
dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00H
int 21H
code ends
end start
段地址X , stack段的段地址为 X-2 , data段的段地址为 X-3
问题(2)
assume cs:code,ds:data,ss:stack
data segment
dw 0123H,0456H
data ends
stack segment
dw 1111H,1111H
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4c00H
int 21H
code ends
end start
- 书上的源码用
0000,0000
填充初始的栈空间,我认为这样不方便理解题目,于是修改成了2个字型数据 1111H,1111H
- ④ 如果段中的数据占N个字节,则程序加载后,该段实际占有的空间为 ________? 答: (N/16+1)x16,意思就是填满16的倍数(在DEBUG里面截图看上去就是整行、整行的),比如,本题中data段的数据占用了4个内存单元(即4个字节),也仍旧需要往后填零,补齐剩余12个内存单元。
- 题目(1)与(2)代码区别在于,data段以及stack段数据所占字节不同,目的是为我们展示这种背后隐藏的“整行补齐”效果
问题(3)
assume cs:code,ds:data,ss:stack
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
push ds:[0]
push ds:[2]
pop ds:[2]
pop ds:[0]
mov ax,4C00H
int 21H
code ends
data segment
dw 0123H,0456H
data ends
stack segment
dw 1111H,1111H
stack ends
end start
data段补齐一行了, stack段并没有补齐的样子
- 问题(3)与前两题源代码不同之处在于,data段以及stack段的代码是放在 code段 代码之后的;
问题(4)
- 问题(3) 的代码可以正确执行。如果去掉入口start,编译器就会从上往下顺序地执行。
问题(5):将a段和b段中的数据依次相加, 将结果存到c段中
assume cs:code
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 1,2,3,4,5,6,7,8
b ends
c segment
db 0,0,0,0,0,0,0,0
c ends
code segment
start:
mov ax,a
mov es,ax
mov ax,c
mov ds,ax
mov bx,0
mov cx,4
s1: mov ax,es:[bx]
add [bx],ax
add bx,2
loop s1
mov ax,b
mov es,ax
mov ax,c
mov ds,ax
mov bx,0
mov cx,4
s2: mov ax,es:[bx]
add [bx],ax
add bx,2
loop s2
mov ax,4C00H
int 21H
code ends
end start
- 遇到
loop指令
,按下debug 【P命令】
全部执行
- 问题(5)使用db关键词定义的是字节型数据,是8位的, 只占用1个内存单元
-
db 1,2,3,4,5,6,7,8
本质上其实是,db 01,02,03,04,05,06,07,08
,8个数据 就占用 8个内存单元
-
loop 循环
中使用 add bx,2
,其实是在用字型(16位)进行传输,或者说叫做,看做一个字为单元进行传输
- 可以这样做是因为,刚好低位内存会送到低位寄存器,高位内存地址会送到高位寄存器
- 这样的话每个loop的循环次数 其实只需要4次(8个数据相加,4=8÷2),代码要写
mov cx,4
add bx,2 到底发生了什么?
- 我对题目的源代码进行了3处修改来观察真正的循环次数
db 1,2,3,4,5,6,7,8,9,9,9,9,9,9,9,9
mov cx,8
mov cx,8
- 可以看到,如果循环次数设置为8,本质上是访问了16个内存单元,不光前8个数据被运算,后面的8个数据也被运算了
- 试想一下,因为代码里是先写a段再b段,所以在内存里其实是a段数据+(可能的补齐整行)+b段数据+(可能的补齐整行),使用
es:[bx]
去遍历,如果是add bx,2
,那么也是依次走两个内存单元,这里的关键在于是依次走
,如果恰好没有补齐整行的零怎么办?这里是有空余的零的, 所以结果暂时感觉没有错,但是循环次数就是循环次数,要求加8个数据就是8个数据运算,不能连后面的数据也一起操作了
mov cx 8 结合 add bx 2 本质 遍历了16个内存单元.png
如果要用技巧,那就要用对!
mov ax,es:[bx]
add [bx],ax
add bx,2
-
es:[bx]
计算出来一个物理地址
-
mov ax,es:[bx]
,将上面这个物理地址起始的2个内存单元(看做一个字)写入ax(低地址送入低位寄存器,高地址送入高位寄存器)
- 虽然使用db定义的时候,是按照01 然后 02 接着03、04、05、06、07、08,这样独立的8个数据,但是在传输的时候可以看做是4个字型数据0201、0403、0605、0807送入寄存器,循环次数就相对减半了
冷静一下
- 因为《汇编语言》这本书在说的是8086 CPU,而8086CPU的寄存器就是存16位的数据,在之前的实验和习题里,基本上自己脑子的思路都是默认用字传输数据,问题(5)突然用db关键词这么一晃,反倒让我有点懵
问题(6) :用push指令将a段中的前8个字型数据,逆序存储到b段中
assume cs:code
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0cH,0Eh,0fH,0ffH
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start: mov ax,a
mov ds,ax
mov ax,b
mov ss,ax
mov sp,10h
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov ax,4c00H
int 21H
code ends
end start
- 问题(6)定义的是字型数据,要逆序移动的也是字型数据,所谓字型,是16位的,是占2个内存单元的,是使用dw关键词的,要与问题(5)使用db关键词定义字节数据区分开
- 问题(6),
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0cH,0Eh,0fH,0ffH
,本质上其实就是dw 0001,0002,0003,0004,0005,0006,0007,0008,0009,000ah,000bh,000cH,000Eh,000fH,00ffH
- 因此,不难理解最后运行截图里,是08 00 07 00 这样的数据,00就来自于字型的补零
inc bx 与 add bx,2 的区别
- 可复习 https://www.jianshu.com/p/c47c4d86d425
-
inc bx
等于 add bx,1
-
[bx]
本质上是DS:[bx]
计算得到一个物理地址
-
add ax,bx
,是字型的加法,也就是2个内存单元,高位+高位,低位+低位的运算
- 数据传输的时候,要看清楚是以字为单元还是以字节单元,1个字型等于2个字节
-
mov al,[bx]
传输字节,搭配inc bx
-
mov ax,[bx]
传输字,搭配 add bx,2