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;
int linewrap = 1;
static int color_map[8] = {0, 4, 2, 6, 1, 5, 3, 7};
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 '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 '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 '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 = color_map[x - 30] + (video_attr & 0xF8);
} else if (x >= 40 && x <= 47) {
// Background color
video_attr = (color_map[x - 40] << 4) + (video_attr & 0x8F);
} else if (x == 1) {
// High intensity foreground
video_attr = video_attr | 8;
} else if (x == 2) {
// Low intensity foreground
video_attr = video_attr & ~8;
} else if (x == 5) {
// High intensity background
video_attr = video_attr | 128;
} else if (x == 6) {
// Low intensity background
video_attr = video_attr & ~128;
} else if (x == 7) {
// Reverse
video_attr = ((video_attr & 0xF0) >> 4) + ((video_attr & 0x0F) << 4);
} else if (x == 8) {
// Invisible make forground match background
video_attr = ((video_attr & 0xF0) >> 4) + (video_attr & 0xF0);
} else {
video_attr = ATTR_NORMAL;
}
break;
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 'h': // Enable line wrapping
if (x == 7) linewrap = 1;
break;
case 'l': // Disable line wrapping
if (x == 7) linewrap = 0;
break;
}
}
static int handle_multichar(int state, char ch) {
static int x, y;
switch (state) {
case 1: // Escape has arrived
switch (ch) {
case '[': // Extended sequence
return 2;
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 char set
return 5;
case 'c': // Reset
cursor_pos = 0;
video_attr = ATTR_NORMAL;
linewrap = 1;
return 0;
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;
}
if (ch >= ' ') {
vidmem[cursor_pos++] = ch;
vidmem[cursor_pos++] = attr;
} else {
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;
}
}
// Scroll if position is off-screen
if (cursor_pos >= SCREENSIZE) {
if (linewrap) {
scroll_up();
cursor_pos -= LINESIZE;
} else {
cursor_pos = SCREENSIZE - CELLSIZE;
}
}
}
}
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;
}