trios/kernel/term.c

207 lines
3.7 KiB
C

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <string_volatile.h>
#include <stdarg.h>
#include <sys.h>
#include "term.h"
static volatile uint16_t *screen = (uint16_t*)0xb8000;
static size_t term_x = 0;
static size_t term_y = 0;
static uint8_t color = 0x0f;
static uint16_t screen_buf[TERM_W * TERM_H * 2];
static inline uint16_t screen_entry(char c, uint8_t col) {
return (uint16_t)c | ((uint16_t)col << 8);
}
void putc(char c) {
switch(c) {
case '\n':
term_x = TERM_W;
break;
case '\t':
if(term_x % 8 == 0) term_x += 8;
term_x += 7 - (term_x % 8);
break;
default:
screen_buf[term_x + term_y * TERM_W] = screen_entry(c, color);
term_x += 1;
break;
}
if(term_x >= TERM_W) {
term_x = 0;
term_y += 1;
}
if(term_y >= TERM_H) {
term_scroll(1);
}
if(c == '\n') {
term_flush();
}
}
void puts(const char *s) {
for(; *s != '\0'; s++) {
putc(*s);
}
}
static uint8_t hex2i(char hex) {
if(hex < 'A') {
return hex - '0';
} else if(hex < 'a') {
return hex - 'A' + 10;
} else {
return hex - 'a' + 10;
}
}
static char i2d(int i, bool upper) {
if(i < 10) {
return '0' + i;
} else {
return 'a' + (i - 10) + (upper ? 'A' - 'a' : 0);
}
}
static void itoa(int n, char *buf, int base, bool upper) {
if(n == 0) {
buf[0] = '0';
buf[1] = '\0';
return;
}
if(n < 0) {
n = -n;
buf[0] = '-';
buf++;
}
char *start = buf;
while(n != 0) {
*buf++ = i2d(n % base, upper);
n /= base;
}
*buf-- = '\0';
while(buf > start) {
char tmp = *start;
*start++ = *buf;
*buf-- = tmp;
}
}
static void utoa(unsigned int n, char *buf, int base, bool upper) {
if(n == 0) {
buf[0] = '0';
buf[1] = '\0';
return;
}
char *start = buf;
while(n != 0) {
*buf++ = i2d(n % base, upper);
n /= base;
}
*buf-- = '\0';
while(buf > start) {
char tmp = *start;
*start++ = *buf;
*buf-- = tmp;
}
}
void vprintf(const char *fstr, va_list args) {
char buf[80];
for(; *fstr != '\0'; fstr++) {
if(*fstr == '%') {
fstr++;
switch(*fstr) {
case '%':
putc('%');
break;
case 's':
puts(va_arg(args, char*));
break;
case 'd':
itoa(va_arg(args, int), buf, 10, false);
puts(buf);
break;
case 'u':
utoa(va_arg(args, unsigned int), buf, 10, false);
puts(buf);
break;
case 'x':
utoa(va_arg(args, unsigned int), buf, 16, false);
puts(buf);
break;
case 'X':
utoa(va_arg(args, unsigned int), buf, 16, true);
puts(buf);
break;
case '@': {
uint8_t hi = hex2i(*++fstr);
uint8_t lo = hex2i(*++fstr);
term_setcol((hi << 4) | lo);
break;
}
default:
break;
}
} else {
putc(*fstr);
}
}
}
void printf(const char *fstr, ...) {
va_list args;
va_start(args, fstr);
vprintf(fstr, args);
va_end(args);
}
void term_clear(void) {
memset(screen_buf, 0, TERM_W * TERM_H * 2);
}
void term_setpos(size_t new_x, size_t new_y) {
term_x = new_x;
term_y = new_y;
}
void term_setcol(uint8_t new_col) {
color = new_col;
}
void term_scroll(ptrdiff_t lines) {
term_y -= lines;
if(lines == 0) return;
if(lines >= TERM_H || lines <= -TERM_H) {
term_clear();
} else if(lines > 0) {
memmove(screen_buf, screen_buf + lines * TERM_W, 2 * (TERM_H - lines) * TERM_W);
memset(screen_buf + (TERM_H - lines) * TERM_W, 0, 2 * lines * TERM_W);
} else {
memmove(screen_buf + lines * TERM_W, screen_buf + lines, (TERM_H + lines) * TERM_W);
}
}
void term_flush(void) {
int_disable();
memcpy_volatile(screen, screen_buf, TERM_W * TERM_H * 2);
int_enable();
}
uint32_t term_save(void) {
return color | ((term_x + term_y * TERM_W) << 8);
}
void term_load(uint32_t state) {
color = state & 0xff;
state >>= 8;
term_x = state % TERM_W;
term_y = state / TERM_W;
}