VGA Graphics Question [x86]
VGA Graphics Question [x86]
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
thanks in advance
-
- Member
- Posts: 62
- Joined: Fri Jun 29, 2007 8:36 pm
-
- 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]
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.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
That's a lot of code to wade through if it's not your own.
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?]
- Kevin McGuire
- Member
- Posts: 843
- Joined: Tue Nov 09, 2004 12:00 am
- Location: United States
- Contact:
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
loader.s
linker.ld
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
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;
}
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
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 = .;
}
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
- Kevin McGuire
- Member
- Posts: 843
- Joined: Tue Nov 09, 2004 12:00 am
- Location: United States
- Contact:
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.
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.
http://www.brackeen.com/vga/
that's a really good vga tutorial, of course in have to be in real mode or VM86
that's a really good vga tutorial, of course in have to be in real mode or VM86
-
- 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:
You drop back to real mode or create a virtual mode task so that you can use int 10h.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?]
That's pretty much the only way to get into a mode without writing a video card driver - which is what Kevin McGuire has posted (his is a very portable one, though).
i was playing around with Kevin's VGA Driver and it would not compile with NASM, here are the results
[The Errors]
What Assembler are you using Kevin?
[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
Sure its a cool way, but those pesky patents see here: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 - which is what Kevin McGuire has posted (his is a very portable one, though).
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?Dex wrote:Sure its a cool way, but those pesky patents see here: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 - which is what Kevin McGuire has posted (his is a very portable one, though).
http://www.freepatentsonline.com/6105101.html
Its OK if you just go to and from realmode, without any trap int
Hi,
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
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).frank wrote:I'm not good at all of this legal stuff but does cover calling BIOS interrupts from VM86 mode?Dex wrote:Sure its a cool way, but those pesky patents see here:
http://www.freepatentsonline.com/6105101.html
Its OK if you just go to and from realmode, without any trap int
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.