I'm trying to write a real mode bootloader and I'm currently having problems trying to enable the A20 line. Here's my code so far, I'm assembling with NASM:
[bits 16]
[global _start]
jmp _start
bios_print:
lodsb
test al, al
jz bios_print_done
mov ah, 0x0E
mov bh, 0
int 0x10
jmp bios_print
bios_print_done:
ret
a20_is_enabled:
push ds
push si
push es
push di
xor ax, ax
mov ds, ax
mov si, BOOT_ID_OFFS
mov ax, BOOT_ID_OFFS_PLUS_1MB_SEGM
mov es, ax
mov di, BOOT_ID_OFFS_PLUS_1MB_OFFS
cmp word [es:di], BOOT_ID
mov ax, 1
jne a20_is_enabled_done
mov ax, word [ds:si]
xor ax, ax
mov [ds:si], ax
cmp word [es:di], BOOT_ID
push ax
xor ax, ax
mov [ds:si], ax
pop ax
mov ax, 1
jne a20_is_enabled_done
mov ax, 0
a20_is_enabled_done:
pop di
pos es
pop si
pop ds
ret
a20_enable_bios:
mov ax, 0x2403
int 0x15
jc a20_enable_bios_failure
test ah, ah
jnz a20_enable_bios_failure
mov ax, 0x2401
int 0x15
jc a20_enable_bios_failure
test ah, ah
jnz a20_enable_bios_failure
mov ax, 1
jmp a20_enable_bios_done
a20_enable_bios_failure:
mov ax, 0
a20_enable_bios_done:
ret
a20_enable:
push si
mov si, word MSG_A20_TRY_BIOS
call bios_print
pop si
call a20_enable_bios
test ax, ax
jz a20_enable_failure
call a20_is_enabled
test ax, ax
jnz a20_enable_success
a20_enable_failure:
push si
mov si, word MSG_A20_FAILURE
call bios_print
pop si
mov ax, 0
jmp a20_enable_done
a20_enable_success:
push si
mov si, word MSG_A20_SUCCESS
call bios_print
pop si
mov ax, 1
a20_enable_done:
ret
_start:
xor ax, ax
mov ds, ax
cld
cli
push si
mov si, word MSG_GREETING
call bios_print
pop si
call a20_enable
test ax, ax
jz boot_error
; TODO
boot_error:
jmp boot_error
BOOT_ID equ 0xAA55
BOOT_ID_OFFS equ 0x7DFE
BOOT_ID_OFFS_PLUS_1MB_SEGM equ 0xFFFF
BOOT_ID_OFFS_PLUS_1MB_OFFS equ BOOT_ID_OFFS + (0x1 << 20) - (BOOT_ID_OFFS_PLUS_1MB_SEGM << 4)
MSG_GREETING db 'Hello from the bootloader', 0xA, 0xD, 0
MSG_A20_TRY_BIOS db 'Trying to enable A20 line via BIOS interrupt', 0xA, 0xD, 0
MSG_A20_SUCCESS db 'Successfully enabled A20 line', 0xA, 0xD, 0
MSG_A20_FAILURE db 'Failed to enable A20 line', 0xA, 0xD, 0
times 510-($-$$) db 0
dw BOOT_ID
The problem is the function a20_is_enabled
which is supposed to check if the A20 line is enabled after a20_enable_bios
has activated it via a BIOS interrupt (I know this is not foolproof, more code will follow here). When I debug the code everything seems to be fine until call a20_is_enabled
. The processor does then indeed perform a near call to to correct address here but no return address is pushed onto the stack (which I have verified with gdb). So when ret
is executed in a20_is_enabled
, the instruction pointer is set to some garbage address. Why is this?
EDIT: note that there is not ORG 0x7C00
at the beginning of my assembly code. This is because I first create an elf file so that I can debug my code using gdb and that doesn't play well with ORG
, So I actually do this:
nasm -f elf32 -g -F dwarf boot.asm -o boot.o
ld -Ttext=0x7c00 -melf_i386 boot.o -o boot.elf
objcopy -O binary boot.elf boot.bin