; another attempt to terminate program with Esc that hooks
; keyboard interrupt
[org 0x100]
jmp start
oldisr: dd 0 ; space for saving old isr
; keyboard interrupt service routine
kbisr: push ax
push es
mov ax, 0xb800
mov es, ax ; point es to video memory
in al, 0x60 ; read a char from keyboard port
cmp al, 0x2a ; is the key left shift
jne nextcmp ; no, try next comparison
mov byte [es:0], 'L' ; yes, print L at top left
jmp nomatch ; leave interrupt routine
nextcmp: cmp al, 0x36 ; is the key right shift
jne nomatch ; no, leave interrupt routine
mov byte [es:0], 'R' ; yes, print R at top left
nomatch: ; mov al, 0x20
; out 0x20, al
pop es
pop ax
jmp far [cs:oldisr] ; call the original ISR
; iret
start: xor ax, ax
mov es, ax ; point es to IVT base
mov ax, [es:9*4]
mov [oldisr], ax ; save offset of old routine
mov ax, [es:9*4+2]
mov [oldisr+2], ax ; save segment of old routine
cli ; disable interrupts
mov word [es:9*4], kbisr ; store offset at n*4
mov [es:9*4+2], cs ; store segment at n*4+2
sti ; enable interrupts
l1: mov ah, 0 ; service 0 – get keystroke
int 0x16 ; call BIOS keyboard service
cmp al, 27 ; is the Esc key pressed
jne l1 ; if no, check for next key
mov ax, 0x4c00 ; terminate program
int 0x21
我很想知道为什么我们只能使用cs:offset来调用此程序中的oldIsr。为什么只是 CS ???
在这条线上
jmp far [cs:oldisr] ; call the original ISR
背后的目的是什么? 请解释一下!!!
答案 0 :(得分:3)
在16b实模式中断处理程序代码中,你不知道段寄存器的值,它们可以是任何东西。通常你可以期望至少stack(ss:sp
)合理有效并且足够大以存储返回地址和少量字节用于处理程序,并且cs
也固定为处理程序代码,因为如果不同cs
值将CPU执行一些不同的指令,而不是你的。
因此,为了避免存储旧的/未知ds
,设置处理程序变量的数据段以及通过ds:
前缀访问它们,然后恢复ds
的麻烦,更容易将变量放在代码本身旁边,并按cs:oldisr
解决,因为已知cs
的值是您需要的。
我会尝试以更简单的方式编写它:
mov ax,[si]
默认情况下使用ds
(* 1),即它实际上正在执行mov ax,[ds:si]
。
您可以通过明确编写段寄存器来覆盖默认值,如mov ax,[cs:si]
。
16b实模式下的物理内存地址为:segment_value * 16 + offset_value
当代码进入您的ISR处理程序时,您不知道在中断时{0}}或ds
中正在运行的代码有什么。它可能指向内存中的任何位置(而不是你的变量!)。
但是你知道,es
指向你的处理程序指令(否则CPU会在别处执行指令,因为CPU会在cs
处执行来自内存的下一条指令。)
只需了解您的处理程序如何保留/设置/恢复cs:ip
,这就是您必须使用es
进行的示例,才能使用ds
。
ds
已经保留在堆栈上,设置为您的代码段,cs
(在旧处理程序中)将恢复它。
1)IRET
默认使用bp
,ss
默认使用stos/movs/cmps
和ds
,只有源es
1}}可以被覆盖,目标的ds
是固定的。