; 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
它背后的目的是什么? 请解释一下!!
在 16b 实模式中断处理程序代码中,您不知道段寄存器的值,它们可以是任何东西。通常你可以期望至少堆栈(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 处理程序时,您不知道正在运行的代码在中断时ds
或es
了什么。它可能指向内存中的任何位置(而不是您的变量!
但是你知道,cs
指向你的处理程序指令(否则CPU会在其他地方执行指令,因为CPU确实在cs:ip
从内存执行下一条指令)。
看看你的处理程序必须如何保留/设置/恢复es
,这就是你必须用ds
做什么的例子,才能使用ds
。
cs
已经保留在堆栈上,设置为您的代码段,IRET
(在旧处理程序中)将恢复它。
1)bp
默认使用ss
,stos/movs/cmps
默认同时使用ds
和es
,只能覆盖源ds
,目标es
固定。