您的位置:首页 > 其它

王爽 -- 汇编语言课程设计2一些问题记录

2012-06-20 16:59 567 查看
整体程序通过重写19号中断例程实现。在19号中断例程中调用不同的子程序,实现不同的功能。

1.动态显示时间的子程序

通过循环读取cmos中的时间信息实现动态显示时间。显示过程中还要实现对F1和esc键做出相应动作。但是,不能用16h号中断来获得相应的键,然后做相应的动作。因为要动态显示时间,必须要循环读取cmos中的时间。在循环中放入读取键盘输入的16h中断,当键盘输入缓冲区为空的时候,16h中断会阻塞,不能达到动态显示的效果。因此要通过9号中断响应键盘中断的方式处理键盘输入。重写9号中断处理程序,定义按下F1和ESC键时的动作。

在此子程序中注意保存原先9号中断的入口地址,因为要重写9号中断必须调用原先的9号中断处理与硬件相关的问题。在上面显示时间的部分,使用预先设定好的bh存放显示的颜色属性值,bl存放字符的ASCII码,这样只要修改bh值就可以修改时间显示的颜色。因此在9号中断中,如果判断按下的是F1键,直接修改bh。9号中断返回到循环读取cmos时间并显示的程序段中时,bh值是新的了,显示的颜色也变了。对于esc键,首先要实现按下esc后跳出无限循环读取并显示时间的程序段。如何跳出?因为在9号中断中,最后使用IRET返回。返回的地址为中断过程压入栈中保存的CS:IP指向的地址。所以我们可以通过在栈中修改CS:IP来实现IRET返回后跳到期望的地址处。我们期望的地址就是循环显示时间的程序段下面的地址showover(显示结束)。CS部分不用修改,只要修改IP。所以在此子程序中还要预先保存esc跳转的目的地址IP。ESC的处理还要包括恢复9号中断原来的处理程序地址。子程序准备结束。

两个bug:

(1)循环显示时间的时候,用si和di分别定位要显示的字符和在屏幕上要显示的位置。在循环开始之前先初始化了两个值。在loop循环中,会修改这两个值。因此在下次刷新时间的时候需要重新对着两个值进行初始化。这样才能保证显示在同一位置和显示内容的正确。之前使用的方法是在每次显示之前使用栈来保存si和di值,一次显示结束后,再用pop恢复si和di的初始值,执行下次循环显示。这里的问题是,在出现esc键盘中断的时候,栈的push和pop操作不能保证是成对出现的,导致错误。例如,显示时间的程序段刚好运行到显示月份,这时候栈中保存着用于初始化si和di的值。按下ESC后,引发键盘中断,保存标志寄存器、CS、IP,然后进入9号键盘中断。我们定义的ESC键盘中断处理直接修改了中断返回的地址,不会回到原先保存的地址处。当我返回到showover处的时候,问题出现了。showover主要做一些寄存器的恢复和子程序的返回。但是之前压入的si和di还在栈中,这样必然导致错误。对于F1键盘中断不存在这样的问题,因为它只是修改BH值,然后返回中断时的地址处继续执行显示时间的程序段。

这里面其实涉及到一个窗口问题。对于ESC中断,如果发生在最后pop结束准备下一个循环显示的时候,程序也不会出错。但是,这个概率是相当小的,不会导致出错的窗口是在太小。

(2)新的9号中断是在原先的19H中断中出现并处理的。原先引发的19h中断会将IF置0,即在19h中断期间不允许其他可屏蔽中断发生。大部分中断都属于可屏蔽中断,9号中断就是其中之一。因此要使9号中断得以起作用,必须在在这个循环显示时间的子程序中将IF置1,然后,在按下ESC后,程序跳转到showover处,子程序准备结束。这时候要把IF重新置0.

19h中断处理程序安装代码:

assume cs:code
code segment
start:mov ax,0
mov es,ax
mov di,200h

mov ax,cs
mov ds,ax
mov si,offset int19
mov cx,offset int19_e - offset int19

mov ax,offset rst - offset int19
add ax,200h
mov [si+2],ax
mov ax,offset stt - offset int19
add ax,200h
mov [si+4],ax

mov ax,offset showt - offset int19
add ax,200h
mov [si+6],ax
mov ax,offset sett - offset int19
add ax,200h
mov [si+8],ax

cld
rep movsb

cli
mov word ptr es:[19h*4],200h
mov word ptr es:[19h*4+2],0
sti

mov ax,4c00h
int 21h

int19:jmp short s
dw 0,0,0,0   ;存储子程序地址
dw 0,0ffffh  ;第一个子程序需要转到的地址
dw 7c00h,0   ;第二个子程序需要的地址
s:push bx
push ds

mov bl,ah
mov bh,0
add bx,bx

mov ax,cs
mov ds,ax
call word ptr [bx+202h]

pop ds
pop bx
iret

rst:jmp dword ptr ds:[20ah]
ret
stt:mov ax,0
mov es,ax
mov bx,7c00h
mov al,1
mov ch,0
mov cl,1
mov dh,0
mov dl,80h
mov ah,2
int 13h
jmp dword ptr ds:[20eh]
ret

showt:jmp short showst
db 9,8,7,4,2,0
db '// ::',0
dw  0,0       ;存储原先9号中断处理程序的段地址和偏移地址
dw  0         ;esc后,跳出循环后的地址
showst:push ax       ;显示时间的子程序需要实现对F1键和esc键的相应动作,需要在此子程序中
push bx       ;重新设定9号键盘中断处理程序。此程序结束后要恢复原先的中断处理程序
push cx
push si
push di
push ds
push es

mov ax,cs  ;cs=0
mov ds,ax  ;ds=0
mov ax,0b800h
mov es,ax
mov bh,42h
mov di,720h
mov si,offset showt- offset int19
add si,202h

mov ax,si
add ax,12
mov bp,ax    ;bp用于寻址原先的9号中断处理程序的地址

push ds:[36]    ;保存原先9号中断处理程序的入口地址
pop ds:[bp]
push ds:[38]
pop ds:[bp+2]

push cs     ;设置新的9号中断处理程序地址
pop ds:[38]
mov ax,offset int9 - offset int19
add ax,200h
mov ds:[36],ax

mov ax,offset showover - offset int19
add ax,200h
mov ds:[bp+4],ax        ;按下esc后跳转出来的地址

pushf            ;打开IF标志,因为此时是在19h中断中,if=0,后面键盘中断无法响应
pop ax
or ax,0200h
push ax
popf

refresht:mov si,bp    ;出现问题了,对于按下F1键,没问题,因为程序处理完会返回到中断的位置继续执行。
sub si,12
mov di,720h    ;而按下esc键却要跳转到一个新位置执行。采用的方法是直接在栈内修改iret执行返回的地址IP
mov cx,6  ;但是看下被键盘中断的程序段,循环显示时间的程序中有push si和push di的栈操作
lpt:mov al,[si] ;如果压栈结束被esc中断,我们确实可以跳到showover处执行。但接下来刚才压入的si和di并未
out 70h,al ;出栈,而是被当作原先栈中保存的内容出栈。显然会引起错误,程序也无法正确回到int19的主程序中
in al,71h  ;简单说来,就是push和pop有很大可能不配对。解决方法:不使用栈来保存si和di。
push ax
push cx
mov cl,4
shr al,cl
pop cx
add al,30h
mov bl,al
mov es:[di],bx
pop ax
and al,0fh
add al,30h
mov bl,al
mov es:[di+2],bx

mov bl,[si+6]
mov es:[di+4],bx

inc si
add di,6
loop lpt
jmp short refresht

showover:pushf         ;显示时间程序结束,重新关闭IF
pop ax
and ax,0fdffh
push ax
popf
pop es
pop ds
pop di
pop si
pop cx
pop bx
pop ax

ret

int9:push ax
push bp
in al,60h

pushf
call dword ptr ds:[bp]

cmp al,3bh  ;F1键
je sf1
cmp al,1    ;esc键
je sesc
jmp short int9ok
sf1:inc bh
jmp short int9ok
sesc:push ds:[bp]
push ds:[bp+2]
pop ds:[38]
pop ds:[36]

mov ax,ds:[bp+4]

mov bp,sp
mov [bp+4],ax
int9ok:pop bp
pop ax
iret

sett:ret

int19_e:nop

code ends
end start


主程序:

assume cs:code,ds:data,ss:stack
data segment
table dw hint,reset,boot,clock,set_c
hint db 'pls select the program you want to run','$'
reset db 'reset pc ------- 0','$'
boot  db 'start system ----1','$'
clock db 'clock -----------2','$'
set_c db 'set clock -------3','$'
data ends
stack segment
dw 64 dup (0)
stack ends
code segment
start:mov ax,stack
mov ss,ax
mov sp,128

mov ax,data
mov ds,ax

mov ax,0b800h
mov es,ax

mov bh,0
mov dh,6
mov dl,16
mov cx,5
mov bp,0

s:push dx
mov ah,2  ;置光标
int 10h

mov dx,table[bp]  ;显示字符串
mov ah,9
int 21h

pop dx
inc dh
add bp,2
loop s

mov ah,2
int 10h   ;置光标,等待输入

push dx   ;计算输入字符偏移地址的时候会修改dx,先保护之,供后面使用
mov al,160
mul dh
mov dh,0
add dx,dx
add ax,dx
mov di,ax
pop dx

getch:mov ah,0
int 16h
cmp ah,0bh
je rst
cmp ah,2
je stt
cmp ah,3
je showt
cmp ah,4
je sett
jmp short getch
rst:mov ah,0
jmp short ok
stt:mov ah,1
jmp short ok
showt:mov ah,2
jmp short ok
sett:mov ah,3
ok:push ax
mov ah,42h
mov es:[di],ax
e_b:mov ah,0
int 16h
cmp ah,1ch  ;回车
je int19
cmp ah,0eh  ;退格
jne short e_b
mov ah,2
int 10h
mov al,0
mov ah,01110111b
mov es:[di],ax
pop ax
jmp short getch

int19:pop ax
int 19h

mov ax,4c00h
int 21h
code ends
end start
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: