sos-code-article10/drivers/x86_videomem.c

271 lines
6.4 KiB
C

/* Copyright (C) 2004 David Decotigny
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include <sos/klibc.h>
#include <hwcore/ioports.h>
#include "x86_videomem.h"
/* The text video memory starts at address 0xB8000. Odd bytes are the
ASCII value of the character, even bytes are attribute for the
preceding character. */
#define VIDEO 0xb8000
/* Console screen size */
#define LINES 25
#define COLUMNS 80
/*
* VGA ports and commands.
*
* @see Ralf Brown's interrupt (and port) list
* http://www-2.cs.cmu.edu/~ralf/files.html
*/
/* VGA ports */
#define VGA_COMMAND_PORT 0x3D4
#define VGA_DATA_PORT 0x3D5
/* VGA commands */
#define VGA_SET_CURSOR_START 0xA
#define VGA_SET_CURSOR_END 0xB
#define VGA_SET_CURSOR_HIGH 0xE
#define VGA_SET_CURSOR_LOW 0xF
/** The structure of a character element in the video memory. @see
http://webster.cs.ucr.edu/AoA DOS edition chapter 23 */
typedef struct {
unsigned char character;
unsigned char attribute;
} __attribute__ ((packed)) x86_video_mem[LINES*COLUMNS];
/** The base pointer for the video memory */
static volatile x86_video_mem *video = (volatile x86_video_mem*)VIDEO;
sos_ret_t sos_x86_videomem_setup(void)
{
/* CRT index port => ask for access to register 0xa ("cursor
start") */
outb(0x0a, VGA_COMMAND_PORT);
/* (RBIL Tables 708 & 654) CRT Register 0xa => bit 5 = cursor OFF */
outb(1 << 5, VGA_DATA_PORT);
return SOS_OK;
}
sos_ret_t sos_x86_videomem_cls(unsigned char attribute)
{
/* Clears the screen */
int i;
for(i = 0 ; i < LINES*COLUMNS ; i++)
{
(*video)[i].character = 0;
(*video)[i].attribute = attribute;
}
return SOS_OK;
}
sos_ret_t sos_x86_videomem_putstring(unsigned char row, unsigned char col,
unsigned char attribute,
const char *str)
{
unsigned video_offs = row*COLUMNS + col;
if (video_offs >= LINES*COLUMNS)
return -SOS_EINVAL;
for ( ; str && *str && (video_offs < LINES*COLUMNS) ; str++, video_offs++)
{
(*video)[video_offs].character = (unsigned char)*str;
(*video)[video_offs].attribute = attribute;
}
return SOS_OK;
}
sos_ret_t sos_x86_videomem_putchar(unsigned char row, unsigned char col,
unsigned char attribute,
unsigned char c)
{
unsigned video_offs = row*COLUMNS + col;
if (video_offs >= LINES*COLUMNS)
return -SOS_EINVAL;
(*video)[video_offs].character = c;
(*video)[video_offs].attribute = attribute;
return SOS_OK;
}
sos_ret_t sos_x86_videomem_printf(unsigned char row, unsigned char col,
unsigned char attribute,
const char *format, /* args */...)
{
char buff[256];
va_list ap;
va_start(ap, format);
vsnprintf(buff, sizeof(buff), format, ap);
va_end(ap);
return sos_x86_videomem_putstring(row, col, attribute, buff);
}
/*
* Console that supports scrolling, based on the low-level code
* above. This console only takes part of the screen, starting at row
* CONSOLE_ROW_START. The rows before that one are free for use by the
* kernel debugging messages.
*/
/* Current row in the high-level console. Must be signed, because of
computations inside sos_screen_putchar() */
static int row;
/* Current column in the high-level console. Must be signed, because
of computations inside sos_screen_putchar() */
static int col;
/* The limit between the low-level console, accessible to the kernel,
and the high-level console, accessible to the user applications
through the sos_screen_putchar() function. */
#define CONSOLE_ROW_START 12
static void sos_screen_set_cursor (unsigned int _row, unsigned int _col)
{
unsigned int pos;
pos = (_row * COLUMNS + _col);
outb(VGA_SET_CURSOR_HIGH, VGA_COMMAND_PORT);
outb( (pos >> 8), VGA_DATA_PORT);
outb(VGA_SET_CURSOR_LOW, VGA_COMMAND_PORT);
outb( (pos & 0xFF), VGA_DATA_PORT);
}
sos_ret_t sos_screen_putchar (char c)
{
if (c == '\r')
{
/* Go to first row */
col = 0;
}
/* New line */
else if (c == '\n')
{
/* Go to next line */
col = 0;
row ++;
}
/* Remove the last character */
else if (c == '\b')
{
/* Next character should be displayed instead of the current
one */
col --;
/* Handle the case where we're at the beginning of a line */
if (col < 0)
{
row --;
col = COLUMNS-1;
if (row < CONSOLE_ROW_START)
{
row = CONSOLE_ROW_START;
col = 0;
}
}
/* Replace the current character with a space */
sos_x86_videomem_putchar
(row, col, SOS_X86_VIDEO_FG_BLUE | SOS_X86_VIDEO_BG_LTGRAY, ' ');
}
else if (c != 0)
{
sos_x86_videomem_putchar
(row, col, SOS_X86_VIDEO_FG_BLUE | SOS_X86_VIDEO_BG_LTGRAY, c);
col++;
if (col == COLUMNS)
{
col = 0;
row++;
}
}
/* Need to scroll ? */
if (row == LINES)
{
int i;
/* Copy each line in the previous line */
for (i = CONSOLE_ROW_START; i < LINES; i++)
memcpy ((char*) video + i * COLUMNS * 2,
(char*) video + ((i + 1) * COLUMNS * 2),
COLUMNS * 2);
/* Reset the last line of the console */
for (i = 0; i < COLUMNS; i++)
sos_x86_videomem_putchar
(LINES-1, i, SOS_X86_VIDEO_FG_BLUE | SOS_X86_VIDEO_BG_LTGRAY, ' ');
row--;
}
sos_screen_set_cursor (row, col);
return SOS_OK;
}
sos_ret_t sos_screen_init (void)
{
int i, j;
row = CONSOLE_ROW_START;
col = 0;
/* Set the first scan line for the cursor, and the blinking
mode. First scan line is 11, so that we have a visible
cursor. */
outb(VGA_SET_CURSOR_START, VGA_COMMAND_PORT);
outb(((0x2 << 5) | 14), VGA_DATA_PORT);
for (i = CONSOLE_ROW_START; i < LINES; i++)
{
for (j = 0; j < COLUMNS; j++)
sos_x86_videomem_putchar
(i, j, SOS_X86_VIDEO_FG_BLUE | SOS_X86_VIDEO_BG_LTGRAY, ' ');
}
sos_screen_set_cursor (row, col);
return SOS_OK;
}