一、单元长度的标号
之前的课程中,我们一直在代码段中使用标号来标记指令、数据、段的起始地址。比如,下面的程序
将code段中的a标号处的8个数据累加,结果存储到b标号处的字中。
assume cs:code
code segment
a: db 1,2,3,4,5,6,7,8
b: dw 0
start:mov si,offset a
mov bx,offset b
mov cx,8
s:mov al,cs:[si]
mov ah,0
add cs:[bx],ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
程序中,code、a、b、start、s都是标号。这些标号仅仅表示了内存单元的地址。我们还可使用一种标号,
这种标号不但表示内存单元的地址,还表示了内存单元的长度,即表示在此标号处的单元是一个字节单元,
还是字单元,还是双字单元。上面的程序还可这样写:
assume cs:code
code segment
a db 1, 2, 3, 4, 5, 6, 7, 8
b dw 0
start: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
在code段中使用的a、b后面没有":",它们是同时描述内存地址和单元长度的标号。标号a,描述了地址
code:0,和从这个地址开始,以后的内存单元地址都是字节单元;而标号b描述了地址code:8,和从这
个地址开始,以后的内存单元都是字单元。
因为这种标号包含了对单元长度的描述,所以在指令中,它可以代表一个段中的内存单元,比如对于程序
中的“b dw 0”
指令 mov ax,b 相当于mov ax,cs:[8]
指令 mov b,2 相当于 mov word ptr cs:[8],2
指令 inc b 相当于 inc word ptr cs:[8]
对于程序中的“a db 1, 2, 3, 4, 5, 6, 7, 8”
指令 mov al,a[si] 相当于 mov al, cs:0[si]
指令 mov al, a[3] 相当于 mov al, cs:0[3]
指令 mov al, a[bx+si+3] 相当于 mov al, cs:0[bx+si+3]
二、其他段中使用数据标号
一般来说,我们不在代码段中定义数据,而是定义到其他段中,在其他段中,我们也可以使用数据标号来
描述存储数据的单元的地址和长度。
注意,在后面有":"的地址标号,只能在代码段中使用,不能再其他段中使用。
如下面程序
assume cs:code, ***es: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 es, 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
注意,如果想在代码段中使用数据标号来访问数据,则需要用伪指令assume将标号所在的段和一个段寄存器
联系起来。否则编译器在编译的时候,无法确定标号的段地址在哪一个寄存器中。比如,我们在上面的程序
中要在代码段中用data段中的数据标号a、b访问数据,则必须用assume将一个寄存器和data段相联。在程序
中我们用ds段寄存器和data段相联,则编译器对相关指令的编译如下:
指令 mov al, a[si] 编译为 mov al, [si+0]
指令 add b, ax 编译为 add [8], ax
因为这些实际编译出的指令,都默认所访问单元的段地址在ds中,而实际要访问的段为data,所以若要访问
正确,在这些指令执行之前,ds中必须为data段的段地址,则我们在程序中使用指令:
mov ax, data
mov ds, ax
设置ds指向data段。
我们也可以将标号当作数据来定义,比如:
data segment
a db 1, 2, 3, 4, 5, 6, 7, 8
b dw 0
c dw a, b ;这里a和b相当于 offset a和offset b
d dd a, b ;相当于 offset a, seg a, offset b, seg b
data ends
seg 操作符,功能为取得某一标号的段地址
三、直接定址表
编写一个子程序,计算sin(x) x∈{0°, 30°, 60°, 90°, 120°, 150°, 180°},并在屏幕中间显示结果
assume cs:code
code segment
start:mov al, 30
mov ah, 0
call showSin
mov ax, 4c00h
int 21h
showSin:jmp short show
table dw arg0, arg30, arg60, arg90, arg120, arg150, arg180 ;字符串偏移地址
arg0 db '0', 0
arg30 db '0.5', 0
arg60 db '0.866', 0
arg90 db '1', 0
arg120 db '0.866', 0
arg150 db '0.5', 0
arg180 db '0', 0
show:push bx
push es
push si
mov bx, 0b800h
mov es, bx
;以下用角度/30作为相对于table的偏移,取得对应的字符串的偏移地址,放在bx中
mov ah, 0
mov bl, 30
div bl
mov bl, al
mov bh, 0
add bx, bx
mov bx, table[bx]
;以下显示sin(x)对应的字符串
mov si, 160*12+40*2
shows:mov ah, cs:[bx]
cmp ah, 0
je showret
mov es:[si], ah
inc bx
add si, 2
jmp short shows
showret:pop si
pop es
pop bx
ret
code ends
end start
执行结果:
程序中我们通过查表的方式直接结算出索要查找的元素在表中的位置,像这种通过依据数据,直接计算出
要找的元素的位置的表,我们称为直接定址表,这种查找属于典型的以空间换取效率。