I'm using QEMU and developing strictly 32-bit (personal preference), using NASM as my assembler. Now I believe the way I put this disk image together is quite different from many suggestions here, and it may be part of my issue. I plan to do this all in assembly, no C whatsoever. Again, personal preference, even if it is an order of magnitude harder to do.
About my issue...
When loading up my IDT, I'm having issues with the section inside each entry that references the OFFSETs. I believe this is because when I compile, it is not truly one large ASM file, so the dynamic addressing/allocation in NASM is acting screwy and thus my IDT doesn't point to the proper places. It seems it's mostly properly configured because it is crashing when I press a key, so at least the interrupts are working!
Code: Select all
%define SECTION_BASE 0x1000
PIC1 equ 0x20 ; IO base address for master PIC.
PIC2 equ 0xA0 ; IO base address for slave PIC.
PIC1_COMMAND equ PIC1
PIC1_DATA equ PIC1+1
PIC2_COMMAND equ PIC2
PIC2_DATA equ PIC2+1
PIC_EOI equ 0x20 ; End-of_interrupt command code.
%macro ISR_OFFSETS 1
ISRLOW%1 equ (SECTION_BASE + isr%1 - $$) & 0xFFFF ; lower 16 bits of offset
ISRHIGH%1 equ (SECTION_BASE + isr%1 - $$) >> 16 ; upper 16 bits of offset
%endmacro
%macro ISR_NOERRORCODE 1
isr%1:
cli
push byte 0
push byte %1
jmp isr_common
%endmacro
%macro IDTENTRY 1
.entry%1:
dw ISRLOW%1 ; Offset 0-15
dw CODE_SELECTOR ; Selector (from GDT)
db 0 ; reserved
db 10001110b ; Present, Ring 0, (0) Storage, 32-bit int gate
dw ISRHIGH%1 ; Offset 16-31
%endmacro
IDT:
IDTENTRY 0
...
IDTENTRY 33 ;keyboard
...
IDTENTRY 47
IDT_Desc:
dw $ - IDT - 1 ; IDT size
dd IDT ; IDT Offset/Base
; Set up ISRs
ISR_NOERRORCODE 0
...
ISR_NOERRORCODE 33 ; keyboard
...
ISR_NOERRORCODE 47
; Define ISR location data for IDT Entries
ISR_OFFSETS 0
...
ISR_OFFSETS 31 ; end of built-in software interrupts
ISR_OFFSETS 32 ; PIC HARDWARE INTERRUPTS START HERE (0x20)
ISR_OFFSETS 33 ; PIC keyboard IRQ (remapped)
...
ISR_OFFSETS 47
PICmaster_Mask dw 0
PICslave_Mask dw 0
PIC_sendEOI: ; send end-of-interrupt command to PIC(s)
; ARGS -> 1: irq #
mov ebx, [esp + 4] ; last irq on stack
mov ax, PIC_EOI
cmp ebx, 8
jl PIC_sendEOI.skipSlave
mov dx, PIC2_COMMAND
out dx, ax
.skipSlave:
mov dx, PIC1_COMMAND
out dx, ax
ret
PIC_remap: ; bh = offsetMaster, bl = offsetSlave
; Save masks
in al, PIC1_DATA
mov byte [PICmaster_Mask], al
in al, PIC2_DATA
mov byte [PICslave_Mask], al
; Initialization command 0x11
mov al, 0x11
out PIC1_COMMAND, al
out PIC2_COMMAND, al
; Update vector offsets
mov al, bl
out PIC1_DATA, al
mov al, bh
out PIC2_DATA, al
; Cascading (skip for now)
xor al, al
out PIC1_DATA, al
out PIC2_DATA, al
; Additional environment information.
mov al, 1
out PIC1_DATA, al
out PIC2_DATA, al
; Restore masks
mov byte al, [PICmaster_Mask]
out PIC1_DATA, al
mov byte al, [PICslave_Mask]
out PIC2_DATA, al
ret
isr_common:
; Code doesn't even get here, so if this is erroneous, please don't mind (but tips are welcome)
pushad ; save state (EDI, ESI, EBP, ESP, EBX, EDX, ECX, EAX) -> 32 bytes
mov ax, ds ; ax = current data segment selector
push eax ; saved onto the stack (4 bytes)
mov ax, DATA_SELECTOR ; activate the ring 0 (kernel) data selector
mov ds, ax ; this handles calls with highest permission
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
pop eax ; restore original selector to all data segments
mov ds, ax ; useful for userspace implementations WAY later
mov es, ax
mov fs, ax
mov gs, ax
popad ; restore state
add esp, 8 ; clean up extra stack variables from IRQ routines (pushed error codes and ISR numbers)
sti ; set interrupts
iret
isr_handler:
; Code doesn't get here either, so don't mind.
mov dword [esi], 0x00515249 ; "IRQ" <-- I did this instead of a string ptr because those don't work either
mov dx, 0x0707
mov bl, 0x0F
call _screenWrite
mov eax, [esp + 40] ; reach back into the stack and pull out the IRQ# pushed earlier
;call _screenPrintDecimal
ret
Code: Select all
;NO org statement, loaded by bootloader to 0x1000
GLOBAL kernel_main
[BITS 32]
...
;includes and blah blah
...
; This is all functional. But actually pressing a key will cause a crash and instant reboot.
kernel_main:
cld
lidt [IDT_Desc]
call _screenCLS
pushad
mov word [cursorOffset], 0x0A01
mov dx, [cursorOffset]
mov dword [esi], 0x00636465 ;"dce"
mov bl, 0x0F ; attrib
call _screenWrite
mov dword [esi], 0x00636465 ; just testing again for the cursor position updates
mov bl, 0x4E
call _screenWrite
mov bl, 0x0A
mov esi, szTestHello ; <-- This isn't the problem I'm asking about per se, but it could be related
call _screenWrite ; Does NOT work, no matter how I try to move the pointer around.
mov word [cursorOffset], dx
popad
mov bh, 0x20
mov bl, 0x28
call PIC_remap
; Unmask only the keyboard for now (bits !NOT! flagged are enabled IRQs)
mov al, 0xFD ; mask = 1111 1101 // PIC1 IRQ #1 (0 being the clock), keyboard enabled.
out PIC1_DATA, al
mov al, 0xFF
out PIC2_DATA, al
sti
hlt
And before I forget, here's my very simple compiling script... Yeh, I'm doing this on Windows (pls no bully )
Code: Select all
nasm "BOOT.asm" -f bin -o "..\bin\boot.bin"
nasm "KERNEL.asm" -f bin -o "..\bin\kernel.bin"
dd if="..\bin\boot.bin" of="..\bin\image.img" bs=512
dd if="..\bin\kernel.bin" of="..\bin\image.img" bs=512 seek=1
"%PROGRAMFILES%\qemu\qemu-system-i386" -drive format=raw,index=0,file="..\bin\image.img"
Also, please go easy on me.
Thanks in advance!