/* 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 #include #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; }