All pastes #2078407 Raw Edit

Unnamed

public text v1 · immutable
#2078407 ·published 2011-08-25 20:58 UTC
rendered paste body
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