汇编语言(王爽)-直接定址表


如何有效的组织数据,以及相关的编程技术

描述了单元长度的标号

代码段中经常使用标号来标记指令、数据、段的起始地址

1
2
3
4
5
6
assume cs:code
code segment
a: db 1,2,3,4,5,6,7,8
b: dw 0
code ends
end

ab这些标号仅仅表示了内存单元的地址

另一种标号,不但可以表示内存单元的地址,还可表示内存单元的长度,即表示在此标号处的单元,是字节单元还是字单元,或者双字单元

1
2
3
4
5
6
assume cs:code
code segment
a db 1,2,3,4,5,6,7,8
b dw 0
code ends
end

a或者b是同时描述内存地址和单元长度的标号

标号a,描述了地址code:0,而且从这个地址开始,以后的内存单元都是字节单元

标号b,描述了地址code:8,而且从这个地址开始,以后的内存单元都是字单元

这种标号包含了对单元长度的描述,所以在指令中,此种标号可以代表一个段中的内存单元

例:b dw 0

  1. 指令:mov ax,b

    相当于:mov ax,cs:[8]

  2. 指令:mov b,2

    相当于:mov word ptr cs:[8],2

  3. 指令:mov al,b会引起编译错误,因为b代表的内存单元是字单元,而al是8位寄存器

例:a db 1,2,3,4,5,6,7,8

  1. 指令:mov al,a[si]

    相当于:mov al,cs:0[si]

这种标号称为数据标号,它标记了存储数据的单元的地址和长度,不同于仅表示地址的地址标号


在其他段中使用的数据标号

一般来说,不再代码段中定义数据,而是将数据定义到其他段中

在其他段中,也可以使用数据标号来描述存储数据的单元的地址和长度

注意:在后面加有“:”的地址标号,只能在代码段中使用,不能在其他段中使用

例:将data段中a标号处的8个数据累加,结果存储到b标号处的字中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

若在代码段中直接使用数据标号访问数据,则需使用伪指令assume将标号所在的段和一个段寄存器联系起来,否则在编译器编译的时候,无法确定标号的段地址在哪一个寄存器中(ds:data)

但并不是用assume指令将段寄存器和某个段相联系后,段寄存器就会真的存放该段的地址,而要在程序中使用指令对段寄存器进行设置(mov ax,datamov ds,ax)

将标号当作数据来定义

可以将标号当作数据来定义,此时,编译器将标号所表示的地址当作数据的值

例:

1
2
3
4
5
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw a,b
data ends

数据标号c处存储了两个字型数据,为标号ab的偏移地址。相当于:

1
2
3
4
5
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw offset a,offset b
data ends

例:

1
2
3
4
5
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dd a,b
data ends

数据标号c处存储了两个双字型数据,为标号a的偏移地址和段地址、标号b的偏移地址和段地址。相当于:

1
2
3
4
5
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw offset a,seg a,offset b,seg b
data ends

seg操作符,功能为取得某一标号的段地址


直接定址表

用查表的方法编写相关程序的技巧

编写子程序,用十六进制的形式在屏幕中间显示给定的字节型数据

分析:

一个字节,在寄存器中以8位二进制方式存储,首先转换为其十六进制,然后将这个十六进制显示在屏幕上

一个字节需两个十六进制数码来表示(一个字节为8位二进制,一个十六进制表示4位二进制),所以,子程序须在屏幕上显示两个ASCII字符,使用0--F来显示十六进制数码

可将一个字节的高4位和低4位分开,分别用它们的值得到对应的数码字符

希望能够在数值0–15和字符0--F之间找到一种映射关系

但数值0–9和字符0--9之间的映射关系是:数值+30h=对应字符的ASCII

数值10–15和字符A--F之间的映射关系是:数值+37h=对应字符的ASCII

具体做法是:建立一张表,表中依次存储字符0--F,通过数值0–15直接查找到对应的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
;用al传送要显示的数据
showbyte:jmp short show
table db '0123456789ABCDEF' ;建立字符表
show: push bx
push es

mov ah,al
shr ah,1
shr ah,1
shr ah,1
shr ah,1 ;右移4位,ah中得到高4位的值
and al,00001111b ;al中为低4位的值

mov bl,ah
mov bh,0
mov ah,table[bx] ;用高4位的值作为相对于table的偏移,取得对应的字符

mov bx,0b800h
mov es,bx
mov es:[160*12+40*2],ah ;显示高4位对应十六进制数码

mov bl,al
mov bh,0
mov al,table[bx] ;用低4位的值作为相对于table的偏移,取得对应的字符

mov es:[160*12+40*2+2],al ;显示低4位对应十六进制数码

pop es
pop bx

ret

在子程序中,在数值0–15和字符0--F之间建立的映射关系为:以数值Ntable表中的偏移,可以找到对应的字符

利用表,在两个数据集合之间建立一种映射关系,使得可以使用查表的方法根据给出的数据得到其在另一集合中的对应数据。这么做的目的有:

  1. 为了算法的清晰和简洁

  2. 为了加快运算速度

  3. 为了使程序易于扩充

编写一个子程序,计算sin(x),xϵ{0°,30°,60°,90°,120°,150°,180°},并在屏幕中间显示计算结果

若使用麦克劳林公式进行计算,其中涉及多次乘法和除法。乘除是非常费时的运算,执行的时间大约是加法、比较等指令的5倍

可以使用一些存储空间换取运算的速度,将计算的sin(x)的结果存储到一张表中,然后用角度值来查表,找到对应的sin(x)的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
;用ax向子程序传递角度
showsin:jmp short show
table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180 ;字符串偏移地址表
ag0 db '0',0 ;sin(0)对应的字符串“0”
ag30 db '0.5',0 ;sin(30)对应的字符串“0.5”
ag60 db '0.866',0 ;sin(60)对应的字符串“0.866”
ag90 db '1',0 ;sin(90)对应的字符串“1”
ag120 db '0.866',0 ;sin(120)对应的字符串“0.866”
ag150 db '0.5',0 ;sin(150)对应的字符串“0.5”
ag180 db '0',0 ;sin(180)对应的字符串“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

子程序中,在角度值X和表示sin(x)的字符串集合table之间建立的映射关系为:以角度/30为table表中的偏移,可以找到对应的字符串的首地址

通过将给出的数据进行计算或比较而得到结果的问题,转化为用给出的数据作为查表的依据,通过查表得到结果的问题

具体的查表的方法,是用查表的依据数据,直接计算出所要查找的元素在表中的位置

像这种可以通过依据数据,直接计算出所要找的元素的位置的表,称为直接定址表


程序入口地址的直接定址表

可以直接在直接定址表中存储子程序的地址,方便的实现不同子程序的调用

实现一个子程序setscreen,为显示输出提供如下功能

  1. 清屏

  2. 设置前景色

  3. 设置背景色

  4. 向上滚动一行

入口参数:

  1. ah寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行

  2. 对于2,3号功能,用al传送颜色值,(al)ϵ{0,1,2,3,4,5,6,7}

功能实现:

  1. 清屏:将显存中当前屏幕中的字符设为空格符

  2. 设置前景色:设置显存中当前屏幕中处于奇地址的属性字节的第0、1、2位

  3. 设置背景色:设置显存中当前屏幕中处于奇地址的属性字节的第4、5、6位

  4. 向上滚动一行:依次将第n+1行的内容复制到第n行;最后一行为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
;清屏
sub1: push bx
push cx
push es

mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
sub1s: mov byte ptr es:[bx],' ' ;将用于显示的字节置为空格
add bx,2
loop sub1s

pop es
pop cx
pop bx
ret

;设置前景色
sub2: push bx
push cx
push es

mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub2s: and byte ptr es:[bx],11111000b ;将字符属性字节0,1,2位置为0
or es:[bx],al ;al中存储设置的前景色,将其设置到字符属性字节中
add bx,2
loop sub2s

pop es
pop cx
pop bx
ret

;设置背景色
sub3: push bx
push cx
push es

mov cl,4
shl al,cl ;颜色值左移四位,用于设置背景色
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s: and byte ptr es:[bx],10001111b ;将字符属性字节5,6,7位置为0
or es:[bx],al ;al中存储设置的前景色,将其设置到字符属性字节中
add bx,2
loop sub3s

pop es
pop cx
pop bx
ret

;向上滚动一行
sub4: push cx
push si
push di
push es
push ds

mov si,0b800h
mov es,si
mov ds,si
mov si,160 ;ds:si指向第n+1行第一个字符,每行80字符,加上字符属性设置字节,共160字符
mov di,0 ;es:di指向第n行第一个字符
cld ;正向传送
mov cx,24 ;共复制24行
sub4s: push cx
mov cx,160
rep movsb ;行中的每一个字符进行复制
pop cx
loop sub4s

mov cx,80 ;只需修改存储字符的字节
mov si,0
sub4s1: mov byte ptr [160*24+si],' ' ;最后一行清空
add si,2
loop sub4s1

pop ds
pop es
pop di
pop si
pop cx
ret

将这些功能子程序的入口地址存储在一个表中,它们在表中的位置和功能号相对应。对应关系为:功能号*2=对应的功能子程序在地址表中的偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
setscreen:jmp short set
table dw sub1,sub2,sub3,sub4
set: push bx
cmp ah,3 ;功能号与3进行比较
ja sret ;高于则转移
mov bl,ah
mov bh,0
add bx,bx ;根据ah中的功能号计算对应子程序在table表中的偏移

call word ptr table[bx] ;调用对应的功能子程序

sret: pop bx
ret

---------------The End---------------
0%