From f5f2930712fdd3c1e5867db2a36a6bf3e843a92a Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Wed, 17 Sep 2025 08:28:28 +0200 Subject: [PATCH] LINC: New emulator for the classic LINC. This emulates the classic LINC. The design was settled in 1965, increasing memory to 2048 words, and adding a Z register, an overflow flag, and an interrupt facility. --- display/display.c | 13 + display/display.h | 1 + linc/CMakeLists.txt | 32 ++ linc/linc_cpu.c | 863 ++++++++++++++++++++++++++++++++++++++++++++ linc/linc_crt.c | 122 +++++++ linc/linc_defs.h | 58 +++ linc/linc_dpy.c | 40 ++ linc/linc_kbd.c | 427 ++++++++++++++++++++++ linc/linc_sys.c | 593 ++++++++++++++++++++++++++++++ linc/linc_tape.c | 439 ++++++++++++++++++++++ linc/linc_tty.c | 6 + makefile | 24 +- 12 files changed, 2615 insertions(+), 3 deletions(-) create mode 100644 linc/CMakeLists.txt create mode 100644 linc/linc_cpu.c create mode 100644 linc/linc_crt.c create mode 100644 linc/linc_defs.h create mode 100644 linc/linc_dpy.c create mode 100644 linc/linc_kbd.c create mode 100644 linc/linc_sys.c create mode 100644 linc/linc_tape.c create mode 100644 linc/linc_tty.c diff --git a/display/display.c b/display/display.c index 020ea62d..4b2a8ed4 100644 --- a/display/display.c +++ b/display/display.c @@ -131,6 +131,10 @@ struct color color_p31 = { p31, ELEMENTS(p31), 100000 }; static struct phosphor p39[] = {{0.2, 1.0, 0.0, 0.5, 0.01}}; struct color color_p39 = { p39, ELEMENTS(p39), 20000 }; +/* orange phosphor for LINC */ +static struct phosphor p19[] = {{1.0, 0.7, 0.0, 0.1, 0.22}}; +struct color color_p19 = { p19, ELEMENTS(p19), 20000 }; + static struct phosphor p40[] = { /* P40 blue-white spot with yellow-green decay (.045s to 10%?) */ {0.4, 0.2, 0.924, 0.5, 0.0135}, @@ -253,6 +257,15 @@ static struct display displays[] = { */ { DIS_III, "III Display", &color_p39, NULL, 1024, 1024 }, + /* + * LINC display + * 512x511 addressable points. + * The horizontal position is a 9-bit unsigned value, but the + * vertical is a one's complement signed 9-bit value with + * both +0 and -0 referring to the same position. + */ + { DIS_LINC, "LINC Display", &color_p19, NULL, 512, 511 }, + /* * Imlac display * 1024x1024 addressable points. diff --git a/display/display.h b/display/display.h index cce79f9f..ad18c0a4 100644 --- a/display/display.h +++ b/display/display.h @@ -47,6 +47,7 @@ enum display_type { DIS_VR17 = 17, DIS_VR20 = 20, DIS_TYPE30 = 30, + DIS_LINC = 40, DIS_VR48 = 48, DIS_III = 111, DIS_TYPE340 = 340, diff --git a/linc/CMakeLists.txt b/linc/CMakeLists.txt new file mode 100644 index 00000000..bce48ea8 --- /dev/null +++ b/linc/CMakeLists.txt @@ -0,0 +1,32 @@ +## LINC simulator +## +## This is an automagically generated file. Do NOT EDIT. +## Any changes you make will be overwritten!! +## +## Make changes to the SIMH top-level makefile and then run the +## "cmake/generate.py" script to regenerate these files. +## +## cd cmake; python -m generate --help +## +## ------------------------------------------------------------ + +if (HAVE_UNITY_FRAMEWORK AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/unit-tests/CMakeLists.txt") + add_subdirectory(unit-tests) +endif () + +add_simulator(linc + SOURCES + linc_cpu.c + linc_crt.c + linc_dpy.c + linc_kbd.c + linc_sys.c + linc_tape.c + linc_tty.c + INCLUDES + ${CMAKE_CURRENT_SOURCE_DIR} + FEATURE_DISPLAY + USES_AIO + LABEL linc + PKG_FAMILY default_family + TEST linc) diff --git a/linc/linc_cpu.c b/linc/linc_cpu.c new file mode 100644 index 00000000..5dcf53fb --- /dev/null +++ b/linc/linc_cpu.c @@ -0,0 +1,863 @@ +/* linc_cpu.c: LINC CPU simulator + + Copyright (c) 2025, 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 "linc_defs.h" + + +/* Debug */ +#define DBG_CPU 0001 + +#define WMASK 07777 /* Full word. */ +#define HMASK 04000 /* H bit; half word select. */ +#define AMASK 03777 /* Full memory address. */ +#define XMASK 01777 /* X part; low memory address. */ +#define DMASK 00777 /* Display coordinate. */ +#define LMASK 07700 /* Left half word. */ +#define RMASK 00077 /* Right half word; character. */ +#define IMASK 00020 /* Index bit. */ +#define BMASK 00017 /* Beta; index register. */ + +#define X(_X) ((_X) & XMASK) + +/* CPU state. */ +static uint16 P; +static uint16 C; +static uint16 S; +static uint16 B; +static uint16 A; +static uint16 L; +static uint16 Z; +static uint16 R; +static uint16 LSW, RSW, SSW; +static uint16 SAM[16]; +static uint16 XL[12]; +static int paused; +static int IBZ; +static int OVF; +static int PINFF; +static int interrupt; +static int interrupt_enable = 0; + +static t_stat stop_reason; + +typedef struct { + uint16 P; + uint16 C; + uint16 S; + uint16 B; + uint16 A; + uint16 L; +} HISTORY; +static HISTORY *history = NULL; +static uint32 history_i, history_m, history_n; + +/* 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 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 UNIT cpu_unit = { UDATA(NULL, UNIT_FIX + UNIT_BINK, MEMSIZE) }; + +REG cpu_reg[] = { + { ORDATAD(P, P, 10, "Program Location") }, + { ORDATAD(C, C, 12, "Control Register") }, + { ORDATAD(A, A, 12, "Accumulator") }, + { ORDATAD(L, L, 1, "Link") }, + { ORDATAD(Z, Z, 12, "?") }, + { ORDATAD(R, R, 6, "Relay Register") }, + { ORDATAD(S, S, 12, "Memroy Address") }, + { ORDATAD(B, B, 12, "Memory Buffer") }, + { ORDATAD(LSW, LSW, 12, "Left Switches") }, + { ORDATAD(RSW, RSW, 12, "Right Switches") }, + { ORDATAD(SSW, SSW, 6, "Sense Switches") }, + { FLDATAD(paused, paused, 1, "Paused") }, + { FLDATAD(IBZ, IBZ, 1, "Interblock zone") }, + { FLDATAD(OVF, OVF, 1, "Overflow") }, + { BRDATAD(SAM, SAM, 8, 8, 16, "Sampled analog inputs") }, + { BRDATAD(XL, XL, 8, 1, 12, "External levels") }, + { NULL } +}; + +static MTAB cpu_mod[] = { + { 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 }, + { NULL, 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 0, 8, 11, 1, 8, 12, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, NULL, DEV_DEBUG, 0, cpu_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static void pcinc(int flag) +{ + if (flag) + P = X(P + 1); +} + +static void memaddr(uint16 addr) +{ + S = addr & WMASK; +} + +static void membuf(uint16 data) +{ + B = data & WMASK; +} + +static void memrd(void) +{ + B = M[S & AMASK]; + sim_interval--; + if (sim_brk_summ && sim_brk_test(S & AMASK, SWMASK('R'))) + stop_reason = STOP_RBKPT; +} + +static void memmd(void) +{ + M[S & AMASK] = B; + if (sim_brk_summ && sim_brk_test(S & AMASK, SWMASK('W'))) + stop_reason = STOP_WBKPT; +} + +static void memwr(void) +{ + sim_interval--; + memmd(); +} + +static int cpu_halfword(void) +{ + switch (C & 07740) { + case 01300: //LDH + case 01340: //STH + case 01400: //SHD + return 1; + default: + return 0; + } +} + +static void cpu_index(void) +{ + uint16 tmp; + if (C & IMASK) { + if (cpu_halfword()) { + B += HMASK; + tmp = B >> 12; + } else { + tmp = 1; + } + membuf((B & 06000) | X(B + tmp)); + memmd(); + } +} + +static void cpu_indexing(void) +{ + uint16 a = C & BMASK; + if (a == 0) + { + pcinc(1); + memaddr(P); + if ((C & IMASK) == 0) { + memrd(); + memaddr(B); + } + } + else + { + memaddr(a); + memrd(); + cpu_index(); + memaddr(B); + } +} + +static void +cpu_misc(void) +{ + switch (C) { + case 00000: //HLT + stop_reason = STOP_HALT; + break; + case 00005: //ZTA + A = Z >> 1; + break; + case 00010: //ENI + interrupt_enable = 1; + break; + case 00011: //CLR + A = L = Z = 0; + break; + case 00013: //Write gate on. + break; + case 00014: //ATR + R = A & RMASK; + break; + case 00015: //RTA + A = R & RMASK; + break; + case 00016: //NOP + break; + case 00017: //COM + A = (~A) & WMASK; + break; + } +} + +static void cpu_set(void) +{ + memaddr(P); + memrd(); + pcinc(1); + if ((C & IMASK) == 0) { + memaddr(B); + memrd(); + } + memaddr(C & BMASK); + memwr(); +} + +static void cpu_sam(void) +{ + // sample analog input C & BMASK + // 0-7 are pots, 10-17 are high speed inputs + // i=0 wait 24 microseconds, i=1 do not wait + if ((C & IMASK) == 0) + sim_interval -= 3; + A = SAM[C & BMASK]; + if (A & 0200) /* One's complement +/-177. */ + A |= 07600; +} + +static void cpu_dis(void) +{ + memaddr(C & BMASK); + memrd(); + cpu_index(); + sim_debug(DBG_CPU, &cpu_dev, "DIS α=%02o B=%04o A=%04o\n", S, B, A); + dpy_dis(B >> 11, B & DMASK, A & DMASK); +} + +static void cpu_xsk(void) +{ + memaddr(C & BMASK); + memrd(); + cpu_index(); + pcinc(X(B) == 01777); +} + +static void cpu_rol(void) +{ + while (C & BMASK) { + if (C & IMASK) { + A = (A << 1) | L; + L = A >> 12; + } else { + A = (A << 1) | (A >> 11); + } + A &= WMASK; + C--; + } +} + +static void cpu_ror(void) +{ + while (C & BMASK) { + Z = (Z >> 1) | ((A & 1) << 11); + if (C & IMASK) { + A |= L << 12; + L = A & 1; + A = A >> 1; + } else { + A = (A >> 1) | (A << 11); + A &= WMASK; + } + C--; + } +} + +static void cpu_scr(void) +{ + while (C & BMASK) { + Z = (Z >> 1) | ((A & 1) << 11); + if (C & IMASK) + L = A & 1; + A = (A & 04000) | (A >> 1); + C--; + } +} + +int cpu_skip(void) +{ + int flag = 0; + switch (C & 057) { + case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007: + case 010: case 011: case 012: case 013: //SXL + flag = XL[C & BMASK]; + break; + case 015: //KST + flag = kbd_struck(); + break; + case 040: case 041: case 042: case 043: case 044: case 045: //SNS + flag = SSW & (1 << (C & 7)); + break; + case 046: //PIN + flag = PINFF; + break; + case 050: //AZE + flag = (A == 0) || (A == WMASK); + break; + case 051: //APO + flag = (A & 04000) == 0; + break; + case 052: //LZE + flag = L == 0; + break; + case 053: //IBZ + flag = IBZ; + sim_debug(DBG_CPU, &cpu_dev, "IBZ%s => %d\n", C & IMASK ? " i" : "", flag); + break; + case 054: //OVF + flag = OVF; + break; + case 055: //ZZZ + flag = (Z & 1) == 0; + break; + default: + flag = 0; + break; + } + if (C & IMASK) + flag = !flag; + return flag; +} + +static void cpu_opr(void) +{ + switch (C & BMASK) { + case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007: + case 010: case 011: case 012: case 013: case 014: + if (C & IMASK) + ; //Pause. + break; + case 015: //KBD + A = kbd_key(C & IMASK); + break; + case 016: //RSW + A = RSW; + break; + case 017: //LSW + A = LSW; + break; + } +} + +static void cpu_lmb(void) +{ + //Instruction field. +} + +static void cpu_umb(void) +{ + //Data field. +} + +static void cpu_tape(void) +{ + memaddr(P); + memrd(); + pcinc(1); + tape_op(); +} + +static void cpu_lda(void) +{ + memrd(); + A = B; +} + +static void cpu_sta(void) +{ + membuf(A); + memwr(); +} + +static void cpu_ada(void) +{ + memrd(); + OVF = ~(A ^ B); + A += B; + A += A >> 12; + A &= WMASK; + OVF &= (A ^ B) & 04000; +} + +static void cpu_adm(void) +{ + cpu_ada(); + membuf(A); + memmd(); +} + +static void cpu_lam(void) +{ + memrd(); + A += L; + L = A >> 12; + A &= WMASK; + A += B; + if (A & 010000) + L = 1; + A &= WMASK; + membuf(A); + memmd(); +} + +static int32 sign_extend(uint16 x) +{ + if (x & 04000) + return -(x ^ WMASK); + else + return x; +} + +static void cpu_mul(void) +{ + int32 product; + memrd(); + // C used for counting. + product = sign_extend(A) * sign_extend(B); + if (product == 0 && ((A ^ B) & HMASK)) + product = 037777777; + else if (product < 0) + product--; + if (S & HMASK) + A = (product >> 11) & WMASK; + else if ((A ^ B) & HMASK) + A = 04000 | (product & 03777); + else + A = product & 03777; + L = A >> 11; +} + +static void cpu_ldh(void) +{ + memrd(); + if ((S & HMASK) == 0) + B >>= 6; + A = B & RMASK; +} + +static void cpu_sth(void) +{ + memrd(); + if (S & HMASK) + membuf((A & RMASK) | (B & LMASK)); + else + membuf((A << 6) | (B & RMASK)); + memmd(); +} + +static void cpu_shd(void) +{ + memrd(); + if ((S & HMASK) == 0) + B >>= 6; + pcinc((A & RMASK) != (B & RMASK)); +} + +static void cpu_sae(void) +{ + memrd(); + pcinc(A == B); +} + +static void cpu_sro(void) +{ + memrd(); + pcinc((B & 1) == 0); + membuf((B >> 1) | (B << 11)); + memmd(); +} + +static void cpu_bcl(void) +{ + memrd(); + A &= ~B; +} + +static void cpu_bse(void) +{ + memrd(); + A |= B; +} + +static void cpu_bco(void) +{ + memrd(); + A ^= B; +} + +static void cpu_dsc(void) +{ + int i, j; + uint16 data; + + memrd(); + data = B; + + // C used for counting. + memaddr(1); + memrd(); + sim_debug(DBG_CPU, &crt_dev, "DSC B=%04o A=%04o\n", B, A); + for (j = 0; j < 2; j++) { + membuf(B + 4); + A &= 07740; + for (i = 0; i < 6; i++) { + if (data & 1) + dpy_dis(B >> 11, B & DMASK, A & DMASK); + data >>= 1; + A += 4; + } + } + memmd(); +} + +static void cpu_add(void) +{ + memaddr(X(C)); + cpu_ada(); +} + +static void cpu_stc(void) +{ + memaddr(X(C)); + membuf(A); + A = 0; + memwr(); +} + +static void cpu_jmp(void) +{ + uint16 tmp = P; + P = X(C); + if (P != 0) { + membuf(06000 | tmp); + memaddr(0); + memwr(); + } +} + +static void +cpu_insn(void) +{ + /* Cycle 0, or I. */ + memaddr(P); + memrd(); + C = B; + + /* Cycle 1, or X. */ + if ((C & 07000) == 01000) + cpu_indexing(); + + if (history) { + history[history_i].P = P; + history[history_i].C = C; + history[history_i].S = S; + } + + /* Cycle 2, or O. */ + pcinc(1); + + /* Cycle 3, or E. */ + switch (C & 07740) { + case 00000: + cpu_misc(); + break; + case 00040: + cpu_set(); + break; + case 00100: + cpu_sam(); + break; + case 00140: + cpu_dis(); + break; + case 00200: + cpu_xsk(); + break; + case 00240: + cpu_rol(); + break; + case 00300: + cpu_ror(); + break; + case 00340: + cpu_scr(); + break; + case 00400: + case 00440: + pcinc(cpu_skip()); + break; + case 00500: + case 00540: + cpu_opr(); + break; + case 00600: + cpu_lmb(); + break; + case 00640: + cpu_umb(); + break; + case 00700: + case 00740: + cpu_tape(); + break; + case 01000: + cpu_lda(); + break; + case 01040: + cpu_sta(); + break; + case 01100: + cpu_ada(); + break; + case 01140: + cpu_adm(); + break; + case 01200: + cpu_lam(); + break; + case 01240: + cpu_mul(); + break; + case 01300: + cpu_ldh(); + break; + case 01340: + cpu_sth(); + break; + case 01400: + cpu_shd(); + break; + case 01440: + cpu_sae(); + break; + case 01500: + cpu_sro(); + break; + case 01540: + cpu_bcl(); + break; + case 01600: + cpu_bse(); + break; + case 01640: + cpu_bco(); + break; + case 01740: + cpu_dsc(); + break; + case 02000: case 02040: case 02100: case 02140: case 02200: case 02240: case 02300: case 02340: + case 02400: case 02440: case 02500: case 02540: case 02600: case 02640: case 02700: case 02740: + case 03000: case 03040: case 03100: case 03140: case 03200: case 03240: case 03300: case 03340: + case 03400: case 03440: case 03500: case 03540: case 03600: case 03640: case 03700: case 03740: + cpu_add(); + break; + case 04000: case 04040: case 04100: case 04140: case 04200: case 04240: case 04300: case 04340: + case 04400: case 04440: case 04500: case 04540: case 04600: case 04640: case 04700: case 04740: + case 05000: case 05040: case 05100: case 05140: case 05200: case 05240: case 05300: case 05340: + case 05400: case 05440: case 05500: case 05540: case 05600: case 05640: case 05700: case 05740: + cpu_stc(); + break; + case 06000: case 06040: case 06100: case 06140: case 06200: case 06240: case 06300: case 06340: + case 06400: case 06440: case 06500: case 06540: case 06600: case 06640: case 06700: case 06740: + case 07000: case 07040: case 07100: case 07140: case 07200: case 07240: case 07300: case 07340: + case 07400: case 07440: case 07500: case 07540: case 07600: case 07640: case 07700: case 07740: + cpu_jmp(); + break; + } + + if (history) { + history[history_i].B = B; + history[history_i].A = A; + history[history_i].L = L; + history_i = (history_i + 1) % history_m; + if (history_n < history_m) + history_n++; + } + + /* Check for interrupts. */ + if ((C & 06000) != 06000 && C != 00010 && interrupt && interrupt_enable) { + interrupt = interrupt_enable = 0; + memaddr(021); + memrd(); + C = B; + cpu_jmp(); + } +} + +t_stat sim_instr(void) +{ + t_stat stat; + + if ((stat = build_dev_tab()) != SCPE_OK) + return stat; + + stop_reason = 0; + paused = 0; + + for (;;) { + AIO_CHECK_EVENT; + if (sim_interval <= 0) { + if ((stat = sim_process_event()) != SCPE_OK) + return stat; + } + + if (sim_brk_summ && sim_brk_test(P, SWMASK('E'))) + return STOP_IBKPT; + +#if 0 + if (paused) + sim_debug(DBG_CPU, &cpu_dev, "Paused\n"); +#endif + if (paused) + sim_interval--; + else + cpu_insn(); + + if (sim_step != 0) { + if (--sim_step == 0) + return SCPE_STEP; + } + + if (stop_reason) + return stop_reason; + } + + 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 >= MEMSIZE) + 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 >= MEMSIZE) + return SCPE_NXM; + M[ea] = val & WMASK; + 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, "P___ C___ S___ B___ A___ L\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++) { + if (stop_cpu) { /* Control-C (SIGINT) */ + stop_cpu = FALSE; + break; /* abandon remaining output */ + } + fprintf (st, "%04o %04o %04o %04o %04o %d ", + history[j].P, + history[j].C, + history[j].S, + history[j].B, + history[j].A, + history[j].L); + insn = history[j].C; + fprint_sym (st, history[j].P, &insn, NULL, SWMASK ('M')); + fputc ('\n', st); + j = (j + 1) % history_m; + } + + return SCPE_OK; +} + +static t_bool pc_is_a_subroutine_call(t_addr **ret_addrs) +{ + return FALSE; +} + +static t_stat +cpu_reset(DEVICE *dptr) +{ + sim_brk_types = SWMASK('E') | SWMASK('R') | SWMASK('W'); + sim_brk_dflt = SWMASK('E'); + sim_vm_is_subroutine_call = &pc_is_a_subroutine_call; + return SCPE_OK; +} diff --git a/linc/linc_crt.c b/linc/linc_crt.c new file mode 100644 index 00000000..e3b1cfef --- /dev/null +++ b/linc/linc_crt.c @@ -0,0 +1,122 @@ +/* linc_crt.c: LINC CRT display + + Copyright (c) 2025, 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 "linc_defs.h" +#include "sim_video.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 }, + { "VVID", SIM_VID_DBG_VIDEO }, + { 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, 12, 1, 8, 12, + NULL, NULL, &crt_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DEBUG | DEV_DISPLAY, 0, crt_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static t_stat +crt_svc(UNIT *uptr) +{ +#ifdef USE_DISPLAY + display_age (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 USE_DISPLAY + if ((dptr->flags & DEV_DIS) != 0 || (sim_switches & SWMASK('P')) != 0) { + display_close (dptr); + sim_cancel (&crt_unit); + } else { + display_reset (); + display_init (DIS_LINC, 1, dptr); + vid_register_quit_callback (&crt_quit_callback); + sim_activate_abs (&crt_unit, 0); + } +#endif + return SCPE_OK; +} + +void +crt_point (uint16 x, uint16 y) +{ + sim_debug(DBG, &crt_dev, "Point %o,%o\n", x, y); +#ifdef USE_DISPLAY + if (crt_dev.flags & DEV_DIS) + return; + display_point(x, y, DISPLAY_INT_MAX, 0); +#endif +} + +/* Hook called when CRT goes idle. */ +void +crt_idle (void) +{ +} + +/* Display high voltage sync. */ +void +crt_hvc (void) +{ +} diff --git a/linc/linc_defs.h b/linc/linc_defs.h new file mode 100644 index 00000000..c92eac99 --- /dev/null +++ b/linc/linc_defs.h @@ -0,0 +1,58 @@ +/* linc_defs.h: LINC simulator definitions + + Copyright (c) 2025, 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. + + 17-Sept-25 LB New simulator. +*/ + +#ifndef LINC_DEFS_H_ +#define LINC_DEFS_H_ 0 + +#include "sim_defs.h" + +#define STOP_HALT 1 +#define STOP_IBKPT 2 +#define STOP_RBKPT 3 +#define STOP_WBKPT 3 + +#define MEMSIZE 2048 + +extern REG cpu_reg[]; +extern uint16 M[]; + +extern DEVICE cpu_dev; +extern DEVICE crt_dev; +extern DEVICE dpy_dev; +extern DEVICE kbd_dev; +extern DEVICE tape_dev; +extern DEVICE tty_dev; + +extern t_bool build_dev_tab(void); +extern void dpy_dis(uint16 h, uint16 x, uint16 y); +extern void crt_point (uint16 x, uint16 y); +extern uint16 kbd_key(uint16 wait); +extern int kbd_struck(void); +extern void tape_op(void); + +#endif /* LINC_DEFS_H_ */ diff --git a/linc/linc_dpy.c b/linc/linc_dpy.c new file mode 100644 index 00000000..861873c8 --- /dev/null +++ b/linc/linc_dpy.c @@ -0,0 +1,40 @@ +#include "linc_defs.h" + +/* Debug */ +#define DBG 0001 + +static DEBTAB dpy_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +DEVICE dpy_dev = { + "DPY", NULL, NULL, NULL, + 0, 8, 12, 1, 8, 12, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, DEV_DEBUG, 0, dpy_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +void dpy_dis(uint16 h, uint16 x, uint16 y) +{ + sim_debug(DBG, &dpy_dev, "DIS %u;%03o, A=%03o\n", h, x, y); + /* Y coordinate +0 and -0 both refer to the same vertical position. */ + if (y < 256) + y += 255; + else + y -= 256; + crt_point(x, y); +} + +/* 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) +{ +} diff --git a/linc/linc_kbd.c b/linc/linc_kbd.c new file mode 100644 index 00000000..8a868400 --- /dev/null +++ b/linc/linc_kbd.c @@ -0,0 +1,427 @@ +#include "linc_defs.h" +#include "sim_video.h" + +/* Debug */ +#define DBG 0001 + +static t_stat kbd_svc(UNIT *uptr); +static t_stat kbd_reset(DEVICE *dptr); + +static UNIT kbd_unit = { + UDATA(&kbd_svc, UNIT_IDLE, 0) +}; + +static DEBTAB kbd_deb[] = { + { "DBG", DBG }, + { NULL, 0 } +}; + +DEVICE kbd_dev = { + "KBD", &kbd_unit, NULL, NULL, + 1, 8, 12, 1, 8, 12, + NULL, NULL, &kbd_reset, + NULL, NULL, NULL, NULL, DEV_DEBUG, 0, kbd_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +/* +CASE 0 1 2 3 4 5 6 7 8 9 DEL + 23 00 01 02 03 04 05 06 07 10 11 13 + + Q W E R T Y U I O P i= + 44 52 30 45 47 54 50 34 42 43 15 + + A S D F G H J K L +. -, + 24 46 27 31 32 33 35 36 37 10 17 + + #[ Z X C V B N M pu |⊟ META/EOL + 22 55 53 26 51 25 41 40 16 21 12 + + SPACE + 14 +*/ + +static int kbd_pressed = 0; +static uint16 kbd_code; + +void kbd_translate(int ch) +{ + switch (ch) { + case '0': + kbd_code = 000; + break; + case '1': + kbd_code = 001; + break; + case '2': + kbd_code = 002; + break; + case '3': + kbd_code = 003; + break; + case '4': + kbd_code = 004; + break; + case '5': + kbd_code = 005; + break; + case '6': + kbd_code = 006; + break; + case '7': + kbd_code = 007; + break; + case '8': + kbd_code = 010; + break; + case '9': + kbd_code = 011; + break; + case '\r': case '\n': + kbd_code = 012; + break; + case '\b': case 0177: + kbd_code = 013; + break; + case ' ': + kbd_code = 014; + break; + case '=': case 'i': + kbd_code = 015; + break; + case 'p': case 'u': + kbd_code = 016; + break; + case ',': case '-': + kbd_code = 017; + break; + case '.': case '+': + kbd_code = 020; + break; +/*case '⊟':*/ case '|': + kbd_code = 021; + break; + case '[': case '#': + kbd_code = 022; + break; + /*case CASE:*/ + kbd_code = 023; + break; + case 'A': case 'a': + kbd_code = 024; + break; + case 'B': case 'b': + kbd_code = 025; + break; + case 'C': case 'c': + kbd_code = 026; + break; + case 'D': case 'd': + kbd_code = 027; + break; + case 'E': case 'e': + kbd_code = 030; + break; + case 'F': case 'f': + kbd_code = 031; + break; + case 'G': case 'g': + kbd_code = 032; + break; + case 'H': case 'h': + kbd_code = 033; + break; + case 'I': /*case 'i':*/ + kbd_code = 034; + break; + case 'J': case 'j': + kbd_code = 035; + break; + case 'K': case 'k': + kbd_code = 036; + break; + case 'L': case 'l': + kbd_code = 037; + break; + case 'M': case 'm': + kbd_code = 040; + break; + case 'N': case 'n': + kbd_code = 041; + break; + case 'O': case 'o': + kbd_code = 042; + break; + case 'P': /*case 'p':*/ + kbd_code = 043; + break; + case 'Q': case 'q': + kbd_code = 044; + break; + case 'R': case 'r': + kbd_code = 045; + break; + case 'S': case 's': + kbd_code = 046; + break; + case 'T': case 't': + kbd_code = 047; + break; + case 'U': /*case 'u':*/ + kbd_code = 050; + break; + case 'V': case 'v': + kbd_code = 051; + break; + case 'W': case 'w': + kbd_code = 052; + break; + case 'X': case 'x': + kbd_code = 053; + break; + case 'Y': case 'y': + kbd_code = 054; + break; + case 'Z': case 'z': + kbd_code = 055; + break; + default: + return; + } + sim_debug(DBG, &kbd_dev, "Key struck %c -> %02o\n", ch, kbd_code); + kbd_pressed = 1; +} + +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) + ; + else + kbd_translate(ch & 0177); + return SCPE_OK; +} + +int kbd_struck(void) +{ + if (kbd_pressed) + sim_debug(DBG, &kbd_dev, "KST\n"); + return kbd_pressed; +} + +uint16 kbd_key(uint16 wait) +{ + if (kbd_pressed) { + sim_debug(DBG, &kbd_dev, "KEY %02o\n", kbd_code); + sim_activate_abs(&kbd_unit, 1); + kbd_pressed = 0; + return kbd_code; + } else { + sim_debug(DBG, &kbd_dev, "KEY paused\n"); + } + return 0; +} + +static void kbd_convert(uint32 key) +{ + switch (key) { + case SIM_KEY_0: /* 0 Q */ + case SIM_KEY_BACKQUOTE: + kbd_code = 000; + break; + case SIM_KEY_1: /* 1 R */ + kbd_code = 001; + break; + case SIM_KEY_2: /* 2 S */ + kbd_code = 002; + break; + case SIM_KEY_3: /* 3 T */ + kbd_code = 003; + break; + case SIM_KEY_4: /* 4 U */ + kbd_code = 004; + break; + case SIM_KEY_5: /* 5 V */ + kbd_code = 005; + break; + case SIM_KEY_6: /* 6 W */ + kbd_code = 006; + break; + case SIM_KEY_7: /* 7 X */ + kbd_code = 007; + break; + case SIM_KEY_8: /* 8 Y */ + kbd_code = 010; + break; + case SIM_KEY_9: /* 9 Z */ + kbd_code = 011; + break; + case SIM_KEY_ENTER: + kbd_code = 012; + break; + case SIM_KEY_BACKSPACE: + case SIM_KEY_DELETE: + kbd_code = 013; + break; + case SIM_KEY_SPACE: /* Space ? */ + case SIM_KEY_SLASH: + kbd_code = 014; + break; + case SIM_KEY_EQUALS: /* i = */ + kbd_code = 015; + break; + case SIM_KEY_F1: /* p u */ + kbd_code = 016; + break; + case SIM_KEY_MINUS: /* - , */ + case SIM_KEY_COMMA: + kbd_code = 017; + break; + case SIM_KEY_PERIOD: /* + . */ + kbd_code = 020; + break; + case SIM_KEY_BACKSLASH: /* | ⊟ */ + kbd_code = 021; + break; + case SIM_KEY_LEFT_BRACKET: /* # [ */ + case SIM_KEY_LEFT_BACKSLASH: + kbd_code = 022; + break; + case SIM_KEY_SHIFT_L: /* CASE _ */ + case SIM_KEY_SHIFT_R: + kbd_code = 023; + break; + case SIM_KEY_A: /* A " */ + case SIM_KEY_SINGLE_QUOTE: + kbd_code = 024; + break; + case SIM_KEY_B: /* B „ */ + kbd_code = 025; + break; + case SIM_KEY_C: /* C < */ + kbd_code = 026; + break; + case SIM_KEY_D: /* D > */ + kbd_code = 027; + break; + case SIM_KEY_E: /* E ] */ + case SIM_KEY_RIGHT_BRACKET: + kbd_code = 030; + break; + case SIM_KEY_F: /* F ˣ */ + kbd_code = 031; + break; + case SIM_KEY_G: /* G : */ + case SIM_KEY_SEMICOLON: + kbd_code = 032; + break; + case SIM_KEY_H: /* H */ + kbd_code = 033; + break; + case SIM_KEY_I: /* I */ + kbd_code = 034; + break; + case SIM_KEY_J: /* J */ + kbd_code = 035; + break; + case SIM_KEY_K: /* K */ + kbd_code = 036; + break; + case SIM_KEY_L: /* L */ + kbd_code = 037; + break; + case SIM_KEY_M: /* M */ + kbd_code = 040; + break; + case SIM_KEY_N: /* N */ + kbd_code = 041; + break; + case SIM_KEY_O: /* O */ + kbd_code = 042; + break; + case SIM_KEY_P: /* P */ + kbd_code = 043; + break; + case SIM_KEY_Q: /* Q */ + kbd_code = 044; + break; + case SIM_KEY_R: /* R */ + kbd_code = 045; + break; + case SIM_KEY_S: /* S */ + kbd_code = 046; + break; + case SIM_KEY_T: /* T */ + kbd_code = 047; + break; + case SIM_KEY_U: /* U */ + kbd_code = 050; + break; + case SIM_KEY_V: /* V */ + kbd_code = 051; + break; + case SIM_KEY_W: /* W */ + kbd_code = 052; + break; + case SIM_KEY_X: /* X */ + kbd_code = 053; + break; + case SIM_KEY_Y: /* Y */ + kbd_code = 054; + break; + case SIM_KEY_Z: /* Z */ + kbd_code = 055; + break; + case SIM_KEY_ALT_L: /* META? */ + case SIM_KEY_ALT_R: + kbd_code = 056; + break; + // → 57 + // ? 60 + // = 61 + // u 62 + // , 63 + // . 64 + // ⊟ 65 + // [ 66 + // _ 67 + // " 70 + // „ 71 + // < 72 + // > 73 + // ] 74 + // ˣ 75 + // : 76 + // ʸ 77 + default: + return; + } + sim_debug(DBG, &kbd_dev, "Key struck %s -> %02o\n", + vid_key_name(key), kbd_code); + kbd_pressed = 1; +} + +static int +kbd_event(SIM_KEY_EVENT *ev) +{ + if (ev->state == SIM_KEYPRESS_DOWN) + kbd_convert(ev->key); + return 0; +} + +static t_stat +kbd_reset(DEVICE *dptr) +{ +#ifdef USE_DISPLAY + vid_display_kb_event_process = kbd_event; +#endif + sim_activate_abs(&kbd_unit, 0); + + return SCPE_OK; +} diff --git a/linc/linc_sys.c b/linc/linc_sys.c new file mode 100644 index 00000000..5bda3e9b --- /dev/null +++ b/linc/linc_sys.c @@ -0,0 +1,593 @@ +/* linc_sys.c: LINC simulator interface + + Copyright (c) 2025, 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. + + 17-Sept-25 LB New simulator. +*/ + +#include "linc_defs.h" + +int32 sim_emax = 1; +char sim_name[] = "LINC"; + +uint16 M[MEMSIZE]; +REG *sim_PC = &cpu_reg[0]; + +DEVICE *sim_devices[] = { + &cpu_dev, + &crt_dev, + &dpy_dev, + &kbd_dev, + &tape_dev, + &tty_dev, + NULL +}; + +const char *sim_stop_messages[SCPE_BASE] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Read Breakpoint", + "Write Breakpoint" +}; + +static t_stat +get_binary_word(FILE *fileref, uint16 *x) +{ + uint16 y; + int c = Fgetc(fileref); + if (c == EOF) + return SCPE_EOF; + y = c & 0xFF; + c = Fgetc(fileref); + if (c == EOF) + return SCPE_IOERR; + if (c & 0xF0) + return SCPE_FMT; + *x = y | (c << 8); + return SCPE_OK; +} + +static t_stat +get_octal_word(FILE *fileref, uint16 *x) +{ + uint16 y, i; + int c; + for (i = 0;;) { + c = Fgetc(fileref); + if (c == EOF) + return SCPE_EOF; + if (c >= '0' && c <= '9') { + y = c - '0'; + i++; + break; + } + } + for (; i < 4;) { + c = Fgetc(fileref); + if (c == EOF) + return SCPE_IOERR; + if (c < '0' || c > '9') + break; + y <<= 3; + y |= c - '0'; + i++; + } + + *x = y; + return SCPE_OK; +} + +t_stat +sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ + t_stat (*get_word)(FILE *fileref, uint16 *x) = get_binary_word; + t_addr addr, length = MEMSIZE, start = 0, end; + long offset = 0; + t_stat stat; + + if (sim_switches & SWMASK('O')) + get_word = get_octal_word; + + while (cptr != NULL && *cptr != 0) { + if (strncasecmp(cptr, "start=", 6) == 0) + start = (t_addr)get_uint(cptr + 6, 8, ~0, &stat); + else if (strncasecmp(cptr, "offset=", 7) == 0) + offset = 2 * (long)get_uint(cptr + 7, 8, ~0, &stat); + else if (strncasecmp(cptr, "block=", 6) == 0) + offset = 512 * (long)get_uint(cptr + 6, 8, ~0, &stat); + else if (strncasecmp(cptr, "length=", 7) == 0) + length = (t_addr)get_uint(cptr + 7, 8, ~0, &stat); + else + return SCPE_ARG; + cptr = strchr(cptr, ' '); + if (cptr == NULL) + break; + while (*cptr == ' ') + cptr++; + } + + end = start + length; + if (end > MEMSIZE) + end = MEMSIZE; + + sim_fseek(fileref, offset, SEEK_SET); + + for (addr = start; addr < end; addr++) { + uint16 x; + t_stat stat = get_word(fileref, &x); + if (stat == SCPE_EOF) + return SCPE_OK; + if (stat != SCPE_OK) + return stat; + M[addr] = x; + } + + return SCPE_OK; +} + +t_bool build_dev_tab(void) +{ + DEVICE *dev; + int i; + + for (i = 0; (dev = sim_devices[i]) != NULL; i++) { + ; + } + + return SCPE_OK; +} + +static t_stat fprint_next(FILE *of, uint16 addr) +{ + fprintf(of, "\n"); + fprint_val(of, ++addr & 01777, 8, 10, PV_LEFT); + fprintf(of, ":\t%04o", M[addr]); + return -1; +} + +static void fprint_misc(FILE *of, uint16 insn) +{ + switch (insn) { + case 00000: + fprintf(of, "HLT"); + break; + case 00005: + fprintf(of, "ZTA"); + break; + case 00010: + fprintf(of, "ENI"); + break; + case 00011: + fprintf(of, "CLR"); + break; + case 00013: + fprintf(of, "%04o", insn); + break; + case 00014: + fprintf(of, "ATR"); + break; + case 00015: + fprintf(of, "RTA"); + break; + case 00016: + fprintf(of, "NOP"); + break; + case 00017: + fprintf(of, "COM"); + break; + default: + fprintf(of, "%04o", insn); + break; + } +} + +static t_stat fprint_index(FILE *of, uint16 insn, uint16 addr) +{ + if (insn & 020) + fprintf(of, " i"); + if (insn & 017) + fprintf(of, " %o", insn & 017); + else + return fprint_next(of, addr); + return SCPE_OK; +} + +static void fprint_set(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "SET"); + fprint_index(of, insn, addr); + fprint_next(of, addr); +} + +static void fprint_sam(FILE *of, uint16 insn) +{ + fprintf(of, "SAM "); +} + +static t_stat fprint_dis(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "DIS"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_xsk(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "XSK"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_rol(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "ROL"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_ror(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "ROR"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_scr(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "SCR"); + return fprint_index(of, insn, addr); +} + +static void fprint_skip(FILE *of, uint16 insn) +{ + char beta[3]; + switch (insn & 057) { + case 000: case 001: case 002: case 003: case 004: case 005: case 006: case 007: + case 010: case 011: case 012: case 013: + fprintf(of, "SXL"); + snprintf(beta, sizeof beta, "%o", insn & 017); + break; + case 015: + fprintf(of, "KST"); + break; + case 040: case 041: case 042: case 043: case 044: case 045: + fprintf(of, "SNS "); + snprintf(beta, sizeof beta, "%o", insn & 7); + break; + case 046: + fprintf(of, "PIN"); + break; + case 050: + fprintf(of, "AZE"); + break; + case 051: + fprintf(of, "APO"); + break; + case 052: + fprintf(of, "LZE"); + break; + case 053: + fprintf(of, "IBZ"); + break; + case 054: + fprintf(of, "OVF"); + break; + case 055: + fprintf(of, "ZZZ"); + break; + default: + fprintf(of, "%04o", insn); + return; + } + if (insn & 020) + fprintf(of, " i" ); + fprintf(of, " %s", beta); +} + +static void fprint_opr(FILE *of, uint16 insn) +{ + switch (insn & 07757) { + case 0500: case 0501: case 0502: case 0503: case 0504: case 0505: case 0506: case 0507: + case 0510: case 0511: case 0512: case 0513: + break; + case 0535: + case 0515: + fprintf(of, "KBD "); + break; + case 0516: + fprintf(of, "RSW "); + break; + case 0517: + fprintf(of, "LSW "); + break; + default: + fprintf(of, "%04o", insn); + break; + } + if (insn & 020) + fprintf(of, "i" ); +} + +static void fprint_lmb(FILE *of, uint16 insn) +{ + fprintf(of, "LMB "); +} + +static void fprint_umb(FILE *of, uint16 insn) +{ + fprintf(of, "UMB "); +} + +static void fprint_tape(FILE *of, uint16 insn, uint16 addr) +{ + switch (insn & 0707) { + case 0700: + fprintf(of, "RDC"); + break; + case 0701: + fprintf(of, "RCG"); + break; + case 0702: + fprintf(of, "RDE"); + break; + case 0703: + fprintf(of, "MTB"); + break; + case 0704: + fprintf(of, "WRC"); + break; + case 0705: + fprintf(of, "WCG"); + break; + case 0706: + fprintf(of, "WRI"); + break; + case 0707: + fprintf(of, "CHK"); + break; + } + if (insn & 020) + fprintf(of, " i"); + if (insn & 010) + fprintf(of, " u"); + fprint_next(of, addr); +} + +static t_stat fprint_lda(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "LDA"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_sta(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "STA"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_ada(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "ADA"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_adm(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "ADM"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_lam(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "LAM"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_mul(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "MUL"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_ldh(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "LDH"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_sth(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "STH"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_shd(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "SHD"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_sae(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "SAE"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_sro(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "SRO"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_bcl(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "BCL"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_bse(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "BSE"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_bco(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "BCO"); + return fprint_index(of, insn, addr); +} + +static t_stat fprint_dsc(FILE *of, uint16 insn, uint16 addr) +{ + fprintf(of, "DSC"); + return fprint_index(of, insn, addr); +} + +static void fprint_add(FILE *of, uint16 insn) +{ + fprintf(of, "ADD %04o", insn & 01777); +} + +static void fprint_stc(FILE *of, uint16 insn) +{ + fprintf(of, "STC %04o", insn & 01777); +} + +static void fprint_jmp(FILE *of, uint16 insn) +{ + fprintf(of, "JMP %04o", insn & 01777); +} + +t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ + t_stat stat; + + if ((sw & SWMASK ('M')) == 0) + return SCPE_ARG; + + if ((stat = build_dev_tab()) != SCPE_OK) + return stat; + + switch (*val & 07740) { + case 00000: + fprint_misc(of, *val); + break; + case 00040: + fprint_set(of, *val, addr); + return -1; + case 00100: + fprint_sam(of, *val); + break; + case 00140: + fprint_dis(of, *val, addr); + break; + case 00200: + fprint_xsk(of, *val, addr); + break; + case 00240: + fprint_rol(of, *val, addr); + break; + case 00300: + fprint_ror(of, *val, addr); + break; + case 00340: + fprint_scr(of, *val, addr); + break; + case 00400: + case 00440: + fprint_skip(of, *val); + break; + case 00500: + case 00540: + fprint_opr(of, *val); + break; + case 00600: + fprint_lmb(of, *val); + break; + case 00640: + fprint_umb(of, *val); + break; + case 00700: + case 00740: + fprint_tape(of, *val, addr); + break; + case 01000: + return fprint_lda(of, *val, addr); + case 01040: + return fprint_sta(of, *val, addr); + case 01100: + return fprint_ada(of, *val, addr); + case 01140: + return fprint_adm(of, *val, addr); + case 01200: + return fprint_lam(of, *val, addr); + case 01240: + return fprint_mul(of, *val, addr); + case 01300: + return fprint_ldh(of, *val, addr); + case 01340: + return fprint_sth(of, *val, addr); + case 01400: + return fprint_shd(of, *val, addr); + case 01440: + return fprint_sae(of, *val, addr); + case 01500: + return fprint_sro(of, *val, addr); + case 01540: + return fprint_bcl(of, *val, addr); + case 01600: + return fprint_bse(of, *val, addr); + case 01640: + return fprint_bco(of, *val, addr); + case 01740: + return fprint_dsc(of, *val, addr); + case 02000: case 02040: case 02100: case 02140: case 02200: case 02240: case 02300: case 02340: + case 02400: case 02440: case 02500: case 02540: case 02600: case 02640: case 02700: case 02740: + case 03000: case 03040: case 03100: case 03140: case 03200: case 03240: case 03300: case 03340: + case 03400: case 03440: case 03500: case 03540: case 03600: case 03640: case 03700: case 03740: + fprint_add(of, *val); + break; + case 04000: case 04040: case 04100: case 04140: case 04200: case 04240: case 04300: case 04340: + case 04400: case 04440: case 04500: case 04540: case 04600: case 04640: case 04700: case 04740: + case 05000: case 05040: case 05100: case 05140: case 05200: case 05240: case 05300: case 05340: + case 05400: case 05440: case 05500: case 05540: case 05600: case 05640: case 05700: case 05740: + fprint_stc(of, *val); + break; + case 06000: case 06040: case 06100: case 06140: case 06200: case 06240: case 06300: case 06340: + case 06400: case 06440: case 06500: case 06540: case 06600: case 06640: case 06700: case 06740: + case 07000: case 07040: case 07100: case 07140: case 07200: case 07240: case 07300: case 07340: + case 07400: case 07440: case 07500: case 07540: case 07600: case 07640: case 07700: case 07740: + fprint_jmp(of, *val); + break; + } + + return SCPE_OK; +} + +t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, + t_value *val, int32 sw) +{ + t_stat stat; + *val = get_uint(cptr, 8, ~0, &stat); + if (stat != SCPE_OK) + return stat; + return 0; +} diff --git a/linc/linc_tape.c b/linc/linc_tape.c new file mode 100644 index 00000000..4692eb15 --- /dev/null +++ b/linc/linc_tape.c @@ -0,0 +1,439 @@ +#include "linc_defs.h" + +#define POS u3 +#define SPEED u4 +#define ACC u5 +#define OFFSET u6 + +#define C (*(uint16 *)cpu_reg[1].loc) +#define A (*(uint16 *)cpu_reg[2].loc) +#define S (*(uint16 *)cpu_reg[6].loc) +#define B (*(uint16 *)cpu_reg[7].loc) +#define paused (*(int *)cpu_reg[11].loc) +#define IBZ (*(int *)cpu_reg[12].loc) + +#define ACC_START 3 +#define ACC_REVERSE 6 +#define ACC_STOP 1 +#define MAX_SPEED (ACC_START * 625) /* 0.1s / 160µs */ +#define IBZ_WORDS 5 +#define DATA_WORDS 256 +#define OTHER_WORDS 7 +#define BLOCK_WORDS (IBZ_WORDS + DATA_WORDS + OTHER_WORDS) +#define START_POS (ACC_START * (625 + (625 * 625))/2) +#define MAX_BLOCKS 512 +#define MAX_POS ((BLOCK_WORDS * MAX_BLOCKS + IBZ_WORDS) * MAX_SPEED) + +#define RDC 0 /* read tape and check */ +#define RCG 1 /* read tape group */ +#define RDE 2 /* read tape */ +#define MTB 3 /* move toward block */ +#define WRC 4 /* write tape and check */ +#define WCG 5 /* write tape group */ +#define WRI 6 /* write tape */ +#define CHK 7 /* check tape */ + +#define DBG 0001 +#define DBG_SEEK 0002 +#define DBG_READ 0004 +#define DBG_WRITE 0010 +#define DBG_POS 0020 + +static uint16 GROUP; +static int16 CURRENT_BLOCK; +static int16 WANTED_BLOCK; + +static t_stat tape_svc(UNIT *uptr); +static t_stat tape_reset(DEVICE *dptr); +static t_stat tape_boot(int32 u, DEVICE *dptr); +static t_stat tape_attach(UNIT *uptr, CONST char *cptr); +static t_stat tape_detach(UNIT *uptr); + +#define UNIT_FLAGS (UNIT_IDLE|UNIT_FIX|UNIT_ATTABLE|UNIT_DISABLE|UNIT_ROABLE) +#define CAPACITY (MAX_BLOCKS * DATA_WORDS) + +static UNIT tape_unit[] = { + { UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) }, + { UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) }, + { UDATA(&tape_svc, UNIT_DIS, 0) }, + { UDATA(&tape_svc, UNIT_DIS, 0) }, + { UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) }, + { UDATA(&tape_svc, UNIT_FLAGS, CAPACITY) } +}; + +static DEBTAB tape_deb[] = { + { "DBG", DBG }, + { "SEEK", DBG_SEEK }, + { "READ", DBG_READ }, + { "WRITE", DBG_WRITE }, + { "POSITION", DBG_POS }, + { NULL, 0 } +}; + +DEVICE tape_dev = { + "TAPE", tape_unit, NULL, NULL, + 6, 8, 12, 1, 8, 12, + NULL, NULL, &tape_reset, + &tape_boot, &tape_attach, &tape_detach, + NULL, DEV_DEBUG, 0, tape_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +void tape_op(void) +{ + uint16 u = (C & 050) >> 3; + UNIT *uptr = &tape_unit[u]; + + if ((uptr->flags & UNIT_ATT) == 0) + return; + + if (uptr->SPEED < 0) { + if ((C & 7) != MTB) { + sim_debug(DBG_SEEK, &tape_dev, "Reverse to forward\n"); + uptr->ACC = ACC_REVERSE; + } + } else if (uptr->POS >= MAX_POS) { + sim_debug(DBG_SEEK, &tape_dev, "End zone; reverse\n"); + uptr->ACC = ACC_REVERSE; + } else if (uptr->SPEED < MAX_SPEED || uptr->ACC < 0) { + sim_debug(DBG_SEEK, &tape_dev, "Speed up\n"); + uptr->ACC = ACC_START; + } + if (!sim_is_active(uptr)) + sim_activate_after(uptr, 1); + paused = 1; + A = 0; + WANTED_BLOCK = B & 0777; + + switch (C & 7) { + case RDC: case RDE: case WRC: case WRI: case CHK: + S = 256 * (B >> 9); + GROUP = 0; + sim_debug(DBG, &tape_dev, "Single tranfer: S=%04o, BN=%03o\n", + S, WANTED_BLOCK); + break; + case RCG: case WCG: + S = 256 * (B & 7); + GROUP = B >> 9; + sim_debug(DBG, &tape_dev, "Group transfer: S=%04o, BN=%03o/%o\n", + S, WANTED_BLOCK, GROUP+1); + break; + case MTB: + sim_debug(DBG, &tape_dev, "Move towards block %03o\n", WANTED_BLOCK); + break; + } +} + +static t_stat tape_seek(UNIT *uptr, t_addr block, t_addr offset) +{ + offset = DATA_WORDS * block + offset; + offset *= 2; + if (sim_fseek(uptr->fileref, offset, SEEK_SET) == -1) + return SCPE_IOERR; + return SCPE_OK; +} + +static uint16 read_word(UNIT *uptr, t_addr block, t_addr offset) +{ + t_stat stat; + uint8 data[2]; + uint16 word; + + stat = tape_seek(uptr, block, offset); + if (stat != SCPE_OK) + ; + if (sim_fread(data, 1, 2, uptr->fileref) != 2) + ; + if (data[1] & 0xF0) + ; + word = data[1]; + word <<= 8; + word |= data[0]; + return word; +} + +static void write_word(UNIT *uptr, t_addr block, t_addr offset, uint16 word) +{ + t_stat stat; + uint8 data[2]; + + stat = tape_seek(uptr, block, offset); + if (stat != SCPE_OK) + ; + data[0] = word & 0xFF; + data[1] = word >> 8; + if (sim_fwrite(data, 1, 2, uptr->fileref) != 2) + ; +} + +/* + IBZ BN G block CS C C G BN IBZ + 5 1 1 256 1 1 1 1 1 5 + --------------------- + 263 + -------------------------- + 268 + + + start - 100 ms + stop - 300 ms + reverse - 100 ms + BN to BN at 60 ips - 43 ms + block length = 43 ms * 60 inch/s = 2.58 inch + + per word - 160 µs + word length = 0.0096 inch + words per inch = 104 + words per second = 6250 + end zone to end zone - 23 s + tape length = 23 * 60 = 1380 inch = 115 feet + end zone length = 5 feet + + */ + +static void tape_done(UNIT *uptr) +{ + sim_debug(DBG, &tape_dev, "Done with block\n"); + + switch (C & 7) { + case RDC: case RCG: case RDE: case CHK: + A = 07777; + break; + case WRI: + A = A ^ 07777; + A++; + A &= 07777; + break; + case MTB: + A = (WANTED_BLOCK + ~CURRENT_BLOCK); + A += A >> 12; + A &= 07777; + break; + } + + switch (C & 7) { + case RDC: + if (A != 07777) { + sim_debug(DBG, &tape_dev, "Check failed; read again\n"); + S &= ~0377; + } else { + sim_debug(DBG, &tape_dev, "Check passed\n"); + paused = 0; + } + break; + case WRC: + sim_debug(DBG, &tape_dev, "Block written, go back and check\n"); + // For now, done. + A = 07777; + paused = 0; + break; + case RCG: case WCG: + if (GROUP == 0) { + sim_debug(DBG, &tape_dev, "Done with group\n"); + paused = 0; + } else { + sim_debug(DBG, &tape_dev, "Blocks left in group: %d\n", GROUP); + GROUP--; + } + WANTED_BLOCK = (WANTED_BLOCK + 1) & 0777; + break; + case RDE: case WRI: + sim_debug(DBG, &tape_dev, "Transfer done\n"); + paused = 0; + break; + case MTB: + sim_debug(DBG, &tape_dev, "Move towards block done, result %04o\n", A); + paused = 0; + break; + case CHK: + sim_debug(DBG, &tape_dev, "Check done\n"); + paused = 0; + break; + } + + if (paused) + ; + else if ((C & 020) == 0) { + sim_debug(DBG_SEEK, &tape_dev, "Instruction done, stop tape\n"); + uptr->ACC = uptr->SPEED > 0 ? -ACC_STOP : ACC_STOP; + } else { + sim_debug(DBG_SEEK, &tape_dev, "Instruction done, keep moving\n"); + } +} + +static void tape_word(UNIT *uptr, uint16 block, uint16 offset) +{ + switch (C & 7) { + case RDC: case RCG: case RDE: case CHK: + B = read_word(uptr, block, offset); + sim_debug(DBG_READ, &tape_dev, + "Read block %03o offset %03o data %04o address %04o\n", + block, offset, B, S); + if ((C & 7) != CHK) + M[S] = B; + break; + case WRC: case WCG: case WRI: + B = M[S]; + sim_debug(DBG_WRITE, &tape_dev, + "Write block %03o offset %03o data %04o address %04o\n", + block, offset, B, S); + write_word(uptr, block, offset, B); + break; + } + S = (S+1) & 03777; + A += B; + A &= 07777; +} + +static t_stat tape_svc(UNIT *uptr) +{ + long pos, block, offset; + + uptr->SPEED += uptr->ACC; + if (uptr->SPEED >= MAX_SPEED) { + uptr->SPEED = MAX_SPEED; + uptr->ACC = 0; + } + else if (uptr->SPEED <= -MAX_SPEED) { + uptr->SPEED = -MAX_SPEED; + uptr->ACC = 0; + } else if (uptr->SPEED == 0 && (uptr->ACC == ACC_STOP || uptr->ACC == -ACC_STOP)) + uptr->ACC = 0; + uptr->POS += uptr->SPEED; + sim_debug(DBG_POS, &tape_dev, "Speed %d, position %d (block %03o)\n", + uptr->SPEED, uptr->POS, uptr->POS / MAX_SPEED / BLOCK_WORDS); + + if (uptr->POS < 0 && uptr->ACC <= 0) { + sim_debug(DBG_SEEK, &tape_dev, "End zone; stop tape\n"); + uptr->ACC = ACC_STOP; + } else if(uptr->POS >= MAX_POS && uptr->ACC >= 0) { + sim_debug(DBG_SEEK, &tape_dev, "End zone; stop tape\n"); + uptr->ACC = -ACC_STOP; + } + + if (uptr->SPEED != 0) + /* The tape takes 160 microseconds between words. This is + approximately 20 memory cycles, 8 microseconds each. */ + sim_activate(uptr, 20); + + pos = uptr->POS / MAX_SPEED; + if (pos < 0) + return SCPE_OK; + + block = pos / BLOCK_WORDS; + offset = pos % BLOCK_WORDS; + if (block >= MAX_BLOCKS) + return SCPE_OK; + + IBZ = offset < IBZ_WORDS; + if (IBZ) + sim_debug(DBG, &tape_dev, "Interblock zone\n"); + + if (uptr->SPEED > -MAX_SPEED && uptr->SPEED < MAX_SPEED) + return SCPE_OK; + + if (!paused) + return SCPE_OK; + + if (uptr->SPEED > 0) { + if (offset == 5) { + /* Forward block number. */ + CURRENT_BLOCK = (uint16)(block + uptr->OFFSET); + sim_debug(DBG_SEEK, &tape_dev, + "Found block number %03o; looking for %03o\n", + CURRENT_BLOCK, WANTED_BLOCK); + if (CURRENT_BLOCK > WANTED_BLOCK) { + sim_debug(DBG_SEEK, &tape_dev, "Reverse to find lower block numbers\n"); + uptr->ACC = -ACC_REVERSE; + } + if ((C & 7) == MTB) + tape_done(uptr); + /* Word 6 is a guard. */ + } else if (offset >= 7 && offset < 263) { + if (CURRENT_BLOCK == WANTED_BLOCK) + tape_word(uptr, (uint16)block, (uint16)(offset - 7)); + } + else if (offset == 263 && CURRENT_BLOCK == WANTED_BLOCK) + /* Checksum here. */ + tape_done(uptr); + } + /* Word 264-265 are "C". */ + /* Word 266 is a guard. */ + else if (offset == 267 && uptr->SPEED < 0) { + /* Reverse block number. */ + CURRENT_BLOCK = (uint16)(block + uptr->OFFSET); + sim_debug(DBG_SEEK, &tape_dev, + "Found reverse block number %03o; looking for %03o\n", + CURRENT_BLOCK, WANTED_BLOCK); + if (CURRENT_BLOCK <= WANTED_BLOCK) { + sim_debug(DBG_SEEK, &tape_dev, "Reverse to find higher block numbers\n"); + uptr->ACC = ACC_REVERSE; + uptr->POS -= MAX_SPEED * BLOCK_WORDS; + } + if ((C & 7) == MTB) + tape_done(uptr); + } + + return SCPE_OK; +} + +static t_stat tape_reset(DEVICE *dptr) +{ + return SCPE_OK; +} + +static t_stat tape_boot(int32 unit_num, DEVICE *dptr) +{ + if (unit_num >= 2 && unit_num <= 3) + return SCPE_ARG; + + //LSW = 0701 + (unit_num << 3); + //RSW = (7 << 9) | 0300; + //P = 020; + return SCPE_OK; +} + +static t_stat tape_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat stat; + t_offset size; + if (uptr - tape_unit >= 2 && uptr - tape_unit <= 3) + return SCPE_ARG; + stat = attach_unit(uptr, cptr); + if (stat != SCPE_OK) + return stat; + size = sim_fsize(uptr->fileref); + if (size == MAX_BLOCKS * DATA_WORDS * 2) + uptr->OFFSET = 0; /* Plain image. */ + else if ((size % (2 * DATA_WORDS)) == 6) { + /* Extended image with additional meta data. */ + uint16 metadata = (uint16)(size / (2 * DATA_WORDS)); + uint16 block_size = read_word(uptr, metadata, 0); + int16 forward_offset = (int16)read_word(uptr, metadata, 1); + int16 reverse_offset = (int16)read_word(uptr, metadata, 2); + sim_debug(DBG, &tape_dev, + "Extended image with block size %o, block offset %d/%d\r\n", + block_size, forward_offset, reverse_offset); + if (block_size != DATA_WORDS) + return SCPE_FMT; + if (forward_offset != reverse_offset) + return SCPE_FMT; + uptr->OFFSET = forward_offset; + } else + return SCPE_FMT; + + uptr->POS = -2 * START_POS; + uptr->SPEED = 0; + return SCPE_OK; +} + +static t_stat tape_detach(UNIT *uptr) +{ + if (uptr - tape_unit >= 2 && uptr - tape_unit <= 3) + return SCPE_ARG; + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_OK; + if (sim_is_active(uptr)) + sim_cancel(uptr); + return detach_unit(uptr); +} diff --git a/linc/linc_tty.c b/linc/linc_tty.c new file mode 100644 index 00000000..8979d2ae --- /dev/null +++ b/linc/linc_tty.c @@ -0,0 +1,6 @@ +#include "linc_defs.h" + +DEVICE tty_dev = { + "TTY", NULL, NULL, NULL, + 0, 8, 12, 1, 8, 12, +}; diff --git a/makefile b/makefile index 67620e02..6d6b0051 100644 --- a/makefile +++ b/makefile @@ -197,6 +197,10 @@ ifneq (3,${SIM_MAJOR}) ifneq (,$(findstring imlac,${MAKECMDGOALS})) VIDEO_USEFUL = true endif + # building the LINC needs video support + ifneq (,$(findstring linc,${MAKECMDGOALS})) + VIDEO_USEFUL = true + endif # building the TT2500 needs video support ifneq (,$(findstring tt2500,${MAKECMDGOALS})) VIDEO_USEFUL = true @@ -2013,7 +2017,15 @@ 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 ${IMLACD}/imlac_bel.c \ ${DISPLAYL} -IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT} +IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT} ${AIO_CCDEFS} + + +LINCD = ${SIMHD}/linc +LINC = ${LINCD}/linc_sys.c ${LINCD}/linc_cpu.c \ + ${LINCD}/linc_crt.c ${LINCD}/linc_dpy.c ${LINCD}/linc_kbd.c \ + ${LINCD}/linc_tape.c ${LINCD}/linc_tty.c \ + ${DISPLAYL} +LINC_OPT = -I ${LINCD} ${DISPLAY_OPT} ${AIO_CCDEFS} TT2500D = ${SIMHD}/tt2500 @@ -2021,7 +2033,7 @@ 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} -TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT} +TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT} ${AIO_CCDEFS} PDP8D = ${SIMHD}/PDP8 @@ -2514,7 +2526,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 intel-mds \ scelbi 3b2 3b2-700 i701 i704 i7010 i7070 i7080 i7090 \ sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp10-ks pdp6 i650 \ - imlac tt2500 sel32 + imlac linc tt2500 sel32 all : ${ALL} @@ -2592,6 +2604,12 @@ $(BIN)imlac$(EXE) : ${IMLAC} ${SIM} $(MAKEIT) OPTS="$(IMLAC_OPT)" +linc : $(BIN)linc$(EXE) + +$(BIN)linc$(EXE) : ${LINC} ${SIM} + $(MAKEIT) OPTS="$(LINC_OPT)" + + tt2500 : $(BIN)tt2500$(EXE) $(BIN)tt2500$(EXE) : ${TT2500} ${SIM}