271 lines
6.4 KiB
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;
|
||
|
}
|