16.1 描述了单元长度的标号
前面的课程中,我们一直在代码段中使用标号来标记指令、数据、段的起始地址。比如,下面的程序将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,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
检测点16.1
下面的程序将code段中a处的8个数据累加,结果存储到b处的双字中,补全程序。
assume cs:code
code segment
a dw 1, 2, 3, 4, 5, 6, 7, 8
b dd 0
start: mov si, 0
mov cx, 8
s: mov ax, a[si] ;a是dw型的,可以直接给ax
add word ptr b[0], ax ;b是dd型的,ax只能和b的低位相加
adc word ptr b[2], 0 ;b的高位和0相加,使用adc进位加法是把低位进位加上
add si, 2 ;用si来控制a中的数据累加
loop s
mov ax, 4c00H
int 21H
code ends
end start
16.2 在其他段中使用数据标号
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
可以将标号当作数据来定义,此时,编译器将标号所表示的地址当作数据的值。
检测点16.2
下面的程序将data段中a处的8个数据累加,结果存储到b处的字中,补全程序。
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 ;把es和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
16.3 直接定址表
;用al传送要显示的数据
showbyte:
jmp short show
table db '0123456789ABCDEF' ;table字符表
show:
push ax
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
mov bl,al
mov bh,0
mov al,table[bx] ;用低4位的值作为相对于table的偏移,取得对应的字符,作为要显示字节的低位
mov es:[160*12+40*2+2],al
pop es
pop bx
ret
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
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 es
push si
mov bx,0b800h
mov es,bx ;es指向显存区域
;用以下角度值/30作为相对于table的偏移,取得对应的字符串的偏移地址,放在bx中
mov ah ,0 ;最大也才180没有超过255,用不上ah
mov bl,30
div bl ;除数为8位,被除数为ax,商放在al里面
mov bl,al ;商给bl,作为table表的索引
mov bh,0
add bx,bx
mov bx,table[bx]
;以下显示sin(x)对应的字符串
mov si,160*12+40*2
shows:
mov ah,cs:[bx] ;判断是否读到了0
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
16.4 程序入口地址的直接定址表
子程序实现功能1:清屏
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
子程序实现功能2:设置前景
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位清零
or es:[bx],al ;用or操作把012三位设置为我们想要的颜色
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret
子程序实现功能3:设置背景色
sub3:
push bx
push cx
push es
mov cl,4
shl al,cl ;al用来传送颜色,al的值为0-7,设置背景色是456位,要把al左移4位、
mov bx,0b800h
mov es,bx
mov bx,1 ;显存中的基地址为属性字节
mov cx,2000
sub3s:
and byte ptr es:[bx],10001111b ;第456位先置0,其他位不变
or es:[bx],al ;es:[bx]和al相或,设置颜色
add bx,2
loop sub3s
pop es
pop cx
pop bx
ret
子程序实现功能4:向上滚动一行
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行
mov di,0 ;es:di指向第n行
cld ;设置正向传输
mov cx,24 ;共复制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=对应的功能子程序在地址表中的偏移。程序如下:
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表中的偏移,即功能号*2=偏移
call word ptr table[bx] ;调用对应的功能子程序
sret:
pop bx
ret
我们也可以将子程序setscreen用以下命令实现:
setscreen:
cmp ah,0
je do1
cmp ah,1
je do2
cmp ah,2
je do3
cmp ah,3
je do4
jmp short sret
do1:
call sub1
jmp short sret
do2:
call sub2
jmp short sret
do3:
call sub3
jmp short sret
do4:
call sub4
jmp short sret
sret:
ret