diff --git a/.travis.yml b/.travis.yml index 282102b2..c19d7250 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" + - SIM="besm6 imds-210 imds-220 imds-225 imds-230 imds-800 imds-810 imlac" - 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 8d4dc6a4..6ec2a7b9 100644 --- a/Visual Studio Projects/Simh.sln +++ b/Visual Studio Projects/Simh.sln @@ -408,6 +408,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "I650", "I650.vcproj", "{95B {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imlac", "imlac.vcproj", "{7AB92E01-D278-4A8F-8493-ED58187AF3A1}" + ProjectSection(ProjectDependencies) = postProject + {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -742,6 +747,10 @@ Global {95B64699-4B93-4BFE-9024-0A2302D9B71A}.Debug|Win32.Build.0 = Debug|Win32 {95B64699-4B93-4BFE-9024-0A2302D9B71A}.Release|Win32.ActiveCfg = Release|Win32 {95B64699-4B93-4BFE-9024-0A2302D9B71A}.Release|Win32.Build.0 = Release|Win32 + {7AB92E01-D278-4A8F-8493-ED58187AF3A1}.Debug|Win32.ActiveCfg = Debug|Win32 + {7AB92E01-D278-4A8F-8493-ED58187AF3A1}.Debug|Win32.Build.0 = Debug|Win32 + {7AB92E01-D278-4A8F-8493-ED58187AF3A1}.Release|Win32.ActiveCfg = Release|Win32 + {7AB92E01-D278-4A8F-8493-ED58187AF3A1}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Visual Studio Projects/imlac.vcproj b/Visual Studio Projects/imlac.vcproj new file mode 100644 index 00000000..ce93995f --- /dev/null +++ b/Visual Studio Projects/imlac.vcproj @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/display/display.c b/display/display.c index 346f9c70..23be9f47 100644 --- a/display/display.c +++ b/display/display.c @@ -125,7 +125,7 @@ struct color color_p29 = { p29, ELEMENTS(p29), 25000 }; /* green phosphor for Tek 611 */ static struct phosphor p31[] = {{0.0, 1.0, 0.77, 0.5, .1}}; -struct color color_p31 = { p31, ELEMENTS(p31), 25000 }; +struct color color_p31 = { p31, ELEMENTS(p31), 100000 }; /* green phosphor for III */ static struct phosphor p39[] = {{0.2, 1.0, 0.0, 0.5, 0.01}}; @@ -251,7 +251,15 @@ static struct display displays[] = { * III display * on PDP-10 */ - { DIS_III, "III Display", &color_p39, NULL, 1024, 1024 } + { DIS_III, "III Display", &color_p39, NULL, 1024, 1024 }, + + /* + * Imlac display + * 1024x1024 addressable points. + * P31 phosphor according to "Heads-Up Display for Flight + * Simulator for Advanced Aircraft" + */ + { DIS_IMLAC, "Imlac Display", &color_p31, NULL, 1024, 1024 } }; /* diff --git a/display/display.h b/display/display.h index 7cbd8834..b680f64b 100644 --- a/display/display.h +++ b/display/display.h @@ -42,6 +42,7 @@ enum display_type { * of the PDP-1, and thus all DEC machines. */ DIS_TX0 = 0, + DIS_IMLAC = 1, DIS_VR14 = 14, DIS_VR17 = 17, DIS_VR20 = 20, diff --git a/display/imlac.c b/display/imlac.c new file mode 100644 index 00000000..91c6dfdb --- /dev/null +++ b/display/imlac.c @@ -0,0 +1,114 @@ +/* imlac.c: Imlac 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 "imlac.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +int imlac_init(void *dev, int debug) +{ + return display_init (DIS_IMLAC, 1, dev); +} + +void imlac_point (int x, int y) +{ + display_point (x, y, DISPLAY_INT_MAX, 0); +} + +int imlac_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 ix = SIGN(dx); + int iy = SIGN(dy); + int ay; + + dx = ABS(dx); + dy = ABS(dy); + + ay = dy/2; + for (;;) { + imlac_point (x, y); + 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 ix = SIGN(dx); + int iy = SIGN(dy); + int ax; + + dx = ABS(dx); + dy = ABS(dy); + + ax = dx/2; + for (;;) { + imlac_point (x, y); + if (y == y2) + break; + if (ax > 0) { + x += ix; + ax -= dy; + } + ax += dx; + y += iy; + } +} + +void +imlac_line (int x1, int y1, int x2, int y2) +{ + int dx = x2 - x1; + int dy = y2 - y1; + if (ABS (dx) > ABS(dy)) + xline (x1, y1, x2, dx, dy); + else + yline (x1, y1, y2, dx, dy); +} + +#if defined(__cplusplus) +} +#endif diff --git a/display/imlac.h b/display/imlac.h new file mode 100644 index 00000000..3bdc5562 --- /dev/null +++ b/display/imlac.h @@ -0,0 +1,38 @@ +/* imlac.h: Imlac 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 imlac_init(void *, int); +extern int imlac_cycle(int, int); +extern void imlac_point(int, int); +extern void imlac_line(int x1, int y1, int x2, int y2); + +#if defined(__cplusplus) +} +#endif diff --git a/imlac/imlac_cpu.c b/imlac/imlac_cpu.c new file mode 100644 index 00000000..bf207568 --- /dev/null +++ b/imlac/imlac_cpu.c @@ -0,0 +1,576 @@ +/* imlac_cpu.c: Imlac 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 "imlac_defs.h" + +/* Debug */ +#define DBG_CPU 0001 +#define DBG_IRQ 0002 +#define DBG_ROM 0004 + +/* Bootstrap ROM type. */ +#define ROM_NONE 0 +#define ROM_TTY 1 +#define ROM_STTY 2 +#define ROM_PTR 3 + +/* CPU state. */ +static uint16 PC; +static uint16 AC; +static uint16 L; +static uint16 DS; +static uint16 IR; +static uint16 MA; +static uint16 MB; +static uint16 SWITCHES; +static int ion_delay = 0; + +/* IRQ state. */ +static uint16 ARM = 0177777; +static uint16 FLAGS = FLAG_SYNC | FLAG_TTY_T; +static uint16 ION; + +/* ROM state. */ +static int rom_type = ROM_NONE; + +static int halt; +uint16 memmask = 017777; + +/* Function declaration. */ +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 uint16 irq_iot (uint16, uint16); +static t_stat rom_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat rom_show_type (FILE *st, UNIT *up, int32 v, CONST void *dp); + +static UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 020000) }; + +REG cpu_reg[] = { + { ORDATAD (PC, PC, 13, "Program Counter") }, + { ORDATAD (AC, AC, 16, "Accumulator") }, + { ORDATAD (L, L, 1, "Link") }, + { ORDATAD (DS, DS, 16, "Data Switches") }, + { ORDATAD (IR, IR, 16, "Instruction") }, + { ORDATAD (MA, MA, 13, "Memory Address") }, + { ORDATAD (MB, MB, 16, "Memory Buffer") }, + { ORDATAD (SWITCHES, SWITCHES, 16, "Toggle switches") }, + { 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 }, + { 0 } +}; + +static DEBTAB cpu_deb[] = { + { "CPU", DBG_CPU }, + { 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 REG irq_reg[] = { + { ORDATAD (ION, ION, 1, "Interrupts on") }, + { ORDATAD (FLAGS, FLAGS, 16, "Flagged interrupts") }, + { ORDATAD (ARM, ARM, 16, "Armed interrupts") }, + { NULL } +}; + +static IMDEV irq_imdev = { + 3, + { { 0010, irq_iot, { NULL, "RDI", NULL, NULL } }, + { 0014, irq_iot, { NULL, "ARM", NULL, NULL } }, + { 0016, irq_iot, { NULL, "IOF", "ION", NULL } } } +}; + +static DEBTAB irq_deb[] = { + { "IRQ", DBG_IRQ }, + { NULL, 0 } +}; + +DEVICE irq_dev = { + "IRQ", NULL, irq_reg, NULL, + 0, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + NULL, NULL, NULL, + &irq_imdev, DEV_DEBUG, 0, irq_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static MTAB rom_mod[] = { + { MTAB_VDV|MTAB_VALR, 0, "TYPE", "TYPE", &rom_set_type, &rom_show_type }, + { 0 } +}; + +static DEBTAB rom_deb[] = { + { "DBG", DBG_ROM }, + { NULL, 0 } +}; + +DEVICE rom_dev = { + "ROM", NULL, NULL, rom_mod, + 0, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, DEV_DEBUG, 0, rom_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static void pcinc (int flag) +{ + if (flag) + PC = (PC + 1) & memmask; +} + +static void memaddr (uint16 addr) +{ + MA = addr & memmask; +} + +static void memrd (void) +{ + MB = M[MA]; +} + +static void memwr (void) +{ + if (rom_type == ROM_NONE || (MA & 0177740) != 040) + M[MA] = MB; +} + +static void cpu_class1 (uint16 insn) +{ + if (insn & 0000001) /* T1: CLA */ + AC = 0; + if (insn & 0000010) /* T1: CLL */ + L = 0; + if (insn & 0000002) /* T2: CMA */ + AC = ~AC; + if (insn & 0000020) /* T2: CML */ + L = !L; + if (insn & 0000004) /* T3: IAC */ + AC++; + if (insn & 0000040) /* T3: ODA */ + AC |= DS; + + halt = !(insn & 0100000); +} + +static void cpu_ral (int n) +{ + int i, x; + for (i = 0; i < n; i++) { + x = L; + L = AC >> 15; + AC = (AC << 1) | x; + } +} + +static void cpu_rar (int n) +{ + int i, x; + for (i = 0; i < n; i++) { + x = L; + L = AC & 1; + AC = (x << 15) | (AC >> 1); + } +} + +static void cpu_class2 (uint16 insn) +{ + int n = insn & 3; + uint32 x; + + if (insn & 0000100) /* DON */ + dp_on (1); + + switch (insn & 0000600) { + case 0000000: /* RAL */ + cpu_ral (n); + break; + case 0000200: /* RAR */ + cpu_rar (n); + break; + case 0000400: /* SAL */ + AC = (AC & 0100000) | ((AC & 037777) << n); + break; + case 0000600: /* SAR */ + if (AC & 0100000) + x = 01600000 >> n; + else + x = 0; + AC = x | ((AC & 077777) >> n); + break; + } +} + +static void cpu_class3 (uint16 insn) +{ + int skip = 0; + + if (insn & 0001) /* ASZ */ + skip |= (AC == 0); + if (insn & 0002) /* ASP */ + skip |= !(AC & 0100000); + if (insn & 0004) /* LSZ */ + skip |= (L == 0); + if (insn & 0010) /* DSF */ + skip |= dp_is_on (); + if (insn & 0020) /* KSF */ + skip |= FLAGS & FLAG_KBD; + if (insn & 0040) /* RSF */ + skip |= FLAGS & FLAG_TTY_R; + if (insn & 0100) /* TSF */ + skip |= FLAGS & FLAG_TTY_T; + if (insn & 0200) /* SSF */ + skip |= FLAGS & FLAG_SYNC; + if (insn & 0400) /* HSF */ + skip |= FLAGS & FLAG_PTR; + + if (insn & 0100000) + skip = !skip; + + pcinc (skip); +} + +static void cpu_iot (uint16 insn) +{ + SUBDEV *dev = dev_tab[(insn >> 3) & 077]; + if (dev == NULL) { + sim_debug (DBG_CPU, &cpu_dev, "Unknown device IOT: %06o\n", IR); + return; + } + AC = dev->iot (insn, AC); +} + +static void cpu_opr (uint16 insn) +{ + switch (insn & 0177000) { + case 0000000: + case 0100000: + cpu_class1 (insn); + break; + case 0003000: + cpu_class2 (insn); + break; + case 0002000: + case 0102000: + cpu_class3 (insn); + break; + case 0001000: + cpu_iot (insn); + break; + default: + sim_debug (DBG_CPU, &cpu_dev, "Unknown instruction: %06o\n", IR); + break; + } +} + +static void +cpu_insn (void) +{ + uint32 t32; + uint16 tmp; + + /* Fetch cycle. */ + memaddr (PC); + memrd (); + IR = MB; + sim_interval--; + + if (((IR >> 12) & 7) != 0) { + /* Memory referecing. */ + memaddr ((IR & 03777) | (PC & 014000)); + if (IR & 0100000) { + /* Defer cycle. */ + if ((MA & 03770) == 010) { + /* Auto incrementing. */ + memrd (); + MB++; + memwr (); + } + memaddr (M[MA]); + } + } + + pcinc (1); + + /* Execute cycle. */ + switch ((IR >> 9) & 074) { + case 000: /* OPR */ + cpu_opr (IR); + break; + case 004: /* LAW, LCW */ + if (IR & 0100000) + AC = -(IR & 03777); + else + AC = IR & 03777; + break; + case 010: /* JMP */ + PC = MA; + break; + case 020: /* DAC */ + MB = AC; + memwr (); + break; + case 024: /* XAM */ + memrd (); + tmp = MB; + MB = AC; + memwr (); + AC = tmp; + break; + case 030: /* ISZ */ + memrd (); + MB++; + memwr (); + pcinc (MB == 0); + break; + case 034: /* JMS */ + MB = PC; + memwr (); + PC = MA; + pcinc (1); + break; + case 044: /* AND */ + memrd (); + AC &= MB; + break; + case 050: /* IOR */ + memrd (); + AC |= MB; + break; + case 054: /* XOR */ + memrd (); + AC ^= MB; + break; + case 060: /* LAC */ + memrd (); + AC = MB; + break; + case 064: /* ADD */ + memrd (); + t32 = AC; + t32 += MB; + AC = t32 & 0177777; + if (t32 & 0200000) + L ^= 1; + break; + case 070: /* SUB */ + memrd (); + t32 = AC; + t32 -= MB; + AC = t32 & 0177777; + if (t32 & 0200000) + L ^= 1; + break; + case 074: /* SAM */ + memrd (); + pcinc (AC == MB); + break; + default: + sim_debug (DBG_CPU, &cpu_dev, "Unknown instruction: %06o\n", IR); + break; + } +} + +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; + + /* Check for interrupts. */ + if (ION && (FLAGS & ARM)) { + sim_debug (DBG_IRQ, &irq_dev, "Interrupt: %06o\n", FLAGS & ARM); + M[0] = PC; + PC = 1; + ION = 0; + } + + cpu_insn (); + + if (sim_step != 0) { + if (--sim_step == 0) + return SCPE_STEP; + } + + if (halt) + return STOP_HALT; + + if (ion_delay && --ion_delay == 0) { + sim_debug (DBG_IRQ, &irq_dev, "Interrupts on\n"); + ION = 1; + } + } + + 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 (ea >= 040000) + return SCPE_NXM; + *vptr = M[ea]; + return SCPE_OK; +} + +static t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) +{ + if (ea >= 040000) + return SCPE_NXM; + M[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 ((M[PC] & 074000) == 034000) { + returns[0] = PC + 1; + *ret_addrs = returns; + return TRUE; + } else { + return FALSE; + } +} + +static t_stat +cpu_reset (DEVICE *dptr) +{ + sim_brk_types = SWMASK('D') | SWMASK('E'); + sim_brk_dflt = SWMASK ('E'); + sim_vm_is_subroutine_call = &cpu_is_pc_a_subroutine_call; + return SCPE_OK; +} + +void +flag_on (uint16 flag) +{ + FLAGS |= flag; + sim_debug (DBG_IRQ, &irq_dev, "Flag on %06o -> %06o\n", flag, FLAGS); +} + +void +flag_off (uint16 flag) +{ + FLAGS &= ~flag; + sim_debug (DBG_IRQ, &irq_dev, "Flag off %06o -> %06o\n", flag, FLAGS); +} + +static uint16 +irq_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0101) { /* RDI */ + AC |= FLAGS; + } + if ((insn & 0771) == 0141) { /* ARM */ + ARM = AC; + } + if ((insn & 0771) == 0161) { /* IOF */ + sim_debug (DBG_IRQ, &irq_dev, "Interrupts off\n"); + ION = 0; + } + if ((insn & 0772) == 0162) { /* ION */ + /* Delay the action until next instruction has executed. */ + ion_delay = 2; + } + return AC; +} + +void +rom_data (uint16 *data) +{ + int i; + for (i = 0; i < 040; i++) + M[040 + i] = data[i]; +} + +static t_stat +rom_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + t_stat r = SCPE_OK; + if (strcmp (cptr, "NONE") == 0) { + rom_type = ROM_NONE; + } else if (strcmp (cptr, "TTY") == 0) { + rom_type = ROM_TTY; + rom_tty (); + } else if (strcmp (cptr, "STTY") == 0) { + rom_type = ROM_STTY; + rom_stty (); + } else if (strcmp (cptr, "PTR") == 0) { + rom_type = ROM_PTR; + rom_ptr (); + } else + r = SCPE_ARG; + return r; +} + +static t_stat +rom_show_type (FILE *st, UNIT *up, int32 v, CONST void *dp) +{ + switch (rom_type) { + case ROM_NONE: + fprintf (st, "TYPE=NONE"); + break; + case ROM_TTY: + fprintf (st, "TYPE=TTY"); + break; + case ROM_STTY: + fprintf (st, "TYPE=STTY"); + break; + case ROM_PTR: + fprintf (st, "TYPE=PTR"); + break; + default: + fprintf (st, "TYPE=(invalid)"); + break; + } + return SCPE_OK; +} diff --git a/imlac/imlac_crt.c b/imlac/imlac_crt.c new file mode 100644 index 00000000..68b69f8b --- /dev/null +++ b/imlac/imlac_crt.c @@ -0,0 +1,136 @@ +/* imlac_crt.c: Imlac 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 "imlac_defs.h" +#include "sim_video.h" +#include "display/imlac.h" +#include "display/display.h" + +/* Function declaration. */ +static t_stat crt_svc (UNIT *uptr); +static t_stat crt_reset (DEVICE *dptr); + +static int crt_quit = FALSE; + +/* Debug */ +#define DBG 0001 + +static UNIT crt_unit = { + UDATA (&crt_svc, UNIT_IDLE, 0) +}; + +static DEBTAB crt_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +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 | DEV_DIS, 0, crt_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static t_stat +crt_svc(UNIT *uptr) +{ +#ifdef HAVE_LIBSDL + imlac_cycle (100, 0); + sim_activate_after (uptr, 100); + if (crt_quit) { + crt_quit = FALSE; + return SCPE_STOP; + } +#endif + return SCPE_OK; +} + +static void crt_quit_callback (void) +{ + crt_quit = TRUE; +} + +static t_stat +crt_reset (DEVICE *dptr) +{ +#ifdef HAVE_LIBSDL + if (dptr->flags & DEV_DIS) { + display_close (dptr); + sim_cancel (&crt_unit); + } else { + display_reset (); + imlac_init (dptr, 1); + sim_activate_abs (&crt_unit, 0); + vid_register_quit_callback (&crt_quit_callback); + } +#endif + return SCPE_OK; +} + +void +crt_point (uint16 x, uint16 y) +{ + sim_debug (DBG, &crt_dev, "Point %d,%d\n", x, y); +#ifdef HAVE_LIBSDL + if (crt_dev.flags & DEV_DIS) + return; + imlac_point ((x & 03777) >> 1, (y & 03777) >> 1); +#endif +} + +void +crt_line (uint16 x1, uint16 y1, uint16 x2, uint16 y2) +{ + sim_debug (DBG, &crt_dev, "Line %d,%d - %d,%d\n", x1, y1, x2, y2); +#ifdef HAVE_LIBSDL + if (crt_dev.flags & DEV_DIS) + return; + imlac_line ((x1 & 03777) >> 1, (y1 & 03777) >> 1, + (x2 & 03777) >> 1, (y2 & 03777) >> 1); +#endif +} + +/* Hook called when CRT goes idle. */ +void +crt_idle (void) +{ +} + +/* Display high voltage sync. */ +void +crt_hvc (void) +{ +} + +void cpu_get_switches (unsigned long *p1, unsigned long *p2) +{ +} + +void cpu_set_switches (unsigned long p1, unsigned long p2) +{ +} diff --git a/imlac/imlac_defs.h b/imlac/imlac_defs.h new file mode 100644 index 00000000..635b6727 --- /dev/null +++ b/imlac/imlac_defs.h @@ -0,0 +1,77 @@ +/* imlac_defs.h: Imlac 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. + + 21-Apr-20 LB New simulator. +*/ + +#ifndef IMLAC_DEFS_H_ +#define IMLAC_DEFS_H_ 0 + +#include "sim_defs.h" + +#define STOP_HALT 1 +#define STOP_IBKPT 2 +#define STOP_ACCESS 3 + +#define FLAG_PTR 010000 +#define FLAG_PTP 000400 +#define FLAG_TTY_T 000040 +#define FLAG_KBD 000020 +#define FLAG_TTY_R 000010 +#define FLAG_SYNC 000002 + +typedef struct { + uint16 num; + uint16 (*iot)(uint16 insn, uint16 AC); + const char *mnemonics[8]; +} SUBDEV; + +typedef struct { + int codes; + SUBDEV subdev[4]; +} IMDEV; + +extern t_bool build_dev_tab (void); +extern void flag_on (uint16 flag); +extern void flag_off (uint16 flag); +extern void dp_on (int flag); +extern uint16 dp_is_on (void); +extern void crt_point (uint16 x, uint16 y); +extern void crt_line (uint16 x1, uint16 y1, uint16 x2, uint16 y2); +extern void crt_idle (void); +extern void crt_hvc (void); +extern void rom_data (uint16 *data); +extern void rom_tty (void); +extern void rom_stty (void); +extern void rom_ptr (void); + +extern REG cpu_reg[]; +extern uint16 M[]; +extern uint16 memmask; +extern SUBDEV *dev_tab[0100]; +extern DEVICE cpu_dev, irq_dev, rom_dev, dp_dev, crt_dev, kbd_dev; +extern DEVICE tty_dev, ptr_dev, ptp_dev, sync_dev; + +#endif /* IMLAC_DEFS_H_ */ diff --git a/imlac/imlac_dp.c b/imlac/imlac_dp.c new file mode 100644 index 00000000..d4eb8693 --- /dev/null +++ b/imlac/imlac_dp.c @@ -0,0 +1,485 @@ +/* imlac_dp.c: Imlac display processor + + 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 "imlac_defs.h" + +/* Debug */ +#define DBG 0001 + +static t_addr DPC; +static t_addr DT; +static uint16 ON = 0; +static uint16 HALT = 0; +static uint16 MODE = 0; +static uint16 XA, YA; +static uint16 SCALE = 2; +static uint16 BLOCK = 0; +static uint16 MIT8K; +static uint16 SGR; +static uint16 SYNC = 1; + +/* Function declaration. */ +static uint16 dp_iot (uint16, uint16); +static t_stat dp_svc (UNIT *uptr); +static uint16 sync_iot (uint16, uint16); +static t_stat sync_svc (UNIT *uptr); + +static IMDEV dp_imdev = { + 3, + { { 0000, dp_iot, { NULL, NULL, NULL, "DLA" } }, + { 0001, dp_iot, { NULL, "CTB", "DOF", NULL } }, + { 0030, dp_iot, { NULL, NULL, NULL, "DCF" } } } +}; + +static UNIT dp_unit = { + UDATA (&dp_svc, UNIT_IDLE, 0) +}; + +static REG dp_reg[] = { + { ORDATAD (DPC, DPC, 16, "Display program counter") }, + { ORDATAD (ON, ON, 1, "Display on") }, + { ORDATAD (HALT, HALT, 1, "Display halted") }, + { ORDATAD (MODE, MODE, 1, "Display mode") }, + { ORDATAD (DT, DT, 16, "Return address") }, + { ORDATAD (XA, XA, 11, "X accumulator") }, + { ORDATAD (YA, YA, 11, "Y accumulator") }, + { ORDATAD (SCALE, SCALE, 3, "Scale") }, + { ORDATAD (BLOCK, BLOCK, 3, "Block") }, + { ORDATAD (MIT8K, MIT8K, 1, "MIT 8K addressing") }, + { ORDATAD (SGR, SGR, 1, "Suppressed grid mode") }, + { NULL } +}; + +static DEBTAB dp_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +DEVICE dp_dev = { + "DP", &dp_unit, dp_reg, NULL, + 1, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + NULL, NULL, NULL, &dp_imdev, DEV_DEBUG, 0, dp_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static UNIT sync_unit = { + UDATA (&sync_svc, UNIT_IDLE, 0) +}; + +static REG sync_reg[] = { + { ORDATAD (SYNC, SYNC, 1, "Flag") }, + { NULL } +}; + +static IMDEV sync_imdev = { + 1, + { { 0007, sync_iot, { NULL, "SCF", "IOS", NULL } } } +}; + +static DEBTAB sync_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +DEVICE sync_dev = { + "SYNC", &sync_unit, sync_reg, NULL, + 1, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + NULL, NULL, NULL, + &sync_imdev, DEV_DEBUG, 0, sync_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +void +dp_on (int flag) +{ + if (!ON && flag) { + MIT8K = 0; + sim_activate_abs (&dp_unit, 0); + sim_debug (DBG, &dp_dev, "Display on\n"); + } else if (ON && !flag) { + sim_cancel (&dp_unit); + sim_debug (DBG, &dp_dev, "Display off\n"); + crt_idle (); + if (SYNC && HALT) + flag_on (FLAG_SYNC); + } + ON = flag; +} + +uint16 +dp_is_on (void) +{ + return ON; +} + +static uint16 +dp_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0001) { /* DLZ */ + sim_debug (DBG, &dp_dev, "DPC cleared\n"); + DPC = 0; + } + if ((insn & 0772) == 0002) { /* DLA */ + sim_debug (DBG, &dp_dev, "DPC set to %06o\n", AC); + DPC |= AC; + } + if ((insn & 0771) == 0011) { /* CTB */ + ; + } + if ((insn & 0772) == 0012) { /* DOF */ + dp_on (0); + } + if ((insn & 0774) == 0304) { /* DCF */ + HALT = 0; + } + return AC; +} + +static t_stat +dp_opr(uint16 insn) +{ + if ((insn & 04000) == 0) { + sim_debug (DBG, &dp_dev, "DHLT "); + HALT = 1; + } + else if (insn == 04000) + sim_debug (DBG, &dp_dev, "DNOP"); + dp_on ((insn & 04000) != 0); /* DHLT */ + + switch (insn & 00014) { + case 000: + if (insn & 1) { + sim_debug (DBG, &dp_dev, "DADR "); + MIT8K = !MIT8K; + } + break; + case 004: + sim_debug (DBG, &dp_dev, "DSTS%o ", insn & 3); + SCALE = insn & 3; + if (SCALE == 0) + SCALE = 1; + else + SCALE *= 2; + break; + case 010: + sim_debug (DBG, &dp_dev, "DSTB%o ", insn & 3); + BLOCK = insn & 3; + break; + case 014: + sim_debug (DBG, &dp_dev, "DSTL%o ", insn & 3); + /* TODO: Light pen. */ + break; + } + if (insn & 00020) { /* DDSP */ + sim_debug (DBG, &dp_dev, "DDSP "); + crt_point (XA, YA); + } + if (insn & 00040) { /* DRJM */ + sim_debug (DBG, &dp_dev, "DRJM "); + DPC = DT; + } + if (insn & 00100) { /* DDYM */ + sim_debug (DBG, &dp_dev, "DDYM "); + YA -= 040; + } + if (insn & 00200) { /* DDXM */ + sim_debug (DBG, &dp_dev, "DDXM "); + XA -= 040; + } + if (insn & 00400) { /* DIYM */ + sim_debug (DBG, &dp_dev, "DIYM "); + YA += 040; + } + if (insn & 01000) { /* DIXM */ + sim_debug (DBG, &dp_dev, "DIXM "); + XA += 040; + } + if (insn & 02000) { /* DHVC */ + sim_debug (DBG, &dp_dev, "DHVC "); + crt_hvc (); + } + + sim_debug (DBG, &dp_dev, "\n"); + return SCPE_OK; +} + +static void +jump (uint16 insn) +{ + DPC = insn & 07777; + if (MIT8K) + DPC |= (insn & 0100000) >> 3; + else + DPC |= BLOCK << 12; +} + +static void +dp_sgr (uint16 insn) +{ + sim_debug (DBG, &dp_dev, "DSGR %o\n", insn & 7); + + SGR = insn & 1; + if (insn & 1) { + sim_debug (DBG, &dp_dev, "Enter SGR mode\n"); + } else { + sim_debug (DBG, &dp_dev, "Exit SGR mode\n"); + } + if (insn & 2) { + sim_debug (DBG, &dp_dev, "SGR: Return\n"); + } + if (insn & 4) { + sim_debug (DBG, &dp_dev, "SGR: Beam on\n"); + } else { + sim_debug (DBG, &dp_dev, "SGR: Beam off\n"); + } +} + +static void +dp_opt (uint16 insn) +{ + switch (insn & 07770) { + case 07660: /* ASG-1 */ + case 07667: + break; + case 07720: /* VIC-1 */ + case 07730: + break; + case 07740: /* MCI-1 */ + case 07750: + break; + case 07760: /* STI-1 or LPA-1 */ + break; + case 07770: /* SGR-1 */ + dp_sgr (insn); + break; + default: + sim_debug (DBG, &dp_dev, "Unknown instruction: %06o ", insn); + break; + } +} + +static void +dp_inc_vector (uint16 byte) +{ + uint16 x1 = XA, y1 = YA; + uint16 dx, dy; + + if (byte == 0200) { + sim_debug (DBG, &dp_dev, "P"); + } else { + sim_debug (DBG, &dp_dev, "%s", byte & 0100 ? "B" : "D"); + if (byte & 00040) + sim_debug (DBG, &dp_dev, "M"); + sim_debug (DBG, &dp_dev, "%o", (byte >> 3) & 3); + if (byte & 00004) + sim_debug (DBG, &dp_dev, "M"); + sim_debug (DBG, &dp_dev, "%o", byte & 3); + } + + dx = SCALE * ((byte >> 3) & 3); + dy = SCALE * (byte & 3); + if (byte & 040) + XA -= dx; + else + XA += dx; + if (byte & 4) + YA -= dy; + else + YA += dy; + if (byte & 0100) + crt_line (x1, y1, XA, YA); + +} + +static void +dp_inc_escape (uint16 byte) +{ + if (byte == 0100) + sim_debug (DBG, &dp_dev, "T"); + else if (byte == 0140) + sim_debug (DBG, &dp_dev, "X"); + else if (byte == 0151) + sim_debug (DBG, &dp_dev, "R"); + else + sim_debug (DBG, &dp_dev, "%03o", byte); + + if (byte & 0100) + MODE = 0; + if (byte & 040) + DPC = DT; + if (byte & 020) + XA += 040; + if (byte & 010) + XA &= 03740; + if (byte & 4) /* Enter PPM mode. */ + ; + if (byte & 2) + YA += 040; + if (byte & 1) + YA &= 03740; +} + +static void +dp_inc (uint16 byte) +{ + if (byte & 0200) { + /* Increment byte. */ + dp_inc_vector (byte); + } else { + /* Escape byte. */ + dp_inc_escape (byte); + } +} + +static void +dp_deim (uint16 insn) +{ + MODE = 1; + sim_debug (DBG, &dp_dev, "E,"); + dp_inc (insn & 0377); + sim_debug (DBG, &dp_dev, "\n"); +} + +static void +dp_dlvh (uint16 insn1, uint16 insn2, uint16 insn3) +{ + uint16 x1 = XA, y1 = YA; + uint16 m, n, dx, dy; + m = insn2 & 07777; + n = insn3 & 07777; + if (insn3 & 010000) { + dx = m; + dy = n; + } else { + dx = n; + dy = m; + } + if (insn3 & 040000) + XA -= SCALE * dx; + else + XA += SCALE * dx; + if (insn3 & 020000) + YA -= SCALE * dy; + else + YA += SCALE * dy; + if (insn2 & 020000) + crt_line (x1, y1, XA, YA); +} + +static void +dp_insn (uint16 insn) +{ + switch ((insn >> 12) & 7) { + case 0: /* DOPR */ + dp_opr (insn); + break; + case 1: /* DLXA */ + sim_debug (DBG, &dp_dev, "DLXA\n"); + XA = (insn & 01777) << 1; + break; + case 2: /* DLYA */ + sim_debug (DBG, &dp_dev, "DLYA\n"); + YA = (insn & 01777) << 1; + break; + case 3: /* DEIM */ + sim_debug (DBG, &dp_dev, "DEIM "); + dp_deim (insn); + break; + case 4: /* DLVH */ + sim_debug (DBG, &dp_dev, "DLVH\n"); + dp_dlvh (insn, M[DPC], M[DPC+1]); + DPC += 2; + break; + case 5: /* DJMS */ + sim_debug (DBG, &dp_dev, "DJMS\n"); + DT = DPC; + /* Fall through. */ + case 6: /* DJMP */ + sim_debug (DBG, &dp_dev, "DJMP\n"); + jump (insn); + break; + case 7: /* optional */ + dp_opt (insn); + break; + } +} + +static t_stat +dp_svc(UNIT * uptr) +{ + uint16 insn; + + if (sim_brk_summ && sim_brk_test(DPC, SWMASK('D'))) { + sim_activate_abs (&dp_unit, 0); + return sim_messagef (SCPE_STOP, "Display processor breakpoint.\n"); + } + + sim_debug (DBG, &dp_dev, "%06o ", DPC); + insn = M[DPC]; + DPC++; + if (MODE) { + sim_debug (DBG, &dp_dev, "INC "); + dp_inc (insn >> 8); + if (MODE) { + sim_debug (DBG, &dp_dev, ","); + dp_inc (insn & 0377); + } + sim_debug (DBG, &dp_dev, "\n"); + } else + dp_insn (insn); + + if (ON) + sim_activate_after (&dp_unit, 2); + + return SCPE_OK; +} + +static t_stat +sync_svc (UNIT *uptr) +{ + sim_debug (DBG, &sync_dev, "40 Hz sync\n"); + SYNC = 1; + if (SYNC && HALT) + flag_on (FLAG_SYNC); + sim_cancel (&sync_unit); + return SCPE_OK; +} + +static uint16 +sync_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0071) { /* SCF */ + sim_debug (DBG, &sync_dev, "Clear flag\n"); + SYNC = 0; + flag_off (FLAG_SYNC); + sim_activate_after (&sync_unit, 25000); + } + if ((insn & 0772) == 0072) { /* IOS */ + } + return AC; +} diff --git a/imlac/imlac_kbd.c b/imlac/imlac_kbd.c new file mode 100644 index 00000000..b141d3d7 --- /dev/null +++ b/imlac/imlac_kbd.c @@ -0,0 +1,641 @@ +/* imlac_kbd.c: Imlac 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 "imlac_defs.h" +#include "sim_video.h" + +/* Debug */ +#define DBG 0001 + +#define KBD_DISPLAY 1 +#define KBD_CONSOLE 2 + +#define SHFT 00400 +#define CTRL 01000 +#define REPT 02000 +#define META 00000 +#define TOP 00000 + +static uint16 KBUF; +static uint16 modifiers; +static int kbd_type = KBD_DISPLAY; + +/* Function declaration. */ +static t_stat kbd_svc (UNIT *uptr); +static t_stat kbd_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat kbd_show_type (FILE *st, UNIT *up, int32 v, CONST void *dp); +static t_stat kbd_reset (DEVICE *dptr); +static uint16 kbd_iot (uint16, uint16); + +static UNIT kbd_unit = { + UDATA (&kbd_svc, UNIT_IDLE, 0) +}; + +static REG kbd_reg[] = { + { ORDATAD (KBUF, KBUF, 16, "Keyboard buffer") }, + { NULL } +}; + +MTAB kbd_mod[] = { + { MTAB_VDV|MTAB_VALR, 1, "TYPE", "TYPE", &kbd_set_type, + &kbd_show_type, NULL, "Set keyboard input type" }, + { 0 } +}; + +static IMDEV kbd_imdev = { + 1, + { { 0002, kbd_iot, { NULL, "KRB", "KCF", "KRC" } } } +}; + +static DEBTAB kbd_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +DEVICE kbd_dev = { + "KBD", &kbd_unit, kbd_reg, kbd_mod, + 0, 8, 16, 1, 8, 16, + NULL, NULL, &kbd_reset, + NULL, NULL, NULL, &kbd_imdev, + DEV_DISABLE | DEV_DEBUG, 0, kbd_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static int32 kbd_translate (int32 ch) +{ + static int32 table[] = { + 01240, 01301, 00202, 01303, 00204, 00205, 00206, 01307, /* ^@ - ^G */ + 00210, 00211, 00212, 01313, 00214, 00215, 00216, 00217, + 01320, 01321, 01322, 01323, 01324, 01325, 01326, 01327, + 00230, 01331, 01332, 00233, 00234, 00235, 00236, 01337, + 00240, 00241, 00242, 00243, 00244, 00245, 00246, 00247, /* SPC - ' */ + 00250, 00251, 00252, 00253, 00254, 00255, 00256, 00257, + 00260, 00261, 00262, 00263, 00264, 00265, 00266, 00267, + 00270, 00271, 00272, 00273, 00274, 00275, 00276, 00277, + 00300, 00301, 00302, 00303, 00304, 00305, 00306, 00307, /* @ - G */ + 00310, 00311, 00312, 00313, 00314, 00315, 00316, 00317, /* H - O */ + 00320, 00321, 00322, 00323, 00324, 00325, 00326, 00327, /* P - W */ + 00330, 00331, 00332, 00333, 00334, 00335, 00336, 00337, /* X - _ */ + 00340, 00341, 00342, 00343, 00344, 00345, 00346, 00347, /* ` - g */ + 00350, 00351, 00352, 00353, 00354, 00355, 00356, 00357, + 00360, 00361, 00362, 00363, 00364, 00365, 00366, 00367, + 00370, 00371, 00372, 00373, 00374, 00375, 00376, 00377 + }; + return table[ch]; +} + +static t_stat kbd_svc (UNIT *uptr) +{ + t_stat ch = sim_poll_kbd (); + + if ((ch & SCPE_KFLAG) == 0) { + sim_activate_after (&kbd_unit, 10000); + return ch; + } + + if (ch & SCPE_BREAK) + KBUF = 0231; + else + KBUF = kbd_translate (ch & 0177); + flag_on (FLAG_KBD); + sim_debug (DBG, &kbd_dev, "Received character %03o\n", KBUF); + return SCPE_OK; +} + +static int +kbd_modifiers (SIM_KEY_EVENT *ev) +{ + uint16 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_WIN_L: + case SIM_KEY_WIN_R: + code = TOP; + 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 +kbd_both (uint32 key) +{ + uint16 code; + switch (key) { + case SIM_KEY_END: + code = 0002; // XMIT + break; + case SIM_KEY_DOWN: + code = 0004; + break; + case SIM_KEY_RIGHT: + code = 0005; + break; + case SIM_KEY_UP: + code = 0006; + break; + case SIM_KEY_LEFT: + code = 0010; + break; + case SIM_KEY_TAB: + code = 0011; + break; +#if 0 + case SIM_KEY_: + code = 0012; // LF + break; +#endif + case SIM_KEY_PAGE_UP: + code = 0014; // FORM + break; + case SIM_KEY_ENTER: + code = 0015; + break; + case SIM_KEY_PAGE_DOWN: + code = 0016; // PAGE XMIT + break; + case SIM_KEY_HOME: + code = 0017; + break; + case SIM_KEY_KP_INSERT: + code = 0030; // KP_0 + break; + case SIM_KEY_PAUSE: + code = 0031; // BRK + break; + case SIM_KEY_KP_DOWN: + code = 0032; // KP_2 + break; + case SIM_KEY_ESC: + code = 0033; + break; + case SIM_KEY_KP_LEFT: + code = 0034; // KP_4 + break; + case SIM_KEY_KP_5: + code = 0035; // KP_5 + break; + case SIM_KEY_KP_RIGHT: + code = 0036; // KP_6 + break; + case SIM_KEY_SPACE: + code = 0040; + break; + case SIM_KEY_BACKSPACE: + case SIM_KEY_DELETE: + code = 0177; + break; + default: + return 0; + } + return code | modifiers; +} + +static int +kbd_shift (uint32 key) +{ + uint16 code; + + code = kbd_both (key); + if (code != 0) + return code; + + switch (key) { + case SIM_KEY_0: + code = ')'; + break; + case SIM_KEY_1: + code = '!'; + break; + case SIM_KEY_2: + return CTRL + ';'; + break; + case SIM_KEY_3: + code = '#'; + break; + case SIM_KEY_4: + code = '$'; + break; + case SIM_KEY_5: + code = '%'; + break; + case SIM_KEY_6: + return CTRL + ':'; + 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 CTRL + '6'; + case SIM_KEY_MINUS: + return CTRL + '-'; + case SIM_KEY_EQUALS: + code = '+'; + break; + case SIM_KEY_LEFT_BRACKET: + return CTRL + '8'; + case SIM_KEY_RIGHT_BRACKET: + return CTRL + '9'; + case SIM_KEY_SEMICOLON: + code = ':'; + break; + case SIM_KEY_SINGLE_QUOTE: + code = '"'; + break; + case SIM_KEY_BACKSLASH: + case SIM_KEY_LEFT_BACKSLASH: + return CTRL + '0'; + case SIM_KEY_COMMA: + code = '<'; + break; + case SIM_KEY_PERIOD: + code = '>'; + break; + case SIM_KEY_SLASH: + code = '?'; + break; + default: + return 0; + } + return code | modifiers; +} + +static int +kbd_noshift (uint32 key) +{ + uint16 code; + + code = kbd_both (key); + if (code != 0) + 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 = CTRL + '7'; + break; + case SIM_KEY_MINUS: + code = '-'; + break; + case SIM_KEY_EQUALS: + code = SHFT + '='; + break; + case SIM_KEY_LEFT_BRACKET: + code = CTRL + ','; + break; + case SIM_KEY_RIGHT_BRACKET: + code = CTRL + '.'; + break; + case SIM_KEY_SEMICOLON: + code = ';'; + break; + case SIM_KEY_SINGLE_QUOTE: + code = SHFT + '\''; + break; + case SIM_KEY_BACKSLASH: + case SIM_KEY_LEFT_BACKSLASH: + code = CTRL + '/'; + break; + case SIM_KEY_COMMA: + code = ','; + break; + case SIM_KEY_PERIOD: + code = '.'; + break; + case SIM_KEY_SLASH: + code = '/'; + break; + default: + return 0; + } + return code | modifiers; +} + +static int +kbd_event (SIM_KEY_EVENT *ev) +{ + sim_debug (DBG, &kbd_dev, "Key %s %s\n", + ev->state == SIM_KEYPRESS_UP ? "up" : "down", + vid_key_name (ev->key)); + + if (kbd_modifiers (ev)) + return 0; + + if (ev->state == SIM_KEYPRESS_DOWN) { + uint16 code; + if (modifiers & SHFT) + code = kbd_shift (ev->key); + else + code = kbd_noshift (ev->key); + if (code != 0) { + KBUF = code | 0200; + sim_debug (DBG, &kbd_dev, "Received character %03o\n", KBUF); + flag_on (FLAG_KBD); + } + } else if (ev->state == SIM_KEYPRESS_UP) + KBUF = 0; + + return 0; +} + +static t_stat +kbd_reset (DEVICE *dptr) +{ +#ifdef HAVE_LIBSDL + vid_display_kb_event_process = NULL; +#endif + if (dptr->flags & DEV_DIS) + return SCPE_OK; + + if (kbd_type == KBD_DISPLAY) +#ifdef HAVE_LIBSDL + vid_display_kb_event_process = kbd_event; +#else + ; +#endif + else if (kbd_type == KBD_CONSOLE) + sim_activate_abs (&kbd_unit, 0); + else + return SCPE_ARG; + + return SCPE_OK; +} + +static uint16 +kbd_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0021) { /* KRC */ + sim_debug (DBG, &kbd_dev, "Read character %03o\n", KBUF); + AC |= KBUF; + if (kbd_type == KBD_CONSOLE) + KBUF = 0; + } + if ((insn & 0772) == 0022) { /* KCF */ + sim_debug (DBG, &kbd_dev, "Clear flag\n"); + flag_off (FLAG_KBD); + if (kbd_type == KBD_CONSOLE) + sim_activate_after (&kbd_unit, 10000); + } + return AC; +} + +static t_stat +kbd_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + t_stat r = SCPE_OK; + if (strcmp (cptr, "DISPLAY") == 0) + kbd_type = KBD_DISPLAY; + else if (strcmp (cptr, "CONSOLE") == 0) + kbd_type = KBD_CONSOLE; + else + r = SCPE_ARG; + return r; +} + +static t_stat +kbd_show_type (FILE *st, UNIT *up, int32 v, CONST void *dp) +{ + switch (kbd_type) { + case KBD_DISPLAY: + fprintf (st, "TYPE=DISPLAY"); + break; + case KBD_CONSOLE: + fprintf (st, "TYPE=CONSOLE"); + break; + default: + fprintf (st, "TYPE=(invalid)"); + break; + } + return SCPE_OK; +} diff --git a/imlac/imlac_pt.c b/imlac/imlac_pt.c new file mode 100644 index 00000000..da0742c5 --- /dev/null +++ b/imlac/imlac_pt.c @@ -0,0 +1,113 @@ +/* imlac_pt.c: Imlac paper tape reader and punch + + 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 "imlac_defs.h" + +/* Function declaration. */ +static uint16 ptr_iot (uint16, uint16); +static uint16 ptp_iot (uint16, uint16); +static t_stat ptr_boot (int32 u, DEVICE *dptr); + +static uint16 ptr_rom[] = { + 0060077, 0020010, 0104076, 0020020, 0001061, 0100011, 0002400, 0010046, + 0001051, 0074075, 0010045, 0002400, 0010053, 0001051, 0003003, 0003003, + 0003002, 0102400, 0010061, 0002400, 0010063, 0001051, 0120010, 0102400, + 0010067, 0100011, 0030020, 0010053, 0110076, 0000002, 0037700, 0037677, +}; + +static IMDEV ptr_imdev = { + 2, + { { 0005, ptr_iot, { NULL, "HRB", "HOF", NULL } }, + { 0006, ptr_iot, { NULL, "HON", "STB", NULL } } } +}; + +DEVICE ptr_dev = { + "PTR", NULL, NULL, NULL, + 0, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + &ptr_boot, NULL, NULL, + &ptr_imdev, DEV_DISABLE | DEV_DEBUG | DEV_DIS, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static IMDEV ptp_imdev = { + 1, + { { 0027, ptp_iot, { "PUN", NULL, NULL, "PSF" } } } +}; + +DEVICE ptp_dev = { + "PTP", NULL, NULL, NULL, + 0, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + NULL, NULL, NULL, + &ptp_imdev, DEV_DISABLE | DEV_DEBUG | DEV_DIS, 0, NULL, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static uint16 +ptr_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0051) { /* HRB */ + AC |= 0; + } + if ((insn & 0772) == 0052) { /* HOF */ + ; + } + if ((insn & 0771) == 0061) { /* HON */ + ; + } + if ((insn & 0772) == 0062) { /* STB */ + ; + } + return AC; +} + +static uint16 +ptp_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0271) { /* PUN */ + ; + } + if ((insn & 0772) == 0274) { /* PSF */ + ; + } + return SCPE_OK; +} + +void +rom_ptr (void) +{ + rom_data (ptr_rom); +} + +static t_stat +ptr_boot (int32 u, DEVICE *dptr) +{ + uint16 *PC = (uint16 *)sim_PC->loc; + set_cmd (0, "ROM TYPE=PTR"); + *PC = 040; + return SCPE_OK; +} diff --git a/imlac/imlac_sys.c b/imlac/imlac_sys.c new file mode 100644 index 00000000..3563da93 --- /dev/null +++ b/imlac/imlac_sys.c @@ -0,0 +1,533 @@ +/* imlac_sys.c: Imlac 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. + + 21-Apr-20 LB New simulator. +*/ + +#include "imlac_defs.h" + +int32 sim_emax = 1; +char sim_name[] = "Imlac"; + +uint16 M[040000]; +SUBDEV *dev_tab[0100]; +REG *sim_PC = &cpu_reg[0]; + +DEVICE *sim_devices[] = { + &cpu_dev, + &rom_dev, + &dp_dev, /* 0-1 */ + &crt_dev, + &kbd_dev, /* 2 */ + &tty_dev, /* 3-4 */ + &ptr_dev, /* 5-6 */ + &sync_dev, /* 7, 30 */ + &irq_dev, /* 10, 14, 16 */ + /* &prot_dev, / * 11 */ + /* &pen_dev, / * 13 */ + &ptp_dev, /* 27 */ + /* &mse_dev, / * 70, 73 */ + /* &bel_dev, / * 71 */ + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Invalid access", +}; + +static t_stat +get4 (FILE *fileref, uint16 *x) +{ + int c; + for (;;) { + c = Fgetc (fileref); + if (c == EOF) + return SCPE_IOERR; + if ((c & 0160) == 0100) { + *x = c & 017; + return SCPE_OK; + } + } +} + +static t_stat +get8 (FILE *fileref, uint16 *x) +{ + uint16 y; + if (get4 (fileref, x) != SCPE_OK) + return SCPE_IOERR; + if (get4 (fileref, &y) != SCPE_OK) + return SCPE_IOERR; + *x = (*x << 4) | y; + return SCPE_OK; +} + +static t_stat +get16 (FILE *fileref, uint16 *x) +{ + uint16 y; + if (get8 (fileref, x) != SCPE_OK) + return SCPE_IOERR; + if (get8 (fileref, &y) != SCPE_OK) + return SCPE_IOERR; + *x = (*x << 8) | y; + return SCPE_OK; +} + +static t_stat +load_stty (FILE *fileref) +{ + int verbose = sim_switches & SWMASK ('V'); + uint16 *PC = (uint16 *)sim_PC->loc; + uint16 x, count, addr; + uint32 csum; + int i; + + /* Discard block loader. */ + for (i = 0; i < 65; i++) { + if (get16 (fileref, &x) != SCPE_OK) + return SCPE_IOERR; + } + + for (;;) { + /* Read count and address. */ + if (get8 (fileref, &count) != SCPE_OK) + return SCPE_IOERR; + if (get16 (fileref, &addr) != SCPE_OK) + return SCPE_IOERR; + + /* Address all ones means done. */ + if (addr == 0177777) { + *PC = 077713 & memmask; + return SCPE_OK; + } + + if (verbose) + sim_messagef (SCPE_OK, "Address %06o: %d words.\n", addr, count); + + csum = 0; + for (i = 0; i < count; i++) { + if (get16 (fileref, &x) != SCPE_OK) + return SCPE_IOERR; + M[(addr++) & memmask] = x; + csum += x; + if (csum & 0200000) { + csum++; + csum &= 0177777; + } + } + + if (get16 (fileref, &x) != SCPE_OK) + return SCPE_IOERR; + if (x != csum) + return SCPE_CSUM; + } +} + +t_stat +sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + if (sim_switches & SWMASK ('T')) + ; + if (sim_switches & SWMASK ('S')) + ; + if (sim_switches & SWMASK ('M')) + ; + if (sim_switches & SWMASK ('P')) + ; + + return load_stty (fileref); +} + +t_bool build_dev_tab (void) +{ + DEVICE *dev; + IMDEV *imdev; + int i, j; + + memset (dev_tab, 0, sizeof dev_tab); + + for (i = 0; (dev = sim_devices[i]) != NULL; i++) { + imdev = (IMDEV *)dev->ctxt; + if (imdev != NULL) { + for (j = 0; j < imdev->codes; j++) + dev_tab[imdev->subdev[j].num] = &imdev->subdev[j]; + } + } + + return SCPE_OK; +} + +static t_stat fprint_class1 (FILE *of, uint16 insn) +{ + switch (insn & 0777) { + case 0000: fprintf (of, "NOP"); break; + case 0001: fprintf (of, "CLA"); break; + case 0002: fprintf (of, "CMA"); break; + case 0003: fprintf (of, "STA"); break; + case 0004: fprintf (of, "IAC"); break; + case 0005: fprintf (of, "COA"); break; + case 0006: fprintf (of, "CIA"); break; + case 0010: fprintf (of, "CLL"); break; + case 0011: fprintf (of, "CAL"); break; + case 0020: fprintf (of, "CML"); break; + case 0030: fprintf (of, "STL"); break; + case 0040: fprintf (of, "ODA"); break; + case 0041: fprintf (of, "LDA"); break; + default: fprintf (of, "%06o", insn); break; + } + return SCPE_OK; +} + +static t_stat fprint_class2 (FILE *of, uint16 insn) +{ + switch (insn & 0770) { + case 0000: fprintf (of, "RAL %o", insn & 7); break; + case 0020: fprintf (of, "RAR %o", insn & 7); break; + case 0040: fprintf (of, "SAL %o", insn & 7); break; + case 0060: fprintf (of, "SAR %o", insn & 7); break; + case 0100: fprintf (of, "DON"); break; + default: fprintf (of, "%06o", insn); break; + } + return SCPE_OK; +} + +static t_stat fprint_class3 (FILE *of, uint16 insn) +{ + switch (insn & 0177777) { + case 0002001: fprintf (of, "ASZ"); break; + case 0102001: fprintf (of, "ASN"); break; + case 0002002: fprintf (of, "ASP"); break; + case 0102002: fprintf (of, "ASM"); break; + case 0002004: fprintf (of, "LSZ"); break; + case 0102004: fprintf (of, "LSN"); break; + case 0002010: fprintf (of, "DSF"); break; + case 0102010: fprintf (of, "DSN"); break; + case 0002020: fprintf (of, "KSF"); break; + case 0102020: fprintf (of, "KSN"); break; + case 0002040: fprintf (of, "RSF"); break; + case 0102040: fprintf (of, "RSN"); break; + case 0002100: fprintf (of, "TSF"); break; + case 0102100: fprintf (of, "TSN"); break; + case 0002200: fprintf (of, "SSF"); break; + case 0102200: fprintf (of, "SSN"); break; + case 0002400: fprintf (of, "HSF"); break; + case 0102400: fprintf (of, "HSN"); break; + default: fprintf (of, "%06o", insn); break; + } + return SCPE_OK; +} + +static t_stat fprint_iot (FILE *of, uint16 insn) +{ + SUBDEV *imdev; + + imdev = dev_tab[(insn >> 3) & 077]; + if (imdev != NULL && imdev->mnemonics[insn & 7] != NULL) { + fprintf (of, "%s", imdev->mnemonics[insn & 7]); + return SCPE_OK; + } + + fprintf (of, "IOT %03o", insn & 0777); + return SCPE_OK; +} + +static t_stat fprint_opr (FILE *of, uint16 insn) +{ + switch ((insn >> 9) & 0177) { + case 0000: + fprintf (of, "HLT "); + if (insn == 0) + break; + /* Fall through. */ + case 0100: + return fprint_class1 (of, insn); + case 0003: + return fprint_class2 (of, insn); + case 0002: + case 0102: + return fprint_class3 (of, insn); + case 0001: + return fprint_iot (of, insn); + default: + fprintf (of, "%06o", insn); + break; + } + + return SCPE_OK; +} + +static t_stat +fprint_cpu (FILE *of, uint16 insn, uint16 addr) +{ + switch ((insn >> 9) & 074) { + case 000: + return fprint_opr (of, insn); + case 004: + if (insn & 0100000) + fprintf (of, "LWC %o", insn & 03777); + else + fprintf (of, "LAW %o", insn & 03777); + return SCPE_OK; + case 010: + fprintf (of, "JMP"); + break; + case 020: + fprintf (of, "DAC"); + break; + case 024: + fprintf (of, "XAM"); + break; + case 030: + fprintf (of, "ISZ"); + break; + case 034: + fprintf (of, "JMS"); + break; + case 044: + fprintf (of, "AND"); + break; + case 050: + fprintf (of, "IOR"); + break; + case 054: + fprintf (of, "XOR"); + break; + case 060: + fprintf (of, "LAC"); + break; + case 064: + fprintf (of, "ADD"); + break; + case 070: + fprintf (of, "SUB"); + break; + case 074: + fprintf (of, "SAM"); + break; + default: + fprintf (of, "%06o", insn); + break; + } + + fprintf (of, " "); + if (insn & 0100000) + fprintf (of, "@"); + fprintf (of, "%o", (insn & 03777) | (addr & 014000)); + + return SCPE_OK; +} + +static t_stat +fprint_dopr (FILE *of, uint16 insn) +{ + if (insn == 04000) { + fprintf (of, "DNOP"); + return SCPE_OK; + } + + switch (insn & 00014) { + case 000: + if (insn & 1) + fprintf (of, "DADR "); + break; + case 004: + fprintf (of, "DSTS %o ", insn & 3); + break; + case 010: + fprintf (of, "DSTB %o ", insn & 3); + break; + case 014: + fprintf (of, "Unknown DP instruction %06o", insn); + break; + } + if (insn & 00020) + fprintf (of, "DDSP "); + if (insn & 00040) + fprintf (of, "DRJM "); + if (insn & 00100) + fprintf (of, "DDYM "); + if (insn & 00200) + fprintf (of, "DDXM "); + if (insn & 00400) + fprintf (of, "DIYM "); + if (insn & 01000) + fprintf (of, "DIXM "); + if (insn & 02000) + fprintf (of, "DHVC "); + if ((insn & 04000) == 0) + fprintf (of, "DHLT "); + + return SCPE_OK; +} + +static t_stat +fprint_inc_byte (FILE *of, uint16 byte) +{ + if (byte & 0200) { + if (byte == 0200) { + fprintf (of, "P"); + return SCPE_OK; + } + + fprintf (of, "%s", byte & 0100 ? "B" : "D"); + if (byte & 00040) + fprintf (of, "M"); + fprintf (of, "%o", (byte >> 3) & 3); + if (byte & 00004) + fprintf (of, "M"); + fprintf (of, "%o", byte & 3); + } else { + if (byte == 0140) + fprintf (of, "X"); + else if (byte == 0060) + fprintf (of, "E"); + else if (byte == 0100) + fprintf (of, "T"); + else if (byte == 0111) + fprintf (of, "N"); + else if (byte == 0151) + fprintf (of, "R"); + else if (byte == 0171) + fprintf (of, "F"); + else { + if (byte & 0100) + fprintf (of, "ESC "); + if (byte & 0040) + fprintf (of, "RJM "); + if (byte & 0020) + fprintf (of, "+X "); + if (byte & 0010) + fprintf (of, "0X "); + if (byte & 0004) + fprintf (of, "PPM "); + if (byte & 0002) + fprintf (of, "+Y "); + if (byte & 0001) + fprintf (of, "0Y "); + } + } + + return SCPE_OK; +} + +static t_stat +fprint_deim (FILE *of, uint16 insn) +{ + fprintf (of, "DEIM "); + fprint_inc_byte (of, (insn >> 8) & 0377); + fprintf (of, ","); + fprint_inc_byte (of, insn & 0377); + return SCPE_OK; +} + +static t_stat +fprint_dp_opt (FILE *of, uint16 insn) +{ + switch (insn) { + case 077771: + fprintf (of, "DGD"); + break; + case 077775: + fprintf (of, "DGB"); + break; + default: + fprintf (of, "Unknown DP instruction: %06o", insn); + break; + } + + return SCPE_OK; +} + +static t_stat +fprint_dp (FILE *of, uint16 insn, uint16 addr) +{ + switch ((insn >> 12) & 7) { + case 0: + return fprint_dopr (of, insn); + case 1: + fprintf (of, "DLXA %o", insn & 07777); + break; + case 2: + fprintf (of, "DLYA %o", insn & 07777); + break; + case 3: + return fprint_deim (of, insn); + case 4: + fprintf (of, "DLVH %04o, %06o, %06o", + insn & 07777, M[addr + 1], M[addr + 2]); + return -2; + case 5: + fprintf (of, "DJMS %o", insn & 07777); + break; + case 6: + fprintf (of, "DJMP %o", insn & 07777); + break; + case 7: + return fprint_dp_opt (of, insn); + } + + return SCPE_OK; +} + +static t_stat fprint_inc (FILE *of, uint16 insn) +{ + fprintf (of, "INC "); + fprint_inc_byte (of, (insn >> 8) & 0377); + fprintf (of, ","); + fprint_inc_byte (of, insn & 0377); + 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 (sw & SWMASK ('M')) + return fprint_cpu (of, *val, addr); + else if (sw & SWMASK ('D')) + return fprint_dp (of, *val, addr); + else if (sw & SWMASK ('I')) + return fprint_inc (of, *val); + else + 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/imlac/imlac_tty.c b/imlac/imlac_tty.c new file mode 100644 index 00000000..6d4d6d1c --- /dev/null +++ b/imlac/imlac_tty.c @@ -0,0 +1,290 @@ +/* imlac_tty.c: Imlac 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 "imlac_defs.h" +#include "sim_tmxr.h" + +/* Debug */ +#define DBG 0001 + +#define TTY_FILE 1 /* Attached to a file. */ +#define TTY_PORT 2 /* Attached to a network port. */ + +static uint16 RBUF, TBUF; +static int tty_type = TTY_PORT; + +/* Function declaration. */ +static uint16 tty_iot (uint16, uint16); +static t_stat tty_r_svc (UNIT *uptr); +static t_stat tty_t_svc (UNIT *uptr); +static t_stat tty_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat tty_show_type (FILE *st, UNIT *up, int32 v, CONST void *dp); +static t_stat tty_boot (int32 u, DEVICE *dptr); +static t_stat tty_attach (UNIT *uptr, CONST char *cptr); +static t_stat tty_detach (UNIT *uptr); + +static TMLN tty_ldsc = { 0 }; +static TMXR tty_desc = { 1, 0, 0, &tty_ldsc }; + +static uint16 tty_rom[] = { + 0060077, 0020010, 0104076, 0020020, 0001032, 0100011, 0002040, 0010046, + 0001031, 0074075, 0010044, 0002040, 0010053, 0001033, 0003003, 0003003, + 0003002, 0002040, 0010061, 0001033, 0120010, 0100011, 0030020, 0010053, + 0110076, 0000000, 0000000, 0000000, 0000000, 0000002, 0037700, 0037677, +}; + +static uint16 stty_rom[] = { + 0001032, 0104101, 0020010, 0020020, 0104004, 0020021, 0100011, 0020022, + 0100011, 0002040, 0010051, 0001033, 0020023, 0044075, 0074076, 0010050, + 0060023, 0044077, 0024022, 0003003, 0003001, 0050022, 0020022, 0030021, + 0010050, 0120010, 0030020, 0010044, 0110000, 0000160, 0000100, 0000017 +}; + +static uint16 mtty_rom[] = { + 0060077, 0020010, 0104076, 0020020, 0001032, 0100011, 0002040, 0010046, + 0001031, 0074075, 0010044, 0002040, 0010053, 0001033, 0003003, 0003003, + 0003002, 0002040, 0010061, 0001033, 0120010, 0100011, 0030020, 0010053, + 0110076, 0004200, 0100040, 0001043, 0010040, 0000002, 0037700, 0037677, +}; + +static UNIT tty_unit[] = { + { UDATA (&tty_r_svc, UNIT_IDLE+UNIT_ATTABLE, 0) }, + { UDATA (&tty_t_svc, UNIT_IDLE+UNIT_ATTABLE, 0) } +}; + +static REG tty_reg[] = { + { ORDATAD (RB, RBUF, 8, "Receive buffer") }, + { ORDATAD (TB, TBUF, 8, "Transmit buffer") }, + { NULL } +}; + +MTAB tty_mod[] = { + { MTAB_VDV|MTAB_VALR, 1, "TYPE", "TYPE", &tty_set_type, + &tty_show_type, NULL, "Set attach type" }, + { MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &tty_desc, "Disconnect a specific line" }, + { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL, + &tmxr_show_summ, (void *) &tty_desc, "Display a summary of line states" }, + { MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, + &tmxr_show_cstat, (void *) &tty_desc, "Display current connections" }, + { MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, NULL, + &tmxr_show_cstat, (void *) &tty_desc, "Display multiplexer statistics" }, + { 0 } +}; + +static IMDEV tty_imdev = { + 2, + { { 0003, tty_iot, { NULL, "RRB", "RCF", "RRC" } }, + { 0004, tty_iot, { NULL, "TPR", "TCF", "TPC" } } } +}; + +static DEBTAB tty_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +DEVICE tty_dev = { + "TTY", tty_unit, tty_reg, tty_mod, + 2, 8, 16, 1, 8, 16, + NULL, NULL, NULL, + tty_boot, tty_attach, tty_detach, + &tty_imdev, DEV_DEBUG, 0, tty_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static t_stat +tty_r_svc(UNIT *uptr) +{ + int32 ch; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_OK; + + if (uptr->fileref != NULL) { + char buf; + if (sim_fread (&buf, 1, 1, uptr->fileref) == 1) { + sim_debug (DBG, &tty_dev, "Received character %03o\n", buf); + RBUF = buf; + flag_on (FLAG_TTY_R); + } + } else { + if (tty_ldsc.conn) { + tmxr_poll_rx (&tty_desc); + ch = tmxr_getc_ln (&tty_ldsc); + if (ch & TMXR_VALID) { + RBUF = sim_tt_inpcvt (ch, TT_GET_MODE (tty_unit[0].flags)); + sim_debug (DBG, &tty_dev, "Received character %03o\n", RBUF); + flag_on (FLAG_TTY_R); + return SCPE_OK; + } + sim_activate_after (uptr, 200); + } else { + int32 ln = tmxr_poll_conn (&tty_desc); + if (ln >= 0) { + tty_ldsc.rcve = 1; + sim_debug (DBG, &tty_dev, "Connect\n"); + sim_activate_after (uptr, 200); + } else { + sim_activate_after (uptr, 10000); + } + } + } + + return SCPE_OK; +} + +static t_stat +tty_t_svc(UNIT *uptr) +{ + int32 ch; + + tmxr_poll_tx (&tty_desc); + + if (!tmxr_txdone_ln (&tty_ldsc)) + return SCPE_OK; + + ch = sim_tt_outcvt (TBUF, TT_GET_MODE (tty_unit[1].flags)); + if (tmxr_putc_ln (&tty_ldsc, ch) == SCPE_STALL) { + sim_activate_after (&tty_unit[1], 200); + } else { + sim_debug (DBG, &tty_dev, "Transmitted character %03o\n", TBUF); + tmxr_poll_tx (&tty_desc); + flag_on (FLAG_TTY_T); + } + + return SCPE_OK; +} + +static uint16 +tty_iot (uint16 insn, uint16 AC) +{ + if ((insn & 0771) == 0031) { /* RRB */ + sim_debug (DBG, &tty_dev, "Read character %03o\n", RBUF); + AC |= RBUF; + } + if ((insn & 0772) == 0032) { /* RCF */ + sim_debug (DBG, &tty_dev, "Clear read flag\n"); + flag_off (FLAG_TTY_R); + sim_activate_after (&tty_unit[0], 200); + } + if ((insn & 0771) == 0041) { /* TPR */ + sim_debug (DBG, &tty_dev, "Write character %03o\n", AC); + TBUF = AC; + sim_activate_after (&tty_unit[1], 200); + } + if ((insn & 0772) == 0042) { /* TCF */ + sim_debug (DBG, &tty_dev, "Clear transmit flag\n"); + flag_off (FLAG_TTY_T); + } + return AC; +} + +static t_stat +tty_set_type (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + t_stat r = SCPE_OK; + if (strcmp (cptr, "FILE") == 0) + tty_type = TTY_FILE; + else if (strcmp (cptr, "PORT") == 0) + tty_type = TTY_PORT; + else + r = SCPE_ARG; + return r; +} + +static t_stat +tty_show_type (FILE *st, UNIT *up, int32 v, CONST void *dp) +{ + switch (tty_type) { + case TTY_FILE: + fprintf (st, "TYPE=FILE"); + break; + case TTY_PORT: + fprintf (st, "TYPE=PORT"); + break; + default: + fprintf (st, "TYPE=(invalid)"); + break; + } + return SCPE_OK; +} + +void rom_tty (void) +{ + rom_data (tty_rom); +} + +void rom_stty (void) +{ + rom_data (stty_rom); +} + +static t_stat tty_boot (int32 u, DEVICE *dptr) +{ + uint16 *PC = (uint16 *)sim_PC->loc; + if (sim_switches & SWMASK ('T')) + set_cmd (0, "ROM TYPE=TTY"); + else if (sim_switches & SWMASK ('S')) + set_cmd (0, "ROM TYPE=STTY"); + else + return sim_messagef (SCPE_ARG, "Must specify one of -S or -T\n"); + *PC = 040; + return SCPE_OK; +} + +static t_stat +tty_attach (UNIT *uptr, CONST char *cptr) +{ + t_stat r; + + switch (tty_type) { + case TTY_PORT: + r = tmxr_attach (&tty_desc, uptr, cptr); + if (r != SCPE_OK) + return r; + sim_activate_abs (uptr, 0); + break; + case TTY_FILE: + r = attach_unit (uptr, cptr); + if (r != SCPE_OK) + return r; + break; + default: + return SCPE_ARG; + } + + return SCPE_OK; +} + +static t_stat +tty_detach (UNIT *uptr) +{ + if (!(uptr->flags & UNIT_ATT)) + return SCPE_OK; + if (sim_is_active (uptr)) + sim_cancel (uptr); + return detach_unit (uptr); +} diff --git a/makefile b/makefile index 9a7b71d4..ec859e4b 100644 --- a/makefile +++ b/makefile @@ -109,6 +109,10 @@ ifneq (,$(findstring besm6,${MAKECMDGOALS})) VIDEO_USEFUL = true BESM6_BUILD = true endif +# building the Imlac needs video support +ifneq (,$(findstring imlac,${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 @@ -601,6 +605,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) DISPLAY340 = ${DISPLAYD}/type340.c DISPLAYNG = ${DISPLAYD}/ng.c DISPLAYIII = ${DISPLAYD}/iii.c + DISPLAYIMLAC = ${DISPLAYD}/imlac.c DISPLAY_OPT += -DUSE_DISPLAY $(VIDEO_CCDEFS) $(VIDEO_LDFLAGS) $(info using libSDL2: $(call find_include,SDL2/SDL)) ifeq (Darwin,$(OSTYPE)) @@ -1488,6 +1493,14 @@ PDP10 = ${PDP10D}/pdp10_fe.c ${PDP11D}/pdp11_dz.c ${PDP10D}/pdp10_cpu.c \ PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} ${NETWORK_OPT} +IMLACD = ${SIMHD}/imlac +IMLAC = ${IMLACD}/imlac_sys.c ${IMLACD}/imlac_cpu.c \ + ${IMLACD}/imlac_dp.c ${IMLACD}/imlac_crt.c ${IMLACD}/imlac_kbd.c \ + ${IMLACD}/imlac_tty.c ${IMLACD}/imlac_pt.c \ + ${DISPLAYL} ${DISPLAYIMLAC} +IMLAC_OPT = -I ${IMLACD} ${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 \ @@ -2203,6 +2216,15 @@ ifneq (,$(call find_test,${PDP10D},pdp10)) $@ $(call find_test,${PDP10D},pdp10) ${TEST_ARG} endif +imlac : ${BIN}imlac${EXE} + +${BIN}imlac${EXE} : ${IMLAC} ${SIM} + ${MKDIRBIN} + ${CC} ${IMLAC} ${SIM} ${IMLAC_OPT} ${CC_OUTSPEC} ${LDFLAGS} +ifneq (,$(call find_test,${IMLAC},imlac)) + $@ $(call find_test,${IMLACD},imlac) ${TEST_ARG} +endif + pdp11 : ${BIN}pdp11${EXE} ${BIN}pdp11${EXE} : ${PDP11} ${SIM}