实验1 8086汇编指令编码和调试
实验任务1
这部分掌握的还可以,略。
实验任务2
使用d命令,查看 FFF0:0
~ FFF0:FF
之间的数据,可以看到有一个01/01/92
,猜测是1992年1月1日。
使用d命令进一步查看,在FFF0:F5
~ FFF0:FC
区间为日期的内存地址。两个2F
对应”/“
现将日期修改为1999年12月12日:31 31 2F 31 31 2F 39 39
然而发现日期并没有被修改成功。
原因为:8086机器的内存,在内存地址C0000
—FFFFF
之间为ROM区域,而日期存在ROM区域,ROM区域无法进行修改。
实验任务3
执行第一个命令:-e b800:0 03 04 03 04 03 04 03 04 03 04
后,可以看到顶部出现了五个红心
执行第二个命令:-f b800:0f00 0f9f 03 04
,可以看到屏幕底部出现了一排红心
尝试向其他字节填充指令:
如输入指令:-f b800:0 0f00 04 05
,则可以看到屏幕被紫色菱形充满
如输入命令:-f b800:10 0f9f 08 09
,则可以看淡满屏蓝色和圆点
8086的显存空间的地址是B8000H~BFFFFH
,共32KB空间,为80x25彩色字符模式的显示缓冲区。向这个地址空间写入数据,写入的内容将立即出现在显示器上。
实验任务4
问题1:
栈顶逻辑地址:0020:30
栈顶物理地址:00230H
观察并记录栈顶偏移地址的寄存器sp
值的变化情况
-a
mov ax, 20
mov ds, ax
mov ss, ax
mov sp, 30
push [0] ; 执行后,寄存器(sp) = _002E_
push [2] ; 执行后,寄存器(sp) = _002C_
push [4] ; 执行后,寄存器(sp) = _002A_
push [6] ; 执行后,寄存器(sp) = _0028_
pop [6] ; 执行后,寄存器(sp) = _002A_
pop [4] ; 执行后,寄存器(sp) = _002C_
pop [2] ; 执行后,寄存器(sp) = _002E_
pop [0] ; 执行后,寄存器(sp) = _0030_
执行指令 | SP指针 |
---|---|
push [0] | 002E |
push [2] | 002C |
push [4] | 002A |
push [6] | 0028 |
pop [6] | 002A |
pop [4] | 002C |
pop [2] | 002E |
pop [0] | 0030 |
问题2:
push[6]
执行结束,查看d 20:20 2f
问题3:
pop[0]
执行结束,查看 d 20:0 7
可以看到并没有任何变化
问题4:
如果将073F:011A
~ 073F:0126
改为以下顺序:
073F:011A pop [0]
073F:011E pop [2]
073F:0122 pop [4]
073F:0126 pop [6]
可以看到数据发生了变化
实验任务5
问题1
mov sp, 30
在执行完mov ss, ax
后立刻被执行了,可以看到mov ss, ax
执行后SP变为了30,下一条指令变成了mov ax, 2010
mov sp, 30
会和mov ss, ax
一起执行,这是因为这是为了确保对SS段寄存器和栈指针的修改不被破坏。(在此期间CPU不会响应其他中断)
问题2
栈初始化时,当SS
指针被修改后,栈空间的值也发生了变化。
可以看到从0020:2A ~ 0020:2D
被填充了01 80
和07 3F
,恰好是IP
和CS
的值,而这个CS:IP
,即073f:0180
对应了栈初始化的下一行指令:mov ax, 2010
查阅网上相关讨论得知,可能是由于这段汇编未在开始时通过伪指令定义栈段和数据段,因此在初始化栈的时候系统认为这是一次中断,而debug中会暂时借用主程序的栈存放
CS
、IP
和Flag
的值,因此在mov ss, ax
和mov sp, 30
被执行后,系统中断,将下一条指令mov ax, 2010
的逻辑地址,也就是CS:IP
的值入栈,然后执行栈初始化。至于A3 01
有人说是Flag的值,一个标记,具体是什么标记尚不清楚。
此外,另一篇博客指出,其实栈中除了明显的CS
、IP
、Flag
等值,还总是保存AX
和BP
两个寄存器的内容。栈中依次存放了:AX
,BP
,IP
,CS
,eflags
的值。一个解释是:由于把此处的位置当做栈了,而-t
命令产生中断,所以就将标识寄存器
、CS
、IP
入栈保存。其实这些讨论也很模糊,没有说到细节,有待进一步研究栈初始化过程中究竟发生了什么事。
相关讨论:
https://zhidao.baidu.com/question/1667892790408757387.html
当栈初始化完成后,后面正常的压栈操作依然从栈底开始进行,直接无视了之前压入栈中的IP
、CS
、Flag
。
然而之前存储的IP
、CS
和Flag
的值却仍然会随着压栈操作向栈顶方向移动。有意思的是,这三个值随着压栈的操作依次往栈顶方向移动,而且IP的值也在一直改变,始终为下一条待执行指令的值,和IP寄存器同步。前面AX
, BX
只有神秘的A3 01
一直没有变过。
很有意思,但是不知道为什么栈中会一直留着CS
、IP
和Flag
的值,也不知道为什么会一直更新。感觉网上的讨论也有些问题。
实验任务6
程序源码:
assume cs:code
code segment
start:
mov cx, 10
mov dl, '0'
s: mov ah, 2
int 21h
add dl, 1
loop s
mov ah, 4ch
int 21h
code ends
end start
编译、链接、执行过程
使用debug调试:
首先使用-r
看到DS
和CS
,DS + 100H
正好是CS
的值,100H也就是256字节,验证了PSP段大小的确是256B,且PSP段和程序段紧邻。
书上说:
PSP区和程序区虽然物理地址连续,却有不同的段地址。
在这里可以得到验证
查看DS
处的内容:
可以看到的确是CD 20
,反汇编指令是INT 20
,这条指令指程序终止,应该就是中断当前程序,然后下一条CALL
指令转移至程序段运行(猜测)
实验任务7
程序代码:
assume cs:code
code segment
mov ax, __cs__
mov ds, ax
mov ax, 0020h
mov es, ax
mov bx, 0
mov cx, __17h__
s: mov al, [bx]
mov es:[bx], al
inc bx
loop s
mov ax, 4c00h
int 21h
code ends
end
(1)第一空:cs
(2)第二空:17h
原因:由于是复制当前程序,当前程序的程序段地址存储在CS
中,因此将CS
中的数据移至DS
寄存器作为数据段,所以第一空填cs。而第二空17h则是通过先编译程序后观察程序的大小决定,如下图:
可以看到程序到mov ax, 4c00h
指令之前共占用了17h个字节,因此需要循环17h次。
使用debug调试程序:-g=0 17
可以看到程序已经正确复制到了0:200
开始的地址空间中
实验总结
- 8086的工作在实模式下,对内存直接操作,观察最明显的是对显存空间的操作。通过直接改写显存地址空间的数据,可以让屏幕上直接显示对应内容。
- 用debug调试起来很麻烦,输错了一个地方只能重来。但也没啥更好的工具了。
- 栈空间的使用方面,栈中会始终保存
AX
、BP
、CS
、IP
和Flag
的值,而且一直处在栈顶位置,具体和CPU中断有关,但是这里无法看到细节内容,需要进一步了解。 - 工作在实模式下尽管操作很方便,但是不利于内存保护。一旦写入错误的位置可能就会导致系统崩溃。保护模式是在实模式的基础上加了一些数据表,通过这些数据表来查找对应的逻辑段和地址,而无法通过实模式那样直接使用一个公式计算地址。但是保护模式的表数据结构定义非常麻烦且容易出错。