VGA Graphics Question [x86]

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.
CompDever
Posts: 23
Joined: Sun Jul 22, 2007 12:21 pm

VGA Graphics Question [x86]

Post by CompDever »

Would it be possible for somebody to upload the source code for an operating system written in C and Asm that changes the color of one pixel to red (and turns off text mode) along with an explanation of how to properly compile it? [It would be best if the code was documented, but it unneccesary. Additionally, it may be noted that i would like this to be a skeletal example meaning that it only has enough functionality to do its job] [this is for x86]

thanks in advance
Ninjarider
Member
Member
Posts: 62
Joined: Fri Jun 29, 2007 8:36 pm

Post by Ninjarider »

for which processor version

pretty much you end up using int 10h to change the display from test to graphic. you then have fun changing **** around.


look for ralph brown's interrupt list.

also which version of assembler will you be using.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Re: VGA Graphics Question [x86]

Post by pcmattman »

CompDever wrote:Would it be possible for somebody to upload the source code for an operating system written in C and Asm that changes the color of one pixel to red (and turns off text mode) along with an explanation of how to properly compile it? [It would be best if the code was documented, but it unneccesary. Additionally, it may be noted that i would like this to be a skeletal example meaning that it only has enough functionality to do its job] [this is for x86]

thanks in advance
By the time you can actually start thinking about graphics you should already have an OS with at least a memory manager and the ability to put text on the screen. You will also need to be able to either drop back to real mode or enter virtual 8086 mode.

That's a lot of code to wade through if it's not your own.
CompDever
Posts: 23
Joined: Sun Jul 22, 2007 12:21 pm

Post by CompDever »

It is not a matter of wheter or not i have an OS with mm and other material: i just need a skeletal sample, for me to learn best. Unless I am greatly mistaken it should not be too difficult for an experienced OSdever to draw up a skeletal drawing OS, is it? [considering you should already have a mm that can be scaled down to this example, and for that matter I can put text on the screen. Plus why would you not be able to remain with the display of that one pixel visible and be forced to drop back real mode of virtual 8086?]
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

This is some code from a old VGA driver of mine so it is not quite as slim as it could be, but it appears to work besides some bugs it might contain.

It does not work under BOCHS for some reason, but it does work under QEMU (which is the only place it has been tested). It is most likely a little bug somewhere in the code. Anyway, it draws two lines that alternate in color. It does this by drawing onto a local frame buffer which is then copied into VGA memory using four planes at four bits per pixel.

simple.c

Code: Select all

#include <asm/io.h>

#define PICTURE_GFX4BPP6BPC	0x0
#define PICTURE_TEXT1C1A	0x1
struct tPictureInfo{
	unsigned long	infoType;
	unsigned long	vMemSize;
	unsigned long	infoWidth;
	unsigned long	infoHeight;
	unsigned long	infoRefresh;
}; typedef struct tPictureInfo PICTUREINFO; typedef struct tPictureInfo* PPICTUREINFO;

#define VGA_GFX_ADDR 0x3CE
#define VGA_GFX_DATA 0x3CF
#define VGA_SEQ_ADDR 0x3C4
#define VGA_SEQ_DATA 0x3C5

#define VGA_GFX_SSR  0x00
#define VGA_GFX_ESRR 0x01
#define VGA_GFX_CCR  0x02
#define VGA_GFX_DRR  0x03
#define VGA_GFX_RMSR 0x04
#define VGA_GFX_GMR  0x05
#define VGA_GFX_MGR  0x06
#define VGA_GFX_CDCR 0x07
#define VGA_GFX_BMR  0x08

#define VGA_SEQ_RR   0x00
#define VGA_SEQ_CMR  0x01
#define VGA_SEQ_MMR  0x02
#define VGA_SEQ_CMSR 0x03 
#define VGA_SEQ_SMMR 0x04

#define VGA_DAC_WADDR 0x3C8
#define VGA_DAC_RADDR 0x3C7
#define VGA_DAC_DATA  0x3C9
#define VGA_DAC_STATE 0x3C7

#define VGA_CRT_ADDR  0x3D4
#define VGA_CRT_DATA  0x3D5

#define VGA_CRT_HTR   0x0
#define VGA_CRT_EHDR  0x1
#define VGA_CRT_SHBR  0x2
#define VGA_CRT_EHBR  0x3
#define VGA_CRT_SHRR  0x4
#define VGA_CRT_EHRR  0x5
#define VGA_CRT_VTR   0x6
#define VGA_CRT_MCR   0x17
#define VGA_CRT_OR    0x13
#define VGA_CRT_OFR   0x07
#define VGA_CRT_MSLR  0x09
#define VGA_CRT_VRER  0x11
#define VGA_CRT_ULR   0x14

#define VGA_ATR_ADDRDATA  0x3C0
#define VGA_ATR_READ  	  0x3C1
#define VGA_ATR_CSR	  0x14

#define VGA_ATR_MCR   0x10

#define CLK_25 0x00
#define CLK_28 0x04
#define CLK_U0 0x08
#define CLK_U1 0x0C

inline void vga_atr_w_ip(unsigned short index, unsigned char data){
	inb(0x3da);
	outb(index, VGA_ATR_ADDRDATA);
	outb(data, VGA_ATR_ADDRDATA);
	return;
}
inline void vga_atr_w(unsigned short index, unsigned char data){
	inb(0x3da);
	outb(index|0x20, VGA_ATR_ADDRDATA);
	outb(data, VGA_ATR_ADDRDATA);
	return;
}
inline unsigned char vga_atr_r_ip(unsigned short index){
	inb(0x3da);
	outb(index, VGA_ATR_ADDRDATA);
	return inb(VGA_ATR_READ);
}
inline unsigned char vga_atr_r(unsigned short index){
	inb(0x3da);
	outb(index|0x20, VGA_ATR_ADDRDATA);
	return inb(VGA_ATR_READ);
}
inline unsigned char vga_seq_r(unsigned short index){
	outb(index, VGA_SEQ_ADDR);
	return inb(VGA_SEQ_DATA);
}
inline unsigned char vga_gfx_r(unsigned short index){
	outb(index, VGA_GFX_ADDR);
	return inb(VGA_GFX_DATA);
}
inline unsigned char vga_dac_r(unsigned short index){
	return inb(index);
}
inline unsigned char vga_crt_r(unsigned short index){
	outb(index, VGA_CRT_ADDR);
	return inb(VGA_CRT_DATA);
}
inline void vga_crt_w(unsigned short index, unsigned char data){
	outb(index, VGA_CRT_ADDR);
	outb(data, VGA_CRT_DATA);
	return;
}
inline void vga_dac_w(unsigned short index, unsigned char data){
	outb(data, index);
	return;
}
inline void vga_seq_w(unsigned char index, unsigned char data){
	outb(index, VGA_SEQ_ADDR);
	outb(data, VGA_SEQ_DATA);
	return;
}
inline void vga_gfx_w(unsigned char index, unsigned char data){
	outb(index, VGA_GFX_ADDR);
	outb(data, VGA_GFX_DATA);
	return;
}

#define EXTCOUNT 0x01		// not correct currently but will work for now. (Actually two that I know of.)
#define CRTCOUNT 0x19
#define SEQCOUNT 0x05
#define ATRCOUNT 0x14
#define GFXCOUNT 0x09

struct tvmode{
	PICTUREINFO 	info;
	unsigned char	extReg[EXTCOUNT];
	unsigned char	crtReg[CRTCOUNT];
	unsigned char	seqReg[SEQCOUNT];
	unsigned char	atrReg[ATRCOUNT];
	unsigned char 	gfxReg[GFXCOUNT];
} video_clgd5446_vmode[] = {
	{
		.info = {
			.infoType = PICTURE_GFX4BPP6BPC,
			.vMemSize = (640*480/2),
			.infoWidth = 640,
			.infoHeight = 480,
			.infoRefresh = 0
		},
		.extReg = {0x01|0xC0|CLK_25},
		.crtReg = {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f,0x0,0x80,0xe,0xf,0x0,0x0,0x0,0x20,0x9c,0x8e,0x8f,0x28,0x7f,0x96,0xb9,0xff,0xff},
		.seqReg = {0xff, 0x0, 0xff, 0x0, 0x4},
		.atrReg = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0x1,0x0,0xff,0x00},
		.gfxReg = {0xff,0,0,0,0,0,0x01,0x00,0xff}
	},
	{
		.info = {
			.infoType = PICTURE_TEXT1C1A,
			.vMemSize = (80*20*2),
			.infoWidth = 80,
			.infoHeight = 20,
			.infoRefresh = 0
		},
		.extReg = {0x03|0xC0|CLK_25},
		.crtReg = {0x5f,0x4f,0x50,0x82,0x55,0x81,0xbf,0x1f,0x00,0x4f,0x0e,0x0f,0x00,0x00,0x02,0x30,0x9c,0x8e,0x8f,0x28,0x1f,0x96,0xb9,0xa3,0xff},
		.seqReg = {0x03,0x00,0x03,0x00,0x02},
		.gfxReg = {0x00,0x00,0x00,0x00,0x00,0x10,0x0e,0x0f,0x0ff},
		.atrReg = {0,1,2,3,4,5,0x14,7,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x0C,0x00,0x0F,0x08}
	}
};

static inline void vga_setmode(struct tvmode *mode){
	unsigned long x;
	// write extension registers
	outb(mode->extReg[0], 0x3C2);
	// write crt registers
	vga_crt_w(0x11, 0x0);
	for(x = 0; x < CRTCOUNT; ++x){
		vga_crt_w(x, mode->crtReg[x]);
	}
	// write sequencer registers
	for(x = 0; x < SEQCOUNT; ++x){
		vga_seq_w(x, mode->seqReg[x]);
	}
	// write graphic registers
	for(x = 0; x < GFXCOUNT; ++x){
		vga_gfx_w(x, mode->gfxReg[x]);
	}
	// reset palette flip-flop
	inb(0x3da);
	// write attribute palette
	for(x = 0; x < 0xF; ++x){
		vga_atr_w_ip(x, mode->atrReg[x]);
	}
	// enable access to attribute palette
	outb(0x20, VGA_ATR_ADDRDATA);
	// reset palette flip-flop
	inb(0x3da);
	// write remaining attribute registers
	for(; x < ATRCOUNT; ++x){
		vga_atr_w(x, mode->atrReg[x]);
	}
	return;
}

static inline void vga_pixel_4plane_4bpp(void *vmem, unsigned long pixeli, unsigned char color){
	unsigned long bytei, biti, bmask;
	bytei = (pixeli/8);
	biti = (pixeli) - (bytei*8);
	bmask = ~(0x80>>biti);

	vga_seq_w(VGA_SEQ_MMR, 1);
	((unsigned char*)vmem)[bytei] = (((unsigned char*)vmem)[bytei]&bmask) | (color&1) << (7-biti);
	vga_seq_w(VGA_SEQ_MMR, 2);
	((unsigned char*)vmem)[bytei] = (((unsigned char*)vmem)[bytei]&bmask) | ((color&2)>>1) << (7-biti);
	vga_seq_w(VGA_SEQ_MMR, 4);
	((unsigned char*)vmem)[bytei] = (((unsigned char*)vmem)[bytei]&bmask) | ((color&4)>>2) << (7-biti);
	vga_seq_w(VGA_SEQ_MMR, 8);
	((unsigned char*)vmem)[bytei] = (((unsigned char*)vmem)[bytei]&bmask) | ((color&8)>>3) << (7-biti);
	return;
}

static void vga_pixel_4plane_4bpp_from(void *lvmem, void *vmem, unsigned long count)
{
	unsigned long x, color[8], cc, y, z;
	for(z = 0; z < 4; ++z)
	{
		vga_seq_w(VGA_SEQ_MMR, (1<<z));
		for(x = 0; x < count; x += 8)
		{
			for(y = 0; y < 8; ++y)
			{
				color[y] = ((unsigned char*)lvmem)[x+y];
			}
			
			for(y = 0, cc = 0; y < 8; ++y)
			{
				cc = ((color[y] << (4+z) >> y) & (0x80 >> y)) | cc;
			}
			((unsigned char*)vmem)[x/8] = cc;
		}
	}
	return;
}

typedef struct multiboot_info
{
	unsigned long flags;
	unsigned long mem_lower;
	unsigned long mem_upper;
	unsigned long boot_device;
	unsigned long cmdline;
	unsigned long mods_count;
	unsigned long mods_addr;
	unsigned long ___a;
	unsigned long mmap_length;
	unsigned long mmap_addr;
} multiboot_info_t;

static unsigned char video_clgd5446_cmap[] = {
	0x00, 0x00, 0x00,	//00 000
	0x00, 0x00, 0xff,	//00 001
	0x00, 0xff, 0x00,	//00 010
	0x00, 0xff, 0xff,	//00 011
	0xff, 0x00, 0x00,	//00 100
	0xff, 0x00, 0xff,	//00 101
	0xff, 0xff, 0x00,	//00 110
	0xff, 0xff, 0xff,	//00 111
};

unsigned char lvmem[640*480];

void main(multiboot_info_t* mb)
{
	unsigned long x, color;
	vga_setmode(&video_clgd5446_vmode[0]);

	// load a color map
	vga_dac_w(VGA_DAC_WADDR, 0);
	for(x = 0; x < (sizeof(video_clgd5446_cmap)/3); ++x){
		vga_dac_w(VGA_DAC_DATA, video_clgd5446_cmap[x*3+0]);
		vga_dac_w(VGA_DAC_DATA, video_clgd5446_cmap[x*3+1]);
		vga_dac_w(VGA_DAC_DATA, video_clgd5446_cmap[x*3+2]);	
	}
	for(; x < 256; ++x){
		vga_dac_w(VGA_DAC_DATA, 62);
		vga_dac_w(VGA_DAC_DATA, 62);
		vga_dac_w(VGA_DAC_DATA, 62);
	}

	for(color = 0; color < 0xffffffff; ++color)
	{
		for(x = 0; x < 80; x += 1)
		{
			lvmem[x] = color*2;
		}
		for(x = 81; x < 200; x += 1)
		{
			lvmem[x] = (color + 1) * 2;
		}
		vga_pixel_4plane_4bpp_from(&lvmem,(void*)0xA0000, 640*80);
		for(x = 0; x < 0xFFFFFF; ++x);
	}

	while(1);
	return;
}
loader.s

Code: Select all

.global _loader					# making entry point visible to linker

# setting up the Multiboot header - see GRUB docs for details
.set ALIGN,    1<<0				# align loaded modules on page boundaries
.set MEMINFO,  1<<1				# provide memory map
.set FLAGS,    ALIGN | MEMINFO	# this is the Multiboot 'flag' field
.set MAGIC,    0x1BADB002		# 'magic number' lets bootloader find the header
.set CHECKSUM, -(MAGIC + FLAGS)	# checksum required

# --kmcguire:
# By putting the multi-boot header in it's own section it and altering linker.ld it seems to help ensure it is always with in
# the maximum search distance for GRUB when looking for the MAGIC value.
.align 4
.section .multiboot
.long MAGIC
.long FLAGS
.long CHECKSUM

# --kmcguire: (return to the .text section)
.text

# reserve initial kernel stack space
.set STACKSIZE, 0x4000			# that is, 16k.
.comm stack, STACKSIZE, 32		# reserve 16k stack on a quadword boundary

_loader:
	mov  $(stack + STACKSIZE), %esp	# set up the stack
	push %ebx					# Multiboot data structure

	cli
	call main					# call kernel proper
        hlt
linker.ld

Code: Select all

ENTRY (_loader)

SECTIONS 
{
	. = 0x00100000;
	start = .; _start = .; __start = .;

	.text :
	{ 
		*(.multiboot)
		*(.text)
	} 

	.rodata ALIGN (0x1000) :
	{
		*(.rodata)
	}

	.data ALIGN (0x1000) :
	{
		*(.data)
	}

	.bss : 
	{ 
		*(.bss)
	}

	end = .; _end = .; __end = .;
}
Compile
gcc -c simple.c
as loader.s

Link
ld -T linker.ld simple.o loader.o -o kernel
Run
You will need to use GRUB to boot the kernel binary with (inside the GRUB console when booting):
kernel /kernel
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

Well. I just tried it on a real computer and it does not work correctly. Makes a bunch of flickering lines. I imagine the problem is in the register values that are loaded into the VGA chipset using the set_mode function.

I might tinker with it a while tonight and try to get it working, very slim chance of getting it to working but then again maybe not.
Pyrofan1
Member
Member
Posts: 234
Joined: Sun Apr 29, 2007 1:13 am

Post by Pyrofan1 »

http://www.brackeen.com/vga/
that's a really good vga tutorial, of course in have to be in real mode or VM86
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

CompDever wrote:It is not a matter of wheter or not i have an OS with mm and other material: i just need a skeletal sample, for me to learn best. Unless I am greatly mistaken it should not be too difficult for an experienced OSdever to draw up a skeletal drawing OS, is it? [considering you should already have a mm that can be scaled down to this example, and for that matter I can put text on the screen. Plus why would you not be able to remain with the display of that one pixel visible and be forced to drop back real mode of virtual 8086?]
You drop back to real mode or create a virtual mode task so that you can use int 10h.

That's pretty much the only way to get into a mode without writing a video card driver 8) - which is what Kevin McGuire has posted (his is a very portable one, though).
CompDever
Posts: 23
Joined: Sun Jul 22, 2007 12:21 pm

Post by CompDever »

Ok, thanks for all of everyone's help. I'm going to look into these resources and see if i can figure it out.
CompDever
Posts: 23
Joined: Sun Jul 22, 2007 12:21 pm

Post by CompDever »

i was playing around with Kevin's VGA Driver and it would not compile with NASM, here are the results

[The Errors]

Code: Select all

C:\asm\vgatest.asm:1: error: attempt to define a local label before any non-local labels
C:\asm\vgatest.asm:1: error: parser: instruction expected
setting up the Multiboot header - see GRUB docs for details:1: error: attempt to define a local label before any non-local labels
setting up the Multiboot header - see GRUB docs for details:1: error: expression syntax error
setting up the Multiboot header - see GRUB docs for details:2: error: symbol `.set' redefined
setting up the Multiboot header - see GRUB docs for details:2: error: parser: instruction expected
setting up the Multiboot header - see GRUB docs for details:3: error: symbol `.set' redefined
setting up the Multiboot header - see GRUB docs for details:3: error: parser: instruction expected
setting up the Multiboot header - see GRUB docs for details:4: error: symbol `.set' redefined
setting up the Multiboot header - see GRUB docs for details:4: error: parser: instruction expected
setting up the Multiboot header - see GRUB docs for details:5: error: symbol `.set' redefined
setting up the Multiboot header - see GRUB docs for details:5: error: parser: instruction expected
--kmcguire::1: warning: unterminated string
the maximum search distance for GRUB when looking for the MAGIC value.:1: error: attempt to define a local label before any non-local labels
the maximum search distance for GRUB when looking for the MAGIC value.:1: error: parser: instruction expected
the maximum search distance for GRUB when looking for the MAGIC value.:2: error: attempt to define a local label before any non-local labels
the maximum search distance for GRUB when looking for the MAGIC value.:2: error: parser: instruction expected
the maximum search distance for GRUB when looking for the MAGIC value.:3: error: attempt to define a local label before any non-local labels
the maximum search distance for GRUB when looking for the MAGIC value.:3: error: parser: instruction expected
the maximum search distance for GRUB when looking for the MAGIC value.:4: error: symbol `.long' redefined
the maximum search distance for GRUB when looking for the MAGIC value.:4: error: parser: instruction expected
the maximum search distance for GRUB when looking for the MAGIC value.:5: error: symbol `.long' redefined
the maximum search distance for GRUB when looking for the MAGIC value.:5: error: parser: instruction expected
--kmcguire: (return to the .text section):1: error: attempt to define a local label before any non-local labels
reserve initial kernel stack space:1: error: symbol `.set' redefined
reserve initial kernel stack space:1: error: parser: instruction expected
reserve initial kernel stack space:2: error: attempt to define a local label before any non-local labels
reserve initial kernel stack space:2: error: parser: instruction expected
reserve initial kernel stack space:5: error: comma or end of line expected
reserve initial kernel stack space:5: error: expression syntax error
reserve initial kernel stack space:6: error: expression syntax error
reserve initial kernel stack space:9: error: comma or end of line expected
What Assembler are you using Kevin?
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Looks like gas from the register prefix (with the GCC toolchain, IIRC).

Cheers,
Adam
User avatar
Dex
Member
Member
Posts: 1444
Joined: Fri Jan 27, 2006 12:00 am
Contact:

Post by Dex »

pcmattman wrote: You drop back to real mode or create a virtual mode task so that you can use int 10h.

That's pretty much the only way to get into a mode without writing a video card driver 8) - which is what Kevin McGuire has posted (his is a very portable one, though).
Sure its a cool way, but those pesky patents :shock: see here:
http://www.freepatentsonline.com/6105101.html
Its OK if you just go to and from realmode, without any trap int
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

Dex wrote:
pcmattman wrote: You drop back to real mode or create a virtual mode task so that you can use int 10h.

That's pretty much the only way to get into a mode without writing a video card driver 8) - which is what Kevin McGuire has posted (his is a very portable one, though).
Sure its a cool way, but those pesky patents :shock: see here:
http://www.freepatentsonline.com/6105101.html
Its OK if you just go to and from realmode, without any trap int
I'm not good at all of this legal stuff but does cover calling BIOS interrupts from VM86 mode?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,
frank wrote:
Dex wrote:Sure its a cool way, but those pesky patents :shock: see here:
http://www.freepatentsonline.com/6105101.html
Its OK if you just go to and from realmode, without any trap int
I'm not good at all of this legal stuff but does cover calling BIOS interrupts from VM86 mode?
I'm not a lawyer either, but it looks to me like it covers calling 16-bit interrupts (that were designed for this method) by using 16-bit protected mode from 32-bit protected mode (instead of switching to real mode or using VM86).

There's no way it can cover using VM86 to call BIOS/ROM interrupts. Intel described how to use VM86 mode for this purpose in the programmer's manual for 80386, over 10 years before this patent was filed. Not only is there a huge amount of prior art, but it's obvious to anyone trained in the field (or anyone that's read an Intel manual).

For reference, take a look at the part that says "Reflecting an interrupt or exception back to the 8086 code involves the following steps" near the bottom of this page in an online copy of Intel's 8036 programmer's manual.

My copy of this manual (complete with the original ASCII art) was copyrighted by Intel in 1986. The patent was filed in 1998.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

That's one of the reasons it confused me. I remember the intel manuals telling you exactly how to use VM86 mode to call 16-bit BIOS interrupts. I don't think that Intel would be encouraging anyone to break patents. Well thanks for clearing that up.
Post Reply