直接定址表
数据标号
之前我们使用的标号形式:
a:db 1,2,3,4,5,6,7,8
start: ...
这个标号仅仅代表内存单元的地址。
我们还可以使用同时描述内存地址和单元长度的标号,即数据标号(没有冒号),如:
a db 1,2,3,4,5,6,7,8
b dw 0
它代表a之后的单元都是字节单元,b之后的单元都是字单元。当指令中出现标号a或b的时候,会同时代表地址和单元长度信息,如果出现字节匹配字的情况出现会报错:
mov ax,b 等价于 mov ax,cs:[8]
mov b,2 等价于 mov word ptr cs:[8],2
mov al,a[si] 等价于 mov al,cs:0[si]
mov al,a[bx+si+3] 等价于 mov al,cs:0[bx+si+3]
这个装段地址的cs是可以自由设置的。
如在下列程序中:
assume cs:code,ds:data
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
mov cx,8
s: mov al,a[si]
mov ah,0
add b,ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
这里的:
mov al,a[si]
add b,ax
等价于:
mov al,ds:0[si]
add ds:8,ax
这是因为:
1、assume语句让data段与ds寄存器联系在了一起
2、ds中存放了data段的程序入口
也就是说在使用数据标号的时候要注意段地址关联到哪一个寄存器。
有了这种关联关系的设置,数据标号可以在其他段引用,带冒号的地址标号不能在其他段使用。
直接定址表
通过依据数据,直接计算出所要找的元素的位置的表,称为直接定址表(本质上还是数字标号的应用)。
如果我们要制作一个程序,希望将一个字节放在al中,在屏幕上显示该数字对应的十六进制值,这个问题就非常适合通过简单的映射关系来解决:
showbyte: jmp short show
table db '0123456789ABCDEF' 字符表
show: push bx
push es
mov ah,al al和ah存放了数字
shr ah,1
shr ah,1
shr ah,1
shr ah,1
and al,00001111b al存低4位,ah存高4位
mov bl,al
mov bh,0 组合成bx
mov al,table[bx] 通过映射得到对应字符
mov es:[160*12+40*2],ah 显示字符
mov bl,al
mov bh,0 组合成bx
mov al,table[bx] 通过映射得到对应字符
mov es:[160*12+40*2+2],al 显示字符
pop es
pop bx
ret
如给数字2Bh,拆分成高4位2和低4位11,通过十六进制转换为2Bh。
利用表我们在两个数据集合之间建立一种映射关系,快速得到对应数据,程序简洁、转换速度快、易于扩充。
程序入口地址的直接定址表
我们可以在直接定址表中存储子程序的地址,从而方便的实现不同子程序的调用。
我们要实现一个程序,用ah寄存器来传递功能号,0、1、2、3代表不同的功能号,对应不同的功能:
setscreen: jmp short set
table dw sub1 sub2 sub3 sub4 创建字符表
set: push bx
cmp ah,3 如果ah大于3就直接跳到sret
ja sret
mov bl,ah
mov bh,0 组成bx寄存器的值
add bx,bx bx扩大一倍
call word ptr table[bx] 调用对应功能的子程序
sret: pop bx
ret
这里功能号乘2等于地址表对应位置的偏移量。
然后就设置4个sub的子程序即可。
通过这种方式调用子程序易于扩展、调用简单。