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[40m[2J[0mÛ[1;47mÜÜ[2CÜÜ[0mß[2C[1;47mÜ[0mßßÜ[2CÛ[1;47mÜ[0mßÛÜ[3CÜ[1;47mÜÜÜ[3CÜ[0mßßÜ[2CÛ[1;47mÜÜ[0mßßÜ[3CÜßßÜ[C[30;41m²²[31;43m²²²²²²²²²²²²²²²[2;2H[1;37mÛÛ[2CÛÛ[2C[0m²[1;43mÛ[2CÛ[47mÜ[C[0m²[1;43mÛ[2CÛ[47mÜ[CÜ[43mÛ[C[47mßß[2C[0m²[1;43mÛ[2CÛ[47mÜ[2C[43mÛÛ[2CÛ[47mÜ[CÜ[43mÛ[C[0;30;41m²²²[31;40m±±[1;30;43m±[33;45m²²²²²²²²²[0;33m±±±±[31m±[43m²²²²[3;2H[1;37mÛÛ[2C[47mß[0mß[2C[1;43mÛÛ[2CÛÛ[CÛÛ[2CÛÛ[CÛÛ[5CÛÛ[2CÛÛ[2CÛÛ[2CÛ[47mß[C[43mÛ[0;30;41m²²²²[1;43m±[31;42m±±±±±±±±±±±[0;33m±[1;41m±±±±[0;33m±[1;45m²²[0;31m±±[43m²²[4;2H[1;37mÛÛ[0mßÛÜ[3C[1;43mÛÛ[47mßß[43mÛ[47mÛ[C[43mÛÛ[2CÛ[47mÛ[C[43m²²[0mÞ[1;47mß[43mÛÛ[CÛÛ[47mßß[43mÛ[47mÛ[2C[43mÛÛ[0mÜÜß[C[30;41m²²²²[1;31;42m±[0;33m±±±±±±[1;31;42m±±±±±±[0;33m±[1;31;42m²[33;41m±[45m²²[31;42m²[30;43m±[31;42m±±±[33;45m²[0;31m±±[43m²²[5;2H[1;33m²²[2C²[47mÜ[2C[43m²²[2C²²[C²²[2C²²[C²²[2C[0mÜ[1;47mÜ[C[33;43m²²[2C²²[2C²²[2C[47mÜ[0;30;41m²[31;40m±[30;41m²²[1;31;42m±[0;33m±[1;41m±±±±[31;42m±[30;43m±[31;40m°[42m±±±[0;33m±±[1;41m±[45m²²[41m±[31;42m²[30;43m±[31;42m±±±±±±[33;45m²[0;31m±±[43m²[6;2H[1;33mÛÛ[0;33mÝ[C[1;43mÛÛ[2CÛÛ[2CÛÛ[CÛÛ[2CÛÛ[CßÛ[2C²²[CÛÛ[2CÛÛ[2CÛÛ[2C[0;30;41m²[31;40m±±[30;41m²[1;31;42m±±[0;33m±[1;41m±[45m²²²[41m±[31;42m±[0;33m±[1;30;43m±±±[0;33m±[1;41m±[45m²[41m±[31;42m±[0;31m²[1;30;43m±[31;42m±±±±±±±±[33;45m²[0;31m±±[43m²[7;1H[1;33mßßßß[C[0;33mÛ[1;43mßß[Cß[0;33mß[2CÛÛ[C[1;43mß[0;33mß[2CÛÛ[2Cß[1;43mßß [0;33mß[C[1;43mß[0;33mß[2CÛÛ[CÜ[1;43mßß[C[0;30;41m²[31;40m±±[30;41m²[1;31;42m±±±[30;43m±[0;33m±±±[1;31;42m²[33;41m±±[31;42m²[0;33m±±±[1;31;42m²[33;45m²[41m±[30;43m±[0;31m±±[1;30;43m±[31;42m±±±±±±±±±[33;45m²[0;31m±±[43m²[8;1H[37;40mÛ[1;47mÜÜ[0mßßÜ[2CÛ[1;47mÜÜ[2CÜÜ[0mÛ[CÛ[1;47mÜ[0mßÛÜ[3CÜ[1;47mÜÜÜ[C[0mÛ[1;47mÜÜ[2CÜÜ[0mß[2C[30;41m²[31;40m±±[30;41m²[1;31;42m±±±[30;43m±±[0;31m±±±±[1;43m±[33;41m±[45m±[41m±±[31;42m²[33;45m±[C[31;42m²²²[30;41m°[0;31m±[1;30;43m±[31;42m±±±±±±±±[33;45m²[0;31m±±±[43m²[C[1;37mÛÛ[2CÛ[47mÜ[2C[43mÛÛ[2CÛÛ[2C[0m²[1;43mÛ[2CÛ[47mÜ[CÜ[43mÛ[C[47mßß[2C[43mÛÛ[2CÛÛ[3C[0;30;41m²[31;40m±±[30;41m²[1;31;42m±[30;43m±[0;31m±±±²±[1;30;41m°[33m±[31;42m²²[C²[33;45m²²[41m±[37;43m±[33;45m±[31;42m²[33;41m±[31;42m²[40m°[0;31m±[1;42m±±±±±±±±±[33;45m²[0;31m±±[43m²[C[1;37mÛÛ[2CÛ[47mß[2C[43mÛÛ[2CÛÛ[2CÛÛ[2CÛÛ[CÛÛ[5CÛÛ[2CÛÛ[3C[0;30;41m²[31;40m±±[30;41m²[1;43m±[0;31m±[1;42m²[41m°[0;31;41mÛÛÛÛ[1;30;43m±[31;42m±²[33;45m±±[2C[0;36;43m°[1;37m±[33;41m±[45m²²²[31;43m±[0;31m±[1;30;43m±[31;42m±±±±±±±±[33;45m²[0;31m±±[43m²[C[1;37mÛÛ[0mÜÜß[3C[1;43mÛÛ[2C±±[2CÛÛ[2CÛ[47mÛ[C[43m²²[5CÛÛ[0mÜÜÜ[1;47mÜ[3C[0;30;41m²[31;40m±±[30;41m²[31;40m±[1;43m±[0;31;41mÛÛÛÛÛÛ[30m²²[31;40m±[36;43m°[1;37m±[40mÛÛ[43m±[33;45m±²²²[41m±[30;43m±[0;31m±±[1;30;43m±[31;42m±±±±±±±[33;45m²[0;31m±±[43m²[C[1;33m²²[6C²²[2C²²[2C²²[2C²²[C²²[5C²²[2C²²[3C[0;30;41m²[31;40m±±[30;41m²[31;40m±[41mÛÛÛÛÛÛÛ[30m²[31;40m±[1m°[42m²²[43m±[0;31;41mÛ[1;43m±±±[42m²[43m±[30;41m°[0;31m°[1m°[42m±[0;33m±[1;31;42m±±±±±±±[33;45m²[0;31m±±[43m²[C[1;33mÛÛ[6CßÛ[2CÛß[2CÛÛ[2CÛÛ[CßÛ[5CÛÛ[2CÛÛ[3C[0;30;41m²[31;40m±±[30;41m²[31;40m±[41mÛÛÛÛÛÛ[30m²[31;40m±[41mÛÛÛÛÛÛÛÛÛÛÛÛ[40m±°[1;42m²[0;33m±[1;30;43m±[31;42m±±±±±±[33;45m²[0;31m±±[43m²[33;40mÜ[1;43mßß[7C[0;33mßÜÜß[3C[1;43mß[0;33mß[2CÛÛ[2Cß[1;43mßß [Cßßß[2C[0;33mß[1;43mßß[3C[0;30;41m²[31;40m±[30;41m²²[31;40m±[43m²[41mÛÛ[40m²[30;41m²[31;40m²[41mÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ[30m²[1;33m±[0;33m±[1;30;43m±[31;42m±±±±[33;45m²[0;31m±±[43m²[15;2H[1;37;40mÜ[47mÜÜÜ[0mÜ[2CÛ[1;47mÜÜ[0mßß[1;47mÜÜ[C[0mÛ[1;47mÜÜ[2CÜÜ[0mÛ[C[1mÛÛ[0mßßÜ[2CÜ[1;47mÜÜ[2C[0mÜßßÜ[30;41m²[31;40m±±[30;41m²²[31;40m±±±[30;41m²[31;40m±[41mÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ[30m²[1;31;43m±[33;41m±[0;33m±[1;30;43m±[31;42m±±[33;45m²[0;31m±±[43m²[16;1H[1;37;47mÛ[43mÛ[3C±[0mÛ[3C[1;43mÛ[47mÜ[5C[43mÛÛ[2CÛÛ[2C[40mÛ[43mÛ[2CÛ[47mÜ[2C[40mß[43mÛ[C[47mÜ[43mÛ[2CÛ[47mÜ[0;30;41m²[31;40m±±[30;41m²²[31;40m±±[30;41m²[31;40m±[41mÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ[40m°[1;42m±[33;45m²[30;43m°[0;33m±[1;30;43m±[33;45m²[31;40m°[0;31m±[43m²[17;1H[37;40mß[1;47mß[40mÜÜÜ[5C[43mÛÛ[5CÛÛ[2CÛÛ[2CÛÛ[2CÛÛ[C[0mÜ[1;43mÛ[40mÜ[C[43mÛÛ[2CÛÛ[C[0;30;41m²[31;40m±±[30;41m²²²²²²[31;40m²[41mÛÛÛÛÛÛÛÛÛÛÛÛ[40m²°[1m°[33;41m±[45m²²[41m±[0;33m±[31m±²[43m²[18;3H[37;40mß[1mßß[43m±[0mÜ[3C[1;43m±±[5CÛÛ[2C±±[2CÛÛ[2CÛÛ[2CÛÛ[CÛÛ[2CÛÛ[2C[0;30;41m²²[31;40m±±±[1;30;43m°°[31;42m±[0;30;41m²[31;40m±±±±±±±±±±±±°[1;42m±[33;41m±[45m²²²²[31;42m±[0;31m±[19;1H[1;33;47mÜÜ[3C[43m²²[3C²²[5C²²[2C²²[2C²²[2C²²[2C²²[C²²[2C²²[C[47mÜÜ[C[0;30;41m²²[43m²[1;33;45m²²²²²[41m±[31;42m±[0;33m±[1;30;47m±±±±±[0;33m±[1;31;42m±[33;41m±±[45m²²²²²[41m±[0;33m±[20;1H[1;43mßÛ[3CÛÛ[3CÛÛ[0;33mÜ[4C[1;43mßÛ[2CÛß[2C[40mÛ[43mÛ[2CÛß[C[0;33mÜ[1;43mÛÛ[CßÛ[2CÛß[CßÛ[3C[0;33m°[1;31;42m²[33;41m±[45m²²[41m±[31;42m²±[0;33m±[1;30;47m±[0mÛÛÛ[1;30;47m±[0;33m±[1;45m²²²²²²²[41m±[0;33m±[30;41m²[21;2H[33;40mß[1;43mßÛÛ[40mß[4C[0;33mß[1;43mß[0;33mÛ[5CßÜÜß[3C[1mÛÛ[0;33mÜÜß[3Cß[1;43mß[2C[0;33mßÜÜß[3Cß[1;43mßÛÛ[40mß[0;33m±±±±[30;41m²[2C[43m²[41m²²²²²[43m²[1;31;42m²[33;41m±[45m²²²[41m±[0;33m±±[1;31;42m±[0;30;41m²[22;68H[1;31;42m±[0;33m±[1;45m²²[41m±[23;68H[0;33m±[1;31;42m²[33;41m±±[31;42m²²[24;69H[0;30;43m²[1;31;42m²±±[21;1H[B[0m
\ 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;
+}