From fb0d5ad0ac3256c0cf3e5147ca4a273f91c69276 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 2 Nov 2021 21:30:56 -0500 Subject: [PATCH] Start of Terminal widget. --- LICENSE | 18 + client/Makefile.djgpp | 2 +- client/build.sh | 2 +- client/client.pro | 9 +- client/data/kanga.ans | 1 + client/src/gui/gui.c | 16 +- client/src/gui/gui.h | 5 +- client/src/gui/image.c | 1 + client/src/gui/terminal.c | 649 ++++++++++ client/src/gui/terminal.h | 97 ++ client/src/main.c | 44 +- client/src/thirdparty/serial/README.md | 48 + client/src/thirdparty/serial/serial.c | 1647 ++++++++++++++++++++++++ client/src/thirdparty/serial/serial.h | 195 +++ client/src/thirdparty/serial/term.C | 104 ++ 15 files changed, 2819 insertions(+), 19 deletions(-) create mode 100644 client/data/kanga.ans create mode 100644 client/src/gui/terminal.c create mode 100644 client/src/gui/terminal.h create mode 100644 client/src/thirdparty/serial/README.md create mode 100644 client/src/thirdparty/serial/serial.c create mode 100644 client/src/thirdparty/serial/serial.h create mode 100644 client/src/thirdparty/serial/term.C diff --git a/LICENSE b/LICENSE index f6a37fa..773cf6e 100644 --- a/LICENSE +++ b/LICENSE @@ -22,14 +22,32 @@ Licenses Used By: Client ====== +DOS Serial Library +------------------ +https://github.com/kstenerud/DOS-Serial-Library +Attribution + MemWatch +-------- http://www.linkdata.se/sourcecode/memwatch/ GPL2 +SDL2 +---- +https://www.libsdl.org/ +BSD 3-Clause + +SDL2_Net +-------- +https://www.libsdl.org/ +BSD 3-Clause + stb_ds.h +-------- https://github.com/nothings/stb Public Domain stb_image.h +----------- https://github.com/nothings/stb Public Domain diff --git a/client/Makefile.djgpp b/client/Makefile.djgpp index 1e8cc93..e9afa66 100644 --- a/client/Makefile.djgpp +++ b/client/Makefile.djgpp @@ -32,7 +32,7 @@ BINDIR = bin ## CHANGE THIS ## # CFLAGS, LDFLAGS, CPPFLAGS, PREFIX can be overriden on CLI -CFLAGS := $(DEBUG) -I$(SRCDIR) -I$(SRCDIR)/dos -I$(SRCDIR)/gui -I$(SRCDIR)/thirdparty -I$(SRCDIR)/thirdparty/memwatch +CFLAGS := $(DEBUG) -I$(SRCDIR) -I$(SRCDIR)/dos -I$(SRCDIR)/gui -I$(SRCDIR)/thirdparty -I$(SRCDIR)/thirdparty/memwatch -I$(SRCDIR)/thirdparty/serial CPPFLAGS := LDFLAGS := PREFIX := /usr/local diff --git a/client/build.sh b/client/build.sh index d521a8d..3cb3665 100755 --- a/client/build.sh +++ b/client/build.sh @@ -18,7 +18,7 @@ # along with this program. If not, see . # -mkdir -p bin obj/dos obj/gui obj/thirdparty/memwatch +mkdir -p bin obj/dos obj/gui obj/thirdparty/memwatch obj/thirdparty/serial source /opt/cross/djgpp/setenv make -f Makefile.djgpp rm bin/client diff --git a/client/client.pro b/client/client.pro index 36dbe9a..aed7ea2 100644 --- a/client/client.pro +++ b/client/client.pro @@ -21,9 +21,11 @@ CONFIG -= qt DESTDIR = $$OUT_PWD/bin -DOS_HEADERS = +DOS_HEADERS = \ + src/thirdparty/serial/serial.h DOS_SOURCES = \ + src/thirdparty/serial/serial.c \ src/dos/keyboard.c \ src/dos/mouse.c \ src/dos/vesa.c @@ -45,6 +47,7 @@ INCLUDEPATH += \ HEADERS = \ $$LINUX_HEADERS \ src/gui/listbox.h \ + src/gui/terminal.h \ src/gui/updown.h \ src/stddclmr.h \ src/thirdparty/stb_ds.h \ @@ -77,6 +80,7 @@ HEADERS = \ SOURCES = \ $$LINUX_SOURCES \ src/gui/listbox.c \ + src/gui/terminal.c \ src/gui/updown.c \ src/thirdparty/memwatch/memwatch.c \ src/gui/memory.c \ @@ -100,7 +104,8 @@ SOURCES = \ src/main.c LIBS = \ - -lSDL2 + -lSDL2 \ + -lSDL2_net OTHER_FILES = \ Makefile.djgpp \ diff --git a/client/data/kanga.ans b/client/data/kanga.ans new file mode 100644 index 0000000..8cfd748 --- /dev/null +++ b/client/data/kanga.ans @@ -0,0 +1 @@ +[?7hÛÜÜÜÜßÜßßÜÛÜßÛÜÜÜÜÜÜßßÜÛÜÜßßÜÜßßܲ²²²²²²²²²²²²²²²²ÛÛÛÛ²ÛÛܲÛÛÜÜÛßß²ÛÛÜÛÛÛÜÜÛ²²²±±±²²²²²²²²²±±±±±²²²²ÛÛßßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛ²²²²±±±±±±±±±±±±±±±±±±²²±±²²ÛÛßÛÜÛÛßßÛÛÛÛÛÛ²²ÞßÛÛÛÛßßÛÛÛÛÜÜß²²²²±±±±±±±±±±±±±±²±²²²±±±±²±±²²²²²Ü²²²²²²²²²²Üܲ²²²²²Ü²±²²±±±±±±±±°±±±±±±²²±²±±±±±±±²±±²ÛÛÝÛÛÛÛÛÛÛÛÛÛßÛ²²ÛÛÛÛÛÛ²±±²±±±±²²²±±±±±±±±²±±²±±±±±±±±±²±±²ßßßßÛßßßßÛÛßßÛÛßßß ßßßÛÛÜßß²±±²±±±±±±±²±±²±±±²²±±±±±±±±±±±±±±²±±²ÛÜÜßßÜÛÜÜÜÜÛÛÜßÛÜÜÜÜÜÛÜÜÜÜß²±±²±±±±±±±±±±±±±±²±²²²°±±±±±±±±±±²±±±²ÛÛÛÜÛÛÛÛ²ÛÛÜÜÛßßÛÛÛÛ²±±²±±±±±²±°±²²²²²±±±²±²°±±±±±±±±±±²±±²ÛÛÛßÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²±±²±±²°ÛÛÛÛ±±²±±°±±²²²±±±±±±±±±±±²±±²ÛÛÜÜßÛÛ±±ÛÛÛÛ²²ÛÛÜÜÜܲ±±²±±ÛÛÛÛÛÛ²²±°±ÛÛ±±²²²±±±±±±±±±±±±²±±²²²²²²²²²²²²²²²²²²±±²±ÛÛÛÛÛÛÛ²±°²²±Û±±±²±°°°±±±±±±±±±²±±²ÛÛßÛÛßÛÛÛÛßÛÛÛÛÛ²±±²±ÛÛÛÛÛÛ²±ÛÛÛÛÛÛÛÛÛÛÛÛ±°²±±±±±±±±²±±²ÜßßßÜÜßßßÛÛßßß ßßßßßß²±²²±²ÛÛ²²²ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²±±±±±±±²±±²ÜÜÜÜÜÛÜÜßßÜÜÛÜÜÜÜÛÛÛßßÜÜÜÜÜßßܲ±±²²±±±²±ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ²±±±±±±²±±²ÛÛ±ÛÛÜÛÛÛÛÛÛÛÜßÛÜÛÛܲ±±²²±±²±ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ°±²°±±²°±²ßßÜÜÜÛÛÛÛÛÛÛÛÛÛÜÛÜÛÛÛÛ²±±²²²²²²²ÛÛÛÛÛÛÛÛÛÛÛÛ²°°±²²±±±²²ßßܱ߱±ÛÛ±±ÛÛÛÛÛÛÛÛÛÛ²²±±±°°±²±±±±±±±±±±±±°±±²²²²±±Üܲ²²²²²²²²²²²²²²²²²Üܲ²²²²²²²±±±±±±±±±±±±²²²²²±±ßÛÛÛÛÛÜßÛÛßÛÛÛßÜÛÛßÛÛßßÛ°²±²²±²±±±ÛÛÛ±±²²²²²²²±±²ßßÛÛßßßÛßÜÜßÛÛÜÜßßßßÜÜßßßÛÛß±±±±²²²²²²²²²±²²²±±±±²±±²²±±²±±²²²²±± \ No newline at end of file diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 6d3c39e..832807e 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -29,6 +29,9 @@ int16_t _guiMetric[METRIC_COUNT]; ColorT _guiColor[COLOR_COUNT]; +FontT *_guiFont8 = NULL; +FontT *_guiFont14 = NULL; +FontT *_guiFont16 = NULL; FontT *_guiFont = NULL; WidgetT *_guiDragWidget = NULL; uint16_t _guiDragOffsetX = 0; @@ -533,7 +536,12 @@ DesktopT *guiStartup(void) { _guiColor[COLOR_LISTBOX_ARROWS_ACTIVE] = vbeMakeColor(168, 168, 168); _guiColor[COLOR_LISTBOX_ARROWS_INACTIVE] = vbeMakeColor( 80, 84, 80); - _guiFont = fontLoad("vga8x14.dat"); + // Load all font sizes. + _guiFont8 = fontLoad("vga8x8.dat"); + _guiFont14 = fontLoad("vga8x14.dat"); + _guiFont16 = fontLoad("vga8x16.dat"); + + _guiFont = _guiFont14; // Font to use for widgets. // Create desktop and return it. Remember it for later. _guiDesktop = desktopNew(); @@ -550,7 +558,11 @@ DesktopT *guiStartup(void) { void guiShutdown(void) { - fontUnload(&_guiFont); + // Unload fonts. + fontUnload(&_guiFont16); + fontUnload(&_guiFont14); + fontUnload(&_guiFont8); + _guiFont = NULL; // Delete all widgets in GUI tree. guiDelete((WidgetT **)&_guiDesktop); diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index 99f97c3..b25de68 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -51,7 +51,7 @@ enum MagicE { MAGIC_TEXTBOX, MAGIC_UPDOWN, MAGIC_LISTBOX, - //MAGIC_TERMINAL, + MAGIC_TERMINAL, //MAGIC_DROPDOWN, MAGIC_COUNT }; @@ -149,6 +149,9 @@ typedef struct WindowS WindowT; extern int16_t _guiMetric[METRIC_COUNT]; extern ColorT _guiColor[COLOR_COUNT]; extern FontT *_guiFont; +extern FontT *_guiFont8; +extern FontT *_guiFont14; +extern FontT *_guiFont16; extern WidgetT *_guiDragWidget; extern uint16_t _guiDragOffsetX; extern uint16_t _guiDragOffsetY; diff --git a/client/src/gui/image.c b/client/src/gui/image.c index 58b233c..6228931 100644 --- a/client/src/gui/image.c +++ b/client/src/gui/image.c @@ -48,6 +48,7 @@ ImageT *imageAllocate(uint16_t w, uint16_t h) { for (y=0; ypixels[y]); } + free(image->pixels); free(image); return NULL; } diff --git a/client/src/gui/terminal.c b/client/src/gui/terminal.c new file mode 100644 index 0000000..c88600a --- /dev/null +++ b/client/src/gui/terminal.c @@ -0,0 +1,649 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + + +// http://www.ansi-bbs.org/BANSI/bansi.txt +// http://ansi-bbs.org/ansi-bbs-core-server.html + + +#include "terminal.h" + + +#define TERMINAL_CELL_GET_FLAG(t,x,y,f) (((t)->cells[(x)][(y)].flags & (1 << (f))) != 0) +#define TERMINAL_CELL_SET_FLAG(t,x,y,f) ((t)->cells[(x)][(y)].flags |= (1 << (f))) +#define TERMINAL_CELL_CLEAR_FLAG(t,x,y,f) ((t)->cells[(x)][(y)].flags &= (~(1 << (f)))) + + +static int16_t terminalConvertToInt(char *number, int16_t defaultValue); +static void terminalDel(WidgetT **widget); +static void terminalMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); +static void terminalPaint(WidgetT *widget, RectT pos); +static void terminalSequenceReset(TerminalT *terminal); + + +void terminalBackgroundSet(TerminalT *terminal, uint8_t color) { + terminal->background = color; +} + + +static int16_t terminalConvertToInt(char *number, int16_t defaultValue) { + char *end = NULL; + int16_t result = (int16_t)strtol(number, &end, 10); + + if (end == NULL) result = defaultValue; + + return result; +} + + +void terminalCursorMove(TerminalT *terminal, uint16_t col, uint16_t row) { + + // Clamp X. + if (col < 1) { + terminal->cursorX = 1; + } else { + if (col > terminal->cols) { + terminal->cursorX = terminal->cols; + } else { + terminal->cursorX = col; + } + } + + // Clamp Y. + if (row < 1) { + terminal->cursorY = 1; + } else { + if (row > terminal->rows) { + terminal->cursorY = terminal->rows; + } else { + terminal->cursorY = row; + } + } +} + + +static void terminalDel(WidgetT **widget) { + TerminalT *t = (TerminalT *)*widget; + uint16_t x; + + terminalSequenceReset(t); + + for (x=0; xcols; x++) { + free(t->cells[x]); + } + free(t->cells); + + free(t); + t = NULL; +} + + +void terminalForegroundSet(TerminalT *terminal, uint8_t color) { + terminal->foreground = color; + terminal->bold = (color > 7); +} + + +WidgetT *terminalInit(WidgetT *widget, uint16_t cols, uint16_t rows) { + TerminalT *t = (TerminalT *)widget; + uint16_t x; + uint16_t y; + + t->base.delMethod = terminalDel; + t->base.paintMethod = terminalPaint; + t->base.mouseEventMethod = terminalMouseEvent; + t->font = _guiFont8; // Default font. Also see terminalNew. + t->cols = cols; + t->rows = rows; + t->cursorX = 1; + t->cursorY = 1; + t->saveX = 1; + t->saveY = 1; + t->bold = 0; + t->blink = 0; + t->reverse = 0; + t->background = TERMINAL_COLOR_BLACK; + t->foreground = TERMINAL_COLOR_LIGHT_GRAY; + t->escFound = 0; + t->inEscape = 0; + t->numberIndex = 0; + t->number[0] = 0; + t->parameters = NULL; + + // Allocate space for column data. + t->cells = (CellT **)malloc(sizeof(CellT *) * cols); + if (!t->cells) return NULL; + // Allocate space for row data. + for (x=0; xcells[x] = (CellT *)malloc(sizeof(CellT) * rows); + if (!t->cells[x]) { + for (y=0; ycells[y]); + } + free(t->cells); + return NULL; + } + } + + // Set up default palette. + t->palette[TERMINAL_COLOR_BLACK] = vbeMakeColor(0x00, 0x00, 0x00); + t->palette[TERMINAL_COLOR_BLUE] = vbeMakeColor(0x00, 0x00, 0xAA); + t->palette[TERMINAL_COLOR_GREEN] = vbeMakeColor(0x00, 0xAA, 0x00); + t->palette[TERMINAL_COLOR_CYAN] = vbeMakeColor(0x00, 0xAA, 0xAA); + t->palette[TERMINAL_COLOR_RED] = vbeMakeColor(0xAA, 0x00, 0x00); + t->palette[TERMINAL_COLOR_MAGENTA] = vbeMakeColor(0xAA, 0x00, 0xAA); + t->palette[TERMINAL_COLOR_BROWN] = vbeMakeColor(0xAA, 0x55, 0x00); + t->palette[TERMINAL_COLOR_LIGHT_GRAY] = vbeMakeColor(0xAA, 0xAA, 0xAA); + t->palette[TERMINAL_COLOR_DARK_GRAY] = vbeMakeColor(0x55, 0x55, 0x55); + t->palette[TERMINAL_COLOR_BRIGHT_BLUE] = vbeMakeColor(0x55, 0x55, 0xFF); + t->palette[TERMINAL_COLOR_BRIGHT_GREEN] = vbeMakeColor(0x55, 0xFF, 0x55); + t->palette[TERMINAL_COLOR_BRIGHT_CYAN] = vbeMakeColor(0x55, 0xFF, 0xFF); + t->palette[TERMINAL_COLOR_BRIGHT_RED] = vbeMakeColor(0xFF, 0x55, 0x55); + t->palette[TERMINAL_COLOR_BRIGHT_MAGENTA] = vbeMakeColor(0xFF, 0x55, 0xFF); + t->palette[TERMINAL_COLOR_YELLOW] = vbeMakeColor(0xFF, 0xFF, 0x55); + t->palette[TERMINAL_COLOR_WHITE] = vbeMakeColor(0xFF, 0xFF, 0xFF); + + // Default attributes is gray on black, no bold, no blink, and dirty. + for (y=0; ycells[x][y].character = ' '; + t->cells[x][y].background = TERMINAL_COLOR_BLACK; + t->cells[x][y].foreground = TERMINAL_COLOR_LIGHT_GRAY; + t->cells[x][y].flags = 0; + TERMINAL_CELL_SET_FLAG(t, x, y, TERMINAL_FLAG_DIRTY); + } + } + + return widget; +} + + +static void terminalMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) { + TerminalT *t = (TerminalT *)widget; + + (void)mouse; + + // Fire callback on mouse up. + if (event == MOUSE_EVENT_LEFT_UP) { + + } +} + + +TerminalT *terminalNew(uint16_t x, uint16_t y, uint16_t cols, uint16_t rows) { + TerminalT *terminal = (TerminalT *)malloc(sizeof(TerminalT)); + WidgetT *widget = NULL; + uint16_t w; + uint16_t h; + + if (!terminal) return NULL; + + // Default font. Also see terminalInit. + w = fontWidthGet(_guiFont8) * cols; + h = fontHeightGet(_guiFont8) * rows; + + widget = widgetInit(W(terminal), MAGIC_TERMINAL, x, y, w, h, 0, 0, 0, 0); + if (!widget) { + free(terminal); + return NULL; + } + + terminal = (TerminalT *)terminalInit((WidgetT *)terminal, cols, rows); + if (!terminal) { + free(terminal); + return NULL; + } + + return terminal; +} + + +static void terminalPaint(WidgetT *widget, RectT pos) { + TerminalT *t = (TerminalT *)widget; + uint16_t x; + uint16_t y; + uint16_t xp; + uint16_t yp; + char c[2]; + + if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) { + + c[1] = 0; + + yp = pos.y; + for (y=0; yrows; y++) { + xp = pos.x; + for (x=0; xcols; x++) { + if (TERMINAL_CELL_GET_FLAG(t, x, y, TERMINAL_FLAG_DIRTY)) { + c[0] = t->cells[x][y].character; + fontRender(t->font, c, t->palette[t->cells[x][y].foreground], t->palette[t->cells[x][y].background], xp, yp); + TERMINAL_CELL_CLEAR_FLAG(t, x, y, TERMINAL_FLAG_DIRTY); + } + xp += fontWidthGet(t->font); + } + yp += fontHeightGet(t->font); + } + + GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY); + } +} + + +void terminalPrint(TerminalT *terminal, char *string) { + uint16_t i; + + for (i=0; iescFound && !terminal->inEscape) { + terminal->escFound = 1; + return; + } + + // Find [. + if (c == '[' && terminal->escFound && !terminal->inEscape) { + terminal->inEscape = 1; + return; + } + + // Is this a sequence that is ESC and then not [ ? + if (terminal->escFound && !terminal->inEscape) { + terminalSequenceReset(terminal); + return; + } + + if (terminal->inEscape) { + switch (c) { + + case '[': + case 27: + // ESC sequence inside sequence. Invalid ANSIBBS. + //***TODO*** Can stick custom sequences here. + break; + + // End of a parameter + case ';': + arrput(terminal->parameters, strdup(terminal->number)); + terminal->number[0] = 0; + terminal->numberIndex = 0; + break; + + // Cursor up. + case 'A': + y = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY - y); + terminalSequenceReset(terminal); + break; + + // Cursor down. + case 'B': + y = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY + y); + terminalSequenceReset(terminal); + break; + + // Cursor forward. + case 'C': + x = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, terminal->cursorX + x, terminal->cursorY); + terminalSequenceReset(terminal); + break; + + // Cursor backward. + case 'D': + x = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, terminal->cursorX - x, terminal->cursorY); + terminalSequenceReset(terminal); + break; + + // Cursor line down. + case 'E': + y = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, 1, terminal->cursorY + y); + //***TODO*** This should allow scrolling. + terminalSequenceReset(terminal); + break; + + // Cursor line up. + case 'F': + y = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, 1, terminal->cursorY - y); + //***TODO*** This should allow scrolling. + terminalSequenceReset(terminal); + break; + + // Cursor horizontal absolute. + case 'G': + x = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, x, terminal->cursorY); + terminalSequenceReset(terminal); + break; + + // Cursor move. + case 'H': + case 'f': + switch (arrlen(terminal->parameters)) { + // Cursor vertical absolute. Kinda. Moves X to 1. + case 0: + y = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, 1, y); + break; + + // Absolute position. + case 1: + x = terminalConvertToInt(terminal->parameters[0], 1); + y = terminalConvertToInt(terminal->number, 1); + terminalCursorMove(terminal, x, y); + break; + + // Invalid nonsense. + default: + break; + } + terminalSequenceReset(terminal); + break; + + // Clear display. + case 'J': + if (arrlen(terminal->parameters) == 0 && terminalConvertToInt(terminal->number, 1) == 2) { + terminalScreenClear(terminal); + terminalCursorMove(terminal, 1, 1); + } + terminalSequenceReset(terminal); + break; + + // Clear from cursor to end of line. + case 'K': + x = terminalConvertToInt(terminal->number, 0); + if (x == 0) { + for (y=terminal->cursorX-1; ycols; y++) { + terminal->cells[y][terminal->cursorY - 1].character = ' '; + TERMINAL_CELL_SET_FLAG(terminal, y, terminal->cursorY - 1, TERMINAL_FLAG_DIRTY); + GUI_SET_FLAG((WidgetT *)terminal, WIDGET_FLAG_DIRTY); + } + } else { + //***TODO*** Unimplemented line clear. + } + terminalSequenceReset(terminal); + break; + + // Insert lines. + case 'L': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Delete lines. + case 'M': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Delete characters. + case 'P': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Scroll up. + case 'S': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Scroll down. + case 'T': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Clear screen with normal attribute. + case 'U': + x = terminal->background; + terminal->background = TERMINAL_COLOR_BLACK; + terminalScreenClear(terminal); + terminal->background = x; + terminalCursorMove(terminal, 1, 1); + terminalSequenceReset(terminal); + break; + + // Back-TAB. + case 'Z': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Set attributes. + case 'm': + arrput(terminal->parameters, strdup(terminal->number)); + for (p=0; pparameters); p++) { + x = terminalConvertToInt(terminal->parameters[p], 0); + switch (x) { + // Reset. + case 0: + terminal->bold = 0; + terminal->blink = 0; + terminal->reverse = 0; + terminal->foreground = TERMINAL_COLOR_LIGHT_GRAY; + terminal->background = TERMINAL_COLOR_BLACK; + break; + + // Bold. + case 1: + terminal->bold = 1; + break; + + // Blink slow. + case 5: + terminal->blink = TERMINAL_FLAG_BLINK_SLOW; + break; + + // Blink fast. + case 6: + terminal->blink = TERMINAL_FLAG_BLINK_FAST; + break; + + // Reverse. + case 7: + if (!terminal->reverse) { + x = terminal->foreground; + terminal->foreground = terminal->background; + terminal->background = x; + terminal->reverse = 1; + } + break; + + // Normal intensity. (22 is the actual code, unsure exactly what 21 is.) + case 21: + case 22: + terminal->bold = 0; + break; + + // Steady. + case 25: + terminal->blink = 0; + break; + + // Un-reverse? + case 27: + if (terminal->reverse) { + x = terminal->foreground; + terminal->foreground = terminal->background; + terminal->background = x; + terminal->reverse = 0; + } + break; + + // Color change. + default: + if (x > 29 && x < 38) terminal->foreground = x - 30; + if (x > 39 && x < 48) terminal->background = x - 40; + break; + + } // switch (x) + } + terminalSequenceReset(terminal); + break; + + // Define scroll region. + case 'r': + //***TODO*** Unimplemented. + terminalSequenceReset(terminal); + break; + + // Cursor save. + case 's': + terminal->saveX = terminal->cursorX; + terminal->saveY = terminal->cursorY; + terminalSequenceReset(terminal); + break; + + // Cursor restore. + case 'u': + terminalCursorMove(terminal, terminal->saveX, terminal->saveY); + terminalSequenceReset(terminal); + break; + + // Something else. + default: + // Number? + if (c >= '0' && c <= '9') { + terminal->number[terminal->numberIndex++] = c; + terminal->number[terminal->numberIndex] = 0; + } else { + // Unknown sequence. + terminalSequenceReset(terminal); + } + + } // switch (c) + + } else { // inEscape + + switch (c) { + + // Bell. + case 7: + //***TODO*** Unimplemented. + break; + + // Backspace. + case 8: + case 128: + //***TODO*** Unimplemented. + break; + + // TAB + case 9: + //***TODO*** Unimplemented. + break; + + // Line feed. + case 10: + terminal->cursorY++; + if (terminal->cursorY > terminal->rows) { + terminal->cursorY--; + //***TODO*** Scroll. + } + terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY); + break; + + // Clear screen. + case 12: + x = terminal->background; + terminal->background = TERMINAL_COLOR_BLACK; + terminalScreenClear(terminal); + terminal->background = x; + terminalCursorMove(terminal, 1, 1); + break; + + // Carrage return. + case 13: + terminalCursorMove(terminal, 1, terminal->cursorY); + break; + + // Non-special character. + default: + // Render character. + terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].character = c; + terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].background = terminal->background; + terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].foreground = terminal->foreground + (terminal->bold ? 8 : 0); + terminal->cells[terminal->cursorX - 1][terminal->cursorY - 1].flags = 0; + if (terminal->blink == TERMINAL_FLAG_BLINK_SLOW) TERMINAL_CELL_SET_FLAG(terminal, terminal->cursorX - 1, terminal->cursorY - 1, TERMINAL_FLAG_BLINK_SLOW); + if (terminal->blink == TERMINAL_FLAG_BLINK_FAST) TERMINAL_CELL_SET_FLAG(terminal, terminal->cursorX - 1, terminal->cursorY - 1, TERMINAL_FLAG_BLINK_FAST); + TERMINAL_CELL_SET_FLAG(terminal, terminal->cursorX - 1, terminal->cursorY - 1, TERMINAL_FLAG_DIRTY); + GUI_SET_FLAG((WidgetT *)terminal, WIDGET_FLAG_DIRTY); + // Move cursor. + terminal->cursorX++; + if (terminal->cursorX > terminal->cols) { + terminal->cursorX = 1; + terminal->cursorY++; + if (terminal->cursorY > terminal->rows) { + terminal->cursorY--; + //***TODO*** Scroll. + } + } + terminalCursorMove(terminal, terminal->cursorX, terminal->cursorY); + break; + + } // switch (c) + + } // inEscape +} + + +void terminalScreenClear(TerminalT *terminal) { + uint16_t x; + uint16_t y; + + // Does not move cursor. + // Should this clear the blink flags? + + for (y=0; yrows; y++) { + for (x=0; xcols; x++) { + terminal->cells[x][y].character = ' '; + TERMINAL_CELL_SET_FLAG(terminal, x, y, TERMINAL_FLAG_DIRTY); + } + } + GUI_SET_FLAG((WidgetT *)terminal, WIDGET_FLAG_DIRTY); + +} + + +static void terminalSequenceReset(TerminalT *terminal) { + terminal->escFound = 0; + terminal->inEscape = 0; + terminal->number[0] = 0; + terminal->numberIndex = 0; + + if (terminal->parameters != NULL) { + while (arrlen(terminal->parameters) > 0) { + free(arrpop(terminal->parameters)); + } + terminal->parameters = NULL; + } +} diff --git a/client/src/gui/terminal.h b/client/src/gui/terminal.h new file mode 100644 index 0000000..506d966 --- /dev/null +++ b/client/src/gui/terminal.h @@ -0,0 +1,97 @@ +/* + * Kangaroo Punch MultiPlayer Game Server Mark II + * Copyright (C) 2020-2021 Scott Duensing + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + + +#ifndef TERMINAL_H +#define TERMINAL_H + + +#include "gui.h" +#include "widget.h" + + +enum TerminalColorsE { + TERMINAL_COLOR_BLACK = 0, + TERMINAL_COLOR_BLUE, + TERMINAL_COLOR_GREEN, + TERMINAL_COLOR_CYAN, + TERMINAL_COLOR_RED, + TERMINAL_COLOR_MAGENTA, + TERMINAL_COLOR_BROWN, + TERMINAL_COLOR_LIGHT_GRAY, + TERMINAL_COLOR_DARK_GRAY, + TERMINAL_COLOR_BRIGHT_BLUE, + TERMINAL_COLOR_BRIGHT_GREEN, + TERMINAL_COLOR_BRIGHT_CYAN, + TERMINAL_COLOR_BRIGHT_RED, + TERMINAL_COLOR_BRIGHT_MAGENTA, + TERMINAL_COLOR_YELLOW, + TERMINAL_COLOR_WHITE, + TERMINAL_COLOR_COUNT +}; + +enum TerminalFlagsE { + TERMINAL_FLAG_DIRTY = 0, + TERMINAL_FLAG_BLINK_FAST, // Must be = 1, don't move! + TERMINAL_FLAG_BLINK_SLOW // Must be = 2, don't move! +}; + + +typedef struct CellS { + uint8_t character; + ColorT foreground; + ColorT background; + uint8_t flags; +} CellT; + +typedef struct TerminalS { + WidgetT base; // Must be first in every widget + FontT *font; + uint8_t foreground; + uint8_t background; + uint8_t bold; + uint8_t blink; + uint8_t reverse; + uint16_t cursorX; // Cursor positions are 1 based. + uint16_t cursorY; // Cursor positions are 1 based. + uint16_t saveX; // Cursor positions are 1 based. + uint16_t saveY; // Cursor positions are 1 based. + uint16_t cols; + uint16_t rows; + ColorT palette[TERMINAL_COLOR_COUNT]; + CellT **cells; + uint8_t escFound; + uint8_t inEscape; + char number[7]; // Maximum 6 digits / sign. + uint8_t numberIndex; + char **parameters; +} TerminalT; + + +void terminalBackgroundSet(TerminalT *terminal, uint8_t color); +void terminalCursorMove(TerminalT *terminal, uint16_t col, uint16_t row); +void terminalForegroundSet(TerminalT *terminal, uint8_t color); +WidgetT *terminalInit(WidgetT *widget, uint16_t cols, uint16_t rows); +TerminalT *terminalNew(uint16_t x, uint16_t y, uint16_t cols, uint16_t rows); +void terminalPrint(TerminalT *terminal, char *string); +void terminalPrintChar(TerminalT *terminal, uint8_t c); +void terminalScreenClear(TerminalT *terminal); + + +#endif // TERMINAL_H diff --git a/client/src/main.c b/client/src/main.c index a1c95f5..c9f7458 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -27,14 +27,12 @@ * - Methods that can change the width of a widget (such as setTitle) need to repaint the parent window as well * - Metrics, colors, etc. should be defined in each widget and not in GUI * - Widgets should support a "changed" callback that can cancel the change - * - Add pgup/pgdn to UpDown - * - Add home/end to Textbox * - Move drawing and surface code into it's own file * - Use LabelT in all widgets that have a label * - Find a light grey to replace white widget data areas * - No thumb in listbox scrollbar * - * - Random crash after adding listbox + * - Random crash after adding listbox - looks memwatch / task related */ @@ -61,6 +59,7 @@ #include "textbox.h" #include "updown.h" #include "listbox.h" +#include "terminal.h" void buttonClick(WidgetT *widget) { @@ -138,6 +137,7 @@ void test(void *data) { WindowT *w1 = NULL; WindowT *w2 = NULL; WindowT *w3 = NULL; + WindowT *w4 = NULL; ButtonT *b1 = NULL; LabelT *l1 = NULL; CheckboxT *c1 = NULL; @@ -149,20 +149,26 @@ void test(void *data) { RadioT *r3b = NULL; PictureT *p1 = NULL; FrameT *f1 = NULL; - TextboxT *t1 = NULL; - TextboxT *t2 = NULL; + TextboxT *tb1 = NULL; + TextboxT *tb2 = NULL; UpdownT *u1 = NULL; ListboxT *lb1 = NULL; + TerminalT *t1 = NULL; + FILE *in = NULL; + char *buffer = NULL; + uint16_t length = 0; (void)data; // Windows - w1 = windowNew(25, 25, 300, 200, "Window 1"); + w1 = windowNew(300, 25, 300, 200, "Window 1"); guiAttach(W(desktop), W(w1)); w2 = windowNew(150, 150, 300, 200, "Window 2"); guiAttach(W(desktop), W(w2)); w3 = windowNew(300, 300, 300, 200, "Window 3"); guiAttach(W(desktop), W(w3)); + w4 = windowNew(10, 10, 7 + 8 + (80 * 8), 26 + 8 + (24 * 8), "Terminal"); + guiAttach(W(desktop), W(w4)); // Window 1 p1 = pictureNew(0, 0, "kanga.png"); @@ -196,12 +202,12 @@ void test(void *data) { guiAttach(W(w2), W(r3b)); radioSetSelected(r1a); radioSetSelected(r2b); - t1 = textboxNew(10, 60, 265, "Test Textbox"); - textboxSetValue(t1, "Really long text string to edit!"); - guiAttach(W(w2), W(t1)); - t2 = textboxNew(10, 85, 265, "Test Textbox"); - textboxSetValue(t2, "Short string."); - guiAttach(W(w2), W(t2)); + tb1 = textboxNew(10, 60, 265, "Test Textbox"); + textboxSetValue(tb1, "Really long text string to edit!"); + guiAttach(W(w2), W(tb1)); + tb2 = textboxNew(10, 85, 265, "Test Textbox"); + textboxSetValue(tb2, "Short string."); + guiAttach(W(w2), W(tb2)); u1 = updownNew(10, 110, 0, 1024, 5, "UpDown"); guiAttach(W(w2), W(u1)); @@ -214,6 +220,20 @@ void test(void *data) { guiAttach(W(f1), W(l1)); c1 = checkboxNew(10, 65, "Test Checkbox"); guiAttach(W(f1), W(c1)); + + // Window 4 - Terminal + t1 = terminalNew(0, 0, 80, 24); + guiAttach(W(w4), W(t1)); + in = fopen("kanga.ans", "rt"); + fseek(in, 0, SEEK_END); + length = ftell(in); + fseek(in, 0, SEEK_SET); + buffer = (char *)malloc(length + 1); + fread(buffer, 1, length, in); + fclose(in); + buffer[length] = 0; + terminalPrint(t1, buffer); + free(buffer); } diff --git a/client/src/thirdparty/serial/README.md b/client/src/thirdparty/serial/README.md new file mode 100644 index 0000000..6129a6b --- /dev/null +++ b/client/src/thirdparty/serial/README.md @@ -0,0 +1,48 @@ +DOS Serial Library +================== + +A serial port (UART) library for DOS written back in the days when 16-bit +systems were still a going concern and far pointers were a thing. + +I'm putting it on Github because I still think it's way cool, and who knows... +Perhaps someone, somewhere, is in dire need of a serial library for DOS (I've +needed it from time to time to deal with some DOS based embedded piece of crap). + +Supports simultaneous communication over up to 4 serial ports. + +Have a look at term.c for an example of how to use it. + + + +How do I build it? +================== + +Good question! It was built to compile under Borland C++ 3 and whatever +Microsoft compiler happened to be current at the time. It conforms to ANSI C +(1989) and it doesn't do anything too crazy, so it shouldn't be too bad in a +more modern compiler. Have a go and let me know how it works! + + + +License +======= + +Copyright 1998 Karl Stenerud + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/client/src/thirdparty/serial/serial.c b/client/src/thirdparty/serial/serial.c new file mode 100644 index 0000000..2cfcd6f --- /dev/null +++ b/client/src/thirdparty/serial/serial.c @@ -0,0 +1,1647 @@ +#include +#include +#include "serial.h" + +#if defined( __DJGPP__ ) +#include +#include +#include +#define Interrupt +#define Far +#define Farpeekw(s, o) _farpeekw((s),(o)) +#define _Outp(a,b) (outp(a,b),(b)) +#define _Outpw(a,w) (outpw(a,w),(w)) +#define CPU_DISABLE_INTERRUPTS() asm("CLI") +#define CPU_ENABLE_INTERRUPTS() asm("STI") +#elif defined( _MSC_VER ) || defined( __WATCOMC__ ) +#define Interrupt __interrupt +#define Far __far +#define Farpeekw(s, o) (*((unsigned short Far*)(MK_FP((s), (o))))) +#define _Outp(a,b) outp(a,b) +#define _Outpw(a,w) outpw(a,w) +#define CPU_DISABLE_INTERRUPTS() __asm CLI +#define CPU_ENABLE_INTERRUPTS() __asm STI +#else /* __BORLANDC__ */ +#define Interrupt interrupt +#define Far far +#define Farpeekw(s, o) (*((unsigned short Far*)(MK_FP((s), (o))))) +#define _Outp(a,b) outp(a,b) +#define _Outpw(a,w) outpw(a,w) +#define CPU_DISABLE_INTERRUPTS() asm CLI +#define CPU_ENABLE_INTERRUPTS() asm STI +#endif /* _MSC_VER */ +#if defined( __386__ ) || defined( __DJGPP__ ) +#define PROTECTED_MODE +#endif + +/* ======================================================================== */ +/* =========================== DEFINES & MACROS =========================== */ +/* ======================================================================== */ + + +/* Tweak Values */ +#define SER_RX_BUFFER_SIZE_BITS 11 /* 2048 */ +#define SER_RX_BUFFER_HIGH_PERCENT 80 +#define SER_RX_BUFFER_LOW_PERCENT 20 + +#define SER_TX_BUFFER_SIZE_BITS 11 /* 2048 */ +#define SER_TX_BUFFER_HIGH_PERCENT 80 +#define SER_TX_BUFFER_LOW_PERCENT 20 + +#define UART_FIFO_DEFAULT_THRESHOLD 14 /* 14, 8, 4, 1, or 0 (off) */ + +#define COM1_DEFAULT_IRQ 4 +#define COM2_DEFAULT_IRQ 3 +#define COM3_DEFAULT_IRQ 4 +#define COM4_DEFAULT_IRQ 3 + +#define COM_MIN COM_1 +#define COM_MAX COM_4 + +#define IRQ_MIN 3 +#define IRQ_MAX 15 +#define IRQ_NONE 0xff + + +/* Receive and transmit buffers + * + * Both head and tail work in pre-increment mode: + * + * H->W->W->W + * * * * + * 0 1 2 3 4 5 ... + * * * * + * T->R->R->R + * + * Buffer empty: + * + * H + * ... 4 5 6 7 ... + * T + * + * Buffer full: + * + * H + * ... 4 5 6 7 ... + * T + */ + +#define SER_RX_BUFFER_SIZE (1L<rx_buff[(C)->rx_tail = ((C)->rx_tail+1) & SER_RX_BUFFER_SIZE_MASK] +#define SER_RX_BUFFER_WRITE(C, D) (C)->rx_buff[(C)->rx_head = ((C)->rx_head+1) & SER_RX_BUFFER_SIZE_MASK] = D +#define SER_RX_BUFFER_INIT(C) (C)->rx_head = (C)->rx_tail = 0 +#define SER_RX_BUFFER_EMPTY(C) ((C)->rx_head == (C)->rx_tail) +#define SER_RX_BUFFER_FULL(C) ((((C)->rx_head+1) & SER_RX_BUFFER_SIZE_MASK) == (C)->rx_tail) +#define SER_RX_BUFFER_CURRENT(C) (((C)->rx_head - (C)->rx_tail) & SER_RX_BUFFER_SIZE_MASK) +#define SER_RX_BUFFER_LOWATER(C) (SER_RX_BUFFER_CURRENT(C) < SER_RX_BUFFER_LOW) +#define SER_RX_BUFFER_HIWATER(C) (SER_RX_BUFFER_CURRENT(C) > SER_RX_BUFFER_HIGH) + +#define SER_TX_BUFFER_SIZE (1L<tx_buff[(C)->tx_tail = ((C)->tx_tail+1) & SER_TX_BUFFER_SIZE_MASK] +#define SER_TX_BUFFER_WRITE(C, D) (C)->tx_buff[(C)->tx_head = ((C)->tx_head+1) & SER_TX_BUFFER_SIZE_MASK] = D +#define SER_TX_BUFFER_INIT(C) (C)->tx_head = (C)->tx_tail = 0 +#define SER_TX_BUFFER_EMPTY(C) ((C)->tx_head == (C)->tx_tail) +#define SER_TX_BUFFER_FULL(C) ((((C)->tx_head+1) & SER_TX_BUFFER_SIZE_MASK) == (C)->tx_tail) +#define SER_TX_BUFFER_CURRENT(C) (((C)->tx_head - (C)->tx_tail) & SER_TX_BUFFER_SIZE_MASK) +#define SER_TX_BUFFER_LOWATER(C) (SER_TX_BUFFER_CURRENT(C) < SER_TX_BUFFER_LOW) +#define SER_TX_BUFFER_HIWATER(C) (SER_TX_BUFFER_CURRENT(C) > SER_TX_BUFFER_HIGH) + + +/* XON/XOFF Flow Control Commands */ +#define SER_XON 0x11 +#define SER_XOFF 0x13 + + +/* PIC Registers & interrupts */ +#define INTERRUPT_VECTOR_OFFSET 8 + +#define PIC_MASTER 0x20 +#define PIC_SLAVE 0xa0 +#define PIC_EOI 0x20 +#define PIC_RR 0x02 + +#define PIC_WRITE_IMR(P, D) outp((P)|1, D) /* interrupt masks */ +#define PIC_WRITE_OCW2(P, D) outp(P, D) /* R, SL, EOI, 0, 0, L2, L1, L0 */ +#define PIC_WRITE_OCW3(P, D) outp(P, (D)|8) /* 0, ESMM, SMM, 0, 1, P, RR, RIS */ +#define PIC_READ_IMR(P) inp((P)|1) /* interrupt masks */ +#define PIC_END_INTERRUPT(P) PIC_WRITE_OCW2(P, PIC_EOI) +static unsigned char PIC_READ_IRR(unsigned int port){PIC_WRITE_OCW3(port, PIC_RR); return inp(port);} +#define PIC_ENABLE_IRQ(I) {if(I <= 7) PIC_WRITE_IMR(PIC_MASTER, PIC_READ_IMR(PIC_MASTER) & ~(1 << I)); \ + else PIC_WRITE_IMR(PIC_SLAVE, PIC_READ_IMR(PIC_SLAVE) & ~(1 << ((I)-7)));} +#define PIC_DISABLE_IRQ(I) {if(I <= 7) PIC_WRITE_IMR(PIC_MASTER, PIC_READ_IMR(PIC_MASTER) | (1 << I)); \ + else PIC_WRITE_IMR(PIC_SLAVE, PIC_READ_IMR(PIC_SLAVE) | (1 << ((I)-7)));} + + +/* UART Registers */ +#define UART_RX_BUFFER 0 +#define UART_TX_BUFFER 0 +#define UART_INTERRUPT_ENABLE 1 + +#define UART_DIVISOR_LATCH_WORD 0 +#define UART_DIVISOR_LATCH_LSB 0 +#define UART_DIVISOR_LATCH_MSB 1 + +#define UART_INTERRUPT_IDENTIFY 2 +#define UART_FIFO_CONTROL 2 +#define UART_LINE_CONTROL 3 +#define UART_MODEM_CONTROL 4 +#define UART_LINE_STATUS 5 +#define UART_MODEM_STATUS 6 + + +/* UART Read commands (B = UART Base Address ) */ +#define UART_READ_DATA(C) inp((C)->base+UART_RX_BUFFER) +#define UART_READ_INTERRUPT_ENABLE(C) inp((C)->base+UART_INTERRUPT_ENABLE) +#define UART_READ_INTERRUPT_IDENTIFY(C) inp((C)->base+UART_INTERRUPT_IDENTIFY) +#define UART_READ_LINE_CONTROL(C) inp((C)->base+UART_LINE_CONTROL) +#define UART_READ_MODEM_CONTROL(C) inp((C)->base+UART_MODEM_CONTROL) +#define UART_READ_LINE_STATUS(C) ((C)->lsr = inp((C)->base+UART_LINE_STATUS)) +#define UART_READ_MODEM_STATUS(C) ((C)->msr = inp((C)->base+UART_MODEM_STATUS)) +#define UART_READ_BPS(C) ((_Outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) | UART_LCR_DIVISOR_LATCH) & 0) | \ + inpw((C)->base+UART_DIVISOR_LATCH_WORD) | \ + (_Outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) & ~UART_LCR_DIVISOR_LATCH) & 0)) + + +/* UART Write Commands (B = UART Base Address , D = Data ) */ +#define UART_WRITE_DATA(C, D) outp((C)->base+UART_TX_BUFFER, D) +#define UART_WRITE_INTERRUPT_ENABLE(C, D) ((C)->ier = _Outp((C)->base+UART_INTERRUPT_ENABLE, D)) +#define UART_WRITE_FIFO_CONTROL(C, D) ((C)->fcr = _Outp((C)->base+UART_FIFO_CONTROL, D)) +#define UART_WRITE_LINE_CONTROL(C, D) ((C)->lcr = _Outp((C)->base+UART_LINE_CONTROL, D)) +#define UART_WRITE_MODEM_CONTROL(C, D) ((C)->mcr = _Outp((C)->base+UART_MODEM_CONTROL, D)) +#define UART_WRITE_BPS(C, D) {outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) | UART_LCR_DIVISOR_LATCH); \ + (C)->dlatch = _Outpw((C)->base+UART_DIVISOR_LATCH_WORD, D); \ + outp((C)->base+UART_LINE_CONTROL, inp((C)->base+UART_LINE_CONTROL) & ~UART_LCR_DIVISOR_LATCH);} + + +/* Interrupt Enable Register Components */ +#define UART_IER_MODEM_STATUS 0x08 +#define UART_IER_ERRORS 0x04 +#define UART_IER_TX_HOLD_EMPTY 0x02 +#define UART_IER_DATA_READY 0x01 + + +/* Interrupt Identify Register Components */ +#define UART_IIR_FIFO_ENABLE_1 0x80 +#define UART_IIR_FIFO_ENABLE_0 0x40 +#define UART_IIR_IDENTIFY_2 0x08 +#define UART_IIR_IDENTIFY_1 0x04 +#define UART_IIR_IDENTIFY_0 0x02 +#define UART_IIR_NO_INTERRUPT_PENDING 0x01 + +#define UART_IIR_MASK (UART_IIR_NO_INTERRUPT_PENDING | UART_IIR_IDENTIFY_0 | UART_IIR_IDENTIFY_1) +#define UART_IIR_LINE_STATUS (UART_IIR_IDENTIFY_0 | UART_IIR_IDENTIFY_1) +#define UART_IIR_DATA_READY UART_IIR_IDENTIFY_1 +#define UART_IIR_TX_HOLD_EMPTY UART_IIR_IDENTIFY_0 +#define UART_IIR_MODEM_STATUS 0x00 +#define UART_IIR_NO_INTERRUPT UART_IIR_NO_INTERRUPT_PENDING + + +/* Fifo Control Register Components */ +#define UART_FCR_TRIGGER_1 0x80 +#define UART_FCR_TRIGGER_0 0x40 +#define UART_FCR_DMA_SELECT 0x08 +#define UART_FCR_TX_FIFO_RESET 0x04 +#define UART_FCR_RX_FIFO_RESET 0x02 +#define UART_FCR_FIFO_ENABLE 0x01 + +#define UART_FCR_TRIGGER_AT_1 0x00 +#define UART_FCR_TRIGGER_AT_4 UART_FCR_TRIGGER_0 +#define UART_FCR_TRIGGER_AT_8 UART_FCR_TRIGGER_1 +#define UART_FCR_TRIGGER_AT_14 (UART_FCR_TRIGGER_0 | UART_FCR_TRIGGER_1) + + +/* Line Control Register Components */ + +#define UART_LCR_DIVISOR_LATCH 0x80 +#define UART_LCR_SET_BREAK 0x40 +#define UART_LCR_STICK_PARITY 0x20 +#define UART_LCR_EVEN_PARITY 0x10 +#define UART_LCR_PARITY_ENABLE 0x08 +#define UART_LCR_STOPBITS 0x04 +#define UART_LCR_WORD_LENGTH_1 0x02 +#define UART_LCR_WORD_LENGTH_0 0x01 + +#define UART_DATA_5 0x00 +#define UART_DATA_6 UART_LCR_WORD_LENGTH_0 +#define UART_DATA_7 UART_LCR_WORD_LENGTH_1 +#define UART_DATA_8 (UART_LCR_WORD_LENGTH_0 | UART_LCR_WORD_LENGTH_1) +#define UART_DATA_MASK (UART_LCR_WORD_LENGTH_0 | UART_LCR_WORD_LENGTH_1) + +#define UART_PARITY_NONE 0x00 +#define UART_PARITY_ODD UART_LCR_PARITY_ENABLE +#define UART_PARITY_EVEN (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY) +#define UART_PARITY_MARK (UART_LCR_PARITY_ENABLE | UART_LCR_STICK_PARITY) +#define UART_PARITY_SPACE (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY | UART_LCR_STICK_PARITY) +#define UART_PARITY_MASK (UART_LCR_PARITY_ENABLE | UART_LCR_EVEN_PARITY | UART_LCR_STICK_PARITY) + +#define UART_STOP_1 0x00 +#define UART_STOP_2 UART_LCR_STOPBITS +#define UART_STOP_MASK UART_LCR_STOPBITS + + +/* Modem Control Register Components */ +#define UART_MCR_LOOPBACK 0x10 +#define UART_MCR_OUT2 0x08 +#define UART_MCR_OUT1 0x04 +#define UART_MCR_RTS 0x02 +#define UART_MCR_DTR 0x01 +#define UART_MCR_MASK ( UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT1 | UART_MCR_OUT2 | UART_MCR_LOOPBACK ) + + +/* Line Status Register Components */ +#define UART_LSR_FIFO_ERROR 0x80 +#define UART_LSR_TX_EMPTY 0x40 +#define UART_LSR_TX_HOLD_EMPTY 0x20 +#define UART_LSR_BREAK_INDICATOR 0x10 +#define UART_LSR_FRAME_ERROR 0x08 +#define UART_LSR_PARITY_ERROR 0x04 +#define UART_LSR_OVERRUN_ERROR 0x02 +#define UART_LSR_DATA_READY 0x01 + + +/* Modem Status Register Components */ +#define UART_MSR_DCD 0x80 +#define UART_MSR_RI 0x40 +#define UART_MSR_DSR 0x20 +#define UART_MSR_CTS 0x10 +#define UART_MSR_DLT_DCD 0x08 +#define UART_MSR_NEW_RING 0x04 +#define UART_MSR_DLT_DSR 0x02 +#define UART_MSR_DLT_CTS 0x01 + + +/* BPS Rates (based on 1.8432 MHz crystal) */ +#define UART_BPS_DIVISOR_50 2304 +#define UART_BPS_DIVISOR_75 1536 +#define UART_BPS_DIVISOR_110 1047 /* 0.026% error */ +#define UART_BPS_DIVISOR_134_5 857 /* 0.058% error */ +#define UART_BPS_DIVISOR_150 768 +#define UART_BPS_DIVISOR_300 384 +#define UART_BPS_DIVISOR_600 192 +#define UART_BPS_DIVISOR_1200 96 +#define UART_BPS_DIVISOR_1800 64 +#define UART_BPS_DIVISOR_2400 48 +#define UART_BPS_DIVISOR_3800 32 +#define UART_BPS_DIVISOR_4800 24 +#define UART_BPS_DIVISOR_7200 16 +#define UART_BPS_DIVISOR_9600 12 +#define UART_BPS_DIVISOR_19200 6 +#define UART_BPS_DIVISOR_38400 3 +#define UART_BPS_DIVISOR_57600 2 +#define UART_BPS_DIVISOR_115200 1 + +/* FIFO size */ +#define UART_FIFO_SIZE_IN_BYTES 16 + +/* ======================================================================== */ +/* ==================== PROTOTYPES, TYPEDEFS & GLOBALS ==================== */ +/* ======================================================================== */ + + +typedef struct +{ + unsigned char port; + unsigned char default_irq; + unsigned char irq; + unsigned char open; + unsigned char ier; + unsigned char fcr; + unsigned char lcr; + unsigned char mcr; + unsigned char lsr; + unsigned char msr; + unsigned char flow_mode; + unsigned char rx_flow_on; + unsigned char tx_flow_on; + unsigned char rx_buff[(unsigned short)SER_RX_BUFFER_SIZE]; + unsigned char tx_buff[(unsigned short)SER_TX_BUFFER_SIZE]; + unsigned int base; + unsigned int dlatch; + unsigned int rx_head; + unsigned int tx_head; + unsigned int rx_tail; + unsigned int tx_tail; +} serial_struct; + + +#if defined( __DJGPP__ ) +typedef _go32_dpmi_seginfo int_handler_ptr; +static _go32_dpmi_seginfo g_old_isrs[16]; +#else +typedef void Interrupt Far (*int_handler_ptr)(void); +static int_handler_ptr g_old_isrs[16]; +#endif +static unsigned char g_isrs_taken[16] = {0}; +#ifdef PROTECTED_MODE +static int_handler_ptr g_isr_addr; +static unsigned int g_isrs_count = 0; +#endif + + +/* serial port data */ +static serial_struct g_comports[COM_MAX+1] = +{ +#if defined( __GNUC__ ) + {port: 0, default_irq: COM1_DEFAULT_IRQ, irq: IRQ_NONE}, + {port: 1, default_irq: COM2_DEFAULT_IRQ, irq: IRQ_NONE}, + {port: 2, default_irq: COM3_DEFAULT_IRQ, irq: IRQ_NONE}, + {port: 3, default_irq: COM4_DEFAULT_IRQ, irq: IRQ_NONE} +#else + {0, COM1_DEFAULT_IRQ, IRQ_NONE}, + {1, COM2_DEFAULT_IRQ, IRQ_NONE}, + {2, COM3_DEFAULT_IRQ, IRQ_NONE}, + {3, COM4_DEFAULT_IRQ, IRQ_NONE} +#endif +}; + + + +/* ======================================================================== */ +/* ====================== INTERRUPT SERVICE ROUTINE ======================= */ +/* ======================================================================== */ + +static void Interrupt com_general_isr(void) +{ + serial_struct* com_min = (serial_struct*)(g_comports); + serial_struct* com_max = com_min + COM_MAX; + unsigned char int_id; + unsigned char data; + unsigned char slave_interrupted = 0; + serial_struct* com; + + /* Disable IRQs for all COM ports, then re-enable interrupts. + * This allows other faster devices to be serviced. + */ + for(com=com_min;com<=com_max;com++) + { + if(com->open) + { + if(com->irq > 7) + slave_interrupted = 1; + PIC_DISABLE_IRQ(com->irq); + } + } + CPU_ENABLE_INTERRUPTS(); + + /* Process all pending interrupts */ + for(com=com_min;com<=com_max;com++) + { + if(com->open) + { + while((int_id=UART_READ_INTERRUPT_IDENTIFY(com) & UART_IIR_MASK) != UART_IIR_NO_INTERRUPT) + { + switch(int_id) + { + case UART_IIR_DATA_READY: + /* Read all data from the UART */ + while(UART_READ_LINE_STATUS(com) & UART_LSR_DATA_READY) + { + data = UART_READ_DATA(com); + + /* Handle XON/XOFF flow control (TX) */ + if(com->flow_mode == SER_HANDSHAKING_XONXOFF && (data == SER_XOFF || data == SER_XON)) + { + com->tx_flow_on = data == SER_XON; + if(!SER_TX_BUFFER_EMPTY(com) && com->tx_flow_on) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY); + } + + /* Store it if there's room, or throw it out */ + else if(!SER_RX_BUFFER_FULL(com)) + { + SER_RX_BUFFER_WRITE(com, data); + + /* Flow control (RX) - Turn off if buffer almost full */ + if(com->rx_flow_on && SER_RX_BUFFER_HIWATER(com)) + { + com->rx_flow_on = 0; + + switch(com->flow_mode) + { + case SER_HANDSHAKING_RTSCTS: + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) & ~UART_MCR_RTS); + break; + case SER_HANDSHAKING_NONE: + break; + case SER_HANDSHAKING_XONXOFF: + UART_WRITE_DATA(com, SER_XOFF); + break; + case SER_HANDSHAKING_DTRDSR: + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) & ~UART_MCR_DTR); + break; + } + } + } + } + break; + /* Change in line status */ + case UART_IIR_LINE_STATUS: + UART_READ_LINE_STATUS(com); + break; + /* Change in modem status */ + case UART_IIR_MODEM_STATUS: + UART_READ_MODEM_STATUS(com); + + /* Handle RTS/CTS or DSR/DTR flow control (TX) */ + if(com->flow_mode == SER_HANDSHAKING_RTSCTS) + com->tx_flow_on = (com->msr & UART_MSR_CTS) != 0; + else if(com->flow_mode == SER_HANDSHAKING_DTRDSR) + com->tx_flow_on = (com->msr & UART_MSR_DSR) != 0; + if(!SER_TX_BUFFER_EMPTY(com) && com->tx_flow_on) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY); + break; + /* UART is empty */ + case UART_IIR_TX_HOLD_EMPTY: + { + int cnt; + for (cnt=0; cnttx_flow_on && !SER_TX_BUFFER_EMPTY(com); cnt++) + UART_WRITE_DATA(com, SER_TX_BUFFER_READ(com)); + if(SER_TX_BUFFER_EMPTY(com) || !com->tx_flow_on) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) & ~UART_IER_TX_HOLD_EMPTY); + break; + } + } + } + } + } + + CPU_DISABLE_INTERRUPTS(); + + /* End the interrupt on the PIC */ + if(slave_interrupted) + PIC_END_INTERRUPT(PIC_SLAVE); + PIC_END_INTERRUPT(PIC_MASTER); + + /* Re-enable all interrupts */ + for(com=com_min;com<=com_max;com++) + if(com->open) + PIC_ENABLE_IRQ(com->irq); + + /* We must explicitely call 'sti' before 'iret' because 'iret' + won't always restore interrupts in a virtual environment */ + CPU_ENABLE_INTERRUPTS(); +} + + + +/* ======================================================================== */ +/* =========================== UTILITY ROUTINES =========================== */ +/* ======================================================================== */ + + +#ifdef PROTECTED_MODE + +/* we do not know the exact size of com_general_isr() function + but for sure it's not longer then 2048 bytes */ +#define ISR_SIZE 2048 + +#if defined( __DJGPP__ ) + +static void serial_dpmi_get_pvect(int vector, _go32_dpmi_seginfo *info) +{ + _go32_dpmi_get_protected_mode_interrupt_vector(vector, info); +} + +static void serial_dpmi_set_pvect(int vector, _go32_dpmi_seginfo *info) +{ + _go32_dpmi_set_protected_mode_interrupt_vector(vector, info); +} + +static int serial_dpmi_lock_memory(void) +{ + unsigned long dataaddr, codeaddr; + __dpmi_meminfo dataregion, coderegion; + + if(__dpmi_get_segment_base_address(_my_cs(), &codeaddr) == 0 && + __dpmi_get_segment_base_address(_my_ds(), &dataaddr) == 0) + { + coderegion.handle = 0; + coderegion.size = ISR_SIZE; + coderegion.address = codeaddr + (unsigned long)com_general_isr; + dataregion.handle = 0; + dataregion.size = sizeof(g_comports); + dataregion.address = codeaddr + (unsigned long)g_comports; + if(__dpmi_lock_linear_region(&coderegion) == 0) + { + if(__dpmi_lock_linear_region(&dataregion) == 0) + { + g_isr_addr.pm_offset = (unsigned long)com_general_isr; + g_isr_addr.pm_selector = _go32_my_cs(); + if(_go32_dpmi_allocate_iret_wrapper(&g_isr_addr) == 0) + return 1; + __dpmi_unlock_linear_region(&dataregion); + } + __dpmi_unlock_linear_region(&coderegion); + } + } + return 0; +} + +static void serial_dpmi_unlock_memory(void) +{ + unsigned long baseaddr; + __dpmi_meminfo region; + + if(__dpmi_get_segment_base_address(_my_ds(), &baseaddr) == 0) + { + region.handle = 0; + region.size = sizeof(g_comports); + region.address = baseaddr + (unsigned long)g_comports; + __dpmi_unlock_linear_region(®ion); + } + if(__dpmi_get_segment_base_address(_my_cs(), &baseaddr) == 0) + { + region.handle = 0; + region.size = ISR_SIZE; + region.address = baseaddr + (unsigned long)com_general_isr; + __dpmi_unlock_linear_region(®ion); + } + _go32_dpmi_free_iret_wrapper(&g_isr_addr); +} + +#else /* ! __DJGPP__ */ + +static void serial_dpmi_get_pvect(int vect, int_handler_ptr *handler) +{ + union REGS r; + unsigned short sel; + unsigned long off; + + /* DPMI get protected mode interrupt vector: Int 31H, Fn 0204H */ + r.x.eax = 0x0204; + r.x.ebx = vect; + int386(0x31, &r, &r); + sel = (unsigned short) r.x.ecx; + off = r.x.edx; + + *handler=(int_handler_ptr) MK_FP(sel, off); +} + +static void serial_dpmi_set_pvect(int vect, int_handler_ptr *handler) +{ + union REGS r; + void Far *ptr; + + /* DPMI set protected mode interrupt vector: Int 31H, Fn 0205H */ + ptr = (void Far *)*handler; + r.x.eax = 0x0205; + r.x.ebx = vect; + r.x.ecx = FP_SEG(ptr); + r.x.edx = FP_OFF(ptr); + int386(0x31, &r, &r); +} + +static int serial_dpmi_lock_linear_memory(void Far *ptr, unsigned long size) +{ + union REGS r; + + /* DPMI get segment base address: Int 31H, Fn 0006H */ + r.x.eax = 0x0006; + r.x.ebx = FP_SEG(ptr); + int386(0x31, &r, &r); + if(r.w.cflag == 0) + { + unsigned long addr = FP_OFF( ptr ) + ((r.w.cx << 16) | r.w.dx); + + /* DPMI lock linear region: Int 31H, Fn 0600H */ + r.x.eax = 0x0600; + r.x.ebx = addr >> 16; + r.x.ecx = addr & 0xFFFF; + r.x.esi = size >> 16; + r.x.edi = size & 0xFFFF; + int386(0x31, &r, &r); + if(r.w.cflag == 0) + { + g_isr_addr = com_general_isr; + return 1; + } + } + return 0; +} + +static void serial_dpmi_unlock_linear_memory(void Far *ptr, unsigned long size) +{ + union REGS r; + + /* DPMI get segment base address: Int 31H, Fn 0006H */ + r.x.eax = 0x0006; + r.x.ebx = FP_SEG(ptr); + int386(0x31, &r, &r); + if(r.w.cflag == 0) + { + unsigned long addr = FP_OFF( ptr ) + ((r.w.cx << 16) | r.w.dx); + + /* DPMI unlock linear region: Int 31H, Fn 0601H */ + r.x.eax = 0x0601; + r.x.ebx = addr >> 16; + r.x.ecx = addr & 0xFFFF; + r.x.esi = size >> 16; + r.x.edi = size & 0xFFFF; + int386(0x31, &r, &r); + } + int386 (0x31, &r, &r); +} + +static int serial_dpmi_lock_memory(void) +{ + if(serial_dpmi_lock_linear_memory(com_general_isr, ISR_SIZE)) + { + if(serial_dpmi_lock_linear_memory(g_comports, sizeof(g_comports))) + return 1; + serial_dpmi_unlock_linear_memory(com_general_isr, ISR_SIZE); + } + return 0; +} + +static void serial_dpmi_unlock_memory(void) +{ + serial_dpmi_unlock_linear_memory(com_general_isr, ISR_SIZE); + serial_dpmi_unlock_linear_memory(g_comports, sizeof(g_comports)); +} + +#endif /* ! __DJGPP__ */ + +static int serial_install_irqhandler(int irq) +{ + /* If we haven't taken this IRQ's ISR already, take it */ + if(!g_isrs_taken[irq]) + { + if( g_isrs_count++ == 0 ) + { + /* lock memory used by interrupt handler in DPMI mode */ + if(!serial_dpmi_lock_memory()) + { + --g_isrs_count; + return SER_ERR_LOCK_MEM; + } + } + serial_dpmi_get_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_old_isrs[irq]); + serial_dpmi_set_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_isr_addr); + g_isrs_taken[irq] = 1; + } + return SER_SUCCESS; +} + +static void serial_remove_irqhandler(int irq) +{ + if(g_isrs_taken[irq]) + { + serial_dpmi_set_pvect(irq+INTERRUPT_VECTOR_OFFSET, &g_old_isrs[irq]); + g_isrs_taken[irq] = 0; + + if( --g_isrs_count == 0 ) + /* unlock memory used by interrupt handler in DPMI mode */ + serial_dpmi_unlock_memory(); + } +} + +#else /* ! PROTECTED_MODE */ + +static int serial_install_irqhandler(int irq) +{ + /* If we haven't taken this IRQ's ISR already, take it */ + if(!g_isrs_taken[irq]) + { + g_old_isrs[irq] = _dos_getvect(irq+INTERRUPT_VECTOR_OFFSET); + _dos_setvect(irq+INTERRUPT_VECTOR_OFFSET, com_general_isr); + g_isrs_taken[irq] = 1; + } + return SER_SUCCESS; +} + +static void serial_remove_irqhandler(int irq) +{ + _dos_setvect(irq+INTERRUPT_VECTOR_OFFSET, g_old_isrs[irq]); + g_isrs_taken[irq] = 0; +} + +#endif /* ! PROTECTED_MODE */ + + +static int serial_find_irq(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + unsigned char imr_m = PIC_READ_IMR(PIC_MASTER); /* Interrupt Mask Registers */ + unsigned char imr_s = PIC_READ_IMR(PIC_SLAVE); + unsigned char irr_m; /* Interrupt Request Registers */ + unsigned char irr_s; + + /* Set up the UART */ + UART_WRITE_MODEM_CONTROL(com, UART_MCR_OUT2); + UART_WRITE_FIFO_CONTROL(com, 0); + + /* Wait until tx hold register is empty */ + while((UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY) == 0); + + CPU_DISABLE_INTERRUPTS(); + + /* Allow any interrupt on PIC */ + PIC_WRITE_IMR(PIC_MASTER, 0); + PIC_WRITE_IMR(PIC_SLAVE, 0); + + /* Do some initial polls to let things settle (win95 needs this) */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY); + PIC_READ_IRR(PIC_MASTER); + PIC_READ_IRR(PIC_SLAVE); + UART_WRITE_INTERRUPT_ENABLE(com, 0); + PIC_READ_IRR(PIC_MASTER); + PIC_READ_IRR(PIC_SLAVE); + + /* Generate an interrupt and record all active IRQs */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY); + irr_m = PIC_READ_IRR(PIC_MASTER); + irr_s = PIC_READ_IRR(PIC_SLAVE); + + /* Remove the interrupt and mask out all IRQs still active */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + irr_m &= ~PIC_READ_IRR(PIC_MASTER); + irr_s &= ~PIC_READ_IRR(PIC_SLAVE); + + /* Interrupt again to make sure */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_TX_HOLD_EMPTY); + irr_m &= PIC_READ_IRR(PIC_MASTER); + irr_s &= PIC_READ_IRR(PIC_SLAVE); + + /* Return everything to normal */ + PIC_WRITE_IMR(PIC_MASTER, imr_m); + PIC_WRITE_IMR(PIC_SLAVE, imr_s); + UART_WRITE_INTERRUPT_ENABLE(com, 0); + + CPU_ENABLE_INTERRUPTS(); + + switch(irr_m) + { + case 0x01: return 0; + case 0x02: return 1; + case 0x04: + switch(irr_s) + { + case 0x01: return 8; + case 0x02: return 9; + case 0x04: return 10; + case 0x08: return 11; + case 0x10: return 12; + case 0x20: return 13; + case 0x40: return 14; + case 0x80: return 15; + default: return 2; + } + case 0x08: return 3; + case 0x10: return 4; + case 0x20: return 5; + case 0x40: return 6; + case 0x80: return 7; + } + return SER_ERR_IRQ_NOT_FOUND; +} + +static void serial_free_irq(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + serial_struct* com_min = (serial_struct*)(g_comports); + serial_struct* com_max = com_min + COM_MAX; + unsigned int irq = com->irq; + serial_struct* ptr; + + if(irq == IRQ_NONE) + return; + + CPU_DISABLE_INTERRUPTS(); + + /* Disable interrupts from the UART */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + UART_WRITE_MODEM_CONTROL(com, UART_MCR_OUT2); + /* Clear the FIFO and rx registers */ + UART_WRITE_FIFO_CONTROL(com, 0); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_DATA(com); + UART_READ_INTERRUPT_IDENTIFY(com); + UART_READ_LINE_STATUS(com); + UART_READ_DATA(com); + /* Clear any other possible interrupt causes */ + UART_READ_INTERRUPT_IDENTIFY(com); + UART_READ_MODEM_STATUS(com); + UART_READ_INTERRUPT_IDENTIFY(com); + UART_READ_LINE_STATUS(com); + UART_READ_INTERRUPT_IDENTIFY(com); + + com->irq = IRQ_NONE; + + CPU_ENABLE_INTERRUPTS(); + + for(ptr=com_min;ptr<=com_max;ptr++) + if(ptr != com && ptr->irq == irq) + return; + + /* Disable interrupts from the PIC and restore the old vector */ + PIC_DISABLE_IRQ(irq); + serial_remove_irqhandler(irq); +} + + +/* ======================================================================== */ +/* ======================== BASIC SERIAL ROUTINES ========================= */ +/* ======================================================================== */ + +int serial_open(int comport, long bps, int data_bits, char parity, int stop_bits, int handshaking) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int rc = SER_SUCCESS; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(com->open) + return SER_ERR_ALREADY_OPEN; + + com->open = 1; + + /* Initialize buffers */ + com->rx_flow_on = 1; + com->tx_flow_on = 1; + SER_RX_BUFFER_INIT(com); + SER_TX_BUFFER_INIT(com); + + /* look in bios tables (0040:0000 - 0040:0006) for com base addresses */ + if(serial_set_base(comport, Farpeekw(0x0040, comport<<1)) != SER_SUCCESS) + return SER_ERR_NO_UART; + + /* Turn off interrupts from UART */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + + /* Auto-detect IRQ if we can */ + if((rc=serial_find_irq(comport)) < 0) + rc = com->default_irq; + + if((rc=serial_set_irq(comport, rc)) != SER_SUCCESS) + return rc; + + /* Turn off interrupts from PIC */ + PIC_DISABLE_IRQ(com->irq); + + /* Set the comport */ + if((rc=serial_set(comport, bps, data_bits, parity, stop_bits, handshaking)) == SER_SUCCESS) + { + UART_WRITE_MODEM_CONTROL(com, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT1 | UART_MCR_OUT2); + rc = serial_set_fifo_threshold(comport, UART_FIFO_DEFAULT_THRESHOLD); + + /* Get some info */ + UART_READ_LINE_STATUS(com); + UART_READ_MODEM_STATUS(com); + } + + /* Re-enable interrupts */ + UART_WRITE_INTERRUPT_ENABLE(com, UART_IER_DATA_READY | UART_IER_MODEM_STATUS | UART_IER_ERRORS); + PIC_ENABLE_IRQ(com->irq); + + if(rc != SER_SUCCESS) /* We failed to set the comport */ + serial_close(comport); + + return rc; +} + +int serial_close(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + /* Restore old interrupts */ + serial_free_irq(comport); + + /* Turn everything off */ + UART_WRITE_INTERRUPT_ENABLE(com, 0); + UART_WRITE_MODEM_CONTROL(com, 0); + serial_set_fifo_threshold(comport, 0); + + com->open = 0; + + return SER_SUCCESS; +} + +int serial_read(int comport, char* data, int len) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + if(data == 0) + return SER_ERR_NULL_PTR; + + CPU_DISABLE_INTERRUPTS(); + /* Fill in the array given to us */ + for(i=0;!SER_RX_BUFFER_EMPTY(com) && i < len;i++) + data[i] = SER_RX_BUFFER_READ(com); + + /* Flow control (RX) - Turn on if buffer almost empty */ + if(!com->rx_flow_on && SER_RX_BUFFER_LOWATER(com)) + { + com->rx_flow_on = 1; + if(com->flow_mode == SER_HANDSHAKING_XONXOFF) + UART_WRITE_DATA(com, SER_XON); + else if(com->flow_mode == SER_HANDSHAKING_RTSCTS) + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) | UART_MCR_RTS); + else if(com->flow_mode == SER_HANDSHAKING_DTRDSR) + UART_WRITE_MODEM_CONTROL(com, UART_READ_MODEM_CONTROL(com) | UART_MCR_DTR); + } + CPU_ENABLE_INTERRUPTS(); + + return i; +} + + +int serial_write(int comport, const char* data, int len) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + if(data == 0) + return SER_ERR_NULL_PTR; + + for(i=0;i < len;i++) + { + /* Wait until we can write */ + while(!(UART_READ_LINE_STATUS(com) & UART_LSR_TX_HOLD_EMPTY)) + {} + + /* Write 1 char */ + if(com->tx_flow_on) + UART_WRITE_DATA(com, data[i]); + else + break; + } + return i; +} + +int serial_write_buffered(int comport, const char* data, int len) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + if(data == 0) + return SER_ERR_NULL_PTR; + + CPU_DISABLE_INTERRUPTS(); + for(i=0;i < len ;i++) + { + /* stop when sent buffer is full */ + if(SER_TX_BUFFER_FULL(com)) + break; + /* Write 1 char */ + SER_TX_BUFFER_WRITE(com, data[i]); + } + + /* If there's data to send, enable TX_HOLD_EMPTY interrupt */ + if(!SER_TX_BUFFER_EMPTY(com)) + UART_WRITE_INTERRUPT_ENABLE(com, UART_READ_INTERRUPT_ENABLE(com) | UART_IER_TX_HOLD_EMPTY); + CPU_ENABLE_INTERRUPTS(); + + return i; +} + + +int serial_set(int comport, long bps, int data_bits, char parity, int stop_bits, int handshaking) +{ + int rc; + + if((rc=serial_set_bps(comport, bps)) == SER_SUCCESS) + if((rc=serial_set_data(comport, data_bits)) == SER_SUCCESS) + if((rc=serial_set_parity(comport, parity)) == SER_SUCCESS) + if((rc=serial_set_stop(comport, stop_bits)) == SER_SUCCESS) + return serial_set_handshaking(comport, handshaking); + return rc; +} + + +/* ======================================================================== */ +/* ========================== SETTINGS ROUTINES =========================== */ +/* ======================================================================== */ + + +int serial_set_base(int comport, int base) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(base < 1 || base > 0xfff) + return SER_ERR_INVALID_BASE; + + com->base = base; + return SER_SUCCESS; +} + +int serial_set_irq(int comport, int irq) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int rc; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(irq < IRQ_MIN || irq > IRQ_MAX) + return SER_ERR_INVALID_IRQ; + if(!com->open) + return SER_ERR_NOT_OPEN; + + /* Remove any ISRs on this com port's current IRQ */ + serial_free_irq(comport); + + if((rc=serial_install_irqhandler(irq)) == SER_SUCCESS) + { + com->irq = irq; + PIC_ENABLE_IRQ(com->irq); + } + return rc; +} + +int serial_set_fifo_threshold(int comport, int threshold) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int i; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(threshold) + { + case 14: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_14); + break; + case 8: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_8); + break; + case 4: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_4); + break; + case 1: + UART_WRITE_FIFO_CONTROL(com, UART_FCR_FIFO_ENABLE | UART_FCR_RX_FIFO_RESET | UART_FCR_TX_FIFO_RESET | UART_FCR_TRIGGER_AT_1); + break; + case 0: + UART_WRITE_FIFO_CONTROL(com, 0); + break; + default: + return SER_ERR_INVALID_FIFO_THRESHOLD; + } + + /* Clear out any garbage in the UART interrupts and FIFO */ + for(i=0;i<16;i++) + UART_READ_DATA(com); + UART_READ_LINE_STATUS(com); + + return SER_SUCCESS; +} + + +int serial_set_bps(int comport, long bps) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(bps) + { + case 115200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_115200); + return SER_SUCCESS; + case 57600L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_57600); + return SER_SUCCESS; + case 38400L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_38400); + return SER_SUCCESS; + case 19200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_19200); + return SER_SUCCESS; + case 9600L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_9600); + return SER_SUCCESS; + case 7200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_7200); + return SER_SUCCESS; + case 4800L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_4800); + return SER_SUCCESS; + case 3800L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_3800); + return SER_SUCCESS; + case 1800L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_1800); + return SER_SUCCESS; + case 2400L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_2400); + return SER_SUCCESS; + case 1200L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_1200); + return SER_SUCCESS; + case 600L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_600); + return SER_SUCCESS; + case 300L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_300); + return SER_SUCCESS; + case 150L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_150); + return SER_SUCCESS; + case 110L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_110); + return SER_SUCCESS; + case 75L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_75); + return SER_SUCCESS; + case 50L: + UART_WRITE_BPS(com, UART_BPS_DIVISOR_50); + return SER_SUCCESS; + } + return SER_ERR_INVALID_BPS; +} + + +int serial_set_data(int comport, int data_bits) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(data_bits) + { + case 5: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_5); + return SER_SUCCESS; + case 6: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_6); + return SER_SUCCESS; + case 7: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_7); + return SER_SUCCESS; + case 8: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_DATA_MASK) | UART_DATA_8); + return SER_SUCCESS; + } + return SER_ERR_INVALID_DATA_BITS; +} + + +int serial_set_parity(int comport, char parity) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(parity) + { + case 'n': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_NONE); + return SER_SUCCESS; + case 'e': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_EVEN); + return SER_SUCCESS; + case 'o': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_ODD); + return SER_SUCCESS; + case 'm': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_MARK); + return SER_SUCCESS; + case 's': + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_PARITY_MASK) | UART_PARITY_SPACE); + return SER_SUCCESS; + } + return SER_ERR_INVALID_PARITY; +} + + +int serial_set_stop(int comport, int stop_bits) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(stop_bits) + { + case 1: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_STOP_MASK) | UART_STOP_1); + return SER_SUCCESS; + case 2: + UART_WRITE_LINE_CONTROL(com, (UART_READ_LINE_CONTROL(com) & ~UART_STOP_MASK) | UART_STOP_2); + return SER_SUCCESS; + } + return SER_ERR_INVALID_STOP_BITS; +} + + +int serial_set_handshaking(int comport, int handshaking) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + com->tx_flow_on = 1; + com->rx_flow_on = 1; + + if(handshaking < SER_HANDSHAKING_NONE || handshaking > SER_HANDSHAKING_DTRDSR) + return SER_ERR_INVALID_HANDSHAKING; + + com->flow_mode = handshaking; + return SER_SUCCESS; +} + + +int serial_set_rts(int comport, int rts) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + if(rts) + UART_WRITE_MODEM_CONTROL(com, com->mcr | UART_MCR_RTS); + else + UART_WRITE_MODEM_CONTROL(com, com->mcr & ~UART_MCR_RTS); + + return SER_SUCCESS; +} + + +int serial_set_dtr(int comport, int dtr) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + if(dtr) + UART_WRITE_MODEM_CONTROL(com, com->mcr | UART_MCR_DTR); + else + UART_WRITE_MODEM_CONTROL(com, com->mcr & ~UART_MCR_DTR); + + return SER_SUCCESS; +} + + +int serial_set_mcr(int comport, int mcr) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + UART_WRITE_MODEM_CONTROL(com, mcr & UART_MCR_MASK ); + + return SER_SUCCESS; +} + + + +int serial_get_base(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(com->base < 1 || com->base > 0xfff) + return SER_ERR_INVALID_BASE; + + return com->base; +} + +int serial_get_irq(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(com->irq < IRQ_MIN || com->irq > IRQ_MAX) + return SER_ERR_INVALID_IRQ; + + return com->irq; +} + +long serial_get_bps(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_BPS(com)) + { + case UART_BPS_DIVISOR_115200: + return 115200L; + case UART_BPS_DIVISOR_57600: + return 57600L; + case UART_BPS_DIVISOR_38400: + return 38400L; + case UART_BPS_DIVISOR_19200: + return 19200L; + case UART_BPS_DIVISOR_9600: + return 9600L; + case UART_BPS_DIVISOR_7200: + return 7200L; + case UART_BPS_DIVISOR_4800: + return 4800L; + case UART_BPS_DIVISOR_3800: + return 3800L; + case UART_BPS_DIVISOR_2400: + return 2400L; + case UART_BPS_DIVISOR_1800: + return 1800L; + case UART_BPS_DIVISOR_1200: + return 1200L; + case UART_BPS_DIVISOR_600: + return 600L; + case UART_BPS_DIVISOR_300: + return 300L; + case UART_BPS_DIVISOR_150: + return 150L; + case UART_BPS_DIVISOR_110: + return 110L; + case UART_BPS_DIVISOR_75: + return 75L; + case UART_BPS_DIVISOR_50: + return 50L; + } + return SER_ERR_INVALID_BPS; +} + + +int serial_get_data(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_LINE_CONTROL(com) & UART_DATA_MASK) + { + case UART_DATA_5: + return 5; + case UART_DATA_6: + return 6; + case UART_DATA_7: + return 7; + case UART_DATA_8: + return 8; + } + return SER_ERR_INVALID_DATA_BITS; +} + + +char serial_get_parity(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_LINE_CONTROL(com) & UART_PARITY_MASK) + { + case UART_PARITY_NONE: + return 'n'; + case UART_PARITY_EVEN: + return 'e'; + case UART_PARITY_ODD: + return 'o'; + case UART_PARITY_MARK: + return 'm'; + case UART_PARITY_SPACE: + return 's'; + } + return SER_ERR_INVALID_PARITY; +} + + +int serial_get_stop(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + switch(UART_READ_LINE_CONTROL(com) & UART_STOP_MASK) + { + case UART_STOP_1: + return 1; + case UART_STOP_2: + return 2; + } + return SER_ERR_INVALID_STOP_BITS; +} + + +int serial_get_handshaking(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->flow_mode; +} + + +int serial_get_rts(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->mcr & UART_MCR_RTS) != 0; +} + + +int serial_get_dtr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->mcr & UART_MCR_DTR) != 0; +} + + +int serial_get_mcr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->mcr; +} + + +int serial_get_dsr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->msr & UART_MSR_DSR) != 0; +} + + +int serial_get_cts(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return (com->msr & UART_MSR_CTS) != 0; +} + + +int serial_get_msr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->msr; +} + + +int serial_get_lsr(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + + return com->lsr; +} + + + +int serial_get_tx_buffered(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int count; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + count = SER_TX_BUFFER_CURRENT(com); + CPU_ENABLE_INTERRUPTS(); + + return count; +} + + +int serial_get_rx_buffered(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + int count; + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + count = SER_RX_BUFFER_CURRENT(com); + CPU_ENABLE_INTERRUPTS(); + + return count; +} + + +int serial_clear_tx_buffer(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + SER_TX_BUFFER_INIT(com); + CPU_ENABLE_INTERRUPTS(); + + return SER_SUCCESS; +} + + +int serial_clear_rx_buffer(int comport) +{ + serial_struct* com = (serial_struct*)(g_comports + comport); + + if(comport < COM_MIN || comport > COM_MAX) + return SER_ERR_INVALID_COMPORT; + if(!com->open) + return SER_ERR_NOT_OPEN; + + CPU_DISABLE_INTERRUPTS(); + SER_RX_BUFFER_INIT(com); + CPU_ENABLE_INTERRUPTS(); + + return SER_SUCCESS; +} diff --git a/client/src/thirdparty/serial/serial.h b/client/src/thirdparty/serial/serial.h new file mode 100644 index 0000000..4764b9f --- /dev/null +++ b/client/src/thirdparty/serial/serial.h @@ -0,0 +1,195 @@ +/* Serial Library 1.4 (22-Jun-2000) (c) 1998 Karl Stenerud + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef SERIAL__HEADER +#define SERIAL__HEADER + +/* COM Ports */ +#define COM_1 0 +#define COM_2 1 +#define COM_3 2 +#define COM_4 3 + +/* Handshaking Modes */ +#define SER_HANDSHAKING_NONE 0 +#define SER_HANDSHAKING_XONXOFF 1 +#define SER_HANDSHAKING_RTSCTS 2 +#define SER_HANDSHAKING_DTRDSR 3 + +/* Error Codes */ +#define SER_SUCCESS 0 /* Function completed successfully */ +#define SER_ERR_UNKNOWN -1 /* An unknown error occured */ +#define SER_ERR_NOT_OPEN -2 /* The specified COM port is not opened */ +#define SER_ERR_ALREADY_OPEN -3 /* The specified COM port is already opened */ +#define SER_ERR_NO_UART -4 /* Could not find a UART for this COM port */ +#define SER_ERR_INVALID_COMPORT -5 /* User specified an invalid COM port */ +#define SER_ERR_INVALID_BASE -6 /* User specified an invalid base address */ +#define SER_ERR_INVALID_IRQ -7 /* User specified an invalid IRQ number */ +#define SER_ERR_INVALID_BPS -8 /* User specified an invalid BPS rate */ +#define SER_ERR_INVALID_DATA_BITS -9 /* User specified an invalid number of data bits */ +#define SER_ERR_INVALID_PARITY -10 /* User specified an invalid parity type */ +#define SER_ERR_INVALID_STOP_BITS -11 /* User specified an invalid number of stop bits */ +#define SER_ERR_INVALID_HANDSHAKING -12 /* User specified an invalid handshaking type */ +#define SER_ERR_INVALID_FIFO_THRESHOLD -13 /* User specified an invalid fifo threshold value */ +#define SER_ERR_NULL_PTR -14 /* User specified a buffer address that was NULL */ +#define SER_ERR_IRQ_NOT_FOUND -15 /* Could not find an IRQ for the specified COM port */ +#define SER_ERR_LOCK_MEM -16 /* Could not lock memory in DPMI mode */ + + +/* Name: serial_open() + * + * Desc: Finds and initializes the specified COM port. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * long bps: Bits per second. + * (50, 75, 110, 150, 300, 600, 1200, 2400, 4800, + * 9600, 19200, 38400, 57600, 115200) + * int data_bits: Number of data word bits (5-8) + * char parity: Parity mode ('e', 'o', 'n', 'm', 's') + * int stop_bits: Stop bits (1-2). If data_bits=5 and stop_bits=2, + * actual stop bits is 1.5. + * int handshaking: Handshaking mode + * (SER_HANDSHAKING_NONE, SER_HANDSHAKING_XONXOFF, + * SER_HANDSHAKING_RTSCTS, SER_HANDSHAKING_DTRDSR) + * + * Return: SER_SUCCESS or an error code + */ +int serial_open(int com, long bps, int data_bits, char parity, int stop_bits, int handshaking); + + +/* Name: serial_close() + * + * Desc: Clean up and close the specified serial port. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * + * Return: SER_SUCCESS or an error code + */ +int serial_close(int com); + + +/* Name: serial_read() + * + * Desc: Read data from the specified serial port buffer. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * char* data: Pointer to data buffer + * int len: Number of bytes to read + * + * Return: number of bytes read or an error code + */ +int serial_read(int com, char* data, int len); + + +/* Name: serial_write() and serial_write_buffered() + * + * Desc: Write data to the serial port. + * serial_write() will write the data directly to serial port and will + * block until it has completely sent the data or handshaking has + * stopped output transmission while serial_write_buffered() will copy + * as much as possible data bytes to the transmit buffer and will return + * immediately enabling asynchronous output. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * char* data: Pointer to data buffer + * int len: Number of bytes to write + * + * Return: number of bytes written or an error code + */ +int serial_write (int com, const char* data, int len); +int serial_write_buffered(int com, const char* data, int len); + + +/* Name: serial_set() + * + * Desc: Change the specified serial port's settings. + * + * Params: int com: Communications port (COM_1, COM_2, COM_3, COM_4) + * long bps: Bits per second. + * (50, 75, 110, 150, 300, 600, 1200, 2400, 4800, + * 9600, 19200, 38400, 57600, 115200) + * int data_bits: Data bits (5, 6, 7, or 8) + * char parity: Parity ('n' = none, 'e' = even, 'o' = odd, 'm' = mark, 's' = space) + * int stop_bits: Stop bits (1-2). If data_bits=5 and stop_bits=2, + * actual stop bits is 1.5. + * int handshaking: Handshaking type (see handshaking modes at top of file) + * + * Return: SER_SUCCESS or an error code + */ +int serial_set(int com, long bps, int data_bits, char parity, int stop_bits, int handshaking); + + +/* serial_set_xxx() functions. These take the same arguments + * as the corresponding ones in serial_set() and return either + * SER_SUCCESS or an error code. + */ +int serial_set_bps (int com, long bps); +int serial_set_parity (int com, char parity); +int serial_set_data (int com, int data); +int serial_set_stop (int com, int stop); +int serial_set_handshaking (int com, int handshaking); + +/* Advanced settings. Use these only if you know what you are doing. */ + +/* UART's FIFO threshold -- 14, 8, 4, 1, or 0 (off) */ +int serial_set_fifo_threshold(int com, int threshold); +/* UART's base address */ +int serial_set_base (int com, int base); +/* UART's IRQ */ +int serial_set_irq (int com, int base); + +int serial_set_rts(int comport, int rts); +int serial_set_dtr(int comport, int dtr); + +int serial_set_mcr(int comport, int mcr); + + +/* serial_get_xxx() functions. These return the same types as are supplied + * to serial_set(). The returned type may be negative, in which case it + * is an error code. + */ +int serial_get_base (int com); +int serial_get_irq (int com); +long serial_get_bps (int com); +char serial_get_parity (int com); +int serial_get_data (int com); +int serial_get_stop (int com); +int serial_get_handshaking (int com); + + +int serial_get_rts(int comport); +int serial_get_dtr(int comport); +int serial_get_cts(int comport); +int serial_get_dsr(int comport); + +int serial_get_mcr(int comport); +int serial_get_msr(int comport); +int serial_get_lsr(int comport); + + +/* get number of bytes or discard data in TX/RX buffers + */ +int serial_get_tx_buffered(int comport); +int serial_get_rx_buffered(int comport); +int serial_clear_tx_buffer(int comport); +int serial_clear_rx_buffer(int comport); + +#endif diff --git a/client/src/thirdparty/serial/term.C b/client/src/thirdparty/serial/term.C new file mode 100644 index 0000000..69c7fcb --- /dev/null +++ b/client/src/thirdparty/serial/term.C @@ -0,0 +1,104 @@ +/* A simple terminal program for testing the serial library by Karl Stenerud + */ + +#include +#include +#include +#include +#include "serial.h" +#include + +char* errors[] = +{ + "Successful", + "Unknown error", + "Port not open", + "Port already open", + "No UART found on that comport", + "Invalid comport", + "Invalid BPS", + "Invalid data bits", + "Invalid parity", + "Invalid stop bits", + "Invalid handshaking", + "Invalid fifo threshold", + "Passed in a NULL pointer", + "", + "", + "" +}; + +char* handshaking[] = +{ + "none", + "xon/xoff", + "rts/cts", + "dtr/dsr", + "", + "" +}; + + +int main(int argc, char* argv[]) +{ + int len; + char buff[100]; + char ch; + int rc; + int com = COM_1; + + if(argc < 2) + { + printf("Usage: term \n"); + return 0; + } + + switch(atoi(argv[1])) + { + case 1: + com = COM_1; + break; + case 2: + com = COM_2; + break; + case 3: + com = COM_3; + break; + case 4: + com = COM_4; + break; + default: + printf("%s: invalid com port number\n", argv[1]); + return 0; + } + + if((rc=serial_open(com, 19200L, 8, 'n', 1, SER_HANDSHAKING_XONXOFF)) != SER_SUCCESS) + { + printf("Can't open port! (%s)\n", errors[-rc]); + return 0; + } + +printf("Opened COM%d. Base 0x%03x, IRQ %d, %ld %d%c%d, Handshaking %s\n", com+1, +serial_get_base(com), serial_get_irq(com), serial_get_bps(com), serial_get_data(com), +(char)serial_get_parity(com), serial_get_stop(com), handshaking[serial_get_handshaking(com)]); + + for(;;) + { + if(kbhit()) + { + if((ch=(char)getch()) == 0x1b) + break; + if((rc=serial_write(com, &ch, 1)) < 0) + printf("Error writing (%s)\n", errors[-rc]); + } + if((len=serial_read(com, buff, 100)) > 0) + write(1, buff, len); + else if(len < 0) + printf("Error reading (%s)\n", errors[-len]); + } + + if((rc=serial_close(com)) != SER_SUCCESS) + printf("Can't close serial port! (%s)\n", errors[-rc]); + + return 0; +}