//----------------------------------------------------------------------------// // 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; }