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?