BIOS int 10h prints garbage on QEMU

I had a problem writing an x86 real-time build program that runs as a loader in QEMU. I am trying to print text through BIOS interrupt 0x10. My code is:

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 use [ORG 0x7c00] as a starting point. I tested the printChar label and named it a letter in AL, and it works great. When I try to load the memory address into the message as follows:

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

I get garbage like 'U' as output on a QEMU emulator. Yesterday I wrote code very similar to this one and I have no problem. What causes my problem and how can I fix it?

+2
source share
2 answers

I recently wrote some general bootloader tips in a Stackoverflow answer that may come in handy. Tip # 1 probably refers to your problem:

When the BIOS goes to your code, you cannot rely on CS, DS, ES, SS, SP registers having real or expected values. They must be configured properly at bootloader startup. You can be guaranteed that your bootloader will be loaded and started from the physical address 0x00007c00 and that the boot disk number will be loaded into the DL register.

Based on the fact that printChar works, and that writing an entire line does not mean that DS: SI does not indicate the right place in memory where your line is. A common reason for this is because the developers incorrectly assume that the CS and / or DS register is set appropriately when the BIOS goes to the bootloader. It must be installed manually. In the case of the start point 0x7c00 for DS, you must set the value 0. In 16-bit real mode, the addresses of the physical memory are calculated from the segment: offset pairs using the formula (segment<<4)+offset . In your case, you are using the offset 0x7C00. The value in DS 0 should give the correct physical address (0 <4) + 0x7c00 = 0x07c00.

You can set DS to 0 at the beginning of your program with something like:

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

In the case of QEMU, the BIOS switches to 0x07c0: 0x0000. It also represents the same physical memory location (0x07c0 <4) +0 = 0x07c00. Such a jump will set CS = 0x07c0 (and not CS = 0). Since there are many segments: offset pairs that map to the same physical memory location, you need to set DS accordingly. You cannot rely on the CS value that you expect. Thus, in QEMU code like this will not install DS correctly (when using ORG 0x7c00 ):

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

This may work on some emulators, such as DOSBOX and some physical hardware, but not at all. The environment in which this code will work is when the BIOS switches to 0x0000: 0x7c00. In this case, CS will be zero when it reaches your bootloader code, and copying CS to DS will work. Do not assume that CS will be zero in all environments, this is the main thing that I do. Always set segment registers to what you want explicitly.

An example of code that should work is:

  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 printChar: pusha mov AH, 0x0E 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 
+5
source

This is problem:

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

If the program does not skip over the text “download”, it executes any instructions that these bytes can (or cannot) represent. Something like this fixes this:

  jmp print_msg loadMsg db "Loading",0 print_msg: mov SI, loadMessage call print 
+1
source

Source: https://habr.com/ru/post/1013792/


All Articles