From f5c4b64aff6011716e12eec55e6f17992354fe8b Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 23 Jul 2012 00:01:12 +0200 Subject: [PATCH] The console can now be rendered to any text buffer. The console renderer now renders to a text buffer, which can be implemented on any device, whether it's the VGA text buffer or a bitmap graphics device with font rendering. This replaces the older code that could only render to a VGA framebuffer and where the input parsing was tightly coupled with the device rendering phase. --- sortix/Makefile | 4 +- sortix/include/sortix/kernel/textbuffer.h | 99 ++++ sortix/kernel.cpp | 29 +- sortix/serialterminal.cpp | 4 +- sortix/textbuffer.cpp | 83 ++++ sortix/textterminal.cpp | 417 +++++++++++++++++ sortix/textterminal.h | 69 +++ sortix/vgaterminal.cpp | 547 ---------------------- sortix/vgaterminal.h | 39 -- sortix/vgatextbuffer.cpp | 193 ++++++++ sortix/vgatextbuffer.h | 70 +++ 11 files changed, 959 insertions(+), 595 deletions(-) create mode 100644 sortix/include/sortix/kernel/textbuffer.h create mode 100644 sortix/textbuffer.cpp create mode 100644 sortix/textterminal.cpp create mode 100644 sortix/textterminal.h delete mode 100644 sortix/vgaterminal.cpp delete mode 100644 sortix/vgaterminal.h create mode 100644 sortix/vgatextbuffer.cpp create mode 100644 sortix/vgatextbuffer.h diff --git a/sortix/Makefile b/sortix/Makefile index a324485f..22dfb61e 100644 --- a/sortix/Makefile +++ b/sortix/Makefile @@ -110,8 +110,10 @@ uart.o \ terminal.o \ linebuffer.o \ logterminal.o \ -vgaterminal.o \ +textterminal.o \ serialterminal.o \ +textbuffer.o \ +vgatextbuffer.o \ descriptors.o \ device.o \ refcount.o \ diff --git a/sortix/include/sortix/kernel/textbuffer.h b/sortix/include/sortix/kernel/textbuffer.h new file mode 100644 index 00000000..884cc909 --- /dev/null +++ b/sortix/include/sortix/kernel/textbuffer.h @@ -0,0 +1,99 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix 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 3 of the License, or (at your option) any later + version. + + Sortix 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 + Sortix. If not, see . + + textbuffer.h + Provides a indexable text buffer for used by text mode terminals. + +*******************************************************************************/ + +#ifndef SORTIX_TEXTBUFFER_H +#define SORTIX_TEXTBUFFER_H + +#include +#include + +namespace Sortix { + +struct TextPos +{ + TextPos() { } + TextPos(size_t x, size_t y) : x(x), y(y) { } + size_t x; + size_t y; +}; + +struct TextChar +{ + TextChar() { } + TextChar(char c, uint8_t vgacolor) : c(c), vgacolor(vgacolor) { } + char c; + uint8_t vgacolor; // Format of +}; + +class TextBuffer +{ +public: + virtual ~TextBuffer() { } + virtual size_t Width() const = 0; + virtual size_t Height() const = 0; + virtual TextChar GetChar(TextPos pos) const = 0; + virtual void SetChar(TextPos pos, TextChar c) = 0; + virtual uint16_t GetCharAttr(TextPos pos) const = 0; + virtual void SetCharAttr(TextPos pos, uint16_t attr) = 0; + virtual void Scroll(ssize_t off, TextChar fillwith) = 0; + virtual void Move(TextPos to, TextPos from, size_t numchars) = 0; + virtual void Fill(TextPos from, TextPos to, TextChar fillwith, + uint16_t fillattr) = 0; + virtual bool GetCursorEnabled() const = 0; + virtual void SetCursorEnabled(bool enablecursor) = 0; + virtual TextPos GetCursorPos() const = 0; + virtual void SetCursorPos(TextPos cursorpos) = 0; + +}; + +// The purpose of this handle class is such that the terminal driver can have +// its backing storage replaced at runtime, for instance if the user changes +// the screen resolution or the graphics driver. The backing text buffer can +// only be changed when there are no references (but our own) to the text buffer +// so don't forget to release it when you are done. +class TextBufferHandle : public Refcounted +{ +public: + TextBufferHandle(TextBuffer* textbuf = NULL, bool deletebuf = true, + TextBuffer* def = NULL, bool deletedef = true); + ~TextBufferHandle(); + TextBuffer* Acquire(); + void Release(TextBuffer* textbuf); + void Replace(TextBuffer* newtextbuf, bool deletebuf = true); + +private: + kthread_mutex_t mutex; + kthread_cond_t unusedcond; + TextBuffer* textbuf; + TextBuffer* def; + size_t numused; + bool deletedef; + bool deletebuf; + + +}; + +} // namespace Sortix + +#endif diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 9d7b33e0..835cb669 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -24,6 +24,9 @@ *******************************************************************************/ #include +#include +#include +#include #include #include #include @@ -42,9 +45,10 @@ #include "pci.h" #include "com.h" #include "uart.h" +#include "vgatextbuffer.h" #include "terminal.h" #include "serialterminal.h" -#include "vgaterminal.h" +#include "textterminal.h" #include "elf.h" #include "initrd.h" #include "vga.h" @@ -90,6 +94,11 @@ void DoWelcome() Log::Print(" BOOTING OPERATING SYSTEM... "); } +static size_t PrintToTextTerminal(void* user, const char* str, size_t len) +{ + return ((TextTerminal*) user)->Print(str, len); +} + extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) { // Initialize system calls. @@ -98,13 +107,19 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // Detect and initialize any serial COM ports in the system. COM::EarlyInit(); - // Initialize the default terminal. - VGATerminal::Init(); - Maxsi::Format::Callback logcallback = VGATerminal::Print; - void* logpointer = NULL; + // Setup a text buffer handle for use by the text terminal. + uint16_t* const VGAFB = (uint16_t*) 0xB8000; + const size_t VGA_WIDTH = 80; + const size_t VGA_HEIGHT = 25; + static uint16_t vga_attr_buffer[VGA_WIDTH*VGA_HEIGHT]; + VGATextBuffer textbuf(VGAFB, vga_attr_buffer, VGA_WIDTH, VGA_HEIGHT); + TextBufferHandle textbufhandle(NULL, false, &textbuf, false); - // Initialize the kernel log. - Log::Init(logcallback, logpointer); + // Setup a text terminal instance. + TextTerminal textterm(&textbufhandle); + + // Register the text terminal as the kernel log and initialize it. + Log::Init(PrintToTextTerminal, &textterm); // Display the boot welcome screen. DoWelcome(); diff --git a/sortix/serialterminal.cpp b/sortix/serialterminal.cpp index d7471444..9047f986 100644 --- a/sortix/serialterminal.cpp +++ b/sortix/serialterminal.cpp @@ -29,7 +29,6 @@ #include "keyboard.h" #include "uart.h" #include "serialterminal.h" -#include "vgaterminal.h" #include "scheduler.h" using namespace Maxsi; @@ -144,7 +143,10 @@ namespace Sortix size_t Print(void* /*user*/, const char* string, size_t stringlen) { + #warning Echoing to the VGA terminal is broken +#if 0 if ( ECHO_TO_VGA ) { VGATerminal::Print(NULL, string, stringlen); } +#endif if ( cursordisabled ) { const char* msg = "\e[h"; diff --git a/sortix/textbuffer.cpp b/sortix/textbuffer.cpp new file mode 100644 index 00000000..8dfff4db --- /dev/null +++ b/sortix/textbuffer.cpp @@ -0,0 +1,83 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix 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 3 of the License, or (at your option) any later + version. + + Sortix 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 + Sortix. If not, see . + + textbuffer.cpp + Provides a indexable text buffer for used by text mode terminals. + +*******************************************************************************/ + +#include +#include +#include +#include + +namespace Sortix { + +TextBufferHandle::TextBufferHandle(TextBuffer* textbuf, bool deletebuf, + TextBuffer* def, bool deletedef) +{ + this->textbuf = textbuf; + this->deletebuf = deletebuf; + this->def = def; + this->deletedef = deletedef; + this->numused = 0; + this->mutex = KTHREAD_MUTEX_INITIALIZER; + this->unusedcond = KTHREAD_COND_INITIALIZER; +} + +TextBufferHandle::~TextBufferHandle() +{ + if ( deletebuf ) + delete textbuf; + if ( deletedef ) + delete def; +} + +TextBuffer* TextBufferHandle::Acquire() +{ + ScopedLock lock(&mutex); + numused++; + if ( textbuf ) + return textbuf; + if ( !def ) + numused--; + return def; +} + +void TextBufferHandle::Release(TextBuffer* textbuf) +{ + ASSERT(textbuf); + ScopedLock lock(&mutex); + ASSERT(numused); + if ( !--numused ) + kthread_cond_signal(&unusedcond); +} + +void TextBufferHandle::Replace(TextBuffer* newtextbuf, bool deletebuf) +{ + ScopedLock lock(&mutex); + while ( numused ) + kthread_cond_wait(&unusedcond, &mutex); + if ( deletebuf ) + delete textbuf; + this->textbuf = newtextbuf; + this->deletebuf = deletebuf; +} + +} // namespace Sortix diff --git a/sortix/textterminal.cpp b/sortix/textterminal.cpp new file mode 100644 index 00000000..00a70140 --- /dev/null +++ b/sortix/textterminal.cpp @@ -0,0 +1,417 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + + This file is part of Sortix. + + Sortix 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 3 of the License, or (at your option) any later + version. + + Sortix 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 + Sortix. If not, see . + + textterminal.cpp + Translates a character stream to a 2 dimensional array of character. + +*******************************************************************************/ + +#include +#include +#include +#include +#include "textterminal.h" + +namespace Sortix { + +const uint16_t DEFAULT_COLOR = COLOR8_LIGHT_GREY << 0U | COLOR8_BLACK << 4U; +const uint16_t ATTR_CHAR = 1U << 0U; + +TextTerminal::TextTerminal(TextBufferHandle* textbufhandle) +{ + this->textbufhandle = textbufhandle; textbufhandle->Refer(); + Reset(); +} + +void TextTerminal::Reset() +{ + vgacolor = DEFAULT_COLOR; + column = line = 0; + ansisavedposx = ansisavedposy = 0; + ansimode = NONE; + TextBuffer* textbuf = textbufhandle->Acquire(); + TextPos fillfrom(0, 0); + TextPos fillto(textbuf->Width()-1, textbuf->Height()-1); + TextChar fillwith(' ', vgacolor); + textbuf->Fill(fillfrom, fillto, fillwith, 0); + textbuf->SetCursorEnabled(true); + UpdateCursor(textbuf); + textbufhandle->Release(textbuf); +} + +size_t TextTerminal::Print(const char* string, size_t stringlen) +{ + TextBuffer* textbuf = textbufhandle->Acquire(); + for ( size_t i = 0; i < stringlen; i++ ) + PutChar(textbuf, string[i]); + UpdateCursor(textbuf); + textbufhandle->Release(textbuf); + return stringlen; +} + +void TextTerminal::PutChar(TextBuffer* textbuf, char c) +{ + if ( ansimode ) + PutAnsiEscaped(textbuf, c); + else switch ( c ) + { + case '\n': Newline(textbuf); break; + case '\r': column = 0; break; + case '\b': Backspace(textbuf); break; + case '\t': Tab(textbuf); break; + case '\e': AnsiReset(); break; + default: + { + if ( textbuf->Width() <= column ) + Newline(textbuf); + TextPos pos(column++, line); + TextChar tc(c, vgacolor); + textbuf->SetChar(pos, tc); + textbuf->SetCharAttr(pos, ATTR_CHAR); + } break; + } +} + +void TextTerminal::UpdateCursor(TextBuffer* textbuf) +{ + textbuf->SetCursorPos(TextPos(column, line)); +} + +void TextTerminal::Newline(TextBuffer* textbuf) +{ + textbuf->SetCharAttr(TextPos(column, line), ATTR_CHAR); + column = 0; + if ( line < textbuf->Height()-1 ) + line++; + else + textbuf->Scroll(1, TextChar(' ', vgacolor)), + line = textbuf->Height()-1; +} + +static TextPos DecrementTextPos(TextBuffer* textbuf, TextPos pos) +{ + if ( !pos.x && !pos.y ) + return pos; + if ( !pos.x ) + return TextPos(textbuf->Width(), pos.y-1); + return TextPos(pos.x-1, pos.y); +} + +void TextTerminal::Backspace(TextBuffer* textbuf) +{ + TextPos pos(column, line); + while ( pos.x || pos.y ) + { + pos = DecrementTextPos(textbuf, pos); + uint16_t attr = textbuf->GetCharAttr(pos); + textbuf->SetChar(pos, TextChar(' ', vgacolor)); + textbuf->SetCharAttr(pos, attr & ~ATTR_CHAR); + if ( attr & ATTR_CHAR ) + break; + } + column = pos.x; + line = pos.y; +} + +void TextTerminal::Tab(TextBuffer* textbuf) +{ + if ( column == textbuf->Width() ) + Newline(textbuf); + // TODO: This does not work correctly if the text buffer width is not a + // multiple of four and the column is near the edge. + unsigned until = 4 - (column % 4); + textbuf->SetCharAttr(TextPos(column, line), ATTR_CHAR); + while ( (until--) != 0 ) + textbuf->SetChar(TextPos(column++, line), TextChar(' ', vgacolor)); +} + +// TODO: This implementation of the 'Ansi Escape Codes' is incomplete and hacky. +void TextTerminal::AnsiReset() +{ + ansiusedparams = 0; + currentparamindex = 0; + ansiparams[0] = 0; + paramundefined = true; + ignoresequence = false; + ansimode = CSI; +} + +void TextTerminal::PutAnsiEscaped(TextBuffer* textbuf, char c) +{ + // Check the proper prefixes are used. + if ( ansimode == CSI ) + { + if ( c != '[' ) { ansimode = NONE; return; } + ansimode = COMMAND; + return; + } + + // Read part of a parameter. + if ( '0' <= c && c <= '9' ) + { + if ( paramundefined ) + ansiusedparams++; + paramundefined = false; + unsigned val = c - '0'; + ansiparams[currentparamindex] *= 10; + ansiparams[currentparamindex] += val; + } + + // Parameter delimiter. + else if ( c == ';' ) + { + if ( currentparamindex == ANSI_NUM_PARAMS - 1 ) + { + ansimode = NONE; + return; + } + paramundefined = true; + ansiparams[++currentparamindex] = 0; + } + + // Left for future standardization, so discard this sequence. + else if ( c == ':' ) + { + ignoresequence = true; + } + + // Run a command. + else if ( 64 <= c && c <= 126 ) + { + if ( !ignoresequence ) + RunAnsiCommand(textbuf, c); + } + + // Something I don't understand, and ignore intentionally. + else if ( c == '?' ) + { + } + + // TODO: There are some rare things that should be supported here. + + // Ignore unknown input. + else + { + ansimode = NONE; + } +} + +void TextTerminal::RunAnsiCommand(TextBuffer* textbuf, char c) +{ + const unsigned width = (unsigned) textbuf->Width(); + const unsigned height = (unsigned) textbuf->Height(); + + switch ( c ) + { + case 'A': // Cursor up + { + unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1; + if ( line < dist ) + line = 0; + else + line -= dist; + } break; + case 'B': // Cursor down + { + unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1; + if ( height <= line + dist ) + line = height-1; + else + line += dist; + } break; + case 'C': // Cursor forward + { + unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1; + if ( width <= column + dist ) + column = width-1; + else + column += dist; + } break; + case 'D': // Cursor backward + { + unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1; + if ( column < dist ) + column = 0; + else + column -= dist; + } break; + case 'E': // Move to beginning of line N lines down. + { + column = 0; + unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1; + if ( height <= line + dist ) + line = height-1; + else + line += dist; + } break; + case 'F': // Move to beginning of line N lines up. + { + column = 0; + unsigned dist = 0 < ansiusedparams ? ansiparams[0] : 1; + if ( line < dist ) + line = 0; + else + line -= dist; + } break; + case 'G': // Move the cursor to column N. + { + unsigned pos = 0 < ansiusedparams ? ansiparams[0]-1 : 0; + if ( width <= pos ) + pos = width-1; + column = pos; + } break; + case 'H': // Move the cursor to line Y, column X. + case 'f': + { + unsigned posy = 0 < ansiusedparams ? ansiparams[0]-1 : 0; + unsigned posx = 1 < ansiusedparams ? ansiparams[1]-1 : 0; + if ( width <= posx ) + posx = width-1; + if ( height <= posy ) + posy = height-1; + column = posx; + line = posy; + } break; + case 'J': // Erase parts of the screen. + { + unsigned mode = 0 < ansiusedparams ? ansiparams[0] : 0; + TextPos from(0, 0); + TextPos to(0, 0); + + if ( mode == 0 ) // From cursor to end. + from = TextPos{column, line}, + to = TextPos{width-1, height-1}; + + if ( mode == 1 ) // From start to cursor. + from = TextPos{0, 0}, + to = TextPos{column, line}; + + if ( mode == 2 ) // Everything. + from = TextPos{0, 0}, + to = TextPos{width-1, height-1}; + + textbuf->Fill(from, to, TextChar(' ', vgacolor), 0); + } break; + case 'K': // Erase parts of the current line. + { + unsigned mode = 0 < ansiusedparams ? ansiparams[0] : 0; + TextPos from(0, 0); + TextPos to(0, 0); + + if ( mode == 0 ) // From cursor to end. + from = TextPos{column, line}, + to = TextPos{width-1, line}; + + if ( mode == 1 ) // From start to cursor. + from = TextPos{0, line}, + to = TextPos{column, line}; + + if ( mode == 2 ) // Everything. + from = TextPos{0, line}, + to = TextPos{width-1, line}; + + textbuf->Fill(from, to, TextChar(' ', vgacolor), 0); + } break; + case 'S': // Scroll a line up and place a new line at the buttom. + { + textbuf->Scroll(1, TextChar(' ', vgacolor)); + line = height-1; + } break; + case 'T': // Scroll a line up and place a new line at the top. + { + textbuf->Scroll(-1, TextChar(' ', vgacolor)); + line = 0; + } break; + case 'm': // Change how the text is rendered. + { + if ( ansiusedparams == 0 ) + { + ansiparams[0] = 0; + ansiusedparams++; + } + + // Convert from the ANSI color scheme to the VGA color scheme. + const unsigned conversion[8] = + { + COLOR8_BLACK, COLOR8_RED, COLOR8_GREEN, COLOR8_BROWN, + COLOR8_BLUE, COLOR8_MAGENTA, COLOR8_CYAN, COLOR8_LIGHT_GREY, + }; + + for ( size_t i = 0; i < ansiusedparams; i++ ) + { + unsigned cmd = ansiparams[i]; + // Turn all attributes off. + if ( cmd == 0 ) + { + vgacolor = DEFAULT_COLOR; + } + // Set text color. + else if ( 30 <= cmd && cmd <= 37 ) + { + unsigned val = cmd - 30; + vgacolor &= 0xF0; + vgacolor |= conversion[val] << 0; + } + // Set background color. + else if ( 40 <= cmd && cmd <= 47 ) + { + unsigned val = cmd - 40; + vgacolor &= 0x0F; + vgacolor |= conversion[val] << 4; + } + // TODO: There are many other things we don't support. + } + } break; + case 'n': // Request special information from terminal. + { + // TODO: Handle this code. + } break; + case 's': // Save cursor position. + { + ansisavedposx = column; + ansisavedposx = line; + } break; + case 'u': // Restore cursor position. + { + column = ansisavedposx; + line = ansisavedposx; + if ( width <= column ) + column = width-1; + if ( height <= line ) + line = height-1; + } break; + case 'l': // Hide cursor. + { + // TODO: This is somehow related to the special char '?'. + if ( 0 < ansiusedparams && ansiparams[0] == 25 ) + textbuf->SetCursorEnabled(false); + } break; + case 'h': // Show cursor. + { + // TODO: This is somehow related to the special char '?'. + if ( 0 < ansiusedparams && ansiparams[0] == 25 ) + textbuf->SetCursorEnabled(true); + } break; + // TODO: Handle other cases. + } + + ansimode = NONE; +} + +} // namespace Sortix diff --git a/sortix/textterminal.h b/sortix/textterminal.h new file mode 100644 index 00000000..ad23125e --- /dev/null +++ b/sortix/textterminal.h @@ -0,0 +1,69 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. + + This file is part of Sortix. + + Sortix 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 3 of the License, or (at your option) any later + version. + + Sortix 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 + Sortix. If not, see . + + textterminal.cpp + An indexable text buffer with the VGA text mode framebuffer as backend. + +*******************************************************************************/ + +#ifndef SORTIX_TEXTTERMINAL_H +#define SORTIX_TEXTTERMINAL_H + +namespace Sortix { + +class TextBufferHandle; + +class TextTerminal //: public Printable ? +{ +public: + TextTerminal(TextBufferHandle* textbufhandle); + ~TextTerminal(); + size_t Print(const char* string, size_t stringlen); + +private: + void PutChar(TextBuffer* textbuf, char c); + void UpdateCursor(TextBuffer* textbuf); + void Newline(TextBuffer* textbuf); + void Backspace(TextBuffer* textbuf); + void Tab(TextBuffer* textbuf); + void PutAnsiEscaped(TextBuffer* textbuf, char c); + void RunAnsiCommand(TextBuffer* textbuf, char c); + void AnsiReset(); + void Reset(); + +private: + TextBufferHandle* textbufhandle; + uint8_t vgacolor; + unsigned column; + unsigned line; + unsigned ansisavedposx; + unsigned ansisavedposy; + enum { NONE = 0, CSI, COMMAND, } ansimode; + static const size_t ANSI_NUM_PARAMS = 16; + unsigned ansiusedparams; + unsigned ansiparams[ANSI_NUM_PARAMS]; + unsigned currentparamindex; + bool paramundefined; + bool ignoresequence; + +}; + +} // namespace Sortix + +#endif diff --git a/sortix/vgaterminal.cpp b/sortix/vgaterminal.cpp deleted file mode 100644 index 4aa04cfa..00000000 --- a/sortix/vgaterminal.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - This file is part of Sortix. - - Sortix 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 3 of the License, or (at your option) any later - version. - - Sortix 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 - Sortix. If not, see . - - vgaterminal.cpp - A terminal based on a text mode buffer. - -*******************************************************************************/ - -#include -#include -#include -#include -#include "vga.h" -#include "vgaterminal.h" - -using namespace Maxsi; - -namespace Sortix { -namespace VGATerminal { - -const unsigned width = 80; -const unsigned height = 25; -const uint16_t DEFAULT_COLOR = (COLOR8_LIGHT_GREY << 8) | (COLOR8_BLACK << 12); -uint16_t* const vga = (uint16_t* const) 0xB8000; -uint16_t vgaattr[width * height]; -const uint16_t VGAATTR_CHAR = (1U<<0U); -unsigned line; -unsigned column; -uint16_t currentcolor; -unsigned ansisavedposx; -unsigned ansisavedposy; -bool showcursor; - -enum -{ - NONE, - CSI, - COMMAND, -} ansimode; - -void UpdateCursor() -{ - if ( showcursor ) - VGA::SetCursor(column, line); - else - VGA::SetCursor(width, height-1); -} - -// Clear the screen, put the cursor at the top left corner, set default -// text color, and reset ANSI escape sequence state. -void Reset() -{ - ansimode = NONE; - - line = 0; - column = 0; - ansisavedposx = 0; - ansisavedposy = 0; - - currentcolor = DEFAULT_COLOR; - - for ( nat y = 0; y < height; y++ ) - { - for ( nat x = 0; x < width; x++ ) - { - unsigned index = y * width + x; - vga[index] = ' ' | DEFAULT_COLOR; - vgaattr[index] = 0; - } - } - - // Reset the VGA cursor. - showcursor = true; - UpdateCursor(); -} - -// Initialize the terminal driver. -void Init() -{ - Reset(); -} - -// Move every line one row up and leaves an empty line at the bottom. -void ScrollUp() -{ - for ( nat y = 1; y < height; y++ ) - { - size_t linesize = width * sizeof(uint16_t); - size_t fromoff = (y-0) * width; - size_t destoff = (y-1) * width; - Memory::Copy(vga + destoff, vga + fromoff, linesize); - Memory::Copy(vgaattr + destoff, vgaattr + fromoff, linesize); - } - - for ( nat x = 0; x < width; x++ ) - { - unsigned index = (height-1) * width + x; - vga[index] = ' ' | currentcolor; - vgaattr[index] = 0; - } -} - -// Move every line one row down and leaves an empty line at the top. -void ScrollDown() -{ - for ( nat y = 1; y < height; y++ ) - { - size_t linesize = width * sizeof(uint16_t); - size_t fromoff = (y-1) * width; - size_t destoff = (y-0) * width; - Memory::Copy(vga + destoff, vga + fromoff, linesize); - Memory::Copy(vgaattr + destoff, vgaattr + fromoff, linesize); - } - - for ( nat x = 0; x < width; x++ ) - { - unsigned index = x; - vga[index] = ' ' | currentcolor; - vgaattr[index] = 0; - } -} - -// Move to the next line. If at bottom, scroll one line up. -void Newline() -{ - vgaattr[line * width + column] |= VGAATTR_CHAR; - if ( line < height - 1 ) - line++; - else - ScrollUp(); - column = 0; - UpdateCursor(); -} - -void ANSIReset(); -void ParseANSIEscape(char c); - -// Print text to the vga framebuffer, simulating how a terminal would -// display the text. -size_t Print(void* /*user*/, const char* string, size_t stringlen) -{ - // Iterate over each character. - size_t left = stringlen; - while ( (left--) > 0 ) - { - char c = *(string++); - - // If we are parsing an escape sequence, parse another char. - if ( ansimode != NONE ) { ParseANSIEscape(c); continue; } - - switch ( c ) - { - // Move cursor to the next line and scroll up (if needed). - case '\n': - { - Newline(); - break; - } - // Move cursor to start of line. - case '\r': - { - column = 0; - break; - } - // Delete the previous character. - case '\b': - { - unsigned pos = line * width + column; - while ( pos ) - { - unsigned nextpos = pos-1; - vga[nextpos] = ' ' | currentcolor; - pos = nextpos; - uint16_t attr = vgaattr[pos]; - vgaattr[pos] = 0; - if ( attr & VGAATTR_CHAR ) { break; } - } - column = pos % width; - line = pos / width; - break; - } - // Expand a tab to a few spaces. - case '\t': - { - if ( column == width ) { Newline(); } - nat until = 4 - (column % 4); - - vgaattr[line * width + (column)] |= VGAATTR_CHAR; - while ( (until--) != 0 ) - { - vga[line * width + (column++)] = ' ' | currentcolor; - } - break; - } - // Initialize an ANSI escape sequence, allowing much more - // powerful control of the terminal than ASCII does. - case '\e': - { - ANSIReset(); - break; - } - // Print this character at the cursor and increment the cursor. - default: - { - if ( column == width ) { Newline(); } - unsigned index = line * width + (column++); - vga[index] = c | currentcolor; - vgaattr[index] |= VGAATTR_CHAR; - break; - } - } - } - - // Update the VGA hardware cursor. - UpdateCursor(); - - return stringlen; -} - -const size_t ANSI_NUM_PARAMS = 16; -nat ansiusedparams; -nat ansiparams[ANSI_NUM_PARAMS]; -nat currentparamindex; -bool paramundefined; -bool ignoresequence; - -// TODO: The ANSI escape code is only partially implemented! - -// Begin an ANSI esacpe sequence. -void ANSIReset() -{ - if ( ansimode == NONE ) - { - ansiusedparams = 0; - currentparamindex = 0; - ansiparams[0] = 0; - paramundefined = true; - ignoresequence = false; - ansimode = CSI; - } -} - -// Reads parameters and changes font properties accordingly. -void AnsiSetFont() -{ - // Convert from the ANSI color scheme to the VGA color scheme. - const unsigned conversion[8] = - { - COLOR8_BLACK, COLOR8_RED, COLOR8_GREEN, COLOR8_BROWN, - COLOR8_BLUE, COLOR8_MAGENTA, COLOR8_CYAN, COLOR8_LIGHT_GREY, - }; - - for ( size_t i = 0; i < ansiusedparams; i++ ) - { - nat cmd = ansiparams[i]; - // Turn all attributes off. - if ( cmd == 0 ) - { - currentcolor = DEFAULT_COLOR; - } - // Set text color. - else if ( 30 <= cmd && cmd <= 37 ) - { - nat val = cmd - 30; - currentcolor &= (0xF0FF); - currentcolor |= (conversion[val] << 8); - } - // Set background color. - else if ( 40 <= cmd && cmd <= 47 ) - { - nat val = cmd - 40; - currentcolor &= (0x0FFF); - currentcolor |= (conversion[val] << 12); - } - // TODO: There are many other things we don't support. - } -} - -// Executes an ASNI escape command based on given parameters. -void RunANSICommand(char c) -{ - switch ( c ) - { - // Cursor up - case 'A': - { - nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1; - if ( line < dist ) { line = 0; } else { line -= dist; } - break; - } - // Cursor down - case 'B': - { - nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1; - if ( height <= line + dist ) { line = height-1; } else { line += dist; } - break; - } - // Cursor forward - case 'C': - { - nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1; - if ( width <= column + dist ) { column = width-1; } else { column += dist; } - break; - } - // Cursor backward - case 'D': - { - nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1; - if ( column < dist ) { column = 0; } else { column -= dist; } - break; - } - // Move to beginning of line N lines down. - case 'E': - { - column = 0; - nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1; - if ( height <= line + dist ) { line = height-1; } else { line += dist; } - break; - } - // Move to beginning of line N lines up. - case 'F': - { - column = 0; - nat dist = ( 0 < ansiusedparams ) ? ansiparams[0] : 1; - if ( line < dist ) { line = 0; } else { line -= dist; } - break; - } - // Move the cursor to column N. - case 'G': - { - nat pos = ( 0 < ansiusedparams ) ? ansiparams[0]-1 : 0; - if ( width <= pos ) { pos = width-1; } - column = pos; - break; - } - // Move the cursor to line Y, column X. - case 'H': - case 'f': - { - nat posy = ( 0 < ansiusedparams ) ? ansiparams[0]-1 : 0; - nat posx = ( 1 < ansiusedparams ) ? ansiparams[1]-1 : 0; - if ( width <= posx ) { posx = width-1; } - if ( height <= posy ) { posy = height-1; } - column = posx; - line = posy; - break; - } - // Erase parts of the screen. - case 'J': - { - nat mode = ( 0 < ansiusedparams ) ? ansiparams[0] : 0; - - nat from = 0; - nat to = 0; - - // From cursor to end. - if ( mode == 0 ) { from = line*width + column; to = height*width - 1; } - - // From start to cursor. - if ( mode == 1 ) { from = 0; to = line*width + column; } - - // Everything. - if ( mode == 2 ) { from = 0; to = height*width - 1; } - - for ( nat i = from; i <= to; i++ ) - { - vga[i] = ' ' | currentcolor; - vgaattr[i] = 0; - } - - break; - } - // Erase parts of the current line. - case 'K': - { - nat mode = ( 0 < ansiusedparams ) ? ansiparams[0] : 0; - - nat from = 0; - nat to = 0; - - // From cursor to end. - if ( mode == 0 ) { from = column; to = width - 1; } - - // From start to cursor. - if ( mode == 1 ) { from = 0; to = column; } - - // Everything. - if ( mode == 2 ) { from = 0; to = width - 1; } - - for ( nat i = from; i <= to; i++ ) - { - unsigned index = line * width + i; - vga[index] = ' ' | currentcolor; - vgaattr[index] = 0; - } - - break; - } - // Scroll a line up and place a new line at the buttom. - case 'S': - { - ScrollUp(); line = height-1; - break; - } - // Scroll a line up and place a new line at the top. - case 'T': - { - ScrollDown(); line = 0; - break; - } - // Change how the text is rendered. - case 'm': - { - if ( ansiusedparams == 0 ) - { - ansiparams[0] = 0; - ansiusedparams++; - } - AnsiSetFont(); - break; - } - // Request special information from terminal. - case 'n': - { - // TODO: Handle this code. - break; - } - // Save cursor position. - case 's': - { - ansisavedposx = column; - ansisavedposx = line; - break; - } - // Restore cursor position. - case 'u': - { - column = ansisavedposx; - line = ansisavedposx; - if ( width <= column ) { column = width-1; } - if ( height <= line ) { line = height-1; } - break; - } - // Hide cursor. - case 'l': - { - // TODO: This is somehow related to the special char '?'. - if ( 0 < ansiusedparams && ansiparams[0] == 25 ) - { - showcursor = false; - UpdateCursor(); - } - break; - } - // Show cursor. - case 'h': - { - // TODO: This is somehow related to the special char '?'. - if ( 0 < ansiusedparams && ansiparams[0] == 25 ) - { - showcursor = true; - UpdateCursor(); - } - break; - } - // TODO: Handle other cases. - } - - ansimode = NONE; -} - -// Parse another char of an ASNI escape code. -void ParseANSIEscape(char c) -{ - // Check the proper prefixes are used. - if ( ansimode == CSI ) - { - if ( c != '[' ) { ansimode = NONE; return; } - ansimode = COMMAND; - } - - // Parse parameters and the command. - else if ( ansimode == COMMAND ) - { - // Read part of a parameter. - if ( '0' <= c && c <= '9' ) - { - if ( paramundefined ) { ansiusedparams++; } - paramundefined = false; - nat val = c - '0'; - ansiparams[currentparamindex] *= 10; - ansiparams[currentparamindex] += val; - } - - // Parameter delimiter. - else if ( c == ';' ) - { - if ( currentparamindex == ANSI_NUM_PARAMS - 1 ) { ansimode = NONE; return; } - paramundefined = true; - ansiparams[++currentparamindex] = 0; - } - - // Left for future standardization, so discard this sequence. - else if ( c == ':' ) - { - ignoresequence = true; - } - - // Run a command. - else if ( 64 <= c && c <= 126 ) - { - if ( !ignoresequence ) { RunANSICommand(c); } - } - - // Something I don't understand, and ignore intentionally. - else if ( c == '?' ) - { - } - - // TODO: There are some rare things that should be supported here. - - // Ignore unknown input. - else - { - ansimode = NONE; - } - } -} - -} // namespace VGATerminal -} // namespace Sortix diff --git a/sortix/vgaterminal.h b/sortix/vgaterminal.h deleted file mode 100644 index e011c4b8..00000000 --- a/sortix/vgaterminal.h +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. - - This file is part of Sortix. - - Sortix 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 3 of the License, or (at your option) any later - version. - - Sortix 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 - Sortix. If not, see . - - vgaterminal.cpp - A terminal based on a text mode buffer. - -*******************************************************************************/ - -#ifndef SORTIX_VGATERMINAL_H -#define SORTIX_VGATERMINAL_H - -namespace Sortix { -namespace VGATerminal { - -void Init(); -void Reset(); -size_t Print(void* user, const char* string, size_t stringlen); - -} // namespace VGATerminal -} // namespace Sortix - -#endif - diff --git a/sortix/vgatextbuffer.cpp b/sortix/vgatextbuffer.cpp new file mode 100644 index 00000000..c949e35c --- /dev/null +++ b/sortix/vgatextbuffer.cpp @@ -0,0 +1,193 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix 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 3 of the License, or (at your option) any later + version. + + Sortix 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 + Sortix. If not, see . + + vgatextbuffer.cpp + An indexable text buffer with the VGA text mode framebuffer as backend. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include "vga.h" +#include "vgatextbuffer.h" + +namespace Sortix { + +VGATextBuffer::VGATextBuffer(uint16_t* vga, uint16_t* attr, + size_t width, size_t height) +{ + this->vga = vga; + this->attr = attr; + this->width = width; + this->height = height; + cursorpos = {0, 0}; + cursorenabled = true; + UpdateCursor(); +} + +VGATextBuffer::~VGATextBuffer() +{ +} + +static TextChar EntryToTextChar(uint16_t entry) +{ + char c = entry & 0x00FF; + uint8_t vgacolor = entry >> 8U; + return TextChar{c, vgacolor}; +} + +static uint16_t CharToTextEntry(TextChar c) +{ + return (uint16_t) c.c | (uint16_t) c.vgacolor << 8U; +} + + +bool VGATextBuffer::UsablePosition(TextPos pos) const +{ + return pos.x < width && pos.y < height; +} + +TextPos VGATextBuffer::CropPosition(TextPos pos) const +{ + if ( width <= pos.x ) + pos.x = width - 1; + if ( height <= pos.y ) + pos.y = height -1; + return pos; +} + +size_t VGATextBuffer::OffsetOfPos(TextPos pos) const +{ + return pos.y * width + pos.x; +} + +size_t VGATextBuffer::Width() const +{ + return width; +} + +size_t VGATextBuffer::Height() const +{ + return height; +} + +TextChar VGATextBuffer::GetChar(TextPos pos) const +{ + if ( UsablePosition(pos) ) + return EntryToTextChar(vga[OffsetOfPos(pos)]); + return {0, 0}; +} + +void VGATextBuffer::SetChar(TextPos pos, TextChar c) +{ + if ( UsablePosition(pos) ) + vga[OffsetOfPos(pos)] = CharToTextEntry(c); +} + +uint16_t VGATextBuffer::GetCharAttr(TextPos pos) const +{ + if ( UsablePosition(pos) ) + return attr[OffsetOfPos(pos)]; + return 0; +} + +void VGATextBuffer::SetCharAttr(TextPos pos, uint16_t attrval) +{ + if ( UsablePosition(pos) ) + attr[OffsetOfPos(pos)] = attrval; +} + +void VGATextBuffer::Scroll(ssize_t off, TextChar fillwith) +{ + if ( !off ) + return; + bool neg = 0 < off; + size_t absoff = off < 0 ? -off : off; + if ( height < absoff ) + absoff = height; + TextPos scrollfrom = neg ? TextPos{0, absoff} : TextPos{0, 0}; + TextPos scrollto = neg ? TextPos{0, 0} : TextPos{0, absoff}; + TextPos fillfrom = neg ? TextPos{0, height-absoff} : TextPos{0, 0}; + TextPos fillto = neg ? TextPos{width-1, height-1} : TextPos{width-1, absoff-1}; + size_t scrollchars = width * (height-absoff); + Move(scrollto, scrollfrom, scrollchars); + Fill(fillfrom, fillto, fillwith, 0); +} + +void VGATextBuffer::Move(TextPos to, TextPos from, size_t numchars) +{ + size_t dest = OffsetOfPos(CropPosition(to)); + size_t src = OffsetOfPos(CropPosition(from)); + if ( dest < src ) + for ( size_t i = 0; i < numchars; i++ ) + vga[dest + i] = vga[src + i], + attr[dest + i] = attr[src + i]; + else if ( src < dest ) + for ( size_t i = 0; i < numchars; i++ ) + vga[dest + numchars-1 - i] = vga[src + numchars-1 - i], + attr[dest + numchars-1 - i] = attr[src + numchars-1 - i]; +} + +void VGATextBuffer::Fill(TextPos from, TextPos to, TextChar fillwith, + uint16_t fillattr) +{ + from = CropPosition(from); + to = CropPosition(to); + size_t start = OffsetOfPos(from); + size_t end = OffsetOfPos(to); + size_t entry = CharToTextEntry(fillwith); + for ( size_t i = start; i <= end; i++ ) + vga[i] = entry, + attr[i] = fillattr; +} + +bool VGATextBuffer::GetCursorEnabled() const +{ + return cursorenabled; +} + +void VGATextBuffer::SetCursorEnabled(bool enablecursor) +{ + cursorenabled = enablecursor; + UpdateCursor(); +} + +TextPos VGATextBuffer::GetCursorPos() const +{ + return cursorpos; +} + +void VGATextBuffer::SetCursorPos(TextPos cursorpos) +{ + this->cursorpos = cursorpos; + UpdateCursor(); +} + +void VGATextBuffer::UpdateCursor() +{ + if ( cursorenabled ) + VGA::SetCursor(cursorpos.x, cursorpos.y); + else + VGA::SetCursor(width, height-1); +} + +} // namespace Sortix diff --git a/sortix/vgatextbuffer.h b/sortix/vgatextbuffer.h new file mode 100644 index 00000000..99d31ffc --- /dev/null +++ b/sortix/vgatextbuffer.h @@ -0,0 +1,70 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix 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 3 of the License, or (at your option) any later + version. + + Sortix 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 + Sortix. If not, see . + + vgatextbuffer.h + An indexable text buffer with the VGA text mode framebuffer as backend. + +*******************************************************************************/ + +#ifndef SORTIX_VGATEXTBUFFER_H +#define SORTIX_VGATEXTBUFFER_H + +#include + +namespace Sortix { + +class VGATextBuffer : public TextBuffer +{ +public: + VGATextBuffer(uint16_t* vga, uint16_t* attr, size_t width, size_t height); + virtual ~VGATextBuffer(); + virtual size_t Width() const; + virtual size_t Height() const; + virtual TextChar GetChar(TextPos pos) const; + virtual void SetChar(TextPos pos, TextChar c); + virtual uint16_t GetCharAttr(TextPos pos) const ; + virtual void SetCharAttr(TextPos pos, uint16_t attrval); + virtual void Scroll(ssize_t off, TextChar fillwith); + virtual void Move(TextPos to, TextPos from, size_t numchars); + virtual void Fill(TextPos from, TextPos to, TextChar fillwith, + uint16_t fillattr); + virtual bool GetCursorEnabled() const; + virtual void SetCursorEnabled(bool enablecursor); + virtual TextPos GetCursorPos() const; + virtual void SetCursorPos(TextPos cursorpos); + +private: + bool UsablePosition(TextPos pos) const; + TextPos CropPosition(TextPos pos) const; + size_t OffsetOfPos(TextPos pos) const; + void UpdateCursor(); + +private: + uint16_t* vga; + uint16_t* attr; + size_t width; + size_t height; + TextPos cursorpos; + bool cursorenabled; + +}; + +} // namespace Sortix + +#endif