4

I'm trying to write a simple hello world bios for Qemu as an academic exercise, and I've gotten Qemu to believe the display is initialized. I see a black screen upon boot, but writing to character video memory 0xb8000 does nothing. I configured the PCI slot, set the VGA registers for 80x25 text mode, and enabled the display. Am I missing something? why is video memory not producing results on screen?

NASM Assembly I have so far below:

[BITS 16]
times 4194304 - 2000 db 0x00
_jmpTo:

;Configure the VGA's PCI slot (hardcoded for now)

mov eax, 0x80001004
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx

or eax, 0x0000037F
and eax, 0xFFFFFFFE
or eax, 0x00000002
mov dx, 0x0CFC
out dx, eax

mov eax, 0x80001008
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
and eax, 0x0000FFFF
or eax, 0x03000000
out dx, eax


;map BARS

mov eax, 0x80001010
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
or eax, 0xe0000000
out dx, eax

mov eax, 0x80001018
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
or eax, 0xe1000000
out dx, eax

mov eax, 0x80001030
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
or eax, 0xB0000001
out dx, eax



;Set VGA Config Registers via I/O

mov dx, 0x03DA
in eax, dx
mov dx, 0x03C0
mov al, 0x10
out dx, al
mov al, 0x0C
out dx, al

mov dx, 0x03DA
in eax, dx
mov dx, 0x03C0
mov al, 0x11
out dx, al
mov al, 0x00
out dx, al

mov dx, 0x03DA
in eax, dx
mov dx, 0x03C0
mov al, 0x12
out dx, al
mov al, 0x0F
out dx, al

mov dx, 0x03DA
in eax, dx
mov dx, 0x03C0
mov al, 0x13
out dx, al
mov al, 0x08
out dx, al

mov dx, 0x03DA
in eax, dx
mov dx, 0x03C0
mov al, 0x14
out dx, al
mov al, 0x00
out dx, al

mov dx, 0x03C2
mov al, 0x67
out dx, al

mov dx, 0x03C4
mov al, 0x00
out dx, al
mov dx, 0x03C5
mov al, 0x01
out dx, al

mov dx, 0x03C4
mov al, 0x01
out dx, al
mov dx, 0x03C5
mov al, 0x00
out dx, al

mov dx, 0x03C4
mov al, 0x02
out dx, al
mov dx, 0x03C5
mov al, 0x03
out dx, al

mov dx, 0x03C4
mov al, 0x04
out dx, al
mov dx, 0x03C5
mov al, 0x0A
out dx, al

mov dx, 0x03C4
mov al, 0x00
out dx, al
mov dx, 0x03C5
mov al, 0x03
out dx, al

mov dx, 0x03CE
mov al, 0x05
out dx, al
mov dx, 0x03CF
mov al, 0x00
out dx, al

mov dx, 0x03CE
mov al, 0x06
out dx, al
mov dx, 0x03CF
mov al, 0x03
out dx, al

;disable write protect
mov dx, 0x03D4
mov al, 0x11
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

;crct
mov dx, 0x03D4
mov al, 0x00
out dx, al
mov dx, 0x03D5
mov al, 0x5F
out dx, al

mov dx, 0x03D4
mov al, 0x01
out dx, al
mov dx, 0x03D5
mov al, 0x4F
out dx, al

mov dx, 0x03D4
mov al, 0x02
out dx, al
mov dx, 0x03D5
mov al, 0x50
out dx, al

mov dx, 0x03D4
mov al, 0x03
out dx, al
mov dx, 0x03D5
mov al, 0x82
out dx, al

mov dx, 0x03D4
mov al, 0x04
out dx, al
mov dx, 0x03D5
mov al, 0x55
out dx, al

mov dx, 0x03D4
mov al, 0x06
out dx, al
mov dx, 0x03D5
mov al, 0xB5
out dx, al

mov dx, 0x03D4
mov al, 0x07
out dx, al
mov dx, 0x03D5
mov al, 0x0D
out dx, al

mov dx, 0x03D4
mov al, 0x08
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x09
out dx, al
mov dx, 0x03D5
mov al, 0x0F
out dx, al

mov dx, 0x03D4
mov al, 0x0A
out dx, al
mov dx, 0x03D5
mov al, 0x0E
out dx, al

mov dx, 0x03D4
mov al, 0x0B
out dx, al
mov dx, 0x03D5
mov al, 0x0F
out dx, al

mov dx, 0x03D4
mov al, 0x0C
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x0D
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x0E
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x0F
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x10
out dx, al
mov dx, 0x03D5
mov al, 0x90
out dx, al

mov dx, 0x03D4
mov al, 0x11
out dx, al
mov dx, 0x03D5
mov al, 0x8C
out dx, al

mov dx, 0x03D4
mov al, 0x12
out dx, al
mov dx, 0x03D5
mov al, 0xAF
out dx, al

mov dx, 0x03D4
mov al, 0x13
out dx, al
mov dx, 0x03D5
mov al, 0x28
out dx, al

mov dx, 0x03D4
mov al, 0x14
out dx, al
mov dx, 0x03D5
mov al, 0x07
out dx, al

mov dx, 0x03D4
mov al, 0x15
out dx, al
mov dx, 0x03D5
mov al, 0x9C
out dx, al

mov dx, 0x03D4
mov al, 0x16
out dx, al
mov dx, 0x03D5
mov al, 0xE3
out dx, al

mov dx, 0x03D4
mov al, 0x17
out dx, al
mov dx, 0x03D5
mov al, 0xE3
out dx, al


;mode control
mov dx, 0x03da
in al, dx     
mov al, 0x10
mov dx, 0x03c0
out dx, al 
mov al, 0x20
out dx, al


mov dx, 0x03C6
mov al, 0xFF
out dx, al

;enable display

mov dx, 0x03da
in al, dx     
mov al, 0x20
mov dx, 0x03c0
out dx, al 



mov ax, 0xB800
mov es, ax
mov di, 0x0000
mov al, 0x41
mov ah, 0x07
mov [es:di], ax



_loop:
jmp _loop
hlt

times (4194304 - ($-$$) - 16) db 0x00

_main:
jmp _jmpTo
_end:

times (4194304 - ($-$$)) db 0x00
8
  • 1
    Your mov [es:di], al is storing to B000:0000, linear address B0000. With the same ES segment, mov byte [es: 8000], 'A' will store to B8000. Or you could use ES=B800 and use offsets starting from 0 for the start of video RAM, so e.g. you could xor di,di or mov byte [es: 0], 'A'. Use a debugger attached to QEMU to check memory contents. Commented Oct 27 at 8:49
  • I noticed this a few minutes after posting, however fixing this did not solve the problem. I made sure the memory address was correct by using the qemu monitor as well. 0x41 at 0xb8000 and 0x07 at 0xb8001 are my updated values, still nothing on the screen. Commented Oct 27 at 9:15
  • 1
    Also, putting hlt inside your infinite loop will make QEMU waste less CPU time when it reaches that, only waking up for interrupts. Commented Oct 27 at 9:21
  • 2
    All the magic numbers are hard to read and check. It would help to add more detailed comments, explaining each individual I/O operation. What's the name of the device register you're writing to, and what effect are you intending to achieve by writing your chosen value? That will also force you to think through all of them again, and you may find a bug that way. Commented Oct 27 at 13:59
  • 1
    Setting up VGA from zero is not easy, there's a lot to go through. Your first course of action should be getting some feedback from QEMU, i.e. logs. See if you can set the log for the VGA component to the maximum verbosity, where you can see individual register access. If this is not possible, consider testing on Bochs for a moment. Bochs can be configured with this level of logging for each component (in this case PCI and VGA). I don't know how true is the Bochs and QEMU implementation of the VGA to the real hardware and how close to each other they are. Worth a shot, IMO. Commented Oct 28 at 9:04

1 Answer 1

0

I figured out how to properly configure settings to write pixels to a bitmap. Text should be straight forward to implement now, I think I'll deal with a proper text mode later as this stuff is quite the headache! Anyway, updated code with annotations is posted in case it helps others. Compiling with NASM in binary format and using the raw binary as bios input to Qemu with -vga std generates a white screen!

Working code:

[BITS 16]
times 4194304 - 2000 db 0x00
_jmpTo:

;Configure the VGA's PCI slot (hardcoded for now)

mov eax, 0x80001004     ;set pci header command reg
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx

or eax, 0x0000037F      
and eax, 0xFFFFFFFE
or eax, 0x00000002
mov dx, 0x0CFC
out dx, eax

mov eax, 0x80001008     ;set info
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
and eax, 0x0000FFFF
or eax, 0x03000000
out dx, eax


;map BARS

mov eax, 0x80001010     ;map base address regs into memory
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
or eax, 0xe0000000
out dx, eax

mov eax, 0x80001018
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
or eax, 0xe1000000
out dx, eax

mov eax, 0x80001030
mov dx, 0x0CF8
out dx, eax

mov dx, 0x0CFC
in eax, dx
or eax, 0xB0000001
out dx, eax



;Set VGA Config Registers via I/O

;disable write protect
mov dx, 0x03C2      ;set misc config
mov al, 0xE7
out dx, al

mov dx, 0x03D4      ;disable protection
mov al, 0x11
out dx, al
mov dx, 0x03D5
in al, dx
and al, 0x7F
out dx, al

;crtc
mov dx, 0x03D4      ;crtc stuff, copied from internet bc lazy
mov al, 0x00        ;(its just sync timing and stuff,
out dx, al      ;there are default settings, not important)
mov dx, 0x03D5
mov al, 0x6B
out dx, al

mov dx, 0x03D4
mov al, 0x01
out dx, al
mov dx, 0x03D5
mov al, 0x59
out dx, al

mov dx, 0x03D4
mov al, 0x02
out dx, al
mov dx, 0x03D5
mov al, 0x5A
out dx, al

mov dx, 0x03D4
mov al, 0x03
out dx, al
mov dx, 0x03D5
mov al, 0x82
out dx, al

mov dx, 0x03D4
mov al, 0x04
out dx, al
mov dx, 0x03D5
mov al, 0x60
out dx, al

mov dx, 0x03D4
mov al, 0x05
out dx, al
mov dx, 0x03D5
mov al, 0x8D
out dx, al

mov dx, 0x03D4
mov al, 0x06
out dx, al
mov dx, 0x03D5
mov al, 0x0B
out dx, al

mov dx, 0x03D4
mov al, 0x07
out dx, al
mov dx, 0x03D5
mov al, 0x3E
out dx, al

mov dx, 0x03D4
mov al, 0x08
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x09
out dx, al
mov dx, 0x03D5
mov al, 0x40
out dx, al

mov dx, 0x03D4
mov al, 0x0A
out dx, al
mov dx, 0x03D5
mov al, 0x06
out dx, al

mov dx, 0x03D4
mov al, 0x0B
out dx, al
mov dx, 0x03D5
mov al, 0x07
out dx, al

mov dx, 0x03D4
mov al, 0x0C
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x0D
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x0E
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x0F
out dx, al
mov dx, 0x03D5
mov al, 0x00
out dx, al

mov dx, 0x03D4
mov al, 0x10
out dx, al
mov dx, 0x03D5
mov al, 0xEA
out dx, al

mov dx, 0x03D4
mov al, 0x11
out dx, al
mov dx, 0x03D5
mov al, 0x0C
out dx, al

mov dx, 0x03D4
mov al, 0x12
out dx, al
mov dx, 0x03D5
mov al, 0xDF
out dx, al

mov dx, 0x03D4
mov al, 0x13
out dx, al
mov dx, 0x03D5
mov al, 0x2D
out dx, al

mov dx, 0x03D4
mov al, 0x14
out dx, al
mov dx, 0x03D5
mov al, 0x08
out dx, al

mov dx, 0x03D4
mov al, 0x15
out dx, al
mov dx, 0x03D5
mov al, 0xE8
out dx, al

mov dx, 0x03D4
mov al, 0x16
out dx, al
mov dx, 0x03D5
mov al, 0x05
out dx, al

mov dx, 0x03D4
mov al, 0x17
out dx, al
mov dx, 0x03D5
mov al, 0xE3
out dx, al

mov dx, 0x03D4
mov al, 0x18
out dx, al
mov dx, 0x03D5
mov al, 0xFF
out dx, al

;seq

mov dx, 0x03C4          ;clear both resets
mov al, 0x00
out dx, al
inc dx
mov al, 0x03
out dx, al

mov dx, 0x03C4          ;clocking mode defaults
mov al, 0x01    
out dx, al
inc dx
mov al, 0x00
out dx, al

mov dx, 0x03C4          ;enable all memory planes
mov al, 0x02
out dx, al
inc dx
mov al, 0x0F
out dx, al

mov dx, 0x03C4          ;linear memory mode (chain enable)
mov al, 0x04
out dx, al
inc dx
mov al, 0x08
out dx, al


;Graphics Controller

mov dx, 0x03CE          ;set / reset 0b1111
mov al, 0x00
out dx, al
inc dx
mov al, 0x0F
out dx, al

mov dx, 0x03CE          ;disable set / reset
mov al, 0x01
out dx, al
inc dx
mov al, 0x00
out dx, al

mov dx, 0x03CE          ;no color compare
mov al, 0x02
out dx, al
inc dx
mov al, 0x00
out dx, al

mov dx, 0x03CE          ;logical OR
mov al, 0x03
out dx, al
inc dx
mov al, 0x10
out dx, al

mov dx, 0x03CE          ;read map 0
mov al, 0x04
out dx, al
inc dx
mov al, 0x00
out dx, al

mov dx, 0x03CE          ;read and write mode 0
mov al, 0x05
out dx, al
inc dx
mov al, 0x00
out dx, al

mov dx, 0x03CE          ;alphanumeric disable, mem map 0
mov al, 0x06
out dx, al
inc dx
mov al, 0x01
out dx, al

mov dx, 0x03CE          ;no color-dont-care
mov al, 0x07
out dx, al
inc dx
mov al, 0x00
out dx, al

mov dx, 0x03CE          ;all bits masked
mov al, 0x08
out dx, al
inc dx
mov al, 0xFF
out dx, al

;Attribute Controller

mov dx, 0x03DA      ;Configure color indexes
in al, dx
mov dx, 0x03C0
mov al, 0x00
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x01
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x02
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x03
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x04
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x05
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x06
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x07
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x08
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x09
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x0A
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x0B
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x0C
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x0D
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x0E
out dx, al
out dx, al

mov dx, 0x03DA
in al, dx
mov dx, 0x03C0
mov al, 0x0F
out dx, al
out dx, al

mov dx, 0x03DA          ;enable graphics
in al, dx
mov dx, 0x03C0
mov al, 0x10
out dx, al
mov al, 0x01
out dx, al

mov dx, 0x03DA          ;no overscan color
in al, dx
mov dx, 0x03C0
mov al, 0x11
out dx, al
mov al, 0x00
out dx, al

mov dx, 0x03DA          ;enable all color planes
in al, dx
mov dx, 0x03C0
mov al, 0x12
out dx, al
mov al, 0x0F
out dx, al

mov dx, 0x03DA          ;no pixel shift
in al, dx
mov dx, 0x03C0
mov al, 0x13
out dx, al
mov al, 0x00
out dx, al

mov dx, 0x03DA          ;no color select
in al, dx
mov dx, 0x03C0
mov al, 0x14
out dx, al
mov al, 0x00
out dx, al

;disable display

mov dx, 0x03da          ;switch off output to configure DAC
in al, dx     
mov al, 0x20
mov dx, 0x03c0
out dx, al 

;DAC

mov al, 0x00
mov dx, 0x03C8          ;Setup 16 color palette
out dx, al
mov dx, 0x3C9
mov al, 0x00
out dx, al
out dx, al
out dx, al

mov al, 0x00
out dx, al
mov al, 0x00
out dx, al
mov al, 0x2A
out dx, al

mov al, 0x00
out dx, al
mov al, 0x2A
out dx, al
mov al, 0x00
out dx, al

mov al, 0x00
out dx, al
mov al, 0x2A
out dx, al
mov al, 0x2A
out dx, al

mov al, 0x2A
out dx, al
mov al, 0x00
out dx, al
mov al, 0x00
out dx, al

mov al, 0x2A
out dx, al
mov al, 0x00
out dx, al
mov al, 0x2A
out dx, al

mov al, 0x2A
out dx, al
mov al, 0x15
out dx, al
mov al, 0x00
out dx, al

mov al, 0x2A
out dx, al
mov al, 0x2A
out dx, al
mov al, 0x2A
out dx, al

mov al, 0x15
out dx, al
mov al, 0x15
out dx, al
mov al, 0x15
out dx, al

mov al, 0x15
out dx, al
mov al, 0x15
out dx, al
mov al, 0x3F
out dx, al

mov al, 0x15
out dx, al
mov al, 0x3F
out dx, al
mov al, 0x15
out dx, al

mov al, 0x15
out dx, al
mov al, 0x3F
out dx, al
mov al, 0x3F
out dx, al

mov al, 0x3F
out dx, al
mov al, 0x15
out dx, al
mov al, 0x15
out dx, al

mov al, 0x3F
out dx, al
mov al, 0x15
out dx, al
mov al, 0x3F
out dx, al

mov al, 0x3F
out dx, al
mov al, 0x3F
out dx, al
mov al, 0x15
out dx, al

mov al, 0x3F
out dx, al
mov al, 0x3F
out dx, al
mov al, 0x3F
out dx, al

;draw

mov eax, 0xE0000000     ;Fill all pixels with white
mov bl, 0xFF
_displayLoop:
mov [eax], bl
inc eax
cmp eax, 0xE002A2FF
jg _displayDone
jmp _displayLoop
_displayDone:

;enable display
mov dx, 0x03da
in al, dx     
mov al, 0x20
mov dx, 0x03c0
out dx, al 



_loop:
jmp _loop
hlt

times (4194304 - ($-$$) - 16) db 0x00

_main:
jmp _jmpTo
_end:

times (4194304 - ($-$$)) db 0x00
Sign up to request clarification or add additional context in comments.

4 Comments

Does QEMU not check segment limits, or does the modern x86 power-on state not have 64K segment limits like real mode is supposed to? You're using mov [eax], bl without setting DS first; assuming the power-on segment base is 0, the logical address is 0000:E0000000, with an offset part larger than 64K. That's supposed to #GP fault (felixcloutier.com/x86/mov#real-address-mode-exceptions) "If a memory operand effective address is outside the CS, DS, ES, FS, or GS segment limit". I think I remember a previous question where QEMU wasn't enforcing real mode seg limits.
On real hardware, this should only work if you switch to protected mode and back to set segment limits to unlimited, i.e. unreal mode. Again, I don't think this is the power-on state for real hardware, which I think is real mode except for CS.base having a large value that doesn't match what you'd get from reloading the CS register. (retrocomputing.stackexchange.com/questions/27035/…)
Also, obviously it would be much faster to fill the screen with rep stosb or rep stosd, like mov eax, 0xFFFFFFFF / mov ecx, 0x2A300 / 4 / mov edi, 0xE0000000 / a32 rep stosd to store to es:0xE0000000 using 32-bit address-size (and operand-size as implied by the stosd mnemonic). But being slow so you can maybe see the screen fill is a feature I guess. As well as having jg-forward over a jmp instead of a typical jb or jne _displayLoop, you could include a pause instruction to slow it down more.
Huh. Not sure about the address mode exceptions. I'm going to be honest, I'm kind of new to assembly, as well as low level stuff in general. I'll have to look into real hardware power-on states, thx

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.