文章目录
前言
1. 段前缀
指令“mov ax,[bx]
”中,内存单元的偏移地址由bx给出,而段地址默认在 ds中。我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器。比如:
1.1 示例演示
(1)mov ax,ds:[bx]
将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ds中。
(2)mov ax,cs:[bx]
将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在 bx中,段地址在cs中。
(3)mov ax,ss:[bx]
将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ss中。
(4)mov ax,es:[bx]
将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在es中。
(5)mov ax,ss:[0]
将一个内存单元的内容送入ax,i这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在ss中。
(6)mov ax,cs:[0]
将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在cs中。
1.2 总结
这些出现在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”、“cs:”、“ss:”或“es:”,在汇编语言中称为。
2. 一段安全的空间
2.1 存在的问题
在8086式中,随意向一段内存空间写入内容是的,因为这段空间中可能存放着重要的系统数据或代码。比如下面的指令:
mov ax,1000h
mov ds,ax
mov al,0
mov ds:[0],al
我们以前在Debug中,为了讲解上的方便,写过类似的指令。但这种做法是不合理的,因为之前我们并没有论证过1000:0中是否存放着重要的系统数据或代码。如果1000:0中存放着重要的系统数据或代码,“mov ds:[0],al”
将其改写,将引发错误。
2.2 示例演示
比如下面的程序。
assume cs:code
code segment
mov ax,0
mov ds,ax
mov ds:[26h],ax
mov ax,4c00h
int 2lh
code ends
end
2.2.1 编译、链接、加载程序
将源程序编辑为p7.asm,编译、连接后生成p7.exe,用Debug加载,跟踪它的运行,如下图所示。
上图中,我们可以看到,源程序中的“mov ds:[26h],ax
”被 masm 翻译为机器码“a3 26 00
”,而 Debug将这个机器码解释为“mov [0026],ax
”。
可见,汇编源程序中的汇编指令“mov ds:[26h],ax
”和 Debug 中的汇编指令“mov [0026],ax
”同义。
2.2.2 运行程序
然后,我们看一下“mov [0026],ax
”的执行结果,如下图所示。
上图中,是在中,在 Debug 里执行“mov [0026],ax
”的结果。
如果在下执行程序p7.exe,将会引起死机。产生这种结果的原因是0:0026处存放着重要的系统数据,而“mov [0026],ax
”将其改写。
2.3 总结
可见,在不能确定一段内存空间中是否存放着重要的数据或代码的时候,不能随意向其中写入内容。
不要忘记,我们是在操作系统的环境中工作,操作系统管理所有的资源,也包括内存。。后面的内容我们会对“使用操作系统给我们分配的空间”有所认识。
但是,同样不能忘记,我们正在学习的是汇编语言,要通过它来获得底层的编程体验,理解计算机底层的基本工作机理。所以我们尽量直接对硬件编程,而不去理会操作系统。
这时,我们似乎面临一种选择,是在操作系统中安全、规矩地编程,还是自由、直接地用汇编语言去操作真实的硬件,了解那些早已被层层系统软件掩盖的真相?在大部分的情况下,我们选择后者,除非我们就是在学习操作系统本身的内容。
注意,我们在纯DOS方式(实模式)下,可以不理会DOS,直接用汇编语言去操作真实的硬件,因为运行在CPU 实模式下的DOS,没有能力对硬件系统进行全面、严格地管理。但。硬件已被这些操作系统利用CPU保护模式所提供的功能全面而严格地管理了。
在后面的课程中,我们需要直接向内存中写入内容,可我们又不希望发生上面图片中的那种情况。所以要找到一段安全的空间供我们使用。。所以,我们使用这段空间是安全的。不过为了谨慎起见,在进入DOS后,我们可以先用Debug查看一下,如果0:200~0:2ff单元的内容都是0的话,则证明DOS和其他合法的程序没有使用这里。
为什么DOS和其他合法的程序一般都不会使用0:200~0:2ff这段空间?我们将在以后的内容中讨论这个问题。
好了,我们总结一下:
(1)我们需要直接向一段内存中写入内容。
(2)这段内存空间不应存放系统或其他程序的数据或代码,否则写入操作很可能引发错误。
(3)DOS方式下,一般情况,0:200~0:2ff空间中没有系统或其他程序的数据或代码。
(4)以后,我们需要直接向一段内存中写入内容时,就使用0:200~0:2f这段空间。
3. 段前缀的使用
3.1 问题引入
我们考虑一个问题,将内存ffff:0~ffff:b单元中的数据复制到0:200~0:20b单元中。
3.2 分析问题
分析一下。
(1)0:200~0:20b单元等同于 0020:0~0020:b单元,它们描述的是同一段内存空间。
(2)复制的过程应用循环实现,简要描述如下。
初始化:
X=0
循环12次:
将ffff:x单元中的数据送入0020:x(需要用一个寄存器中转)
X=X+1
(3)在循环中,源始单元ffff:X和目标单元 0020:X的偏移地址X是变量。我们用 bx来存放。
(4)。
3.3 代码实现
程序如下。
assume cs:code
code segment
mov bx,0 ;(bx)=0,偏移地址从0开始
mov cx,12 ;(cx)=12,循环12次
s: mov ax,0ffffh
mov ds,ax ;(ds)=0ffffh
mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
mov ax,0020h
mov ds,ax ;(ds)=0020h
mov [bx],dl ;((ds)*16+(bx))=(dl),将中d1的数据送入0020:bx
inc bx ;(bx)=(bx)+1
loop s
mov ax,4c00h
int 2lh
code ends
end
3.4 程序的改进
3.4.1 分析
因源始单元ffff:X和目标单元0020:X相距大于64KB,在不同的64KB段里,上面的程序中,每次循环要设置两次ds。这样做是正确的,但是效率不高。我们可以分别存放源始单元:ffffX和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。
3.4.2 代码实现
改进的程序如下。
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax ;(ds)=0ffffh
mov ax,0020h
mov es,aX ;(es)=0020h
mov bx,0 ;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0
mov cx,12 ;(cx)=12,循环12次
s: mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入d1
mov es:[bx],dl ;((es)*16+(bx))=(dl),将dl中的数据送入0020:bx
inc bx ;(bx)=(bx)+1
1oop s
mov ax,4c00h
int 21h
code ends
end
改进后的程序中,使用es存放目标空间0020:0~0020:b的段地址,用ds存放源始空间ffff:0~ffff:b的段地址。在访问内存单元的指令“mov es:[bx],al
”中,,这样就不必在循环中重复设置ds。
结语
今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。
也可以点点关注,避免以后找不到我哦!
Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!