Page 1 of 1

only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 7:40 am
by michaellangford
Note: This is my first question, so please bear with me.

I have been working on a simple kernel that has a basic file system and graphics. I first got 320x200x8 graphics working perfectly, using GRUB 2 to load my kernel, and using the multiboot header with GRUB 2 to change graphics mode:
working 320x200x8
working 320x200x8
VirtualBox_Clement_21_06_2016_09_19_53.png (3.47 KiB) Viewed 3134 times
I soon found the resolution to be too small, and so I changed the target resolution on the multiboot header to 640x400x8, and modified my graphics driver to work with the changes. When I boot the kernel, the display works at first, but when the console grows downward about a quarter of the way to the bottom, nothing is shown:
not working 640x400x8
not working 640x400x8
VirtualBox_Clement_21_06_2016_09_24_40.png (3.04 KiB) Viewed 3134 times
Here is my boot.s file with the multiboot header.:

Code: Select all


.set ALIGN,    1<<0             
.set MEMINFO,  1<<1 
.set VIDINFO,  1<<2
.set FLAGS,    ALIGN | MEMINFO | VIDINFO
.set MAGIC,    0x1BADB002       
.set CHECKSUM, -(MAGIC + FLAGS)

#the multiboot header
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
.long 0, 0, 0, 0, 0
.long 0 #set graphics mode
.long 640, 400, 8  #width, height, depth

.section .bootstrap_stack, "aw", @nobits
stack_bottom:
.skip 16384 # 16 KiB
stack_top:

.section .text
.global _start
.type _start, @function
_start:

	movl $stack_top, %esp

	call kernel_main

	cli
	hlt
.Lhang:
	jmp .Lhang


.size _start, . - _start


Here is my vga code (vga.h & vga.c):

Code: Select all


#ifndef VGA_H
#define VGA_H


/* Hardware 8-bit color constants. */
enum vga_color {
	COLOR_BLACK = 0,
	COLOR_BLUE = 1,
	COLOR_GREEN = 2,
	COLOR_CYAN = 3,
	COLOR_RED = 4,
	COLOR_MAGENTA = 5,
	COLOR_BROWN = 6,
	COLOR_LIGHT_GREY = 7,
	COLOR_DARK_GREY = 8,
	COLOR_LIGHT_BLUE = 9,
	COLOR_LIGHT_GREEN = 10,
	COLOR_LIGHT_CYAN = 11,
	COLOR_LIGHT_RED = 12,
	COLOR_LIGHT_MAGENTA = 13,
	COLOR_LIGHT_BROWN = 14,
	COLOR_WHITE = 15,
};

#define Video_Width  640
#define Video_Height 400

typedef uint8_t pixel;

void putpixel(int x,int y, int color);
void graphics_clear(int color);
void graphics_fillrect(size_t x, size_t y, size_t width, size_t height, int color);
// WARNING ----- Will have to be updated for different screen resolution
void graphics_rect(size_t x, size_t y, size_t width, size_t height, int color);
void graphics_drawline(size_t x1, size_t y1, size_t x2, size_t y2, int color);
void graphics_bitmap( int x, int y, int width, int height, uint8_t *data, int forecolor, int backcolor);
void graphics_char( int x, int y, char ch, int forecolor, int backcolor);
void graphics_string( int x, int y, const char *ch, int forecolor, int backcolor);
void graphics_init();

#endif


Code: Select all


#include "system.h"
#include "font.h"

//===============GRAPHICS====================
const size_t memory_location = 0xA0000;
const size_t pitch = 1;

pixel* graphics_buffer;

void putpixel(int x,int y, int color) {
    unsigned where = x*pitch + y*Video_Width*pitch;
    graphics_buffer[where] = color;
}

void graphics_clear(int color)
{
	for (size_t y = 0; y < Video_Height; y++) {
		for (size_t x = 0; x < Video_Width; x++) {
			putpixel(x, y, color);
		}
	}
}

void graphics_fillrect(size_t x, size_t y, size_t width, size_t height, int color)
{
	for (size_t ypos = y; ypos < (y+height); ypos++) {
		for (size_t xpos = x; xpos < (x+width); xpos++) {
			putpixel(xpos, ypos, color);
		}
	}
}


// WARNING ----- Will have to be updated for different screen resolution
void graphics_rect(size_t x, size_t y, size_t width, size_t height, int color)
{
	unsigned where = x*pitch + y*Video_Width*pitch;
    graphics_buffer[where] = color;
    for(size_t top=0; top < width; top++)
    {
    	graphics_buffer[where+top] = color;
    }
    for(size_t lside=0; lside < height; lside++)
    {
    	graphics_buffer[where+(lside*Video_Width)] = color;
    }
    for(size_t rside=0; rside < height; rside++)
    {
    	graphics_buffer[where+width+(rside*Video_Width)] = color;
    }
    for(size_t bottom=0; bottom < width; bottom++)
    {
    	graphics_buffer[where+bottom + Video_Width*height] = color;
    }

}

void graphics_drawline(size_t x1, size_t y1, size_t x2, size_t y2, int color)
{
	double xpos = (double)x1;
	double ypos = (double)y1;
	double xdir=(double)x2-(double)x1;
	double ydir=(double)y2-(double)y1;

    double length = sqroot(sqr(xdir)+sqr(ydir));

    if (length != 0){
        xdir = xdir/length;
        ydir = ydir/length;
    }
    while(true)
	{
		if (sqroot(sqr(xpos-(double)x1)+sqr(ypos-(double)y1)) >= length)
			break;
		putpixel(xpos, ypos, color);
		xpos += xdir;
		ypos += ydir;
	}
}

void graphics_bitmap( int x, int y, int width, int height, uint8_t *data, int forecolor, int backcolor)
{
	int i,j,b;
	int value;

	b=0;

	for(j=0;j<height;j++) {
		for(i=0;i<width;i++) {
			value = ((*data)<<b)&0x80;
			if(value) {
				putpixel(x+i,y+j,forecolor);
			} else {
				putpixel(x+i,y+j,backcolor);
			}
			b++;
			if(b==8) {
				data++;
				b=0;
			}
		}
	}
}

void graphics_char( int x, int y, char ch, int forecolor, int backcolor)
{
  int u = ((int)ch)*FONT_WIDTH*FONT_HEIGHT/8;
	return graphics_bitmap(x,y,FONT_WIDTH,FONT_HEIGHT,&fontdata[u],forecolor,backcolor);
}

void graphics_string( int x, int y, const char *ch, int forecolor, int backcolor)
{
	int _char=0;
	while(*ch) {
		graphics_char( x + _char*8, y, *ch, forecolor, backcolor);
		ch++;
		_char++;
	}
}

void graphics_init() {
	graphics_buffer = (pixel*) memory_location;
	graphics_clear(256);
  //can't use printf because its not initialized yet
  graphics_string(0, 0, "vga: ready\n", COLOR_RED, COLOR_BLACK);
}


Sorry this is so long!

I have noticed that the amount of memory required to change from 320x200x8 to 640x400x8 is about four times more than the former resolution, and the point at which the display ceases to work is about a quarter of the way down the screen. By the way, my grub.cfg file is just a very basic menuentry and multiboot command. Is it possible I have to tell GRUB in the cfg file what resolution to use, or that the vga memory is not initialized properly? Thanks!

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 7:53 am
by Octocontrabass
michaellangford wrote:const size_t memory_location = 0xA0000;
The VGA framebuffer at this address is 128kB (and only 64kB can be used in graphics mode). The 640x480x8 screen resolution requires a 250kB frame buffer.

You must use the data provided by GRUB to find the framebuffer.

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 8:04 am
by michaellangford
Thank you! that makes good sense.
Where does GRUB store the parameters by default? or does the location differ?

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 9:00 am
by Combuster
See the specification http://git.savannah.gnu.org/cgit/grub.g ... multiboot2 - in particular lines 977-1065. Legacy multiboot has a corresponding section for video.

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 9:04 am
by michaellangford
Combuster wrote:See the specification http://git.savannah.gnu.org/cgit/grub.g ... multiboot2 - in particular lines 977-1065. Legacy multiboot has a corresponding section for video.
Thanks! will give it a read!

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 11:12 am
by BenLunt
If you wish to not rely on GRUB and do the work yourself, you can get this information from the (VESA) BIOS.

The BIOS will return mode information including where, if it exists, the linear frame buffer is located.

Calling service 0x4F00 to see if the VBE is available, then calling service 0x4F01 to get the information about the found mode(s). This information will indicate whether a linear frame buffer is supported. If not, you must use Bank Switching and 0xA0000 as you have already done so far.

If you use the above services, you can retrieve all available modes supported by the video card, then finding the ones also supported by the current monitor, then you can switch between them using service 0x4F02.

Just a thought.
http://www.fysnet.net/osdesign_book_series.htm

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 12:38 pm
by michaellangford
Thank you all for your help!

Here is working 640x480x8 video mode:
VirtualBox_Clement_21_06_2016_14_04_25.png

Re: only half of screen displays at 640X400x8 video mode

Posted: Tue Jun 21, 2016 12:40 pm
by michaellangford
By the way, that file system is not actually FAT, its just a VSFS (very simple file system !) that is somewhat like FAT, and has a file allocation table. [-X