(1)编写下述程序并运行,然后用 Debug 加载、跟踪。
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
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] ;出栈后元素放到数据段data中,偏移地址为2
pop ds:[0] ;出栈后元素放到数据段data中,偏移地址为0
mov ax,4c00h ;程序返回
int 21h
code ends
end start ;start标号处作为程序入口
- CPU 执行程序,程序返回前,data 段中的数据为 没有变化。
end start 指明程序将标号 start 作为程序入口,代码段部分分为以下功能:定义空栈,设置寄存器 DS 的值,两次入栈,两次出栈。由于两次入栈出栈的地址逆序,且都是针对 DS 操作,所以以寄存器 DS 为标识的代码段部分数据没有改变。
- CPU 执行程序,程序返回前,cs= 076C、ss= 0769、ds= 075A。
程序加载后,系统为代码段、数据段和栈段自动分配内存空间,空间的段地址分别由代码段寄存器 CS、栈段寄存器 SS 和 数据段寄存器 DS 表示。直接使用 Debug 加载程序后,使用 r 指令查看各段寄存器的内容即可。
- 设程序加载后,code 段的段地址为 X,则 data 段的段地址为 X-2,stack 段的段地址为 X-1。使用 d 指令查看数据段的内容:
在 076A:0~076A:F 这段内存存放了数据段,共 16 个字节;栈段的段地址为 076B,如果偏移为零,则可以写作 076A:10,而上图中恰好有连续 10 个字节的零,与定义的栈段内容相符,为证实这一结论,使用 u 指令查看 076A:20 的汇编指令内容:
这部分恰好是定义的代码段部分,所以 076A:10~076A:1F 部分是定义的栈段,后面 6 字节系统自动做了填充,以 16 字节对齐。由此得知,系统以各段的定义顺序为各段分配空间,并且空间以 16 字节对齐。
(2)编写下述程序并运行,然后用 Debug 加载、跟踪。
assume cs:code,ds:data,ss:stack
data segment ;数据段
dw 0123H,0456H
data ends
stack segment ;栈段
dw 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] ;出栈后元素放到数据段data中,偏移地址为2
pop ds[0] ;出栈后元素放到数据段data中,偏移地址为0
mov ax,4c20h ;程序返回
int 21h
code ends
end start
- CPU 执行程序,程序返回前, data 段中的数据为 前 4 个字节存放定义的数据、后面为填充数据。
- CPU 执行程序,程序返回前,cs= 076C、ss= 076B、ds=076A。
- 设程序加载后,code 段的段地址为 X,则 data 段的段地址为 X-2,stack 段的段地址为 X-1。
- 对于如下定义的段:
name segment
...
name ends
如果段中的数据占 N 个字节,则程序加载后,该段实际占有的空间为 N/16*16+(N%16!=0)*16。
前面由实验得知,各段的大小在实际存储时有 16 字节的对齐。所以,当 N 能整除 16 时,该段实际占用空间为 N;当 N 不能整除 16 时,占有的空间向上 16 字节对齐。
(3)编写下述程序并运行,然后用 Debug 加载、跟踪。
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] ;出栈后元素放到数据段data中,偏移地址为2
pop ds:[0] ;出栈后元素放到数据段data中,偏移地址为0
mov ax,4c00h ;程序返回
int 21h
code ends
data segment ;数据段
dw 0123H,0456H
data ends
stack segment ;栈段
dw 0,0
stack ends
end start
- CPU 执行程序,程序返回前, data 段中的数据为 。
这段程序与上段程序不同的是,数据段、代码段和栈段的定义顺序不同。根据寄存器的段地址可知,各段的存放顺序与定义顺序相同,即先代码段、然后数据段、最后栈段。根据下一题的答案,076A0~076CF 部分存放代码段,076D:0 ~076D:F 部分存放数据段,076E:0~076E:F 部分存放栈段。
- CPU 执行程序,程序返回前,cs= 076A、ss= 076E、ds= 076D。
- 设程序加载后,code 段的段地址为 X,则 data 段的段地址为 X+3,stack 段的段地址为 X+4。
(4)如果将上面三题中最后一条伪指令 end start 改为 end,则哪个程序仍可以正确执行。
end start 指明程序入口为 start 标号处,没有指明程序入口时程序将从加载的第一条指令开始执行,所以只有(3)能够正确运行。这里也可以看出,代码和数据在内存中的存放形式都是一致的,关键是编写代码的人去控制 CPU 执行指令实为指令的代码、使用实为数据的代码。所以,在编写程序时最好指定程序入口。
(5)编写 code 段中的代码,将 a 段和 b 段中的数据依次相加,将结果存到 c 段中。
assume cs:code
a segment ;段a
db 1,2,3,4,5,6,7,8 ;使用dw定义时以字为单位,使用db定义时以字节为单位
a ends
b segment ;段b
db 1,2,3,4,5,6,7,8
b ends
c segment ;段c
db 0,0,0,0,0,0,0,0
c ends
code segment ;代码段
start:
? ;待完成部分
code ends
end start
- 题目要求将两个段 a 和 b 的内容依次相加,将相加结果存入另一个段 c 中,每个段的大小都是 8 个字节。即,考虑首先将段 a 的内容复制到 段 c 中,然后再将段 b 和段 c 的内容相加,在代码部分使用两个循环。
- 首先,我们需要使用段寄存器指向某个段,段寄存器有 CS、DS、SS、ES,CS 用于代码段的管理、SS 用于栈段的管理,这里使用 DS 和 ES 两个段寄存器。待完成部分:
mov ax,a
mov ds,ax ;使用段寄存器DS指向段a
mov ax,c
mov es,ax ;使用段寄存器ES指向段c
mov cx,8 ;定义循环次数,一共有8个字节的数据
mov bx,0 ;定义偏移量
s1: mov al,ds:[bx] ;每次处理1个字节
mov es:[bx],al
inc bx ;每次偏移1个字节
loop s1 ;第一个循环完成将段a的内容复制到段c
mov ax,b ;移动
mov ds,ax ;使用段寄存器DS指向段b
mov cx,8 ;定义循环次数,一共有8个字节的数据
mov bx,0 ;定义偏移量
s2: mov al,ds:[bx] ;每次处理1个字节
add es:[bx],al ;相加
inc bx ;每次偏移1个字节
loop s2 ;第二个循环完成段b和段c的内容相加
mov ax,4c00h ;程序返回
int 21h
先使用指令 g 跳转到 076D:0010 处查看段 a 和 段 c 的内容:
执行完第一次循环后查看段 c 的内容:
查看段 b 的内容和执行完第二次循环后查看段 c 的内容:
(6)编写 code 段中的代码,用 push 指令将 a 段中的 word 数据,逆序存储到 b 段中。
assume cs:code
a segment
dw 1,2,3,4,5,6,7,8
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
?
code ends
end start
- 题目要求逆序存放,利用栈先进后出的特点即可。由于入栈时栈顶寄存器 SP 的值减小而出栈时 SP 的值增大。所以,直接将段 a 中的数据入栈,同时将段 b 作为栈段,即可将段 a 中的字数据逆序存放到段 b 中。
mov ax,a
mov ds,ax ;使用段寄存器DS指向段a
mov ax,b
mov ss,ax ;使用段寄存器SS指向段b
mov sp,16 ;初始化空栈,SP指向栈顶的下一个位置
mov cx,8 ;循环次数
mov bx,0 ;偏移量
s: push ds:[bx]
add bx,2 ;每次偏移一个字
loop s
mov ax,4c00h;程序返回
int 21h
先使用指令 g 跳转到 076C:0013 处查看循环前段 a 和 段 c 的内容(暂不清楚栈为什么栈段部分不全为零):
执行循环后段 c 的内容: