一、预备知识
ps:这一章我同样不分开做知识点总结了,一个是因为觉得能力不够,没有那么多的发现,而且掌握也不算好,还有一点是现在时间不够,每一次作业都要花好久的时间
(1)dw:字型数据;db:字节型数据
(2)如果代码和数据放在一个段中,反汇编时,如果从0开始,会把数据当成指令机器码反汇编,所以反汇编时注意sp的值
(3)8086CPU不允许将一个数值直接送入段寄存器中
(4)调试时,可以通过r命令修改ip的值或在t/g命令中指定执行的起始地址
(5)end 标号:表示程序到标号处开始,到此结束,后面的标号有时候可以省略
(6)使用assume仅仅表示将某个逻辑段和某个段寄存器关联起来;真正当做特定的段使用,需要在代码段中设置相应的段寄存器值
二、实验任务
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]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
end start
先用r查看各寄存器的值,CX的值为42h,前面的数据一共用了20h,剩下的一共是22h,从0~21h
接着用g命令多步执行,到程序返回前停下,所以用g 001d,接着查看data段中的数据
(1)CPU执行程序,程序返回前,data段中的数据为多少?
0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
(2)CPU执行程序,程序返回前,cs=0772h,ss=0771h,ds=0770h
(3)设程序加载后,code段的段地址为X,则data段的段地址为X-2,stack段的段地址为X-1
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]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
end start
这个程序和上面的程序一样的方法调试
(1)CPU执行程序,程序返回前,data段中的数据为多少?
0123h,0456h
(2)CPU执行程序,程序返回前,cs=0772h,ss=0771h,ds=0770h
(3)设程序加载后,code段的段地址为X,则data段的段地址为X-2,stack段的段地址为X-1
(4)对于如下定义的段:
name segment
……
name ends
如果段中的数据占N个字节,则程序加载后,该段实际占有的空间为((N+15)/16)* 16
ps:这道题我确实无从下手,不知道是该怎么处理,最后没办法,去查了一下
N分为被16整除和不被16整除。
当N被16整除时: 占有的空间为(N/16)*16
当N不被16整除时: 占有的空间为(N/16+1)*16,N/16得出的是可以整除的部分,还有一个余数,余数肯定小于16,加上一个16。
程序加载后分配空间是以16个字节为单位的,也就是说如果不足16个字节的也分配16个字节。
两种情况总结成一个通用的公式:((N+15)/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]
pop ds:[0]
mov ax,4c00h
int 21h
code ends
data segment
dw 0123h,0456h
data ends
stack segment
dw 0,0
stack ends
end start
这个程序和上面的程序一样的方法调试
(1)CPU执行程序,程序返回前,data段中的数据为多少?
0123h,0456h
(2)CPU执行程序,程序返回前,cs=0772h,ss=0774h,ds=0773h
(3)设程序加载后,code段的段地址为X,则data段的段地址为X+3,stack段的段地址为X+4
4、如果将(1)、(2)、(3)题中的最后一条伪指令“end start”改为“end”(也就是说,不知名程序的入口),则哪个程序仍然可以正确执行?请说明原因
(3)可以,(1)、(2)的开始都是数据段,而(3)的开始是指令
(1)、(2)如果直接执行,机器会把数据当成指令才执行,而(3)指令的一开始偏移地址就是0,直接执行不会有问题
5、程序如下,编写code段中的代码,将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
c1 segment
db 0,0,0,0,0,0,0,0
c1 ends
code segment
start:
???
code ends
end start
源代码:
接着,调试一下,看看能不能达到预期的效果
在实现数据相加前,逻辑段c中放的是原本的数据
很明显,最后数据的确相加了,而且存在了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
c1 segment
db 0,0,0,0,0,0,0,0
c1 ends
code segment
start: mov ax,c1
mov es,ax
mov ax,b
mov ds,ax
mov ax,a
mov ss,bx //问题出在这里,不应该是ss,bx,而应该是ss,ax
mov bx,0
mov cx,8
s: mov al,ds:[bx]
add al,ss:[bx]
mov es:[bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ends
end start
我在调试的时候才发现这个程序的问题
前面都没有什么问题,直到我用d命令查看寄存器的值的时候,很奇怪,为什么这个数据是这个样子的
我看了一下到程序返回前各寄存器的值,一看,ss的值还是0,也就是说,数据段中的数据并没有到栈段中去
而那些数据都是操作系统自己的数据
因此,我去看了一下程序,问题出在我上面标出来的那一行
改过来之后程序就没有问题了
6、程序如下,编写code段中的代码,用push指令将a段中的前8个字型数据,逆序存储到b段中
assume cs:code
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0dh,0fh,0ffh
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
???
code ends
end start
源代码:
这个代码是借鉴了书上P133页的代码
下面是调试过程:
这里是在push操作执行前,查看逻辑段b的8个子单元
在程序返回前,再次查看逻辑段b的8个子单元
最后实现了逆序存储
总结:
(1)当提到逆序存储的时候,可以利用栈的先进后出的特点来实现这个问题
(2)可以利用寄存器cx的值来进行精确反汇编,同时可以用反汇编得到的偏移地址的值进行多步执行
(3)当程序达不到自己想要的结果时,可以通过单步执行来寻找问题,也可以利用寄存器的值来寻找问题
(4)要看清楚给的数据是字型数据还是字节型数据,用相应的寄存器去实现题目的问题
(5)前面的几个程序,由每个段写的位置可见,代码段,数据段和栈段的段地址之间存在一定的关系