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

来源:互联网 发布:字幕滚动编辑软件 编辑:程序博客网 时间:2024/05/28 17:07

整体程序通过重写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:codecode 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:nopcode endsend start



主程序:

assume cs:code,ds:data,ss:stackdata 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 endsstack segment     dw 64 dup (0)stack endscode 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 21hcode endsend start