How do you achieve VGA graphics WITHOUT RealMode/BIOS calls?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
rwosdev
Member
Member
Posts: 49
Joined: Thu Apr 26, 2018 11:21 pm

How do you achieve VGA graphics WITHOUT RealMode/BIOS calls?

Post by rwosdev »

My OS is at the stage where it can dynamically load and link drivers from PE format data into the kernel.

So it's time for a graphics driver to keep me motivated, because I want a "proper" OS. I know I could set the graphics during the Real Mode stages but I really don't like that solution.

Obviously if some 16-bit BIOS routine can properly initialise the video device, what's really stopping me from doing the same with IO ports in protected mode?

Really I only plan to run my OS in virtual machines (QEMU/Bochs/VirtualBox) and they all seem to simulate the same graphics device, similar to Cirrus Logic devices (please correct me if I'm wrong).

So I'd be more than happy to write the best Cirrus driver I can, and share the code here if I can get some pointers on what I need to do to:

1. Properly switch modes from the default 80x25 textmode to at least 320x200 256 colours using IO ports only
2. Plot specific pixels on the screen

Here's my current code which gets QEMU to a pure black screen, Bochs to an almost solid grey but with some corrupt pixels at the bottom and VirtualBox to a black background with grey lines and rectangles all over the place (seems like a textmode hang over).

Note: I memset locations 0xA000 and 0xA0000 to 0xFF after this is executed, also I have little idea what I'm doing I just tried different registers to achieve a different result than my first attempt (random pixels/rectangles/lines everywhere).

Code: Select all

	WRITEREG VGA_MISC_WRITE, 0x03
	
	; Plane mask
	WRITEREG VGA_SEQ_INDEX, 2
	WRITEREG VGA_SEQ_DATA, 0	
	
	; Extended memory
	WRITEREG VGA_SEQ_INDEX, 4
	WRITEREG VGA_SEQ_DATA, 2
	;
	; CRTC crap, horizontal total and end, vertical total
	WRITEREG VGA_CRTC_INDEX, 0
	WRITEREG VGA_CRTC_DATA, 0
	WRITEREG VGA_CRTC_INDEX, 1
	WRITEREG VGA_CRTC_DATA, 1
	WRITEREG VGA_CRTC_INDEX, 6
	WRITEREG VGA_CRTC_DATA, 0

	; Screen start address
	WRITEREG VGA_CRTC_INDEX, 0xC
	WRITEREG VGA_CRTC_DATA, 0xA0
	WRITEREG VGA_CRTC_INDEX, 0xD
	WRITEREG VGA_CRTC_DATA, 0x00
	
	WRITEREG VGA_GC_INDEX, 6
	WRITEREG VGA_GC_DATA, 00000001b
	
mallard
Member
Member
Posts: 280
Joined: Tue May 13, 2014 3:02 am
Location: Private, UK

Re: How do you achieve VGA graphics WITHOUT RealMode/BIOS ca

Post by mallard »

What you want to do is entirely possible and widely compatible (the only time you may have problems is on a system booted from UEFI that leaves the display in a high-resolution mode; not a problem if you're only targetting VMs).

Its seems you're on the right track with the code you have so fare... The Wiki contains complete-ish listings of the register settings for various modes, the FreeVGA site contains detailed reference material (you'll want the pages under "VGA Chipset Reference").

If you're interested in higher output resolutions, most VMs implement a virtual graphics adaptor based on the "Bochs Graphics Adaptor" or "BGA". There's some documentation on the Wiki for that, under a somewhat misleading title (it has little to do with VBE)...
Image
rwosdev
Member
Member
Posts: 49
Joined: Thu Apr 26, 2018 11:21 pm

Re: How do you achieve VGA graphics WITHOUT RealMode/BIOS ca

Post by rwosdev »

The Bochs Graphics Adaptor you mentioned looks very suitable to what I need right now, as the Wiki says it's supported in QEMU, Bochs and VirtualBox, so that's really cool. Then maybe later on when most of the OS is done (been at it probably 6 months collectively) I could add basic support for an actual video card like Intel/AMD and have it on real hardware.

I'm probably going to spend the weekend experimenting with BGA, shaping the type of driver design I want etc. I'll definitely let you know how I get on and I'll post the code here if it works out.

Thanks! 8)
rwosdev
Member
Member
Posts: 49
Joined: Thu Apr 26, 2018 11:21 pm

Re: How do you achieve VGA graphics WITHOUT RealMode/BIOS ca

Post by rwosdev »

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
Attachments
Running in all emulators!
Running in all emulators!
Last edited by rwosdev on Sat Apr 28, 2018 1:42 am, edited 2 times in total.
Octocontrabass
Member
Member
Posts: 5586
Joined: Mon Mar 25, 2013 7:01 pm

Re: How do you achieve VGA graphics WITHOUT RealMode/BIOS ca

Post by Octocontrabass »

rwosdev wrote:In QEMU, my setup returns 0xFD000008 as the VRAM address but this causes a few pixels to be missed at the top, but I've observed no ill effects from aligning it to 0x1000 so it is 0xFD000000.
The lowest four bits of that BAR indicate that it's a prefetchable 32-bit memory space BAR, they're not part of the address. You can read more about that in the wiki.
rwosdev
Member
Member
Posts: 49
Joined: Thu Apr 26, 2018 11:21 pm

Re: How do you achieve VGA graphics WITHOUT RealMode/BIOS ca

Post by rwosdev »

Overlooked that, thanks!
Post Reply