diff --git a/linc/CMakeLists.txt b/linc/CMakeLists.txt deleted file mode 100644 index bce48ea8..00000000 --- a/linc/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -## 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/README.md b/linc/README.md new file mode 100644 index 00000000..57687814 --- /dev/null +++ b/linc/README.md @@ -0,0 +1,135 @@ +# LINC emulator + +This is an emulator for the classic LINC from 1965. + +### Devices + +- CPU - LINC processor. If throttled to 125k, approximately original speed. +- CRT - CRT display. It can be disabled to run headless. +- DPY - point-plotting display controller. +- KBD - keyboard. Input comes from typing in the CRT window. +- SAM - sampled analog inputs. +- TAPE - four tape drives. +- TTY - teletype. + +### LOAD + +Software can be loaded into memory using the `LOAD` command. It will +normally read a file with 16-bit little endian words; the most +significant four bits of each word should be zero. If the `-O` switch +is supplied, the input is a text file with octal numbers separated by +whitespace. + +To start reading from some particular position in the input file, add +`OFFSET=` after the file name and supply a number in octal. + +To specify where in the memory to put the data, add `START=` after the +file name; the default is 0. To specify how many words to load, use +`LENGTH=`; the default is to write until the end of memory. + +A binary input file can be a tape image. A plain image is 512 blocks +of 256 words each, totalling 262144 bytes. If the `-E` switch is +used, there is a check for the extended image format that can have +empty guard blocks at the beginning and the end. The last three words +in the extended format specify the block size, first forward block +number, and first reverse block number. + +To state which tape block to start reading, add `BLOCK=` after the +file name and supply an octal number in the range 0-777. + +### DO + +The SIMH `DO` command has been modified. With arguments, it will +execute a script file as normal. Without argument, it acts like the +DO button on the LINC control panel. This executes one instruction +from the left switches, with the right switches providing a second +word if needed. + +### Tape drives + +To mount a tape image *file* on drive *n*, type `ATTACH TAPE +`. The plain or extended image format will be detected +automatically. Tape drives are numbered 0, 1, 4, and 5. + +The `BOOT TAPEn` command will act like entering a tape read command in +the switches and starting the computer. The default is to read eight +blocks starting at 300, and start from location 20. This is the +conventional way to run LAP6. You can also add `RDC=` or `RCG=` to +boot some particular blocks. `START=` can be used to specify the +start address; it defaults to 20. + +### Keyboard + +The keys `0-9`, `A-Z`, and `Space` are mapped to their corresponding +LINC keys. `Enter` is mapped to `EOL`, `Delete` and `Backspace` are +mapped to `DEL`, and `Shift` is mapped to `CASE`. To type an upper +case symbol on some key, press `CASE`, release it, and then type the +key. For convenience, `Alt` is mapped to `META`. + +The remaining keys are mapped thusly: +- `F1` is mapped to the `pu` key. +- `=` is mapped to the `i=` key. +- `-` and `,` are mapped to the `-,` key. +- `.` is mapped to the `+. key. +- `\` is mapped to the `|⊟` key. +- `[` and `left backslash` are mapped to the `*[` key. + +The remaining upper case symbols: +- `CASE A` - `"`. +- `CASE B` - `„`. +- `CASE C` - `<`. +- `CASE D` - `>`. +- `CASE E` - `]`. +- `CASE F` - `*`. +- `CASE G` - `:`. +- `CASE Space` - `?`. + +### Teletype + +The TTY device implmenents a teletype for printing output. When a +file is attached, it will receive text decoded at 110 baud from relay +output 0. + +Some characters are translated by LAP6 from the LINC character to ASCII: +- `i` to `&` +- `p` to `'` +- `|` to `\` +- `u` to `%` +- `⊟` to `$` +- `_` to `@` +- `"` to `^` +- `„` to `;` + +### CPU + +Registers: +- P - Instruction location, 10 bits. +- C - Current instruction, 12 bits. +- S - Memory address, 12 bits - 11 for address, 1 for halfword select. +- B - Memory data buffer, 12 bits. +- A - Accumulator, 12 bits - one's complement. +- Z - Various, 12 bits. +- L - Link, 1 bit. +- OVF - Overflow, 1 bit. +- IBZ - Tape interblock zone, 1 bit. +- ENI - Interrupt enabled, 1 bit. +- PINFF - Pause Interrupt enabled, 1 bit. + +Switches: +- LSW - Left switches, 12 bits. +- RSW - Right switches, 12 bits. +- SSW - Sense switches, 6 bits. + +Inputs: +- INTREQ, Interrupt request, 1 bit. +- R - Relays, 6 bits. +- XL - External levels, 12 of 1 bit each. +- SAM - Sampled analog inputs, 16 of 8 bits each; one's complement. + +### Documentation + +Programming: +https://bitsavers.org/pdf/washingtonUniversity/linc/Programming_the_LINC_Second_Edition_Jan69.pdf + +Using the LAP6 operating system, text editor, assembler, tape filing system: +https://bitsavers.org/pdf/washingtonUniversity/linc/LINC_Reference_Manuals/LINC_Vol_16_Section_3_LAP6_Handbook_May67.pdf diff --git a/linc/linc_cpu.c b/linc/linc_cpu.c index 5dcf53fb..c8d174bc 100644 --- a/linc/linc_cpu.c +++ b/linc/linc_cpu.c @@ -28,19 +28,17 @@ /* Debug */ -#define DBG_CPU 0001 +#define DBG_CPU 0001 +#define DBG_INT 0002 -#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 INSN_ENI 00010 +#define INSN_NOP 00016 +#define INSN_OPR 00500 +#define INSN_MTP 00700 +#define INSN_JMP 06000 #define X(_X) ((_X) & XMASK) +#define C03 (C & BMASK) /* CPU state. */ static uint16 P; @@ -57,9 +55,10 @@ static uint16 XL[12]; static int paused; static int IBZ; static int OVF; +static int INTREQ; +static int ENI = 0; static int PINFF; -static int interrupt; -static int interrupt_enable = 0; +static int DO = 0; static t_stat stop_reason; @@ -80,6 +79,8 @@ 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 t_stat linc_boot(int32 flag, CONST char *ptr); +static t_stat linc_do(int32 flag, CONST char *ptr); static UNIT cpu_unit = { UDATA(NULL, UNIT_FIX + UNIT_BINK, MEMSIZE) }; @@ -90,16 +91,21 @@ REG cpu_reg[] = { { ORDATAD(L, L, 1, "Link") }, { ORDATAD(Z, Z, 12, "?") }, { ORDATAD(R, R, 6, "Relay Register") }, - { ORDATAD(S, S, 12, "Memroy Address") }, + { ORDATAD(S, S, 12, "Memory 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") }, + { FLDATAD(IBZ, IBZ, 1, "Interblock zone") }, + { FLDATAD(OVF, OVF, 1, "Overflow") }, + { FLDATAD(INTREQ, INTREQ, 1, "Interrupt") }, + { FLDATAD(ENI, ENI, 1, "Interrupt Enable") }, + { FLDATAD(PIN, PINFF, 1, "Pause Interrupt") }, + + { BRDATAD(SAM, SAM, 8, 8, 16, "Sampled analog inputs") }, + { BRDATAD(XL, XL, 8, 1, 12, "External levels") }, { NULL } }; @@ -111,6 +117,7 @@ static MTAB cpu_mod[] = { static DEBTAB cpu_deb[] = { { "CPU", DBG_CPU }, + { "INTERRUPT", DBG_INT }, { NULL, 0 } }; @@ -122,41 +129,86 @@ DEVICE cpu_dev = { NULL, NULL, NULL, NULL, NULL, NULL }; -static void pcinc(int flag) +static CTAB linc_cmd[] = { + { "BOOT", &linc_boot, 0, + "BOOT {unit} boot simulator\n" + "BOOT TAPE{n} RCG={blocks} boot tape from specified blocks\n", NULL, &run_cmd_message }, + { "DO", &linc_do, 0, + "DO {unit} boot simulator\n" + "DO execute instruction in LSW and RSW\n", NULL, NULL }, + { NULL } +}; + +static void cpu_ndxp(int flag) { if (flag) P = X(P + 1); } -static void memaddr(uint16 addr) +static void cpu_ndxc() +{ + C = (C & ~BMASK) | ((C + 1) & BMASK); +} + +static void cpu_set_S(uint16 addr) { S = addr & WMASK; } -static void membuf(uint16 data) +static void cpu_set_B(uint16 data) { B = data & WMASK; } -static void memrd(void) +static void cpu_4ndxb() { - B = M[S & AMASK]; + cpu_set_B(B + 4); +} + +static void cpu_4ndxa() +{ + A = (A + 4) & WMASK; +} + +static void cpu_mem_read(void) +{ + cpu_set_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) +static void cpu_mem_modify(void) { M[S & AMASK] = B; if (sim_brk_summ && sim_brk_test(S & AMASK, SWMASK('W'))) stop_reason = STOP_WBKPT; } -static void memwr(void) +static void cpu_mem_write(void) { sim_interval--; - memmd(); + cpu_mem_modify(); +} + +static void cpu_insn_addr() +{ + if (!DO) { + cpu_set_S(P); + cpu_ndxp(1); + } +} + +static void cpu_insn_read() +{ + if (!DO) + cpu_mem_read(); +} + +static void cpu_fetch() +{ + cpu_insn_addr(); + cpu_insn_read(); } static int cpu_halfword(void) @@ -181,30 +233,26 @@ static void cpu_index(void) } else { tmp = 1; } - membuf((B & 06000) | X(B + tmp)); - memmd(); + cpu_set_B((B & 06000) | X(B + tmp)); + cpu_mem_modify(); } } 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); + uint16 a = C03; + if (a == 0) { + cpu_insn_addr(); + if ((C & IMASK) == 0) { + cpu_insn_read(); + cpu_set_S(B); } + } else { + cpu_set_S(a); + cpu_mem_read(); + cpu_index(); + cpu_set_S(B); + } } static void @@ -214,15 +262,23 @@ cpu_misc(void) case 00000: //HLT stop_reason = STOP_HALT; break; + case 00002: //PDP + sim_debug(DBG_CPU, &cpu_dev, "This is not a PDP-12.\n"); + break; case 00005: //ZTA A = Z >> 1; break; case 00010: //ENI - interrupt_enable = 1; + sim_debug(DBG_INT, &cpu_dev, "Interrupt enabled.\n"); + ENI = 1; break; case 00011: //CLR A = L = Z = 0; break; + case 00012: //DIN + sim_debug(DBG_INT, &cpu_dev, "Interrupt disabled.\n"); + ENI = 0; + break; case 00013: //Write gate on. break; case 00014: //ATR @@ -241,33 +297,31 @@ cpu_misc(void) static void cpu_set(void) { - memaddr(P); - memrd(); - pcinc(1); + cpu_fetch(); if ((C & IMASK) == 0) { - memaddr(B); - memrd(); + cpu_set_S(B); + cpu_mem_read(); } - memaddr(C & BMASK); - memwr(); + cpu_set_S(C03); + cpu_mem_write(); } static void cpu_sam(void) { - // sample analog input C & BMASK + // sample analog input C03 // 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]; + A = SAM[C03]; if (A & 0200) /* One's complement +/-177. */ - A |= 07600; + A |= 07400; } static void cpu_dis(void) { - memaddr(C & BMASK); - memrd(); + cpu_set_S(C03); + cpu_mem_read(); 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); @@ -275,15 +329,16 @@ static void cpu_dis(void) static void cpu_xsk(void) { - memaddr(C & BMASK); - memrd(); + cpu_set_S(C03); + cpu_mem_read(); cpu_index(); - pcinc(X(B) == 01777); + cpu_ndxp(X(B) == 01777); } static void cpu_rol(void) { - while (C & BMASK) { + C = (C & ~BMASK) | (~B & BMASK); + while (C03 != 017) { if (C & IMASK) { A = (A << 1) | L; L = A >> 12; @@ -291,13 +346,14 @@ static void cpu_rol(void) A = (A << 1) | (A >> 11); } A &= WMASK; - C--; + cpu_ndxc(); } } static void cpu_ror(void) { - while (C & BMASK) { + C = (C & ~BMASK) | (~B & BMASK); + while (C03 != 017) { Z = (Z >> 1) | ((A & 1) << 11); if (C & IMASK) { A |= L << 12; @@ -307,28 +363,29 @@ static void cpu_ror(void) A = (A >> 1) | (A << 11); A &= WMASK; } - C--; + cpu_ndxc(); } } static void cpu_scr(void) { - while (C & BMASK) { + C = (C & ~BMASK) | (~B & BMASK); + while (C03 != 017) { Z = (Z >> 1) | ((A & 1) << 11); if (C & IMASK) L = A & 1; A = (A & 04000) | (A >> 1); - C--; + cpu_ndxc(); } } int cpu_skip(void) { - int flag = 0; + int flag; 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]; + flag = XL[C03]; break; case 015: //KST flag = kbd_struck(); @@ -338,6 +395,8 @@ int cpu_skip(void) break; case 046: //PIN flag = PINFF; + sim_debug(DBG_INT, &cpu_dev, "Pause interrupt enabled.\n"); + PINFF = 0; break; case 050: //AZE flag = (A == 0) || (A == WMASK); @@ -369,9 +428,9 @@ int cpu_skip(void) static void cpu_opr(void) { - switch (C & BMASK) { + switch (C03) { 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: + case 010: case 011: case 012: case 013: if (C & IMASK) ; //Pause. break; @@ -389,37 +448,39 @@ static void cpu_opr(void) static void cpu_lmb(void) { - //Instruction field. + /* Lower memory bank. */ + sim_debug(DBG_CPU, &cpu_dev, "This is not micro-LINC 300.\n"); } static void cpu_umb(void) { - //Data field. + /* Upper memory bank. */ + sim_debug(DBG_CPU, &cpu_dev, "This is not micro-LINC 300.\n"); } static void cpu_tape(void) { - memaddr(P); - memrd(); - pcinc(1); + cpu_fetch(); tape_op(); } static void cpu_lda(void) { - memrd(); + cpu_mem_read(); A = B; } static void cpu_sta(void) { - membuf(A); - memwr(); + cpu_set_B(A); + /* Do not write immediate value if executing out of switches. */ + if (!DO || (C & IMASK) == 0) + cpu_mem_write(); } static void cpu_ada(void) { - memrd(); + cpu_mem_read(); OVF = ~(A ^ B); A += B; A += A >> 12; @@ -430,13 +491,13 @@ static void cpu_ada(void) static void cpu_adm(void) { cpu_ada(); - membuf(A); - memmd(); + cpu_set_B(A); + cpu_mem_modify(); } static void cpu_lam(void) { - memrd(); + cpu_mem_read(); A += L; L = A >> 12; A &= WMASK; @@ -444,40 +505,43 @@ static void cpu_lam(void) 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; + cpu_set_B(A); + cpu_mem_modify(); } 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--; + uint32 factor, product; + cpu_mem_read(); + + C &= ~BMASK; + L = (A ^ B) >> 11; + if (A & HMASK) + A ^= WMASK; + if (B & HMASK) + B ^= WMASK; + Z = B; + cpu_set_B(A); + factor = B; + product = A = 0; + while (C03 < 12) { + if (Z & 1) + product += factor; + Z >>= 1; + factor <<= 1; + cpu_ndxc(); + } if (S & HMASK) - A = (product >> 11) & WMASK; - else if ((A ^ B) & HMASK) - A = 04000 | (product & 03777); + A = product >> 11; else A = product & 03777; - L = A >> 11; + if (L) + A ^= 07777; } static void cpu_ldh(void) { - memrd(); + cpu_mem_read(); if ((S & HMASK) == 0) B >>= 6; A = B & RMASK; @@ -485,91 +549,90 @@ static void cpu_ldh(void) static void cpu_sth(void) { - memrd(); + cpu_mem_read(); if (S & HMASK) - membuf((A & RMASK) | (B & LMASK)); + cpu_set_B((A & RMASK) | (B & LMASK)); else - membuf((A << 6) | (B & RMASK)); - memmd(); + cpu_set_B((A << 6) | (B & RMASK)); + cpu_mem_modify(); } static void cpu_shd(void) { - memrd(); + cpu_mem_read(); if ((S & HMASK) == 0) B >>= 6; - pcinc((A & RMASK) != (B & RMASK)); + cpu_ndxp((A & RMASK) != (B & RMASK)); } static void cpu_sae(void) { - memrd(); - pcinc(A == B); + cpu_mem_read(); + cpu_ndxp(A == B); } static void cpu_sro(void) { - memrd(); - pcinc((B & 1) == 0); - membuf((B >> 1) | (B << 11)); - memmd(); + cpu_mem_read(); + cpu_ndxp((B & 1) == 0); + cpu_set_B((B >> 1) | (B << 11)); + cpu_mem_modify(); } static void cpu_bcl(void) { - memrd(); + cpu_mem_read(); A &= ~B; } static void cpu_bse(void) { - memrd(); + cpu_mem_read(); A |= B; } static void cpu_bco(void) { - memrd(); + cpu_mem_read(); A ^= B; } static void cpu_dsc(void) { - int i, j; - uint16 data; + cpu_mem_read(); + Z = B; - memrd(); - data = B; - - // C used for counting. - memaddr(1); - memrd(); + cpu_set_S(1); + cpu_mem_read(); 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; + + C &= ~BMASK; + while (C03 < 12) { + if (C03 == 0 || C03 == 6) { + A &= 07740; + cpu_4ndxb(); } + if (Z & 1) + dpy_dis(B >> 11, B & DMASK, A & DMASK); + Z >>= 1; + cpu_4ndxa(); + cpu_ndxc(); } - memmd(); + cpu_mem_write(); } static void cpu_add(void) { - memaddr(X(C)); + cpu_set_S(X(C)); cpu_ada(); } static void cpu_stc(void) { - memaddr(X(C)); - membuf(A); + cpu_set_S(X(C)); + cpu_set_B(A); A = 0; - memwr(); + cpu_mem_write(); } static void cpu_jmp(void) @@ -577,9 +640,9 @@ static void cpu_jmp(void) uint16 tmp = P; P = X(C); if (P != 0) { - membuf(06000 | tmp); - memaddr(0); - memwr(); + cpu_set_B(INSN_JMP | tmp); + cpu_set_S(0); + cpu_mem_write(); } } @@ -587,9 +650,9 @@ static void cpu_insn(void) { /* Cycle 0, or I. */ - memaddr(P); - memrd(); - C = B; + cpu_fetch(); + if (!DO) + C = B; /* Cycle 1, or X. */ if ((C & 07000) == 01000) @@ -602,7 +665,6 @@ cpu_insn(void) } /* Cycle 2, or O. */ - pcinc(1); /* Cycle 3, or E. */ switch (C & 07740) { @@ -632,7 +694,7 @@ cpu_insn(void) break; case 00400: case 00440: - pcinc(cpu_skip()); + cpu_ndxp(cpu_skip()); break; case 00500: case 00540: @@ -721,14 +783,96 @@ cpu_insn(void) 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; +t_stat cpu_do(void) +{ + t_stat stat; + + DO = 1; + C = LSW; + cpu_set_B(RSW); + cpu_insn(); + DO = 0; + + sim_interval = 1; + /* Can not return from DO until the instruction is done, + i.e. not paused. */ + while (paused) { + AIO_CHECK_EVENT; + if (sim_interval <= 0) { + if ((stat = sim_process_event()) != SCPE_OK) + return stat; + } + sim_interval--; + } + return SCPE_OK; +} + +static int jmp_or_eni(void) +{ + return (C & 06000) == INSN_JMP || (C == INSN_ENI); +} + +static int mtp_or_opr(void) +{ + return (C & 07700) == INSN_MTP || (C & 07700) == INSN_OPR; +} + +static void cpu_interrupt(void) +{ + if (!INTREQ) + return; + if (!ENI) + return; + + sim_debug(DBG_INT, &cpu_dev, "Interrupt requested and enabled.\n"); + + if (jmp_or_eni()) { + sim_debug(DBG_INT, &cpu_dev, "Interrupt not taken after JMP or ENI.\n"); + return; + } + + if (paused) { + if (!mtp_or_opr()) { + sim_debug(DBG_INT, &cpu_dev, "Pause only interrupted for MTP or OPR.\n"); + return; + } + if (PINFF) + return; + sim_debug(DBG_INT, &cpu_dev, "Pause interrupted.\n"); + PINFF = 1; + paused = 0; + } + + sim_debug(DBG_INT, &cpu_dev, "Interrupt taken.\n"); + + cpu_set_S(021); + cpu_mem_read(); + C = B; + if (history) { + history[history_i].P = 07777; + history[history_i].C = C; + history[history_i].S = S; + } + + ENI = 0; /* Except for OPR. */ + if ((C & 06000) == INSN_JMP) cpu_jmp(); + else if ((C & 07700) == INSN_OPR) { + ENI = 1; /* OPR doesn't disable interrupts. */ + cpu_opr(); + } else if (C == INSN_NOP) + ; + else + sim_debug(DBG_INT, &cpu_dev, "Invalid interrupt instruction.\n"); + + if (history) { + history[history_i].B = B; + history[history_i].A = A; + history_i = (history_i + 1) % history_m; + if (history_n < history_m) + history_n++; } } @@ -739,8 +883,18 @@ t_stat sim_instr(void) if ((stat = build_dev_tab()) != SCPE_OK) return stat; + /* Stepping is based on sim_step, not sim_interval. The latter is + approximately memory cycles, not instructions. */ + sim_cancel_step(); + + /* Because we check sim_step before cpu_insn. */ + if (sim_step) + sim_step++; + stop_reason = 0; paused = 0; + PINFF = 0; + ENI = 0; for (;;) { AIO_CHECK_EVENT; @@ -752,19 +906,19 @@ t_stat sim_instr(void) 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 + /* Can not return from a STEP until the instruction is done, + i.e. not paused. */ + if (!paused && sim_step != 0) { + if (--sim_step == 0) + return SCPE_STEP; + } + if (paused) sim_interval--; else cpu_insn(); - if (sim_step != 0) { - if (--sim_step == 0) - return SCPE_STEP; - } + cpu_interrupt(); if (stop_reason) return stop_reason; @@ -828,36 +982,87 @@ cpu_show_hist(FILE *st, UNIT *uptr, int32 val, CONST void *desc) 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, + if (history[j].P == 07777) + fprintf (st, "---- "); + else + fprintf (st, "%04o ", history[j].P); + fprintf (st, "%04o %04o %04o %04o %d ", 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); + 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; + sim_vm_cmd = linc_cmd; return SCPE_OK; } + +static t_stat linc_boot(int32 flag, CONST char *cptr) +{ + char dev[CBUFSIZE], arg[CBUFSIZE]; + char bbuf[CBUFSIZE], gbuf[CBUFSIZE]; + t_value block; + t_stat stat; + + /* Is it BOOT TAPE? */ + cptr = get_glyph(cptr, dev, 0); + if (*dev == 0) + return SCPE_ARG; + if (strncmp(dev, "TAPE", 4) != 0) + return run_cmd(RU_BOOT, dev); + + /* Yes. Is there an argument after? */ + if (*cptr == 0) + return run_cmd(RU_BOOT, dev); + + bbuf[0] = 0; + strcpy(gbuf, "20"); + while (*cptr) { + cptr = get_glyph(cptr, arg, 0); + if (strncmp(arg, "RDC=", 4) == 0) + LSW = 0700, strcpy(bbuf, arg + 4); + else if (strncmp(arg, "RCG=", 4) == 0) + LSW = 0701, strcpy(bbuf, arg + 4); + else if (strncmp(arg, "START=", 6) == 0) + LSW = 0701, strcpy(gbuf, arg + 6); + else + return SCPE_ARG; + } + + if (*bbuf == 0) + return SCPE_ARG; + + /* It's a BOOT TAPE RDC= or RCG=, so start from switches. */ + block = get_uint(bbuf, 8, ~0, &stat); + if (stat != SCPE_OK) + return stat; + + RSW = block; + stat = cpu_do(); + if (stat != SCPE_OK) + return stat; + return run_cmd(RU_GO, gbuf); +} + +static t_stat linc_do(int32 flag, CONST char *cptr) +{ + /* With arguments, regular DO to execute script. */ + if (*cptr != 0) + return do_cmd(flag, cptr); + + /* No arguments, push the DO button on the LINC control panel. */ + return cpu_do(); +} diff --git a/linc/linc_crt.c b/linc/linc_crt.c index e3b1cfef..d9d1ab84 100644 --- a/linc/linc_crt.c +++ b/linc/linc_crt.c @@ -44,6 +44,7 @@ static UNIT crt_unit = { static DEBTAB crt_deb[] = { { "DBG", DBG }, { "VVID", SIM_VID_DBG_VIDEO }, + { "KVID", SIM_VID_DBG_KEY }, { NULL, 0 } }; @@ -58,7 +59,7 @@ DEVICE crt_dev = { 1, 8, 12, 1, 8, 12, NULL, NULL, &crt_reset, NULL, NULL, NULL, - NULL, DEV_DISABLE | DEV_DEBUG | DEV_DISPLAY, 0, crt_deb, + NULL, DEV_DISABLE | DEV_DEBUG, 0, crt_deb, NULL, NULL, NULL, NULL, NULL, NULL }; @@ -109,14 +110,7 @@ crt_point (uint16 x, uint16 y) #endif } -/* Hook called when CRT goes idle. */ -void -crt_idle (void) -{ -} - -/* Display high voltage sync. */ -void -crt_hvc (void) +void crt_toggle_fullscreen(void) { + vid_set_fullscreen(!vid_is_fullscreen ()); } diff --git a/linc/linc_defs.h b/linc/linc_defs.h index c92eac99..a1b9f26e 100644 --- a/linc/linc_defs.h +++ b/linc/linc_defs.h @@ -36,6 +36,18 @@ #define STOP_RBKPT 3 #define STOP_WBKPT 3 +#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 TMASK 00777 /* Tape block. */ +#define LMASK 07700 /* Left half word. */ +#define RMASK 00077 /* Right half word; character. */ +#define IMASK 00020 /* Index bit. */ +#define UMASK 00010 /* Tape unit bit. */ +#define BMASK 00017 /* Beta; index register. */ + #define MEMSIZE 2048 extern REG cpu_reg[]; @@ -49,10 +61,13 @@ extern DEVICE tape_dev; extern DEVICE tty_dev; extern t_bool build_dev_tab(void); +extern t_stat cpu_do(void); extern void dpy_dis(uint16 h, uint16 x, uint16 y); extern void crt_point (uint16 x, uint16 y); +extern void crt_toggle_fullscreen(void); extern uint16 kbd_key(uint16 wait); extern int kbd_struck(void); extern void tape_op(void); +extern t_stat tape_metadata(FILE *, uint16 *, int16 *, int16 *); #endif /* LINC_DEFS_H_ */ diff --git a/linc/linc_kbd.c b/linc/linc_kbd.c index 8a868400..6a6ae1f8 100644 --- a/linc/linc_kbd.c +++ b/linc/linc_kbd.c @@ -4,12 +4,10 @@ /* Debug */ #define DBG 0001 -static t_stat kbd_svc(UNIT *uptr); -static t_stat kbd_reset(DEVICE *dptr); +#define A (*(uint16 *)cpu_reg[2].loc) +#define paused (*(int *)cpu_reg[11].loc) -static UNIT kbd_unit = { - UDATA(&kbd_svc, UNIT_IDLE, 0) -}; +static t_stat kbd_reset(DEVICE *dptr); static DEBTAB kbd_deb[] = { { "DBG", DBG }, @@ -17,8 +15,8 @@ static DEBTAB kbd_deb[] = { }; DEVICE kbd_dev = { - "KBD", &kbd_unit, NULL, NULL, - 1, 8, 12, 1, 8, 12, + "KBD", NULL, NULL, NULL, + 0, 8, 12, 1, 8, 12, NULL, NULL, &kbd_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, 0, kbd_deb, NULL, NULL, NULL, NULL, NULL, NULL @@ -44,170 +42,6 @@ CASE 0 1 2 3 4 5 6 7 8 9 DEL 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) @@ -219,11 +53,11 @@ 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 { + } else if (wait) { sim_debug(DBG, &kbd_dev, "KEY paused\n"); + paused = 1; } return 0; } @@ -399,12 +233,19 @@ static void kbd_convert(uint32 key) // ˣ 75 // : 76 // ʸ 77 + case SIM_KEY_F11: + crt_toggle_fullscreen(); + return; default: return; } sim_debug(DBG, &kbd_dev, "Key struck %s -> %02o\n", vid_key_name(key), kbd_code); - kbd_pressed = 1; + if (paused) + A = kbd_code; + else + kbd_pressed = 1; + paused = 0; } static int @@ -421,7 +262,6 @@ 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 index 5bda3e9b..1de0d3ce 100644 --- a/linc/linc_sys.c +++ b/linc/linc_sys.c @@ -104,28 +104,35 @@ 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; + int16 forward_offset = 0, reverse_offset; + uint16 block_size; long offset = 0; t_stat stat; + if (sim_switches & SWMASK('E')) { + stat = tape_metadata(fileref, &block_size, &forward_offset, &reverse_offset); + if (stat != SCPE_OK) + return stat; + if (block_size != 256) + return SCPE_FMT; + } + 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); + while (*cptr !=0) { + char gbuf[100]; + cptr = get_glyph(cptr, gbuf, 0); + if (strncmp(gbuf, "START=", 6) == 0) + start = (t_addr)get_uint(gbuf + 6, 8, ~0, &stat); + else if (strncmp(gbuf, "OFFSET=", 7) == 0) + offset = 2 * (long)get_uint(gbuf + 7, 8, ~0, &stat); + else if (strncmp(gbuf, "BLOCK=", 6) == 0) + offset = 512 * ((long)get_uint(gbuf + 6, 8, ~0, &stat) - forward_offset); + else if (strncmp(gbuf, "LENGTH=", 7) == 0) + length = (t_addr)get_uint(gbuf + 7, 8, ~0, &stat); else return SCPE_ARG; - cptr = strchr(cptr, ' '); - if (cptr == NULL) - break; - while (*cptr == ' ') - cptr++; } end = start + length; @@ -162,7 +169,7 @@ t_bool build_dev_tab(void) static t_stat fprint_next(FILE *of, uint16 addr) { fprintf(of, "\n"); - fprint_val(of, ++addr & 01777, 8, 10, PV_LEFT); + fprint_val(of, ++addr & XMASK, 8, 10, PV_LEFT); fprintf(of, ":\t%04o", M[addr]); return -1; } @@ -182,8 +189,8 @@ static void fprint_misc(FILE *of, uint16 insn) case 00011: fprintf(of, "CLR"); break; - case 00013: - fprintf(of, "%04o", insn); + case 00012: + fprintf(of, "DIN"); break; case 00014: fprintf(of, "ATR"); @@ -198,17 +205,17 @@ static void fprint_misc(FILE *of, uint16 insn) fprintf(of, "COM"); break; default: - fprintf(of, "%04o", insn); + fprintf(of, "MSC %o", insn); break; } } static t_stat fprint_index(FILE *of, uint16 insn, uint16 addr) { - if (insn & 020) + if (insn & IMASK) fprintf(of, " i"); - if (insn & 017) - fprintf(of, " %o", insn & 017); + if (insn & BMASK) + fprintf(of, " %o", insn & BMASK); else return fprint_next(of, addr); return SCPE_OK; @@ -223,7 +230,7 @@ static void fprint_set(FILE *of, uint16 insn, uint16 addr) static void fprint_sam(FILE *of, uint16 insn) { - fprintf(of, "SAM "); + fprintf(of, "SAM%s %o", insn & IMASK ? " i" : "", insn & BMASK); } static t_stat fprint_dis(FILE *of, uint16 insn, uint16 addr) @@ -263,7 +270,7 @@ static void fprint_skip(FILE *of, uint16 insn) 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); + snprintf(beta, sizeof beta, "%o", insn & BMASK); break; case 015: fprintf(of, "KST"); @@ -297,7 +304,7 @@ static void fprint_skip(FILE *of, uint16 insn) fprintf(of, "%04o", insn); return; } - if (insn & 020) + if (insn & IMASK) fprintf(of, " i" ); fprintf(of, " %s", beta); } @@ -322,7 +329,7 @@ static void fprint_opr(FILE *of, uint16 insn) fprintf(of, "%04o", insn); break; } - if (insn & 020) + if (insn & IMASK) fprintf(of, "i" ); } @@ -364,9 +371,9 @@ static void fprint_tape(FILE *of, uint16 insn, uint16 addr) fprintf(of, "CHK"); break; } - if (insn & 020) + if (insn & IMASK) fprintf(of, " i"); - if (insn & 010) + if (insn & UMASK) fprintf(of, " u"); fprint_next(of, addr); } @@ -463,17 +470,17 @@ static t_stat fprint_dsc(FILE *of, uint16 insn, uint16 addr) static void fprint_add(FILE *of, uint16 insn) { - fprintf(of, "ADD %04o", insn & 01777); + fprintf(of, "ADD %04o", insn & XMASK); } static void fprint_stc(FILE *of, uint16 insn) { - fprintf(of, "STC %04o", insn & 01777); + fprintf(of, "STC %04o", insn & XMASK); } static void fprint_jmp(FILE *of, uint16 insn) { - fprintf(of, "JMP %04o", insn & 01777); + fprintf(of, "JMP %04o", insn & XMASK); } t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) @@ -582,12 +589,108 @@ t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) return SCPE_OK; } +struct symbol { + const char *name; + uint16 value; +}; + +static const struct symbol symbols[] = { + { "U", 00010 }, + { "I", 00020 }, + { "HLT", 00000 }, + { "ZTA", 00005 }, + { "CLR", 00011 }, + { "DIN", 00012 }, + { "ATR", 00014 }, + { "RTA", 00015 }, + { "NOP", 00016 }, + { "COM", 00017 }, + { "SET", 00040 }, + { "SAM", 00100 }, + { "DIS", 00140 }, + { "XSK", 00200 }, + { "ROL", 00240 }, + { "ROR", 00300 }, + { "SCR", 00340 }, + { "SXL", 00400 }, + { "KST", 00415 }, + { "SNS", 00440 }, + { "AZE", 00450 }, + { "APO", 00451 }, + { "LZE", 00452 }, + { "IBZ", 00453 }, + { "OVF", 00454 }, + { "ZZZ", 00455 }, + { "OPR", 00500 }, + { "KBD", 00515 }, + { "RSW", 00516 }, + { "LSW", 00517 }, + { "LMB", 00600 }, + { "UMB", 00640 }, + { "RDC", 00700 }, + { "RCG", 00701 }, + { "RDE", 00702 }, + { "MTB", 00703 }, + { "WRC", 00704 }, + { "WCG", 00705 }, + { "WRI", 00706 }, + { "CHK", 00707 }, + { "LDA", 01000 }, + { "STA", 01040 }, + { "ADA", 01100 }, + { "ADM", 01140 }, + { "LAM", 01200 }, + { "MUL", 01240 }, + { "LDH", 01300 }, + { "STH", 01340 }, + { "SHD", 01400 }, + { "SAE", 01440 }, + { "SRO", 01500 }, + { "BCL", 01540 }, + { "BSE", 01600 }, + { "BCO", 01640 }, + { "DSC", 01740 }, + { "ADD", 02000 }, + { "STC", 04000 }, + { "JMP", 06000 } +}; + t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { + char gbuf[CBUFSIZE]; + t_value val2; t_stat stat; + int i; + *val = get_uint(cptr, 8, ~0, &stat); - if (stat != SCPE_OK) - return stat; - return 0; + if (*val > 07777) + return SCPE_ARG; + if (stat == SCPE_OK) + return SCPE_OK; + + if (*cptr == '-') { + stat = parse_sym(cptr + 1, addr, uptr, val, sw); + if (stat != SCPE_OK) + return stat; + *val ^= 07777; + if (stat == SCPE_OK) + return SCPE_OK; + } + + cptr = get_glyph(cptr, gbuf, 0); + for (i = 0; i < sizeof symbols / sizeof symbols[0]; i++) { + if (strcmp(gbuf, symbols[i].name) != 0) + continue; + *val = symbols[i].value; + if (*cptr) { + stat = parse_sym(cptr, addr, uptr, &val2, sw); + if (stat != SCPE_OK) + return stat; + *val |= val2; + } + return SCPE_OK; + } + + return SCPE_ARG; } diff --git a/linc/linc_tape.c b/linc/linc_tape.c index 4692eb15..f5014769 100644 --- a/linc/linc_tape.c +++ b/linc/linc_tape.c @@ -5,10 +5,13 @@ #define ACC u5 #define OFFSET u6 +#define P (*(uint16 *)cpu_reg[0].loc) #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 LSW (*(uint16 *)cpu_reg[8].loc) +#define RSW (*(uint16 *)cpu_reg[9].loc) #define paused (*(int *)cpu_reg[11].loc) #define IBZ (*(int *)cpu_reg[12].loc) @@ -24,6 +27,8 @@ #define MAX_BLOCKS 512 #define MAX_POS ((BLOCK_WORDS * MAX_BLOCKS + IBZ_WORDS) * MAX_SPEED) +#define GOOD_CHECKSUM 07777 + #define RDC 0 /* read tape and check */ #define RCG 1 /* read tape group */ #define RDE 2 /* read tape */ @@ -100,10 +105,10 @@ void tape_op(void) uptr->ACC = ACC_START; } if (!sim_is_active(uptr)) - sim_activate_after(uptr, 1); + sim_activate(uptr, 20); paused = 1; A = 0; - WANTED_BLOCK = B & 0777; + WANTED_BLOCK = B & TMASK; switch (C & 7) { case RDC: case RDE: case WRC: case WRI: case CHK: @@ -124,25 +129,25 @@ void tape_op(void) } } -static t_stat tape_seek(UNIT *uptr, t_addr block, t_addr offset) +static t_stat tape_seek(FILE *fileref, t_addr block, t_addr offset) { offset = DATA_WORDS * block + offset; offset *= 2; - if (sim_fseek(uptr->fileref, offset, SEEK_SET) == -1) + if (sim_fseek(fileref, offset, SEEK_SET) == -1) return SCPE_IOERR; return SCPE_OK; } -static uint16 read_word(UNIT *uptr, t_addr block, t_addr offset) +static uint16 read_word(FILE *fileref, t_addr block, t_addr offset) { t_stat stat; uint8 data[2]; uint16 word; - stat = tape_seek(uptr, block, offset); + stat = tape_seek(fileref, block, offset); if (stat != SCPE_OK) ; - if (sim_fread(data, 1, 2, uptr->fileref) != 2) + if (sim_fread(data, 1, 2, fileref) != 2) ; if (data[1] & 0xF0) ; @@ -152,17 +157,17 @@ static uint16 read_word(UNIT *uptr, t_addr block, t_addr offset) return word; } -static void write_word(UNIT *uptr, t_addr block, t_addr offset, uint16 word) +static void write_word(FILE *fileref, t_addr block, t_addr offset, uint16 word) { t_stat stat; uint8 data[2]; - stat = tape_seek(uptr, block, offset); + stat = tape_seek(fileref, block, offset); if (stat != SCPE_OK) ; data[0] = word & 0xFF; data[1] = word >> 8; - if (sim_fwrite(data, 1, 2, uptr->fileref) != 2) + if (sim_fwrite(data, 1, 2, fileref) != 2) ; } @@ -197,23 +202,22 @@ static void tape_done(UNIT *uptr) switch (C & 7) { case RDC: case RCG: case RDE: case CHK: - A = 07777; + A = GOOD_CHECKSUM; break; case WRI: - A = A ^ 07777; - A++; - A &= 07777; + A = (A ^ 07777) + 1; + A &= WMASK; break; case MTB: A = (WANTED_BLOCK + ~CURRENT_BLOCK); A += A >> 12; - A &= 07777; + A &= WMASK; break; } switch (C & 7) { case RDC: - if (A != 07777) { + if (A != GOOD_CHECKSUM) { sim_debug(DBG, &tape_dev, "Check failed; read again\n"); S &= ~0377; } else { @@ -224,7 +228,7 @@ static void tape_done(UNIT *uptr) case WRC: sim_debug(DBG, &tape_dev, "Block written, go back and check\n"); // For now, done. - A = 07777; + A = GOOD_CHECKSUM; paused = 0; break; case RCG: case WCG: @@ -235,7 +239,7 @@ static void tape_done(UNIT *uptr) sim_debug(DBG, &tape_dev, "Blocks left in group: %d\n", GROUP); GROUP--; } - WANTED_BLOCK = (WANTED_BLOCK + 1) & 0777; + WANTED_BLOCK = (WANTED_BLOCK + 1) & TMASK; break; case RDE: case WRI: sim_debug(DBG, &tape_dev, "Transfer done\n"); @@ -253,7 +257,7 @@ static void tape_done(UNIT *uptr) if (paused) ; - else if ((C & 020) == 0) { + else if ((C & IMASK) == 0) { sim_debug(DBG_SEEK, &tape_dev, "Instruction done, stop tape\n"); uptr->ACC = uptr->SPEED > 0 ? -ACC_STOP : ACC_STOP; } else { @@ -265,7 +269,7 @@ 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); + B = read_word(uptr->fileref, block, offset); sim_debug(DBG_READ, &tape_dev, "Read block %03o offset %03o data %04o address %04o\n", block, offset, B, S); @@ -277,12 +281,12 @@ static void tape_word(UNIT *uptr, uint16 block, uint16 offset) 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); + write_word(uptr->fileref, block, offset, B); break; } - S = (S+1) & 03777; + S = (S+1) & AMASK; A += B; - A &= 07777; + A &= WMASK; } static t_stat tape_svc(UNIT *uptr) @@ -384,43 +388,70 @@ static t_stat tape_reset(DEVICE *dptr) static t_stat tape_boot(int32 unit_num, DEVICE *dptr) { + uint16 block = 0300; + uint16 blocks = 8; + uint16 quarter = 0; + t_stat stat; + if (unit_num >= 2 && unit_num <= 3) return SCPE_ARG; + if (blocks == 0) + return SCPE_ARG; + + if (blocks == 1) + LSW = RDC; + else + LSW = RCG, quarter = blocks - 1; + LSW |= 0700 | (unit_num << 3); + RSW = (quarter << 9) | block; + stat = cpu_do(); + if (stat != SCPE_OK) + return stat; + P = 020; + return SCPE_OK; +} - //LSW = 0701 + (unit_num << 3); - //RSW = (7 << 9) | 0300; - //P = 020; +t_stat tape_metadata(FILE *fileref, uint16 *block_size, int16 *forward_offset, int16 *reverse_offset) +{ + t_offset size = sim_fsize(fileref); + if (size == MAX_BLOCKS * DATA_WORDS * 2) { + /* Plain image. */ + *block_size = DATA_WORDS; + *forward_offset = 0; + *reverse_offset = 0; + } else if ((size % (2 * DATA_WORDS)) == 6) { + /* Extended image with additional meta data. */ + uint16 metadata = (uint16)(size / (2 * DATA_WORDS)); + *block_size = read_word(fileref, metadata, 0); + *forward_offset = (int16)read_word(fileref, metadata, 1); + *reverse_offset = (int16)read_word(fileref, metadata, 2); + } else + return SCPE_FMT; return SCPE_OK; } static t_stat tape_attach(UNIT *uptr, CONST char *cptr) { t_stat stat; - t_offset size; + uint16 block_size; + int16 forward_offset, reverse_offset; + 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 + stat = tape_metadata(uptr->fileref, &block_size, &forward_offset, &reverse_offset); + if (stat != SCPE_OK) + return stat; + sim_debug(DBG, &tape_dev, + "Tape 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; uptr->POS = -2 * START_POS; uptr->SPEED = 0; diff --git a/linc/linc_tty.c b/linc/linc_tty.c index 8979d2ae..89044e8c 100644 --- a/linc/linc_tty.c +++ b/linc/linc_tty.c @@ -1,6 +1,124 @@ #include "linc_defs.h" -DEVICE tty_dev = { - "TTY", NULL, NULL, NULL, - 0, 8, 12, 1, 8, 12, + +/* Data bits, 110 baud rate. */ +#define BIT_TIME 1120 +/* Sample rate to find the start bit edge. */ +#define START_TIME (BIT_TIME / 5) +/* After finding the edge, wait until the middle of the first data bit. */ +#define FIRST_TIME (BIT_TIME + (BIT_TIME - START_TIME) / 2) + +#define R (*(uint16 *)cpu_reg[5].loc) + +/* Debug */ +#define DBG 0001 +#define DBG_BIT 0002 + +#define DATA u3 /* Character being assembled. */ +#define STATE u4 /* 0 for start bit, 1 for stop bit, otherwise data. */ +#define PREVIOUS u5 /* Previous level seen. */ + +/* When a start bit is found, the state is set to 10 and then + decremented for each bit that is processed. */ +#define STATE_START 0 +#define STATE_STOP 1 +/* STATE_DATA 2-9 */ +#define STATE_FIRST 10 + +/* Function declaration. */ +static t_stat tty_svc(UNIT *uptr); +static t_stat tty_attach(UNIT *uptr, CONST char *cptr); +static t_stat tty_detach(UNIT *uptr); + +static UNIT tty_unit = { + UDATA(&tty_svc, UNIT_IDLE | UNIT_ATTABLE, 0) }; + +static DEBTAB tty_deb[] = { + { "DBG", DBG }, + { "BIT", DBG_BIT }, + { NULL, 0 } +}; + +DEVICE tty_dev = { + "TTY", &tty_unit, NULL, NULL, + 1, 8, 12, 1, 8, 12, + NULL, NULL, NULL, + NULL, &tty_attach, &tty_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, tty_deb, + NULL, NULL, NULL, NULL, NULL, NULL +}; + +static void tty_output(UNIT *uptr) +{ + uint8 ch = uptr->DATA; + sim_debug(DBG, &tty_dev, "Character %03o '%c'\n", ch, ch & 0177); + fputc(ch & 0177, uptr->fileref); + fflush(uptr->fileref); +} + +static t_stat tty_svc(UNIT *uptr) +{ + switch (uptr->STATE) { + case STATE_START: + if (uptr->PREVIOUS == 0 || (R & 1) == 1) { + /* Keep looking for start bit. */ + uptr->PREVIOUS = R & 1; + sim_activate(uptr, START_TIME); + return SCPE_OK; + } + + sim_debug(DBG_BIT, &tty_dev, "Start bit edge found.\n"); + uptr->STATE = STATE_FIRST; + uptr->DATA = 0; + /* Wait until the middle of the first data bit. Since the edge + was just seen, this is a little longer than the time between + data bits. */ + sim_activate(uptr, FIRST_TIME); + break; + + default: + sim_debug(DBG_BIT, &tty_dev, "Data bit %d is %d\n", + STATE_FIRST - 1 - uptr->STATE, R & 1); + uptr->DATA >>= 1; + uptr->DATA |= (R & 1) << 7; + sim_activate(uptr, BIT_TIME); + break; + + case STATE_STOP: + sim_debug(DBG_BIT, &tty_dev, "Stop bit is %d\n", R & 1); + if (R & 1) + tty_output(uptr); + else + sim_debug(DBG, &tty_dev, "Framing error.\n"); + uptr->PREVIOUS = R & 1; + /* Look for next start bit. */ + sim_activate(uptr, START_TIME); + break; + } + + /* Decrease the state counter, first through the data bits, then + the stop bit, and finally the start bit. */ + uptr->STATE--; + return SCPE_OK; +} + +static t_stat tty_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat stat = attach_unit(uptr, cptr); + if (stat != SCPE_OK) + return stat; + uptr->STATE = 0; + uptr->PREVIOUS = 0; + sim_activate(uptr, 1); + return SCPE_OK; +} + +static t_stat tty_detach(UNIT *uptr) +{ + 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/tests/classic-test.do b/linc/tests/classic-test.do index 071760f8..859ff404 100644 --- a/linc/tests/classic-test.do +++ b/linc/tests/classic-test.do @@ -1,106 +1,110 @@ +cd %~p0 + # The tests check writing to the tape, so use a copy. copy classic-test.linc clobbered.linc attach tape0 clobbered.linc echo CONTRL -load classic-test.linc block=10 start=0 length=400 +load -e classic-test.linc block=0 start=0 length=400 break 34 # Special treatment for this test, because it's supposed to halt. echo *** Test: 70 - HLTTST *** -load classic-test.linc block=11 start=400 length=400 +load -e classic-test.linc block=1 start=400 length=400 assert 400==70 go 401 assert P==402 continue assert P==34 -call test 1 SAETST 012 002 -call test 2 BCLTST 013 003 -call test 3 BSETST 014 004 -call test 4 BCOTST 015 005 -call test 5 ROTL1 016 006 -call test 6 ROTL2 017 007 -call test 7 ROTL3 020 010 -call test 10 ROTL4 021 011 -call test 11 ROTL5 022 012 -call test 12 ROTR1 023 013 -call test 13 ROTR2 024 014 -call test 14 ROTR3 025 015 -call test 15 ROTR4 026 016 -call test 16 ROTR5 027 017 -call test 17 CLRTST 030 020 -call test 20 ADDONE 031 021 -call test 21 COMT1 032 022 -call test 22 SCRT1 033 023 -call test 23 SCRT2 034 024 -call test 24 SCRT3 035 025 -call test 25 SCRT4 037 027 -call test 26 ADDT1 041 031 -call test 27 FADRT1 042 032 -call test 30 FADRT2 043 033 -call test 31 iBETA1 045 035 -call test 32 iBETA2 046 036 -call test 33 iBETA3 047 037 -call test 34 iBETA4 050 040 -call test 35 LDAT1 051 041 -call test 36 STAT1 052 042 -call test 37 ADMT1 053 043 -call test 40 LAMT1 054 044 -call test 41 MULT1 055 045 -call test 42 SROT1 056 046 -call test 43 SETT1 057 047 -call test 44 SETT2 060 050 -call test 45 XSKT1 061 051 -call test 46 XSKT2 062 052 -call test 47 AZET1 063 053 -call test 50 APOT1 064 054 -call test 51 LZET1 065 055 -call test 52 HWCT1 066 056 -call test 53 HWCT2 067 057 -call test 54 HWCT3 070 060 -call test 55 HWCT4 071 061 -call test 56 HWCT5 072 062 -call test 57 RANADD 073 063 -call test 60 ATRT1 074 064 -call test 61 IBZT1 075 065 -call test 62 JMPUP 076 066 -call test 63 JMPDWN 077 067 -call test 64 TAPETS 100 070 -call test 65 MTBTST 111 101 -call test 66 DISTST 112 102 -call test 67 DSCTST 113 103 -call test 700 OVFT1 114 104 -call test 701 ZTAT1 115 105 -call test 702 ZCLR1 116 106 -deposit SAM[0] 177 -call test 703 ZCLRT2 117 107 -;call test 704 ENIT1 120 110 deposit RSW 0300 deposit LSW 0700 deposit SSW 77 -deposit XL[0] 1 -deposit XL[1] 1 -deposit XL[2] 1 -deposit XL[3] 1 -deposit XL[4] 1 -deposit XL[5] 1 -deposit XL[6] 1 -deposit XL[7] 1 -deposit XL[8] 1 -deposit XL[9] 1 +deposit SAM[0] 177 +deposit XL[0] 1 +deposit XL[1] 1 +deposit XL[2] 1 +deposit XL[3] 1 +deposit XL[4] 1 +deposit XL[5] 1 +deposit XL[6] 1 +deposit XL[7] 1 +deposit XL[8] 1 +deposit XL[9] 1 deposit XL[10] 1 deposit XL[11] 1 -call test 71 MISCTS 121 111 + +call test 1 SAETST 002 +call test 2 BCLTST 003 +call test 3 BSETST 004 +call test 4 BCOTST 005 +call test 5 ROTL1 006 +call test 6 ROTL2 007 +call test 7 ROTL3 010 +call test 10 ROTL4 011 +call test 11 ROTL5 012 +call test 12 ROTR1 013 +call test 13 ROTR2 014 +call test 14 ROTR3 015 +call test 15 ROTR4 016 +call test 16 ROTR5 017 +call test 17 CLRTST 020 +call test 20 ADDONE 021 +call test 21 COMT1 022 +call test 22 SCRT1 023 +call test 23 SCRT2 024 +call test 24 SCRT3 025 +call test 25 SCRT4 027 +call test 26 ADDT1 031 +call test 27 FADRT1 032 +call test 30 FADRT2 033 +call test 31 iBETA1 035 +call test 32 iBETA2 036 +call test 33 iBETA3 037 +call test 34 iBETA4 040 +call test 35 LDAT1 041 +call test 36 STAT1 042 +call test 37 ADMT1 043 +call test 40 LAMT1 044 +call test 41 MULT1 045 +call test 42 SROT1 046 +call test 43 SETT1 047 +call test 44 SETT2 050 +call test 45 XSKT1 051 +call test 46 XSKT2 052 +call test 47 AZET1 053 +call test 50 APOT1 054 +call test 51 LZET1 055 +call test 52 HWCT1 056 +call test 53 HWCT2 057 +call test 54 HWCT3 060 +call test 55 HWCT4 061 +call test 56 HWCT5 062 +call test 57 RANADD 063 +call test 60 ATRT1 064 +call test 61 IBZT1 065 +call test 62 JMPUP 066 +call test 63 JMPDWN 067 +call test 64 TAPETS 070 +call test 65 MTBTST 101 +call test 66 DISTST 102 +call test 67 DSCTST 103 +call test 700 OVFT1 104 +call test 701 ZTAT1 105 +call test 702 ZCLR1 106 +call test 703 ZCLRT2 107 +deposit INTREQ 1 +call test 704 ENIT1 110 +call test 71 MISCTS 111 echo DIAGNOSTICS PASSED quit :test echo *** Test: %1 - %2 *** -load classic-test.linc block=%3 start=400 length=400 +load -e classic-test.linc block=%3 start=400 length=400 assert 400==%1 -deposit 21 1%4 +deposit 21 1%3 go 401 assert P==34 return diff --git a/linc/tests/linc_test.ini b/linc/tests/linc_test.ini index 2aa23d1f..7fede77e 100644 --- a/linc/tests/linc_test.ini +++ b/linc/tests/linc_test.ini @@ -1,4 +1,4 @@ -cd linc/tests +cd %~p0 set crt disabled reset do classic-test.do diff --git a/linc/tests/tty.do b/linc/tests/tty.do new file mode 100644 index 00000000..b70a3008 --- /dev/null +++ b/linc/tests/tty.do @@ -0,0 +1,53 @@ +SET CRT DISABLED +RESET +DELETE printer.txt +ATTACH TTY printer.txt +DEPOSIT 0020 RTA +DEPOSIT 0021 BSE i +DEPOSIT 0022 1 +DEPOSIT 0023 ATR +DEPOSIT 0024 SET i 12 +DEPOSIT 0025 -430 +DEPOSIT 0026 XSK i 12 +DEPOSIT 0027 JMP 26 +DEPOSIT 0030 SET i 1 +DEPOSIT 0031 57 +DEPOSIT 0032 SET i 2 +DEPOSIT 0033 -20 +DEPOSIT 0034 LDA i 1 +DEPOSIT 0035 STC 44 +DEPOSIT 0036 RTA +DEPOSIT 0037 SET i 11 +DEPOSIT 0040 -13 +DEPOSIT 0041 BCL i +DEPOSIT 0042 1 +DEPOSIT 0043 SRO i +DEPOSIT 0045 ADD 42 +DEPOSIT 0046 ATR +DEPOSIT 0047 SET i 12 +DEPOSIT 0050 -430 +DEPOSIT 0051 XSK i 12 +DEPOSIT 0052 JMP 51 +DEPOSIT 0053 XSK i 11 +DEPOSIT 0054 JMP 41 +DEPOSIT 0055 XSK i 2 +DEPOSIT 0056 JMP 34 +DEPOSIT 0057 HLT +DEPOSIT 0060 3220 +DEPOSIT 0061 3212 +DEPOSIT 0062 3230 +DEPOSIT 0063 3230 +DEPOSIT 0064 3236 +DEPOSIT 0065 3100 +DEPOSIT 0066 3250 +DEPOSIT 0067 3212 +DEPOSIT 0070 3230 +DEPOSIT 0071 3212 +DEPOSIT 0072 3250 +DEPOSIT 0073 3262 +DEPOSIT 0074 3240 +DEPOSIT 0075 3212 +DEPOSIT 0076 3032 +DEPOSIT 0077 3024 +GO 20 +QUIT diff --git a/linc/tests/tty.lap6 b/linc/tests/tty.lap6 new file mode 100644 index 00000000..fefd4cdd --- /dev/null +++ b/linc/tests/tty.lap6 @@ -0,0 +1,53 @@ + @20 + RTA + BSE i [MARK STATE + 1 + ATR + SET i 12 [GIVE TTY TIME + -430 + XSK i 12 + JMP p-1 + SET i 1 [TEXT DATA + 2A-1 + SET i 2 [TEXT LENGTH + 2A-2B +#1A LDA i 1 [GET NEXT CHARACTER + STC 1D + RTA + SET i 11 [OUTPUT 11 BITS + -13 [1 START, 8 DATA +#1B BCL i [AND 2 STOP +#1C 1 + SRO i +#1D + ADD 1C + ATR [OUTPUT BIT TO RELAY + SET i 12 + -430 [DELAY FOR 110 BAUD + XSK i 12 + JMP p-1 + XSK i 11 + JMP 1B [NEXT BIT + XSK i 2 + JMP 1A [NEXT CHARACTER + HLT + + [ASCII TEXT WITH ONE 0 START + [BIT AND TWO 1 STOP BITS +#2A 3220 [H + 3212 [E + 3230 [L + 3230 [L + 3236 [O + 3100 [ + 3250 [T + 3212 [E + 3230 [L + 3212 [E + 3250 [T + 3262 [Y + 3240 [P + 3212 [E + 3032 [CR + 3024 [LF +#2B