不加冒号的数据标号
直接定址表顾名思义就是可以通过给定的数据直接找到所需的地址,主要通过数据标号来实现。
到现在为止我们学习的数据标号的写法都是形如
a: db 1,2,3
其实还有一种写法是这样
a db 1,2,3
看上去只是少了个冒号,但是表示的含义可是大不相同。第一种写法a表示内存单元的地址,而第二种写法a不仅表示内存单元的地址,还表示了内存单元的长度。
1 ;将a中的数据相加,结果存储在b中 2 assume cs:code 3 4 code segment 5 6 a: db 1,2,3 7 b: dw 0 8 9 start: mov si,offset a 10 mov bx,offset b 11 mov cx,3 12 s: 13 mov al,cs:[si] 14 mov ah,0 15 add cs:[bx],ax 16 inc si 17 loop s 18 19 mov ax,4c00h 20 int 21h 21 code ends 22 end start
可以看到,结果已经存储在b中,采用不加冒号的数据标号写法可以使程序更加简洁。
1 ;将a中的数据相加,结果存储在b中 2 assume cs:code 3 4 code segment 5 6 a db 1,2,3 7 b dw 0 8 9 start: mov si,0 10 mov cx,3 11 s: 12 mov al,a[si] 13 mov ah,0 14 add b,ax 15 inc si 16 loop s 17 18 mov ax,4c00h 19 int 21h 20 code ends 21 end start
因为不加冒号的数据标号还代表了内存单元的长度,所以"mov al,a[si]"就相当于"mov al,cs:[0]si","add b,ax"就相当于"add cs:[3]".
任何事物都有其两面性,世上没有完全完美的东西,不加冒号的写法有一点是需要注意的:当把数据写在数据段时,必须用assume伪指令将数据段与某一个段寄存器联系起来,否则编译的时候会报错。还是刚才例子如果我们这么写
1 ;将a中的数据相加,结果存储在b中 2 assume cs:code 3 4 data segment 5 a db 1,2,3 6 b dw 0 7 data ends 8 9 code segment 10 11 start: mov si,0 12 mov cx,3 13 s: 14 mov al,a[si] 15 mov ah,0 16 add b,ax 17 inc si 18 loop s 19 20 mov ax,4c00h 21 int 21h 22 code ends 23 end start
没错,can't address with segment register,错误信息已经很明显,定位不到段地址,这是为什么呢?上文已经说过,形如
mov al,a[si]
的指令在编译时是编译成怎样子的指令
mov al,cs:0[si]
我们把数据写在代码段中用的段地址是cs,而现在数据是写在数据段中,编译器在编译时就不知道数据段所对应的段寄存是多少了,所以需要用assume指令进行关联。
1 ;将a中的数据相加,结果存储在b中 2 assume cs:code,es:data 3 4 data segment 5 a db 1,2,3 6 b dw 0 7 data ends 8 9 code segment 10 11 start: mov ax,data 12 mov es,ax 13 mov si,0 14 mov cx,3 15 s: 16 mov al,a[si] 17 mov ah,0 18 add b,ax 19 inc si 20 loop s 21 22 mov ax,4c00h 23 int 21h 24 code ends 25 end start
直接定址表
我们通过一个例子来学习
1 ;以十六进制的形式在屏幕上显示给定的字节型数据 2 3 assume cs:code 4 5 code segment 6 7 start: 8 mov al,'100' 9 call show 10 11 mov ax,4c00h 12 int 21h 13 14 show: 15 jmp short show1 16 table db '0123456789ABCDEF' 17 18 show1: 19 push es 20 push bx 21 mov ah,al 22 shr ah,1 23 shr ah,1 24 shr ah,1 25 shr ah,1 ;右移4位得到高位地址 26 and al,00001111b 27 28 mov bl,ah 29 mov bh,0 30 mov ah,table[bx];高4位的值作为偏移地址得到十六进制的值 31 32 mov bx,0b800h 33 mov es,bx 34 mov es:[160*12+40*2],ah 35 36 mov bl,al 37 mov bh,0 38 mov al,table[bx] 39 mov es:[160*12+40*2],al 40 41 pop bx 42 pop es 43 ret 44 45 code ends 46 end start
在这个例子中,我们使用字节型数据的高4位和低4位来查找作为十六进制数据的偏移值,从而可以快速的找到对应的十六进制,这种直接的映射方法就叫做直接定址表。