中断程序设计
首先读一下以下的程序,我们来理解以下代码讲了什么?
STACK SEGMENT PARA 'STACK'
DB 100 DUP(?)
STACK ENDS
DATA SEGMENT
DATA ENDS
ASSUME DS: SEG1, CS: SEG2 ;数据段,代码段
;10
SEG1 SEGMENT
INTOFF DW ?
INTSEG DW ?
SEG1 ENDS
SEG2 SEGMENT
START:
MOV AX, SEG1
MOV DS, AX
MOV ES, AX
;21
XOR AX, AX
MOV ES, AX ;开辟ES的拓展空间
CALL STORE
CALL INIT
WAITFOR:
MOV AH, 01H ;键盘输入并且回显(AL = 输入字符)
INT 21H ;指令中断(INT 21H),然后T一下,就可以键盘输入了
CMP AL, 'Q' ;如果是'Q'就是跳转
JE KEY_Q
CMP AL, 'I' ;如果为'I'就跳转
JE KEY_I
JMP WAITFOR
KEY_Q:
CALL RESTORE
MOV AH, 4CH ;带返回码结束(AH = 4CH)
INT 21H
KEY_I:
MOV AX, 200H ;输出DX的对应的字符'A'
MOV BL, 0
DIV BL
JMP WAITFOR
STORE:
MOV AX, ES:[00H * 4] ;在前面这个ES地址中“:”寻找地址[00H * 4]
MOV INTOFF, AX
MOV AX, ES:[00H * 4 + 2] ;分别存进INTOFF与INTSEG中
MOV INTSEG, AX
RET
INIT:
MOV AX, OFFSET NEWINT
MOV ES:[00H * 4], AX ;先存下NEWINT的后4位所在位置"X:Y"(X)
MOV AX, SEG NEWINT
MOV ES:[00H * 4 + 2], AX ;再存下NEWINT的前4位所在地址(Y)
RET
RESTORE:
MOV AX, INTOFF
MOV ES:[00H * 4], AX
MOV AX, INTSEG
MOV ES:[00H * 4 + 2], AX
RET
NEWINT:
MOV BP, SP
ADD WORD PTR [BP], 2
MOV AH, 02H
MOV DL, 'A'
INT 21H
IRET
SEG2 ENDS
END START
我先写了一些注释。
最开始的时候我们会调用这个STORE:
STORE:
MOV AX, ES:[00H * 4] ;在前面这个ES地址中“:”寻找地址[00H * 4]
MOV INTOFF, AX
MOV AX, ES:[00H * 4 + 2] ;分别存进INTOFF与INTSEG中
MOV INTSEG, AX
RET
主要就是先把ES里面的前两个元素存到INTOFF中去,把ES中第三第四个元素存到INTSEG中去,保存之用,因为后面将调用的INIT也会影响到其。
然后就是RET返回去调用(CALL)INIT这个所谓的初始化调用了。
INIT:
MOV AX, OFFSET NEWINT
MOV ES:[00H * 4], AX ;先存下NEWINT的后4位所在位置"X:Y"(X)
MOV AX, SEG NEWINT
MOV ES:[00H * 4 + 2], AX ;再存下NEWINT的前4位所在地址(Y)
RET
我们会得到NEWINIT所在的地址,然后按照STORE中的方式那样,我们将原先ES:[0000]到ES:[0003]中的这4个位置,我们拿来存了NEWINIT的开始地址了,所以我们为什么之前用了INTOFF和INTSEG来存,也就是这个道理了。
再者,我们就是去跑一个WAITFOR的循环了,为什么我在这里不去使用WAIT呢?而是使用了WAITFOR,是因为WAIT其实是有这个专有名词的,我还是避开雷区吧。
WAITFOR:
MOV AH, 01H ;键盘输入并且回显(AL = 输入字符)
INT 21H ;指令中断(INT 21H),然后T一下,就可以键盘输入了
CMP AL, 'Q' ;如果是'Q'就是跳转
JE KEY_Q
CMP AL, 'I' ;如果为'I'就跳转
JE KEY_I
JMP WAITFOR
回到这个看似怎么也跳不出去的循环。
首先讲解以下INT 21H和它的搭档使用的“MOV AH, 01H”。
这两个是搭档使用的,我们INT 21H中,读它的状态,就是由AH位置的状态来转移过来的,这里有个功能表,是它的对应的功能与操作。
INT 21H 指令说明及使用方法(汇编语言学习)可以对应AH功能的学习。
那么,我们现在就知道了,AH等于01H的时候,其实就是键盘输入并且回显的这样一个作用,那么我们如果输入一个字符串,它既不是' I ' 也不是 ' Q ',那么,就会一直重复这个循环。
如果我们输入的是一个' I ',那么,我们会跳转到KEY_I:
KEY_I:
MOV AX, 200H ;输出DX的对应的字符'A'
MOV BL, 0
DIV BL
JMP WAITFOR
首先,我们让AH=02H,这是有何用意呢?我们查表可以知道,02H在“INT 21H”的时候,是用来输出使用的,输出的字符就是DL对应的字符,那么DL的字符应该是从哪里得到呢?我们这时候会去调用到NEWINIT:
NEWINT:
MOV BP, SP
ADD WORD PTR [BP], 2
MOV AH, 02H
MOV DL, 'A'
INT 21H
IRET
我们知道了,DL='A',那么输出的字符也就是‘A’了,返回。
然后我们对应的JMP去返回到WAITFOR中去。
现在看过了输入为' I '的时候,我们再来看看输入为' Q '的时候。
KEY_Q:
CALL RESTORE
MOV AH, 4CH ;带返回码结束(AH = 4CH)
INT 21H
我们呢,会调用这个函数。
这时候我们会先进入RESTORE这个函数,是什么用的呢?我们来看看:
RESTORE:
MOV AX, INTOFF
MOV ES:[00H * 4], AX
MOV AX, INTSEG
MOV ES:[00H * 4 + 2], AX
RET
我们中断返回,把原来放在ES[0000]~ES:[0003]的原先的数就给返回回来了,也就是把中断返回的过程了。然后我们RET回来再看。
我们让AH=4CH,查表可以知道,这时候再调用“INT 21H”是带返回码结束的这样一个作用,也就是在这里的时候,我们就退出了循环的操作了。也是结束了。
接下去,我们改变了存放的地址,再来看:
我们要做的是编写60H中断程序
将程序改写为响应60H中断请求,并要求使用DOS功能调用的方式设置中断向量。
主要是将原来使用[00H * 4]的地方改成了[60H * 4]。
STACK SEGMENT PARA 'STACK'
DB 100 DUP(?)
STACK ENDS
DATA SEGMENT
DATA ENDS
ASSUME DS: SEG1, CS: SEG2 ;数据段,代码段
;10
SEG1 SEGMENT
INTOFF DW ?
INTSEG DW ?
SEG1 ENDS
SEG2 SEGMENT
START:
MOV AX, SEG1
MOV DS, AX
MOV ES, AX
;21
XOR AX, AX
MOV ES, AX ;开辟ES的拓展空间
CALL STORE
CALL INIT
WAITFOR:
MOV AH, 01H ;键盘输入并且回显(AL = 输入字符)
INT 21H ;指令中断(INT 21H),然后T一下,就可以键盘输入了
CMP AL, 'Q' ;如果是'Q'就是跳转
JE KEY_Q
CMP AL, 'I' ;如果为'I'就跳转
JE KEY_I
JMP WAITFOR
KEY_Q:
CALL RESTORE
MOV AH, 4CH ;带返回码结束(AH = 4CH)
INT 21H
KEY_I:
INT 60H
NOP
JMP WAITFOR
STORE:
MOV AX, ES:[60H * 4] ;在前面这个ES地址中“:”寻找地址[00H * 4]
MOV INTOFF, AX
MOV AX, ES:[60H * 4 + 2] ;分别存进INTOFF与INTSEG中
MOV INTSEG, AX
RET
INIT:
MOV AX, OFFSET NEWINT
MOV ES:[60H * 4], AX ;先存下NEWINT的后4位所在位置"X:Y"(X)
MOV AX, SEG NEWINT
MOV ES:[60H * 4 + 2], AX ;再存下NEWINT的前4位所在地址(Y)
RET
RESTORE:
MOV AX, INTOFF
MOV ES:[60H * 4], AX
MOV AX, INTSEG
MOV ES:[60H * 4 + 2], AX
RET
NEWINT:
MOV BP, SP
ADD WORD PTR [BP], 2
MOV AH, 02H
MOV DL, 'A'
INT 21H
IRET
SEG2 ENDS
END START
但是题目中要求的是,让我们使用DOS功能调用的方式设置中断向量。
所以在KEY_I的时候需要作出一些改动(仅改动了KEY_I):
STACK SEGMENT PARA 'STACK'
DB 100 DUP(?)
STACK ENDS
DATA SEGMENT
DATA ENDS
ASSUME DS: SEG1, CS: SEG2 ;数据段,代码段
;10
SEG1 SEGMENT
INTOFF DW ?
INTSEG DW ?
SEG1 ENDS
SEG2 SEGMENT
START:
MOV AX, SEG1
MOV DS, AX
MOV ES, AX
;21
XOR AX, AX
MOV ES, AX ;开辟ES的拓展空间
CALL STORE
CALL INIT
WAITFOR:
MOV AH, 01H ;键盘输入并且回显(AL = 输入字符)
INT 21H ;指令中断(INT 21H),然后T一下,就可以键盘输入了
CMP AL, 'Q' ;如果是'Q'就是跳转
JE KEY_Q
CMP AL, 'I' ;如果为'I'就跳转
JE KEY_I
JMP WAITFOR
KEY_Q:
CALL RESTORE
MOV AH, 4CH ;带返回码结束(AH = 4CH)
INT 21H
KEY_I:
CLI
PUSH DS
MOV DX, OFFSET NEWINT
MOV AX, SEG NEWINT
MOV DS, AX
MOV AH, 60H
MOV AL, 0
INT 60H
POP DS
STI
STORE:
MOV AX, ES:[60H * 4] ;在前面这个ES地址中“:”寻找地址[00H * 4]
MOV INTOFF, AX
MOV AX, ES:[60H * 4 + 2] ;分别存进INTOFF与INTSEG中
MOV INTSEG, AX
RET
INIT:
MOV AX, OFFSET NEWINT
MOV ES:[60H * 4], AX ;先存下NEWINT的后4位所在位置"X:Y"(X)
MOV AX, SEG NEWINT
MOV ES:[60H * 4 + 2], AX ;再存下NEWINT的前4位所在地址(Y)
RET
RESTORE:
MOV AX, INTOFF
MOV ES:[60H * 4], AX
MOV AX, INTSEG
MOV ES:[60H * 4 + 2], AX
RET
NEWINT:
MOV BP, SP
ADD WORD PTR [BP], 2
MOV AH, 02H
MOV DL, 'A'
INT 21H
IRET
SEG2 ENDS
END START