软盘启动后
按3进入 显示时钟模式 改写了int 9h 中断例程 主要是在循环读入时钟时 能够接收键盘输入
按F1改变颜色
按ESC 返回到主选单 并将int 9h例程改回原来的
当改变颜色超过大约16次后 按任何键 都会有报警声
按键次数过多后 按键不起任何作用,也无法按ESC退出
麻烦大神帮忙看看 !!!!!!!!!!!!!!
assume cs:code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;数据段
data segment
data ends
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;程序段
code segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;主程序
;;;将功能程序写进软盘
start:
mov ax,cs
mov es,ax
mov bx,offset beginrw
;mov ax,offset beginrw-offset endrw
;计算要复制的字节占几个扇区,实际复制时多加一个扇区,就不用考虑余数了
;mov dx,0
;mov cx,512
;div cx
;inc al
mov al,3
mov ch,0
mov cl,1
mov dl,0
mov dh,0
mov ah,3
int 13h
mov ax,4c00h
int 21h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;子程序位置
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;任务程序部分
beginrw:
x: jmp short beginner
hc: dw 0,0,0,0 ;保存int 9h的段地址 偏移地址
a: db '1) reset pc',0
b: db '2) start system',0
c: db '3) clock',0
d: db '4) set clock',0
e: db 'Please input the number:',0
hr: db '00/00/00 00:00:00',0 ;年月日 时分秒
sj: dw 9,8,7,4,2,0
s: dw offset a-offset x, offset b-offset x, offset c-offset x,offset d-offset x,offset e-offset x
jpsr: dw 0 ;保存键盘输入的字符的扫描码
beginner:
;;;;;;;;;;;;;;;;;;;;; 装载其他扇区
mov ax,7c0h
mov es,ax
mov bx,512
mov al,2 ;装入2个扇区
mov ch,0
mov cl,2 ;从第二个扇区开始
mov dl,0
mov dh,0
mov ah,2
int 13h
call xshxx ;显示选项
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;调用int 16h中断,从键盘缓冲区读取字符 ah=扫描码 al=ASCII码
chxshr:
mov ah,0
int 16h
cmp ah,2
jb chxshr ;扫描码小于2 则重新读取字符
je jrscp ;扫描码等于2 表示选择1选项
cmp ah,3
je jstsys ;扫描码等于3 表示选择2选项
cmp ah,4
je jxhhour ;扫描码等于4 表示选择3选项
cmp ah,5
je jstclk ;扫描码等于5 表示选择4选项
ja chxshr ;扫描码大于5 重新读取字符
jrscp: call rscp ;1
jstsys: call stsys ;2
jxhhour: call qingp
call gn3 ;3
call qingp
jmp short beginner ;从功能3退出后 重新显示选项
jstclk: call qingp
call stclk ;4
call qingp
jmp short beginner
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gn3:
push ax
push bx
push cx
push dx
push ds
mov ax,7c0h
mov ds,ax
mov bx,offset hc-offset x
mov ax,0
mov es,ax
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ax,es:[9h*4]
mov ds:[bx],ax ;ip
mov ax, es:[9h*4+2]
mov ds:[bx+2],ax ;原int 9h的 cs保存
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cli ;关闭外中断,设置新的int9h 中断
mov word ptr es:[9h*4+2],7c0h ;设置段地址
mov word ptr es:[9h*4],offset xjpzd-offset x ;设置偏移地址
sti ;打开外中断 等待键盘输入
;当有键盘输入时 中断程序调用xjpzd子程序
;获取键盘输入的字符 存入jpsr内存空间
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov cl,00101110b ;设置显示的颜色
xhhour:
call xshour
;;;;;;;;;;;;;;;;;;;;;;;;;;判断键盘输入的字符
mov bx,offset jpsr-offset x
mov al,ds:[bx]
mov byte ptr ds:[bx],0 ;将取走的字符清零
cmp al,1
je overgn3 ;扫描码等于1 即按下ESC键 时间调用结束
cmp al,3bh
jne xhhour ;扫描码不等于F1 循环显示时间
inc cl ;设置颜色
jmp short xhhour ;循环显示时间
overgn3:
;;;;;;;;还原int 9h中断的入口地址
cli
mov bx,0
mov ds,bx
mov ax,7c0h
mov es,ax
mov bx,offset hc-offset x
mov ax,es:[bx+2]
mov ds:[9h*4+2],ax ;原int 9h的 cs
mov ax,es:[bx]
mov ds:[9h*4],ax ;ip
sti
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
xjpzd: ;处理ESC与F1 的 中断例程
push ax
push bx
push es
in al,60h
;cmp al,1
;je bcccc
;cmp al,3bh
;jne xjpzds
bcccc:
mov bx,7c0h
mov es,bx
mov bx,offset jpsr-offset x
mov es:[bx],al ;保存扫描码
xjpzds:
;;;;;调用原int 9h中断,
;mov bx,offset hc-offset x
pushf
call dword ptr es:[2]
pop es
pop bx
pop ax
iret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;4
stclk:
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;3显示时间
xshour:
push ax
push bx
push si
push ds
push cx
mov ax,7c0h
mov ds,ax
mov si,offset sj-offset x
mov bx,offset hr-offset x
mov cx,6
nyrsfm:
push cx
mov ax,ds:[si]
;mov ah,0
out 70h,al
in al,71h
mov cl,4 ;位移数
shl ax,cl ;ah中是低位字节
shr al,cl ;al中是高位字节
add ax,3030h ;将数字变为对应的字符
mov ds:[bx],ah ;ax存入字符串中
mov ds:[bx+1],al
add bx,3
add si,2
pop cx
loop nyrsfm
;dh=行号(0~24) dl=列号(0~79)
;cl=颜色 ds:si指向字符串首地址
mov dh,8
mov dl,32
pop cx ;颜色
mov si,offset hr-offset x
call show_str
pop ds
pop si
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;2引导现有操作系统
stsys:
mov ax,0
mov es,ax
mov bx,7c00h ;es:bx指向0:7c00的内存单元
push bx
push ax
mov al,1
mov ch,0
mov cl,1
mov dl,80h
mov dh,0
mov ah,2
int 13h
mov bx,sp
jmp dword ptr ss:[bx]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
rscp: ;1重启系统
mov ax,0ffffh ;段地址
push ax
mov ax,0 ;偏移地址
push ax
mov bx,sp
jmp dword ptr ss:[bx]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
xshxx:
push ax
push bx
push cx
push dx
push ds
push si
mov ax,7c0h
mov ds,ax
mov dh,2 ;第2行
mov dl,30 ;第30列
mov bx,offset s-offset x
mov cx,5
shstrrw:
mov si,[bx]
push cx
mov cl,01001110b ;颜色
call show_str
add bx,2
inc dh
pop cx
loop shstrrw
pop si
pop ds
pop dx
pop cx
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;在指定位置,用指定颜色显示以0结尾的字符串
;dh=行号(0~24) dl=列号(0~79)
;cl=颜色 ds:si指向字符串首地址
;无返回值
show_str:
push es
push ax
push bx
push dx
push di
push cx
push ds
push si
mov ax,0b800h
mov es,ax ;es指向显存
;计算行地址
mov ah,160 ;每行有160个字符
mov al,dh
mul ah
mov bx,ax ;将行地址放入bx中
;计算列地址
add dl,dl
mov dh,0
mov di,dx ;将列地址放入di中
;es:[bx+di]指向显示字符串的位置,行加160,列加2
mov ah,cl ;将颜色送入ah中存放
str_xh:
mov cl,ds:[si]
cmp cl,0
je str_fh ;cx为0时子程序返回
mov al,cl
mov es:[bx+di],ax ;将字符和字符的属性送入显存
add di,2 ;指向下一列
inc si ;指向下一个字符
jmp short str_xh ;用jmp进行循环 jcxz的条件满足后跳出循环
str_fh:
pop si
pop ds
pop cx
pop di
pop dx
pop bx
pop ax
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;清屏
qingp:
push bx
push cx
push es
push si
mov cx,0b800h
mov es,cx
mov bx,0 ;指向行 步进为160字节 循环25次
mov cx,25
qpl:
push cx
mov cx,80
mov si,0 ;指向列 步进2字符 循环80次
qph:
mov byte ptr es:[bx+si],' '
mov al,es:[bx+si+1]
and al,00001111b
mov es:[bx+si+1],al
add si,2
loop qph
pop cx
add bx,160
loop qpl
pop si
pop es
pop cx
pop bx
ret
endrw:
nop
code ends
end start
------解决思路----------------------
你自己的不改变 bios 的键盘缓存吧。
我的意思是,如果进入 3. 后的按键调用原 int9 后仍然被放进了 bios 的键盘缓冲区,在主菜单那里 int16h 会被读取到;这个,要么干脆都使用 int16h (功能 3. 处可以用 AH=01h 功能检测是否有按键),要么就得在主菜单处先读取空 bios 键盘缓冲区,然后再等待按键。