Goto sanos source index
//
// video.c
//
// 6845 Video Controller
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os/krnl.h>
#define VIDEO_PORT_REG 0x3D4
#define VIDEO_PORT_DATA 0x3D5
#define VIDEO_REG_CURSOR_MSB 0x0E
#define VIDEO_REG_CURSOR_LSB 0x0F
#define COLS 80
#define LINES 25
#define CELLSIZE 2
#define LINESIZE (COLS * CELLSIZE)
#define SCREENSIZE (LINESIZE * LINES)
#define TABSTOP 8
#define ATTR_NORMAL 0x07
#define ATTR_INVERSE 0x70
unsigned char *vidmem;
int cursor_pos;
unsigned char video_attr = ATTR_NORMAL;
int video_state = 0;
int saved_cursor_pos = 0;
void init_video()
{
// Set video base address (use mapped video base)
vidmem = (unsigned char *) VIDBASE_ADDRESS;
// Get cursor position
outp(VIDEO_PORT_REG, VIDEO_REG_CURSOR_MSB);
cursor_pos = inp(VIDEO_PORT_DATA) & 0xFF;
cursor_pos <<= 8;
outp(VIDEO_PORT_REG, VIDEO_REG_CURSOR_LSB);
cursor_pos += inp(VIDEO_PORT_DATA) & 0xFF;
cursor_pos <<= 1;
}
void show_cursor()
{
// Set new cursor position
unsigned int pos = cursor_pos >> 1;
outp(VIDEO_PORT_REG, VIDEO_REG_CURSOR_LSB);
outp(VIDEO_PORT_DATA, pos & 0xFF);
outp(VIDEO_PORT_REG, VIDEO_REG_CURSOR_MSB);
outp(VIDEO_PORT_DATA, pos >> 8);
}
void hide_cursor()
{
// Set cursor position off screen
unsigned int pos = COLS * LINES + 1;
outp(VIDEO_PORT_REG, VIDEO_REG_CURSOR_LSB);
outp(VIDEO_PORT_DATA, pos & 0x0ff);
outp(VIDEO_PORT_REG, VIDEO_REG_CURSOR_MSB);
outp(VIDEO_PORT_DATA, pos >> 8);
}
void set_cursor(int col, int line)
{
cursor_pos = (line * COLS + col) * CELLSIZE;
}
static void clear_line(unsigned char *p)
{
int i;
unsigned char attr = video_attr;
for (i = 0; i < COLS; i++)
{
*p++ = ' ';
*p++ = attr;
}
}
static void scroll_up()
{
// Scroll screen up
memcpy(vidmem, vidmem + LINESIZE, SCREENSIZE - LINESIZE);
// Clear bottom row
clear_line(vidmem + SCREENSIZE - LINESIZE);
}
static void scroll_down()
{
// Scroll screen down
memcpy(vidmem + LINESIZE, vidmem, SCREENSIZE - LINESIZE);
// Clear top row
clear_line(vidmem);
}
static void handle_sequence(int x, int y, char ch)
{
switch (ch)
{
case 'A': // Cursor up
while (x-- > 0)
{
cursor_pos -= LINESIZE;
if (cursor_pos < 0)
{
cursor_pos += LINESIZE;
break;
}
}
break;
case 'B': // Cursor down
while (x-- > 0)
{
cursor_pos += LINESIZE;
if (cursor_pos >= SCREENSIZE)
{
cursor_pos -= LINESIZE;
break;
}
}
break;
case 'C': // Cursor right
cursor_pos += x * CELLSIZE;
if (cursor_pos >= SCREENSIZE) cursor_pos = SCREENSIZE - 1;
break;
case 'D': // Cursor left
cursor_pos -= x * CELLSIZE;
if (cursor_pos < 0) cursor_pos = 0;
break;
case 'L': // Insert line
while (x-- > 0)
{
int sol = cursor_pos - cursor_pos % LINESIZE;
memcpy(vidmem + sol + LINESIZE, vidmem + sol, SCREENSIZE - LINESIZE - sol);
clear_line(vidmem + sol * LINESIZE);
}
break;
case 'M': // Delete line
while (x-- > 0)
{
int sol = cursor_pos - cursor_pos % LINESIZE;
memcpy(vidmem + sol, vidmem + sol + LINESIZE, SCREENSIZE - LINESIZE - sol);
clear_line(vidmem + SCREENSIZE - LINESIZE);
}
break;
case '@': // Insert character
while (x-- > 0)
{
memcpy(vidmem + cursor_pos + CELLSIZE, vidmem + cursor_pos, SCREENSIZE - cursor_pos - 1);
vidmem[cursor_pos] = ' ';
vidmem[cursor_pos + 1] = video_attr;
}
break;
case 'P': // Delete character
while (x-- > 0)
{
memcpy(vidmem + cursor_pos, vidmem + cursor_pos + CELLSIZE, SCREENSIZE - cursor_pos - 1);
vidmem[SCREENSIZE - 2] = ' ';
vidmem[SCREENSIZE - 1] = video_attr;
}
break;
case 's': // Save cursor
saved_cursor_pos = cursor_pos;
break;
case 'u': // Restore cursor
cursor_pos = saved_cursor_pos;
break;
case 'J': // Clear screen/eos
if (x == 1)
{
unsigned char *p = vidmem + cursor_pos;
while (p < vidmem + SCREENSIZE)
{
*p++ = ' ';
*p++ = video_attr;
}
}
else
{
unsigned char *p = vidmem;
while (p < vidmem + SCREENSIZE)
{
*p++ = ' ';
*p++ = video_attr;
}
cursor_pos = 0;
}
break;
case 'H': // Position
{
int line = x;
int col = y;
if (line < 1) line = 1;
if (line > LINES) line = LINES;
if (col < 1) col = 1;
if (col > COLS) col = COLS;
set_cursor(col - 1, line - 1);
break;
}
case 'K': // Clear to end of line
{
int pos = cursor_pos;
do {
vidmem[pos++] = ' ';
vidmem[pos++] = video_attr;
} while ((pos) % LINESIZE != 0);
break;
}
case 'm': // Set character enhancements
// Modified for ANSI color attributes 3/15/07 - C Girdosky
if (x >= 30 && x <= 37) // Foreground color
video_attr = (x - 30) + (video_attr & 0xF8);
else if (x >= 40 && x <= 47) // Background color
video_attr = ((x - 40) << 4) + (video_attr & 0x8F);
else if (x == 1) // High intensity foreground
video_attr = video_attr | 8;
else if (x == 5) // High intensity background
video_attr = video_attr | 128;
else if (x == 8) // Invisible make forground match background
video_attr = ((video_attr & 0xF0) >> 4) + (video_attr & 0xF0);
else if (x == 7) // Reverse
video_attr = ((video_attr & 0xF0) >> 4) + ((video_attr & 0x0F) << 4);
else
video_attr = ATTR_NORMAL;
break;
}
}
static int handle_multichar(int state, char ch)
{
static int x, y;
switch (state)
{
case 1: // Escape has arrived
switch (ch)
{
case 'P': // Cursor down a line
cursor_pos += LINESIZE;
if (cursor_pos >= SCREENSIZE) cursor_pos -= LINESIZE;
return 0;
case 'K': // Cursor left
if (cursor_pos > 0) cursor_pos -= CELLSIZE;
return 0;
case 'H': // Cursor up
cursor_pos -= LINESIZE;
if (cursor_pos < 0) cursor_pos += LINESIZE;
return 0;
case 'D': // Scroll forward
scroll_up();
return 0;
case 'M': // Scroll reverse
scroll_down();
return 0;
case 'G': // Cursor home
cursor_pos = 0;
return 0;
case '[': // Extended sequence
return 2;
case '(': // Extended char set
return 5;
default:
return 0;
}
case 2: // Seen Esc-[
if (ch == '?') return 3;
if (ch >= '0' && ch <= '9')
{
x = ch - '0';
return 3;
}
if (ch == 's' || ch == 'u')
handle_sequence(0, 0, ch);
else if (ch == 'r' || ch == 'm')
handle_sequence(0, 0, ch);
else
handle_sequence(1, 1, ch);
return 0;
case 3: // Seen Esc-[<digit>
if (ch >= '0' && ch <= '9')
{
x = x * 10 + (ch - '0');
return 3;
}
if (ch == ';')
{
y = 0;
return 4;
}
handle_sequence(x, 1, ch);
return 0;
case 4: // Seen Esc-[<digits>;
if (ch >= '0' && ch <= '9')
{
y = y * 10 + (ch - '0');
return 4;
}
handle_sequence(x, y, ch);
return 0;
case 5: // Seen Esc-(
// Ignore char set selection
return 0;
default:
return 0;
}
}
void print_buffer(const char *str, int len)
{
int ch;
int i;
unsigned char *p;
char *end;
unsigned char attr = video_attr;
if (!str) return;
end = (char *) str + len;
while (str < end)
{
ch = *str++;
// If we are inside a multi-character sequence handle it using state machine
if (video_state > 0)
{
video_state = handle_multichar(video_state, ch);
attr = video_attr;
continue;
}
switch (ch)
{
case 0:
break;
case '\n': // Newline
cursor_pos = (cursor_pos / LINESIZE + 1) * LINESIZE;
break;
case '\r': // Carriage return
cursor_pos = (cursor_pos / LINESIZE) * LINESIZE;
break;
case '\t':
cursor_pos = (cursor_pos / (TABSTOP * CELLSIZE) + 1) * (TABSTOP * CELLSIZE);
break;
case 8: // Backspace
if (cursor_pos > 0) cursor_pos -= CELLSIZE;
break;
case 12: // Formfeed
p = (unsigned char *) vidmem;
for (i = 0; i < COLS * LINES; i++)
{
*p++ = ' ';
*p++ = attr;
}
cursor_pos = 0;
break;
case 27: // Escape
video_state = 1;
break;
default: // Normal character
vidmem[cursor_pos++] = ch;
vidmem[cursor_pos++] = attr;
break;
}
// Scroll if position is off-screen
if (cursor_pos >= SCREENSIZE)
{
scroll_up();
cursor_pos -= LINESIZE;
}
}
}
void print_string(const char *str)
{
if (str) print_buffer(str, strlen(str));
show_cursor();
}
void print_char(int ch)
{
char c = ch;
print_buffer(&c, 1);
show_cursor();
}
void clear_screen()
{
int i;
unsigned char *p;
unsigned char attr = video_attr;
// Fill screen with background color
p = (unsigned char *) vidmem;
for (i = 0; i < COLS * LINES; i++)
{
*p++ = ' ';
*p++ = attr;
}
// Set cursor to upper-left corner of the screen
cursor_pos = 0;
show_cursor();
}
int screen_proc(struct proc_file *pf, void *arg)
{
int i, j;
unsigned char *p;
// Dump screen to proc files
p = (unsigned char *) vidmem;
for (i = 0; i < LINES; i++)
{
for (j = 0; j < COLS; j++)
{
proc_write(pf, p, 1);
p += 2;
}
proc_write(pf, "\r\n", 2);
}
return 0;
}