BITS 16 CPU 386 ORG 7c00h %define IVT_OFFSET(n) (n*4) ; n'th entry in the IVT, offset part %define IVT_SEGMENT(n) ((n*4)+2) ; segment selector part DIV8_OPCODE equ 0xf6 DIV16_OPCODE equ 0xf7 start: cli xor ax, ax mov ds, ax ; IRQ0 : division by zero mov word [ds:IVT_OFFSET(0)], div0_handler mov word [ds:IVT_SEGMENT(0)], 0 ; IRQ9 : keyboard service needed mov word [ds:IVT_OFFSET(9)], keyb_handler mov word [ds:IVT_SEGMENT(9)], 0 sti haltcpu: hlt jmp haltcpu ; IN : al - char to be written bios_writechar: pusha mov ah, 0x0e xor bx, bx int 10h popa ret ; write a NUL-terminated string using int 10h ; IN : bx - address of the string bios_puts: push ax .loop: mov al, [bx] test al, al jz .end call bios_writechar add bx, 1 jmp .loop .end: pop ax ret ; division by zero handler div0_handler: mov ax, sp ; save the stack pointer - important! pusha push ax ; the top of the stack should now point to the instruction, which raised ; the exception. we know that it's DIV, since it's the only one that can ; report an IRQ0. the opcode is always followed by a ModRM byte, which in ; turn can have another one or two displacement bytes following it. the job ; of this handler is to skip over the bad instruction and continue execution ; in a normal way. if the IP gets popped off the stack unedited, the CPU ; will fall into an endless loop. mov bx, div_text call bios_puts mov bx, ax mov di, [bx] ; get the IP of the instruction mov al, [di] ; get the opcode cmp al, DIV8_OPCODE sete bl cmp al, DIV16_OPCODE sete cl or cl, bl jz .unk_opcode ; no idea what that is ; we're sure that it's a DIV, now get the ModRM mov bl, [di+1] mov al, bl and al, 0b11_000000 ; mask the Mod field in bl jz .checkRM ; Mod = 00 cmp al, 0b11_000000 je .skip2 ; Mod = 11 cmp al, 0b10_000000 je .skip4 ; Mod = 10 cmp al, 0b01_000000 je .skip3 ; Mod = 01 .checkRM: ; if Mod was 00, we need to check if RM=110, in which case we have to skip ; 4 bytes - otherwise, we skip 2 mov al, bl and al, 0b00_000_111 cmp al, 0b00_000_110 jne .skip2 .skip4: mov si, 4 jmp .end .skip3: mov si, 3 jmp .end .skip2: mov si, 2 jmp .end .unk_opcode: mov si, 1 ; just advance the pointer by 1 .end: pop ax mov bx, ax add di, si mov [bx], di popa iret ; keyboard handler keyb_handler: pusha .spin: in al, 0x64 and al, 1 jz .spin in al, 0x60 mov bx, keyb_text call bios_puts mov al, 0x20 out 0x20, al xor dx, dx div dx popa iret keyb_text db 'Keyboard handler',0x0d,0x0a,0 div_text db 'Division by zero',0x0d,0x0a,0 TIMES 510-($-$$) db 90h ; NOP dw 0xaa55