内存驻留程序



学习DOS下内存驻留程序的基本思想,了解与熟悉用汇编语言编写程序。本课程设计将完成一个小的.com程序,运行程序后,你的所有按键输入(指在DOS或Windows的DOS模式下)将不被接受,所有输入将被替换成特定的字符串(回车键除外)。

二、内存驻留程序的基本框架(framework of a TSR)

  内存驻留程序的基本思想就是让程序一直停留在内存中,不断的执行特定的命令。但内存驻留如何被执行呢?一般地,内存驻留程序都是通过修改BIOS或DOS的系统中断向量表来实现的。比如修改向量表中16H位置的中断(这个中断接收键盘的按键,在DOS中,按键按下,这个中断就会被调用),让其指向我的程序,这时若有按键被按下,则执行的是我的程序。下面是一个最简单的框架:

CSEG SEGMENT
  ASSUME   CS:CSEG, DS:CSEG
  ORG    100H
Start:
  JMP    Initialize
new_keyboard_io PROC FAR // 这一部分是驻留在内存的内容
  STI
  NOP
  IRET
new_keyboard_io ENDP // 到这里结束

Initialize:
  MOV DX, OFFSET new_keyboard_io // 新的键盘处理程序
  MOV AL, 16H // 需更改的向量号(interrupt index)
  MOV AH, 25H // 更改系统中断向量表
  INT 21H

MOV DX, OFFSET Initialize
  INT 27H // 将标签Initialize前的程序驻留内存

CSEG ENDS
  END Start



三、实现原来设计程序

首先,我需要还是需要捕获用户的回车键,所以需要将原来的DOS本身的键盘处理程序保留起来。下面的代码:

old_keyboard_io DD ?
……
Initialize:
……
  MOV AL, 16H          ; Interrupt index in vector table
  MOV AH, 35H          ; Get the interrupt dealing
  INT 21H            ; program's pointer
  MOV old_keyboard_io, BX    ; offset
  MOV old_keyboard_io[2], ES   ; base address
……



  old_keyboard_io用来储存原键盘处理程序的指针,其中INT 21H – AH=35H,是获得其指针,返回值在ES:BX中。ES是指针的基地址,BX是偏移量。

  其次,就是实现我原来设计的功能,截获按键信息,并改为特定的字符串。下面的实现的代码:

……
Hello_Msg DB 'Kasi, haha!'    ; string to display when catch a key-press
Msg_Index DW 0          ; which char in the string been displayed(char index)
……
new_keyboard_io PROC FAR
  ASSUME CS:CSEG, DS:CSEG
    STI

    CMP AH, 00H        ; INT 16H - AH = 0 to catch
    JE new_io_0        ; key-press func
    ASSUME DS:nothing
    JMP old_keyboard_io    ; No catch, jump to old handler
new_io_0:
    PUSHF
  ASSUME DS:nothing
    CALL old_keyboard_io
    CMP AL, 0DH        ; Is a ENTER been pressed ?
    JNE new_io_1       ; no, output string 'Kasi, haha!'
    MOV Msg_Index, 0     ; yes, reset the string index
    JMP new_io_done      ; and return
new_io_1:
    PUSH SI
    MOV SI, Msg_Index     ; Get current char index
    MOV AL, Hello_Msg[SI]   ; Get current char

    INC SI          ; Next char in the Hello_Msg
    CMP SI, 11        ; Reach the end of the Hello_Msg ?
    JNE new_io_2       ; no, jump
    MOV SI, 0         ; yes, set the char index to the beginning
new_io_2:
    MOV Msg_Index, SI     ; Save the char index
    POP SI
new_io_done:
    IRET
new_keyboard_io ENDP
……


  下面的分段说明:

    CMP AH, 00H        ; INT 16H - AH = 0 to catch
    JE new_io_0        ; key-press func
    ASSUME DS:nothing
    JMP old_keyboard_io    ; No catch, jump to old handler



  这一段代码是根据书上抄下来的,先检测AH中是否为0(INT 21H - AH=0表示用户按下键盘),不为0就进入old_keyboard_io,由系统原来的处理程序去处理用户的请求。这里”ASSUME DS:nothing”是告诉编译器忽略DS的内容,这样才能正确跳转。

new_io_0:
    PUSHF
  ASSUME DS:nothing
    CALL old_keyboard_io
    CMP AL, 0DH        ; Is a ENTER been pressed ?
    JNE new_io_1       ; no, output string 'Kasi, haha!'
    MOV Msg_Index, 0     ; yes, reset the string index
    JMP new_io_done      ; and return



  如果是有按键被按下,则先检测按键是否为回车键(0DH),如果不是则跳转到new_io_1去处理,否则将字符串的索引置0(Msg_Inedx = 0)并结束程序。

new_io_1:
    PUSH SI
    MOV SI, Msg_Index     ; Get current char index
    MOV AL, Hello_Msg[SI]   ; Get current char

    INC SI          ; Next char in the Hello_Msg
    CMP SI, 11        ; Reach the end of the Hello_Msg ?
    JNE new_io_2       ; no, jump
    MOV SI, 0         ; yes, set the char index to the beginning
new_io_2:
    MOV Msg_Index, SI     ; Save the char index
    POP SI


  若用户按下的不是回车键,将Hello_Msg[Msg_Index]这个字符放入AL中(因为AL是INT 21H – AH=16H调用的返回值)并让Msg_Index的值加1,然后判断Msg_Index是否指向Hello_Msg的尾部了,是的话将Msg_Index置0。
  这样,就完成了整个程序。

四、调试程序

  程序写好了,当然就要编译和运行。编译通过,但程序运行后却没有任何效果。
  按理说,程序应该是没有问题的,但为何没有任何效果呢?我怀疑new_keyboard_io是不是没其作用,如何检查错误呢?用debug一步步跟踪显然不明智,于是我在这里加了一个断点:

new_keyboard_io PROC FAR
  ASSUME CS:CSEG, DS:CSEG
    STI
      INT 03H        ; break point
    CMP AH, 00H        ; INT 16H - AH = 0 to catch



  编译运行,并在debug用a命令写入

mov ah, 10
mov al, 00
int 21



  手动调用INT 21H – AH=16H,希望能在程序中停住,看new_keyboard_io是否被执行了。但我在debug中一t(trace),整个debug就出问题了,原因不明,看来不能用这种方法试验。
  那我就换一个方法,用一个没有任何命令的new_keyboard_io作测试,代码如下:

CSEG SEGMENT
  ASSUME CS:CSEG, DS:CSEG
  ORG 100H
Start:
  JMP Initialize
new_keyboard_io PROC FAR
  ASSUME CS:CSEG, DS:CSEG
    STI
      NOP
    IRET
new_keyboard_io ENDP
Initialize:
  ASSUME CS:CSEG, DS:CSEG
    MOV DX, OFFSET new_keyboard_io
    MOV AL, 16H
    MOV AH, 25H
    INT 21H

    MOV DX, OFFSET Initialize
    INT 27H

CSEG ENDS
  END Start


  编译运行之后,任何按键输入都不起作用了,看来new_keyboard_io还是被执行了的,那问题就出现在我写的new_keyboard_io的代码里面了。我查了查书,INT 21H – AH=00H是接受按键消息的啊。但我还发现了一个INT 21H – AH=10H也是接受键盘消息的,会不会DOS在提示符(c:>)下用的是AH=10H呢?我马上在原程序中加了一下代码:

……
    CMP AH, 00H        ; INT 16H - AH = 0 to catch
    JE new_io_0        ; key-press func
;-------------------------------
; In the DOS prompt(C:>), DOS uses
; INT 16H - AH = 10H to get a char, not
; AH = 00H
    CMP AH, 10H        ; new added codes
    JE new_io_0
;-------------------------------
    ASSUME DS:nothing
    JMP old_keyboard_io    ; No catch, jump to old handler
……
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值