Reputation: 33
; 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
I'm wonder that why we can only use cs:offset for the purpose to call oldIsr in this program. Why just CS ???
AT THIS LINE
jmp far [cs:oldisr] ; call the original ISR
WHAT is the purpose behind the it? Please explain it!!!
Upvotes: 1
Views: 1162
Reputation: 16626
Inside 16b real mode interrupt handler code you don't know the values of segment registers, they can be anything. Usually you can expect at least stack (ss:sp
) to be reasonably valid and large enough to store return address and few bytes for handler, and also cs
is fixed to the handler code, because in case of different cs
value the CPU would execute some different instructions, not yours.
So to avoid hassle of storing old/unknown ds
, setting up data segment of handler variables, and accessing them through ds:
prefixes, then restoring ds
back, it is easier to have the variable next to the code itself, and address it by cs:oldisr
, as the value of cs
is known to be what you need.
I will try to write it in more simple way:
mov ax,[si]
is by default using ds
(*1), i.e. it is actually doing mov ax,[ds:si]
.
You can override the default by writing the segment register explicitly like mov ax,[cs:si]
.
Physical memory address in 16b real mode is: segment_value*16+offset_value
When the code does enter your ISR handler, you do NOT know, what the running code had in ds
or es
at the moment of interrupt. It may point anywhere in the memory (NOT to your variables!).
But you know, that cs
points to your handler instructions (otherwise CPU would execute instructions elsewhere, because CPU does execute next instruction from memory at cs:ip
).
Just look how your handler has to preserve/set/restore es
, that's example what you would have to do with ds
, to be able to use the ds
.
The cs
is already preserved on stack, set to your code segment, and IRET
(in old handler) will restore it.
1) The bp
is using by default ss
, the stos/movs/cmps
are using by default both ds
and es
, and only the source ds
can be overridden, the es
for target is fixed.
Upvotes: 3