描述单元长度的标号
我们可以使用下面的标号来表示数据的开始:
···code segmenta:db 1,2,3,4,5,6,7,8b:dw 0···code ends···
a,b都是代表对应数据的起始地址,但并不能判断数据的长度或类型。下面一段程序将a中的8个数累加存入b中:
assume cs:codecode segmenta db 1,2,3,4,5,6,7,8b dw 0start mov si,0mov cx,8s:mov al,a[si]mov ah,0add b,axinc siloop smov ax,4c00hint 21hcode endsend start
code段中a和b后并没有”:”号,这种写法同时描述内存地址和单元长度的标号。a描述了地址code:0和从这个地址开始后的内存单元都是字节单元,而b描述了地址code:8和从这个地址开始以后的内存单元都是字单元。所以b相当于CS:[8],a[si]相当于CS:0[si],使用这种标号,我们可以间接地访问内存数据。
其它段中使用数据标号
刚说的第一种标号即加”:”号的标号,只能使用在代码段中,不能在其他段中使用。如果想要在其它段中(如data段)使用标号可以使用第二种:
assume cs:code,ds:datadata segmenta db 1,2,3,4,5,6,7,8b dw 0data ends···start mov ax,datamov ds,axmov si,0mov al,a[si]···
如果想在代码段中直接使用数据标号访问数据,需要使用assume伪指令将标号所在段和一个寄存器联系起来,是让寄存器明白,我们要访问的数据在ds指向的段中,但编译器并不会真的将段地址存入ds中,我们做了如下假设之后,编译器在编译的时候就会默认ds中已经存放了data的地址,如下面的编译例子:
mov al,a[si]编译为:mov al,[si+0]
可以看出编译器默认了a[si]在ds所在的段中。所以我们需要手工指定ds指向data:
mov ax,datamov ds,ax
也可以这么使用:
data segmenta db 1,2,3,4,5,6,7,8b dw 0c a,bdata ends
c处存放的是a和b的偏移地址,相当于c dw offset a,offset b。同理c dd a,b相当于c dw offset a,seg a,offset b,seg b即存的是a和b的段地址和偏移地址。
直接定址表
使用查表的方法编写相关程序,如输出一个字节型数据的16进制形式(子程序):
showbyte jmp short showtable db '0123456789ABCDEF'show:push bxpush esmov ah,alshe ah,1she ah,1she ah,1she ah,1 ;右移四位,位移子程序限制使用的寄存器数,只能这么移and al,00001111bmov bl,almov bh,0mov ah,table[bx] ;高四位作为相对于table的偏移,取得对应字符mov bx,0b800hmov es,bxmov es:[160*12+40*2],ahmov bl,almov bh,0mov al,table[bx]mov es:[160*12+40*2+2],alpop espop bxret
可见我们直接使用需要的数值和地址的映射关系来寻找需要的数据。
程序入口地址的直接定址表
可以看书P296的例程,主要思想是,编写多个子程序实现不同功能,每个子程序有自己的标号,如sub1,sub2···等。将它们存在一个表中:
table dw sub1,sub2,sub3,sub4
然后按照之前的方法使用如:
setscreen:jmp short settable dw sub1,sub2,sub3,sub4set:push bxcmp ah,3ja sretmov bl,ahmov bh,0add bx,bxcall word ptr table[bx]sret:pop bxret
