Switernal's Blogs

看见每一种可能。

博客园 首页 新随笔 联系 订阅 管理

实验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机器的内存,在内存地址C0000FFFFF之间为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 8007 3F,恰好是IPCS的值,而这个CS:IP,即073f:0180对应了栈初始化的下一行指令:mov ax, 2010

查阅网上相关讨论得知,可能是由于这段汇编未在开始时通过伪指令定义栈段和数据段,因此在初始化栈的时候系统认为这是一次中断,而debug中会暂时借用主程序的栈存放CSIPFlag的值,因此在mov ss, axmov sp, 30被执行后,系统中断,将下一条指令mov ax, 2010的逻辑地址,也就是CS:IP的值入栈,然后执行栈初始化。至于A3 01有人说是Flag的值,一个标记,具体是什么标记尚不清楚。
此外,另一篇博客指出,其实栈中除了明显的CSIPFlag等值,还总是保存AXBP两个寄存器的内容。栈中依次存放了:AXBPIPCSeflags的值。一个解释是:由于把此处的位置当做栈了,而-t命令产生中断,所以就将 标识寄存器CSIP入栈保存。

其实这些讨论也很模糊,没有说到细节,有待进一步研究栈初始化过程中究竟发生了什么事。

相关讨论:

https://zhidao.baidu.com/question/1667892790408757387.html

https://tieba.baidu.com/p/5459150294

http://www.cppblog.com/Tim/archive/2012/06/13/178666.html

当栈初始化完成后,后面正常的压栈操作依然从栈底开始进行,直接无视了之前压入栈中的IPCSFlag

然而之前存储的IPCSFlag的值却仍然会随着压栈操作向栈顶方向移动。有意思的是,这三个值随着压栈的操作依次往栈顶方向移动,而且IP的值也在一直改变,始终为下一条待执行指令的值,和IP寄存器同步。前面AX, BX

只有神秘的A3 01一直没有变过。

很有意思,但是不知道为什么栈中会一直留着CSIPFlag的值,也不知道为什么会一直更新。感觉网上的讨论也有些问题。

实验任务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看到DSCSDS + 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开始的地址空间中

实验总结

  1. 8086的工作在实模式下,对内存直接操作,观察最明显的是对显存空间的操作。通过直接改写显存地址空间的数据,可以让屏幕上直接显示对应内容。
  2. 用debug调试起来很麻烦,输错了一个地方只能重来。但也没啥更好的工具了。
  3. 栈空间的使用方面,栈中会始终保存AXBPCSIPFlag的值,而且一直处在栈顶位置,具体和CPU中断有关,但是这里无法看到细节内容,需要进一步了解。
  4. 工作在实模式下尽管操作很方便,但是不利于内存保护。一旦写入错误的位置可能就会导致系统崩溃。保护模式是在实模式的基础上加了一些数据表,通过这些数据表来查找对应的逻辑段和地址,而无法通过实模式那样直接使用一个公式计算地址。但是保护模式的表数据结构定义非常麻烦且容易出错。
posted on 2021-10-25 15:25  Switernal  阅读(309)  评论(2编辑  收藏  举报