Search (using Google):  Web Karig

 

10 March 2004

VBE experiment 2

My first experiment with VBE was to get information about my Soyo's 800x600 high-color mode. Unfortunately, subsequent tests revealed that, while the Soyo's video card might be capable of 800x600 resolution, the built-in monitor would not display more than 640x480 pixels at a time. So the video mode 0x114 that I was hoping to use has proven useless, and I'm settling for video mode 0x111 (640x480 resolution, 65,536 colors) instead.

Naturally, I wrote a boot sector to see if I could activate video mode 0x111 and put some color onto the screen.

The purpose of the code is to cover the whole screen with six equal-sized horizontal bars of color — red on top, then green, blue, magenta, yellow, and cyan.

The first thing the code does, as always, is to set up a stack so that BIOS routines can be called.

main:
; ------ Set up real-mode stack for BIOS calls.
		cli
		in	al, 0x70 ; Disable NMIs.
		or	al, 0x80
		out	0x70, al

		mov	ax, 0x1000
		mov	ss, ax
		xor	sp, sp

		in	al, 0x70 ; Re-enable NMIs.
		and	al, 0x7F
		out	0x70, al
		sti

The next lines of code solve a small problem I was having with an earlier version of this boot sector. The code essentially halts execution for three seconds in order to give the BIOS floppy driver time to finish and to turn off the floppy drive's LED before I turn off interrupts and enter protected mode. Without this delay, I enter protected mode before the LED is turned off, and the LED stays on — which is not a huge problem here, because the color bars still show up on the screen, but it looks unprofessional to have the LED stay on. The floppy drive isn't doing anything, so the LED should be off.

To delay for three seconds, I use the system timer. The timer "ticks" eighteen times per second, so I set the timer to zero, and I just keep reading the timer until its return value is 54.

; ------ Delay for three seconds.
; ------ (Gives floppy controller time to finish BEFORE
; ------  we clear interrupts and enter protected mode.)
		xor	cx, cx
		xor	dx, dx
		mov	ah, 1
		int	0x1A     ; set system timer to zero

	.zz:	xor	ah, ah
		int	0x1A
		cmp	dx, 54
		jl	.zz

Now I activate the graphic mode. I pass the mode number in BX — bits 0..8 contain the mode number itself, bit 14 is set to enable a linear frame buffer, and bit 15 is cleared so that the video memory will be cleared. (The VBE 2.0 specification explains mode numbers. See also the VBE 3.0 specification.)

; ------ Set VBE mode: 800x600, 64K colors
		mov	bx, 0x4111 ; mode 0x111, linear, clear memory
		mov	ax, 0x4F02
		int	0x10

Next, I enable the A20 line so that odd-numbered megabytes are accessible to the processor. I need access to high memory here because my code writes to a buffer in memory and then copies the buffer into the video memory. For 640x480 resolution and 16-bit color, I need a 600KB buffer. I could shoehorn the buffer into conventional memory if I had to, but Karig will put the buffer into high memory anyway (the kernel, editor, and working block buffers will go into conventional memory), so I might as well write the code to handle a high-memory buffer now.

Note also that I won't be needing the BIOS services anymore, so I can shut off the interrupts and keep them off.

; ------ Shut off interrupts.
		cli
		in	al, 0x70
		or	al, 0x80
		out	0x70, al

; ------ Enable A20 line. (Method used in ColorForth)
		mov	al, 0xD1
		out	0x64, al
	.20:	in	al, 0x64
		and	al, 2
		jnz	.20
		mov	al, 0x4B
		out	0x60, al

To use 32-bit code, I enter protected mode.

; ------ Load GDT and enter protected mode.
		lgdt	[gdt]
		mov	eax, cr0
		or	al, 1
		mov	cr0, eax
		jmp	dword 8:pmstart
[BITS 32]
pmstart:
		mov	eax, dseg-gdt
		mov	ds, eax
		mov	es, eax
		mov	fs, eax
		mov	gs, eax
		mov	ss, eax

Now I can draw pixels on the graphic screen. I draw the six color bands to a buffer in memory (at address 0x200000). Because each color band is going to be 640 pixels wide by (480/6) or 80 pixels high, I need to store the 16-bit (two-byte) color value 640*80 or 51,200 times. Storing two 16-bit values into EAX means that I need to write to memory only 51200/2 or 25,600 times for each of the six bands.

; ------ Draw color bands into screen buffer.
		cld
		mov	edx, 6-1      ; six color bands to draw
	.d:	mov	ecx, 640*80/2 ; number of pixels to draw / 2
		mov	edi, [offsets+edx*4]
		mov	eax, [colors+edx*4]
		rep	stosd
		dec	edx
		jns	.d

The color bands drawn, I copy them into video memory, which my previous experiment with VBE revealed to be at physical address 0xE0000000. (If the code were more general, it would have asked the BIOS for the location of the linear frame buffer, a procedure previously explained.)

; ------ Copy screen buffer into video memory.
		mov	esi, [offsets]
		mov	edi, 0xE0000000
		mov	ecx, 640 * 480 * 2 / 4
		rep	movsd

The screen should immediately display the six horizontal bands of color, and the code simply halts here.

; ------ Hang computer.
		jmp	$

I tested this code last night, and it works on my system. It might not work on yours if your system does not map its video memory to address 0xE0000000, or if it is very old and does not support video mode 0x111.

Check the index for other entries.