I wrote a 16 bit print function using BIOS ints but it infinitely printed the same gibberish so i went into the Bochs debugger and went through each instruction in my function and noticed that the instruction mov dl,[bx]
that i had written got replaced by mov dl,ds:[edi]
, machine code 67 8A 17
.
I'm aware that the ds segment selector being added is normal but why did bx
get replaced with di
?
Here is the full code for the print_str
function:
;BX = ptr to null terminaetd string
print_str:
pusha
next_char:
mov dl,[bx]
test dl,dl
jz print_str_return
mov ah,0x0E
mov al,dl
int 0x10
inc bx
jmp next_char
print_str_return:
popa
ret
This only happens when I call the function; if I copy the function's body to where the call
instruction should be, the assembler does not replace bx
with edi
. I'm very confused by this.
The code for boot.asm looks like this:
[extern kernel_entry]
[bits 16]
mov [BOOT_DRIVE], dl
mov ax,0
mov ds,ax
mov es,ax
mov ss,ax
;Set up a temporary stack (~65kB)
mov ax,0xFFFF
mov sp,ax
mov bp,sp
...
mov bx,msg
call print_str
...
%include "printer.asm"
msg: db "test string",0
...
Here is what the instruction looks like according to the bochs debugger.
di
holds a gibberish value while bx
holds the real address of the string but the assembler decided to replace bx
with edi
?
I solved the issue.
The [bits 32]
directive was the culprit. The assembler was apparently encoding my print_str
function as 32 bit instructions (because all my includes are at the end of the file and the directive is right above them). All i did to fix this is re-add the [bits 16]
directive before my includes but after the 32 bit protected mode code. Here is the code:
cli ;Disable interrupts
lgdt [gdtr_data] ;Load the GDT
;Set first bit (protected mode enable) in control register 0
mov eax,cr0
or eax,0x1
mov cr0,eax
jmp CODE_SEGMENT:post_flush
post_flush:
;-----------------------------------------------32 bit Protected mode-----------------------------------------------
[bits 32]
;Set up a new kernel stack (~630 kB)
mov eax,0x9FC00
mov ebp,eax
mov esp,ebp
call kernel_entry ;Jump to kernel, 'void kernel_entry()' is defined on src/MKernel/entry.c
jmp $
[bits 16]
%include "printer.asm"
...
From what the comments have pointed out, it seems like the way x86 encodes the 16 bit instruction mov dl,ds:[bx]
is identical to the way it would encode the 32 bit instruction mov dl,ds:[edi]
which was exactly my problem.