os-k/src/kaleid/kernel/io/terminal.c

211 lines
4.9 KiB
C

//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
// Authors: spectral` //
// NeoX //
// //
// Desc: Early terminal functions //
//----------------------------------------------------------------------------//
#define _UNLOCKED_IO
#include <kalkern/io/terminal.h>
//
// VGA-related macros
//
#define ComputeColorCode(fg, bg) ((fg) | (bg) << 4)
#define ComputeOffset(kt, x, y) ((y) * kt->kt_width + (x))
#define ComputeEntry(ch, cl) (((ushort)(ch)) | (ushort)(cl) << 8)
//
// VGA output
//
static terminal_t _vga_term = {
.kt_buffer = (ushort *)0xB8000,
.kt_width = 80,
.kt_height = 25,
.kt_curr_x = 0,
.kt_curr_y = 0,
.kt_color = ComputeColorCode(KTERM_COLOR_LGREY, KTERM_COLOR_BLACK),
.kt_lock = INITLOCK(KLOCK_MUTEX),
.kt_init = FALSE,
};
//
// Standard output terminal
//
terminal_t *stdout;
//
// Debugging terminal
//
terminal_t *stddbg;
//
// Initialize standard output
//
void InitTerms(void)
{
Assert(!stdout && _vga_term.kt_init != INITOK);
_vga_term.kt_init = INITOK;
stddbg = &_vga_term;
// to be switched to VESA
stdout = &_vga_term;
ClearTerm(stdout);
}
//
// Fill terminal with spaces
//
status_t ClearTerm(terminal_t *kt)
{
if (kt == NULL)
return BAD_ARG_NULL;
Assert(kt->kt_init == INITOK);
LockTerm(kt);
ClearTermUnlocked(kt);
UnlockTerm(kt);
return SUCCESS;
}
//
// Change the color code
//
status_t ChTermColor(terminal_t *kt, uchar color)
{
if (color > KTERM_COLOR_WHITE)
return BAD_ARG_RANGE;
if (kt == NULL)
return BAD_ARG_NULL;
LockTerm(kt);
kt->kt_color = color;
UnlockTerm(kt);
return SUCCESS;
}
//
// Write a single character on the terminal
//
status_t PutOnTerm(terminal_t *kt, char ch)
{
if (kt == NULL)
return BAD_ARG_NULL;
Assert(kt->kt_init == INITOK);
LockTerm(kt);
PutOnTermUnlocked(kt, ch);
UnlockTerm(kt);
return SUCCESS;
}
//
// Print string on terminal
//
status_t PrintOnTerm(terminal_t *kt, const char *str)
{
if (kt == NULL)
return BAD_ARG_NULL;
Assert(kt->kt_init == INITOK);
LockTerm(kt);
while (*str) {
PutOnTermUnlocked(kt, *str++);
}
UnlockTerm(kt);
return SUCCESS;
}
//----------------------------------------------------------//
// UNLOCKED VERSIONS //
// //
// Direct use is highly deprecated //
// Useful in rare instances //
//----------------------------------------------------------//
//
// Fill terminal with spaces (UNLOCKED version)
// XXX would '\0' work too?
//
void ClearTermUnlocked(terminal_t *kt)
{
size_t i;
const ushort filler = ComputeEntry(' ', kt->kt_color);
const size_t bufsize = kt->kt_width * kt->kt_height;
for (i = 0; i < bufsize; i++) {
// XXX implement memsetw()
kt->kt_buffer[i] = filler;
}
kt->kt_curr_x = kt->kt_curr_y = 0;
}
//
// Write a single character on the terminal (UNLOCKED version)
//
void PutOnTermUnlocked(terminal_t *kt, char ch)
{
int i;
size_t prev_row;
// carriage return takes us back to the beginning of the line
// no further test should be necessary
if (ch == '\r') { kt->kt_curr_x = 0; return; }
// line feed first takes us to the very end of the line
// later in this function we actually do the line feed
else if (ch == '\n') { kt->kt_curr_y = kt->kt_width - 1; }
// tabulations account for "TABSIZE" spaces
else if (ch == '\t') {
prev_row = kt->kt_curr_y;
// compiler will optimize this away
for (i = 0; i < TABSIZE; i++) {
// tabulations can't spread over two lines
if (kt->kt_curr_y == prev_row) {
PutOnTermUnlocked(kt, ' ');
}
}
}
else {
// actually write on the buffer
const size_t offset = ComputeOffset(kt, kt->kt_curr_x, kt->kt_curr_y);
kt->kt_buffer[offset] = ComputeEntry(ch, kt->kt_color);
}
// end of line?
if (++kt->kt_curr_x == kt->kt_width) {
kt->kt_curr_x = 0;
// line feed + end of buffer?
if (++kt->kt_curr_y == kt->kt_height) {
kt->kt_curr_y = 0;
}
}
}
//
// Print string on terminal (UNLOCKED version)
//
void PrintOnTermUnlocked(terminal_t *kt, const char *str)
{
while (*str) {
PutOnTermUnlocked(kt, *str++);
}
}