当前位置: 代码迷 >> 汇编语言 >> 王爽 第二版 课程设计2 循环显示时钟 出错
  详细解决方案

王爽 第二版 课程设计2 循环显示时钟 出错

热度:193   发布时间:2016-05-02 04:30:12.0
求助 王爽 第二版 课程设计2 循环显示时钟 出错
软盘启动后 
按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 键盘缓冲区,然后再等待按键。
  相关解决方案