汇编语言段前缀及段前缀的使用

(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中。

mov ax,ss:[0]

将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在ss中。

(6)  mov ax,cs:[0]

将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在cs中。

这些出现在访问内存单元的指令中,用于显式地指明内存单元的段地址的“ds:”"cs:”“ss:”"es:”在汇编语言中称为段前缀

  mov ax,ds:[bx]

  mov ax,cs:[bx]

  mov ax,ss:[bx]

  mov ax,es:[bx]

  mov ax,ss:[0]

  mov ax,cs:[0]

一段安全的空间

在8086模式中,随意向一段内存空间写入内容是很危险的,因为这段内存空间可能存放着重要的代码或数据.

assume cs:code
code segment
    mov ax,0
    mov ds,ax
    mov ds:[26H],ax

    mov ax:4c00H
    int 21h
code ends
end

会发生错误.

在操作系统环境中工作,操作系统管理所有资源,也包括内存,如果我们需要向内存空间写入数据的话,要使用操作系统给我们分配的空间.而不应直接用地址任意指定内存单元,向里面写入.

;将内存ffff:0~ffff:b单元中的数据拷贝到0:200~0:20b单元中
assume cs:code
code segment
    mov ax,0ffffH
    mov ds,ax

    mov ax,0020H
    mov es,ax

    mov dx,0
    mov bx,0
    mov cx,12
  s:mov dl,ds:[bx]
    mov es:[bx],dl
    inc bx
    loop s

    mov ax,4c00H
    int 21h
code ends
end

不要忘记,我们是在操作系统的环境中工作,操作系统管理所有的资源,也包括内存。如果我们需要向内存空间写入数据的话,要使用操作系统给我们分配的空间,而不应直接用地址任意指定内存单元,向里面写入。下一章我们会对“使用操作系统给我们分配的空间”有所认识。
但是,同样不能忘记,我们正在学习的是汇编语言,要通过它来获得底层的编程体验,理解计算机底层的基本工作机理。所以我们尽量直接对硬件编程,而不去理会操作系统。
我们似乎面临一种选择,是在操作系统中安全、规矩地编程,还是自由、直接地用汇编语言去操作真实的硬件,了解那些早已被层层系统软件掩盖的真相?在大部分的情况
下,我们选择后者,除非我们就是在学习操作系统本身的内容。
注意,我们在纯DOS方式(实模式)下,可以不理会DOS,直接用汇编语言去操作真实的硬件,因为运行在CPU实模式下的DOS,没有能力对硬件系统进行全面、严格地管理。但在Windows 2000, Unix这些运行于CPU保护模式下的操作系统中,不理会操作系统,用汇编语言去操作真实的硬件,是根本不可能的。硬件已被这些操作系统利用CPU保护模式所提供的功能全面而严格地管理了。
在后面的课程中,我们需要直接向内存中写入内容,可我们又不希望发生那种情况。所以要找到一段安全的空间供我们使用。在一般的PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用0:200}0:2ff(00200h}002fth)的256个字节的空间。所以,我们使用这段空间是安全的。不过为了谨慎起见,在进入DOS后,我们可以先用Debug查看一下,如果0:200}0:2ff单元的内容都是0的话,则证明DOS和其他合法的程序没有使用这里。
为什么DOS和其他合法的程序一般都不会使用0:200-0:2ff这段空间?我们将在以后的课程中讨论这个问题。

总结:

(1) 我们需要向一段内存写入内容

(2) 这段内存空间不应存放系统或其他程序的数据和代码,否则写入操作很可能发生错误

(3) Dos方式下一般情况 0:200-0:300空间中没有系统或其他程序数据和代码

(4) 以后如果需要向一段内存中直接写入内容时,就使用0:200-0:300这段空间
0:200-0:300 可转为 0200H-0300H 即 020:0-030:0

段前缀的使用

我们考虑一个问题,将内存ffff:0-ffff:b单元中的数据复制到0:200-0:20b单元中。
分析
(1) 0:200-0:20b单元等同于0020:0-0020:b单元,它们描述的是同一段内存空间。
(2)复制的过程应用循环实现,简要描述如下。

初始化:
X=0
循环12次:
将ffff:X单元中的数据送入。020 :X需要用一个寄存器中转)
X=X+1

(3)在循环中,源始单元ffff:X和目标单元0020:X的偏移地址X是变量。我们用bx来存放。
(4)将0:200-0:20b用0020:00020: b描述,就是为了使目标单元的偏移地址和源始单元的偏移地址从同一数值0开始。
问题:计算ffff:0~ffff:b字节单元中的数据的和,结果存储在dx中

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)=Offffh
    mov dl,[bx]     ;(d1)=((ds)*16+(bx)),将ffff:bx中的数据送入dl


 

    mov ax,002h
    mov ds,ax       ;(ds)=0020h
    mov [bx],dl     ;((ds) *16+(bx) )=(dl),将中dl的数据送入0020:bx



    inc bx         ;(bx)= (bx)+1
    loop s

    mov ax,4c00h
    int 21h
code ends
end

因源始单元ffff:x和目标单元0020:X相距大于64KB,在不同的64KB段里,程序中,每次循环要设置两次ds。这样做是正确的,但是效率不高。我们可以使用两个段寄存器分别存放源始单元ffff:X和目标单元0020:X的段地址,这样就可以省略循环中需要重复做12次的设置ds的程序段。
改进的程序如下

assume cs:code
code segment
    mov ax,0ffffh
    mov ds,ax           ;(ds)=Offffh


    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 al,[bx]         ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl

    mov es:[bx],dl      ;((es)*16+(bx))=(dl),将dl中的数据送入0020:bx

    inc bx             ;(bx)=(bx) +1
    loop s;           
    
    mov ax,4c00h
code ends
end 

程序中,使用es存放目标空间0020:0-0020:b的段地址,用ffff:0-ffff:b的段地址。在访问内存单元的指令"mov es:[bx],al”中,ds存放源始空间显式地用段前缀“es:",给出单元的段地址,这样就不必在循环中重复设置ds


发布日期:

所属分类: 易语言 标签: