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); +}