Search code examples
assemblyx86bochs

Why does BX get substituted with EDI by the assembler?


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. Bogs debugger

di holds a gibberish value while bx holds the real address of the string but the assembler decided to replace bx with edi?


Solution

  • 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.