1.描述了单元长度的标号

一般来说,普通的标号仅仅代表了内存单元的地址

比如:

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

还有一种标号,还可以表示内存的单元长度

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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中没有:

1
2
3
这就说明了:
标号a:描述了地址code:0和从这个地址开始,以后的内存单元都是字节单元
标号b:描述了地址code:8和从这个地址开始,以后的内存单元都是字单元

这样就nb了

比如:对于程序中的b dw 0

1
2
3
指令: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

1
2
3
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]

这种标号叫做数据标号,它标记了存储数据的单元的地址和长度【字?字节?】

【我咋感觉这个和数组差不多,数组首地址……】

2.再其他段中使用数据标号

1.一般来说:我们不在代码段中定义数据,而是将数据定义到其他段中。再其他段中,我们也可以使用数据标号来描述存储数据的单元的地址和长度

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

下面的程序将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 segemnt
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将标号所在的段和一个段寄存器联系起来

1
assume cs:code,ds:data....

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

比如

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

相当于:数据标号存储的2个字型数据为标号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,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处存储的2个双字数据为标号a的偏移地址和段地址,标号b的偏移地址和段地址

1
2
3
4
5
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dd offset a, seg a, offset b, seg b
data ends
1
seg操作符,功能是取得某一个标号的段地址

3.直接定址表

需要用到多种映射关系的时候,我们可以将它制成一张表,这样有利于我们查找

比如:015和字符0f之间建立一个映射关系

思路

1
以数值N为table表中的偏移来找到对应的字符

子程序

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
;用 al 传送要显示的数据
showbyte:
jmp short show
table db '0123456789ABCDEF' ;建立表单

show:
;设置子程序
push bx
push es

mov ah,al
mov bl,4
shr ah,bl ;右移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

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

mov es:[160*12+40*2+2],al

pop es
pop bx

code ends
end start


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

首先就是说:我们可以将这些字功能的入口地址存储再一个表中,它们在表中的位置和功能号相对应。

来设计功能吧

1.清屏:也就是将显存中当前屏幕中的字符串设为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sub1:
push bx
push cx
push es
mov bx,0b800H
mov es,bx
mov bx,0
mov cx,2000
subls:
mov byte ptr es:[bx],' '
add bx,2
loop subls

pop es
pop cx
pop bx
ret

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 ;然后用or来设置前景色中的值为al
add bx,2
loop subs2

pop es
pop cx
pop bx
ret

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
or es:[bx],al ;因为要改变4 5 6 的位置,所以在前面需要shl 将al的值移动到456位置的地方
add bx,2
loop sub3s

pop es
pop cx
pop bx
ret

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
sub4:
push cx
push si
push di
push es
push ds
;因为下面需要用到,所以要保存到
mov si,0b800h
mov es,si
mov ds,si
mov si,160
mov di,0
cld ;设置copy的方向
mov cx,24 ;一共要复制24行

sub4s: ;把n+1行的东东复制到n行
push cx
mov cx,160
rep movsb ;进行复制咯 ds:si复制到es:di中
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