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.
1 Answers
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:
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.

0000:0000to0000:03ff. - fuz