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

187 lines
4.3 KiB
C

//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
// Authors: spectral` //
// NeoX //
// //
// Desc: Early terminal functions //
//----------------------------------------------------------------------------//
#include "kernel/io/terminal.h"
//
// VGA-related macros
//
#define ComputeColorCode(fg, bg) ((fg) | (bg) << 4)
#define ComputeEntryOffset(kt, x, y) ((y) * kt->kt_width + (x))
#define ComputeEntry(ch, cl) (((ushort)(ch)) | (ushort)(cl) << 8)
//
// VGA output
//
static struct kterm _kt_vga = {
.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 = NULL,
#ifndef _NO_DEBUG
.kt_init = FALSE,
#endif
};
//
// Standard output terminal
//
struct kterm *kt_stdout;
//
// Initialize standard output
//
void kterm_init(void)
{
assert(!kt_stdout && !_kt_vga.kt_init && "kterm_init() called twice");
#ifndef _NO_DEBUG
_kt_vga.kt_init = TRUE;
#endif
// to be switched to VESA
kt_stdout = &_kt_vga;
ktclear();
}
//
// Fills terminal with spaces
// XXX would '\0' work too?
//
status_t kterm_clear(struct kterm *kt)
{
size_t i;
if (kt == NULL)
return BAD_ARG_NULL;
assert(kt->kt_init && "kterm_clear called before initialization");
kterm_lock(kt);
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 memset()
kt->kt_buffer[i] = filler;
}
kt->kt_curr_x = kt->kt_curr_y = 0;
kterm_unlock(kt);
return SUCCESS;
}
//
// Change the color code
//
status_t kterm_change_color(struct kterm *kt, uchar color)
{
if (color > KTERM_COLOR_WHITE)
return BAD_ARG_RANGE;
if (kt == NULL)
return BAD_ARG_NULL;
kterm_lock(kt);
kt->kt_color = color;
kterm_unlock(kt);
return SUCCESS;
}
//
// Writes a single character on the terminal (UNLOCKED version)
//
// DEPRECATED:
// - always use kterm_putch (LOCKED version)
// - doesn't check for NULL input
//
void kterm_putch_unlocked(struct kterm *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 4 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) {
kterm_putch_unlocked(kt, ' ');
}
}
}
// XXX check whether we were given a writable character
else {
const size_t offset = ComputeEntryOffset(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;
}
}
}
//
// Writes a single character on the terminal (LOCKED version)
//
status_t kterm_putch(struct kterm *kt, char ch)
{
if (kt == NULL)
return BAD_ARG_NULL;
kterm_lock(kt);
kterm_putch_unlocked(kt, ch);
kterm_unlock(kt);
return SUCCESS;
}
//
// Print string on kterminal
//
status_t kterm_print(struct kterm *kt, const char *str)
{
if (kt == NULL)
return BAD_ARG_NULL;
kterm_lock(kt);
while (*str) {
kterm_putch_unlocked(kt, *str++);
}
kterm_unlock(kt);
return SUCCESS;
}