实现一个子程序 setscreen,为显示输出提供如下功能:
① 清屏
② 设置前景色
③ 设置背景色
④ 向上滚动一行
|
入口参数说明:
① 用 ah 寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;
② 对于2、3号功能,用 al 传送颜色值,(al)∈{0,1,2,3,4,5,6,7}
|
assume cs:code
code segment
start: mov ax , cs
mov ds , ax
mov si , offset setscreen
mov ax , 0
mov es , ax
mov di , 200h
mov cx , offset setscreenend - offset setscreen
cld
rep movsb
mov ax , 0
mov es , ax
mov word ptr es:[7ch*4] , 200h
mov word ptr es:[7ch*4+2] , 0
mov ah , 1 ; 功能1 设置前景色
mov al , 1 ; 蓝色
int 7ch
call delay
mov ah , 2 ; 功能2 设置背景色
mov al , 3 ; 红色
int 7ch
call delay
mov ah , 3 ; 功能3 向上滚动一行
int 7ch
call delay
mov ah , 0 ; 清屏
int 7ch
call delay
mov ax , 4c00h
int 21h
delay: push ax
push dx
mov dx , 6h
mov ax , 0
s: sub ax , 1
sbb dx , 0
cmp ax , 0
jne s
cmp dx , 0
jne s
pop dx
pop ax
ret
org 200h ; 伪指令,表示下一条指令从偏移地址200h开始;因为我们把下面的中断例程安装到了0:200h
; 如果没有org 200h,中断例程安装到指定位置后,标号所代表的相对偏移地址也变了,是从200h开始算偏移的,和编译文件的时候不一样了,后面call不能找到我们要的执行代码;
|
setscreen: jmp short set ; 调试的时候变成 jmp 020Ah
; 这条命令是段内短转移,set的偏移是从当前指令开始计算的,所以前面写的代码,不用org也不会出错。
table dw sub1 , sub2 , sub3 , sub4 ; 这里的值在编译的时候编译器就设置好了
set: push bx
cmp ah , 3
ja sret
mov bl , ah
mov bh , 0
add bx , bx
call word ptr table[bx] ; 程序编译到这里的时候 CS是当前程序CS:IP的CS
;等到中断程序安装好后,table==cs:200h,cs==0;执行到这里的时候CS==0
sret: pop bx
iret
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 ; 重置前景色
or es:[bx] , 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
or es:[bx] , 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
mov di , 0
cld
mov cx , 24
sub4s1: push cx
mov cx , 160
rep movsb
pop cx
loop sub4s1
mov cx , 80
mov si , 0
sub4s2: mov byte ptr [160*24+si] , ' '
add si , 2
loop sub4s2
pop ds
pop es
pop di
pop si
pop cx
ret
setscreenend: nop
code ends
end start
|
; 上面的程序可以优化,分开来写 assume cs:code
code segment
start: mov ah , 1 ; 功能号
mov al , 1 ; 颜色值
int 7ch
call delay
mov ah , 2 ; 功能号
mov al , 2 ; 颜色值
int 7ch
mov ah , 3
int 7ch
call delay
mov ah , 0
int 7ch
mov ax , 4c00h
int 21h
delay: push ax
push dx
mov dx , 5h
mov ax , 0
s: sub ax , 1
sbb dx , 0
cmp ax , 0
jne s
cmp dx , 0
jne s
pop dx
pop ax
ret
code ends
end
|
; 上面的程序不分开来写的话,还可以优化,在执行int 21h中断前恢复原来7ch的中断例程入口地址 assume cs:code
stack segment
db 128 dup (0)
stack ends
code segment
start: mov ax , cs
mov ds , ax
mov si , offset setscreen
mov ax , 0
mov es , ax
mov di , 204h
mov cx , offset setscreenend - offset setscreen
cld
rep movsb
mov ax , stack
mov ss , ax
mov sp , 128
push es:[7ch*4] ; 保存原7ch的中断例程入口地址
pop es:[200h]
push es:[7ch*4+2]
pop es:[202h]
mov word ptr es:[7ch*4] , 204h
mov word ptr es:[7ch*4+2] , 0
; 这里是自己调用中断号测试代码和延迟代码(没放上来)
push es:[200h] ; 恢复原7ch的中断例程入口地址
pop es:[7ch*4]
push es:[202h]
pop es:[7ch*4+2]
mov ax , 4c00h
int 21h
|