程序集 16 位中断


; 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 处理程序时,您不知道正在运行的代码在中断时dses了什么。它可能指向内存中的任何位置(而不是您的变量!

但是你知道,cs指向你的处理程序指令(否则CPU会在其他地方执行指令,因为CPU确实在cs:ip从内存执行下一条指令)。

看看你的处理程序必须如何保留/设置/恢复es,这就是你必须用ds做什么的例子,才能使用ds

cs已经保留在堆栈上,设置为您的代码段,IRET(在旧处理程序中)将恢复它。


1)bp默认使用ssstos/movs/cmps默认同时使用dses,只能覆盖源ds,目标es固定。

最新更新