Assembly 16-bit interrupts

; 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

Answers (1)

Ped7g
Ped7g

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

Related Questions