Compare commits

...
10 Commits
6 changed files with 326 additions and 51 deletions
+2
View File
@@ -7,6 +7,7 @@
*.obj *.obj
*.elf *.elf
*.src *.src
obj/*
# Linker output # Linker output
*.ilk *.ilk
@@ -39,6 +40,7 @@
*.x86_64 *.x86_64
*.hex *.hex
*.8xp *.8xp
bin/*
# Debug files # Debug files
*.dSYM/ *.dSYM/
+11 -6
View File
@@ -2,9 +2,9 @@
# Makefile Options # Makefile Options
# ---------------------------- # ----------------------------
NAME = DEMO NAME = TERMCE
ICON = icon.png ICON = icon.png
DESCRIPTION = "CE C Toolchain Demo" DESCRIPTION = "Serial Terminal for CE"
COMPRESSED = NO COMPRESSED = NO
CFLAGS = -Wall -Wextra -Oz CFLAGS = -Wall -Wextra -Oz
@@ -12,11 +12,16 @@ CXXFLAGS = -Wall -Wextra -Oz
# ---------------------------- # ----------------------------
all: build-font
include $(shell cedev-config --makefile) include $(shell cedev-config --makefile)
FONTPACKNAME = termFont FONTPACKNAME = TermFont
FONTDIR = $(SRCDIR)/gfx FONTDIR = $(SRCDIR)/gfx
DEPS = $(BINDIR)/$(FONTPACKNAME).8xv
$(BINDIR)/$(FONTPACKNAME).8xv: $(SRCDIR)/gfx/$(FONTPACKNAME).bin SHELL := /bin/bash
$(Q)convbin --iformat bin --input $(SRCDIR)/gfx/$(FONTPACKNAME).bin --oformat 8xv --output $(BINDIR)/$(FONTPACKNAME).8xv --archive --name $(FONTPACKNAME)
build-font:
mkdir $(BINDIR)
python3 scripts/convert-fonttiles.py $(FONTDIR)/$(FONTPACKNAME).png $(BINDIR)/$(FONTPACKNAME).bin
convbin --iformat bin --oformat 8xv --input $(BINDIR)/$(FONTPACKNAME).bin --output $(BINDIR)/$(FONTPACKNAME).8xv --name $(FONTPACKNAME)
@@ -1,9 +1,11 @@
#!/usr/bin/python #!/usr/bin/python
from PIL import Image from PIL import Image
import struct import struct, sys
tilesheetPNG = Image.open("TermFont.png") print("[convert-fonttiles] converting file " + sys.argv[1] + " to binary file " + sys.argv[2])
tilesheetBIN = open("TermFont.bin", "wb")
tilesheetPNG = Image.open(sys.argv[1])
tilesheetBIN = open(sys.argv[2], "wb")
tilesheetPixels = tilesheetPNG.load() tilesheetPixels = tilesheetPNG.load()
tilesheetX, tilesheetY = tilesheetPNG.size tilesheetX, tilesheetY = tilesheetPNG.size
@@ -16,7 +18,11 @@ def generateTile(x,y,image):
for py in range(0,8): for py in range(0,8):
row = [] row = []
for px in range(0,8): for px in range(0,8):
r,g,b,a = image[(8*x)+px,(8*y)+py] pixelvalues = image[(8*x)+px,(8*y)+py]
try:
r,g,b,a = pixelvalues
except ValueError:
r,g,b = pixelvalues
row.append(((r+g+b)>384)) row.append(((r+g+b)>384))
tile.append(sum([int(row[b])*(2**(7-b)) for b in range(0,8)])) tile.append(sum([int(row[b])*(2**(7-b)) for b in range(0,8)]))
return tile return tile
@@ -27,3 +33,5 @@ for y in range(0,vertTiles):
generatedTileData += generateTile(x,y,tilesheetPixels) generatedTileData += generateTile(x,y,tilesheetPixels)
tilesheetBIN.write(bytearray(generatedTileData)) tilesheetBIN.write(bytearray(generatedTileData))
print("[convert-fonttiles] conversion complete.")
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 29 KiB

+300 -40
View File
@@ -21,7 +21,7 @@ srl_device_t srl;
bool has_srl_device = false; bool has_srl_device = false;
uint8_t srl_buf[512]; uint8_t srl_buf[1024];
long* vram = (long*)lcd_Ram; long* vram = (long*)lcd_Ram;
@@ -96,9 +96,56 @@ uint8_t colour_gray = 0b1000;
uint8_t keyboardState = 0x00; uint8_t keyboardState = 0x00;
bool newTiles = true; /* ---------------------------------------------------------------------
* ANSI / VT100 escape sequence parser state
*
* Supports a practical subset of CSI ("ESC [ ... letter") sequences:
* CUU/CUD/CUF/CUB - cursor up/down/forward/back (A/B/C/D)
* CUP/HVP - absolute cursor position (H/f)
* ED - erase in display, modes 0/1/2 (J)
* EL - erase in line, modes 0/1/2 (K)
* SGR - colours + bold/reverse, see below (m)
*
* SGR uses the existing 4-bit (RGBI) palette: bit0=red, bit1=green,
* bit2=blue, bit3=intensity ("gray"/bold bit). Standard ANSI codes
* 30-37/40-47 map straight onto that 3-bit RGB value, and 90-97/100-107
* add the intensity bit for the "bright" variants. 256-colour / truecolour
* sequences (38;5;n / 38;2;r;g;b) aren't supported by a 16-colour display,
* so they're parsed-but-ignored rather than crashing or printing garbage.
*
* Anything else we don't recognise (DEC private modes via "ESC[?", OSC
* strings like "ESC]0;title BEL" used for window titles, single-byte
* escapes like ESC c) is consumed and discarded rather than dumped to
* the screen as literal characters.
* --------------------------------------------------------------------- */
const char* keyactions[1024]; typedef enum {
TERM_NORMAL,
TERM_ESC,
TERM_CSI,
TERM_OSC
} term_state_t;
term_state_t termState = TERM_NORMAL;
#define MAX_CSI_PARAMS 8
int csiParams[MAX_CSI_PARAMS];
uint8_t csiParamCount = 0;
bool csiPrivate = false;
uint8_t baseFgColour = 0x0F; /* default: full white text */
uint8_t baseBgColour = 0x00; /* default: black background */
bool reverseVideo = false;
/* Tracks whether the previous byte was a CR, so a CRLF pair only advances one line */
bool sawCR = false;
/* Set by Ctrl+V (0x16): the next byte is drawn as a raw glyph, bypassing all parsing */
bool literalNext = false;
int scrollAmount = 0;
bool newTiles = true;
long lcdregisters; long lcdregisters;
@@ -157,7 +204,8 @@ int begin() {
void scroll() { void scroll() {
memcpy(renderGoal+80, renderGoal+120, 2000); memcpy(renderGoal+80, renderGoal+120, 2000);
memset(renderGoal+1080, 0, 80); memset(renderGoal+1080, 0, 80);
newTiles = true; scrollAmount++;
cursorY -= 1;
} }
int end() { int end() {
@@ -171,36 +219,242 @@ int end() {
return exit_Code; return exit_Code;
} }
/* Current colour byte (high nibble = fg, low nibble = bg), honouring SGR reverse video */
uint8_t colourByte(void) {
uint8_t fg = reverseVideo ? baseBgColour : baseFgColour;
uint8_t bg = reverseVideo ? baseFgColour : baseBgColour;
return (uint8_t)((fg << 4) | bg);
}
/* Blank out renderGoal tiles [startIdx, endIdx] (inclusive) using the current colour */
void eraseRange(int startIdx, int endIdx) {
uint16_t blank = (uint16_t)((0x20 << 8) | colourByte());
if (startIdx < 0) startIdx = 0;
if (endIdx > 1199) endIdx = 1199;
for (int i = startIdx; i <= endIdx; i++) {
renderGoal[i] = blank;
}
newTiles = true;
}
/* Execute a fully-parsed CSI sequence ("ESC [ params finalByte") */
void handleEscapeCommand(char finalByte) {
int p0 = csiParams[0];
int n = (p0 == 0) ? 1 : p0;
int tmp;
switch (finalByte) {
case 'A': /* CUU - cursor up */
tmp = (int)cursorY - n;
cursorY = (uint8_t)(tmp < 0 ? 0 : tmp);
break;
case 'B': /* CUD - cursor down */
tmp = (int)cursorY + n;
cursorY = (uint8_t)(tmp > 25 ? 25 : tmp);
break;
case 'C': /* CUF - cursor forward */
tmp = (int)cursorX + n;
cursorX = (uint8_t)(tmp > 39 ? 39 : tmp);
break;
case 'D': /* CUB - cursor back */
tmp = (int)cursorX - n;
cursorX = (uint8_t)(tmp < 0 ? 0 : tmp);
break;
case 'H': /* CUP */
case 'f': { /* HVP - treated the same here */
int row = csiParams[0];
int col = csiParams[1];
if (row <= 0) row = 1;
if (col <= 0) col = 1;
row--;
col--;
if (row > 25) row = 25;
if (col > 39) col = 39;
cursorY = (uint8_t)row;
cursorX = (uint8_t)col;
break;
}
case 'J': { /* ED - erase in display */
int mode = csiParams[0];
int cur = (40 * (cursorY + 2)) + cursorX;
int scrStart = 80;
int scrEnd = 1119;
if (mode == 1) {
eraseRange(scrStart, cur);
} else if (mode == 2 || mode == 3) {
eraseRange(scrStart, scrEnd);
} else {
eraseRange(cur, scrEnd);
}
break;
}
case 'K': { /* EL - erase in line */
int mode = csiParams[0];
int rowStart = 40 * (cursorY + 2);
int rowEnd = rowStart + 39;
int cur = rowStart + cursorX;
if (mode == 1) {
eraseRange(rowStart, cur);
} else if (mode == 2) {
eraseRange(rowStart, rowEnd);
} else {
eraseRange(cur, rowEnd);
}
break;
}
case 'm': { /* SGR - colours / attributes */
int count = csiParamCount + 1;
for (int i = 0; i < count; i++) {
int code = csiParams[i];
if (code == 0) {
baseFgColour = (uint8_t)(colour_red | colour_green | colour_blue | colour_gray);
baseBgColour = 0x00;
reverseVideo = false;
} else if (code == 1) {
baseFgColour |= colour_gray;
} else if (code == 2 || code == 22) {
baseFgColour &= (uint8_t)~colour_gray;
} else if (code == 7) {
reverseVideo = true;
} else if (code == 27) {
reverseVideo = false;
} else if (code >= 30 && code <= 37) {
baseFgColour = (uint8_t)((baseFgColour & colour_gray) | (code - 30));
} else if (code == 39) {
baseFgColour = (uint8_t)(colour_red | colour_green | colour_blue | colour_gray);
} else if (code >= 40 && code <= 47) {
baseBgColour = (uint8_t)((baseBgColour & colour_gray) | (code - 40));
} else if (code == 49) {
baseBgColour = 0x00;
} else if (code >= 90 && code <= 97) {
baseFgColour = (uint8_t)(colour_gray | (code - 90));
} else if (code >= 100 && code <= 107) {
baseBgColour = (uint8_t)(colour_gray | (code - 100));
}
/* anything else (italics, underline, 256-colour, etc.) is silently ignored */
}
break;
}
default:
/* Unimplemented CSI command - ignore it */
break;
}
}
bool step() { bool step() {
usb_HandleEvents(); usb_HandleEvents();
if(has_srl_device) { if(has_srl_device) {
char in_buf[64]; char in_buf[1024];
/* Read up to 64 bytes from the serial buffer */ /* Read up to 1024 bytes from the serial buffer */
size_t bytes_read = srl_Read(&srl, in_buf, sizeof in_buf); size_t bytes_read = srl_Read(&srl, in_buf, sizeof in_buf);
if (bytes_read > 0) {
for (idx = 0; idx < bytes_read; idx++) {
char symbol = in_buf[idx];
/* Check for an error (e.g. device disconneced) */ /* --- Ctrl+V literal-next: bypass all parsing for exactly one byte --- */
if(bytes_read < 0) { if (literalNext) {
dbg_printf("error %d on srl_Read\n", bytes_read); literalNext = false;
has_srl_device = false; if (cursorX >= 40) {
} else if(bytes_read > 0) { cursorX = 0;
idx = 0; cursorY += 1;
char symbol; }
char escape_code[16]; if (cursorY > 25) {
bool bs = false; scroll();
while (idx < bytes_read) { }
symbol = in_buf[idx]; renderGoal[(40 * (cursorY+2)) + cursorX] = (uint16_t)((symbol << 8) | colourByte());
cursorX += 1;
continue;
}
/* --- escape sequence state machine --- */
if (termState == TERM_ESC) {
if (symbol == '[') {
termState = TERM_CSI;
csiParamCount = 0;
memset(csiParams, 0, sizeof csiParams);
csiPrivate = false;
} else if (symbol == ']') {
/* OSC string (e.g. window title) - swallow until terminator */
termState = TERM_OSC;
} else {
/* Unimplemented single-byte escape (ESC c, ESC 7, etc.) - discard */
termState = TERM_NORMAL;
}
continue;
}
if (termState == TERM_OSC) {
if (symbol == 0x07 || symbol == 0x1B) { /* BEL or ESC terminates OSC */
termState = TERM_NORMAL;
}
continue;
}
if (termState == TERM_CSI) {
if (symbol >= '0' && symbol <= '9') {
if (csiParamCount < MAX_CSI_PARAMS) {
int v = (csiParams[csiParamCount] * 10) + (symbol - '0');
csiParams[csiParamCount] = (v > 9999) ? 9999 : v;
}
} else if (symbol == ';') {
if (csiParamCount < MAX_CSI_PARAMS - 1) csiParamCount++;
} else if (symbol == '?' || symbol == '>' || symbol == '=') {
/* DEC private mode marker - parse but don't act on it */
csiPrivate = true;
} else if (symbol >= 0x40 && symbol <= 0x7E) {
if (!csiPrivate) {
handleEscapeCommand(symbol);
}
termState = TERM_NORMAL;
} else {
/* Stray byte we don't expect inside a CSI sequence - bail out */
termState = TERM_NORMAL;
}
continue;
}
if (symbol == 0x1B) { /* ESC */
termState = TERM_ESC;
continue;
}
if (symbol == 0x16) { /* Ctrl+V - next byte is drawn literally */
literalNext = true;
continue;
}
/* --- plain character / legacy control-character handling --- */
int increment_X = 1; int increment_X = 1;
if (symbol == 0x27) bool bs = false;
bool wasPrevCR = sawCR;
sawCR = false;
if (symbol == 0x7F) { if (symbol == 0x7F) {
symbol = 0x20; symbol = 0x20;
cursorX -= (cursorX>0); cursorX -= (cursorX>0);
increment_X = 0; increment_X = 0;
} }
if (symbol == 0x0D) { if (symbol == 0x0D) { /* CR - also treated as a full newline on its own */
cursorX = 0; cursorX = 0;
cursorY += 1; cursorY += 1;
symbol = 0x20; symbol = 0x20;
increment_X = 0; increment_X = 0;
sawCR = true;
}
if (symbol == 0x0A) { /* LF - next line; skip the move if a CR just did it */
if (!wasPrevCR) {
cursorX = 0;
cursorY += 1;
}
symbol = 0x20;
increment_X = 0;
} }
if (symbol == 0x05) { if (symbol == 0x05) {
srl_Write(&srl, "TerminalCE", 10); srl_Write(&srl, "TerminalCE", 10);
@@ -220,16 +474,15 @@ bool step() {
} }
if (cursorY > 25) { if (cursorY > 25) {
scroll(); scroll();
cursorY = 25;
} }
renderGoal[((40 * (cursorY+2)) + cursorX)] = (256*symbol+0xF0); renderGoal[(40 * (cursorY+2)) + cursorX] = (uint16_t)((symbol << 8) | colourByte());
if (!bs) { if (!bs) {
cursorX += increment_X; cursorX += increment_X;
} }
idx += 1;
} }
newTiles = true; newTiles = true;
} }
/*
kb_Scan(); kb_Scan();
idx8 = 0; idx8 = 0;
idx = 0; idx = 0;
@@ -249,9 +502,9 @@ bool step() {
} }
idx += 1; idx += 1;
} }
if (send[0] != 0) { if (send[0] != 0) {
srl_Write(&srl, send, 1); srl_Write(&srl, send, 1);
} }*/
} }
return !kb_On; return !kb_On;
} }
@@ -260,12 +513,10 @@ int renderTile(int indx) {
unsigned int tileY = indx / 40; unsigned int tileY = indx / 40;
unsigned int tileX = indx - ( 40 * tileY ); unsigned int tileX = indx - ( 40 * tileY );
uint16_t tileData = renderGoal[indx]; uint16_t tileData = renderGoal[indx];
uint8_t symbol = (uint8_t)((tileData & 0xFF00) >> 8); uint8_t symbol1 = (uint8_t)((tileData & 0xFF00) >> 8);
uint8_t colour = (uint8_t)(tileData & 0x00FF); uint8_t colour = (uint8_t)(tileData & 0x00FF);
uint8_t fgColour = 0x00; uint8_t bgColour = colour & 0x0F;
uint8_t bgColour = 0x00; uint8_t fgColour = (colour & 0xF0) >> 4;
bgColour = colour & 0x0F;
fgColour = (colour & 0xF0) >> 4;
uint8_t pixelX = 0; uint8_t pixelX = 0;
uint8_t pixelY = 0; uint8_t pixelY = 0;
uint32_t tile[8]; uint32_t tile[8];
@@ -275,9 +526,9 @@ int renderTile(int indx) {
int bit; int bit;
while (pixelY < 8) { while (pixelY < 8) {
if (canUseFont) { if (canUseFont) {
rowData = fontBytes[(8*symbol)+pixelY]; rowData = fontBytes[(8*symbol1)+pixelY];
} else { } else {
rowData = symbol; rowData = symbol1;
} }
row = 0; row = 0;
@@ -305,13 +556,20 @@ int renderTile(int indx) {
return 0; return 0;
} }
int draw() { int draw(int maxTiles) {
while (scrollAmount > 0) {
memcpy(lcd_Ram+2560, lcd_Ram+3840, 32000);
memcpy(renderCurrent+80, renderCurrent+120, 2000);
memset(lcd_Ram+34560, 0, 1280);
scrollAmount--;
}
int drawIdx = 0; int drawIdx = 0;
int renderedNow = 0; int renderedNow = 0;
if (newTiles == false) { if (newTiles == false) {
return 0; return 0;
} }
while (renderedNow < 160) { while (renderedNow < maxTiles) {
if (renderCurrent[drawIdx] != renderGoal[drawIdx]) { if (renderCurrent[drawIdx] != renderGoal[drawIdx]) {
exit_Code = renderTile(drawIdx); exit_Code = renderTile(drawIdx);
renderCurrent[drawIdx] = renderGoal[drawIdx]; renderCurrent[drawIdx] = renderGoal[drawIdx];
@@ -321,7 +579,6 @@ int draw() {
renderedNow += 1; renderedNow += 1;
} }
if (drawIdx > 1200) { if (drawIdx > 1200) {
newTiles = false;
return 0; return 0;
} }
drawIdx += 1; drawIdx += 1;
@@ -334,13 +591,16 @@ int main() {
if (exit_Code != 0 ) { if (exit_Code != 0 ) {
return exit_Code; return exit_Code;
} }
uint8_t cycleCounter = 0;
while (step()) { // No rendering allowed in step! while (step()) { // No rendering allowed in step!
exit_Code = draw(); // As little non-rendering logic as possible if (cycleCounter % 16 == 0) {
if (exit_Code != 0 ) { exit_Code = draw(1000); // As little non-rendering logic as possible
end(); if (exit_Code != 0 ) {
return exit_Code; end();
return exit_Code;
}
} }
cycleCounter++;
} }
return end(); return end();
} }