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

整体程序通过重写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


你可能感兴趣的:(c,汇编,table,System,存储,语言)