diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d5f21cc --- /dev/null +++ b/.clang-format @@ -0,0 +1,57 @@ +BasedOnStyle: LLVM +MaxEmptyLinesToKeep: 1 +ColumnLimit: 120 +ExperimentalAutoDetectBinPacking: true + +# Pack +PackConstructorInitializers: Never +BinPackParameters: false +BinPackArguments: false + +# Indentation +UseTab: Always +IndentWidth: 4 +ContinuationIndentWidth: 4 +TabWidth: 4 +AccessModifierOffset: -4 +IndentCaseBlocks: false +IndentCaseLabels: false + +# Namespace +CompactNamespaces: true +NamespaceIndentation: None +FixNamespaceComments: true + +# Allow +AllowShortLambdasOnASingleLine: Inline +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false + +# Align +AlignConsecutiveShortCaseStatements: + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCaseColons: false +AlignAfterOpenBracket: AlwaysBreak +AlignEscapedNewlines: Left + +# Break +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +# BreakConstructorInitializers: AfterColon +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true + +# Insert +InsertBraces: false +InsertNewlineAtEOF: true + +# Remove +RemoveBracesLLVM: true +KeepEmptyLines: + AtStartOfFile: false + AtStartOfBlock: false + AtEndOfFile: false diff --git a/.gitignore b/.gitignore index d988f2f..4a76e53 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ docs warn.log *.bz2 *~ +*.json \ No newline at end of file diff --git a/include/nds/arm9/console.h b/include/nds/arm9/console.h index 61849e4..51a47e8 100644 --- a/include/nds/arm9/console.h +++ b/include/nds/arm9/console.h @@ -168,6 +168,14 @@ typedef struct PrintConsole bool consoleInitialised; /*!< True if the console is initialized */ bool loadGraphics; /*!< True if consoleInit should attempt to load font graphics into background memory */ + + int bg2Id; // ID of bg used for ANSI background colors! + u16 *fontBg2Map, *fontBg2Gfx; // map & gfx for the 2nd bg + u16 fontCurPal2; // palette for background colors + + // Internal buffer for potential ANSI escape sequences, as all the characters may not be available in one con_write() call. + char escBuf[32]; + int escBufLen; }PrintConsole; @@ -198,7 +206,7 @@ void consoleSetWindow(PrintConsole* console, int x, int y, int width, int height this should only be used when using a single console or without changing the console that is returned, other wise use consoleInit() \return A pointer to the console with the default values */ -PrintConsole* consoleGetDefault(void); +const PrintConsole* consoleGetDefault(void); /*! \brief Make the specified console the render target \param console A pointer to the console struct (must have been initialized with consoleInit(PrintConsole* console) @@ -215,9 +223,11 @@ PrintConsole *consoleSelect(PrintConsole* console); \param tileBase the tile graphics base \param mainDisplay if true main engine is used, otherwise false \param loadGraphics if true the default font graphics will be loaded into the layer + \param ansiBgColors if true, uses `layer + 1` and `tileBase + 1` for ANSI background color escape sequences. + make sure the next initialized console does not use `layer + 1` and `tileBase + 1` ! \return A pointer to the current console. */ -PrintConsole* consoleInit(PrintConsole* console, int layer, BgType type, BgSize size, int mapBase, int tileBase, bool mainDisplay, bool loadGraphics); +PrintConsole* consoleInit(PrintConsole* console, int layer, BgType type, BgSize size, int mapBase, int tileBase, bool mainDisplay, bool loadGraphics, bool ansiBgColors); /*! \brief Initialize the console to a default state for prototyping. This function sets the console to use sub display, VRAM_C, and BG0 and enables MODE_0_2D on the diff --git a/source/arm9/console-cursor.c b/source/arm9/console-cursor.c new file mode 100644 index 0000000..e36573f --- /dev/null +++ b/source/arm9/console-cursor.c @@ -0,0 +1,96 @@ +/*--------------------------------------------------------------------------------- + +Copyright (C) 2025 +trustytrojan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source +distribution. + +---------------------------------------------------------------------------------*/ +#include "console-priv.h" + +// These save the palette and character of whatever tile the cursor moves on top of. +// fb2mv should always contain the full block ASCII character (219). +static u16 fbmvUnderCursor, fb2mvUnderCursor; + +void consoleSaveTileUnderCursor(void) { + fbmvUnderCursor = *consoleFontBgMapAtCursor(); + fb2mvUnderCursor = *consoleFontBg2MapAtCursor(); +} + +void consoleRestoreTileUnderCursor(void) { + *consoleFontBgMapAtCursor() = fbmvUnderCursor; + *consoleFontBg2MapAtCursor() = fb2mvUnderCursor; +} + +void consoleDrawCursor(void) { + // foreground: this is the character itself. we need to turn it black, + // which would usually be a fontCurPal of (0 << 12). however fbmvUnderCursor + // is not being computed on the fly (like below), so 99% of the time it + // does not have those bits zeroed inside. (who uses black on black?) + // so we need to zero-out those bits to make it black: + *consoleFontBgMapAtCursor() = 0x0fff & fbmvUnderCursor; + + // background: just use bright write (15) + *consoleFontBg2MapAtCursor() = (15 << 12) | consoleComputeFontBg2MapValue(219); +} + +void consoleSetCursorPos(const int x, const int y) { + if (currentConsole->bg2Id != -1) + consoleRestoreTileUnderCursor(); + + currentConsole->cursorX = x; + currentConsole->cursorY = y; + + if (currentConsole->bg2Id != -1) { + consoleSaveTileUnderCursor(); + + // original tile saved! now let's make the cursor visible with + // white background and black foreground. + consoleDrawCursor(); + } +} + +void consoleSetCursorY(const int y) { + consoleSetCursorPos(currentConsole->cursorX, y); +} + +void consoleSetCursorX(const int x) { + consoleSetCursorPos(x, currentConsole->cursorY); +} + +int clamp(const int n, const int min, const int max) { + if (n < min) + return min; + if (n > max) + return max; + return n; +} + +void consoleMoveCursorX(const int dx) { + const PrintConsole *const c = currentConsole; + const int newX = c->cursorX + dx; + const int maxX = c->windowWidth - 1; + consoleSetCursorX(clamp(newX, 0, maxX)); +} + +void consoleMoveCursorY(const int dy) { + const PrintConsole *const c = currentConsole; + const int newY = c->cursorY + dy; + const int maxY = c->windowHeight - 1; + consoleSetCursorY(clamp(newY, 0, maxY)); +} diff --git a/source/arm9/console-esc.c b/source/arm9/console-esc.c new file mode 100644 index 0000000..f9389c3 --- /dev/null +++ b/source/arm9/console-esc.c @@ -0,0 +1,253 @@ +/*--------------------------------------------------------------------------------- + +Copyright (C) 2025 +trustytrojan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source +distribution. + +---------------------------------------------------------------------------------*/ +#include "console-priv.h" +#include +#include +// #include + +static void updateColorBright(const int param, int *const color, int *const bgcolor, int *const bright) { + if (param == 0) { // Reset + *color = 15; // fg: bright white + *bgcolor = 0; // bg: black + *bright = 0; + } else if (param == 1) { // Bright/bold + *bright = 1; + } else if (param >= 30 && param <= 37) { // fg color + *color = param - 30; + } else if (param >= 40 && param <= 47) { // bg color + *bgcolor = param - 40; + } else if (param >= 90 && param <= 97) { // bright fg color + *color = param - 90 + 8; + } else if (param >= 100 && param <= 107) { // bright bg color + *bgcolor = param - 100 + 8; + } else if (param == 39 || param == 49) { // Default color + *color = 15; // fg: bright white + *bgcolor = 0; // bg: black + } +} + +static void consoleParseColor(const char *escapeseq) { + if (!currentConsole) + return; + + // Special case: \x1b[m resets attributes + if (*escapeseq == 'm') { + currentConsole->fontCurPal = 15 << 12; // Default color (bright white) + currentConsole->fontCurPal2 = 0; // black bg + return; + } + + // ignore truecolor rgb sequences in the form `ESC[38;2;{r};{g};{b}m` + if (siscanf(escapeseq, "38;2;%*d;%*d;%*dm") > 0) + return; + + unsigned id; + // 256 color format sequence: `ESC[38;5;{ID}m` + // support it, but just use 0-15, ignore everything else. + if (siscanf(escapeseq, "38;5;%um", &id) > 0) { + if (id <= 15) + currentConsole->fontCurPal = id << 12; + return; + } + + // -1 color means unchanged + int color = -1, bgcolor = -1, bright = 0, items_matched, chars_consumed, param; + const char *p = escapeseq; + + // %n does not match anything, it stores the number of characters + // consumed thus far into the next pointer! use this to advance `p`. + + // start consuming arguments, delimited with ';' + while (true) { + // Try to parse a parameter followed by a semicolon + items_matched = siscanf(p, "%d;%n", ¶m, &chars_consumed); + if (items_matched > 0) { + updateColorBright(param, &color, &bgcolor, &bright); + p += chars_consumed; + continue; + } + + // If that failed, try to parse the final parameter ending in 'm' + items_matched = siscanf(p, "%dm", ¶m); + if (items_matched > 0) + updateColorBright(param, &color, &bgcolor, &bright); + + // End of sequence + break; + } + + // handle cases when only bold (1) modifier is used. this only affects foreground. + // this should simply turn the current color into its bright variant. + int final_color = -1; + if (color != -1) { + final_color = color; + if (bright && final_color < 8) + final_color += 8; + } else if (bright) { + // if only intensity is set, brighten the current color + int current_color = (currentConsole->fontCurPal >> 12); + if (current_color < 8) + final_color = current_color + 8; + else + final_color = current_color; + } + + // final_param is a 4-bit integer (0-15). this is placed into the + // last 4 bits of fontCurPal, which is |'d with the character offset. + // this ALSO means we can extract it out of an FBMV by >>'ing 12. + // see consoleComputeFontBgMapValue() for reference. + + if (final_color != -1) + currentConsole->fontCurPal = final_color << 12; + if (bgcolor != -1) + currentConsole->fontCurPal2 = bgcolor << 12; +} + +static void consoleParseCsiSequence(void) { + PrintConsole *const c = currentConsole; + const char *const seq = c->escBuf + 2; // Skip "\e[" + const int len = c->escBufLen; + + // The last character decides the function of the sequence + const char command = c->escBuf[c->escBufLen - 1]; + + switch (command) { + // Private modes - commonly end in 'h' or 'l'. We don't implement any (yet), so just consume them. + case 'h': + case 'l': + break; + + // Move cursor up + case 'A': { + int dy = 1; + siscanf(seq, "%dA", &dy); + consoleMoveCursorY(-dy); + break; + } + + // Move cursor down + case 'B': { + int dy = 1; + siscanf(seq, "%dB", &dy); + consoleMoveCursorY(dy); + break; + } + + // Move cursor right + case 'C': { + int dx = 1; + siscanf(seq, "%dC", &dx); + consoleMoveCursorX(dx); + break; + } + + // Move cursor left + case 'D': { + int dx = 1; + siscanf(seq, "%dD", &dx); + consoleMoveCursorX(-dx); + break; + } + + // Set cursor position + case 'H': + case 'f': { + int x, y; + if (siscanf(seq, "%d;%d", &y, &x) == 2) + consoleSetCursorPos(x, y); + else + consoleSetCursorPos(0, 0); + break; + } + + // Screen clear + case 'J': + consoleCls(seq[len - 2]); + break; + + // Line clear + case 'K': + consoleClearLine(seq[len - 2]); + break; + + // Save cursor position + case 's': + c->prevCursorX = c->cursorX; + c->prevCursorY = c->cursorY; + break; + + // Load cursor position + case 'u': + consoleSetCursorPos(c->prevCursorX, c->prevCursorY); + break; + + // Color/style modes + case 'm': + consoleParseColor(seq); + break; + } +} + +static void dumpAndResetEscBuf(void) { + PrintConsole *const c = currentConsole; + for (int i = 0; i < c->escBufLen; i++) + consolePrintChar(c->escBuf[i]); + c->escBufLen = 0; +} + +void consoleUpdateEscapeSequence(const char ch) { + PrintConsole *const c = currentConsole; + + if (!c) + return; + + if (ch == '\e') { + // We're most likely supposed to start buffering a new sequence. + dumpAndResetEscBuf(); + } + + // Append to buffer + c->escBuf[c->escBufLen++] = ch; + c->escBuf[c->escBufLen] = '\0'; // just to be safe + + // Do we only have an escape in the buffer? + if (c->escBufLen == 1 && c->escBuf[0] == '\e') + // Don't continue, we don't want to dump the buffer now. + return; + + // Do we have a CSI at the beginning of the buffer? + if (c->escBuf[0] == '\e' && c->escBuf[1] == '[') { + // Did we just form a full sequence? The sequences we parse all end in alphabetical characters. + if (isalpha(ch)) { + consoleParseCsiSequence(); + c->escBufLen = 0; + } + } else { + dumpAndResetEscBuf(); + } + + if (c->escBufLen >= sizeof(c->escBuf) - 1) + // Escape sequences shouldn't be this long. + dumpAndResetEscBuf(); +} diff --git a/source/arm9/console-print.c b/source/arm9/console-print.c new file mode 100644 index 0000000..ac269c3 --- /dev/null +++ b/source/arm9/console-print.c @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------- + +Copyright (C) 2025 +trustytrojan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source +distribution. + +---------------------------------------------------------------------------------*/ +#include "console-priv.h" + +u16 *consoleFontBgMapAt(const int x, const int y) { + const PrintConsole *const c = currentConsole; + const int xOffset = x + c->windowX; + const int yOffset = (y + c->windowY) * c->consoleWidth; + return c->fontBgMap + xOffset + yOffset; +} + +u16 *consoleFontBg2MapAt(const int x, const int y) { + const PrintConsole *const c = currentConsole; + const int xOffset = x + c->windowX; + const int yOffset = (y + c->windowY) * c->consoleWidth; + return c->fontBg2Map + xOffset + yOffset; +} + +u16 *consoleFontBgMapAtCursor(void) { + const PrintConsole *const c = currentConsole; + return consoleFontBgMapAt(c->cursorX, c->cursorY); +} + +u16 *consoleFontBg2MapAtCursor(void) { + const PrintConsole *const c = currentConsole; + return consoleFontBg2MapAt(c->cursorX, c->cursorY); +} + +u16 consoleComputeFontBgMapValue(const char ch) { + const PrintConsole *const c = currentConsole; + return c->fontCurPal | (u16)(ch + c->fontCharOffset - c->font.asciiOffset); +} + +u16 consoleComputeFontBg2MapValue(const char ch) { + const PrintConsole *const c = currentConsole; + return c->fontCurPal2 | (u16)(ch + c->fontCharOffset - c->font.asciiOffset); +} + +void consoleCommitChar(const char ch) { + PrintConsole *const c = currentConsole; + + *consoleFontBgMapAtCursor() = consoleComputeFontBgMapValue(ch); // fg + + if (c->bg2Id != -1) + *consoleFontBg2MapAtCursor() = consoleComputeFontBg2MapValue(219); // bg + + ++c->cursorX; + + if (c->bg2Id != -1) { + consoleSaveTileUnderCursor(); + consoleDrawCursor(); + } +} + +// could have a better name, since we aren't always printing a character +void consolePrintChar(const char ch) { + if (!ch) + return; + + PrintConsole *const c = currentConsole; + + if (!c->fontBgMap) + return; + + if (ch + c->fontCharOffset - c->font.asciiOffset > c->font.numChars) + // this prevents weird stuff showing up when printing! + return; + + if (c->PrintChar && c->PrintChar(c, ch)) + return; + + if (c->cursorX >= c->windowWidth) { + if (currentConsole->bg2Id == -1) + c->cursorX = 0; + else + consoleSetCursorX(0); + newRow(); + } + + switch (ch) { + case '\a': + // bell character: TODO: add a callback for applications to respond to this. e.g. playing a bell sound + break; + + case '\b': + /* + the old code here actually moved the cursor back one (and up one row + if needed) and then "erased" the character by writing a space to fontBgMap. + + in a linux terminal emulator with the termios attr ICANON off, + backspace does NOT move the cursor. if the ECHO termios attr is on + it prints the \b character (visualized as ^?). + + with ICANON and ECHO on, backspace does NOT wrap the cursor around to the + last row. + + what dkp was trying to do here is emulate "canonical mode" + (ICANON termios attr), which tells the terminal to line-buffer + input from the keyboard before sending it to stdin. but it ALSO + does what WE (the console) were doing here before: erasing the current character + and moving the cursor back. THIS SHOULD BE KEYBOARD.C's JOB! + + SOLUTION: + keyboard.c should have both an "echo" and "line buffer" option. + when keyboard.c's "line buffering" is on, **it should interface with the + currently selected console** to erase the character at the cursor and then move + the cursor back. when "echo" is on, keyboard.c should send what it gets to the + console to be ***immediately*** rendered, with non-visual characters visualized. + for example, left arrow becomes ^[[D. + + the responsibilities should NOT be mixed together. + */ + break; + + case '\t': + if (currentConsole->bg2Id == -1) + c->cursorX += c->tabSize - ((c->cursorX) % (c->tabSize)); + else + consoleMoveCursorX(c->tabSize - ((c->cursorX) % (c->tabSize))); + break; + + case '\n': + newRow(); + // also return to first column: + + case '\r': + if (currentConsole->bg2Id == -1) + c->cursorX = 0; + else + consoleSetCursorX(0); + break; + + default: + *consoleFontBgMapAtCursor() = consoleComputeFontBgMapValue(ch); // fg + + if (c->bg2Id != -1) + *consoleFontBg2MapAtCursor() = consoleComputeFontBg2MapValue(219); // bg + + ++c->cursorX; + + if (c->bg2Id != -1) { + consoleSaveTileUnderCursor(); + consoleDrawCursor(); + } + } +} diff --git a/source/arm9/console-priv.h b/source/arm9/console-priv.h new file mode 100644 index 0000000..daecfba --- /dev/null +++ b/source/arm9/console-priv.h @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------- + +Copyright (C) 2025 +trustytrojan + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source +distribution. + +---------------------------------------------------------------------------------*/ +#pragma once + +#include +#include +#include + +// console.c +extern PrintConsole *currentConsole; +void consoleCls(char mode); +void consoleClearLine(char mode); +void newRow(); + +// console-print.c +u16 *consoleFontBgMapAt(int x, int y); +u16 *consoleFontBg2MapAt(int x, int y); +u16 *consoleFontBgMapAtCursor(void); +u16 *consoleFontBg2MapAtCursor(void); +u16 consoleComputeFontBgMapValue(char); +u16 consoleComputeFontBg2MapValue(char); +void consolePrintChar(char); + +// console-cursor.c +void consoleSaveTileUnderCursor(void); +void consoleRestoreTileUnderCursor(void); +void consoleMoveCursorX(int dx); // clamps cursorX to windowWidth +void consoleMoveCursorY(int dy); // clamps cursorY to windowHeight +void consoleSetCursorX(int x); +void consoleSetCursorY(int y); +void consoleSetCursorPos(int x, int y); +void consoleDrawCursor(void); + +// console-esc.c +void consoleUpdateEscapeSequence(char c); +int consoleParseEscapeSequence(const char *ptr, int len); diff --git a/source/arm9/console.c b/source/arm9/console.c index 77b0a91..cb865e2 100644 --- a/source/arm9/console.c +++ b/source/arm9/console.c @@ -36,52 +36,58 @@ distribution. #include #include - -PrintConsole defaultConsole = -{ - //Font: - { - (u16*)default_font_bin, //font gfx - 0, //font palette - 0, //font color count - 1, //bpp - 0, //first ascii character in the set - 256, //number of characters in the font set - true //convert single color +#include "console-priv.h" + + +const PrintConsole defaultConsole = { + .font = { + .gfx = (u16*)default_font_bin, + .pal = 0, + .numColors = 0, + .bpp = 1, + .asciiOffset = 0, + .numChars = 256, + .convertSingleColor = true }, - 0, //font background map - 0, //font background gfx - 22, //map base - 3, //char base - 0, //bg layer in use - -1, //bg id - 0,0, //cursorX cursorY - 0,0, //prevcursorX prevcursorY - 32, //console width - 24, //console height - 0, //window x - 0, //window y - 32, //window width - 24, //window height - 3, //tab size - 0, //font character offset - 0, //selected palette - 0, //print callback - false, //console initialized - true, //load graphics + .fontBgMap = 0, + .fontBgGfx = 0, + .mapBase = 22, + .gfxBase = 3, + .bgLayer = 0, + .bgId = -1, + .cursorX = 0, + .cursorY = 0, + .prevCursorX = 0, + .prevCursorY = 0, + .consoleWidth = 32, + .consoleHeight = 24, + .windowX = 0, + .windowY = 0, + .windowWidth = 32, + .windowHeight = 24, + .tabSize = 3, + .fontCharOffset = 0, + .fontCurPal = 0, + .PrintChar = NULL, + .consoleInitialised = false, + .loadGraphics = true, + .bg2Id = -1, + .fontBg2Map = NULL, + .fontBg2Gfx = NULL, + .fontCurPal2 = 0, + .escBuf = {}, + .escBufLen = 0 }; PrintConsole currentCopy; PrintConsole* currentConsole = ¤tCopy; -PrintConsole* consoleGetDefault(void){return &defaultConsole;} - -void consolePrintChar(char c); +const PrintConsole* consoleGetDefault(void){return &defaultConsole;} //--------------------------------------------------------------------------------- -static void consoleCls(char mode) { +void consoleCls(char mode) { //--------------------------------------------------------------------------------- int i = 0; @@ -131,68 +137,33 @@ static void consoleCls(char mode) { } } } -//--------------------------------------------------------------------------------- -static void consoleClearLine(char mode) { -//--------------------------------------------------------------------------------- - - int i = 0; - int colTemp; - - switch (mode) - { - case '[': - case '0': - { - colTemp = currentConsole->cursorX ; - - while(i++ < (currentConsole->windowWidth - colTemp)) { - consolePrintChar(' '); - } - - currentConsole->cursorX = colTemp; - - break; - } - case '1': - { - colTemp = currentConsole->cursorX ; - - currentConsole->cursorX = 0; - - while(i++ < ((currentConsole->windowWidth - colTemp)-2)) { - consolePrintChar(' '); - } - - currentConsole->cursorX = colTemp; - break; - } - case '2': - { - colTemp = currentConsole->cursorX ; - - currentConsole->cursorX = 0; - - while(i++ < currentConsole->windowWidth) { - consolePrintChar(' '); - } - - currentConsole->cursorX = colTemp; - - break; - } - default: - { - colTemp = currentConsole->cursorX ; - - while(i++ < (currentConsole->windowWidth - colTemp)) { - consolePrintChar(' '); - } +void consoleClearLine(char mode) { + int line = currentConsole->cursorY; + + // \e[K is same as \e[0K: from cursor to end of line + // so use its parameters as the default. + int start = currentConsole->cursorX; + int end = currentConsole->windowWidth; + + if (mode == '1') { + // start of line to cursor + start = 0; + end = currentConsole->cursorX; + } else if (mode == '2') { + // whole line + start = 0; + end = currentConsole->windowWidth; + } - currentConsole->cursorX = colTemp; + // The character offset for ' ' in the font. + const u16 blank = ' ' + currentConsole->fontCharOffset - currentConsole->font.asciiOffset; - break; - } + for (int i = start; i < end; ++i) { + // Clear with default colors (fg: white, bg: black) regardless of current palette. + *consoleFontBgMapAt(i, line) = (15 << 12) | blank; + if (currentConsole->bg2Id != -1) + *consoleFontBg2MapAt(i, line) = (0 << 12) | blank; } } @@ -203,132 +174,19 @@ ssize_t nocash_write(struct _reent *r, void *fd, const char *ptr, size_t len) { return len; } +ssize_t con_write(struct _reent *r, void *fd, const char *ptr, size_t len) { + if (!ptr || len <= 0) + return -1; -//--------------------------------------------------------------------------------- -ssize_t con_write(struct _reent *r,void *fd,const char *ptr, size_t len) { -//--------------------------------------------------------------------------------- - - char chr; - - int i, count = 0; - char *tmp = (char*)ptr; - int intensity = 0; - - if(!tmp || len<=0) return -1; - - i = 0; - - while(icursorY = (currentConsole->cursorY - parameter) < 0 ? 0 : currentConsole->cursorY - parameter; - escaping = false; - break; - case 'B': - siscanf(escapeseq,"[%dB", ¶meter); - currentConsole->cursorY = (currentConsole->cursorY + parameter) > currentConsole->windowHeight - 1 ? currentConsole->windowHeight - 1 : currentConsole->cursorY + parameter; - escaping = false; - break; - case 'C': - siscanf(escapeseq,"[%dC", ¶meter); - currentConsole->cursorX = (currentConsole->cursorX + parameter) > currentConsole->windowWidth - 1 ? currentConsole->windowWidth - 1 : currentConsole->cursorX + parameter; - escaping = false; - break; - case 'D': - siscanf(escapeseq,"[%dD", ¶meter); - currentConsole->cursorX = (currentConsole->cursorX - parameter) < 0 ? 0 : currentConsole->cursorX - parameter; - escaping = false; - break; - ///////////////////////////////////////// - // Cursor position movement - ///////////////////////////////////////// - case 'H': - case 'f': - siscanf(escapeseq,"[%d;%df", ¤tConsole->cursorY , ¤tConsole->cursorX ); - escaping = false; - break; - ///////////////////////////////////////// - // Screen clear - ///////////////////////////////////////// - case 'J': - consoleCls(escapeseq[escapelen-2]); - escaping = false; - break; - ///////////////////////////////////////// - // Line clear - ///////////////////////////////////////// - case 'K': - consoleClearLine(escapeseq[escapelen-2]); - escaping = false; - break; - ///////////////////////////////////////// - // Save cursor position - ///////////////////////////////////////// - case 's': - currentConsole->prevCursorX = currentConsole->cursorX ; - currentConsole->prevCursorY = currentConsole->cursorY ; - escaping = false; - break; - ///////////////////////////////////////// - // Load cursor position - ///////////////////////////////////////// - case 'u': - currentConsole->cursorX = currentConsole->prevCursorX ; - currentConsole->cursorY = currentConsole->prevCursorY ; - escaping = false; - break; - ///////////////////////////////////////// - // Color scan codes - ///////////////////////////////////////// - case 'm': - siscanf(escapeseq,"[%d;%dm", ¶meter, &intensity); - - //only handle 30-37,39 and intensity for the color changes - parameter -= 30; - - //39 is the reset code - if(parameter == 9){ - parameter = 15; - } - else if(parameter > 8){ - parameter -= 2; - } - else if(intensity){ - parameter += 8; - } - if(parameter < 16 && parameter >= 0){ - currentConsole->fontCurPal = parameter << 12; - } - - escaping = false; - break; - } - } while (escaping); - continue; - } - - consolePrintChar(chr); + for (size_t i = 0; i < len; ++i) { + const char chr = ptr[i]; + if (chr == '\e' || currentConsole->escBufLen > 0) + consoleUpdateEscapeSequence(chr); + else + consolePrintChar(chr); } - return count; + return len; } static const devoptab_t dotab_stdout = { @@ -381,17 +239,20 @@ void consoleLoadFont(PrintConsole* console) { const u8* in = (const u8*)console->font.gfx; u32* out = (u32*)console->fontBgGfx; - for (i = 0; i < console->font.numChars * 8; i ++) { + u32* out2 = (u32*)console->fontBg2Gfx; + for ( i = 0; i < console->font.numChars * 8; i ++) { unsigned cur = *in++; int j; u32 temp = 0; - for (j = 0; j < 8; j ++) { + for ( j = 0; j < 8; j ++) { temp |= ((cur&1) * 0xf) << (j*4); cur >>= 1; } *out++ = temp; + if (console->bg2Id != -1) + *out2++ = temp; } goto _setUpPalette; @@ -408,7 +269,7 @@ void consoleLoadFont(PrintConsole* console) { console->fontCurPal <<= 12; } else { - for (i = 0; i < console->font.numChars * 16; i++) { + for ( i = 0; i < console->font.numChars * 16; i++) { u16 temp = 0; if(console->font.gfx[i] & 0xF) @@ -425,27 +286,28 @@ void consoleLoadFont(PrintConsole* console) { _setUpPalette: //set up the palette for color printing - palette[1 * 16 - 1] = RGB15(0,0,0); //30 normal black - palette[2 * 16 - 1] = RGB15(15,0,0); //31 normal red - palette[3 * 16 - 1] = RGB15(0,15,0); //32 normal green - palette[4 * 16 - 1] = RGB15(15,15,0); //33 normal yellow - - palette[5 * 16 - 1] = RGB15(0,0,15); //34 normal blue - palette[6 * 16 - 1] = RGB15(15,0,15); //35 normal magenta - palette[7 * 16 - 1] = RGB15(0,15,15); //36 normal cyan - palette[8 * 16 - 1] = RGB15(24,24,24); //37 normal white - - palette[9 * 16 - 1 ] = RGB15(15,15,15); //40 bright black - palette[10 * 16 - 1] = RGB15(31,0,0); //41 bright red - palette[11 * 16 - 1] = RGB15(0,31,0); //42 bright green - palette[12 * 16 - 1] = RGB15(31,31,0); //43 bright yellow - - palette[13 * 16 - 1] = RGB15(0,0,31); //44 bright blue - palette[14 * 16 - 1] = RGB15(31,0,31); //45 bright magenta - palette[15 * 16 - 1] = RGB15(0,31,31); //46 bright cyan - palette[16 * 16 - 1] = RGB15(31,31,31); //47 & 39 bright white + palette[1 * 16 - 1] = RGB15(0, 0, 0); // normal black + palette[2 * 16 - 1] = RGB15(15, 0, 0); // normal red + palette[3 * 16 - 1] = RGB15(0, 15, 0); // normal green + palette[4 * 16 - 1] = RGB15(15, 15, 0); // normal yellow + + palette[5 * 16 - 1] = RGB15(0, 0, 15); // normal blue + palette[6 * 16 - 1] = RGB15(15, 0, 15); // normal magenta + palette[7 * 16 - 1] = RGB15(0, 15, 15); // normal cyan + palette[8 * 16 - 1] = RGB15(24, 24, 24); // normal white + + palette[9 * 16 - 1] = RGB15(15, 15, 15); // bright black + palette[10 * 16 - 1] = RGB15(31, 0, 0); // bright red + palette[11 * 16 - 1] = RGB15(0, 31, 0); // bright green + palette[12 * 16 - 1] = RGB15(31, 31, 0); // bright yellow + + palette[13 * 16 - 1] = RGB15(0, 0, 31); // bright blue + palette[14 * 16 - 1] = RGB15(31, 0, 31); // bright magenta + palette[15 * 16 - 1] = RGB15(0, 31, 31); // bright cyan + palette[16 * 16 - 1] = RGB15(31, 31, 31); // bright white console->fontCurPal = 15 << 12; + console->fontCurPal2 = 0 << 12; } } else if(console->font.bpp == 8) { @@ -492,7 +354,8 @@ void consoleLoadFont(PrintConsole* console) { PrintConsole* consoleInit(PrintConsole* console, int layer, BgType type, BgSize size, int mapBase, int tileBase, - bool mainDisplay, bool loadGraphics){ + bool mainDisplay, bool loadGraphics, + bool ansiBgColors){ //--------------------------------------------------------------------------------- static bool firstConsoleInit = true; @@ -517,13 +380,21 @@ PrintConsole* consoleInit(PrintConsole* console, int layer, if(mainDisplay) { console->bgId = bgInit(layer, type, size, mapBase, tileBase); + console->bg2Id = ansiBgColors ? bgInit(layer + 1, type, size, mapBase + 1, tileBase) : -1; } else { console->bgId = bgInitSub(layer, type, size, mapBase, tileBase); + console->bg2Id = ansiBgColors ? bgInitSub(layer + 1, type, size, mapBase + 1, tileBase) : -1; } - console->fontBgGfx = (u16*)bgGetGfxPtr(console->bgId); - console->fontBgMap = (u16*)bgGetMapPtr(console->bgId); + console->fontBgGfx = bgGetGfxPtr(console->bgId); + console->fontBgMap = bgGetMapPtr(console->bgId); + + if (console->bg2Id != -1) { + console->fontBg2Gfx = bgGetGfxPtr(console->bg2Id); + console->fontBg2Map = bgGetMapPtr(console->bg2Id); + } + console->escBufLen = 0; console->consoleInitialised = 1; consoleCls('2'); @@ -580,18 +451,17 @@ void consoleDebugInit(DebugDevice device){ //--------------------------------------------------------------------------------- // Places the console in a default mode using bg0 of the sub display, and vram c for // font and map..this is provided for rapid prototyping and nothing more -PrintConsole* consoleDemoInit(void) { //--------------------------------------------------------------------------------- +PrintConsole* consoleDemoInit(void) { videoSetModeSub(MODE_0_2D); vramSetBankC(VRAM_C_SUB_BG); - return consoleInit(NULL, defaultConsole.bgLayer, BgType_Text4bpp, BgSize_T_256x256, defaultConsole.mapBase, defaultConsole.gfxBase, false, true); + return consoleInit(NULL, defaultConsole.bgLayer, BgType_Text4bpp, BgSize_T_256x256, defaultConsole.mapBase, defaultConsole.gfxBase, false, true, false); } -//--------------------------------------------------------------------------------- -static void newRow() { -//--------------------------------------------------------------------------------- - +void newRow() { + if (currentConsole->bg2Id != -1) + consoleRestoreTileUnderCursor(); currentConsole->cursorY ++; @@ -602,73 +472,21 @@ static void newRow() { currentConsole->cursorY --; for(rowCount = 0; rowCount < currentConsole->windowHeight - 1; rowCount++) - for(colCount = 0; colCount < currentConsole->windowWidth; colCount++) - currentConsole->fontBgMap[(colCount + currentConsole->windowX) + (rowCount + currentConsole->windowY) * currentConsole->consoleWidth] = - currentConsole->fontBgMap[(colCount + currentConsole->windowX) + (rowCount + currentConsole->windowY + 1) * currentConsole->consoleWidth]; - - for(colCount = 0; colCount < currentConsole->windowWidth; colCount++) - currentConsole->fontBgMap[(colCount + currentConsole->windowX) + (rowCount + currentConsole->windowY) * currentConsole->consoleWidth] = - (' ' + currentConsole->fontCharOffset - currentConsole->font.asciiOffset); - - } -} - - -//--------------------------------------------------------------------------------- -void consolePrintChar(char c) { -//--------------------------------------------------------------------------------- - if (c==0) return; - if(currentConsole->fontBgMap == 0) return; - - if(currentConsole->PrintChar) - if(currentConsole->PrintChar(currentConsole, c)) - return; - - if(currentConsole->cursorX >= currentConsole->windowWidth) { - currentConsole->cursorX = 0; - - newRow(); - } - - switch(c) { - /* - The only special characters we will handle are tab (\t), carriage return (\r), line feed (\n) - and backspace (\b). - Carriage return & line feed will function the same: go to next line and put cursor at the beginning. - For everything else, use VT sequences. - - Reason: VT sequences are more specific to the task of cursor placement. - The special escape sequences \b \f & \v are archaic and non-portable. - */ - case 8: - currentConsole->cursorX--; - - if(currentConsole->cursorX < 0) { - if(currentConsole->cursorY > 0) { - currentConsole->cursorX = currentConsole->windowX - 1; - currentConsole->cursorY--; - } else { - currentConsole->cursorX = 0; - } + for(colCount = 0; colCount < currentConsole->windowWidth; colCount++) { + *consoleFontBgMapAt(colCount, rowCount) = *consoleFontBgMapAt(colCount, rowCount + 1); + if (currentConsole->bg2Id != -1) + *consoleFontBg2MapAt(colCount, rowCount) = *consoleFontBg2MapAt(colCount, rowCount + 1); } - currentConsole->fontBgMap[currentConsole->cursorX + currentConsole->windowX + (currentConsole->cursorY + currentConsole->windowY) * currentConsole->consoleWidth] = currentConsole->fontCurPal | (u16)(' ' + currentConsole->fontCharOffset - currentConsole->font.asciiOffset); - - break; - - case 9: - currentConsole->cursorX += currentConsole->tabSize - ((currentConsole->cursorX)%(currentConsole->tabSize)); - break; - case 10: - newRow(); - case 13: - currentConsole->cursorX = 0; - break; - default: - currentConsole->fontBgMap[currentConsole->cursorX + currentConsole->windowX + (currentConsole->cursorY + currentConsole->windowY) * currentConsole->consoleWidth] = currentConsole->fontCurPal | (u16)(c + currentConsole->fontCharOffset - currentConsole->font.asciiOffset); - ++currentConsole->cursorX ; - break; + for(colCount = 0; colCount < currentConsole->windowWidth; colCount++) { + *consoleFontBgMapAt(colCount, rowCount) = consoleComputeFontBgMapValue(' '); + if (currentConsole->bg2Id != -1) + *consoleFontBg2MapAt(colCount, rowCount) = consoleComputeFontBg2MapValue(' '); + } } + + if (currentConsole->bg2Id != -1) + consoleSaveTileUnderCursor(); } //---------------------------------------------------------------------------------