Compilation – BIOS INT 10H Printing garbage on QEMU

I have a problem writing x86 real-mode assembler that runs as a bootloader in QEMU. I am trying to print text via BIOS interrupt 0x10. My code is:

< /p>

print:
pusha
.loop:
mov AL, [SI]
cmp AL, 0
je .end
call printChar
inc SI
jmp .loop
.end:
popa
ret

printChar:
pusha
mov AH, 0x0E
mov BH, 0
mov BL, 0x0F
int 0x10
popa
ret

I used [ORG 0x7c00] as the origin. I tested the printChar tag and called it with some letters in AL and it worked fine. When I tried to load the memory address to a message like this:

loadMsg db "Loading",0
mov SI, loadMessage
call print

I output garbage like’U’ as output on the QEMU emulator. Yesterday, I wrote a code similar to this, and there was no problem at all. What caused my problem and how to solve it?

I recently wrote some General Bootloader Tips in Stackoverflow, which may be useful for you .May hint #1 applies to your question:

When the BIOS jumps to your code you can’t rely on CS,DS,ES, SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register .

Based on the fact that printChar works, and writing the entire string does not mean DS: SI does not point to the correct location in the memory where the string is located. The usual reason is when the BIOS jumps When it comes to the bootloader, the developer mistakenly believes that the CS and/or DS registers are set correctly. It must be set manually. In your case, the offset you used is 0x7C00. The value in DS 0 will produce the correct physical address of (0 << 4)0x7c00 = 0x07c00. You can set DS to 0 at the beginning of the program, for example:

xor ax, ax; set AX to zero
mov ds, ax; DS = 0

In the case of QEMU, the BIOS jumps to 0x07c0: 0x0000. This also means the same physical memory location (0x07c0 <4) 0 = 0x07c00. Such a jump The transfer will set CS = 0x07c0 (not CS = 0). Since there are many segments: offset pairs mapped to the same physical memory location, DS needs to be set appropriately. You cannot rely on CS as the value you expect. So in QEMU , Like this Such code cannot even set DS correctly (when ORG 0x7c00 is used):

mov ax, cs
mov ds, ax; Make DS=CS

This may apply For some emulators, such as DOSBOX and some physical hardware, but not all. The environment where this code can work is when the BIOS jumps to 0x0000:0x7c00. In this case, CS will be zero when it reaches the bootloader code. And copying CS to DS will work. Don’t think that CS is zero in all environments is the point I’m making. Always set the segment register to the explicit you want.

It should work The code example is as follows:

BITS 16
ORG 0x7c00
GLOBAL main

main:
xor ax, ax ; AX = 0
mov ds, ax; DS = 0
mov bx, 0x7c00

cli; Turn off interrupts for SS:SP update
; to avoid a problem with buggy 8088 CPUs
mov ss, ax; SS = 0x0000
mov sp, bx; SP = 0x7c00
; We'll set the stack starting just below
; where the bootloader is at 0x0:0x7c00. The
; stack can be placed anywhere in usable and
; unused RAM.
sti; Turn interrupts back on

mov SI, loadMsg
call print

cli
.endloop:
hlt
jmp .endloop; When finished effectively put our bootloader
; in endless cycle

print:
pusha
.loop:
mov AL, [SI]; No segment on [SI] means implicit DS:[SI]
cmp AL, 0
je .end
call printChar
inc SI
jmp .loop
.end:
popa
ret< br />
printChar:
pusha
mov AH, 0x0E
mov BH, 0
mov BL, 0x0F
int 0x10
popa< br /> ret

; Place the Data after our code
loadMsg db "Loading",0

times 510-($- $$) db 0; padding with 0 at the end
dw 0xAA55; PC boot signature

I encountered a problem when writing an x86 real-mode assembler that runs as a bootloader in QEMU .I am trying to print text via BIOS interrupt 0x10. My code is:

print:
pusha
.loop:
mov AL, [SI]
cmp AL, 0
je .end
call printC har
inc SI
jmp .loop
.end:
popa
ret

printChar:
pusha
mov AH, 0x0E
mov BH, 0
mov BL, 0x0F
int 0x10
popa
ret

I use [ORG 0x7c00] as Origin. I tested the printChar tag and called it with some letters in AL and it worked fine. When I tried to load the memory address to a message like this:

loadMsg db "Loading",0
mov SI, loadMessage
call print

I output garbage like’U’ as output on the QEMU emulator. Yesterday, I wrote a There is no problem with this similar code. What caused my problem and how to solve it?

I recently wrote some General Bootloader Tips in Stackoverflow, which may be useful to you. Maybe tip #1 applies to your question: < p>

When the BIOS jumps to your code you can’t rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.

Based on the fact that printChar works, and writing the entire string does not mean DS: SI does not point to the correct location in the memory where the string is located. The usual reason is that when the BIOS jumps to the boot loader, the developer mistakenly believes that CS And/or the DS register has been set correctly. It must be set manually. In your case, if the origin is 0x7c00, the offset you use is 0x7C00. A value of 0 in DS will produce (0 << 4) 0x7c00 = The correct physical address of 0x07c00. You can set DS to 0 at the beginning of the program, for example:

xor ax, ax; set AX to zero
mov ds, ax; DS = 0

In the case of QEMU, the BIOS jumps to 0x07c0: 0x0000. This also means the same physical memory location (0x07c0 <4) 0 = 0x07c00. Such a jump will set CS = 0x07c0 (not CS = 0) Since there are many segments: offset pairs mapped to the same physical memory location, DS needs to be set appropriately. You cannot rely on CS as the value you expect. So in QEMU, code like this cannot even set DS correctly (when When using ORG 0x7c00):

mov ax, cs
m ov ds, ax; Make DS=CS

This may apply to some emulators, such as DOSBOX and some physical hardware, but not all. The environment where this code can work is BIOS jump to 0x0000: 0x7c00 In this case, CS will be zero when it reaches the bootloader code, and copying CS to DS will work. Don’t think that CS is zero in all environments is the point I’m working on. Always set the segment register Set it to the explicit you want.

The code example that should work is as follows:

BITS 16
ORG 0x7c00
GLOBAL main

main:
xor ax, ax; AX = 0
mov ds, ax; DS = 0
mov bx, 0x7c00

cli; Turn off interrupts for SS:SP update
; to avoid a problem with buggy 8088 CPUs
mov ss, ax; SS = 0x0000
mov sp, bx; SP = 0x7c00
; We'll set the stack starting just below
; where the bootloader is at 0x0:0x7c00. The
; stack can be placed anywhere in usable and
; unused RAM.
sti; Turn interrupts back on

mov SI, loadMsg
call print

cli
.endloo p:
hlt
jmp .endloop; When finished effectively put our bootloader
; in endless cycle

print:
pusha
.loop :
mov AL, [SI]; No segment on [SI] means implicit DS:[SI]
cmp AL, 0
je .end
call printChar
inc SI
jmp .loop
.end:
popa
ret

printChar:
pusha
mov AH, 0x0E< br /> mov BH, 0
mov BL, 0x0F
int 0x10
popa
ret

; Place the Data after our code
loadMsg db "Loading",0

times 510-($- $$) db 0; padding with 0 at the end
dw 0xAA55; PC boot signature

Leave a Comment

Your email address will not be published.