Compare commits
10
Commits
5330bb19b3
...
7e999db4ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e999db4ae | ||
|
|
55e85c3a38 | ||
|
|
090754310b | ||
|
|
98124e6245 | ||
|
|
51741348c9 | ||
|
|
14ebfba6b2 | ||
|
|
f3b8a1d00c | ||
|
|
8947c929b7 | ||
|
|
70e922c293 | ||
|
|
da7e923bc2 |
@@ -7,6 +7,7 @@
|
||||
*.obj
|
||||
*.elf
|
||||
*.src
|
||||
obj/*
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
@@ -39,6 +40,7 @@
|
||||
*.x86_64
|
||||
*.hex
|
||||
*.8xp
|
||||
bin/*
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# Makefile Options
|
||||
# ----------------------------
|
||||
|
||||
NAME = DEMO
|
||||
NAME = TERMCE
|
||||
ICON = icon.png
|
||||
DESCRIPTION = "CE C Toolchain Demo"
|
||||
DESCRIPTION = "Serial Terminal for CE"
|
||||
COMPRESSED = NO
|
||||
|
||||
CFLAGS = -Wall -Wextra -Oz
|
||||
@@ -12,11 +12,16 @@ CXXFLAGS = -Wall -Wextra -Oz
|
||||
|
||||
# ----------------------------
|
||||
|
||||
all: build-font
|
||||
|
||||
include $(shell cedev-config --makefile)
|
||||
|
||||
FONTPACKNAME = termFont
|
||||
FONTPACKNAME = TermFont
|
||||
FONTDIR = $(SRCDIR)/gfx
|
||||
DEPS = $(BINDIR)/$(FONTPACKNAME).8xv
|
||||
|
||||
$(BINDIR)/$(FONTPACKNAME).8xv: $(SRCDIR)/gfx/$(FONTPACKNAME).bin
|
||||
$(Q)convbin --iformat bin --input $(SRCDIR)/gfx/$(FONTPACKNAME).bin --oformat 8xv --output $(BINDIR)/$(FONTPACKNAME).8xv --archive --name $(FONTPACKNAME)
|
||||
SHELL := /bin/bash
|
||||
|
||||
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
|
||||
from PIL import Image
|
||||
import struct
|
||||
import struct, sys
|
||||
|
||||
tilesheetPNG = Image.open("TermFont.png")
|
||||
tilesheetBIN = open("TermFont.bin", "wb")
|
||||
print("[convert-fonttiles] converting file " + sys.argv[1] + " to binary file " + sys.argv[2])
|
||||
|
||||
tilesheetPNG = Image.open(sys.argv[1])
|
||||
tilesheetBIN = open(sys.argv[2], "wb")
|
||||
tilesheetPixels = tilesheetPNG.load()
|
||||
|
||||
tilesheetX, tilesheetY = tilesheetPNG.size
|
||||
@@ -16,7 +18,11 @@ def generateTile(x,y,image):
|
||||
for py in range(0,8):
|
||||
row = []
|
||||
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))
|
||||
tile.append(sum([int(row[b])*(2**(7-b)) for b in range(0,8)]))
|
||||
return tile
|
||||
@@ -27,3 +33,5 @@ for y in range(0,vertTiles):
|
||||
generatedTileData += generateTile(x,y,tilesheetPixels)
|
||||
|
||||
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 |
+301
-41
@@ -21,7 +21,7 @@ srl_device_t srl;
|
||||
|
||||
bool has_srl_device = false;
|
||||
|
||||
uint8_t srl_buf[512];
|
||||
uint8_t srl_buf[1024];
|
||||
|
||||
long* vram = (long*)lcd_Ram;
|
||||
|
||||
@@ -96,9 +96,56 @@ uint8_t colour_gray = 0b1000;
|
||||
|
||||
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;
|
||||
|
||||
@@ -157,7 +204,8 @@ int begin() {
|
||||
void scroll() {
|
||||
memcpy(renderGoal+80, renderGoal+120, 2000);
|
||||
memset(renderGoal+1080, 0, 80);
|
||||
newTiles = true;
|
||||
scrollAmount++;
|
||||
cursorY -= 1;
|
||||
}
|
||||
|
||||
int end() {
|
||||
@@ -171,36 +219,242 @@ int end() {
|
||||
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() {
|
||||
usb_HandleEvents();
|
||||
if(has_srl_device) {
|
||||
char in_buf[64];
|
||||
/* Read up to 64 bytes from the serial buffer */
|
||||
char in_buf[1024];
|
||||
/* Read up to 1024 bytes from the serial buffer */
|
||||
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) */
|
||||
if(bytes_read < 0) {
|
||||
dbg_printf("error %d on srl_Read\n", bytes_read);
|
||||
has_srl_device = false;
|
||||
} else if(bytes_read > 0) {
|
||||
idx = 0;
|
||||
char symbol;
|
||||
char escape_code[16];
|
||||
bool bs = false;
|
||||
while (idx < bytes_read) {
|
||||
symbol = in_buf[idx];
|
||||
/* --- Ctrl+V literal-next: bypass all parsing for exactly one byte --- */
|
||||
if (literalNext) {
|
||||
literalNext = false;
|
||||
if (cursorX >= 40) {
|
||||
cursorX = 0;
|
||||
cursorY += 1;
|
||||
}
|
||||
if (cursorY > 25) {
|
||||
scroll();
|
||||
}
|
||||
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;
|
||||
if (symbol == 0x27)
|
||||
bool bs = false;
|
||||
bool wasPrevCR = sawCR;
|
||||
sawCR = false;
|
||||
if (symbol == 0x7F) {
|
||||
symbol = 0x20;
|
||||
cursorX -= (cursorX>0);
|
||||
increment_X = 0;
|
||||
}
|
||||
if (symbol == 0x0D) {
|
||||
if (symbol == 0x0D) { /* CR - also treated as a full newline on its own */
|
||||
cursorX = 0;
|
||||
cursorY += 1;
|
||||
symbol = 0x20;
|
||||
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) {
|
||||
srl_Write(&srl, "TerminalCE", 10);
|
||||
@@ -220,16 +474,15 @@ bool step() {
|
||||
}
|
||||
if (cursorY > 25) {
|
||||
scroll();
|
||||
cursorY = 25;
|
||||
}
|
||||
renderGoal[((40 * (cursorY+2)) + cursorX)] = (256*symbol+0xF0);
|
||||
renderGoal[(40 * (cursorY+2)) + cursorX] = (uint16_t)((symbol << 8) | colourByte());
|
||||
if (!bs) {
|
||||
cursorX += increment_X;
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
newTiles = true;
|
||||
}
|
||||
/*
|
||||
kb_Scan();
|
||||
idx8 = 0;
|
||||
idx = 0;
|
||||
@@ -249,9 +502,9 @@ bool step() {
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
if (send[0] != 0) {
|
||||
srl_Write(&srl, send, 1);
|
||||
}
|
||||
if (send[0] != 0) {
|
||||
srl_Write(&srl, send, 1);
|
||||
}*/
|
||||
}
|
||||
return !kb_On;
|
||||
}
|
||||
@@ -260,12 +513,10 @@ int renderTile(int indx) {
|
||||
unsigned int tileY = indx / 40;
|
||||
unsigned int tileX = indx - ( 40 * tileY );
|
||||
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 fgColour = 0x00;
|
||||
uint8_t bgColour = 0x00;
|
||||
bgColour = colour & 0x0F;
|
||||
fgColour = (colour & 0xF0) >> 4;
|
||||
uint8_t bgColour = colour & 0x0F;
|
||||
uint8_t fgColour = (colour & 0xF0) >> 4;
|
||||
uint8_t pixelX = 0;
|
||||
uint8_t pixelY = 0;
|
||||
uint32_t tile[8];
|
||||
@@ -275,9 +526,9 @@ int renderTile(int indx) {
|
||||
int bit;
|
||||
while (pixelY < 8) {
|
||||
if (canUseFont) {
|
||||
rowData = fontBytes[(8*symbol)+pixelY];
|
||||
rowData = fontBytes[(8*symbol1)+pixelY];
|
||||
} else {
|
||||
rowData = symbol;
|
||||
rowData = symbol1;
|
||||
}
|
||||
row = 0;
|
||||
|
||||
@@ -305,13 +556,20 @@ int renderTile(int indx) {
|
||||
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 renderedNow = 0;
|
||||
if (newTiles == false) {
|
||||
return 0;
|
||||
}
|
||||
while (renderedNow < 160) {
|
||||
while (renderedNow < maxTiles) {
|
||||
if (renderCurrent[drawIdx] != renderGoal[drawIdx]) {
|
||||
exit_Code = renderTile(drawIdx);
|
||||
renderCurrent[drawIdx] = renderGoal[drawIdx];
|
||||
@@ -321,7 +579,6 @@ int draw() {
|
||||
renderedNow += 1;
|
||||
}
|
||||
if (drawIdx > 1200) {
|
||||
newTiles = false;
|
||||
return 0;
|
||||
}
|
||||
drawIdx += 1;
|
||||
@@ -334,13 +591,16 @@ int main() {
|
||||
if (exit_Code != 0 ) {
|
||||
return exit_Code;
|
||||
}
|
||||
|
||||
uint8_t cycleCounter = 0;
|
||||
while (step()) { // No rendering allowed in step!
|
||||
exit_Code = draw(); // As little non-rendering logic as possible
|
||||
if (exit_Code != 0 ) {
|
||||
end();
|
||||
return exit_Code;
|
||||
if (cycleCounter % 16 == 0) {
|
||||
exit_Code = draw(1000); // As little non-rendering logic as possible
|
||||
if (exit_Code != 0 ) {
|
||||
end();
|
||||
return exit_Code;
|
||||
}
|
||||
}
|
||||
cycleCounter++;
|
||||
}
|
||||
return end();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user