diff --git a/.travis.yml b/.travis.yml
index c19d7250..edcc8d84 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,7 +9,7 @@ env:
- SIM="microvax2 vax730 vax750 vax780 vax8200 vax8600 microvax2000 infoserver100 infoserver150vxt microvax3100 microvax3100e vaxstation3100m30 vaxstation3100m38 vaxstation3100m76 vaxstation4000m60"
- SIM="microvax3100m80 vaxstation4000vlc infoserver1000 nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri i7094 ibm1130"
- SIM="id16 id32 sds lgp h316 cdc1700 swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 isys8030 isys8024"
- - SIM="besm6 imds-210 imds-220 imds-225 imds-230 imds-800 imds-810 imlac"
+ - SIM="besm6 imds-210 imds-220 imds-225 imds-230 imds-800 imds-810 imlac tt2500"
- SIM="scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 sigma uc15 i650"
sudo: required
install: sh -ex .travis/deps.sh
diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln
index b64a2a45..c7eda848 100644
--- a/Visual Studio Projects/Simh.sln
+++ b/Visual Studio Projects/Simh.sln
@@ -414,8 +414,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imlac", "imlac.vcproj", "{7
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "isdk80", "isdk80.vcproj", "{6641B210-6A57-4A74-A484-042D7E10EA14}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
+ EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ids880", "ids880.vcproj", "{7544004D-B821-4577-BA3A-24C38CE27AFA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tt2500", "tt2500.vcproj", "{5CC55A0C-32F3-429F-8661-2E82BF999217}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8}
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -763,6 +774,10 @@ Global
{7544004D-B821-4577-BA3A-24C38CE27AFA}.Debug|Win32.Build.0 = Debug|Win32
{7544004D-B821-4577-BA3A-24C38CE27AFA}.Release|Win32.ActiveCfg = Release|Win32
{7544004D-B821-4577-BA3A-24C38CE27AFA}.Release|Win32.Build.0 = Release|Win32
+ {5CC55A0C-32F3-429F-8661-2E82BF999217}.Debug|Win32.ActiveCfg = Debug|Win32
+ {5CC55A0C-32F3-429F-8661-2E82BF999217}.Debug|Win32.Build.0 = Debug|Win32
+ {5CC55A0C-32F3-429F-8661-2E82BF999217}.Release|Win32.ActiveCfg = Release|Win32
+ {5CC55A0C-32F3-429F-8661-2E82BF999217}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Visual Studio Projects/tt2500.vcproj b/Visual Studio Projects/tt2500.vcproj
new file mode 100644
index 00000000..d573468d
--- /dev/null
+++ b/Visual Studio Projects/tt2500.vcproj
@@ -0,0 +1,387 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/display/display.c b/display/display.c
index 23be9f47..01c1b4d1 100644
--- a/display/display.c
+++ b/display/display.c
@@ -259,7 +259,15 @@ static struct display displays[] = {
* P31 phosphor according to "Heads-Up Display for Flight
* Simulator for Advanced Aircraft"
*/
- { DIS_IMLAC, "Imlac Display", &color_p31, NULL, 1024, 1024 }
+ { DIS_IMLAC, "Imlac Display", &color_p31, NULL, 1024, 1024 },
+
+ /*
+ * TT2500 display
+ * 512x512 addressable points.
+ * P31 phosphor according to "Heads-Up Display for Flight
+ * Simulator for Advanced Aircraft"
+ */
+ { DIS_TT2500, "TT2500 Display", &color_p31, NULL, 512, 512 }
};
/*
diff --git a/display/display.h b/display/display.h
index b680f64b..94fb3a47 100644
--- a/display/display.h
+++ b/display/display.h
@@ -51,6 +51,7 @@ enum display_type {
DIS_III = 111,
DIS_TYPE340 = 340,
DIS_NG = 999,
+ DIS_TT2500 = 2500,
};
/*
diff --git a/display/tt2500.c b/display/tt2500.c
new file mode 100644
index 00000000..6faadb64
--- /dev/null
+++ b/display/tt2500.c
@@ -0,0 +1,115 @@
+/* tt2500.c: TT2500 display interface.
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff
+
+*/
+
+#include "display.h"
+#include "tt2500.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int tt2500_init(void *dev, int debug)
+{
+ return display_init (DIS_TT2500, 1, dev);
+}
+
+static void tt2500_point (int x, int y, int i)
+{
+ if (i < 7)
+ display_point (x, y, DISPLAY_INT_MAX*(7-i)/7, 0);
+}
+
+int tt2500_cycle(int us, int slowdown)
+{
+ return display_age (us, slowdown);
+}
+
+#define ABS(_X) ((_X) >= 0 ? (_X) : -(_X))
+#define SIGN(_X) ((_X) >= 0 ? 1 : -1)
+
+static void
+xline (int x, int y, int x2, int dx, int dy, int i)
+{
+ int ix = SIGN(dx);
+ int iy = SIGN(dy);
+ int ay;
+
+ dx = ABS(dx);
+ dy = ABS(dy);
+
+ ay = dy/2;
+ for (;;) {
+ tt2500_point (x, y, i);
+ if (x == x2)
+ break;
+ if (ay > 0) {
+ y += iy;
+ ay -= dx;
+ }
+ ay += dy;
+ x += ix;
+ }
+}
+
+static void
+yline (int x, int y, int y2, int dx, int dy, int i)
+{
+ int ix = SIGN(dx);
+ int iy = SIGN(dy);
+ int ax;
+
+ dx = ABS(dx);
+ dy = ABS(dy);
+
+ ax = dx/2;
+ for (;;) {
+ tt2500_point (x, y, i);
+ if (y == y2)
+ break;
+ if (ax > 0) {
+ x += ix;
+ ax -= dy;
+ }
+ ax += dx;
+ y += iy;
+ }
+}
+
+void
+tt2500_line (int x1, int y1, int x2, int y2, int i)
+{
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+ if (ABS (dx) > ABS(dy))
+ xline (x1, y1, x2, dx, dy, i);
+ else
+ yline (x1, y1, y2, dx, dy, i);
+}
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/display/tt2500.h b/display/tt2500.h
new file mode 100644
index 00000000..f422a0b2
--- /dev/null
+++ b/display/tt2500.h
@@ -0,0 +1,37 @@
+/* tt2500.h: TT2500 display interface.
+
+ Copyright (c) 2020, Lars Brinkhoff.
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff
+*/
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern int tt2500_init(void *, int);
+extern int tt2500_cycle(int, int);
+extern void tt2500_line(int x1, int y1, int x2, int y2, int i);
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/makefile b/makefile
index 0da33c1d..afec4b1d 100644
--- a/makefile
+++ b/makefile
@@ -115,6 +115,10 @@ endif
ifneq (,$(findstring imlac,${MAKECMDGOALS}))
VIDEO_USEFUL = true
endif
+# building the TT2500 needs video support
+ifneq (,$(findstring tt2500,${MAKECMDGOALS}))
+ VIDEO_USEFUL = true
+endif
# building the PDP6, KA10 or KI10 needs video support
ifneq (,$(or $(findstring pdp6,${MAKECMDGOALS}),$(findstring pdp10-ka,${MAKECMDGOALS}),$(findstring pdp10-ki,${MAKECMDGOALS})))
VIDEO_USEFUL = true
@@ -642,6 +646,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin)
DISPLAYNG = ${DISPLAYD}/ng.c
DISPLAYIII = ${DISPLAYD}/iii.c
DISPLAYIMLAC = ${DISPLAYD}/imlac.c
+ DISPLAYTT2500 = ${DISPLAYD}/tt2500.c
DISPLAY_OPT += -DUSE_DISPLAY $(VIDEO_CCDEFS) $(VIDEO_LDFLAGS)
$(info using libSDL2: $(call find_include,SDL2/SDL))
ifeq (Darwin,$(OSTYPE))
@@ -1561,6 +1566,14 @@ IMLAC = ${IMLACD}/imlac_sys.c ${IMLACD}/imlac_cpu.c \
IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT}
+TT2500D = ${SIMHD}/tt2500
+TT2500 = ${TT2500D}/tt2500_sys.c ${TT2500D}/tt2500_cpu.c \
+ ${TT2500D}/tt2500_dpy.c ${TT2500D}/tt2500_crt.c ${TT2500D}/tt2500_tv.c \
+ ${TT2500D}/tt2500_key.c ${TT2500D}/tt2500_uart.c ${TT2500D}/tt2500_rom.c \
+ ${DISPLAYL} ${DISPLAYTT2500}
+TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT}
+
+
PDP8D = ${SIMHD}/PDP8
PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \
${PDP8D}/pdp8_dt.c ${PDP8D}/pdp8_lp.c ${PDP8D}/pdp8_mt.c \
@@ -2247,6 +2260,15 @@ ifneq (,$(call find_test,${IMLAC},imlac))
$@ $(call find_test,${IMLACD},imlac) ${TEST_ARG}
endif
+tt2500 : ${BIN}tt2500${EXE}
+
+${BIN}tt2500${EXE} : ${TT2500} ${SIM}
+ ${MKDIRBIN}
+ ${CC} ${TT2500} ${SIM} ${TT2500_OPT} ${CC_OUTSPEC} ${LDFLAGS}
+ifneq (,$(call find_test,${TT2500},tt2500))
+ $@ $(call find_test,${TT2500D},tt2500) ${TEST_ARG}
+endif
+
pdp11 : ${BIN}pdp11${EXE}
${BIN}pdp11${EXE} : ${PDP11} ${SIM}
diff --git a/tt2500/tests/test.ascii b/tt2500/tests/test.ascii
new file mode 100644
index 00000000..5e99ee69
--- /dev/null
+++ b/tt2500/tests/test.ascii
@@ -0,0 +1 @@
+LOGO@@@@@@@@JAN@@A@A@@@AEJ\JtbJAN@@@EAA
diff --git a/tt2500/tests/tt2500_test.ini b/tt2500/tests/tt2500_test.ini
new file mode 100644
index 00000000..51010d57
--- /dev/null
+++ b/tt2500/tests/tt2500_test.ini
@@ -0,0 +1,17 @@
+cd %~p0
+
+set tv disabled
+set crt disabled
+
+on error goto failed
+
+break 1235
+
+load test.ascii
+run 100
+
+:failed
+if (PC != 669) echof "\n*** TEST FAILED\n"; exit 1
+
+echof "\n*** TEST PASSED\n"
+exit 0
diff --git a/tt2500/tt2500_cpu.c b/tt2500/tt2500_cpu.c
new file mode 100644
index 00000000..1c6cb259
--- /dev/null
+++ b/tt2500/tt2500_cpu.c
@@ -0,0 +1,708 @@
+/* tt2500_cpu.c: TT2500 CPU simulator
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include "tt2500_defs.h"
+#ifdef USE_DISPLAY
+#include "display/display.h"
+#endif
+
+
+/* Debug */
+#define DBG_CPU 0001
+#define DBG_FET 0002
+#define DBG_EXE 0004
+#define DBG_STATE 0010
+#define DBG_INT 0020
+
+/* CPU state. */
+static uint16 PC;
+static uint16 IR;
+static int ROM = 1;
+int C, V, N, Z;
+static uint16 IM = 0;
+static uint16 STACK[16];
+static uint16 SP = 0;
+static uint16 R[64];
+static uint16 RES, FLAGS, INTS, STARS;
+static uint16 new_XR;
+
+static int halt;
+
+typedef struct {
+ uint16 PC;
+ uint16 IR;
+ uint16 MA;
+ uint16 MB;
+ uint16 AC;
+ uint16 L;
+} HISTORY;
+static HISTORY *history = NULL;
+static uint32 history_i, history_m, history_n;
+
+/* Function declaration. */
+static t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
+static t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
+static t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw);
+static t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw);
+static t_stat cpu_reset (DEVICE *dptr);
+static void cpu_update (void);
+
+static UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 020000) };
+
+static BITFIELD flags_bits[] = {
+ BIT(KB),
+ BIT(RSD),
+ BITNCF(2),
+ ENDBITS
+};
+
+static BITFIELD ints_bits[] = {
+ BIT(2KHZ),
+ BIT(RRD),
+ BIT(60HZ),
+ BITNC,
+ ENDBITS
+};
+
+static BITFIELD stars_bits[] = {
+ BIT(WRAP),
+ BIT(MINUS1),
+ ENDBITS
+};
+
+REG cpu_reg[] = {
+ { ORDATAD (PC, PC, 13, "Program Counter") },
+ { ORDATAD (ROM, ROM, 1, "Read from ROM") },
+ { ORDATAD (IR, IR, 16, "Instruction") },
+ { ORDATAD (XR, R[REG_XR], 12, "Execute register") },
+ { ORDATAD (A, R[REG_ALATCH], 16, "A latch") },
+ { ORDATAD (IM, IM, 16, "Immediate") },
+ { ORDATAD (RES, RES, 16, "Result") },
+ { HRDATADF (FLAGS, FLAGS, 4, "Flags", flags_bits ) },
+ { HRDATADF (INTS, INTS, 4, "Interrupts", ints_bits ) },
+ { HRDATADF (STARS, STARS, 4, "Stars", stars_bits ) },
+ { BRDATAD (REG, R, 8, 16, 64, "Registers") },
+ { NULL }
+};
+
+static MTAB cpu_mod[] = {
+ { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle },
+ { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL },
+ { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
+ &cpu_set_hist, &cpu_show_hist },
+ { 0 }
+};
+
+static DEBTAB cpu_deb[] = {
+ { "CPU", DBG_CPU },
+ { "FETCH", DBG_FET },
+ { "EXECUTE", DBG_EXE },
+ { "STATE", DBG_STATE },
+ { "INT", DBG_INT },
+ { NULL, 0 }
+};
+
+DEVICE cpu_dev = {
+ "CPU", &cpu_unit, cpu_reg, cpu_mod,
+ 0, 8, 16, 1, 8, 16,
+ &cpu_ex, &cpu_dep, &cpu_reset,
+ NULL, NULL, NULL, NULL, DEV_DEBUG, 0, cpu_deb,
+ NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static uint16 crm_read (uint16 addr)
+{
+ if (ROM && addr < 32)
+ return tt2500_rom[addr];
+
+ ROM = 0;
+ return CRM[addr];
+}
+
+static uint16 bus_read (uint16 reg)
+{
+ if ((reg & 060) == 020) {
+ RES = dev_tab[reg]->read (reg);
+ sim_debug (DBG_STATE, &cpu_dev, "%06o <= BUS[%02o]\n", RES, reg);
+ } else {
+ RES = R[reg];
+ }
+ return RES;
+}
+
+static uint16 cpu_rot (uint16 data, uint16 n)
+{
+ return (data >> n) + (data << (16 - n));
+}
+
+static uint16 cpu_ars (uint16 data, uint16 n)
+{
+ uint32 sign = 0;
+ if (data & 0100000)
+ sign = 0177777 << 16;
+ return (data >> n) + (sign >> n);
+}
+
+uint16 cpu_alu (uint16 insn, uint16 op, uint16 adata, uint16 bdata)
+{
+ uint32 result;
+
+ V = 0;
+
+ switch (op) {
+ case ALU_A: result = adata; break;
+ case ALU_ANDN: result = adata & ~bdata; break;
+ case ALU_AND: result = adata & bdata; break;
+ case ALU_NOR: result = (~(adata | bdata)) & 0177777; break;
+ case ALU_IOR: result = adata | bdata; break;
+ case ALU_XOR: result = adata ^ bdata; break;
+ case ALU_MROT: result = adata & cpu_rot (R[R[REG_ALATCH]], insn & 017); break;
+ case ALU_ROT: result = cpu_rot (adata, insn & 017); break;
+ case ALU_DEC: result = adata - 1; V = (result == 077777); break;
+ case ALU_XADD: bdata += C; /* Fall through. */
+ case ALU_ADD:
+ result = adata + bdata;
+ V = (((~adata ^ bdata) & (bdata ^ result)) >> 15) & 1;
+ break;
+ case ALU_XSUB: bdata += C; /* Fall through. */
+ case ALU_SUB:
+ result = adata - bdata;
+ V = (((adata ^ bdata) & (~bdata ^ result)) >> 15) & 1;
+ break;
+ case ALU_INC: result = adata + 1; V = (result == 0100000); break;
+ case ALU_ARS: result = cpu_ars (adata, insn & 017); break;
+ default: result = 0; break;
+ }
+
+ C = !!(result & 0200000);
+ result &= 0177777;
+ N = !!(result & 0100000);
+ Z = (result == 0);
+
+ sim_debug (DBG_STATE, &cpu_dev, "ALU: %06o %06o => %06o (%c%c%c%c)\n",
+ adata, bdata, result,
+ C ? 'C' : '-',
+ V ? 'V' : '-',
+ N ? 'N' : '-',
+ Z ? 'Z' : '-');
+
+ return result;
+}
+
+static uint16 mem_read (uint16 address)
+{
+ if ((address & 0170000) == 0170000 && (DSR & DSR_TVON) == 0)
+ return FONT[address - 0170000];
+ else
+ return MEM[address];
+}
+
+static void mem_write (uint16 address, uint16 data)
+{
+ if ((address & 0170000) == 0170000 && (DSR & DSR_TVON) == 0)
+ FONT[address - 0170000] = (uint8)data;
+ else
+ MEM[address] = data;
+}
+
+static void cpu_reg_op (uint16 insn)
+{
+ uint16 a = (insn >> 6) & 7;
+ uint16 b = insn & 7;
+ uint16 alu_op;
+ uint16 adata;
+ uint16 bdata;
+ uint16 result;
+
+ if (IM != 0) {
+ adata = IR;
+ IM = 0;
+ } else if ((insn & 01000) != 0 && (insn & 030000) != 020000) {
+ IM = IR;
+ return;
+ } else
+ adata = R[a];
+ if (insn & 010)
+ bdata = 0;
+ else
+ bdata = R[b];
+
+ alu_op = insn & 06060;
+
+ if ((insn & 030000) == 020000) {
+ if ((insn & 01000) == 0) {
+ if (insn & 04000) {
+ sim_debug (DBG_STATE, &cpu_dev, "MEM[%06o] <= %06o <= REG[%02o]\n",
+ adata, bdata, b);
+ mem_write (adata, bdata);
+ } else {
+ bdata = R[b] = mem_read(adata);
+ sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o <= MEM[%06o]\n",
+ b, bdata, adata);
+ }
+ }
+ if (alu_op != 0)
+ alu_op |= 04000;
+ }
+
+ result = cpu_alu (insn, alu_op, adata, bdata);
+
+ switch (insn & 030000) {
+ case 030000:
+ if (C) {
+ case 000000:
+ sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", a, result);
+ R[a] = result;
+ }
+ break;
+ case 010000:
+ break;
+ case 020000:
+ sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", a, result);
+ R[a] = result;
+ if (insn & 01000) {
+ IM = 0;
+ if (insn & 04000) {
+ sim_debug (DBG_STATE, &cpu_dev, "CWRITE[%04o]\n", RES);
+ CRM[RES] = result;
+ } else {
+ sim_debug (DBG_STATE, &cpu_dev, "CREAD[%04o]\n", RES);
+ R[a] = crm_read (RES);
+ V = 0;
+ C = 0;
+ N = !!(R[a] & 0100000);
+ Z = (R[a] == 0);
+ sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o (%c%c%c%c)\n",
+ a, R[a],
+ C ? 'C' : '-',
+ V ? 'V' : '-',
+ N ? 'N' : '-',
+ Z ? 'Z' : '-');
+ }
+ }
+ break;
+ }
+
+ R[REG_ALATCH] = a;
+ sim_debug (DBG_STATE, &cpu_dev, "A <= %o\n", R[REG_ALATCH]);
+ RES = result;
+}
+
+static void cpu_jump (uint16 insn, int push)
+{
+ if (push) {
+ STACK[SP] = PC;
+ sim_debug (DBG_STATE, &cpu_dev, "STACK[%02o] <= %04o\n", SP, PC);
+ SP = (SP + 1) & 017;
+ }
+ PC = insn & 07777;
+}
+
+static void cpu_dis (uint16 insn)
+{
+ uint16 data;
+ uint16 mask;
+
+ switch (insn & 01400) {
+ case 00000:
+ data = ((RES >> 15) & 1) | ((RES >> 13) & 2) |
+ ((RES >> 11) & 4) | ((RES >> 9) & 8);
+ break;
+ case 00400:
+ data = FLAGS;
+ break;
+ case 01000:
+ data = INTS;
+ break;
+ case 01400:
+ data = STARS;
+ break;
+ default:
+ return;
+ }
+
+ mask = (insn >> 4) & 017;
+ PC = PC + (data & ~mask);
+}
+
+static void cpu_popj (void)
+{
+ SP = (SP - 1) & 017;
+ PC = STACK[SP];
+ sim_debug (DBG_STATE, &cpu_dev, "PC <= %04o <= STACK[%02o]\n", PC, SP);
+}
+
+static void bus_write (uint16 reg, uint16 data)
+{
+ switch (reg) {
+ case 012: PC = data & 07777; break;
+ case 014: dpy_magic (data, &R[2], &R[3], R[4], R[5]); break;
+ case 015: dpy_chartv (data); break;
+ case 016: cpu_popj (); break;
+ case 023: new_XR = data; break;
+ case 020: case 021: case 022: case 024: case 025: case 026: case 027:
+ case 030: case 031: case 032: case 033: case 034: case 035: case 036: case 037:
+ sim_debug (DBG_STATE, &cpu_dev, "BUS[%02o] <= %06o\n", reg, data);
+ dev_tab[reg]->write (reg, data);
+ break;
+ default: /* 40-77 is scratchpad. */
+ R[reg] = data;
+ sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", reg, data);
+ break;
+ }
+}
+
+static void cpu_bus (uint16 insn)
+{
+ uint16 a = (insn >> 6) & 7;
+ uint16 b = insn & 077;
+ uint16 bb = insn & 01000;
+
+ if ((insn & 0176000) == 0072000) {
+ cpu_dis (insn);
+ return;
+ }
+
+ if (bb) {
+ switch (a) {
+ case 2: PC = RES;
+ case 4: dpy_magic (RES, &R[2], &R[3], R[4], R[5]); return;
+ case 5: dpy_chartv (R[b]); return;
+ case 6: cpu_popj (); return;
+ default:
+ sim_debug (DBG_CPU, &cpu_dev, "Unknown instruction: %06o\n", IR);
+ break;
+ }
+ }
+
+ if (insn & 02000) {
+ bus_write (b, R[a]);
+ } else {
+ R[a] = bus_read (b);
+ sim_debug (DBG_STATE, &cpu_dev, "REG[%02o] <= %06o\n", a, R[a]);
+ }
+}
+
+static void cpu_branch (uint16 insn)
+{
+ uint16 target = insn & 03777;
+ int jump = 0;
+
+ switch (insn & 070000) {
+ case 000000: jump = !C; break;
+ case 010000: jump = !V; break;
+ case 020000: jump = N; break;
+ case 030000: jump = !Z; break;
+ case 040000: jump = N ^ V; break;
+ case 050000: jump = INTS; break;
+ case 060000: jump = !(R[REG_XR] & 04000); new_XR = R[REG_XR] + 1; break;
+ case 070000: jump = FLAGS; break;
+ }
+
+ if (insn & 04000)
+ jump = !jump;
+
+ if (jump) {
+ if (insn & 02000)
+ target -= 04000;
+ PC = (PC + target) & 07777;
+ }
+}
+
+static void
+cpu_fetch (void)
+{
+ /* Fetch cycle. */
+ IR = crm_read (PC);
+ sim_debug (DBG_FET, &cpu_dev, "%04o: %06o\n", PC, IR);
+ sim_interval--;
+
+ if (history) {
+ history[history_i].PC = PC;
+ history[history_i].IR = IR;
+ }
+
+ PC = (PC + 1) & 07777;
+}
+
+static void cpu_update (void)
+{
+ new_XR &= 07777;
+ if (R[REG_XR] != new_XR)
+ sim_debug (DBG_STATE, &cpu_dev, "XR <= %04o\n", new_XR);
+ R[REG_XR] = new_XR;
+ R[011] = (new_XR >> 6) & 077;
+ R[012] = new_XR & 077;
+ R[015] = (new_XR >> 6) & 7;
+ R[016] = new_XR & 7;
+}
+
+static void
+cpu_execute (void)
+{
+ if (IM != 0) {
+ sim_debug (DBG_EXE, &cpu_dev, "%06o\n", IM);
+ cpu_reg_op (IM);
+ return;
+ }
+
+ if (cpu_dev.dctrl & DBG_EXE) {
+ t_value val = IR;
+ sim_debug (DBG_EXE, &cpu_dev, "%06o (", IR);
+ fprint_sym (sim_deb, PC-1, &val, NULL, SWMASK ('M'));
+ sim_debug (DBG_EXE, &cpu_dev, ")\n");
+ }
+
+ switch ((IR >> 12) & 017) {
+ case 000: case 001: case 002: case 003:
+ cpu_reg_op (IR);
+ break;
+ case 004:
+ cpu_jump (IR, 1);
+ break;
+ case 005:
+ cpu_jump (IR, 0);
+ break;
+ case 007:
+ cpu_bus (IR);
+ break;
+ case 010: case 011: case 012: case 013:
+ case 014: case 015: case 016: case 017:
+ cpu_branch (IR);
+ break;
+ default:
+ sim_debug (DBG_CPU, &cpu_dev, "Unknown instruction: %06o\n", IR);
+ break;
+ }
+}
+
+static void
+cpu_insn (void)
+{
+ cpu_update ();
+ cpu_execute ();
+ cpu_fetch ();
+
+ if (history) {
+ history_i = (history_i + 1) % history_m;
+ if (history_n < history_m)
+ history_n++;
+ }
+}
+
+t_stat sim_instr (void)
+{
+ t_stat reason;
+
+ if ((reason = build_dev_tab ()) != SCPE_OK)
+ return reason;
+
+ halt = 0;
+
+ for (;;) {
+ AIO_CHECK_EVENT;
+ if (sim_interval <= 0) {
+ if ((reason = sim_process_event()) != SCPE_OK)
+ return reason;
+ }
+
+ if (sim_brk_summ && sim_brk_test(PC, SWMASK('E')))
+ return STOP_IBKPT;
+
+ cpu_insn ();
+
+ if (sim_step != 0) {
+ if (--sim_step == 0)
+ return SCPE_STEP;
+ }
+
+ if (halt)
+ return STOP_HALT;
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat
+cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
+{
+ t_stat r;
+ uint32 x;
+
+ if (cptr == NULL)
+ return SCPE_ARG;
+
+ x = get_uint (cptr, 10, 1000000, &r);
+ if (r != SCPE_OK)
+ return r;
+
+ history = (HISTORY *)calloc (x, sizeof (*history));
+ if (history == NULL)
+ return SCPE_MEM;
+
+ history_m = x;
+ history_n = 0;
+ history_i = 0;
+ return SCPE_OK;
+}
+
+static t_stat
+cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
+{
+ t_value insn;
+ uint32 i, j;
+
+ fprintf (st, "PC____ IR____\n");
+
+ if (history_i >= history_n)
+ j = history_i - history_n;
+ else
+ j = history_m + history_i - history_n;
+
+ for (i = 0; i < history_n; i++) {
+ fprintf (st, "%06o %06o ",
+ history[j].PC,
+ history[j].IR);
+ insn = history[j].IR;
+ fprint_sym (st, history[j].PC, &insn, NULL, SWMASK ('M'));
+ fputc ('\n', st);
+ j = (j + 1) % history_m;
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw)
+{
+ if (vptr == NULL)
+ return SCPE_ARG;
+ if (sw & SIM_SW_STOP)
+ sw |= SWMASK ('C');
+ if (sw & SWMASK ('C')) {
+ if (ea >= 4096)
+ return SCPE_NXM;
+ *vptr = crm_read (ea);
+ } else {
+ if (ea >= 65536)
+ return SCPE_NXM;
+ *vptr = mem_read (ea);
+ }
+ return SCPE_OK;
+}
+
+static t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw)
+{
+ if (sw & SWMASK ('C')) {
+ if (ea >= 4096)
+ return SCPE_NXM;
+ CRM[ea] = val & 0177777;
+ } else {
+ if (ea >= 65536)
+ return SCPE_NXM;
+ mem_write (ea, val & 0177777);
+ }
+ return SCPE_OK;
+}
+
+static t_bool cpu_is_pc_a_subroutine_call (t_addr **ret_addrs)
+{
+ static t_addr returns[2] = { 0, 0 };
+
+ if ((CRM[PC] & 0170000) == 040000) {
+ returns[0] = PC + 1;
+ *ret_addrs = returns;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static t_stat
+cpu_reset (DEVICE *dptr)
+{
+ ROM = 1;
+ PC = 0;
+ IR = 010000;
+ IM = 0;
+ SP = 0;
+ C = V = N = Z = 0;
+ new_XR = 0;
+ RES = FLAGS = INTS = STARS = 0;
+
+ sim_brk_types = SWMASK ('E');
+ sim_brk_dflt = SWMASK ('E');
+ sim_vm_is_subroutine_call = &cpu_is_pc_a_subroutine_call;
+ return SCPE_OK;
+}
+
+static const char *flag_name (uint16 flag)
+{
+ switch (flag) {
+ case FLAG_KB: return "KB";
+ case FLAG_RSD: return "RSD";
+ case INT_2KHZ: return "2KHZ";
+ case INT_RRD: return "RRD";
+ case INT_60HZ: return "60HZ";
+ case STAR_WRAP: return "WRAP";
+ case STAR_MINUS1: return "MINUS1";
+ default: return "(unknown)";
+ }
+}
+
+void flag_on (uint16 flag)
+{
+ sim_debug (DBG_INT, &cpu_dev, "Flag on %03o (%s)\n", flag, flag_name (flag));
+ FLAGS |= flag & 017;
+ flag >>= 4;
+ INTS |= flag & 017;
+ flag >>= 4;
+ STARS |= flag & 017;
+}
+
+void flag_off (uint16 flag)
+{
+ sim_debug (DBG_INT, &cpu_dev, "Flag off %03o (%s)\n",
+ flag, flag_name (flag));
+ FLAGS &= ~(flag & 017);
+ flag >>= 4;
+ INTS &= ~(flag & 017);
+ flag >>= 4;
+ STARS &= ~(flag & 017);
+}
+
+#ifdef USE_DISPLAY
+/* Called from display library to get data switches. */
+void
+cpu_get_switches (unsigned long *p1, unsigned long *p2)
+{
+}
+
+/* Called from display library to set data switches. */
+void
+cpu_set_switches (unsigned long p1, unsigned long p2)
+{
+}
+#endif
diff --git a/tt2500/tt2500_crt.c b/tt2500/tt2500_crt.c
new file mode 100644
index 00000000..9f694161
--- /dev/null
+++ b/tt2500/tt2500_crt.c
@@ -0,0 +1,106 @@
+/* tt2500_crt.c: TT2500 CRT display
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include "tt2500_defs.h"
+#include "sim_video.h"
+#include "display/tt2500.h"
+#include "display/display.h"
+
+/* Function declaration. */
+static t_stat crt_svc (UNIT *uptr);
+static t_stat crt_reset (DEVICE *dptr);
+
+/* Debug */
+#define DBG 0001
+
+static UNIT crt_unit = {
+ UDATA (&crt_svc, UNIT_IDLE, 0)
+};
+
+static DEBTAB crt_deb[] = {
+ { "DBG", DBG },
+ { "KEY", SIM_VID_DBG_KEY },
+ { NULL, 0 }
+};
+
+#ifdef USE_DISPLAY
+#define CRT_DIS 0
+#else
+#define CRT_DIS DEV_DIS
+#endif
+
+DEVICE crt_dev = {
+ "CRT", &crt_unit, NULL, NULL,
+ 1, 8, 16, 1, 8, 16,
+ NULL, NULL, &crt_reset,
+ NULL, NULL, NULL,
+ NULL, DEV_DISABLE | DEV_DEBUG | CRT_DIS, 0, crt_deb,
+ NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static t_stat
+crt_svc(UNIT *uptr)
+{
+#ifdef USE_DISPLAY
+ tt2500_cycle (100, 0);
+ if (!display_is_blank ())
+ sim_activate_after (uptr, 100);
+ if (dpy_quit) {
+ dpy_quit = FALSE;
+ return SCPE_STOP;
+ }
+#endif
+ return SCPE_OK;
+}
+
+static t_stat
+crt_reset (DEVICE *dptr)
+{
+#ifdef USE_DISPLAY
+ if (dptr->flags & DEV_DIS || sim_switches & SWMASK('P')) {
+ display_close (dptr);
+ sim_cancel (&crt_unit);
+ } else {
+ display_reset ();
+ tt2500_init (dptr, 1);
+ vid_register_quit_callback (&dpy_quit_callback);
+ }
+#endif
+ return SCPE_OK;
+}
+
+void
+crt_line (uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint16 i)
+{
+ sim_debug (DBG, &crt_dev, "Line %d,%d - %d,%d @ %d\n", x1, y1, x2, y2, i);
+#ifdef USE_DISPLAY
+ if ((crt_dev.flags & DEV_DIS) == 0 && !sim_is_active (&crt_unit))
+ sim_activate_abs (&crt_unit, 0);
+ if (crt_dev.flags & DEV_DIS)
+ return;
+ tt2500_line (x1, y1, x2, y2, i);
+#endif
+}
diff --git a/tt2500/tt2500_defs.h b/tt2500/tt2500_defs.h
new file mode 100644
index 00000000..bd3a7730
--- /dev/null
+++ b/tt2500/tt2500_defs.h
@@ -0,0 +1,104 @@
+/* tt2500_defs.h: TT2500 simulator definitions
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+
+ 24-Sep-20 LB New simulator.
+*/
+
+#ifndef TT2500_DEFS_H_
+#define TT2500_DEFS_H_ 0
+
+#include "sim_defs.h"
+
+#define STOP_HALT 1
+#define STOP_IBKPT 2
+#define STOP_ACCESS 3
+
+#define FLAG_KB 001
+#define FLAG_RSD 002
+#define INT_2KHZ (001 << 4)
+#define INT_RRD (002 << 4)
+#define INT_60HZ (004 << 4)
+#define STAR_WRAP (001 << 8)
+#define STAR_MINUS1 (002 << 8)
+
+/* ALU operations. */
+#define ALU_A 00000
+#define ALU_ANDN 00020
+#define ALU_AND 00040
+#define ALU_NOR 00060
+#define ALU_IOR 02000
+#define ALU_XOR 02020
+#define ALU_MROT 02040
+#define ALU_ROT 04000
+#define ALU_DEC 04020
+#define ALU_XADD 04040
+#define ALU_ADD 04060
+#define ALU_SUB 06000
+#define ALU_XSUB 06020
+#define ALU_INC 06040
+#define ALU_ARS 06060
+
+/* Register. */
+#define REG_ALATCH 010
+#define REG_YCOR 020
+#define REG_XCOR 021
+#define REG_SCROLL 022
+#define REG_XR 023
+#define REG_UART 024
+#define REG_DSR 025
+#define REG_KEY 026
+
+/* DSR TV on bit. */
+#define DSR_TVON 0010000
+
+typedef struct {
+ uint16 reg[4];
+ uint16 (*read)(uint16 reg);
+ void (*write)(uint16 reg, uint16 data);
+} TTDEV;
+
+extern t_bool build_dev_tab (void);
+extern void flag_on (uint16 flag);
+extern void flag_off (uint16 flag);
+extern uint16 cpu_alu (uint16 insn, uint16 op, uint16 adata, uint16 bdata);
+extern void dpy_magic (uint16 data, uint16 *r2, uint16 *r3, uint16 r4, uint16 r5);
+extern void dpy_chartv (uint16 data);
+extern void dpy_quit_callback (void);
+extern void crt_line (uint16 x1, uint16 y1, uint16 x2, uint16 y2, uint16 i);
+extern void tv_line (int row, uint8 *data, uint8 *chars);
+extern void tv_refresh (void);
+
+extern REG cpu_reg[];
+extern uint16 MEM[], CRM[];
+extern uint16 DSR;
+extern uint8 FONT[];
+extern DEVICE cpu_dev, dpy_dev, crt_dev, tv_dev, key_dev, uart_dev;
+extern TTDEV *dev_tab[];
+extern int C, V, N, Z;
+extern int dpy_quit;
+
+extern uint16 tt2500_rom[];
+
+#endif /* TT2500_DEFS_H_ */
diff --git a/tt2500/tt2500_dpy.c b/tt2500/tt2500_dpy.c
new file mode 100644
index 00000000..7d9ff112
--- /dev/null
+++ b/tt2500/tt2500_dpy.c
@@ -0,0 +1,284 @@
+/* imlac_dpy.c: TT2500 display.
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include
+#include "tt2500_defs.h"
+
+/* Debug */
+#define DBG_REG 0001
+#define DBG_VEC 0002
+#define DBG_TXT 0004
+#define DBG_60HZ 0010
+#define DBG_2KHZ 0020
+
+/* DSR */
+#define DSR_VEC 0160000
+#define DSR_TXT 0006000
+
+static t_stat dpy_60hz_svc (UNIT *uptr);
+static t_stat dpy_2khz_svc (UNIT *uptr);
+static t_stat dpy_reset (DEVICE *dptr);
+static void dpy_text_line (void);
+
+static uint8 black[4096], green[4096];
+uint8 FONT[4096];
+uint8 LINE[73];
+static uint16 YCOR;
+static uint16 XCOR;
+static uint16 SCROLL;
+uint16 DSR = 0;
+static uint16 ROW = 0;
+static uint16 COL = 0;
+int dpy_quit = FALSE;
+
+/* DSR
+160000 Vector beam: 0=on, 7=off.
+ 10000 TV on.
+ 6000 Color mode.
+ 0000
+ 2000 Dark background.
+ 4000
+ 6000
+
+tv-off tv-off-const 1 001 (unused)
+ tv-green-const 2 010 (unused)
+
+tv-blank green-blank-const 4 100 (flash)
+tv-active tv-dark-const 5 101 (normal) "green chars on field ebony"
+ dark-blank-const 7 111 (unused)
+*/
+
+/* Function declaration. */
+static uint16 dpy_read (uint16);
+static void dpy_write (uint16, uint16);
+
+static UNIT dpy_unit = {
+ UDATA (dpy_2khz_svc, UNIT_IDLE, 0)
+};
+
+static BITFIELD dsr_bits[] = {
+ BITNCF(10),
+ BITF(TXT,2),
+ BITF(ON,1),
+ BITF(VEC,3),
+ ENDBITS
+};
+
+static REG dpy_reg[] = {
+ { ORDATAD (YCOR, YCOR, 9, "Y coordinate") },
+ { ORDATAD (XCOR, XCOR, 9, "X coordinate") },
+ { ORDATAD (SCROLL, SCROLL, 16, "Scroll") },
+ { ORDATADF (DSR, DSR, 16, "Status register", dsr_bits ) },
+ { NULL }
+};
+
+static DEBTAB dpy_deb[] = {
+ { "REG", DBG_REG },
+ { "VEC", DBG_VEC },
+ { "TXT", DBG_TXT },
+ { "60HZ", DBG_60HZ },
+ { "2KHZ", DBG_2KHZ },
+ { NULL, 0 }
+};
+
+static TTDEV dpy_ttdev = {
+ { REG_YCOR, REG_XCOR, REG_SCROLL, REG_DSR },
+ dpy_read,
+ dpy_write,
+};
+
+DEVICE dpy_dev = {
+ "DPY", &dpy_unit, dpy_reg, NULL,
+ 1, 8, 16, 1, 8, 16,
+ NULL, NULL, dpy_reset,
+ NULL, NULL, NULL, &dpy_ttdev, DEV_DEBUG, 0, dpy_deb,
+ NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+/* To ensure the two clocks are always in sync, dpy_60hz_svc is called
+ from dpy_2khz_svc. */
+
+static t_stat dpy_60hz_svc (UNIT *uptr)
+{
+ sim_debug (DBG_60HZ, &dpy_dev, "60 Hz interrupt\n");
+ flag_on (INT_60HZ);
+ tv_refresh ();
+ return SCPE_OK;
+}
+
+static t_stat dpy_2khz_svc (UNIT *uptr)
+{
+ static int n = 0;
+ t_stat r;
+
+ /* 30 text lines, plus one extra for the page refresh interrupt, per
+ 60 Hz page comes to 538 microseconds. */
+ r = sim_activate_after (uptr, 538);
+ if (r != SCPE_OK)
+ return r;
+
+ if (++n == 31) {
+ n = 0;
+ return dpy_60hz_svc (&dpy_unit);
+ }
+
+ sim_debug (DBG_2KHZ, &dpy_dev, "2 kHz interrupt\n");
+ dpy_text_line ();
+ flag_on (INT_2KHZ);
+ return SCPE_OK;
+}
+
+static uint16 dpy_read (uint16 reg)
+{
+ uint16 data = 0;
+ switch (reg) {
+ case REG_YCOR:
+ data = YCOR;
+ sim_debug (DBG_REG, &dpy_dev, "%06o <= YCOR\n", data);
+ break;
+ case REG_XCOR:
+ data = XCOR;
+ sim_debug (DBG_REG, &dpy_dev, "%06o <= XCOR\n", data);
+ break;
+ case REG_SCROLL:
+ data = SCROLL;
+ sim_debug (DBG_REG, &dpy_dev, "%06o <= SCROLL\n", data);
+ break;
+ case REG_DSR:
+ data = DSR;
+ sim_debug (DBG_REG, &dpy_dev, "DSR <= %06o\n", data);
+ break;
+ }
+ return data;
+}
+
+static void dpy_write (uint16 reg, uint16 data)
+{
+ switch (reg) {
+ case REG_YCOR:
+ sim_debug (DBG_REG, &dpy_dev, "YCOR <= %06o\n", data);
+ YCOR = data;
+ break;
+ case REG_XCOR:
+ sim_debug (DBG_REG, &dpy_dev, "XCOR <= %06o\n", data);
+ XCOR = data;
+ break;
+ case REG_SCROLL:
+ sim_debug (DBG_REG, &dpy_dev, "SCROLL <= %06o\n", data);
+ SCROLL = data;
+ flag_off (INT_60HZ);
+ COL = 0;
+ ROW = 29;
+ break;
+ case REG_DSR:
+ sim_debug (DBG_REG, &dpy_dev, "DSR <= %06o\n", data);
+ DSR = data;
+ break;
+ }
+}
+
+static t_stat dpy_reset (DEVICE *dptr)
+{
+ memset (black, 0, sizeof black);
+ memset (green, 0377, sizeof green);
+ sim_activate_abs (&dpy_unit, 0);
+ return SCPE_OK;
+}
+
+void dpy_magic (uint16 xr, uint16 *r2, uint16 *r3, uint16 r4, uint16 r5)
+{
+ uint32 x = *r2, y = *r3;
+ uint16 x0, y0, x1, y1, dx, dy;
+
+ sim_debug (DBG_VEC, &dpy_dev, "MAGIC %06o\n", xr);
+ sim_debug (DBG_VEC, &dpy_dev, "X,YCOR = %06o, %06o\n", XCOR, YCOR);
+ sim_debug (DBG_VEC, &dpy_dev, "X,YPOS = %06o, %06o\n", *r2, *r3);
+ sim_debug (DBG_VEC, &dpy_dev, "SIN,COS = %06o, %06o\n", r4, r5);
+
+ x0 = x1 = XCOR;
+ y0 = y1 = YCOR;
+ dx = (r4 & 0100000) ? -1 : 1;
+ dy = (r5 & 0100000) ? -1 : 1;
+
+ flag_on (STAR_WRAP);
+ while (xr & 04000) {
+ sim_interval--;
+ x = cpu_alu (0, ALU_ADD, x, r4);
+ if (V)
+ x1 = (XCOR += dx);
+
+ sim_interval--;
+ y = cpu_alu (0, ALU_ADD, y, r5);
+ if (V)
+ y1 = (YCOR += dy);
+
+ if ((XCOR & 01000) != 0 || (YCOR & 01000) != 0) {
+ x1 -= dx;
+ y1 -= dy;
+ flag_off (STAR_WRAP);
+ break;
+ }
+
+ xr++;
+ }
+ crt_line (x0, y0, x1, y1, DSR >> 13);
+
+ *r2 = x;
+ *r3 = y;
+}
+
+void dpy_chartv (uint16 data)
+{
+ sim_debug (DBG_TXT, &dpy_dev, "CHARTV %03o (%06o)\n", data & 0377, data);
+ flag_off (INT_2KHZ);
+ memmove (LINE, LINE + 1, 72);
+ LINE[72] = (uint8)data;
+}
+
+static void dpy_text_line (void)
+{
+ uint8 *font;
+
+ if ((DSR & 016000) == 010000)
+ font = green;
+ else if (DSR & 010000)
+ font = FONT;
+ else
+ font = black;
+
+ tv_line (ROW, LINE, font);
+ if (dpy_dev.dctrl)
+ tv_refresh ();
+
+ ROW++;
+ if (ROW == 30)
+ ROW = 0;
+}
+
+void dpy_quit_callback (void)
+{
+ dpy_quit = TRUE;
+}
diff --git a/tt2500/tt2500_key.c b/tt2500/tt2500_key.c
new file mode 100644
index 00000000..b1af41f2
--- /dev/null
+++ b/tt2500/tt2500_key.c
@@ -0,0 +1,551 @@
+/* tt2500_key.c: TT2500 keyboard device
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include "tt2500_defs.h"
+#include "sim_video.h"
+
+/* Debug */
+#define DBG 0001
+
+#define KEY_DISPLAY 1
+#define KEY_CONSOLE 2
+#define KEY_TYPE 3
+
+#define SHFT 01000
+#define CTRL 02000
+#define META 04000
+
+#define NOKEY 0177777
+
+static uint16 KBUF;
+static uint16 suffix = NOKEY;
+static uint16 modifiers;
+
+/* Function declaration. */
+static t_stat key_svc (UNIT *uptr);
+static t_stat key_reset (DEVICE *dptr);
+static uint16 key_read (uint16 reg);
+static void key_write (uint16 reg, uint16 data);
+
+static UNIT key_unit = {
+ UDATA (&key_svc, UNIT_IDLE, 0)
+};
+
+static REG key_reg[] = {
+ { ORDATAD (KBUF, KBUF, 16, "Keyboard buffer") },
+ { NULL }
+};
+
+MTAB key_mod[] = {
+ { KEY_TYPE, KEY_DISPLAY, "DISPLAY", "DISPLAY", NULL, NULL, NULL,
+ "Get keyboard events from display windows"},
+ { KEY_TYPE, KEY_CONSOLE, "CONSOLE", "CONSOLE", NULL, NULL, NULL,
+ "Get keyboard events from console"},
+ { 0 }
+};
+
+static DEBTAB key_deb[] = {
+ { "DBG", DBG },
+ { NULL, 0 }
+};
+
+static TTDEV key_ttdev = {
+ { REG_KEY, 0, 0, 0 },
+ key_read,
+ key_write,
+};
+
+DEVICE key_dev = {
+ "KEY", &key_unit, key_reg, key_mod,
+ 0, 8, 16, 1, 8, 16,
+ NULL, NULL, &key_reset,
+ NULL, NULL, NULL, &key_ttdev,
+ DEV_DISABLE | DEV_DEBUG, 0, key_deb,
+ NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static t_stat key_svc (UNIT *uptr)
+{
+ t_stat ch = sim_poll_kbd ();
+
+ if ((ch & SCPE_KFLAG) == 0) {
+ sim_activate_after (&key_unit, 10000);
+ return ch;
+ }
+
+ if (ch & SCPE_BREAK)
+ KBUF = 0377;
+ else
+ KBUF = ch & 0177;
+ flag_on (FLAG_KB);
+ sim_debug (DBG, &key_dev, "Received character %03o\n", KBUF);
+ return SCPE_OK;
+}
+
+static int
+key_modifiers (SIM_KEY_EVENT *ev)
+{
+ int code = 0;
+
+ switch (ev->key) {
+ case SIM_KEY_SHIFT_L:
+ case SIM_KEY_SHIFT_R:
+ code = SHFT;
+ break;
+ case SIM_KEY_CTRL_L:
+ case SIM_KEY_CTRL_R:
+ case SIM_KEY_CAPS_LOCK:
+ code = CTRL;
+ break;
+ case SIM_KEY_ALT_L:
+ case SIM_KEY_ALT_R:
+ code = META;
+ break;
+ }
+
+ if (ev->state == SIM_KEYPRESS_DOWN)
+ modifiers |= code;
+ else if (ev->state == SIM_KEYPRESS_UP)
+ modifiers &= ~code;
+
+ return code != 0;
+}
+
+static int
+key_both (uint32 key)
+{
+ uint16 code = NOKEY;
+ switch (key) {
+ case SIM_KEY_TAB:
+ code = 0011;
+ break;
+ case SIM_KEY_PAGE_UP:
+ code = 0014;
+ break;
+ case SIM_KEY_ENTER:
+ code = 0015;
+ break;
+ case SIM_KEY_ESC:
+ code = 0033;
+ break;
+ case SIM_KEY_SPACE:
+ code = 0040;
+ break;
+ case SIM_KEY_BACKSPACE:
+ case SIM_KEY_DELETE:
+ code = 0177;
+ break;
+ case SIM_KEY_F11:
+ vid_set_fullscreen (!vid_is_fullscreen ());
+ break;
+ }
+ return code;
+}
+
+static int
+key_shift (uint32 key)
+{
+ uint16 code = key_both (key);
+ if (code != NOKEY)
+ return code;
+
+ switch (key) {
+ case SIM_KEY_0:
+ code = ')';
+ break;
+ case SIM_KEY_1:
+ code = '!';
+ break;
+ case SIM_KEY_2:
+ return '@';
+ break;
+ case SIM_KEY_3:
+ code = '#';
+ break;
+ case SIM_KEY_4:
+ code = '$';
+ break;
+ case SIM_KEY_5:
+ code = '%';
+ break;
+ case SIM_KEY_6:
+ return '^';
+ case SIM_KEY_7:
+ code = '&';
+ break;
+ case SIM_KEY_8:
+ code = '*';
+ break;
+ case SIM_KEY_9:
+ code = '(';
+ break;
+ case SIM_KEY_A:
+ code = 'A';
+ break;
+ case SIM_KEY_B:
+ code = 'B';
+ break;
+ case SIM_KEY_C:
+ code = 'C';
+ break;
+ case SIM_KEY_D:
+ code = 'D';
+ break;
+ case SIM_KEY_E:
+ code = 'E';
+ break;
+ case SIM_KEY_F:
+ code = 'F';
+ break;
+ case SIM_KEY_G:
+ code = 'G';
+ break;
+ case SIM_KEY_H:
+ code = 'H';
+ break;
+ case SIM_KEY_I:
+ code = 'I';
+ break;
+ case SIM_KEY_J:
+ code = 'J';
+ break;
+ case SIM_KEY_K:
+ code = 'K';
+ break;
+ case SIM_KEY_L:
+ code = 'L';
+ break;
+ case SIM_KEY_M:
+ code = 'M';
+ break;
+ case SIM_KEY_N:
+ code = 'N';
+ break;
+ case SIM_KEY_O:
+ code = 'O';
+ break;
+ case SIM_KEY_P:
+ code = 'P';
+ break;
+ case SIM_KEY_Q:
+ code = 'Q';
+ break;
+ case SIM_KEY_R:
+ code = 'R';
+ break;
+ case SIM_KEY_S:
+ code = 'S';
+ break;
+ case SIM_KEY_T:
+ code = 'T';
+ break;
+ case SIM_KEY_U:
+ code = 'U';
+ break;
+ case SIM_KEY_V:
+ code = 'V';
+ break;
+ case SIM_KEY_W:
+ code = 'W';
+ break;
+ case SIM_KEY_X:
+ code = 'X';
+ break;
+ case SIM_KEY_Y:
+ code = 'Y';
+ break;
+ case SIM_KEY_Z:
+ code = 'Z';
+ break;
+ case SIM_KEY_BACKQUOTE:
+ return '~';
+ case SIM_KEY_MINUS:
+ return '_';
+ case SIM_KEY_EQUALS:
+ code = '+';
+ break;
+ case SIM_KEY_LEFT_BRACKET:
+ return '{';
+ case SIM_KEY_RIGHT_BRACKET:
+ return '}';
+ case SIM_KEY_SEMICOLON:
+ code = ':';
+ break;
+ case SIM_KEY_SINGLE_QUOTE:
+ code = '"';
+ break;
+ case SIM_KEY_BACKSLASH:
+ case SIM_KEY_LEFT_BACKSLASH:
+ return '|';
+ case SIM_KEY_COMMA:
+ code = '<';
+ break;
+ case SIM_KEY_PERIOD:
+ code = '>';
+ break;
+ case SIM_KEY_SLASH:
+ code = '?';
+ break;
+ }
+ return code;
+}
+
+static int
+key_noshift (uint32 key)
+{
+ uint16 code = key_both (key);
+ if (code != NOKEY)
+ return code;
+
+ switch (key) {
+ case SIM_KEY_0:
+ code = '0';
+ break;
+ case SIM_KEY_1:
+ code = '1';
+ break;
+ case SIM_KEY_2:
+ code = '2';
+ break;
+ case SIM_KEY_3:
+ code = '3';
+ break;
+ case SIM_KEY_4:
+ code = '4';
+ break;
+ case SIM_KEY_5:
+ code = '5';
+ break;
+ case SIM_KEY_6:
+ code = '6';
+ break;
+ case SIM_KEY_7:
+ code = '7';
+ break;
+ case SIM_KEY_8:
+ code = '8';
+ break;
+ case SIM_KEY_9:
+ code = '9';
+ break;
+ case SIM_KEY_A:
+ code = 'a';
+ break;
+ case SIM_KEY_B:
+ code = 'b';
+ break;
+ case SIM_KEY_C:
+ code = 'c';
+ break;
+ case SIM_KEY_D:
+ code = 'd';
+ break;
+ case SIM_KEY_E:
+ code = 'e';
+ break;
+ case SIM_KEY_F:
+ code = 'f';
+ break;
+ case SIM_KEY_G:
+ code = 'g';
+ break;
+ case SIM_KEY_H:
+ code = 'h';
+ break;
+ case SIM_KEY_I:
+ code = 'i';
+ break;
+ case SIM_KEY_J:
+ code = 'j';
+ break;
+ case SIM_KEY_K:
+ code = 'k';
+ break;
+ case SIM_KEY_L:
+ code = 'l';
+ break;
+ case SIM_KEY_M:
+ code = 'm';
+ break;
+ case SIM_KEY_N:
+ code = 'n';
+ break;
+ case SIM_KEY_O:
+ code = 'o';
+ break;
+ case SIM_KEY_P:
+ code = 'p';
+ break;
+ case SIM_KEY_Q:
+ code = 'q';
+ break;
+ case SIM_KEY_R:
+ code = 'r';
+ break;
+ case SIM_KEY_S:
+ code = 's';
+ break;
+ case SIM_KEY_T:
+ code = 't';
+ break;
+ case SIM_KEY_U:
+ code = 'u';
+ break;
+ case SIM_KEY_V:
+ code = 'v';
+ break;
+ case SIM_KEY_W:
+ code = 'w';
+ break;
+ case SIM_KEY_X:
+ code = 'x';
+ break;
+ case SIM_KEY_Y:
+ code = 'y';
+ break;
+ case SIM_KEY_Z:
+ code = 'z';
+ break;
+ case SIM_KEY_BACKQUOTE:
+ code = '`';
+ break;
+ case SIM_KEY_MINUS:
+ code = '-';
+ break;
+ case SIM_KEY_EQUALS:
+ code = '=';
+ break;
+ case SIM_KEY_LEFT_BRACKET:
+ code = '[';
+ break;
+ case SIM_KEY_RIGHT_BRACKET:
+ code = ']';
+ break;
+ case SIM_KEY_SEMICOLON:
+ code = ';';
+ break;
+ case SIM_KEY_SINGLE_QUOTE:
+ code = '\'';
+ break;
+ case SIM_KEY_BACKSLASH:
+ case SIM_KEY_LEFT_BACKSLASH:
+ code = '/';
+ break;
+ case SIM_KEY_COMMA:
+ code = ',';
+ break;
+ case SIM_KEY_PERIOD:
+ code = '.';
+ break;
+ case SIM_KEY_SLASH:
+ code = '/';
+ break;
+ }
+ return code;
+}
+
+static int
+key_event (SIM_KEY_EVENT *ev)
+{
+ sim_debug (DBG, &key_dev, "Key %s %s\n",
+ ev->state == SIM_KEYPRESS_UP ? "up" : "down",
+ vid_key_name (ev->key));
+
+ if (key_modifiers (ev))
+ return 0;
+
+ if (ev->state == SIM_KEYPRESS_DOWN) {
+ uint16 code;
+ if (modifiers & SHFT)
+ code = key_shift (ev->key);
+ else
+ code = key_noshift (ev->key);
+ if (code == NOKEY)
+ return 1;
+ if (modifiers & CTRL)
+ code &= 037;
+ if (modifiers & META) {
+ suffix = code;
+ code = 033;
+ }
+ KBUF = code;
+ sim_debug (DBG, &key_dev, "Received character %03o\n", KBUF);
+ flag_on (FLAG_KB);
+ } else if (ev->state == SIM_KEYPRESS_UP)
+ KBUF = 0;
+
+ return 0;
+}
+
+static t_stat
+key_reset (DEVICE *dptr)
+{
+#ifdef USE_DISPLAY
+ vid_display_kb_event_process = NULL;
+#endif
+ if (dptr->flags & DEV_DIS)
+ return SCPE_OK;
+
+ if ((key_unit.flags & KEY_TYPE) == 0) {
+#if defined(USE_DISPLAY) || (defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL))
+ key_unit.flags |= KEY_DISPLAY;
+#else
+ key_unit.flags |= KEY_CONSOLE;
+#endif
+ }
+
+ if (key_unit.flags & KEY_DISPLAY)
+#ifdef USE_DISPLAY
+ vid_display_kb_event_process = key_event;
+#else
+ ;
+#endif
+ else if (key_unit.flags & KEY_CONSOLE)
+ sim_activate_abs (&key_unit, 0);
+ else
+ return SCPE_ARG;
+
+ return SCPE_OK;
+}
+
+static uint16 key_read (uint16 reg)
+{
+ uint16 code = KBUF;
+ sim_debug (DBG, &key_dev, "Read key %o\n", code);
+ if (suffix == NOKEY) {
+ flag_off (FLAG_KB);
+ if (key_unit.flags & KEY_CONSOLE)
+ sim_activate_abs (&key_unit, 0);
+ } else {
+ KBUF = suffix;
+ suffix = NOKEY;
+ }
+ return code;
+}
+
+static void key_write (uint16 reg, uint16 data)
+{
+}
diff --git a/tt2500/tt2500_rom.c b/tt2500/tt2500_rom.c
new file mode 100644
index 00000000..b0405147
--- /dev/null
+++ b/tt2500/tt2500_rom.c
@@ -0,0 +1,99 @@
+/* tt2500_cpu.c: TT2500 bootstrap ROM contents.
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include "tt2500_defs.h"
+
+uint16 tt2500_rom[] =
+{
+/* BEGIN 0 */ 0010000, /* (NOP) */
+/* 1 */ 0010000, /* (NOP) */
+/* 2 */ 0010000, /* (NOP) */
+/* START 3 */ 0040025, /* (PUSHJ GETC & GET CHAR) */
+/* 4 */ 0007001, /* (SUBI 0 WD & IS WORD "LOGO") */
+/* 5 */ 0147577, /* (147577) */
+/* 6 */ 0133774, /* (BNE START) */
+/* 7 */ 0040022, /* (PUSHJ GETW) */
+/* 10 */ 0074201, /* (GET ADR WD) */
+/* 11 */ 0040022, /* (PUSHJ GETW) */
+/* 12 */ 0074301, /* (GET CNT WD) */
+/* LOOP 13 */ 0040022, /* (PUSHJ GETW) */
+/* 14 */ 0004220, /* (DEC ADR) */
+/* 15 */ 0025100, /* (CWRITE WD) */
+/* 16 */ 0004320, /* (DEC CNT) */
+/* 17 */ 0133773, /* (BNE LOOP) */
+/* 20 */ 0074023, /* (GET 0 XR) */
+/* 21 */ 0050100, /* (JUMP 100) */
+/* GETW 22 */ 0040025, /* (PUSHJ GETC) */
+/* 23 */ 0040025, /* (PUSHJ GETC) */
+/* 24 */ 0040025, /* (PUSHJ GETC) */
+/* GETC 25 */ 0073320, /* (DIS INTS 2) */
+/* 26 */ 0050025, /* (JUMP GETC) */
+/* 27 */ 0010000, /* (NOP) */
+/* 30 */ 0074424, /* (GET CH UART) */
+/* 31 */ 0001444, /* (ANDI CH CH) */
+/* 32 */ 0000017, /* (17) */
+/* 33 */ 0004114, /* (ROT WD 14) */
+/* 34 */ 0001141, /* (ANDI WD WD) */
+/* 35 */ 0177760, /* (177760) */
+/* 36 */ 0002104, /* (IOR WD CH) */
+/* 37 */ 0076016 /* (POPJ) */
+
+
+#if 0
+ 0010000, /* NOP */
+ 0010000, /* NOP */
+ 0010000, /* NOP */
+ 0040025, /* PUSHJ GETC Call subroutine to read TTY character. */
+ 0007001, /* SUBI 0 WD 32-bit instruction computes 147577 - WD */
+ 0147577, /* 147577 (The constant "LOGO" is stored here.) */
+ 0133774, /* BNE START Branch to START if result Not zero. */
+ 0040022, /* PUSHJ GETW Reads 4 characters to make 16-bit word. */
+ 0074201, /* GET ADR WD Move word from WD to ADR (register 2). */
+ 0040022, /* PUSHJ GETW Get next data word into WD. */
+ 0074301, /* GET CNT WD Move it to CNT. */
+ 0040022, /* PUSHJ GETW Get another data word. */
+ 0004220, /* DEC ADR Decrement the Address and use it to */
+ 0025100, /* CWRITE WD write [WD] into control memory. */
+ 0004320, /* DEC CNT Decrement the word in CNT (count). */
+ 0133773, /* BNE LOOP Branch to LOOP unless CNT is zero. */
+ 0074023, /* GET 0 XR Makes PC leave Bootstrap Loader. */
+ 0050100, /* JUMP 100 Jump to location 100. */
+ 0040025, /* PUSHJ GETC Get 4 bits and shift into WD. */
+ 0040025, /* PUSHJ GETC Get 4 more. */
+ 0040025, /* PUSHJ GETC Get 4 more. */
+ 0073320, /* DIS INTS 2 Skip 2 steps if TTY interrupt active. */
+ 0050025, /* JUMP GETC If not, go back and wait. */
+ 0010000, /* NOP (Skip over this.) */
+ 0074424, /* GET CH UART Put the character into CH. */
+ 0001444, /* ANDI CH CH Mask out all but last four bits by */
+ 0000017, /* 17 ANDing with 0 000 000 000 001 111. */
+ 0004114, /* ROT WD 14 Rotate WD four places right. */
+ 0001141, /* ANDI WD WD Zero out last four bits by */
+ 0177760, /* 177760 ANDing with 1 111 111 111 110 000. */
+ 0002104, /* IOR WD CH Finally, OR them together. */
+ 0075600, /* POPJ Return to calling program. */
+#endif
+};
diff --git a/tt2500/tt2500_sys.c b/tt2500/tt2500_sys.c
new file mode 100644
index 00000000..1306a78e
--- /dev/null
+++ b/tt2500/tt2500_sys.c
@@ -0,0 +1,485 @@
+/* tt2500_sys.c: TT2500 simulator interface
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+
+ 24-Sep-20 LB New simulator.
+*/
+
+#include "tt2500_defs.h"
+
+static uint16 null_read (uint16 reg);
+static void null_write (uint16 reg, uint16 data);
+
+int32 sim_emax = 1;
+char sim_name[] = "TT2500";
+
+uint16 CRM[4096];
+uint16 MEM[65536];
+REG *sim_PC = &cpu_reg[0];
+TTDEV *dev_tab[0100];
+
+static t_addr sym_addr = -1;
+static int sym_immediate = 0;
+
+static TTDEV null_dev = {
+ { 0, 0, 0, 0 },
+ null_read,
+ null_write,
+};
+
+DEVICE *sim_devices[] = {
+ &cpu_dev,
+ &dpy_dev,
+ &crt_dev,
+ &tv_dev,
+ &key_dev,
+ &uart_dev,
+ NULL
+};
+
+const char *sim_stop_messages[SCPE_BASE] = {
+ "Unknown error",
+ "HALT instruction",
+ "Breakpoint",
+ "Invalid access",
+};
+
+static t_stat
+get4 (FILE *fileref, uint16 *x)
+{
+ int c = Fgetc (fileref);
+ if (c == EOF)
+ return SCPE_FMT;
+ *x = c & 017;
+ return SCPE_OK;
+}
+
+static t_stat
+get6 (FILE *fileref, uint16 *x)
+{
+ int c = Fgetc (fileref);
+ if (c == EOF)
+ return SCPE_FMT;
+ *x = c & 077;
+ return SCPE_OK;
+}
+
+static t_stat
+get8 (FILE *fileref, uint16 *x)
+{
+ uint16 y;
+ t_stat r;
+ r = get4 (fileref, x);
+ if (r != SCPE_OK)
+ return r;
+ r = get4 (fileref, &y);
+ if (r != SCPE_OK)
+ return r;
+ *x = (*x << 4) | y;
+ return SCPE_OK;
+}
+
+static t_stat
+get16 (FILE *fileref, uint16 *x)
+{
+ uint16 y;
+ t_stat r;
+ r = get8 (fileref, x);
+ if (r != SCPE_OK)
+ return r;
+ r = get8 (fileref, &y);
+ if (r != SCPE_OK)
+ return r;
+ *x = (*x << 8) | y;
+ return SCPE_OK;
+}
+
+static uint16 checksum;
+
+static t_stat
+get18 (FILE *fileref, uint16 *x)
+{
+ uint16 y;
+ t_stat r;
+ r = get6 (fileref, x);
+ if (r != SCPE_OK)
+ return r;
+ r = get6 (fileref, &y);
+ if (r != SCPE_OK)
+ return r;
+ *x = (*x << 6) | y;
+ r = get6 (fileref, &y);
+ if (r != SCPE_OK)
+ return r;
+ *x = (*x << 6) | y;
+ checksum = (checksum + *x) & 0177777;
+ return SCPE_OK;
+}
+
+static t_stat load_loader (FILE *f, int verbose)
+{
+ uint16 i, x, y, count, addr;
+ t_stat r;
+
+ x = 0;
+ do {
+ r = get4 (f, &y);
+ if (r != SCPE_OK)
+ return r;
+ x = ((x << 4) + y) & 0177777;
+ } while (x != 0147577);
+
+ /* Read count and address. */
+ r = get16 (f, &addr);
+ if (r != SCPE_OK)
+ return r;
+ r = get16 (f, &count);
+ if (r != SCPE_OK)
+ return r;
+
+ if (verbose)
+ fprintf (stderr, "Loader: address %06o, %o words\n", addr, count);
+
+ for (i = 1; i <= count; i++) {
+ r = get16 (f, &x);
+ if (r != SCPE_OK)
+ return r;
+ CRM[addr - i] = x;
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat load_block (FILE *f, int verbose)
+{
+ uint16 i, x, y, type, count, addr;
+ t_stat r;
+
+ x = 0;
+ do {
+ r = get6 (f, &y);
+ if (r != SCPE_OK)
+ return r;
+ x = ((x << 6) + y) & 0177777;
+ } while (x != 0120116);
+
+ checksum = 0;
+
+ /* Read type. */
+ r = get18 (f, &type);
+ if (r != SCPE_OK)
+ return r;
+
+ switch (type) {
+ case 0:
+ r = get18 (f, &x);
+ if (r != SCPE_OK)
+ return r;
+ if (verbose) {
+ t_value val = x;
+ fprintf (stderr, "Execute: instruction %06o\n", x);
+ fprint_sym (stderr, 0, &val, 0, SWMASK ('M'));
+ fputc ('\n', stderr);
+ }
+ return SCPE_EOF;
+ case 1:
+ case 2:
+ break;
+ default:
+ return SCPE_FMT;
+ }
+
+ r = get18 (f, &addr);
+ if (r != SCPE_OK)
+ return r;
+ r = get18 (f, &count);
+ if (r != SCPE_OK)
+ return r;
+
+ if (type == 1) {
+ if (verbose)
+ fprintf (stderr, "Load control store: address %06o, %o words\n",
+ addr, count);
+ for (i = 0; i < count; i++) {
+ r = get18 (f, &x);
+ if (r != SCPE_OK)
+ return r;
+ CRM[(addr++) & 07777] = x;
+ }
+ } else if (type == 2) {
+ if (verbose)
+ fprintf (stderr, "Load RAM: address %06o, %o words\n",
+ addr, count);
+ for (i = 0; i < count; i++) {
+ r = get18 (f, &x);
+ if (r != SCPE_OK)
+ return r;
+ if ((addr & 0170000) == 0170000) {
+ FONT[(addr++) & 07777] = x & 0377;
+ FONT[(addr++) & 07777] = x >> 8;
+ i++;
+ } else
+ MEM[(addr++) & 0177777] = x;
+ }
+ }
+
+ r = get18 (f, &x);
+ if (r != SCPE_OK)
+ return r;
+ /* Should be 0, but some blocks checksum to 1. */
+ if (checksum != 0 && checksum != 1)
+ return SCPE_CSUM;
+ return SCPE_OK;
+}
+
+t_stat
+sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag)
+{
+ int verbose = sim_switches & SWMASK ('V');
+ t_stat r;
+
+ r = load_loader (fileref, verbose);
+ if (r != SCPE_OK)
+ return r;
+
+ for (;;) {
+ r = load_block (fileref, verbose);
+ if (r == SCPE_EOF)
+ return SCPE_OK;
+ if (r != SCPE_OK)
+ return r;
+ }
+}
+
+static uint16 null_read (uint16 reg)
+{
+ return 0;
+}
+
+static void null_write (uint16 reg, uint16 data)
+{
+}
+
+t_bool build_dev_tab (void)
+{
+ TTDEV *ttdev;
+ DEVICE *dev;
+ int i, j;
+
+ for (i = 0; i < 0100; i++)
+ dev_tab[i] = &null_dev;
+
+ for (i = 0; (dev = sim_devices[i]) != NULL; i++) {
+ ttdev = (TTDEV *)dev->ctxt;
+ if (ttdev != NULL) {
+ for (j = 0; j < 4; j++)
+ if (ttdev->reg[j] != 0)
+ dev_tab[ttdev->reg[j]] = ttdev;
+ }
+ }
+
+ return SCPE_OK;
+}
+
+static const char *register_names[] =
+ {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "A-latch", "s11", "s12", "s13", "MAGIC", "CHARTV", "s16", "s17",
+ "YCOR", "XCOR", "SCROLL", "XR", "UART", "DSR", "KEY", "d27",
+ "d30", "d31", "d32", "d33", "d34", "d35", "d36", "d37",
+ "scratch40", "scratch41", "scratch42", "scratch43", "scratch44", "scratch45", "scratch46", "scratch47",
+ "scratch50", "scratch51", "scratch52", "scratch53", "scratch54", "scratch55", "scratch56", "scratch57",
+ "scratch60", "scratch61", "scratch62", "scratch63", "scratch64", "scratch65", "scratch66", "scratch67",
+ "scratch70", "scratch71", "scratch72", "scratch73", "scratch74", "scratch75", "scratch76", "scratch77"
+ };
+
+static t_stat
+fprint_sto (FILE *of, uint16 insn)
+{
+ uint16 a = (insn >> 6) & 7;
+ uint16 b = insn & 017;
+
+ const char *op;
+ switch (insn & 077060) {
+ case 020000: op = "READ"; break;
+ case 020020: op = "READD"; break;
+ case 021000: op = "CREAD"; break;
+ case 021020: op = "CREADD"; break;
+ case 022040: op = "READI"; break;
+ case 023040: op = "CREADI"; break;
+ case 024000: op = "WRITE"; break;
+ case 024020: op = "WRITED"; break;
+ case 025000: op = "CWRITE"; break;
+ case 025020: op = "CWRITED"; break;
+ case 026040: op = "WRITEI"; break;
+ case 027040: op = "CWRITEI"; break;
+ case 074000: op = "GET"; break;
+ default: fprintf (of, "???"); return SCPE_OK;
+ }
+ fprintf (of, "%s %s %s", op, register_names[a], register_names[b]);
+ return SCPE_OK;
+}
+
+static t_stat
+fprint_reg (FILE *of, uint16 insn)
+{
+ static const char *name[] =
+ { "A", "ANDN", "AND", "NOR", "IOR", "XOR", "MROT", "??",
+ "ROT", "DEC", "XADD", "ADD", "SUB", "XSUB", "INC", "ARS" };
+ uint16 op = (insn >> 4) & 3;
+ uint16 a = (insn >> 6) & 7;
+ uint16 b = insn & 017;
+
+ if (insn == 010000) {
+ fprintf (of, "NOP");
+ return SCPE_OK;
+ } else if ((insn & 037060) == 01000) {
+ sym_immediate = 1;
+ fprintf (of, "LOD %s", register_names[a]);
+ return SCPE_OK;
+ }
+
+ switch (insn & 030000) {
+ case 000000: break;
+ case 010000: fprintf (of, "T "); break;
+ case 020000: return fprint_sto (of, insn);
+ case 030000: fprintf (of, "IFC "); break;
+ }
+
+ sym_immediate = insn & 01000;
+
+ op += (insn >> 8) & 014;
+ fprintf (of, "%s%s %s %s", name[op], sym_immediate ? "I" : "",
+ register_names[a], register_names[b]);
+ return SCPE_OK;
+}
+
+static t_stat
+fprint_dis (FILE *of, uint16 insn)
+{
+ fprintf (of, "DIS ");
+ switch (insn & 01400) {
+ case 00000: fprintf (of, "BUS"); break;
+ case 00400: fprintf (of, "FLAGS"); break;
+ case 01000: fprintf (of, "INTS"); break;
+ case 01400: fprintf (of, "STARS"); break;
+ }
+ fprintf (of, " %o", (~insn >> 4) & 017);
+ return SCPE_OK;
+}
+
+static t_stat
+fprint_bus (FILE *of, uint16 insn)
+{
+ uint16 a = (insn >> 6) & 7;
+ uint16 b = insn & 077;
+
+ if ((insn & 076000) == 072000)
+ return fprint_dis (of, insn);
+
+ switch (insn) {
+ case 0075400: fprintf (of, "MAGIC"); return SCPE_OK;
+ case 0076014: fprintf (of, "MAGIC"); return SCPE_OK;
+ case 0076015: fprintf (of, "CHARTV"); return SCPE_OK;
+ case 0075500: fprintf (of, "CHARTV"); return SCPE_OK;
+ case 0075600: fprintf (of, "POPJ"); return SCPE_OK;
+ case 0076016: fprintf (of, "POPJ"); return SCPE_OK;
+ case 0076716: fprintf (of, "POPJI"); return SCPE_OK;
+ }
+
+ fprintf (of, "%s ", insn & 02000 ? "PUT" : "GET");
+ fprintf (of, "%s %s", register_names[a], register_names[b]);
+ return SCPE_OK;
+}
+
+static t_stat
+fprint_branch (FILE *of, uint16 insn, uint16 addr)
+{
+ static const char *condition[] =
+ { "CC", "CS", "VS", "VC", "MI", "PL", "NE", "EQ",
+ "GE", "LT", "IS", "IC", "XCI", "XSI", "FS", "FC" };
+ uint16 target = insn & 03777;
+ if (insn & 02000)
+ target = target - 04000;
+ target += addr + 1;
+ fprintf (of, "B%s %06o", condition[(insn >> 11) & 017], target);
+ return SCPE_OK;
+}
+
+static t_stat
+fprint_cpu (FILE *of, uint16 insn, uint16 addr)
+{
+ switch ((insn >> 12) & 017) {
+ case 000: case 001: case 002: case 003:
+ return fprint_reg (of, insn);
+ case 004:
+ fprintf (of, "PUSHJ %04o", insn & 07777);
+ break;
+ case 005:
+ fprintf (of, "JUMP %04o", insn & 07777);
+ break;
+ case 006:
+ fprintf (of, "(undef)");
+ break;
+ case 007:
+ fprint_bus (of, insn);
+ break;
+ case 010: case 011: case 012: case 013:
+ case 014: case 015: case 016: case 017:
+ fprint_branch (of, insn, addr);
+ break;
+ }
+ return SCPE_OK;
+}
+
+t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
+ UNIT *uptr, int32 sw)
+{
+ t_stat reason;
+
+ if ((reason = build_dev_tab ()) != SCPE_OK)
+ return reason;
+
+ if (sym_addr == addr - 1 && sym_immediate) {
+ fprintf (of, "%06o", (unsigned)*val);
+ return SCPE_OK;
+ }
+
+ sym_addr = addr;
+ sym_immediate = 0;
+
+ if (sw & SWMASK ('M'))
+ return fprint_cpu (of, *val, addr);
+ return SCPE_ARG;
+}
+
+t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr,
+ t_value *val, int32 sw)
+{
+ t_stat reason;
+ *val = get_uint (cptr, 8, ~0, &reason);
+ if (reason != SCPE_OK)
+ return reason;
+ return 0;
+}
diff --git a/tt2500/tt2500_tv.c b/tt2500/tt2500_tv.c
new file mode 100644
index 00000000..b03eb2b4
--- /dev/null
+++ b/tt2500/tt2500_tv.c
@@ -0,0 +1,145 @@
+/* tt2500_tv.c: TT2500 TV text display
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include "tt2500_defs.h"
+#include "sim_video.h"
+#include "display/tt2500.h"
+#include "display/display.h"
+
+/* Function declaration. */
+static t_stat tv_svc (UNIT *uptr);
+static t_stat tv_reset (DEVICE *dptr);
+
+static uint32 surface[8 * 16 * 72];
+static uint32 palette[2];
+static VID_DISPLAY *vptr = NULL;
+
+/* Debug */
+#define DBG 0001
+
+static UNIT tv_unit = {
+ UDATA (&tv_svc, UNIT_IDLE, 0)
+};
+
+static DEBTAB tv_deb[] = {
+ { "DBG", DBG },
+ { "KEY", SIM_VID_DBG_KEY },
+ { NULL, 0 }
+};
+
+#if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL)
+#define TV_DIS 0
+#else
+#define TV_DIS DEV_DIS
+#endif
+
+DEVICE tv_dev = {
+ "TV", &tv_unit, NULL, NULL,
+ 1, 8, 16, 1, 8, 16,
+ NULL, NULL, &tv_reset,
+ NULL, NULL, NULL,
+ NULL, DEV_DISABLE | DEV_DEBUG | TV_DIS, 0, tv_deb,
+ NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static t_stat
+tv_svc(UNIT *uptr)
+{
+ SIM_KEY_EVENT ev;
+
+ sim_activate_after (uptr, 10000);
+
+ if (dpy_quit) {
+ dpy_quit = FALSE;
+ return SCPE_STOP;
+ }
+
+ if (vid_poll_kb (&ev) == SCPE_OK) {
+#ifdef USE_DISPLAY
+ if (vid_display_kb_event_process != NULL)
+ vid_display_kb_event_process (&ev);
+#endif
+ }
+ return SCPE_OK;
+}
+
+static t_stat
+tv_reset (DEVICE *dptr)
+{
+ t_stat r;
+ if (dptr->flags & DEV_DIS || sim_switches & SWMASK('P')) {
+ sim_cancel (&tv_unit);
+ if (vptr != NULL)
+ vid_close_window (vptr);
+ vptr = NULL;
+ } else if (vptr == NULL) {
+ r = vid_open_window (&vptr, dptr, "Text display", 576, 480, 0);
+ if (r != SCPE_OK)
+ return r;
+ sim_activate_abs (&tv_unit, 0);
+ vid_register_quit_callback (&dpy_quit_callback);
+ palette[0] = vid_map_rgb_window (vptr, 0x00, 0x00, 0x00);
+ palette[1] = vid_map_rgb_window (vptr, 0x00, 0xFF, 0x30);
+ }
+ return SCPE_OK;
+}
+
+static void tv_character (int row, int col, uint8 c, uint8 *font)
+{
+ uint16 i, j, pixels, address;
+
+ address = 16 * c;
+ for (i = 0; i < 16; i++) {
+ pixels = font[address + i];
+ for (j = 0; j < 8; j++) {
+ surface[8 * (72 * i + col) + j] = palette[(pixels >> 7) & 1];
+ pixels <<= 1;
+ }
+ }
+}
+
+void tv_line (int row, uint8 *line, uint8 *font)
+{
+ int col;
+
+ line[72] = 0;
+ sim_debug (DBG, &tv_dev, "Text row %d: %s\n", row, (char *)line);
+
+ if (tv_dev.flags & DEV_DIS)
+ return;
+
+ for (col = 0; col < 72; col++)
+ tv_character (row, col, *line++, font);
+ vid_draw_window (vptr, 0, 16 * row, 8 * 72, 16, surface);
+}
+
+void tv_refresh (void)
+{
+ if (tv_dev.flags & DEV_DIS)
+ return;
+ sim_debug (DBG, &tv_dev, "Refresh screen.\n");
+ vid_refresh_window (vptr);
+}
diff --git a/tt2500/tt2500_uart.c b/tt2500/tt2500_uart.c
new file mode 100644
index 00000000..0afa1008
--- /dev/null
+++ b/tt2500/tt2500_uart.c
@@ -0,0 +1,237 @@
+/* tt2500_uart.c: TT2500 serial port device
+
+ Copyright (c) 2020, Lars Brinkhoff
+
+ 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
+ LARS BRINKHOFF 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.
+
+ Except as contained in this notice, the name of Lars Brinkhoff shall not be
+ used in advertising or otherwise to promote the sale, use or other dealings
+ in this Software without prior written authorization from Lars Brinkhoff.
+*/
+
+#include "tt2500_defs.h"
+#include "sim_tmxr.h"
+
+/* Debug */
+#define DBG_TX 0001
+#define DBG_RX 0002
+
+#define UART_FILE 1 /* Attached to a file. */
+#define UART_PORT 2 /* Attached to a network port. */
+#define UART_TYPE 3 /* File or port. */
+#define UART_REVERSE 4 /* Transmit bits in reverse order. */
+
+static uint16 RBUF, TBUF;
+
+/* Function declaration. */
+static t_stat uart_r_svc (UNIT *uptr);
+static t_stat uart_t_svc (UNIT *uptr);
+static t_stat uart_reset (DEVICE *dptr);
+static t_stat uart_attach (UNIT *uptr, CONST char *cptr);
+static t_stat uart_detach (UNIT *uptr);
+static uint16 uart_read (uint16);
+static void uart_write (uint16, uint16);
+
+static TMLN uart_ldsc = { 0 };
+static TMXR uart_desc = { 1, 0, 0, &uart_ldsc };
+
+static UNIT uart_unit[] = {
+ { UDATA (&uart_r_svc, UNIT_IDLE+UNIT_ATTABLE, 0) },
+ { UDATA (&uart_t_svc, UNIT_IDLE+UNIT_ATTABLE, 0) }
+};
+
+static REG uart_reg[] = {
+ { ORDATAD (RB, RBUF, 8, "Receive buffer") },
+ { ORDATAD (TB, TBUF, 8, "Transmit buffer") },
+ { NULL }
+};
+
+MTAB uart_mod[] = {
+ { UART_TYPE, UART_PORT, "PORT", "PORT", NULL, NULL, NULL,
+ "Attach to port"},
+ { UART_TYPE, UART_FILE, "FILE", "FILE", NULL, NULL, NULL,
+ "Attach to file"},
+ { UART_REVERSE, UART_REVERSE, "REVERSE", "REVERSE", NULL, NULL, NULL,
+ "Transmit bits in reverse order"},
+ { UART_REVERSE, 0, NULL, "NOREVERSE", NULL, NULL, NULL,
+ "Transmit bits in normal order"},
+ { MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
+ &tmxr_dscln, NULL, &uart_desc, "Disconnect a specific line" },
+ { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL,
+ &tmxr_show_summ, (void *) &uart_desc, "Display a summary of line states" },
+ { MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, NULL,
+ &tmxr_show_cstat, (void *) &uart_desc, "Display current connections" },
+ { MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, NULL,
+ &tmxr_show_cstat, (void *) &uart_desc, "Display multiplexer statistics" },
+ { 0 }
+};
+
+static DEBTAB uart_deb[] = {
+ { "RX", DBG_RX },
+ { "TX", DBG_TX },
+ { NULL, 0 }
+};
+
+static TTDEV uart_ttdev = {
+ { REG_UART, 0, 0, 0 },
+ uart_read,
+ uart_write,
+};
+
+DEVICE uart_dev = {
+ "UART", uart_unit, uart_reg, uart_mod,
+ 2, 8, 16, 1, 8, 16,
+ NULL, NULL, uart_reset,
+ NULL, uart_attach, uart_detach,
+ &uart_ttdev, DEV_DEBUG, 0, uart_deb,
+ NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static t_stat
+uart_r_svc(UNIT *uptr)
+{
+ int32 ch;
+
+ if ((uptr->flags & UNIT_ATT) == 0)
+ return SCPE_OK;
+
+ if (uptr->fileref != NULL) {
+ unsigned char buf;
+ if (sim_fread (&buf, 1, 1, uptr->fileref) == 1) {
+ sim_debug (DBG_RX, &uart_dev, "Received character %03o\n", buf);
+ RBUF = buf;
+ flag_on (INT_RRD);
+ }
+ } else if (uart_ldsc.conn) {
+ tmxr_poll_rx (&uart_desc);
+ ch = tmxr_getc_ln (&uart_ldsc);
+ if (ch & TMXR_VALID) {
+ RBUF = sim_tt_inpcvt (ch, TT_GET_MODE (uart_unit[0].flags));
+ sim_debug (DBG_RX, &uart_dev, "Received character %03o\n", RBUF);
+ flag_on (INT_RRD);
+ return SCPE_OK;
+ }
+ sim_activate_after (uptr, 200);
+ } else {
+ int32 ln = tmxr_poll_conn (&uart_desc);
+ if (ln >= 0) {
+ uart_ldsc.rcve = 1;
+ sim_debug (DBG_RX, &uart_dev, "Connect\n");
+ sim_activate_after (uptr, 200);
+ } else {
+ sim_activate_after (uptr, 10000);
+ }
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat
+uart_t_svc(UNIT *uptr)
+{
+ int32 ch;
+
+ tmxr_poll_tx (&uart_desc);
+
+ if (!tmxr_txdone_ln (&uart_ldsc))
+ return SCPE_OK;
+
+ ch = sim_tt_outcvt (TBUF, TT_GET_MODE (uart_unit[1].flags));
+ if (tmxr_putc_ln (&uart_ldsc, ch) == SCPE_STALL) {
+ sim_activate_after (&uart_unit[1], 200);
+ } else {
+ sim_debug (DBG_TX, &uart_dev, "Transmitted character %03o\n", TBUF);
+ tmxr_poll_tx (&uart_desc);
+ flag_on (FLAG_RSD);
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat
+uart_reset (DEVICE *dptr)
+{
+ if ((uart_unit[0].flags & UART_TYPE) == 0)
+ uart_unit[0].flags |= UART_PORT;
+
+ flag_off (INT_RRD);
+ flag_on (FLAG_RSD);
+ return SCPE_OK;
+}
+
+static t_stat
+uart_attach (UNIT *uptr, CONST char *cptr)
+{
+ t_stat r;
+
+ if (uptr->flags & UART_PORT) {
+ r = tmxr_attach (&uart_desc, uptr, cptr);
+ if (r != SCPE_OK)
+ return r;
+ sim_activate_abs (uptr, 0);
+ } else if (uptr->flags & UART_FILE) {
+ r = attach_unit (uptr, cptr);
+ if (r != SCPE_OK)
+ return r;
+ sim_activate_abs (uptr, 0);
+ } else {
+ return SCPE_ARG;
+ }
+
+ return SCPE_OK;
+}
+
+static t_stat
+uart_detach (UNIT *uptr)
+{
+ if (!(uptr->flags & UNIT_ATT))
+ return SCPE_OK;
+ if (sim_is_active (uptr))
+ sim_cancel (uptr);
+ return detach_unit (uptr);
+}
+
+static uint16 uart_read (uint16 reg)
+{
+ sim_debug (DBG_RX, &uart_dev, "Read character %03o\n", RBUF);
+ flag_off (INT_RRD);
+ sim_activate_after (&uart_unit[0], 200);
+ return RBUF;
+}
+
+static uint16 reverse (uint16 data)
+{
+ uint16 i, x = 0;
+ for (i = 1; i <= 0200; i <<= 1) {
+ x <<= 1;
+ if (data & i)
+ x++;
+ }
+ return x;
+}
+
+static void uart_write (uint16 reg, uint16 data)
+{
+ data &= 0377;
+ if (uart_unit[0].flags & UART_REVERSE)
+ data = reverse (data);
+ sim_debug (DBG_TX, &uart_dev, "Write character %03o\n", data);
+ TBUF = data;
+ sim_activate_after (&uart_unit[1], 200);
+ flag_off (FLAG_RSD);
+}