直接定址表
描述单元长度的标号(数据标号)
普通代码标号(如 s:)只表示地址,不含类型信息。
数据标号(定义在 db/dw/dd 旁边,无冒号)同时描述地址和单元长度:
asm
assume cs:code
code segment
a db 1,2,3,4,5,6,7,8 ; 数据标号 a,字节型
b dw 0 ; 数据标号 b,字型
start:
mov si, 0
mov cx, 8
s:
mov al, a[si] ; 访问 a 偏移 si 处的字节
mov ah, 0
add b, ax ; b 是字单元,合法
inc si
loop s
mov ax, 4c00h
int 21h
code ends
end start数据标号的等价写法:
asm
; 以下等价
mov ax, b ↔ mov ax, cs:[8]
mov b, 2 ↔ mov word ptr cs:[8], 2
inc b ↔ inc word ptr cs:[8]
; 注意:类型不匹配会编译报错
mov al, b ; 错误!b 是字单元,al 是 8 位寄存器在其他段中使用数据标号
数据通常定义在 data 段而非 code 段。此时用 assume 将 data 段与 DS 关联,编译器才能正确生成访问指令:
asm
assume cs:code, ds:data ; 关键: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 ; 运行时手动设置 DS(assume 不自动设置!)
mov si, 0
mov cx, 8
s:
mov al, a[si] ; 编译为 mov al, ds:[0+si]
mov ah, 0
add b, ax ; 编译为 add ds:[8], ax
inc si
loop s
mov ax, 4c00h
int 21h
code ends
end start关键点:
assume只是告诉编译器"标号默认在哪个段寄存器下访问",不会自动为 DS/ES 赋值,程序员必须手动mov ds, ax。
将标号当数据使用
可以把标号本身(地址值)作为数据存储:
asm
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw a, b ; c 处存储 a 和 b 的偏移地址
; 等价于:c dw offset a, offset b
data endsdd 版本(存段地址:偏移地址):
asm
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dd a, b ; 每项 4 字节:低字=偏移地址,高字=段地址
; 等价于:c dw offset a, seg a, offset b, seg b
; seg 操作符:取标号所在段的段地址
data ends直接定址表(查表法)
核心思想: 将计算结果预先存入表中,通过索引(数值)直接查表,避免复杂运算。
示例 1:以十六进制显示字节
将字节的高 4 位和低 4 位分别映射到 0~9, A~F 字符:
asm
showbyte:
jmp short show
table db '0123456789ABCDEF' ; 字符对照表
show:
push bx
push es
push cx
mov ah, al
mov cl, 4
shr ah, cl ; ah = 高4位(0~15)
and al, 00001111b ; al = 低4位(0~15)
mov bx, 0b800h
mov es, bx
; 显示高位字符
mov bl, ah
mov bh, 0
mov ah, table[bx] ; 用高4位值查表
mov es:[160*12+40*2], ah
; 显示低位字符
mov bl, al
mov bh, 0
mov al, table[bx] ; 用低4位值查表
mov es:[160*12+40*2+2], al
pop cx
pop es
pop bx
ret示例 2:查表显示 sin 值
将预先计算的 sin 字符串的偏移地址存入表中,通过角度值(/30 为索引)查表:
asm
showsin:
jmp short show
table dw ag0, ag30, ag60, ag90, ag120, ag150, ag180
ag0 db '0', 0
ag30 db '0.5', 0
ag60 db '0.866', 0
ag90 db '1', 0
ag120 db '0.866', 0
ag150 db '0.5', 0
ag180 db '0', 0
show:
push bx
push si
push es
; 用角度/30 作为 table 的索引
mov ah, 0
mov bl, 30
div bl ; al = 角度 / 30(索引)
mov bl, al
mov bh, 0
add bx, bx ; bx*2(因为 table 存的是字型偏移地址)
mov bx, table[bx] ; 取字符串偏移地址
; 在屏幕中间显示字符串
mov bx, 0b800h
mov es, bx
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 shows
showret:
pop es
pop si
pop bx
ret为什么用直接定址表
- 算法清晰简洁: 查表比分支判断(
if/else)更简洁 - 速度快: 一次内存访问 vs. 多次比较跳转
- 易于扩充: 只需修改表格内容,无需修改算法逻辑
类比 Rust:
table[index]≈match的查表替代版;还类似于函数指针表(vtable 的原型)。