Did it!
Here is my BGA configuration and mode setting code, you call VGAInit to configure which returns a bool 0 or 1 in EAX for success or fail, so you have an opportunity to do something about the result, then when you actually want to change the resolution you call VGAEnable.
This works perfectly in QEMU, VirtualBox and Bochs. On the plus side, this enables an arbitrary (720x480) resolution AND 24 bits-per-pixel so you can have proper, full colour HD graphics in your OS, on the downside it is probably constrained to those environments so if you want to take your hard work to real hardware, you're going to have to look at the specs and datasheets needed and write a whole new graphics driver for that hardware (it has been done).
QEMU notes: I love QEMU, it's the fastest for development in my opinion. But it's configured to use PCI Bochs Graphics VBE, and I couldn't get the ISA configuration which Bochs and VirtualBox use. QEMU crashed or didn't open depending on what I did.
Emulators with an ISA setup use the hardcoded address
0xE0000000 for VRAM.
Emulators with a PCI setup need you to read the VRAM address from BAR0 of PCI device
0x1234:0x1111 (
vendor:
device slot respectively)
So for QEMU and any other possible emulators/configurations that demand the PCI setup, I also needed a tiny bit of PCI enumeration code to read the VRAM address off BAR0 which isn't that complex. That code (which I will leave as an exercise to the reader) is based off the Wiki's
PCI page with slight adjustments and a function to read 32-bit values from the PCI configuration space. In QEMU, my setup returns 0xFD000008 as the VRAM address but this causes a few pixels to be missed at the top...
EDIT: As noted in the post below, the last 4 bits aren't meant to be used so the '8' isn't part of the address. The address is also 16-byte aligned. You can discard those bits to get something like 0xFD000000 etc
I tested this with memset code to draw a grey screen with a black line in the middle which works in all emulators, that code is at the bottom.
Thanks for letting me know about BGA!
LJ
Code: Select all
%macro READREG16 1
mov dx, %1
in ax, dx
%endmacro
%macro WRITEREG16 2
mov dx, %1
mov ax, %2
out dx, ax
%endmacro
VBE_DISPI_IOPORT_INDEX EQU 0x01CE
VBE_DISPI_IOPORT_DATA EQU 0x01CF
VBE_DISPI_INDEX_ID EQU 0x0
VBE_DISPI_INDEX_XRES EQU 0x1
VBE_DISPI_INDEX_YRES EQU 0x2
VBE_DISPI_INDEX_BPP EQU 0x3
VBE_DISPI_INDEX_ENABLE EQU 0x4
VBE_DISPI_INDEX_BANK EQU 0x5
VBE_DISPI_INDEX_VIRT_WIDTH EQU 0x6
VBE_DISPI_INDEX_VIRT_HEIGHT EQU 0x7
VBE_DISPI_INDEX_X_OFFSET EQU 0x8
VBE_DISPI_INDEX_Y_OFFSET EQU 0x9
VBE_DISPI_ID0 EQU 0xB0C0
VBE_DISPI_ID1 EQU 0xB0C1
VBE_DISPI_ID2 EQU 0xB0C2
VBE_DISPI_ID3 EQU 0xB0C3
VBE_DISPI_ID4 EQU 0xB0C4
VBE_DISPI_DISABLED EQU 0x00
VBE_DISPI_ENABLED EQU 0x01
VBE_DISPI_GETCAPS EQU 0x02
VBE_DISPI_8BIT_DAC EQU 0x20
VBE_DISPI_LFB_ENABLED EQU 0x40
VBE_DISPI_NOCLEARMEM EQU 0x80
VBE_DISPI_LFB_PHYSICAL_ADDRESS EQU 0xE0000000
VGASetMode:
; Returns 1 or 0 (true/false) if successful or not.
push edx
.setVersion:
; Try set 0xB0C4 for 8MB VRAM.
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID
WRITEREG16 VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID4
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID
READREG16 VBE_DISPI_IOPORT_DATA
cmp ax, VBE_DISPI_ID4
jne .error
.configure:
; Try set 24 bits per pixel
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP
WRITEREG16 VBE_DISPI_IOPORT_DATA, 24
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP
READREG16 VBE_DISPI_IOPORT_DATA
; Set resolution
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES
WRITEREG16 VBE_DISPI_IOPORT_DATA, 720
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES
WRITEREG16 VBE_DISPI_IOPORT_DATA, 480
.ok:
mov eax, 1
jmp .done
.error:
mov eax, 0
.done:
pop edx
ret
VGAEnable:
WRITEREG16 VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE
WRITEREG16 VBE_DISPI_IOPORT_DATA, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED
ret
Fill the screen with 24bpp grey and draw a line at x=10, y=10:
Code: Select all
VRAM = VGAFindVRAMAddress(); // PCI enumeration code for QEMU but returns hardcoded address 0xE0000000 when device 0x1234:0x1111 isn't found.
VGAEnable();
memset(VRAM, 0xAF, (720 * 480) * 3); // Fill with grey
memset(VRAM + (10 * 3) + ((3 * 720) * 10), 0x00, 100 * 3); // Draw line