8

I am writing my own OS in 16 bit x86 Assembly, and I'm trying to register my own interrupt, something like INT 21H in MS-DOS. I couldn't find anything on the web. I'm using NASM as the assembler.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Xyz
  • 212
  • 2
  • 9

1 Answers1

12

You can turn off interrupts and then modify the interrupt vector table (IVT) directly. The real mode IVT can be found at 0x0000:0x0000 through 0x0000:0x03FF. Each entry in the table is 4 bytes. The first 2 bytes are the offset of the interrupt service routine (ISR) and the second 2 bytes are the segment.

As an example the keyboard interrupt (IRQ1) is interrupt vector 9. It will be located at offset 9*4=36=0x0024, Thus the IVT entry will be at 0x0000:0x0024. Here is an example of a simple bootloader that hooks the keyboard interrupt:

KBD_BUFSIZE equ 32                 ; Keyboard Buffer length. **Must** be a power of 2
                                   ;     Maximum buffer size is 2^15 (32768)
KBD_IVT_OFFSET equ 9*4             ; Base address of keyboard interrupt (IRQ) in IVT

bits 16
org 0x7c00

start:
    xor ax, ax                     ; AX=0
    mov ds, ax                     ; DS=0 since we use an ORG of 0x7c00.
                                   ;     0x0000<<4+0x7C00=0x07C00
    mov ss, ax
    mov sp, 0x7c00                 ; SS:SP stack pointer set below bootloader

    ; ****** Hooks the keyboard interrupt here ******
    cli                            ; Don't want to be interrupted when updating IVT
    mov word [KBD_IVT_OFFSET], kbd_isr
                                   ; DS set to 0x0000 above. These MOV are relative to DS 
                                   ; 0x0000:0x0024 = IRQ1 offset in IVT
    mov [KBD_IVT_OFFSET+2], ax     ; 0x0000:0x0026 = IRQ1 segment in IVT
    sti                            ; Enable interrupts

    mov ax, 0xb800
    mov es, ax                     ; Set ES to text mode segment (page 0)
    xor di, di                     ; DI screen offset = 0 (upper left)
    mov ah, 0x1F                   ; AH = White on Blue screen attribute
    mov bx, keyboard_map           ; BX = address of translate table used by XLAT
    cld                            ; String instructions set to forward direction

.main_loop:
    hlt                            ; Halt processor until next interrupt
    mov si, [kbd_read_pos]
    cmp si, [kbd_write_pos]
    je .main_loop                  ; If (read_pos == write_pos) then buffer empty and
                                   ;     we're finished

    lea cx, [si+1]                 ; Index of next read (tmp = read_pos + 1)
    and si, KBD_BUFSIZE-1          ; Normalize read_pos to be within 0 to KBD_BUFSIZE
    mov al, [kbd_buffer+si]        ; Get next scancode
    mov [kbd_read_pos], cx         ; read_pos++ (read_pos = tmp)
    test al, 0x80                  ; Is scancode a key up event?
    jne .main_loop                 ;     If so we are finished

    xlat                           ; Translate scancode to ASCII character
    test al, al
    je .main_loop                  ; If character to print is NUL we are finished
    stosw                          ; Display character on console in white on blue

    jmp .main_loop

; Keyboard ISR (IRQ1)
kbd_isr:
    push ax                        ; Save all registers we modify
    push si
    push cx

    in al, 0x60                    ; Get keystroke

    mov cx, [cs:kbd_write_pos]
    mov si, cx
    sub cx, [cs:kbd_read_pos]
    cmp cx, KBD_BUFSIZE            ; If (write_pos-read_pos)==KBD_BUFSIZE then buffer full
    je .end                        ;    If buffer full throw char away, we're finished

    lea cx, [si+1]                 ; Index of next write (tmp = write_pos + 1)
    and si, KBD_BUFSIZE-1          ; Normalize write_pos to be within 0 to KBD_BUFSIZE
    mov [cs:kbd_buffer+si], al     ; Save character to buffer
    mov [cs:kbd_write_pos], cx     ; write_pos++ (write_pos = tmp)

.end:
    mov al, 0x20
    out 0x20, al                   ; Send EOI to Master PIC

    pop cx                         ; Restore all modified registers
    pop si
    pop ax
    iret

align 2
kbd_read_pos:  dw 0
kbd_write_pos: dw 0
kbd_buffer:    times KBD_BUFSIZE db 0

; Scancode to ASCII character translation table
keyboard_map:
    db  0,  27, '1', '2', '3', '4', '5', '6', '7', '8'    ; 9
    db '9', '0', '-', '=', 0x08                           ; Backspace
    db 0x09                                               ; Tab
    db 'q', 'w', 'e', 'r'                                 ; 19
    db 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0x0a       ; Enter key
    db 0                                                  ; 29   - Control
    db 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';'   ; 39
    db "'", '`', 0                                        ; Left shift
    db "\", 'z', 'x', 'c', 'v', 'b', 'n'                  ; 49
    db 'm', ',', '.', '/', 0                              ; Right shift
    db '*'
    db 0                                                  ; Alt
    db ' '                                                ; Space bar
    db 0                                                  ; Caps lock
    db 0                                                  ; 59 - F1 key ... >
    db 0,   0,   0,   0,   0,   0,   0,   0
    db 0                                                  ; < ... F10
    db 0                                                  ; 69 - Num lock
    db 0                                                  ; Scroll Lock
    db 0                                                  ; Home key
    db 0                                                  ; Up Arrow
    db 0                                                  ; Page Up
    db '-'
    db 0                                                  ; Left Arrow
    db 0
    db 0                                                  ; Right Arrow
    db '+'
    db 0                                                  ; 79 - End key
    db 0                                                  ; Down Arrow
    db 0                                                  ; Page Down
    db 0                                                  ; Insert Key
    db 0                                                  ; Delete Key
    db 0,   0,   0
    db 0                                                  ; F11 Key
    db 0                                                  ; F12 Key
    times 128 - ($-keyboard_map) db 0                     ; All other keys are undefined

times 510 - ($-$$) db 0                                   ; Boot signature
dw 0xAA55

When run from BOCHS it would appear like:

enter image description here

For more information on how this particular keyboard ISR works you can see my previous Stackoverflow answer


If your intention is to create your own Int 21h handler, you will need to update the IVT like the example above but the offset in the IVT will be 0x21*4 = 0x0000:0x0084.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Would it be safe to update the IVT with one atomic 32-bit store, like `mov dword [KBD_IVT_OFFSET], kbd_isr`, without disabling interrupts? (I know that would require a 386-compatible CPU, and not saying it would be a better answer. I'm just curious.) – Peter Cordes Aug 05 '18 at 19:24
  • 1
    @PeterCordes That doesn't exist on an 8086, and since this is in a bootloader unless you are targeting other processors THIS is the preferred way of doing it for 16-bit code. The OP also said he is writing a 16-bit OS. Unless I specify otherwise I don't even target 80186- I'll target the lowest common denominator the 8086 – Michael Petch Aug 05 '18 at 19:27
  • I know all that, see the 2nd half of my comment. It would be safe on a 386, though, right? – Peter Cordes Aug 05 '18 at 19:28
  • @PeterCordes : On a 386 (in real mode) sure, but then it would no longer be 16-bit. – Michael Petch Aug 05 '18 at 19:31
  • Thanks. And BTW, if you want to be really belt-and-suspenders, would you `cli` before setting `ss` and `sp`, in case of buggy 8086 CPUs that don't defer interrupts properly on `mov` to `ss`? – Peter Cordes Aug 05 '18 at 19:31
  • @PeterCordes You can see my previous answers that discuss that very fact. Something I discuss in my [General Bootloader Tips](https://stackoverflow.com/a/32705076/3857942) – Michael Petch Aug 05 '18 at 19:32