commit 9af6fd21e700826c6785e901c279a4ed0c6902dd Author: Bob Supnik Date: Tue Nov 6 20:44:00 2001 -0800 simh v2.5 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..069c6644 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.jpg binary +*.sln binary +*.vcproj binary +*.exe binary +*.bin binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f20c730c --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +#ignore thumbnails created by windows +Thumbs.db +#Ignore files built by Visual Studio +*.obj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +[Bb]in +[Dd]ebug*/ +*.lib +*.sbr +*.bak +obj/ +[Rr]elease*/ +_ReSharper*/ +[Tt]est[Rr]esult* +*.o +BIN/ +ipch/ diff --git a/hp2100_cpu.c b/hp2100_cpu.c new file mode 100644 index 00000000..1987a14f --- /dev/null +++ b/hp2100_cpu.c @@ -0,0 +1,1454 @@ +/* hp2100_cpu.c: HP 2100 CPU simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-Oct-00 RMS Added dynamic device number support + + The register state for the HP 2100 CPU is: + + AR<15:0> A register - addressable as location 0 + BR<15:0> B register - addressable as location 1 + PC<14:0> P register (program counter) + SR<15:0> S register + E extend flag (carry out) + O overflow flag + + The 21MX adds a pair of index registers: + + XR<15:0> X register + YR<15:0> Y register + + The original HP 2116 has four instruction formats: memory reference, + shift, alter/skip, and I/O. The HP 2100 added extended memory reference + and extended arithmetic. The HP21MX added extended byte, bit, and word + instructions as well as extended memory. + + The memory reference format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| op |cp| offset | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <14:11> mnemonic action + + 0010 AND A = A & M[MA] + 0011 JSB M[MA] = P, P = MA + 1 + 0100 XOR A = A ^ M[MA] + 0101 JMP P = MA + 0110 IOR A = A | M[MA] + 0111 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 1000 ADA A = A + M[MA] + 1001 ADB B = B + M[MA] + 1010 CPA skip if A != M[MA] + 1011 CPB skip if B != M[MA] + 1100 LDA A = M[MA] + 1101 LDB B = M[MA] + 1110 STA M[MA] = A + 1111 STB M[MA] = B + + <15,10> mode action + + 0,0 page zero direct MA = IR<9:0> + 0,1 current page direct MA = PC<14:0>'IR,9:0> + 1,0 page zero indirect MA = M[IR<9:0>] + 1,1 current page indirect MA = M[PC<14:10>'IR<9:0>] + + Memory reference instructions can access an address space of 32K words. + An instruction can directly reference the first 1024 words of memory + (called page zero), as well as 1024 words of the current page; it can + indirectly access all 32K. +*/ + +/* The shift format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0|ab| 0|s1| op1 |ce|s2|sl| op2 | shift + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | \---+---/ | | | \---+---/ + | | | | | | | + | | | | | | +---- shift 2 opcode + | | | | | +---------- skip if low bit == 0 + | | | | +------------- shift 2 enable + | | | +---------------- clear Extend + | | +---------------------- shift 1 opcode + | +---------------------------- shift 1 enable + +---------------------------------- A/B select + + The alter/skip format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0|ab| 1|regop| e op|se|ss|sl|in|sz|rs| alter/skip + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | \-+-/ \-+-/ | | | | | | + | | | | | | | | +- reverse skip sense + | | | | | | | +---- skip if register == 0 + | | | | | | +------- increment register + | | | | | +---------- skip if low bit == 0 + | | | | +------------- skip if sign bit == 0 + | | | +---------------- skip if Extend == 0 + | | +--------------------- clr/com/set Extend + | +--------------------------- clr/com/set register + +---------------------------------- A/B select + + The I/O transfer format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 0 0 0|ab| 1|hc| opcode | device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | \---+---/\-------+-------/ + | | | | + | | | +--------- device select + | | +---------------------- opcode + | +---------------------------- hold/clear flag + +---------------------------------- A/B select + + The IO transfer instruction controls the specified device. + Depending on the opcode, the instruction may set or clear + the device flag, start or stop I/O, or read or write data. +*/ + +/* The 2100 added an extended memory reference instruction; + the 21MX added extended arithmetic, operate, byte, word, + and bit instructions. Note that the HP 21xx is, despite + the right-to-left bit numbering, a big endian system. + Bits <15:8> are byte 0, and bits <7:0> are byte 1. + + + The extended memory reference format (HP 2100) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0|op| 0| opcode | extended mem ref + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended arithmetic format (HP 2100) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0 0 0|dr| 0 0| opcode |shift count| extended arithmetic + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended operate format (HP 21MX) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0|op| 0| 1 1 1 1 1| opcode | extended operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended byte and word format (HP 21MX) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0 1 0 1 1 1 1 1 1| opcode | extended byte/word + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended bit operate format (HP 21MX) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0 1 0 1 1 1 1 1 1 1| opcode | extended bit operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +*/ + +/* This routine is the instruction decode routine for the HP 2100. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + infinite indirection loop + unimplemented instruction and stop_inst flag set + unknown I/O device and stop_dev flag set + I/O error in I/O simulator + + 2. Interrupts. I/O devices are modelled as four parallel arrays: + + device commands as bit array dev_cmd[2][31..0] + device flags as bit array dev_flg[2][31..0] + device flag buffers as bit array dev_fbf[2][31..0] + device controls as bit array dev_ctl[2][31..0] + + The HP 2100 interrupt structure is based on flag, flag buffer,. + and control. If a device flag is set, the flag buffer is set, + the control bit is set, and the device is the highest priority + on the interrupt chain, it requests an interrupt. When the + interrupt is acknowledged, the flag buffer is cleared, preventing + further interrupt requests from that device. The combination of + flag and control set blocks interrupts from lower priority devices. + + Command plays no direct role in interrupts. The command flop + tells whether a device is active. It is set by STC and cleared + by CLC; it is also cleared when the device flag is set. Simple + devices don't need to track command separately from control. + + 3. Non-existent memory. On the HP 2100, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + hp2100_defs.h add interrupt request definition + hp2100_cpu.c add device information table entry + hp2100_sys.c add sim_devices table entry +*/ + +#include "hp2100_defs.h" + +#define ILL_ADR_FLAG 0100000 +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_MSIZE (UNIT_V_UF) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_V_2100 (UNIT_V_UF + 1) /* 2100 vs 2116 */ +#define UNIT_2100 (1 << UNIT_V_2100) +#define UNIT_V_21MX (UNIT_V_UF + 2) /* 21MX vs 2100 */ +#define UNIT_21MX (1 << UNIT_V_21MX) + +unsigned int16 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 saved_AR = 0; /* A register */ +int32 saved_BR = 0; /* B register */ +int32 PC = 0; /* P register */ +int32 SR = 0; /* S register */ +int32 XR = 0; /* X register */ +int32 YR = 0; /* Y register */ +int32 E = 0; /* E register */ +int32 O = 0; /* O register */ +int32 dev_cmd[2] = { 0 }; /* device command */ +int32 dev_ctl[2] = { 0 }; /* device control */ +int32 dev_flg[2] = { 0 }; /* device flags */ +int32 dev_fbf[2] = { 0 }; /* device flag bufs */ +struct DMA dmac[2] = { { 0 }, { 0 } }; /* DMA channels */ +int32 ion = 0; /* interrupt enable */ +int32 ion_defer = 0; /* interrupt defer */ +int32 intaddr = 0; /* interrupt addr */ +int32 mfence = 0; /* mem prot fence */ +int32 maddr = 0; /* mem prot err addr */ +int32 ind_max = 16; /* iadr nest limit */ +int32 stop_inst = 1; /* stop on ill inst */ +int32 stop_dev = 2; /* stop on ill dev */ +int32 ibkpt_addr = ILL_ADR_FLAG | AMASK; /* breakpoint addr */ +int32 old_PC = 0; /* previous PC */ + +extern int32 sim_int_char; +int32 shift (int32 inval, int32 flag, int32 oper); +int32 calc_dma (void); +int32 calc_int (void); +void dma_cycle (int32 chan); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat dma0_reset (DEVICE *dptr); +t_stat dma1_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK, + MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (P, PC, 15) }, + { ORDATA (A, saved_AR, 16) }, + { ORDATA (B, saved_BR, 16) }, + { ORDATA (X, XR, 16) }, + { ORDATA (Y, YR, 16) }, + { ORDATA (S, SR, 16) }, + { FLDATA (E, E, 0) }, + { FLDATA (O, O, 0) }, + { FLDATA (ION, ion, 0) }, + { FLDATA (ION_DEFER, ion_defer, 0) }, + { ORDATA (IADDR, intaddr, 6) }, + { FLDATA (MPCTL, dev_ctl[PRO/32], INT_V (PRO)) }, + { FLDATA (MPFLG, dev_flg[PRO/32], INT_V (PRO)) }, + { FLDATA (MPFBF, dev_fbf[PRO/32], INT_V (PRO)) }, + { ORDATA (MFENCE, mfence, 15) }, + { ORDATA (MADDR, maddr, 16) }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (STOP_DEV, stop_dev, 1) }, + { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, + { ORDATA (OLDP, old_PC, 15), REG_RO }, + { ORDATA (BREAK, ibkpt_addr, 16) }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (T2100, cpu_unit.flags, UNIT_V_2100), REG_HRO }, + { FLDATA (T21MX, cpu_unit.flags, UNIT_V_21MX), REG_HRO }, + { ORDATA (HCMD, dev_cmd[0], 32), REG_HRO }, + { ORDATA (LCMD, dev_cmd[1], 32), REG_HRO }, + { ORDATA (HCTL, dev_ctl[0], 32), REG_HRO }, + { ORDATA (LCTL, dev_ctl[1], 32), REG_HRO }, + { ORDATA (HFLG, dev_flg[0], 32), REG_HRO }, + { ORDATA (LFLG, dev_flg[1], 32), REG_HRO }, + { ORDATA (HFBF, dev_fbf[0], 32), REG_HRO }, + { ORDATA (LFBF, dev_fbf[1], 32), REG_HRO }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_2100 + UNIT_21MX, 0, "2116", "2116", NULL }, + { UNIT_2100 + UNIT_21MX, UNIT_2100, "2100", "2100", NULL }, + { UNIT_2100 + UNIT_21MX, UNIT_21MX, "21MX", "21MX", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +/* DMA controller data structures + + dmax_dev DMAx device descriptor + dmax_reg DMAx register list +*/ + +UNIT dma0_unit = { UDATA (NULL, 0, 0) }; + +REG dma0_reg[] = { + { FLDATA (CMD, dev_cmd[DMA0/32], INT_V (DMA0)) }, + { FLDATA (CTL, dev_ctl[DMA0/32], INT_V (DMA0)) }, + { FLDATA (FLG, dev_flg[DMA0/32], INT_V (DMA0)) }, + { FLDATA (FBF, dev_fbf[DMA0/32], INT_V (DMA0)) }, + { ORDATA (CW1, dmac[0].cw1, 16) }, + { ORDATA (CW2, dmac[0].cw2, 16) }, + { ORDATA (CW3, dmac[0].cw3, 16) } }; + +DEVICE dma0_dev = { + "DMA0", &dma0_unit, dma0_reg, NULL, + 1, 8, 1, 1, 8, 16, + NULL, NULL, &dma0_reset, + NULL, NULL, NULL }; + +UNIT dma1_unit = { UDATA (NULL, 0, 0) }; + +REG dma1_reg[] = { + { FLDATA (CMD, dev_cmd[DMA1/32], INT_V (DMA1)) }, + { FLDATA (CTL, dev_ctl[DMA1/32], INT_V (DMA1)) }, + { FLDATA (FLG, dev_flg[DMA1/32], INT_V (DMA1)) }, + { FLDATA (FBF, dev_fbf[DMA1/32], INT_V (DMA1)) }, + { ORDATA (CW1, dmac[1].cw1, 16) }, + { ORDATA (CW2, dmac[1].cw2, 16) }, + { ORDATA (CW3, dmac[1].cw3, 16) } }; + +DEVICE dma1_dev = { + "DMA1", &dma1_unit, dma1_reg, NULL, + 1, 8, 1, 1, 8, 16, + NULL, NULL, &dma1_reset, + NULL, NULL, NULL }; + +/* Extended instruction decode tables */ + +static const t_bool ext_addr[192] = { /* ext inst format */ + 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + +static const t_bool exg_breq[16] = { /* ext grp B only */ + 0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,0 }; + +static const t_bool exg_addr[32] = { /* ext grp format */ + 1,0,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,0,1,0,0,2,2,0,0,0,1,3,3,3,2,2 }; + +/* Interrupt defer table */ + +static const t_bool defer_tab[] = { 0, 1, 1, 1, 0, 0, 0, 1 }; + +/* Device dispatch table */ + +int32 devdisp (int32 devno, int32 inst, int32 IR, int32 outdat); +int32 cpuio (int32 op, int32 IR, int32 outdat); +int32 ovfio (int32 op, int32 IR, int32 outdat); +int32 pwrio (int32 op, int32 IR, int32 outdat); +int32 proio (int32 op, int32 IR, int32 outdat); +int32 dmsio (int32 op, int32 IR, int32 outdat); +int32 dmpio (int32 op, int32 IR, int32 outdat); +int32 nulio (int32 op, int32 IR, int32 outdat); + +int32 (*dtab[64])() = { + &cpuio, &ovfio, &dmsio, &dmsio, &pwrio, &proio, &dmpio, &dmpio, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + +/* Dynamic device information table */ + +extern int32 ptrio (int32 op, int32 IR, int32 outdat); +extern int32 ptpio (int32 op, int32 IR, int32 outdat); +extern int32 ttyio (int32 op, int32 IR, int32 outdat); +extern int32 clkio (int32 op, int32 IR, int32 outdat); +extern int32 lptio (int32 op, int32 IR, int32 outdat); +extern int32 mtdio (int32 op, int32 IR, int32 outdat); +extern int32 mtcio (int32 op, int32 IR, int32 outdat); +extern int32 dpdio (int32 op, int32 IR, int32 outdat); +extern int32 dpcio (int32 op, int32 IR, int32 outdat); + +struct hpdev infotab[] = { + { PTR, 0, 0, 0, 0, &ptrio }, + { PTP, 0, 0, 0, 0, &ptpio }, + { TTY, 0, 0, 0, 0, &ttyio }, + { CLK, 0, 0, 0, 0, &clkio }, + { LPT, 0, 0, 0, 0, &lptio }, + { MTD, 0, 0, 0, 0, &mtdio }, + { MTC, 0, 0, 0, 0, &mtcio }, + { DPD, 0, 0, 0, 0, &dpdio }, + { DPC, 0, 0, 0, 0, &dpcio }, + { 0 } }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 IR, MA, absel, i, t, intrq, dmarq; +int32 err_PC, M1, dev, iodata, op, sc, q, r, wc; +t_stat reason; + +#define DMAR0 1 +#define DMAR1 2 +#define SEXT(x) (((x) & SIGN)? (((int32) (x)) | ~DMASK): ((int32) (x))) +#define LDBY(a) ((M[(a) >> 1] >> (((a) & 1)? 0: 8)) & 0377) +#define STBY(a,d) MA = (a) >> 1; \ + MP_TEST (MA); \ + if (!MEM_ADDR_OK (MA)) break; \ + if ((a) & 1) M[MA] = (M[MA] & 0177400) | ((d) & 0377); \ + else M[MA] = (M[MA] & 0377) | (((d) & 0377) << 8) + +/* Memory protection tests */ + +#define MP_TEST(x) if (CTL (PRO) && ((x) > 1) && ((x) < mfence)) { \ + maddr = err_PC | 0100000; \ + setFLG (PRO); \ + intrq = PRO; \ + break; } + +#define MP_TESTJ(x) if (CTL (PRO) && ((x) < mfence)) { \ + maddr = err_PC | 0100000; \ + setFLG (PRO); \ + intrq = PRO; \ + break; } + +/* Restore register state */ + +AR = saved_AR & DMASK; /* restore reg */ +BR = saved_BR & DMASK; +err_PC = PC = PC & AMASK; /* load local PC */ +reason = 0; + +/* Restore I/O state */ + +for (i = VARDEV; i <= DEVMASK; i++) dtab[i] = NULL; +for (i = 0; infotab[i].devno != 0; i++) { /* loop thru dev */ + dev = infotab[i].devno; /* get dev # */ + if (infotab[i].ctl) { setCMD (dev); } /* restore cmd */ + else { clrCMD (dev); } + if (infotab[i].ctl) { setCTL (dev); } /* restore ctl */ + else { clrCTL (dev); } + if (infotab[i].flg) { setFLG (dev); } /* restore flg */ + else { clrFLG (dev); } + if (infotab[i].fbf) { setFBF (dev); } /* restore fbf */ + else { clrFBF (dev); } + dtab[dev] = infotab[i].iot; } /* set I/O dispatch */ +dmarq = calc_dma (); /* recalc DMA masks */ +intrq = calc_int (); /* recalc interrupts */ + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + dmarq = calc_dma (); /* recalc DMA reqs */ + intrq = calc_int (); } /* recalc interrupts */ + +if (dmarq) { + if (dmarq & DMAR0) dma_cycle (0); /* DMA1 cycle? */ + if (dmarq & DMAR1) dma_cycle (1); /* DMA2 cycle? */ + dmarq = calc_dma (); /* recalc DMA reqs */ + intrq = calc_int (); } /* recalc interrupts */ + +if (intrq && ((intrq <= PRO) || !ion_defer)) { /* interrupt request? */ + clrFBF (intrq); /* clear flag buffer */ + intaddr = intrq; /* save int addr */ + IR = M[intrq]; /* get dispatch instr */ + ion_defer = 1; /* defer interrupts */ + intrq = 0; /* clear request */ + clrCTL (PRO); } /* protection off */ + +else { if (PC == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save address */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + err_PC = PC; /* save PC for error */ + IR = M[PC]; /* fetch instr */ + PC = (PC + 1) & AMASK; + sim_interval = sim_interval - 1; + ion_defer = 0; } +absel = (IR & AB)? 1: 0; /* get A/B select */ + +/* Memory reference instructions */ + +if (IR & MROP) { /* mem ref? */ + MA = IR & (IA | DISP); /* ind + disp */ + if (IR & CP) MA = ((PC - 1) & PAGENO) | MA; /* current page? */ + for (i = 0; (i < ind_max) && (MA & IA); i++) /* resolve multi- */ + MA = M[MA & AMASK]; /* level indirect */ + if (i >= ind_max) { /* indirect loop? */ + reason = STOP_IND; + break; } + + switch ((IR >> 11) & 017) { /* decode IR<14:11> */ + case 002: /* AND */ + AR = AR & M[MA]; + break; + case 003: /* JSB */ + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = PC; + old_PC = PC; + PC = (MA + 1) & AMASK; + if (IR & IA) ion_defer = 1; + break; + case 004: /* XOR */ + AR = AR ^ M[MA]; + break; + case 005: /* JMP */ + MP_TESTJ (MA); + old_PC = PC; + PC = MA; + if (IR & IA) ion_defer = 1; + break; + case 006: /* IOR */ + AR = AR | M[MA]; + break; + case 007: /* ISZ */ + MP_TEST (MA); + t = (M[MA] + 1) & DMASK; + if (MEM_ADDR_OK (MA)) M[MA] = t; + if (t == 0) PC = (PC + 1) & AMASK; + break; + +/* Memory reference instructions, continued */ + + case 010: /* ADA */ + t = (int32) AR + (int32) M[MA]; + if (t > DMASK) E = 1; + if (((~AR ^ M[MA]) & (AR ^ t)) & SIGN) O = 1; + AR = t & DMASK; + break; + case 011: /* ADB */ + t = (int32) BR + (int32) M[MA]; + if (t > DMASK) E = 1; + if (((~BR ^ M[MA]) & (BR ^ t)) & SIGN) O = 1; + BR = t & DMASK; + break; + case 012: /* CPA */ + if (AR != M[MA]) PC = (PC + 1) & AMASK; + break; + case 013: /* CPB */ + if (BR != M[MA]) PC = (PC + 1) & AMASK; + break; + case 014: /* LDA */ + AR = M[MA]; + break; + case 015: /* LDB */ + BR = M[MA]; + break; + case 016: /* STA */ + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = AR; + break; + case 017: /* STB */ + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = BR; + break; } /* end case IR */ + } /* end if mem ref */ + +/* Alter/skip instructions */ + +else if ((IR & NMROP) == ASKP) { /* alter/skip? */ + int skip = 0; /* no skip */ + + if (IR & 000400) t = 0; /* CLx */ + else t = ABREG[absel]; + if (IR & 001000) t = t ^ DMASK; /* CMx */ + if (IR & 000001) { /* RSS? */ + if ((IR & 000040) && (E != 0)) skip = 1;/* SEZ,RSS */ + if (IR & 000100) E = 0; /* CLE */ + if (IR & 000200) E = E ^ 1; /* CME */ + if (((IR & 000030) == 000030) && /* SSx,SLx,RSS */ + (t == 0100001)) skip = 1; + if (((IR & 000030) == 000020) && /* SSx,RSS */ + ((t & SIGN) != 0)) skip = 1; + if (((IR & 000030) == 000010) && /* SLx,RSS */ + ((t & 1) != 0)) skip = 1; + if (IR & 000004) { /* INx */ + t = (t + 1) & DMASK; + if (t == 0) E = 1; + if (t == SIGN) O = 1; } + if ((IR & 000002) && (t != 0)) skip = 1;/* SZx,RSS */ + if ((IR & 000072) == 0) skip = 1; /* RSS */ + } /* end if RSS */ + else { if ((IR & 000040) && (E == 0)) skip = 1;/* SEZ */ + if (IR & 000100) E = 0; /* CLE */ + if (IR & 000200) E = E ^ 1; /* CME */ + if ((IR & 000020) && /* SSx */ + ((t & SIGN) == 0)) skip = 1; + if ((IR & 000010) && /* SLx */ + ((t & 1) == 0)) skip = 1; + if (IR & 000004) { /* INx */ + t = (t + 1) & DMASK; + if (t == 0) E = 1; + if (t == SIGN) O = 1; } + if ((IR & 000002) && (t == 0)) skip = 1;/* SZx */ + } /* end if ~RSS */ + ABREG[absel] = t; /* store result */ + PC = (PC + skip) & AMASK; /* add in skip */ + } /* end if alter/skip */ + +/* Shift instructions */ + +else if ((IR & NMROP) == SHFT) { /* shift? */ + t = shift (ABREG[absel], IR & 01000, IR >> 6); /* do first shift */ + if (IR & 000040) E = 0; /* CLE */ + if ((IR & 000010) && ((t & 1) == 0)) /* SLx */ + PC = (PC + 1) & AMASK; + ABREG[absel] = shift (t, IR & 00020, IR); /* do second shift */ + } /* end if shift */ + +/* I/O instructions */ + +else if ((IR & NMROP) == IOT) { /* I/O? */ + dev = IR & DEVMASK; /* get device */ + t = (IR >> 6) & 07; /* get subopcode */ + if (CTL (PRO) && ((t == ioHLT) || (dev != OVF))) { + maddr = err_PC | 0100000; + setFLG (PRO); + intrq = PRO; + break; } + iodata = devdisp (dev, t, IR, ABREG[absel]); /* process I/O */ + if ((t == ioMIX) || (t == ioLIX)) ABREG[absel] = iodata & DMASK; + if (t == ioHLT) reason = STOP_HALT; + else reason = iodata >> IOT_V_REASON; + ion_defer = defer_tab[t]; /* set defer */ + dmarq = calc_dma (); /* recalc DMA */ + intrq = calc_int (); /* recalc interrupts */ + } /* end if I/O */ + +/* Extended instructions */ + +else if (cpu_unit.flags & (UNIT_2100 | UNIT_21MX)) { /* ext instr? */ + register int32 awc; + + op = (IR >> 4) & 0277; /* get opcode */ + if (ext_addr[op]) { /* extended mem ref? */ + MA = M[PC]; /* get next address */ + PC = (PC + 1) & AMASK; + for (i = 0; (i < ind_max) && (MA & IA); i++) + MA = M[MA & AMASK]; + if (i >= ind_max) { + reason = STOP_IND; + break; } } + sc = (IR & 017); /* get shift count */ + if (sc == 0) sc = 16; + switch (op) { /* decode IR<11:4> */ + case 0010: /* MUL */ + t = SEXT (AR) * SEXT (M[MA]); + BR = (t >> 16) & DMASK; + AR = t & DMASK; + O = 0; + break; + case 0020: /* DIV */ + if ((M[MA] == 0) || /* divide by zero? */ + ((BR == SIGN) && (AR == 0) && (M[MA] == DMASK))) { + O = 1; /* set overflow */ + break; } + t = (SEXT (BR) << 16) || (int32) AR; + q = t / SEXT (M[MA]); /* quotient */ + r = t % SEXT (M[MA]); /* remainder */ + if ((q >= 077777) || (q < -0100000)) { /* quo too large? */ + if (BR & SIGN) { /* negative divd? */ + AR = (-AR) & DMASK; /* take abs value */ + BR = ((BR ^ DMASK) + (AR == 0)) & DMASK; } + O = 1; } + else { AR = q & DMASK; /* set quo, rem */ + BR = r & DMASK; + O = 0; } + break; + case 0210: /* DLD */ + AR = M[MA]; + MA = (MA + 1) & AMASK; + BR = M[MA]; + break; + case 0220: /* DST */ + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = AR; + MA = (MA + 1) & AMASK; + if (MEM_ADDR_OK (MA)) M[MA] = BR; + break; + +/* Extended arithmetic instructions */ + + case 0001: /* ASL */ + t = (SEXT (BR) >> (16 - sc)) & DMASK; + if (t != ((BR & SIGN)? DMASK: 0)) O = 1; + BR = (BR & SIGN) || ((BR << sc) & 077777) || + (AR >> (16 - sc)); + AR = (AR << sc) & DMASK; + break; + case 0002: /* LSL */ + BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; + AR = (AR << sc) & DMASK; + break; + case 0004: /* RRL */ + t = BR; + BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; + AR = ((AR << sc) | (t >> (16 - sc))) & DMASK; + break; + case 0041: /* ASR */ + AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; + BR = (SEXT (BR) >> sc) & DMASK; + break; + case 0042: /* LSR */ + AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; + BR = BR >> sc; + break; + case 0044: /* RRR */ + t = AR; + AR = ((AR >> sc) | (BR << (16 - sc))) & DMASK; + BR = ((BR >> sc) | (t << (16 - sc))) & DMASK; + break; + +/* Extended instruction group */ + + case 0076: /* ext inst grp, A */ + if (exg_breq[IR & 017]) { /* must have B set? */ + reason = stop_inst; + break; } + case 0276: case 0277: /* ext inst grp, B */ + if ((cpu_unit.flags & UNIT_21MX) == 0) { + reason = stop_inst; + break; } + op = IR & 037; /* get sub opcode */ + if (exg_addr[op]) { /* mem addr? */ + MA = M[PC]; /* get next address */ + PC = (PC + 1) & AMASK; + for (i = 0; (i < ind_max) && (MA & IA); i++) + MA = M[MA & AMASK]; + if (i >= ind_max) { + reason = STOP_IND; + break; } } + if (exg_addr[op] == 2) { /* word of zero? */ + wc = M[MA]; /* get count */ + if (M[PC] == 0) M[PC] = wc; /* save count */ + awc = PC; /* and addr */ + PC = (PC + 1) & AMASK; } + if (exg_addr[op] == 3) { /* second address? */ + M1 = M[PC]; /* get next address */ + PC = (PC + 1) & AMASK; + for (i = 0; (i < ind_max) && (M1 & IA); i++) + M1 = M[M1 & AMASK]; + if (i >= ind_max) { + reason = STOP_IND; + break; } } + switch (op) { /* case on sub op */ + t_bool cmpeql; + +/* Extended instruction group: index register instructions */ + + case 000: /* SAX, SBX */ + MA = (MA + XR) & AMASK; + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = ABREG[absel]; + break; + case 001: /* CAX, CBX */ + XR = ABREG[absel]; + break; + case 002: /* LAX, LBX */ + MA = (MA + XR) & AMASK; + ABREG[absel] = M[MA]; + break; + case 003: /* STX */ + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = XR; + break; + case 004: /* CXA, CXB */ + ABREG[absel] = XR; + break; + case 005: /* LDX */ + XR = M[MA]; + break; + case 006: /* ADX */ + t = XR + M[MA]; + if (t > DMASK) E = 1; + if (((~XR ^ M[MA]) & (XR ^ t)) & SIGN) O = 1; + XR = t & DMASK; + break; + case 007: /* XAX, XBX */ + t = XR; + XR = ABREG[absel]; + ABREG[absel] = t; + break; + case 010: /* SAY, SBY */ + MA = (MA + YR) & AMASK; + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = ABREG[absel]; + break; + case 011: /* CAY, CBY */ + YR = ABREG[absel]; + break; + case 012: /* LAY, LBY */ + MA = (MA + YR) & AMASK; + ABREG[absel] = M[MA]; + break; + case 013: /* STY */ + MP_TEST (MA); + if (MEM_ADDR_OK (MA)) M[MA] = YR; + break; + case 014: /* CYA, CYB */ + ABREG[absel] = YR; + break; + case 015: /* LDY */ + YR = M[MA]; + break; + case 016: /* ADY */ + t = YR + M[MA]; + if (t > DMASK) E = 1; + if (((~YR ^ M[MA]) & (YR ^ t)) & SIGN) O = 1; + YR = t & DMASK; + break; + case 017: /* XAY, XBY */ + t = YR; + YR = ABREG[absel]; + ABREG[absel] = t; + break; + case 020: /* ISX */ + XR = (XR + 1) & DMASK; + if (XR == 0) PC = (PC + 1) & AMASK; + break; + case 021: /* DSX */ + XR = (XR - 1) & DMASK; + if (XR == 0) PC = (PC + 1) & AMASK; + break; + case 022: /* JLY */ + MP_TESTJ (MA); + old_PC = PC; + YR = PC; + PC = MA; + break; + case 030: /* ISY */ + YR = (YR + 1) & DMASK; + if (YR == 0) PC = (PC + 1) & AMASK; + break; + case 031: /* DSY */ + YR = (YR - 1) & DMASK; + if (YR == 0) PC = (PC + 1) & AMASK; + break; + case 032: /* JPY */ + MA = (M[PC] + YR) & AMASK; /* no indirect */ + PC = (PC + 1) & AMASK; + MP_TESTJ (MA); + old_PC = PC; + PC = MA; + break; + +/* Extended instruction group: byte */ + + case 023: /* LBT */ + AR = LDBY (BR); + break; + case 024: /* SBT */ + STBY (BR, AR); + break; + case 025: /* MBT */ + while (M[awc]) { /* while count */ + q = LDBY (AR); /* load byte */ + STBY (BR, q); /* store byte */ + AR = (AR + 1) & DMASK; /* incr src */ + BR = (BR + 1) & DMASK; /* incr dst */ + M[awc] = (M[awc] - 1) & DMASK; } + break; + case 026: /* CBT */ + cmpeql = TRUE; + while (M[awc]) { /* while count */ + q = LDBY (AR); /* get src1 */ + r = LDBY (BR); /* get src2 */ + if (cmpeql && (q != r)) { /* compare */ + PC = (PC + 1 + (q > r)) & AMASK; + cmpeql = FALSE; } + AR = (AR + 1) & DMASK; + BR = (BR + 1) & DMASK; + M[awc] = (M[awc] - 1) & DMASK; } + break; + case 027: /* SFB */ + q = AR & 0377; /* test byte */ + r = (AR >> 8) & 0377; /* term byte */ + for (;;) { /* scan */ + t = LDBY (BR); /* get byte */ + if (t == q) break; /* test match? */ + BR = (BR + 1) & DMASK; + if (t == r) { /* term match? */ + PC = (PC + 1) & AMASK; + break; } } + break; + +/* Extended instruction group: bit, word */ + + case 033: /* SBS */ + MP_TEST (M1); + if (MEM_ADDR_OK (M1)) M[M1] = M[M1] | M[MA]; + break; + case 034: /* CBS */ + MP_TEST (M1); + if (MEM_ADDR_OK (M1)) M[M1] = M[M1] & ~M[MA]; + break; + case 035: /* TBS */ + if ((M[M1] & M[MA]) != M[MA]) PC = (PC + 1) & AMASK; + break; + case 036: /* CMW */ + cmpeql = TRUE; + while (M[awc]) { /* while count */ + q = SEXT (M[AR & AMASK]); + r = SEXT (M[BR & AMASK]); + if (cmpeql && (q != r)) { /* compare */ + PC = (PC + 1 + (q > r)) & AMASK; + cmpeql = FALSE; } + AR = (AR + 1) & DMASK; + BR = (BR + 1) & DMASK; + M[awc] = (M[awc] - 1) & DMASK; } + break; + case 037: /* MVW */ + while (M[awc]) { /* while count */ + MP_TEST (BR & AMASK); + if (MEM_ADDR_OK (BR & AMASK)) + M[BR & AMASK] = M[AR & AMASK]; + BR = (BR + 1) & DMASK; + AR = (AR + 1) & DMASK; + M[awc] = (M[awc] - 1) & DMASK; } + break; } /* end ext group */ + +/* Floating point instructions */ + + case 0240: /* FAD */ + case 0241: /* FSB */ + case 0242: /* FMP */ + case 0243: /* FDV */ + case 0244: /* FIX */ + case 0245: /* FLT */ + default: + reason = stop_inst; } /* end switch IR */ + } /* end if extended */ +} /* end while */ + +/* Simulation halted */ + +saved_AR = AR & DMASK; +saved_BR = BR & DMASK; +for (i = 0; infotab[i].devno != 0; i++) { /* save dynamic info */ + dev = infotab[i].devno; + infotab[i].ctl = CMD (dev); + infotab[i].ctl = CTL (dev); + infotab[i].flg = FLG (dev); + infotab[i].fbf = FBF (dev); } +dev_flg[0] = dev_flg[0] & M_FXDEV; /* clear dynamic info */ +dev_fbf[0] = dev_fbf[0] & M_FXDEV; +dev_ctl[0] = dev_ctl[0] & M_FXDEV; +dev_flg[1] = dev_fbf[1] = dev_ctl[1] = 0; +return reason; +} + +/* Shift micro operation */ + +int32 shift (int32 t, int32 flag, int32 op) +{ +int32 oldE; + +op = op & 07; /* get shift op */ +if (flag) { /* enabled? */ + switch (op) { /* case on operation */ + case 00: /* signed left shift */ + return ((t & SIGN) | ((t << 1) & 077777)); + case 01: /* signed right shift */ + return ((t & SIGN) | (t >> 1)); + case 02: /* rotate left */ + return (((t << 1) | (t >> 15)) & DMASK); + case 03: /* rotate right */ + return (((t >> 1) | (t << 15)) & DMASK); + case 04: /* left shift, 0 sign */ + return ((t << 1) & 077777); + case 05: /* ext right rotate */ + oldE = E; + E = t & 1; + return ((t >> 1) | (oldE << 15)); + case 06: /* ext left rotate */ + oldE = E; + E = (t >> 15) & 1; + return (((t << 1) | oldE) & DMASK); + case 07: /* rotate left four */ + return (((t << 4) | (t >> 12)) & DMASK); + } /* end case */ + } /* end if */ +if (op == 05) E = t & 1; /* disabled ext rgt rot */ +if (op == 06) E = (t >> 15) & 1; /* disabled ext lft rot */ +return t; /* input unchanged */ +} + +/* Device dispatch */ + +int32 devdisp (int32 devno, int32 inst, int32 IR, int32 dat) +{ +if (dtab[devno]) return dtab[devno] (inst, IR, dat); +else return nulio (inst, IR, dat); +} + +/* Calculate DMA requests */ + +int32 calc_dma (void) +{ +int32 r = 0; + +if (CMD (DMA0) && dmac[0].cw3 && /* check DMA0 cycle */ + FLG (dmac[0].cw1 & DEVMASK)) r = r | DMAR0; +if (CMD (DMA0) && dmac[1].cw3 && /* check DMA1 cycle */ + FLG (dmac[1].cw1 & DEVMASK)) r = r | DMAR1; +return r; +} + +/* Calculate interrupt requests + + This routine takes into account all the relevant state of the + interrupt system: ion, dev_flg, dev_buf, and dev_ctl. + + 1. dev_flg & dev_ctl determines the end of the priority grant. + The break in the chain will occur at the first device for + which dev_flag & dev_ctl is true. This is determined by + AND'ing the set bits with their 2's complement; only the low + order (highest priority) bit will differ. 1 less than + that, or'd with the single set bit itself, is the mask of + possible interrupting devices. If ION is clear, only devices + 4 and 5 are eligible to interrupt. + 2. dev_flg & dev_ctl & dev_fbf determines the outstanding + interrupt requests. All three bits must be on for a device + to request an interrupt. This is the masked under the + result from #1 to determine the highest priority interrupt, + if any. + */ + +int32 calc_int (void) +{ +int32 j, lomask, mask[2], req[2]; + +lomask = dev_flg[0] & dev_ctl[0] & ~M_NXDEV; /* start chain calc */ +req[0] = lomask & dev_fbf[0]; /* calc requests */ +lomask = lomask & (-lomask); /* chain & -chain */ +mask[0] = lomask | (lomask - 1); /* enabled devices */ +req[0] = req[0] & mask[0]; /* highest request */ +if (ion) { /* ion? */ + if (lomask == 0) { /* no break in chn? */ + mask[1] = dev_flg[1] & dev_ctl[1]; /* do all devices */ + req[1] = mask[1] & dev_fbf[1]; + mask[1] = mask[1] & (-mask[1]); + mask[1] = mask[1] | (mask[1] - 1); + req[1] = req[1] & mask[1]; } + else req[1] = 0; } +else { req[0] = req[0] & (INT_M (PWR) | INT_M (PRO)); + req[1] = 0; } +if (req[0]) { /* if low request */ + for (j = 0; j < 32; j++) { /* find dev # */ + if (req[0] & INT_M (j)) return j; } } +if (req[1]) { /* if hi request */ + for (j = 0; j < 32; j++) { /* find dev # */ + if (req[1] & INT_M (j)) return (32 + j); } } +return 0; +} + +/* Device 0 (CPU) I/O routine */ + +int32 cpuio (int32 inst, int32 IR, int32 dat) +{ +int i; + +switch (inst) { /* case on opcode */ +case ioFLG: /* flag */ + ion = (IR & HC)? 0: 1; /* interrupts off/on */ + return dat; +case ioSFC: /* skip flag clear */ + if (!ion) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (ion) PC = (PC + 1) & AMASK; + return dat; +case ioLIX: /* load */ + dat = 0; /* returns 0 */ + break; +case ioCTL: /* control */ + if (IR & AB) { /* = CLC 06..77 */ + for (i = 6; i <= DEVMASK; i++) devdisp (i, inst, AB + i, 0); } + break; +default: + break; } +if (IR & HC) ion = 0; /* HC option */ +return dat; +} + +/* Device 1 (overflow) I/O routine */ + +int32 ovfio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag */ + O = (IR & HC)? 0: 1; /* clear/set overflow */ + return dat; +case ioSFC: /* skip flag clear */ + if (!O) PC = (PC + 1) & AMASK; + break; /* can clear flag */ +case ioSFS: /* skip flag set */ + if (O) PC = (PC + 1) & AMASK; + break; /* can clear flag */ +case ioMIX: /* merge */ + dat = dat | SR; + break; +case ioLIX: /* load */ + dat = SR; + break; +case ioOTX: /* output */ + SR = dat; + break; +default: + break; } +if (IR & HC) O = 0; /* HC option */ +return dat; +} + +/* Device 4 (power fail) I/O routine */ + +int32 pwrio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ +case ioMIX: /* merge */ + dat = dat | intaddr; + break; +case ioLIX: /* load */ + dat = intaddr; + break; +default: + break; } +return dat; +} + +/* Device 5 (memory protect) I/O routine */ + +int32 proio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ +case ioSFC: /* skip flag clear */ + if (FLG (PRO)) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + return dat; +case ioMIX: /* merge */ + dat = dat | maddr; + break; +case ioLIX: /* load */ + dat = maddr; + break; +case ioOTX: /* output */ + mfence = dat & AMASK; + break; +case ioCTL: /* control clear/set */ + if ((IR & AB) == 0) { /* STC */ + setCTL (PRO); + clrFLG (PRO); } + break; +default: + break; } +return dat; +} + +/* Devices 2,3 (secondary DMA) I/O routine */ + +int32 dmsio (int32 inst, int32 IR, int32 dat) +{ +int32 ch; + +ch = IR & 1; /* get channel num */ +switch (inst) { /* case on opcode */ +case ioMIX: /* merge */ + dat = dat | dmac[ch].cw3; + break; +case ioLIX: /* load */ + dat = dmac[ch].cw3; + break; +case ioOTX: /* output */ + if (CTL (DMALT0 + ch)) dmac[ch].cw3 = dat; + else dmac[ch].cw2 = dat; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { clrCTL (DMALT0 + ch); } /* CLC */ + else { setCTL (DMALT0 + ch); } /* STC */ + break; +default: + break; } +return dat; +} + +/* Devices 6,7 (primary DMA) I/O routine */ + +int32 dmpio (int32 inst, int32 IR, int32 dat) +{ +int32 ch, ddev; + +ch = IR & 1; /* get channel number */ +ddev = dmac[ch].cw1 & DEVMASK; /* get target device */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag */ + if ((IR & HC) == 0) { clrCMD (DMA0 + ch); } /* set -> abort */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (DMA0 + ch) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (DMA0 + ch) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioMIX: case ioLIX: /* load, merge */ + dat = DMASK; + break; +case ioOTX: /* output */ + dmac[ch].cw1 = dat; + break; +case ioCTL: /* control */ + if (IR & AB) { clrCTL (DMA0 + ch); } /* CLC: cmd unchgd */ + else { setCTL (DMA0 + ch); /* STC: set ctl, cmd */ + setCMD (DMA0 + ch); } + break; +default: + break; } +if (IR & HC) { clrFLG (DMA0 + ch); } /* HC option */ +return dat; +} + +/* DMA cycle routine */ + +void dma_cycle (int32 ch) +{ +int32 temp, dev, MA; + +dev = dmac[ch].cw1 & DEVMASK; /* get device */ +MA = dmac[ch].cw2 & AMASK; /* get mem addr */ +if (dmac[ch].cw2 & DMA2_OI) { /* input? */ + temp = devdisp (dev, ioLIX, HC + dev, 0); /* do LIA dev,C */ + if (MEM_ADDR_OK (MA)) M[MA] = temp & DMASK; } /* store data */ +else devdisp (dev, ioOTX, HC + dev, M[MA]); /* do OTA dev,C */ +dmac[ch].cw2 = (dmac[ch].cw2 & DMA2_OI) | ((dmac[ch].cw2 + 1) & AMASK); +dmac[ch].cw3 = (dmac[ch].cw3 + 1) & DMASK; /* incr wcount */ +if (dmac[ch].cw3) { /* more to do? */ + if (dmac[ch].cw1 & DMA1_STC) devdisp (dev, ioCTL, dev, 0); } +else { if (dmac[ch].cw1 & DMA1_CLC) devdisp (dev, ioCTL, AB + dev, 0); + else if ((dmac[ch].cw1 & DMA1_STC) && ((dmac[ch].cw2 & DMA2_OI) == 0)) + devdisp (dev, ioCTL, dev, 0); + setFLG (DMA0 + ch); /* set DMA flg */ + clrCMD (DMA0 + ch); } /* clr DMA cmd */ +return; +} + +/* Unimplemented device routine */ + +int32 nulio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ +case ioSFC: /* skip flag clear */ + PC = (PC + 1) & AMASK; + return (stop_dev << IOT_V_REASON) | dat; +case ioSFS: /* skip flag set */ + return (stop_dev << IOT_V_REASON) | dat; +default: + break; } +if (IR & HC) { clrFLG (IR & DEVMASK); } /* HC option */ +return (stop_dev << IOT_V_REASON) | dat; +} + +/* Reset routines */ + +t_stat cpu_reset (DEVICE *dptr) +{ +AR = BR = 0; +XR = YR = 0; +E = 0; +O = 0; +ion = ion_defer = 0; +clrCMD (PWR); +clrCTL (PWR); +clrFLG (PWR); +clrFBF (PWR); +clrCMD (PRO); +clrCTL (PRO); +clrFLG (PRO); +clrFBF (PRO); +mfence = 0; +maddr = 0; +return cpu_svc (&cpu_unit); +} + +t_stat dma0_reset (DEVICE *tptr) +{ +clrCMD (DMA0); +clrCTL (DMA0); +clrFLG (DMA0); +clrFBF (DMA0); +dmac[0].cw1 = dmac[0].cw2 = dmac[0].cw3 = 0; +return SCPE_OK; +} + +t_stat dma1_reset (DEVICE *tptr) +{ +clrCMD (DMA1); +clrCTL (DMA1); +clrFLG (DMA1); +clrFBF (DMA1); +dmac[1].cw1 = dmac[1].cw2 = dmac[1].cw3 = 0; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 d; + +if (addr >= MEMSIZE) return SCPE_NXM; +if (addr == 0) d = saved_AR; +else if (addr == 1) d = saved_BR; +else d = M[addr]; +if (vptr != NULL) *vptr = d & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (addr == 0) saved_AR = val & DMASK; +else if (addr == 1) saved_BR = val & DMASK; +else M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Breakpoint service */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Set device number */ + +extern char *read_line (char *ptr, int size, FILE *stream); +extern t_value get_uint (char *cptr, int radix, t_value max, t_stat *status); + +t_stat hp_setdev (UNIT *uptr, int32 ord) +{ +char cbuf[CBUFSIZE], *cptr; +int32 i, olddev, newdev; +t_stat r; + +olddev = infotab[ord].devno; +printf ("Device number: %-o ", olddev); +cptr = read_line (cbuf, CBUFSIZE, stdin); +if ((cptr == NULL) || (*cptr == 0)) return SCPE_OK; +newdev = get_uint (cptr, 8, DEVMASK, &r); +if (r != SCPE_OK) return r; +if (newdev < VARDEV) return SCPE_ARG; +for (i = 0; infotab[i].devno != 0; i++) { + if ((i != ord) && (newdev == infotab[i].devno)) + return SCPE_ARG; } +infotab[ord].devno = newdev; +return SCPE_OK; +} + +/* Set device number for data/control pair */ + +t_stat hp_setdev2 (UNIT *uptr, int32 ord) +{ +int32 olddev; +t_stat r; + +olddev = infotab[ord].devno; +if ((r = hp_setdev (uptr, ord)) != SCPE_OK) return r; +if (infotab[ord].devno == DEVMASK) { + infotab[ord].devno = olddev; + return SCPE_ARG; } +infotab[ord + 1].devno = infotab[ord].devno + 1; +return SCPE_OK; +} diff --git a/hp2100_defs.h b/hp2100_defs.h new file mode 100644 index 00000000..2a5845be --- /dev/null +++ b/hp2100_defs.h @@ -0,0 +1,173 @@ +/* hp2100_defs.h: HP 2100 simulator definitions + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-Oct-00 RMS Added dynamic device numbers + 14-Apr-99 RMS Changed t_addr to unsigned + + The author gratefully acknowledges the help of Jeff Moffat in answering + questions about the HP2100. +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_IODV 2 /* must be 2 */ +#define STOP_HALT 3 /* HALT */ +#define STOP_IBKPT 4 /* breakpoint */ +#define STOP_IND 5 /* indirect loop */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define AMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE) + +/* Machine architecture */ + +#define SIGN 0100000 /* sign */ +#define DMASK 0177777 /* data mask */ +#define AR M[0] /* A = location 0 */ +#define BR M[1] /* B = location 1 */ +#define ABREG M /* register array */ + +/* Memory reference instructions */ + +#define IA 0100000 /* indirect address */ +#define MROP 0070000 /* opcode */ +#define AB 0004000 /* A/B select */ +#define CP 0002000 /* current page */ +#define DISP 0001777 /* page displacement */ +#define PAGENO 0076000 /* page number */ + +/* Other instructions */ + +#define NMROP 0102000 /* non-mrf opcode */ +#define SHFT 0000000 /* shift */ +#define ASKP 0002000 /* alter/skip */ +#define XTND 0100000 /* extend */ +#define IOT 0102000 /* I/O */ +#define HC 0001000 /* hold/clear */ +#define DEVMASK 0000077 /* device mask */ + +/* DMA channels */ + +#define DMA1_STC 0100000 /* DMA - issue STC */ +#define DMA1_CLC 0020000 /* DMA - issue CLC */ +#define DMA2_OI 0100000 /* DMA - output/input */ + +struct DMA { /* DMA channel */ + int32 cw1; /* device select */ + int32 cw2; /* direction, address */ + int32 cw3; /* word count */ +}; + +/* I/O macros */ + +#define INT_V(x) ((x) & 037) /* device bit pos */ +#define INT_M(x) (1u << INT_V (x)) /* device bit mask */ +#define setCMD(D) dev_cmd[(D)/32] = dev_cmd[(D)/32] | INT_M ((D)) +#define clrCMD(D) dev_cmd[(D)/32] = dev_cmd[(D)/32] & ~INT_M (D) +#define setCTL(D) dev_ctl[(D)/32] = dev_ctl[(D)/32] | INT_M ((D)) +#define clrCTL(D) dev_ctl[(D)/32] = dev_ctl[(D)/32] & ~INT_M (D) +#define setFBF(D) dev_fbf[(D)/32] = dev_fbf[(D)/32] | INT_M (D) +#define clrFBF(D) dev_fbf[(D)/32] = dev_fbf[(D)/32] & ~INT_M (D) +#define setFLG(D) dev_flg[(D)/32] = dev_flg[(D)/32] | INT_M (D); \ + setFBF(D) +#define clrFLG(D) dev_flg[(D)/32] = dev_flg[(D)/32] & ~INT_M (D); \ + clrFBF(D) +#define CMD(D) ((dev_cmd[(D)/32] >> INT_V (D)) & 1) +#define CTL(D) ((dev_ctl[(D)/32] >> INT_V (D)) & 1) +#define FLG(D) ((dev_flg[(D)/32] >> INT_V (D)) & 1) +#define FBF(D) ((dev_fbf[(D)/32] >> INT_V (D)) & 1) + +#define IOT_V_REASON 16 +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* I/O instruction sub-opcodes */ + +#define ioHLT 0 /* halt */ +#define ioFLG 1 /* set/clear flag */ +#define ioSFC 2 /* skip on flag clear */ +#define ioSFS 3 /* skip on flag set */ +#define ioMIX 4 /* merge into A/B */ +#define ioLIX 5 /* load into A/B */ +#define ioOTX 6 /* output from A/B */ +#define ioCTL 7 /* set/clear control */ + +/* I/O devices - fixed assignments */ + +#define CPU 000 /* interrupt control */ +#define OVF 001 /* overflow */ +#define DMALT0 002 /* DMA 0 alternate */ +#define DMALT1 003 /* DMA 1 alternate */ +#define PWR 004 /* power fail */ +#define PRO 005 /* parity/mem protect */ +#define DMA0 006 /* DMA channel 0 */ +#define DMA1 007 /* DMA channel 1 */ +#define VARDEV (DMA1 + 1) /* start of var assign */ +#define M_NXDEV (INT_M (CPU) | INT_M (OVF) | \ + INT_M (DMALT0) | INT_M (DMALT1)) +#define M_FXDEV (M_NXDEV | INT_M (PWR) | INT_M (PRO) | \ + INT_M (DMA0) | INT_M (DMA1)) + +/* I/O devices - variable assignment defaults */ + +#define PTR 010 /* paper tape reader */ +#define TTY 011 /* console */ +#define PTP 012 /* paper tape punch */ +#define CLK 013 /* clock */ +#define LPT 014 /* line printer */ +#define MTD 020 /* mag tape data */ +#define MTC 021 /* mag tape control */ +#define DPD 022 /* disk pack data */ +#define DPC 023 /* disk pack control */ + +/* Dynamic device information table */ + +struct hpdev { + int32 devno; /* device number */ + int32 cmd; /* saved command */ + int32 ctl; /* saved control */ + int32 flg; /* saved flag */ + int32 fbf; /* saved flag buf */ + int32 (*iot)(); /* I/O routine */ +}; + +/* Offsets in device information table */ + +#define inPTR 0 /* infotab ordinals */ +#define inPTP 1 +#define inTTY 2 +#define inCLK 3 +#define inLPT 4 +#define inMTD 5 +#define inMTC 6 +#define inDPD 7 +#define inDPC 8 + +#define UNIT_DEVNO (1 << UNIT_V_UF) /* dummy flag */ diff --git a/hp2100_dp.c b/hp2100_dp.c new file mode 100644 index 00000000..d2bc6407 --- /dev/null +++ b/hp2100_dp.c @@ -0,0 +1,602 @@ +/* hp2100_dp.c: HP 2100 disk pack simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dp 12557A cartridge disk system +*/ + +#include "hp2100_defs.h" + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_W_UF 2 /* # flags */ +#define FNC u3 /* saved function */ +#define CYL u4 /* cylinder */ + +#define DP_W_NUMWD 7 +#define DP_NUMWD (1 << DP_W_NUMWD) /* words/sector */ +#define DP_NUMSC 12 /* sectors/track */ +#define DP_NUMTR 203 /* tracks/surface */ +#define DP_NUMSF 4 /* surfaces/track */ +#define DP_SIZE (DP_NUMSF * DP_NUMTR * DP_NUMSC * DP_NUMWD) +#define DP_NUMDRV 4 /* # drives */ + +/* Command word */ + +#define CW_V_FNC 12 /* function */ +#define CW_M_FNC 017 +#define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC) +#define FNC_STA 000 /* status check */ +#define FNC_WD 001 /* write */ +#define FNC_RD 002 /* read */ +#define FNC_SEEK 003 /* seek */ +#define FNC_REF 005 /* refine */ +#define FNC_CHK 006 /* check */ +#define FNC_INIT 011 /* init */ +#define FNC_AR 013 /* address */ +#define FNC_SEEK1 020 /* fake - seek1 */ +#define FNC_SEEK2 021 /* fake - seek2 */ +#define FNC_CHK1 022 /* fake - check1 */ +#define FNC_AR1 023 /* fake - arec1 */ +#define CW_V_DRV 0 /* drive */ +#define CW_M_DRV 03 +#define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV) + +/* Disk address words */ + +#define DA_V_CYL 0 /* cylinder */ +#define DA_M_CYL 0377 +#define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) +#define DA_V_HD 8 /* head */ +#define DA_M_HD 03 +#define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD) +#define DA_V_SC 0 /* sector */ +#define DA_M_SC 017 +#define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC) + +/* Status */ + +#define STA_ATN 0100000 /* attention */ +#define STA_1ST 0040000 /* first seek */ +#define STA_OVR 0020000 /* overrun */ +#define STA_RWU 0010000 /* rw unsafe */ +#define STA_ACU 0004000 /* access unsafe */ +#define STA_HUNT 0002000 /* hunting */ +#define STA_SKI 0001000 /* incomplete */ +#define STA_SKE 0000400 /* seek error */ +/* 0000200 /* unused */ +#define STA_NRDY 0000100 /* not ready */ +#define STA_EOC 0000040 /* end of cylinder */ +#define STA_AER 0000020 /* addr error */ +#define STA_FLG 0000010 /* flagged */ +#define STA_BSY 0000004 /* seeking */ +#define STA_DTE 0000002 /* data error */ +#define STA_ERR 0000001 /* any error */ +#define STA_ALLERR (STA_ATN + STA_1ST + STA_OVR + STA_RWU + STA_ACU + \ + STA_HUNT + STA_SKI + STA_SKE + STA_NRDY + STA_EOC + \ + STA_FLG + STA_DTE) + +extern unsigned int16 M[]; +extern struct hpdev infotab[]; +extern int32 PC; +extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; +int32 dpc_busy = 0; /* cch busy */ +int32 dpc_cnt = 0; /* check count */ +int32 dpc_eoc = 0; /* end of cyl */ +int32 dpc_sta[DP_NUMDRV] = { 0 }; /* status regs */ +int32 dpc_stime = 10; /* seek time */ +int32 dpc_ctime = 10; /* command time */ +int32 dpc_xtime = 5; /* xfer time */ +int32 rarc = 0, rarh = 0, rars = 0; /* record addr */ +int32 dpd_obuf = 0, dpd_ibuf = 0; /* dch buffers */ +int32 dpc_obuf = 0; /* cch buffers */ +int32 dpbptr = 0; /* buffer ptr */ +unsigned int16 dbuf[DP_NUMWD]; /* sector buffer */ + +t_stat dpc_svc (UNIT *uptr); +t_stat dpc_reset (DEVICE *dptr); +t_stat dpc_vlock (UNIT *uptr, int32 val); +t_stat dpc_attach (UNIT *uptr, char *cptr); +t_stat dpc_detach (UNIT *uptr); +t_stat dpd_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat dpd_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +void dp_go (int32 fnc, int32 drv, int32 time, int32 attdev); +extern t_bool hp_setdev2 (UNIT *uptr, int32 val); + +/* DPD data structures + + dpd_dev DPD device descriptor + dpd_unit DPD unit list + dpd_reg DPD register list +*/ + +UNIT dpd_unit = { UDATA (NULL, UNIT_FIX, DP_NUMWD) }; + +REG dpd_reg[] = { + { ORDATA (IBUF, dpd_ibuf, 16) }, + { ORDATA (OBUF, dpd_obuf, 16) }, + { FLDATA (CMD, infotab[inDPD].cmd, 0) }, + { FLDATA (CTL, infotab[inDPD].ctl, 0) }, + { FLDATA (FLG, infotab[inDPD].flg, 0) }, + { FLDATA (FBF, infotab[inDPD].fbf, 0) }, + { DRDATA (BPTR, dpbptr, DP_W_NUMWD) }, + { ORDATA (DEVNO, infotab[inDPD].devno, 6), REG_RO }, + { NULL } }; + +DEVICE dpd_dev = { + "DPD", &dpd_unit, dpd_reg, NULL, + 1, 10, DP_W_NUMWD, 1, 8, 16, + &dpd_ex, &dpd_dep, &dpc_reset, + NULL, NULL, NULL }; + +/* DPC data structures + + dpc_dev DPC device descriptor + dpc_unit DPC unit list + dpc_reg DPC register list + dpc_mod DPC modifier list +*/ + +UNIT dpc_unit[] = { + { UDATA (&dpc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DP_SIZE) }, + { UDATA (&dpc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DP_SIZE) }, + { UDATA (&dpc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DP_SIZE) }, + { UDATA (&dpc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DP_SIZE) } }; + +REG dpc_reg[] = { + { ORDATA (OBUF, dpc_obuf, 16) }, + { ORDATA (BUSY, dpc_busy, 3), REG_RO }, + { ORDATA (RARC, rarc, 8) }, + { ORDATA (RARH, rarh, 2) }, + { ORDATA (RARS, rars, 4) }, + { ORDATA (CNT, dpc_cnt, 5) }, + { FLDATA (CMD, infotab[inDPC].cmd, 0) }, + { FLDATA (CTL, infotab[inDPC].ctl, 0) }, + { FLDATA (FLG, infotab[inDPC].flg, 0) }, + { FLDATA (FBF, infotab[inDPC].fbf, 0) }, + { FLDATA (EOC, dpc_eoc, 0) }, + { DRDATA (CTIME, dpc_ctime, 24), PV_LEFT }, + { DRDATA (STIME, dpc_stime, 24), PV_LEFT }, + { DRDATA (XTIME, dpc_xtime, 24), REG_NZ + PV_LEFT }, + { ORDATA (STA0, dpc_sta[0], 16) }, + { ORDATA (STA1, dpc_sta[1], 16) }, + { ORDATA (STA2, dpc_sta[2], 16) }, + { ORDATA (STA3, dpc_sta[3], 16) }, + { GRDATA (UFLG0, dpc_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (UFLG1, dpc_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (UFLG2, dpc_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (UFLG3, dpc_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { ORDATA (DEVNO, infotab[inDPC].devno, 6), REG_RO }, + { NULL } }; + +MTAB dpc_mod[] = { +/* { UNIT_WLK, 0, "write enabled", "ENABLED", &dpc_vlock }, */ +/* { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &dpc_vlock }, */ + { UNIT_DEVNO, inDPD, NULL, "DEVNO", &hp_setdev2 }, + { 0 } }; + +DEVICE dpc_dev = { + "DPC", dpc_unit, dpc_reg, dpc_mod, + DP_NUMDRV, 8, 24, 1, 8, 16, + NULL, NULL, &dpc_reset, + NULL, &dpc_attach, &dpc_detach }; + +/* IOT routines */ + +int32 dpdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (devd); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (devd) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (devd) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioOTX: /* output */ + dpd_obuf = dat; + break; +case ioMIX: /* merge */ + dat = dat | dpd_ibuf; + break; +case ioLIX: /* load */ + dat = dpd_ibuf; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { /* CLC */ + clrCTL (devd); /* clr ctl, cmd */ + clrCMD (devd); } + else { setCTL (devd); /* STC */ + setCMD (devd); } /* set ctl, cmd */ + break; +default: + break; } +if (IR & HC) { clrFLG (devd); } /* H/C option */ +return dat; +} + +int32 dpcio (int32 inst, int32 IR, int32 dat) +{ +int32 i, devc, fnc, drv; + +devc = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (devc); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (devc) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (devc) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioOTX: /* output */ + dpc_obuf = dat; + break; +case ioLIX: /* load */ + dat = 0; +case ioMIX: /* merge */ + for (i = 0; i < DP_NUMDRV; i++) + if (dpc_sta[i] & STA_ATN) dat = dat | (1 << i); + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { /* CLC? */ + clrCMD (devc); /* clr cmd, ctl */ + clrCTL (devc); /* cancel non-seek */ + if (dpc_busy) sim_cancel (&dpc_unit[dpc_busy - 1]); + dpc_busy = 0; } /* clr busy */ + else if (!CTL (devc)) { /* set and now clr? */ + setCMD (devc); /* set cmd, ctl */ + setCTL (devc); + drv = CW_GETDRV (dpc_obuf); /* get fnc, drv */ + fnc = CW_GETFNC (dpc_obuf); /* from cmd word */ + switch (fnc) { /* case on fnc */ + case FNC_SEEK: /* seek */ + dpc_sta[drv] = (dpc_sta[drv] | STA_BSY) & + ~(STA_SKE | STA_SKI | STA_HUNT | STA_1ST); + dp_go (fnc, drv, dpc_xtime, devc); + break; + case FNC_STA: case FNC_AR: /* rd sta, addr rec */ + dp_go (fnc, drv, dpc_xtime, 0); + break; + case FNC_CHK: /* check */ + dp_go (fnc, drv, dpc_xtime, devc); + break; + case FNC_REF: case FNC_RD: case FNC_WD: /* ref, read, write */ + dp_go (fnc, drv, dpc_ctime, devc); + break; + case FNC_INIT: /* init */ + dpc_sta[drv] = dpc_sta[drv] | STA_FLG; + setFLG (devc); /* set cch flg */ + clrCMD (devc); /* clr cch cmd */ + break; + } /* end case */ + } /* end else */ + break; +default: + break; } +if (IR & HC) { clrFLG (devc); } /* H/C option */ +return dat; +} + +/* Unit service + + Unit must be attached; detach cancels operation. + + Seek substates + seek - transfer cylinder + seek1 - transfer head/surface + seek2 - done + Address record + ar - transfer cylinder + ar1 - transfer head/surface, finish operation + Status check - transfer status, finish operation + Refine sector - erase sector, finish operation + Check data + chk - transfer sector count + chk1 - finish operation + Read + Write +*/ + +#define GETDA(x,y,z) \ + (((((x) * DP_NUMSF) + (y)) * DP_NUMSC) + (z)) * DP_NUMWD + +t_stat dpc_svc (UNIT *uptr) +{ +int32 i, da, drv, devc, devd, err, st, maxsc; + +err = 0; /* assume no err */ +drv = uptr - dpc_dev.units; /* get drive no */ +devc = infotab[inDPC].devno; /* get cch devno */ +devd = infotab[inDPD].devno; /* get dch devno */ +switch (uptr -> FNC) { /* case function */ + +case FNC_SEEK: /* seek, need cyl */ + if (CMD (devd)) { /* dch active? */ + rarc = DA_GETCYL (dpd_obuf); /* take cyl word */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + uptr -> FNC = FNC_SEEK1; } /* advance state */ + sim_activate (uptr, dpc_xtime); /* no, wait more */ + return SCPE_OK; +case FNC_SEEK1: /* seek, need hd/sec */ + if (CMD (devd)) { /* dch active? */ + rarh = DA_GETHD (dpd_obuf); /* get head */ + rars = DA_GETSC (dpd_obuf); /* get sector */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + st = abs (rarc - uptr -> CYL) * dpc_stime; /* calc cyl diff */ + if (st == 0) st = dpc_xtime; /* min time */ + sim_activate (uptr, st); /* schedule op */ + uptr -> CYL = rarc; /* on cylinder */ + dpc_busy = 0; /* ctrl is free */ + uptr -> FNC = FNC_SEEK2; } /* advance state */ + else sim_activate (uptr, dpc_xtime); /* no, wait more */ + return SCPE_OK; +case FNC_SEEK2: /* seek done */ + if (dpc_busy) sim_activate (uptr, dpc_xtime); /* ctrl busy? wait */ + else { dpc_sta[drv] = (dpc_sta[drv] | STA_ATN) & ~STA_BSY; + if (uptr -> CYL >= DP_NUMTR) { /* error? */ + dpc_sta[drv] = dpc_sta[drv] | STA_SKE; + uptr -> CYL = 0; } + setFLG (devc); /* set cch flg */ + clrCMD (devc); } /* clr cch cmd */ + return SCPE_OK; + +case FNC_AR: /* arec, need cyl */ + if (CMD (devd)) { /* dch active? */ + rarc = DA_GETCYL (dpd_obuf); /* take cyl word */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + uptr -> FNC = FNC_AR1; } /* advance state */ + sim_activate (uptr, dpc_xtime); /* no, wait more */ + return SCPE_OK; +case FNC_AR1: /* arec, need hd/sec */ + if (CMD (devd)) { /* dch active? */ + rarh = DA_GETHD (dpd_obuf); /* get head */ + rars = DA_GETSC (dpd_obuf); /* get sector */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); } /* clr dch cmd */ + else { sim_activate (uptr, dpc_xtime); /* no, wait more */ + return SCPE_OK; } + break; /* done */ + +case FNC_STA: /* read status */ + if (CMD (devd)) { /* dch active? */ + dpd_ibuf = dpc_sta[drv] | ((dpc_sta[drv] & STA_ALLERR)? STA_ERR: 0); + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + dpc_sta[drv] = dpc_sta[drv] & /* clr sta flags */ + ~(STA_ATN | STA_DTE | STA_FLG | STA_AER | STA_EOC); + dpc_busy = 0; } /* ctlr is free */ + else sim_activate (uptr, dpc_xtime); /* wait more */ + return SCPE_OK; + +case FNC_REF: /* refine sector */ + if ((uptr -> CYL != rarc) || (rars >= DP_NUMSC)) + dpc_sta[drv] = dpc_sta[drv] | STA_AER; + else { for (i = 0; i < DP_NUMWD; i++) dbuf[i] = 0; + da = GETDA (rarc, rarh, rars); /* get address */ + rars = rars + 1; /* incr sector */ + if (rars >= DP_NUMSC) { /* end of trk? */ + rars = 0; /* wrap to */ + rarh = rarh ^ 1; } /* next surf */ + if (err = fseek (uptr -> fileref, da * sizeof (int16), + SEEK_SET)) break; + fxwrite (dbuf, sizeof (int16), DP_NUMWD, uptr -> fileref); + err = ferror (uptr -> fileref); } + break; + +case FNC_CHK: /* check, need cnt */ + if (CMD (devd)) { /* dch active? */ + dpc_cnt = dpd_obuf & 037; /* get count */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dpc_ctime); /* schedule op */ + uptr -> FNC = FNC_CHK1; } /* advance state */ + else sim_activate (uptr, dpc_xtime); /* wait more */ + return SCPE_OK; +case FNC_CHK1: + if ((uptr -> CYL != rarc) || (rars >= DP_NUMSC)) + dpc_sta[drv] = dpc_sta[drv] | STA_AER; + else { maxsc = ((2 - (rarh & 1)) * DP_NUMSC) - rars; + if (dpc_cnt > maxsc) { /* too many sec? */ + dpc_sta[drv] = dpc_sta[drv] | STA_EOC; + rarh = rarh & ~1; /* rar = 0/2, 0 */ + rars = 0; } + else { i = rars + dpc_cnt; /* final sector */ + rars = i % DP_NUMSC; /* reposition */ + rarh = rarh ^ ((i / DP_NUMSC) & 1); } } + break; /* done */ + +case FNC_RD: /* read */ + if (!CMD (devd)) break; /* dch clr? done */ + if (FLG (devd)) dpc_sta[drv] = dpc_sta[drv] | STA_OVR; + if (dpbptr == 0) { /* new sector? */ + if ((uptr -> CYL != rarc) || (rars >= DP_NUMSC)) { + dpc_sta[drv] = dpc_sta[drv] | STA_AER; + break; } + if (dpc_eoc) { /* end of cyl? */ + dpc_sta[drv] = dpc_sta[drv] | STA_EOC; + break; } + da = GETDA (rarc, rarh, rars); /* get address */ + rars = rars + 1; /* incr address */ + if (rars >= DP_NUMSC) { /* end of trk? */ + rars = 0; /* wrap to */ + rarh = rarh ^ 1; /* next cyl */ + dpc_eoc = ((rarh & 1) == 0); } /* calc eoc */ + if (err = fseek (uptr -> fileref, da * sizeof (int16), + SEEK_SET)) break; + fxread (dbuf, sizeof (int16), DP_NUMWD, uptr -> fileref); + if (err = ferror (uptr -> fileref)) break; } + dpd_ibuf = dbuf[dpbptr++]; /* get word */ + if (dpbptr >= DP_NUMWD) dpbptr = 0; /* wrap if last */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dpc_xtime); /* sched next word */ + return SCPE_OK; + +case FNC_WD: /* write */ + if (dpc_eoc) { /* end of cyl? */ + dpc_sta[drv] = dpc_sta[drv] | STA_EOC; /* set status */ + break; } /* done */ + if (FLG (devd)) dpc_sta[drv] = dpc_sta[drv] | STA_OVR; + dbuf[dpbptr++] = dpd_obuf; /* store word */ + if (!CMD (devd)) { /* dch clr? done */ + for ( ; dpbptr < DP_NUMWD; dpbptr++) dbuf[dpbptr] = 0; } + if (dpbptr >= DP_NUMWD) { /* buffer full? */ + if ((uptr -> CYL != rarc) || (rars >= DP_NUMSC)) { + dpc_sta[drv] = dpc_sta[drv] | STA_AER; + break; } + da = GETDA (rarc, rarh, rars); /* get address */ + rars = rars + 1; /* incr address */ + if (rars >= DP_NUMSC) { /* end of trk? */ + rars = 0; /* wrap to */ + rarh = rarh ^ 1; /* next cyl */ + dpc_eoc = ((rarh & 1) == 0); } /* calc eoc */ + if (err = fseek (uptr -> fileref, da * sizeof (int16), + SEEK_SET)) return TRUE; + fwrite (dbuf, sizeof (int16), DP_NUMWD, uptr -> fileref); + if (err = ferror (uptr -> fileref)) break; + dpbptr = 0; } + if (CMD (devd)) { /* dch active? */ + setFLG (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dpc_xtime); /* sched next word */ + return SCPE_OK; } + break; } /* end case fnc */ + +dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* request attn */ +setFLG (devc); /* set cch flg */ +clrCMD (devc); /* clr cch cmd */ +dpc_busy = 0; /* ctlr is free */ +if (err != 0) { /* error? */ + perror ("DP I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; } +return SCPE_OK; +} + +/* Start disk operation */ + +void dp_go (int32 fnc, int32 drv, int32 time, int32 dev) +{ +if (dev && ((dpc_unit[drv].flags & UNIT_ATT) == 0)) { /* attach check? */ + dpc_sta[drv] = STA_NRDY; /* not attached */ + setFLG (dev); /* set cch flag */ + clrCMD (dev); } /* clr cch cmd */ +else { dpc_busy = drv + 1; /* set busy */ + dpbptr = 0; /* init buf ptr */ + dpc_eoc = 0; /* clear end cyl */ + dpc_unit[drv].FNC = fnc; /* save function */ + sim_activate (&dpc_unit[drv], time); } /* activate unit */ +return; +} + +/* Reset routine */ + +t_stat dpc_reset (DEVICE *dptr) +{ +int32 i; + +dpd_ibuf = dpd_obuf = 0; /* clear buffers */ +dpc_busy = dpc_obuf = 0; +dpc_eoc = 0; +dpbptr = 0; +rarc = rarh = rars = 0; /* clear rar */ +infotab[inDPC].cmd = infotab[inDPD].cmd = 0; /* clear cmd */ +infotab[inDPC].ctl = infotab[inDPD].ctl = 0; /* clear ctl */ +infotab[inDPC].fbf = infotab[inDPD].fbf = 0; /* clear flg */ +infotab[inDPC].flg = infotab[inDPD].flg = 0; /* clear fbf */ +for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */ + sim_cancel (&dpc_unit[i]); /* cancel activity */ + dpc_unit[i].FNC = 0; /* clear function */ + dpc_unit[i].CYL = 0; + dpc_sta[i] = (dpc_sta[i] & STA_1ST) | + ((dpc_unit[i].flags & UNIT_ATT)? 0: STA_NRDY); } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat dpc_attach (UNIT *uptr, char *cptr) +{ +int32 drv; +t_stat r; + +drv = uptr - dpc_dev.units; /* get drive no */ +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; +dpc_sta[drv] = (dpc_sta[drv] | STA_1ST) & ~STA_NRDY; /* update status */ +return r; +} + +/* Detach routine */ + +t_stat dpc_detach (UNIT* uptr) +{ +int32 drv; + +drv = uptr - dpc_dev.units; /* get drive no */ +dpc_sta[drv] = (dpc_sta[drv] | STA_NRDY) & ~STA_1ST; /* update status */ +if (drv == (dpc_busy + 1)) dpc_busy = 0; /* update busy */ +sim_cancel (uptr); /* cancel op */ +return detach_unit (uptr); /* detach unit */ +} + +/* Write lock/enable routine */ + +t_stat dpc_vlock (UNIT *uptr, int32 val) +{ +if (uptr -> flags & UNIT_ATT) return SCPE_ARG; +return SCPE_OK; +} + +/* Buffer examine */ + +t_stat dpd_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= DP_NUMWD) return SCPE_NXM; +if (vptr != NULL) *vptr = dbuf[addr] & DMASK; +return SCPE_OK; +} + +/* Buffer deposit */ + +t_stat dpd_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= DP_NUMWD) return SCPE_NXM; +dbuf[addr] = val & DMASK; +return SCPE_OK; +} \ No newline at end of file diff --git a/hp2100_lp.c b/hp2100_lp.c new file mode 100644 index 00000000..1cb45cc4 --- /dev/null +++ b/hp2100_lp.c @@ -0,0 +1,141 @@ +/* hp2100_lp.c: HP 2100 line printer simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt 12653A line printer + + 15-Oct-00 RMS Added variable device number support +*/ + +#include "hp2100_defs.h" + +#define LPT_BUSY 0000001 /* busy */ +#define LPT_NRDY 0100000 /* not ready */ + +extern int32 PC; +extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; +int32 lpt_ctime = 10; /* char time */ +int32 lpt_stopioe = 0; /* stop on error */ +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +extern struct hpdev infotab[]; +extern t_bool hp_setdev (UNIT *uptr, int32 val); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 7) }, + { FLDATA (CMD, infotab[inLPT].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inLPT].ctl, 0) }, + { FLDATA (FLG, infotab[inLPT].flg, 0) }, + { FLDATA (FBF, infotab[inLPT].fbf, 0) }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (CTIME, lpt_ctime, 31), PV_LEFT }, + { DRDATA (PTIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { ORDATA (DEVNO, infotab[inLPT].devno, 6), REG_RO }, + { NULL } }; + +MTAB lpt_mod[] = { + { UNIT_DEVNO, inLPT, NULL, "DEVNO", &hp_setdev }, + { 0 } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL }; + +/* Line printer IOT routine */ + +int32 lptio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (dev); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioOTX: /* output */ + lpt_unit.buf = dat & 0177; + break; +case ioLIX: /* load */ + dat = 0; /* default sta = 0 */ +case ioMIX: /* merge */ + if ((lpt_unit.flags & UNIT_ATT) == 0) dat = dat | LPT_BUSY | LPT_NRDY; + else if (sim_is_active (&lpt_unit)) dat = dat | LPT_BUSY; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { clrCTL (dev); } /* CLC */ + else { setCTL (dev); /* STC */ + sim_activate (&lpt_unit, /* schedule completion */ + (lpt_unit.buf < 040)? lpt_unit.wait: lpt_ctime); } + break; +default: + break; } +if (IR & HC) { clrFLG (dev); } /* H/C option */ +return dat; +} + +t_stat lpt_svc (UNIT *uptr) +{ +int32 dev; + +dev = infotab[inLPT].devno; /* get dev no */ +if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); +setFLG (dev); /* set flag */ +if (putc (lpt_unit.buf & 0177, lpt_unit.fileref) == EOF) { + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } +lpt_unit.pos = ftell (lpt_unit.fileref); /* update pos */ +return SCPE_OK; +} + +/* Reset routine - called from SCP, flags in infotab */ + +t_stat lpt_reset (DEVICE *dptr) +{ +infotab[inLPT].cmd = infotab[inLPT].ctl = 0; /* clear cmd, ctl */ +infotab[inLPT].flg = infotab[inLPT].fbf = 0; /* clear flg, fbf*/ +lpt_unit.buf = 0; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/hp2100_mt.c b/hp2100_mt.c new file mode 100644 index 00000000..585e2306 --- /dev/null +++ b/hp2100_mt.c @@ -0,0 +1,467 @@ +/* hp2100_mt.c: HP 2100 magnetic tape simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt 12559A nine track magnetic tape + + 04-Oct-98 RMS V2.4 magtape format + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "hp2100_defs.h" + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define DB_V_SIZE 16 /* max data buf */ +#define DBSIZE (1 << DB_V_SIZE) /* max data cmd */ +#define DBMASK (DBSIZE - 1) + +/* Command - mtc_fnc */ + +#define FNC_CLR 0300 /* clear */ +#define FNC_WC 0031 /* write */ +#define FNC_RC 0023 /* read */ +#define FNC_GAP 0011 /* write gap */ +#define FNC_FSR 0003 /* forward space */ +#define FNC_BSR 0041 /* backward space */ +#define FNC_REW 0201 /* rewind */ +#define FNC_RWS 0101 /* rewind and offline */ +#define FNC_WFM 0035 /* write file mark */ + +/* Status - stored in mtc_sta */ + +#define STA_LOCAL 0400 /* local */ +#define STA_EOF 0200 /* end of file */ +#define STA_BOT 0100 /* beginning of tape */ +#define STA_EOT 0040 /* end of tape */ +#define STA_TIM 0020 /* timing error */ +#define STA_REJ 0010 /* programming error */ +#define STA_WLK 0004 /* write locked */ +#define STA_PAR 0002 /* parity error */ +#define STA_BUSY 0001 /* busy */ + +extern unsigned int16 M[]; +extern struct hpdev infotab[]; +extern int32 PC; +extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; +int32 mtc_fnc = 0; /* function */ +int32 mtc_sta = 0; /* status register */ +int32 mtc_dtf = 0; /* data xfer flop */ +int32 mtc_1st = 0; /* first svc flop */ +int32 mtc_ctime = 1000; /* command wait */ +int32 mtc_xtime = 10; /* data xfer time */ +int32 mtc_stopioe = 1; /* stop on error */ +unsigned int8 dbuf[DBSIZE] = { 0 }; /* data buffer */ +t_mtrlnt mtbptr = 0, mtbmax = 0; /* buffer ptrs */ +static const int32 mtc_cmd[] = { + FNC_WC, FNC_RC, FNC_GAP, FNC_FSR, FNC_BSR, FNC_REW, FNC_RWS, FNC_WFM }; + +t_stat mtc_svc (UNIT *uptr); +t_stat mtc_reset (DEVICE *dptr); +t_stat mtc_vlock (UNIT *uptr, int32 val); +t_stat mtc_attach (UNIT *uptr, char *cptr); +t_stat mtc_detach (UNIT *uptr); +t_stat mtd_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat mtd_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +extern t_bool hp_setdev2 (UNIT *uptr, int32 val); + +/* MTD data structures + + mtd_dev MTD device descriptor + mtd_unit MTD unit list + mtd_reg MTD register list +*/ + +UNIT mtd_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, DBSIZE) }; + +REG mtd_reg[] = { + { FLDATA (CMD, infotab[inMTD].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inMTD].ctl, 0), REG_HRO }, + { FLDATA (FLG, infotab[inMTD].flg, 0) }, + { FLDATA (FBF, infotab[inMTD].fbf, 0), REG_HRO }, + { DRDATA (BPTR, mtbptr, DB_V_SIZE + 1) }, + { DRDATA (BMAX, mtbmax, DB_V_SIZE + 1) }, + { ORDATA (DEVNO, infotab[inMTD].devno, 6), REG_RO }, + { NULL } }; + +DEVICE mtd_dev = { + "MTD", &mtd_unit, mtd_reg, NULL, + 1, 10, 16, 1, 8, 8, + &mtd_ex, &mtd_dep, &mtc_reset, + NULL, NULL, NULL }; + +/* MTC data structures + + mtc_dev MTC device descriptor + mtc_unit MTC unit list + mtc_reg MTC register list + mtc_mod MTC modifier list +*/ + +UNIT mtc_unit = { UDATA (&mtc_svc, UNIT_ATTABLE, 0) }; + +REG mtc_reg[] = { + { ORDATA (FNC, mtc_fnc, 8) }, + { ORDATA (STA, mtc_sta, 9) }, + { ORDATA (BUF, mtc_unit.buf, 8) }, + { FLDATA (CMD, infotab[inMTC].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inMTC].ctl, 0) }, + { FLDATA (FLG, infotab[inMTC].flg, 0) }, + { FLDATA (FBF, infotab[inMTC].fbf, 0) }, + { FLDATA (DTF, mtc_dtf, 0) }, + { FLDATA (FSVC, mtc_1st, 0) }, + { DRDATA (POS, mtc_unit.pos, 31), PV_LEFT }, + { DRDATA (CTIME, mtc_ctime, 24), REG_NZ + PV_LEFT }, + { DRDATA (XTIME, mtc_xtime, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, mtc_stopioe, 0) }, + { FLDATA (WLK, mtc_unit.flags, UNIT_V_WLK), REG_HRO }, + { ORDATA (CDEVNO, infotab[inMTC].devno, 6), REG_RO }, + { NULL } }; + +MTAB mtc_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", &mtc_vlock }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &mtc_vlock }, + { UNIT_DEVNO, inMTD, NULL, "DEVNO", &hp_setdev2 }, + { 0 } }; + +DEVICE mtc_dev = { + "MTC", &mtc_unit, mtc_reg, mtc_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mtc_reset, + NULL, &mtc_attach, &mtc_detach }; + +/* IOT routines */ + +int32 mtdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (devd); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (devd) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (devd) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioOTX: /* output */ + mtc_unit.buf = dat & 0377; /* store data */ + break; +case ioMIX: /* merge */ + dat = dat | mtc_unit.buf; + break; +case ioLIX: /* load */ + dat = mtc_unit.buf; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) mtc_dtf = 0; /* CLC: clr xfer flop */ + break; +default: + break; } +if (IR & HC) { clrFLG (devd); } /* H/C option */ +return dat; +} + +int32 mtcio (int32 inst, int32 IR, int32 dat) +{ +int32 i, devc, devd, valid; + +devc = IR & DEVMASK; /* get device no */ +devd = devc - 1; +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (devc); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (devc) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (devc) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioOTX: /* output */ + dat = dat & 0377; + if (dat == FNC_CLR) { /* clear? */ + if (((mtc_fnc == FNC_REW) || (mtc_fnc == FNC_RWS)) && + sim_is_active (&mtc_unit)) break; + mtc_reset (&mtc_dev); /* if not rewind, */ + clrCTL (devc); /* init device */ + clrFLG (devc); + clrCTL (devd); + clrFLG (devd); + break; } + for (i = valid = 0; i < sizeof (mtc_cmd); i++) /* is fnc valid? */ + if (dat == mtc_cmd[i]) valid = 1; + if (!valid || sim_is_active (&mtc_unit) || /* is cmd valid? */ + ((mtc_unit.flags & UNIT_ATT) == 0) || + ((mtc_sta & STA_BOT) && + ((dat == FNC_BSR) || (dat == FNC_REW) || (dat == FNC_RWS))) || + ((mtc_unit.flags & UNIT_WLK) && + ((dat == FNC_WC) || (dat == FNC_GAP) || (dat == FNC_WFM)))) + mtc_sta = mtc_sta | STA_REJ; + else { sim_activate (&mtc_unit, mtc_ctime); /* start tape */ + mtc_fnc = dat; /* save function */ + mtc_sta = STA_BUSY; /* unit busy */ + mtbptr = 0; /* init buffer ptr */ + clrFLG (devc); /* clear flags */ + clrFLG (devd); + mtc_1st = 1; /* set 1st flop */ + mtc_dtf = 1; } /* set xfer flop */ + break; +case ioLIX: /* load */ + dat = 0; +case ioMIX: /* merge */ + if (mtc_unit.flags & UNIT_ATT) { /* construct status */ + mtc_sta = mtc_sta & ~(STA_LOCAL | STA_WLK | STA_BUSY); + if (sim_is_active (&mtc_unit)) mtc_sta = mtc_sta | STA_BUSY; + if (mtc_unit.flags & UNIT_WLK) mtc_sta = mtc_sta | STA_WLK; } + else mtc_sta = STA_BUSY | STA_LOCAL; + dat = dat | mtc_sta; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { clrCTL (devc); } /* CLC */ + else { setCTL (devc); } /* STC */ + break; +default: + break; } +if (IR & HC) { clrFLG (devc); } /* H/C option */ +return dat; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt + + Can't be write locked, can only write lock detached unit +*/ + +t_stat mtc_svc (UNIT *uptr) +{ +int32 devc, devd, err, i; +static t_mtrlnt bceof = { 0 }; + +if ((mtc_unit.flags & UNIT_ATT) == 0) { /* offline? */ + mtc_sta = STA_LOCAL | STA_BUSY | STA_REJ; + return IORETURN (mtc_stopioe, SCPE_UNATT); } +devc = infotab[inMTC].devno; /* get device nos */ +devd = infotab[inMTD].devno; + +err = 0; /* assume no errors */ +switch (mtc_fnc & 0377) { /* case on function */ +case FNC_REW: /* rewind */ + mtc_unit.pos = 0; /* BOT */ + setFLG (devc); /* set cch flg */ + mtc_sta = (mtc_sta | STA_BOT) & ~STA_BUSY; /* update status */ + break; +case FNC_RWS: /* rewind and offline */ + mtc_unit.pos = 0; /* BOT */ + mtc_sta = STA_LOCAL | STA_BUSY; /* set status */ + return detach_unit (uptr); /* don't set cch flg */ +case FNC_WFM: /* write file mark */ + fseek (mtc_unit.fileref, mtc_unit.pos, SEEK_SET); + fxwrite (&bceof, sizeof (t_mtrlnt), 1, mtc_unit.fileref); + err = ferror (mtc_unit.fileref); + mtc_unit.pos = mtc_unit.pos + sizeof (t_mtrlnt); /* update tape pos */ + mtc_sta = mtc_sta | STA_EOF; /* set EOF status */ +case FNC_GAP: /* erase gap */ + setFLG (devc); /* set cch flg */ + mtc_sta = mtc_sta & ~STA_BUSY; /* update status */ + break; + +/* Unit service, continued */ + +case FNC_FSR: /* space forward */ + setFLG (devc); /* set cch flg */ + mtc_sta = mtc_sta & ~STA_BUSY; /* update status */ + fseek (mtc_unit.fileref, mtc_unit.pos, SEEK_SET); + fxread (&mtbmax, sizeof (t_mtrlnt), 1, mtc_unit.fileref); + if ((err = ferror (mtc_unit.fileref)) || /* error or eof? */ + feof (mtc_unit.fileref)) mtc_sta = mtc_sta | STA_EOT; + else if (mtbmax == 0) { /* zero bc? */ + mtc_sta = mtc_sta | STA_EOF; /* EOF */ + mtc_unit.pos = mtc_unit.pos + sizeof (t_mtrlnt); } + else mtc_unit.pos = mtc_unit.pos + ((MTRL (mtbmax) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); /* update position */ + break; +case FNC_BSR: /* space reverse */ + setFLG (devc); /* set cch flg */ + mtc_sta = mtc_sta & ~STA_BUSY; /* update status */ + if (mtc_unit.pos == 0) { /* at BOT? */ + mtc_sta = mtc_sta | STA_BOT; /* update status */ + break; } + fseek (mtc_unit.fileref, mtc_unit.pos - sizeof (t_mtrlnt), SEEK_SET); + fxread (&mtbmax, sizeof (t_mtrlnt), 1, mtc_unit.fileref); + if ((err = ferror (mtc_unit.fileref)) || /* error or eof? */ + feof (mtc_unit.fileref)) mtc_unit.pos = 0; + else if (mtbmax == 0) { /* zero bc? */ + mtc_sta = mtc_sta | STA_EOF; /* EOF */ + mtc_unit.pos = mtc_unit.pos - sizeof (t_mtrlnt); } + else mtc_unit.pos = mtc_unit.pos - ((MTRL (mtbmax) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); /* update position */ + if (mtc_unit.pos == 0) mtc_sta = mtc_sta | STA_BOT; + break; + +/* Unit service, continued */ + +case FNC_RC: /* read */ + if (mtc_1st) { /* first svc? */ + mtc_1st = 0; /* clr 1st flop */ + fseek (mtc_unit.fileref, mtc_unit.pos, SEEK_SET); + fxread (&mtbmax, sizeof (t_mtrlnt), 1, mtc_unit.fileref); + if ((err = ferror (mtc_unit.fileref)) || + feof (mtc_unit.fileref)) { /* error or eof? */ + setFLG (devc); /* set cch flg */ + mtc_sta = (mtc_sta | STA_EOT) & ~STA_BUSY; + break; } + if (mtbmax == 0) { /* tape mark? */ + mtc_unit.pos = mtc_unit.pos + sizeof (t_mtrlnt); + setFLG (devc); /* set cch flg */ + mtc_sta = (mtc_sta | STA_EOF) & ~STA_BUSY; + break; } + mtbmax = MTRL (mtbmax); /* ignore errors */ + mtc_unit.pos = mtc_unit.pos + ((mtbmax + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); /* update position */ + if ((mtbmax > DBSIZE) || (mtbmax < 12)) { + setFLG (devc); /* set cch flg */ + mtc_sta = (mtc_sta | STA_PAR) & ~STA_BUSY; + break; } + i = fxread (dbuf, sizeof (int8), mtbmax, mtc_unit.fileref); + for ( ; i < mtbmax; i++) dbuf[i] = 0; /* fill with 0's */ + err = ferror (mtc_unit.fileref); } + if (mtbptr < mtbmax) { /* more chars? */ + if (FLG (devd)) mtc_sta = mtc_sta | STA_TIM; + mtc_unit.buf = dbuf[mtbptr++]; /* fetch next */ + setFLG (devd); /* set dch flg */ + sim_activate (uptr, mtc_xtime); } /* re-activate */ + else { setFLG (devc); /* set cch flg */ + mtc_sta = mtc_sta & ~STA_BUSY; } /* update status */ + break; +case FNC_WC: /* write */ + if (mtc_dtf) { /* xfer flop set? */ + if (!mtc_1st) { /* not first? */ + if (mtbptr < DBSIZE) /* room in buffer? */ + dbuf[mtbptr++] = mtc_unit.buf; + else mtc_sta = mtc_sta | STA_PAR; } + mtc_1st = 0; /* clr 1st flop */ + setFLG (devd); /* set dch flag */ + sim_activate (uptr, mtc_xtime); /* re-activate */ + break; } + if (mtbptr) { /* write buffer */ + fseek (mtc_unit.fileref, mtc_unit.pos, SEEK_SET); + fxwrite (&mtbptr, sizeof (t_mtrlnt), 1, mtc_unit.fileref); + fxwrite (dbuf, sizeof (int8), mtbptr, mtc_unit.fileref); + fxwrite (&mtbptr, sizeof (t_mtrlnt), 1, mtc_unit.fileref); + err = ferror (mtc_unit.fileref); + mtc_unit.pos = mtc_unit.pos + ((mtbptr + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); } + setFLG (devc); /* set cch flg */ + mtc_sta = mtc_sta & ~STA_BUSY; /* update status */ + break; } + +/* Unit service, continued */ + +if (err != 0) { /* I/O error */ + perror ("MT I/O error"); + clearerr (mtc_unit.fileref); + IORETURN (mtc_stopioe, SCPE_IOERR); } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mtc_reset (DEVICE *dptr) +{ +mtc_fnc = 0; +infotab[inMTC].cmd = infotab[inMTD].cmd = 0; /* clear cmd */ +infotab[inMTC].ctl = infotab[inMTD].ctl = 0; /* clear ctl */ +infotab[inMTC].flg = infotab[inMTD].flg = 0; /* clear flg */ +infotab[inMTC].fbf = infotab[inMTD].fbf = 0; /* clear fbf */ +sim_cancel (&mtc_unit); /* cancel activity */ +if (mtc_unit.flags & UNIT_ATT) mtc_sta = ((mtc_unit.pos)? 0: STA_BOT) | + ((mtc_unit.flags & UNIT_WLK)? STA_WLK: 0); +else mtc_sta = STA_LOCAL | STA_BUSY; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mtc_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* update status */ +mtc_sta = STA_BOT | ((uptr -> flags & UNIT_WLK)? STA_WLK: 0); +return r; +} + +/* Detach routine */ + +t_stat mtc_detach (UNIT* uptr) +{ +mtc_sta = STA_LOCAL | STA_BUSY; /* update status */ +return detach_unit (uptr); /* detach unit */ +} + +/* Write lock/enable routine */ + +t_stat mtc_vlock (UNIT *uptr, int32 val) +{ +if (uptr -> flags & UNIT_ATT) return SCPE_ARG; +return SCPE_OK; +} + +/* Buffer examine */ + +t_stat mtd_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= DBSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = dbuf[addr] & 0377; +return SCPE_OK; +} + +/* Buffer deposit */ + +t_stat mtd_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= DBSIZE) return SCPE_NXM; +dbuf[addr] = val & 0377; +return SCPE_OK; +} \ No newline at end of file diff --git a/hp2100_stddev.c b/hp2100_stddev.c new file mode 100644 index 00000000..d78d1937 --- /dev/null +++ b/hp2100_stddev.c @@ -0,0 +1,543 @@ +/* hp2100_stddev.c: HP2100 standard devices + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + tty console + clk 12639C time base generator + + 15-Oct-00 RMS Added dynamic device number support +*/ + +#include "hp2100_defs.h" +#include +#define UNIT_V_UC (UNIT_V_UF + 1) /* UC only */ +#define UNIT_UC (1 << UNIT_V_UC) +#define TM_MODE 0100000 /* mode change */ +#define TM_KBD 0040000 /* enable keyboard */ +#define TM_PRI 0020000 /* enable printer */ +#define TM_PUN 0010000 /* enable punch */ +#define CLK_V_ERROR 4 /* clock overrun */ +#define CLK_ERROR (1 << CLK_V_ERROR) + +extern unsigned int16 M[]; +extern int32 PC; +extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; +extern UNIT cpu_unit; +extern struct hpdev infotab[]; +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ +int32 ttp_stopioe = 0; +int32 tty_buf = 0, tty_mode = 0; /* tty buffer, mode */ +int32 clk_select = 0; /* clock time select */ +int32 clk_error = 0; /* clock error */ +int32 clk_delay[8] = /* clock intervals */ + { 50, 500, 5000, 50000, 500000, 5000000, 50000000, 50000000 }; +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tty_reset (DEVICE *dptr); +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); +extern t_bool hp_setdev (UNIT *uptr, int32 val); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_mod PTR modifiers + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (CMD, infotab[inPTR].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inPTR].ctl, 0) }, + { FLDATA (FLG, infotab[inPTR].flg, 0) }, + { FLDATA (FBF, infotab[inPTR].fbf, 0) }, + { DRDATA (POS, ptr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { ORDATA (DEVNO, infotab[inPTR].devno, 6), REG_RO }, + { NULL } }; + +MTAB ptr_mod[] = { + { UNIT_DEVNO, inPTR, NULL, "DEVNO", &hp_setdev }, + { 0 } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_mod PTP modifiers + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (CMD, infotab[inPTP].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inPTP].ctl, 0) }, + { FLDATA (FLG, infotab[inPTP].flg, 0) }, + { FLDATA (FBF, infotab[inPTP].fbf, 0) }, + { DRDATA (POS, ptp_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { ORDATA (DEVNO, infotab[inPTP].devno, 6), REG_RO }, + { NULL } }; + +MTAB ptp_mod[] = { + { UNIT_DEVNO, inPTP, NULL, "DEVNO", &hp_setdev }, + { 0 } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL }; + +/* TTY data structures + + tty_dev TTY device descriptor + tty_unit TTY unit descriptor + tty_reg TTY register list + tty_mod TTy modifiers list +*/ + +#define TTI 0 +#define TTO 1 +#define TTP 2 + +UNIT tty_unit[] = { + { UDATA (&tti_svc, UNIT_UC, 0), KBD_POLL_WAIT }, + { UDATA (&tto_svc, UNIT_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&tto_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT } }; + +REG tty_reg[] = { + { ORDATA (BUF, tty_buf, 8) }, + { ORDATA (MODE, tty_mode, 16) }, + { FLDATA (CMD, infotab[inTTY].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inTTY].ctl, 0) }, + { FLDATA (FLG, infotab[inTTY].flg, 0) }, + { FLDATA (FBF, infotab[inTTY].fbf, 0) }, + { DRDATA (KPOS, tty_unit[TTI].pos, 31), PV_LEFT }, + { DRDATA (KTIME, tty_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPOS, tty_unit[TTO].pos, 31), PV_LEFT }, + { DRDATA (TTIME, tty_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (PPOS, tty_unit[TTP].pos, 31), PV_LEFT }, + { FLDATA (STOP_IOE, ttp_stopioe, 0) }, + { ORDATA (DEVNO, infotab[inTTY].devno, 6), REG_RO }, + { FLDATA (UC, tty_unit[TTI].flags, UNIT_V_UC), REG_HRO }, + { NULL } }; + +MTAB tty_mod[] = { + { UNIT_UC, 0, "lower case", "LC", NULL }, + { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, + { UNIT_DEVNO, inTTY, NULL, "DEVNO", &hp_setdev }, + { 0 } }; + +DEVICE tty_dev = { + "TTY", tty_unit, tty_reg, tty_mod, + 3, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, NULL, NULL }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_mod CLK modifiers + clk_reg CLK register list +*/ + +UNIT clk_unit = { + UDATA (&clk_svc, 0, 0) }; + +REG clk_reg[] = { + { ORDATA (SEL, clk_select, 3) }, + { FLDATA (CMD, infotab[inCLK].cmd, 0), REG_HRO }, + { FLDATA (CTL, infotab[inCLK].ctl, 0) }, + { FLDATA (FLG, infotab[inCLK].flg, 0) }, + { FLDATA (FBF, infotab[inCLK].fbf, 0) }, + { FLDATA (ERR, clk_error, CLK_V_ERROR) }, + { DRDATA (TIME0, clk_delay[0], 31), PV_LEFT }, + { DRDATA (TIME1, clk_delay[1], 31), PV_LEFT }, + { DRDATA (TIME2, clk_delay[2], 31), PV_LEFT }, + { DRDATA (TIME3, clk_delay[3], 31), PV_LEFT }, + { DRDATA (TIME4, clk_delay[4], 31), PV_LEFT }, + { DRDATA (TIME5, clk_delay[5], 31), PV_LEFT }, + { DRDATA (TIME6, clk_delay[6], 31), PV_LEFT }, + { DRDATA (TIME7, clk_delay[7], 31), PV_LEFT }, + { ORDATA (DEVNO, infotab[inCLK].devno, 6), REG_RO }, + { NULL } }; + +MTAB clk_mod[] = { + { UNIT_DEVNO, inCLK, NULL, "DEVNO", &hp_setdev }, + { 0 } }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL }; + +/* Paper tape reader: IOT routine */ + +int32 ptrio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (dev); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioMIX: /* merge */ + dat = dat | ptr_unit.buf; + break; +case ioLIX: /* load */ + dat = ptr_unit.buf; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { clrCTL (dev); } /* CLC */ + else { setCTL (dev); /* STC */ + sim_activate (&ptr_unit, ptr_unit.wait); } + break; +default: + break; } +if (IR & HC) { clrFLG (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 dev, temp; + +dev = infotab[inPTR].devno; /* get device no */ +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* read byte */ + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; } +setFLG (dev); /* set flag */ +ptr_unit.buf = temp & 0377; /* put byte in buf */ +ptr_unit.pos = ftell (ptr_unit.fileref); +return SCPE_OK; +} + +/* Reset routine - called from SCP, flags in infotab */ + +t_stat ptr_reset (DEVICE *dptr) +{ +infotab[inPTR].cmd = infotab[inPTR].ctl = 0; /* clear cmd, ctl */ +infotab[inPTR].flg = infotab[inPTR].fbf = 0; /* clear flg, fbf */ +ptr_unit.buf = 0; +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape reader bootstrap routine */ + +#define CHANGE_DEV (1 << 24) +#define PBOOT_MASK 077 +#define PBOOT_SIZE (sizeof (pboot) / sizeof (int32)) + +static const int32 pboot[] = { +0107700, 0063770, 0106501, 0004010, +0002400, 0006020, 0063771, 0073736, +0006401, 0067773, 0006006, 0027717, +0107700, 0102077, 0027700, 0017762, +0002003, 0027712, 0003104, 0073774, +0017762, 0017753, 0070001, 0073775, +0063775, 0043772, 0002040, 0027751, +0017753, 0044000, 0000000, 0002101, +0102000, 0037775, 0037774, 0027730, +0017753, 0054000, 0027711, 0102011, +0027700, 0102055, 0027700, 0000000, +0017762, 0001727, 0073776, 0017762, +0033776, 0127753, 0000000, 0103700+CHANGE_DEV, +0102300+CHANGE_DEV, 0027764, 0102500+CHANGE_DEV, 0127762, +0173775, 0153775, 0170100, 0177765 }; + +t_stat ptr_boot (int32 unit) +{ +int32 i, dev; + +dev = infotab[inPTR].devno; /* get device no */ +PC = (MEMSIZE - 1) & ~PBOOT_MASK; /* start at mem top */ +for (i = 0; i < PBOOT_SIZE; i++) /* copy bootstrap */ + M[PC + i] = (pboot[i] & CHANGE_DEV)? /* insert ptr dev no */ + ((pboot[i] | dev) & DMASK): pboot[i]; +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptpio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (dev); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioLIX: /* load */ + dat = 0; + break; +case ioOTX: /* output */ + ptp_unit.buf = dat; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { clrCTL (dev); } /* CLC */ + else { setCTL (dev); /* STC */ + sim_activate (&ptp_unit, ptp_unit.wait); } + break; +default: + break; } +if (IR & HC) { clrFLG (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +int32 dev; + +dev = infotab[inPTP].devno; /* get device no */ +setFLG (dev); /* set device flag */ +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* output byte */ + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; } +ptp_unit.pos = ftell (ptp_unit.fileref); /* update position */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +infotab[inPTP].cmd = infotab[inPTP].ctl = 0; /* clear cmd, ctl */ +infotab[inPTP].flg = infotab[inPTP].fbf = 0; /* clear flg, fbf */ +ptp_unit.buf = 0; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Terminal: IOT routine */ + +int32 ttyio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (dev); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioMIX: /* merge */ + dat = dat | tty_buf; + break; +case ioLIX: /* load */ + dat = tty_buf; + break; +case ioOTX: /* output */ + if (dat & TM_MODE) tty_mode = dat; + else tty_buf = dat & 0377; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { clrCTL (dev); } /* CLC */ + else { setCTL (dev); /* STC */ + if (!(tty_mode & TM_KBD)) /* output? */ + sim_activate (&tty_unit[TTO], tty_unit[TTO].wait); } + break; +default: + break; } +if (IR & HC) { clrFLG (dev); } /* H/C option */ +return dat; +} + +/* Unit service routines */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp, dev; + +dev = infotab[inTTY].devno; /* get device no */ +sim_activate (&tty_unit[TTI], tty_unit[TTI].wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +temp = temp & 0177; +if ((tty_unit[TTO].flags & UNIT_UC) && islower (temp)) /* force upper case? */ + temp = toupper (temp); +if (tty_mode & TM_KBD) { /* keyboard enabled? */ + tty_buf = temp; /* put char in buf */ + tty_unit[TTI].pos = tty_unit[TTI].pos + 1; + setFLG (dev); /* set flag */ + if (tty_mode & TM_PRI) sim_putchar (temp); } /* echo? */ +return SCPE_OK; +} + +t_stat tto_svc (UNIT *uptr) +{ +int32 ch, ret, dev; + +dev = infotab[inTTY].devno; /* get device no */ +ret = SCPE_OK; /* assume ok */ +setFLG (dev); /* set done flag */ +ch = tty_buf; +tty_buf = 0377; /* defang buf */ +if (tty_mode & TM_PRI) { /* printing? */ + ret = sim_putchar (ch & 0177); /* output char */ + tty_unit[TTO].pos = tty_unit[TTO].pos + 1; } +if (tty_mode & TM_PUN) { /* punching? */ + if ((tty_unit[TTP].flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ttp_stopioe, SCPE_UNATT); + if (putc (ch, tty_unit[TTP].fileref) == EOF) { /* output char */ + perror ("TTP I/O error"); + clearerr (tty_unit[TTP].fileref); + return SCPE_IOERR; } + tty_unit[TTP].pos = ftell (tty_unit[TTP].fileref); } +return ret; +} + +/* Reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ +infotab[inTTY].cmd = infotab[inTTY].ctl = 0; /* clear cmd, ctl */ +infotab[inTTY].flg = infotab[inTTY].fbf = 0; /* clear flg, fbf */ +tty_mode = 0; +tty_buf = 0; +sim_activate (&tty_unit[TTI], tty_unit[TTI].wait); /* activate poll */ +sim_cancel (&tty_unit[TTO]); /* cancel output */ +return SCPE_OK; +} + +/* Clock: IOT routine */ + +int32 clkio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ +case ioFLG: /* flag clear/set */ + if ((IR & HC) == 0) { setFLG (dev); } /* STF */ + break; +case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & AMASK; + return dat; +case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & AMASK; + return dat; +case ioMIX: /* merge */ + dat = dat | clk_error; +case ioLIX: /* load */ + dat = clk_error; + break; +case ioOTX: /* output */ + clk_select = dat & 07; + break; +case ioCTL: /* control clear/set */ + if (IR & AB) { /* CLC */ + clrCTL (dev); /* turn off clock */ + sim_cancel (&clk_unit); } /* deactivate unit */ + else { setCTL (dev); /* turn on clock */ + clk_error = 0; /* clear error */ + sim_activate (&clk_unit, clk_delay[clk_select]); } + break; +default: + break; } +if (IR & HC) { clrFLG (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +int32 dev; + +dev = infotab[inCLK].devno; /* get device no */ +if (FLG (dev)) clk_error = CLK_ERROR; /* overrun? */ +setFLG (dev); /* set device flag */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +infotab[inCLK].cmd = infotab[inCLK].ctl = 0; /* clear cmd, ctl */ +infotab[inCLK].flg = infotab[inCLK].fbf = 0; /* clear flg, fbf */ +clk_error = 0; /* clear error */ +clk_select = 0; /* clear select */ +sim_cancel (&clk_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/hp2100_sys.c b/hp2100_sys.c new file mode 100644 index 00000000..3cb00d39 --- /dev/null +++ b/hp2100_sys.c @@ -0,0 +1,527 @@ +/* hp2100_sys.c: HP 2100 simulator interface + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added examine to file support + 15-Oct-00 RMS Added dynamic device number support + 27-Oct-98 RMS V2.4 load interface +*/ + +#include "hp2100_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE dma0_dev, dma1_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tty_dev, clk_dev, lpt_dev; +extern DEVICE mtd_dev, mtc_dev; +extern DEVICE dpd_dev, dpc_dev; +extern REG cpu_reg[]; +extern unsigned int16 M[]; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "HP 2100"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 3; + +DEVICE *sim_devices[] = { &cpu_dev, + &dma0_dev, &dma1_dev, + &ptr_dev, &ptp_dev, + &tty_dev, + &clk_dev, &lpt_dev, + &mtd_dev, &mtc_dev, + &dpd_dev, &dpc_dev, + NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "Non-existent I/O device", + "HALT instruction", + "Breakpoint", + "Indirect address loop" }; + +/* Binary loader + + The binary loader consists of blocks preceded and trailed by zero frames. + A block consists of 16b words (punched big endian), as follows: + + count'xxx + origin + word 0 + : + word count-1 + checksum + + The checksum includes the origin but not the count. +*/ + +int32 fgetw (FILE *fileref) +{ +int c1, c2; + +if ((c1 = fgetc (fileref)) == EOF) return -1; +if ((c2 = fgetc (fileref)) == EOF) return -1; +return ((c1 & 0377) << 8) | (c2 & 0377); +} + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 origin, csum, zerocnt, count, word, i; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +for (zerocnt = 1;; zerocnt = -10) { /* block loop */ + for (;; zerocnt++) { /* skip 0's */ + if ((count = fgetc (fileref)) == EOF) return SCPE_OK; + else if (count) break; + else if (zerocnt == 0) return SCPE_OK; } + if (fgetc (fileref) == EOF) return SCPE_FMT; + if ((origin = fgetw (fileref)) < 0) return SCPE_FMT; + csum = origin; /* seed checksum */ + for (i = 0; i < count; i++) { /* get data words */ + if ((word = fgetw (fileref)) < 0) return SCPE_FMT; + if (MEM_ADDR_OK (origin)) M[origin] = word; + origin = origin + 1; + csum = csum + word; } + if ((word = fgetw (fileref)) < 0) return SCPE_FMT; + if ((word ^ csum) & DMASK) return SCPE_CSUM; } +return SCPE_OK; +} + +/* Symbol tables */ + +#define I_V_FL 16 /* flag start */ +#define I_M_FL 017 /* flag mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_NPNC 1 /* no operand + C */ +#define I_V_MRF 2 /* mem ref */ +#define I_V_ASH 3 /* alter/skip, shift */ +#define I_V_ESHF 4 /* extended shift */ +#define I_V_EMRF 5 /* extended mem ref */ +#define I_V_IOT1 6 /* I/O + HC */ +#define I_V_IOT2 7 /* I/O only */ +#define I_V_EG1Z 010 /* ext grp, 1 op + 0 */ +#define I_V_EG2 011 /* ext grp, 2 op */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_NPNC (I_V_NPNC << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_ASH (I_V_ASH << I_V_FL) +#define I_ESHF (I_V_ESHF << I_V_FL) +#define I_EMRF (I_V_EMRF << I_V_FL) +#define I_IOT1 (I_V_IOT1 << I_V_FL) +#define I_IOT2 (I_V_IOT2 << I_V_FL) +#define I_EG1Z (I_V_EG1Z << I_V_FL) +#define I_EG2 (I_V_EG2 << I_V_FL) + +static const int32 masks[] = { + 0177777, 0176777, 0074000, 0170000, + 0177760, 0177777, 0176700, 0177700, + 0177777, 0177777 }; + +static const char *opcode[] = { + "NOP", "NOP", "AND", "JSB", + "XOR", "JMP", "IOR", "ISZ", + "ADA", "ADB" ,"CPA", "CPB", + "LDA", "LDB", "STA", "STB", + "ASL", "LSL", "RRL", + "ASR", "LSR", "RRR", + "MPY", "DIV", "DLD", "DST", + "FAD", "FSB", "FMP", "FDV", + "FIX", "FLT", + "STO", "CLO", "SOC", "SOS", + "HLT", "STF", "CLF", + "SFC", "SFS", "MIA", "MIB", + "LIA", "LIB", "OTA", "OTB", + "STC", "CLC", + "SAX", "SBX", "CAX", "CBX", + "LAX", "LBX", "STX", + "CXA", "CXB", "LDX", + "ADX", "XAX", "XBX", + "SAY", "SBY", "CAY", "CBY", + "LAY", "LBY", "STY", + "CYA", "CYB", "LDY", + "ADY", "XAY", "XBY", + "ISX", "DSX", "JLY", "LBT", + "SBT", "MBT", "CBT", "SBT", + "ISY", "DSY", "JPY", "SBS", + "CBS", "TBS", "CMW", "MVW", + NULL, /* decode only */ + NULL }; + +static const int32 opc_val[] = { + 0000000+I_NPN, 0002000+I_NPN, 0010000+I_MRF, 0014000+I_MRF, + 0020000+I_MRF, 0024000+I_MRF, 0030000+I_MRF, 0034000+I_MRF, + 0040000+I_MRF, 0044000+I_MRF, 0050000+I_MRF, 0054000+I_MRF, + 0060000+I_MRF, 0064000+I_MRF, 0070000+I_MRF, 0074000+I_MRF, + 0100020+I_ESHF, 0100040+I_ESHF, 0100100+I_ESHF, + 0101020+I_ESHF, 0101040+I_ESHF, 0101100+I_ESHF, + 0100200+I_EMRF, 0100400+I_EMRF, 0104200+I_EMRF, 0104400+I_EMRF, + 0105000+I_EMRF, 0105020+I_EMRF, 0105040+I_EMRF, 0105060+I_EMRF, + 0105100+I_NPN, 0105120+I_NPN, + 0102101+I_NPN, 0103101+I_NPN, 0102201+I_NPNC, 0102301+I_NPNC, + 0102000+I_IOT1, 0102100+I_IOT2, 0103100+I_IOT2, + 0102200+I_IOT2, 0102300+I_IOT2, 0102400+I_IOT1, 0106400+I_IOT1, + 0102500+I_IOT1, 0106500+I_IOT1, 0102600+I_IOT1, 0106600+I_IOT1, + 0102700+I_IOT1, 0106700+I_IOT1, + 0101740+I_EMRF, 0105740+I_EMRF, 0101741+I_NPN, 0105741+I_NPN, + 0101742+I_EMRF, 0105742+I_EMRF, 0105743+I_EMRF, + 0101744+I_NPN, 0105744+I_NPN, 0105745+I_EMRF, + 0105746+I_EMRF, 0101747+I_NPN, 0105747+I_NPN, + 0101750+I_EMRF, 0105750+I_EMRF, 0101751+I_NPN, 0105751+I_NPN, + 0101752+I_EMRF, 0105752+I_EMRF, 0105753+I_EMRF, + 0101754+I_NPN, 0105754+I_NPN, 0105755+I_EMRF, + 0105756+I_EMRF, 0101757+I_NPN, 0105757+I_NPN, + 0105760+I_NPN, 0105761+I_NPN, 0105762+I_EMRF, 0105763+I_NPN, + 0105764+I_NPN, 0105765+I_EG1Z, 0105766+I_EG1Z, 0105767+I_NPN, + 0105770+I_NPN, 0105771+I_NPN, 0105772+I_EMRF, 0105773+I_EG2, + 0105774+I_EG2, 0105775+I_EG2, 0105776+I_EG1Z, 0105777+I_EG1Z, + 0000000+I_ASH, /* decode only */ + -1 }; + +/* Decode tables for shift and alter/skip groups */ + +static const char *stab[] = { + "ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF", + "BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF", + "CLA", "CMA", "CCA", "CLB", "CMB", "CCB", + "SEZ", "CLE", "CLE", "CME", "CCE", + "SSA", "SSB", "SLA", "SLB", + "ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF", + "BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF", + "INA", "INB", "SZA", "SZB", "RSS", + NULL }; + +static const int32 mtab[] = { + 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, + 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, + 0007400, 0007400, 0007400, 0007400, 0007400, 0007400, + 0002040, 0002040, 0002300, 0002300, 0002300, + 0006020, 0006020, 0004010, 0004010, + 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, + 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, + 0006004, 0006004, 0006002, 0006002, 0002001, + 0 }; + +static const int32 vtab[] = { + 0001000, 0001100, 0001200, 0001300, 0001400, 0001500, 0001600, 0001700, + 0005000, 0005100, 0005200, 0005300, 0005400, 0005500, 0005600, 0005700, + 0002400, 0003000, 0003400, 0006400, 0007000, 0007400, + 0002040, 0000040, 0002100, 0002200, 0002300, + 0002020, 0006020, 0000010, 0004010, + 0000020, 0000021, 0000022, 0000023, 0000024, 0000025, 0000026, 0000027, + 0004020, 0004021, 0004022, 0004023, 0004024, 0004025, 0004026, 0004027, + 0002004, 0006004, 0002002, 0006002, 0002001, + -1 }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, cm, i, j, inst, disp; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, FMTASC ((inst >> 8) & 0177)); + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + case I_V_NPNC: /* no operands + C */ + fprintf (of, "%s", opcode[i]); + if (inst & HC) fprintf (of, " C"); + break; + case I_V_MRF: /* mem ref */ + disp = inst & DISP; /* displacement */ + fprintf (of, "%s ", opcode[i]); /* opcode */ + if (inst & CP) { /* current page? */ + if (cflag) fprintf (of, "%-o", (addr & PAGENO) | disp); + else fprintf (of, "C %-o", disp); } + else fprintf (of, "%-o", disp); /* page zero */ + if (inst & IA) fprintf (of, ",I"); + break; + case I_V_ASH: /* shift, alter-skip */ + cm = FALSE; + for (i = 0; mtab[i] != 0; i++) { + if ((inst & mtab[i]) == vtab[i]) { + if (cm) fprintf (of, ","); + cm = TRUE; + fprintf (of, "%s", stab[i]); } } + if (!cm) return SCPE_ARG; /* nothing decoded? */ + break; + case I_V_ESHF: /* extended shift */ + disp = inst & 017; /* shift count */ + if (disp == 0) disp = 16; + fprintf (of, "%s %d", opcode[i], disp); + break; + case I_V_EMRF: /* extended mem ref */ + fprintf (of, "%s %-o", opcode[i], val[1] & AMASK); + if (val[1] & IA) fprintf (of, ",I"); + return -1; /* extra word */ + case I_V_IOT1: /* IOT with H/C */ + fprintf (of, "%s %-o", opcode[i], inst & DEVMASK); + if (inst & HC) fprintf (of, ",C"); + break; + case I_V_IOT2: /* IOT */ + fprintf (of, "%s %-o", opcode[i], inst & DEVMASK); + break; + case I_V_EG1Z: /* ext grp 1 op + 0 */ + fprintf (of, "%s %-o", opcode[i], val[1] & AMASK); + if (val[1] & IA) fprintf (of, ",I"); + return -2; /* extra words */ + case I_V_EG2: /* ext grp 2 op */ + fprintf (of, "%s %-o", opcode[i], val[1] & AMASK); + if (val[1] & IA) fprintf (of, ",I"); + fprintf (of, " %-o", val[2] & AMASK); + if (val[2] & IA) fprintf (of, ",I"); + return -2; } /* extra words */ + return SCPE_OK; } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get address with indirection + + Inputs: + *cptr = pointer to input string + Outputs: + val = address + -1 if error +*/ + +int32 get_addr (char *cptr) +{ +int32 d; +t_stat r; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, ','); /* get next field */ +d = get_uint (gbuf, 8, AMASK, &r); /* construe as addr */ +if (r != SCPE_OK) return -1; +if (*cptr != 0) { /* more? */ + cptr = get_glyph (cptr, gbuf, 0); /* look for indirect */ + if (strcmp (gbuf, "I")) return -1; + d = d | IA; } +return d; +} + +/* Symbolic input + + Inputs: + *iptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *iptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k, clef, tbits; +t_stat r, ret; +char *cptr, gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*iptr)) iptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*iptr == '\'') && iptr++)) { /* ASCII char? */ + if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) iptr[0] & 0177; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*iptr == '"') && iptr++)) { /* char string? */ + if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) iptr[0] & 0177) << 8) | + ((t_value) iptr[1] & 0177); + return SCPE_OK; } + +/* Instruction parse */ + +ret = SCPE_OK; +cptr = get_glyph (iptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i]) { /* found opcode? */ + val[0] = opc_val[i] & DMASK; /* get value */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + + switch (j) { /* case on class */ + case I_V_NPN: /* no operand */ + break; + case I_V_NPNC: /* no operand + C */ + if (*cptr != 0) { + cptr = get_glyph (cptr, gbuf, 0); + if (strcmp (gbuf, "C")) return SCPE_ARG; + val[0] = val[0] | HC; } + break; + case I_V_MRF: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if (k = (strcmp (gbuf, "C") == 0)) { /* C specified? */ + val[0] = val[0] | CP; + cptr = get_glyph (cptr, gbuf, 0); } + else if (k = (strcmp (gbuf, "Z") == 0)) { /* Z specified? */ + cptr = get_glyph (cptr, gbuf, ','); } + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + if ((d & AMASK) <= DISP) val[0] = val[0] | d; + else if (cflag && !k && (((addr ^ d) & PAGENO) == 0)) + val[0] = val[0] | (d & (IA | DISP)) | CP; + else return SCPE_ARG; + break; + case I_V_ESHF: /* extended shift */ + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 10, 16, &r); + if ((r != SCPE_OK) || (d == 0)) return SCPE_ARG; + val[0] = val[0] | (d & 017); + break; + case I_V_EMRF: /* extended mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + val[1] = d; + ret = -1; + break; + case I_V_IOT1: /* IOT + optional C */ + cptr = get_glyph (cptr, gbuf, ','); /* get device */ + d = get_uint (gbuf, 8, DEVMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + if (*cptr != 0) { + cptr = get_glyph (cptr, gbuf, 0); + if (strcmp (gbuf, "C")) return SCPE_ARG; + val[0] = val[0] | HC; } + break; + case I_V_IOT2: /* IOT */ + cptr = get_glyph (cptr, gbuf, 0); /* get device */ + d = get_uint (gbuf, 8, DEVMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + break; + case I_V_EG1Z: /* ext grp 1 op + 0 */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + val[1] = d; + val[2] = 0; + ret = -2; + break; + case I_V_EG2: /* ext grp 2 op */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((k = get_addr (gbuf)) < 0) return SCPE_ARG; + val[1] = d; + val[2] = k; + ret = -2; + break; } /* end case */ + if (*cptr != 0) return SCPE_ARG; /* junk at end? */ + return ret; + } /* end if opcode */ + +/* Shift or alter-skip + + Each opcode is matched by a mask, specifiying the bits affected, and + the value, specifying the value. As opcodes are processed, the mask + values are used to specify which fields have already been filled in. + + The mask has two subfields, the type bits (A/B and A/S), and the field + bits. The type bits, once specified by any instruction, must be + consistent in all other instructions. The mask bits assure that no + field is filled in twice. + + Two special cases: + + 1. The dual shift field in shift requires checking how much of the + target word has been filled in before assigning the shift value. + To implement this, shifts are listed twice is the decode table. + If the current subopcode is a shift in the first part of the table + (entries 0..15), and CLE has been seen or the first shift field is + filled in, the code forces a mismatch. The glyph will match in + the second part of the table. + + 2. CLE processing must be deferred until the instruction can be + classified as shift or alter-skip, since it has two different + bit values in the two classes. To implement this, CLE seen is + recorded as a flag and processed after all other subopcodes. +*/ + +clef = FALSE; +tbits = 0; +val[0] = 0; +for (cptr = get_glyph (iptr, gbuf, ','); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, ',')) { /* loop thru glyphs */ + if (strcmp (gbuf, "CLE") == 0) { /* CLE? */ + if (clef) return SCPE_ARG; /* already seen? */ + clef = TRUE; /* set flag */ + continue; } + for (i = 0; stab[i] != NULL; i++) { /* find subopcode */ + if ((strcmp (gbuf, stab[i]) == 0) && + ((i >= 16) || (!clef && ((val[0] & 001710) == 0)))) break; } + if (stab[i] == NULL) return SCPE_ARG; + if (tbits & mtab[i] & (AB | ASKP) & (vtab[i] ^ val[0])) return SCPE_ARG; + if (tbits & mtab[i] & ~(AB | ASKP)) return SCPE_ARG; + tbits = tbits | mtab[i]; /* fill type+mask */ + val[0] = val[0] | vtab[i]; } /* fill value */ +if (clef) { /* CLE seen? */ + if (val[0] & ASKP) { /* alter-skip? */ + if (val[0] & 0300) return SCPE_ARG; /* already filled in? */ + else val[0] = val[0] | 0100; } + else val[0] = val[0] | 040; } /* fill in shift */ +return ret; +} diff --git a/i1401_cd.c b/i1401_cd.c new file mode 100644 index 00000000..8bed81b2 --- /dev/null +++ b/i1401_cd.c @@ -0,0 +1,282 @@ +/* i1401_cd.c: IBM 1402 card reader/punch + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cdr card reader + cdp card punch + stack stackers (5 units) + 0 normal + 1 1 + 2 2/8 + 3 unused + 4 4 + + Cards are represented as ASCII text streams terminated by newlines. + This allows cards to be created and edited as normal files. +*/ + +#include "i1401_defs.h" +#include + +extern unsigned char M[]; +extern int32 ind[64], ssa, iochk; +extern char bcd_to_ascii[64]; +extern char ascii_to_bcd[128]; +int32 s1sel, s2sel, s4sel, s8sel; +char rbuf[CBUFSIZE]; /* > CDR_WIDTH */ +t_stat cdr_svc (UNIT *uptr); +t_stat cdr_boot (int32 unitno); +t_stat cdr_attach (UNIT *uptr, char *cptr); +t_stat cd_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); + +/* Card reader data structures + + cdr_dev CDR descriptor + cdr_unit CDR unit descriptor + cdr_reg CDR register list +*/ + +UNIT cdr_unit = { + UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), 100 }; + +REG cdr_reg[] = { + { FLDATA (LAST, ind[IN_LST], 0) }, + { FLDATA (ERR, ind[IN_READ], 0) }, + { FLDATA (S1, s1sel, 0) }, + { FLDATA (S2, s2sel, 0) }, + { DRDATA (POS, cdr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT }, + { BRDATA (*BUF, rbuf, 8, 8, CDR_WIDTH), REG_HRO }, + { NULL } }; + +DEVICE cdr_dev = { + "CDR", &cdr_unit, cdr_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + &cdr_boot, &cdr_attach, NULL }; + +/* CDP data structures + + cdp_dev CDP device descriptor + cdp_unit CDP unit descriptor + cdp_reg CDP register list +*/ + +UNIT cdp_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; + +REG cdp_reg[] = { + { FLDATA (ERR, ind[IN_PNCH], 0) }, + { FLDATA (S4, s4sel, 0) }, + { FLDATA (S8, s8sel, 0) }, + { DRDATA (POS, cdp_unit.pos, 31), PV_LEFT }, + { NULL } }; + +DEVICE cdp_dev = { + "CDP", &cdp_unit, cdp_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL }; + +/* Stacker data structures + + stack_dev STACK device descriptor + stack_unit STACK unit descriptors + stack_reg STACK register list +*/ + +UNIT stack_unit[] = { + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } }; + +REG stack_reg[] = { + { DRDATA (POS0, stack_unit[0].pos, 31), PV_LEFT }, + { DRDATA (POS1, stack_unit[1].pos, 31), PV_LEFT }, + { DRDATA (POS28, stack_unit[2].pos, 31), PV_LEFT }, + { DRDATA (POS4, stack_unit[4].pos, 31), PV_LEFT }, + { NULL } }; + +DEVICE stack_dev = { + "STKR", stack_unit, stack_reg, NULL, + 5, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL }; + +/* Card read routine + + Modifiers have been checked by the caller + No modifiers are recognized (column binary is not implemented) +*/ + +t_stat read_card (int32 ilnt, int32 mod) +{ +int32 i; +t_stat r; + +if (sim_is_active (&cdr_unit)) { /* busy? */ + sim_cancel (&cdr_unit); /* cancel */ + if (r = cdr_svc (&cdr_unit)) return r; } /* process */ +if ((cdr_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +ind[IN_READ] = ind[IN_LST] = s1sel = s2sel = 0; /* default stacker */ +for (i = 0; i < CBUFSIZE; i++) rbuf[i] = 0; /* clear buffer */ +fgets (rbuf, CBUFSIZE, cdr_unit.fileref); /* read card */ +if (feof (cdr_unit.fileref)) return STOP_NOCD; /* eof? */ +if (ferror (cdr_unit.fileref)) { /* error? */ + perror ("Card reader I/O error"); + clearerr (cdr_unit.fileref); + if (iochk) return SCPE_IOERR; + ind[IN_READ] = 1; + return SCPE_OK; } +cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ +if (ssa) { /* if last cd on */ + i = getc (cdr_unit.fileref); /* see if more */ + if (feof (cdr_unit.fileref)) ind[IN_LST] = 1; /* eof? set flag */ + fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); } +for (i = 0; i < CDR_WIDTH; i++) { /* cvt to BCD */ + rbuf[i] = ascii_to_bcd[rbuf[i]]; + M[CDR_BUF + i] = (M[CDR_BUF + i] & WM) | rbuf[i]; } +M[CDR_BUF - 1] = 060; /* mem mark */ +sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ +return SCPE_OK; +} + +/* Card reader service. If a stacker select is active, copy to the + selected stacker. Otherwise, copy to the normal stacker. If the + unit is unattached, simply exit. +*/ + +t_stat cdr_svc (UNIT *uptr) +{ +int32 i; + +if (s1sel) uptr = &stack_unit[1]; /* stacker 1? */ +else if (s2sel) uptr = &stack_unit[2]; /* stacker 2? */ +else uptr = &stack_unit[0]; /* then default */ +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +for (i = 0; i < CDR_WIDTH; i++) rbuf[i] = bcd_to_ascii[rbuf[i]]; +for (i = CDR_WIDTH - 1; (i >= 0) && (rbuf[i] == ' '); i--) rbuf[i] = 0; +rbuf[CDR_WIDTH] = 0; /* null at end */ +fputs (rbuf, uptr -> fileref); /* write card */ +fputc ('\n', uptr -> fileref); /* plus new line */ +if (ferror (uptr -> fileref)) { /* error? */ + perror ("Card stacker I/O error"); + clearerr (uptr -> fileref); + if (iochk) return SCPE_IOERR; } +uptr -> pos = ftell (uptr -> fileref); /* update position */ +return SCPE_OK; +} + +/* Card punch routine + + Modifiers have been checked by the caller + No modifiers are recognized (column binary is not implemented) +*/ + +t_stat punch_card (int32 ilnt, int32 mod) +{ +int32 i; +static char pbuf[CDP_WIDTH + 1]; /* + null */ +UNIT *uptr; + +if (s8sel) uptr = &stack_unit[2]; /* stack 8? */ +else if (s4sel) uptr = &stack_unit[4]; /* stack 4? */ +else uptr = &cdp_unit; /* normal output */ +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +ind[IN_PNCH] = s4sel = s8sel = 0; /* clear flags */ + +M[CDP_BUF - 1] = 012; /* set prev loc */ +for (i = 0; i < CDP_WIDTH; i++) pbuf[i] = bcd_to_ascii[M[CDP_BUF + i] & CHAR]; +for (i = CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) pbuf[i] = 0; +pbuf[CDP_WIDTH] = 0; /* trailing null */ +fputs (pbuf, uptr -> fileref); /* output card */ +fputc ('\n', uptr -> fileref); /* plus new line */ +if (ferror (uptr -> fileref)) { /* error? */ + perror ("Card punch I/O error"); + clearerr (uptr -> fileref); + if (iochk) return SCPE_IOERR; + ind[IN_PNCH] = 1; } +uptr -> pos = ftell (uptr -> fileref); /* update position */ +return SCPE_OK; +} + +/* Select stack routine + + Modifiers have been checked by the caller + Modifiers are 1, 2, 4, 8 for the respective stack +*/ + +t_stat select_stack (int32 ilnt, int32 mod) +{ +if (mod == 1) s1sel = 1; +else if (mod == 2) s2sel = 1; +else if (mod == 4) s4sel = 1; +else if (mod == 8) s8sel = 1; +return SCPE_OK; +} + +/* Card reader/punch reset */ + +t_stat cd_reset (DEVICE *dptr) +{ +ind[IN_LST] = ind[IN_READ] = ind[IN_PNCH] = 0; /* clear indicators */ +s1sel = s2sel = s4sel = s8sel = 0; /* clear stacker sel */ +sim_cancel (&cdr_unit); /* clear reader event */ +return SCPE_OK; +} + +/* Card reader attach */ + +t_stat cdr_attach (UNIT *uptr, char *cptr) +{ +ind[IN_LST] = ind[IN_READ] = 0; /* clear last card */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +#define BOOT_START 100 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned char)) + +static const unsigned char boot_rom[] = { + OP_R + WM, /* R */ + OP_SWM + WM, BCD_ZERO, BCD_ZERO, BCD_ONE, /* SWM 001 */ + OP_CS + WM, BCD_ZERO, BCD_ZERO, BCD_ONE, /* CS 001 111 */ + BCD_ONE, BCD_ONE, BCD_ONE }; + +t_stat cdr_boot (int32 unitno) +{ +int32 i; +extern int32 saved_IS; + +for (i = 0; i < CDR_WIDTH; i++) M[CDR_BUF + i] = 0; /* clear buffer */ +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_IS = BOOT_START; +return SCPE_OK; +} diff --git a/i1401_cpu.c b/i1401_cpu.c new file mode 100644 index 00000000..9b49fc99 --- /dev/null +++ b/i1401_cpu.c @@ -0,0 +1,1085 @@ +/* i1401_cpu.c: IBM 1401 CPU simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + + The register state for the IBM 1401 is: + + IS I storage address register (PC) + AS A storage address register (address of first operand) + BS B storage address register (address of second operand) + ind[0:63] indicators + SSA sense switch A + IOCHK I/O check + PRCHK process check + + The IBM 1401 is a variable instruction length, decimal data system. + Memory consists of 4000, 8000, 12000, or 16000 BCD characters, each + containing six bits of data and a word mark. There are no general + registers; all instructions are memory to memory, using explicit + addresses or an address pointer from a prior instruction. + + BCD numeric data consists of the low four bits of a character (DIGIT), + encoded as X, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, X, X, X, X, X. The high + two bits (ZONE) encode the sign of the data as +, +, -, +. Character + data uses all six bits of a character. Numeric and character fields are + delimited by a word mark. Fields are typically processed in descending + address order (low-order data to high-order data). + + The 1401 encodes a decimal address, and an index register number, in + three characters: + + character zone digit + addr + 0 <1:0> of thousands hundreds + addr + 1 index register # tens + addr + 2 <3:2> of thousands ones + + Normally the digit values 0, 11, 12, 13, 14, 15 are illegal in addresses. + However, in indexing, digits are passed through the adder, and illegal + values are normalized to legal counterparts. + + The 1401 has six instruction formats: + + op A and B addresses, if any, from AS and BS + op d A and B addresses, if any, from AS and BS + op aaa B address, if any, from BS + op aaa d B address, if any, from BS + op aaa bbb + op aaa bbb d + + where aaa is the A address, bbb is the B address, and d is a modifier. + The opcode has word mark set; all other characters have word mark clear. +*/ + +/* This routine is the instruction decode routine for the IBM 1401. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + illegal addresses or instruction formats + I/O error in I/O simulator + + 2. Interrupts. The 1401 has no interrupt structure. + + 3. Non-existent memory. On the 1401, references to non-existent + memory halt the processor. + + 4. Adding I/O devices. These modules must be modified: + + i1401_cpu.c add IO dispatches to iodisp + i1401_sys.c add pointer to data structures to sim_devices +*/ + +#include "i1401_defs.h" + +#define ILL_ADR_FLAG 100000 /* invalid addr flag */ +#define save_ibkpt (cpu_unit.u3) /* saved bkpt addr */ +#define MM(x) x = x - 1; \ + if (x < 0) { \ + x = BA + MAXMEMSIZE - 1; \ + reason = STOP_WRAP; \ + break; } +#define PP(x) x = x + 1; \ + if (ADDR_ERR (x)) { \ + x = BA + (x % MAXMEMSIZE); \ + reason = STOP_WRAP; \ + break; } +#define BRANCH if (ADDR_ERR (AS)) { \ + reason = STOP_INVBR; \ + break; } \ + if (cpu_unit.flags & XSA) BS = IS; \ + else BS = BA + 0; \ + oldIS = saved_IS; \ + IS = AS; + +unsigned char M[MAXMEMSIZE] = { 0 }; /* main memory */ +int32 saved_IS = 0; /* saved IS */ +int32 AS = 0; /* AS */ +int32 BS = 0; /* BS */ +int32 as_err = 0, bs_err = 0; /* error flags */ +int32 oldIS = 0; /* previous IS */ +int32 ind[64] = { 0 }; /* indicators */ +int32 ssa = 1; /* sense switch A */ +int32 prchk = 0; /* process check stop */ +int32 iochk = 0; /* I/O check stop */ +int32 ibkpt_addr = ILL_ADR_FLAG + MAXMEMSIZE; /* breakpoint addr */ +extern int32 sim_int_char; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +int32 store_addr_h (int32 addr); +int32 store_addr_t (int32 addr); +int32 store_addr_u (int32 addr); +t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr); +t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod); + +extern t_stat read_card (int32 ilnt, int32 mod); +extern t_stat punch_card (int32 ilnt, int32 mod); +extern t_stat select_stack (int32 mod); +extern t_stat carriage_control (int32 mod); +extern t_stat write_line (int32 ilnt, int32 mod); +extern t_stat inq_io (int32 flag, int32 mod); +extern t_stat mt_io (int32 unit, int32 flag, int32 mod); +extern t_stat mt_func (int32 unit, int32 mod); +extern t_stat sim_activate (UNIT *uptr, int32 delay); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BCD + STDOPT, + MAXMEMSIZE) }; + +REG cpu_reg[] = { + { DRDATA (IS, saved_IS, 14), PV_LEFT }, + { DRDATA (AS, AS, 14), PV_LEFT }, + { DRDATA (BS, BS, 14), PV_LEFT }, + { FLDATA (ASERR, as_err, 0) }, + { FLDATA (BSERR, bs_err, 0) }, + { FLDATA (SSA, ssa, 0) }, + { FLDATA (SSB, ind[IN_SSB], 0) }, + { FLDATA (SSC, ind[IN_SSC], 0) }, + { FLDATA (SSD, ind[IN_SSD], 0) }, + { FLDATA (SSE, ind[IN_SSE], 0) }, + { FLDATA (SSF, ind[IN_SSF], 0) }, + { FLDATA (SSG, ind[IN_SSG], 0) }, + { FLDATA (EQU, ind[IN_EQU], 0) }, + { FLDATA (UNEQ, ind[IN_UNQ], 0) }, + { FLDATA (HIGH, ind[IN_HGH], 0) }, + { FLDATA (LOW, ind[IN_LOW], 0) }, + { FLDATA (OVF, ind[IN_OVF], 0) }, + { FLDATA (IOCHK, iochk, 0) }, + { FLDATA (PRCHK, prchk, 0) }, + { DRDATA (OLDIS, oldIS, 14), REG_RO + PV_LEFT }, + { DRDATA (BREAK, ibkpt_addr, 17), PV_LEFT }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { XSA, XSA, "XSA", "XSA", NULL }, + { XSA, 0, "no XSA", "NOXSA", NULL }, + { HLE, HLE, "HLE", "HLE", NULL }, + { HLE, 0, "no HLE", "NOHLE", NULL }, + { BBE, BBE, "BBE", "BBE", NULL }, + { BBE, 0, "no BBE", "NOBBE", NULL }, + { MA, MA, "MA", 0, NULL }, + { MA, 0, "no MA", 0, NULL }, + { MR, MR, "MR", "MR", NULL }, + { MR, 0, "no MR", "NOMR", NULL }, + { EPE, EPE, "EPE", "EPE", NULL }, + { EPE, 0, "no EPE", "NOEPE", NULL }, + { UNIT_MSIZE, 4000, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8000, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12000, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16000, NULL, "16K", &cpu_set_size }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 14, 1, 8, 7, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +/* Opcode table - length, dispatch, and option flags. This table is also + used by the symbolic input routine to validate instruction lengths */ + +const int32 op_table[64] = { + 0, /* 00: illegal */ + L1 | L2 | L4 | L5, /* read */ + L1 | L2 | L4 | L5, /* write */ + L1 | L2 | L4 | L5, /* write and read */ + L1 | L2 | L4 | L5, /* punch */ + L1 | L4, /* read and punch */ + L1 | L2 | L4 | L5, /* write and read */ + L1 | L2 | L4 | L5, /* write, read, punch */ + L1, /* 10: read feed */ + L1, /* punch feed */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ | MA, /* modify address */ + L7 | AREQ | BREQ | MDV, /* multiply */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* 20: illegal */ + L1 | L4 | L7 | BREQ | NOWM, /* clear storage */ + L1 | L4 | L7 | AREQ | BREQ, /* subtract */ + 0, /* illegal */ + L5 | IO, /* magtape */ + L1 | L8 | BREQ, /* branch wm or zone */ + L1 | L8 | BREQ | BBE, /* branch if bit eq */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* 30: move zones */ + L7 | AREQ | BREQ, /* move supress zero */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ | NOWM, /* set word mark */ + L7 | AREQ | BREQ | MDV, /* divide */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* 40: illegal */ + 0, /* illegal */ + L2 | L5, /* select stacker */ + L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* load */ + L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* move */ + HNOP | L1, /* nop */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ | MR, /* move to record */ + L1 | L4 | AREQ | MLS, /* 50: store A addr */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* zero and subtract */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* 60: illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* add */ + L1 | L4 | L5 | L8, /* branch */ + L1 | L4 | L7 | AREQ | BREQ, /* compare */ + L1 | L4 | L7 | AREQ | BREQ, /* move numeric */ + L1 | L4 | L7 | AREQ | BREQ, /* move char edit */ + L2 | L5, /* carriage control */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | MLS, /* 70: store B addr */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* zero and add */ + HNOP | L1 | L4, /* halt */ + L1 | L4 | L7 | AREQ | BREQ, /* clear word mark */ + 0, /* illegal */ + 0, /* illegal */ + 0 }; /* illegal */ + +const int32 len_table[9] = { 0, L1, L2, 0, L4, L5, 0, L7, L8 }; + +/* Address character conversion tables. Illegal characters are marked by + the flag BA but also contain the post-adder value for indexing */ + +const int32 hun_table[64] = { + BA+000, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 000, BA+300, BA+400, BA+500, BA+600, BA+700, + BA+1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, + 1800, 1900, 1000, BA+1300, BA+1400, BA+1500, BA+1600, BA+1700, + BA+2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 2900, 2000, BA+2300, BA+2400, BA+2500, BA+2600, BA+2700, + BA+3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700, + 3800, 3900, 3000, BA+3300, BA+3400, BA+3500, BA+3600, BA+3700 }; + +const int32 ten_table[64] = { + BA+00, 10, 20, 30, 40, 50, 60, 70, + 80, 90, 00, BA+30, BA+40, BA+50, BA+60, BA+70, + X1+00, X1+10, X1+20, X1+30, X1+40, X1+50, X1+60, X1+70, + X1+80, X1+90, X1+00, X1+30, X1+40, X1+50, X1+60, X1+70, + X2+00, X2+10, X2+20, X2+30, X2+40, X2+50, X2+60, X2+70, + X2+80, X2+90, X2+00, X2+30, X2+40, X2+50, X2+60, X2+70, + X3+00, X3+10, X3+20, X3+30, X3+40, X3+50, X3+60, X3+70, + X3+80, X3+90, X3+00, X3+30, X3+40, X3+50, X3+60, X3+70 }; + +const int32 one_table[64] = { + BA+0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0, BA+3, BA+4, BA+5, BA+6, BA+7, + BA+4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, + 4008, 4009, 4000, BA+4003, BA+4004, BA+4005, BA+4006, BA+4007, + BA+8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, + 8008, 8009, 8000, BA+8003, BA+8004, BA+8005, BA+8006, BA+8007, + BA+12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007, + 12008, 12009, 12000, BA+12003, BA+12004, BA+12005, BA+12006, BA+12007 }; + +static const int32 bin_to_bcd[16] = { + 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; + +static const int32 bcd_to_bin[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7 }; + +/* ASCII to BCD conversion */ + +const char ascii_to_bcd[128] = { + 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 052, 077, 013, 053, 034, 060, 032, /* 040 - 077 */ + 017, 074, 054, 037, 033, 040, 073, 021, + 012, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 076, 035, 016, 072, + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 075, 036, 055, 020, 057, + 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 000, 000, 000, 000, 000 }; + +/* BCD to ASCII conversion - also the "full" print chain */ + +char bcd_to_ascii[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '#', '@', ':', '>', '(', + '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '\'', ',', '%', '=', '\\', '+', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '"' }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 IS, D, ilnt, flags; +int32 op, xa, t, wm, dev, unit; +int32 a, b, i, bsave, carry; +int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal; +t_stat reason, r1, r2; + +/* Indicator resets - a 1 marks an indicator that resets when tested */ + +static const int32 ind_table[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 1, 1, 0, 1, 0, 0, 0, /* 30 - 37 */ + 0, 0, 1, 0, 0, 0, 0, 0, /* 40 - 47 */ + 0, 0, 1, 0, 1, 0, 0, 0, /* 50 - 57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 67 */ + 0, 0, 1, 0, 0, 0, 0, 0 }; /* 70 - 77 */ + +/* Character collation table for compare with HLE option */ + +static const int32 col_table[64] = { + 000, 067, 070, 071, 072, 073, 074, 075, + 076, 077, 066, 024, 025, 026, 027, 030, + 023, 015, 056, 057, 060, 061, 062, 063, + 064, 065, 055, 016, 017, 020, 021, 022, + 014, 044, 045, 046, 047, 050, 051, 052, + 053, 054, 043, 007, 010, 011, 012, 013, + 006, 032, 033, 034, 035, 036, 037, 040, + 041, 042, 031, 001, 002, 003, 004, 005 }; + +/* Summing table for two decimal digits, converted back to BCD */ + +static const int32 sum_table[20] = { + 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + +/* Legal modifier tables */ + +static const int32 w_mod[] = { BCD_S, BCD_SQUARE, -1 }; +static const int32 ss_mod[] = { 1, 2, 4, 8, -1 }; +static const int32 mtf_mod[] = { BCD_B, BCD_E, BCD_M, BCD_R, BCD_U, -1 }; + + +/* Restore saved state */ + +IS = saved_IS; +D = 0; +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +saved_IS = IS; /* commit prev instr */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; } + +if (IS == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save ibkpt */ + ibkpt_addr = ibkpt_addr + ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + +sim_interval = sim_interval - 1; + +/* Instruction fetch */ + +if ((M[IS] & WM) == 0) { /* WM under op? */ + reason = STOP_NOWM; /* no, error */ + break; } +op = M[IS] & CHAR; /* get opcode */ +flags = op_table[op]; /* get op flags */ +if ((flags == 0) || (flags & ALLOPT & ~cpu_unit.flags)) { + reason = STOP_NXI; /* illegal inst? */ + break; } +if (op == OP_SAR) BS = AS; /* SAR? save ASTAR */ +PP (IS); + +if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* WM? 1 char inst */ +D = t; /* could be D char */ +AS = hun_table[t]; /* could be A addr */ +PP (IS); /* if %xy, BA is set */ + +if ((t = M[IS]) & WM) { /* WM? 2 char inst */ + AS = AS | BA; /* ASTAR bad */ + if (!(flags & MLS)) BS = AS; + goto CHECK_LENGTH; } +AS = AS + ten_table[t]; /* build A addr */ +dev = t; /* save char as dev */ +PP (IS); + +if ((t = M[IS]) & WM) { /* WM? 3 char inst */ + AS = AS | BA; /* ASTAR bad */ + if (!(flags & MLS)) BS = AS; + goto CHECK_LENGTH; } +AS = AS + one_table[t]; /* finish A addr */ +unit = (t == BCD_ZERO)? 0: t; /* save char as unit */ +xa = (AS >> V_INDEX) & M_INDEX; /* get index reg */ +if (xa && (D != BCD_PERCNT) && (cpu_unit.flags & XSA)) { /* indexed? */ + AS = AS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] + + one_table[M[xa + 2] & CHAR]; + AS = (AS & INDEXMASK) % MAXMEMSIZE; } +if (!(flags & MLS)) BS = AS; /* not MLS? B = A */ +PP (IS); + +if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* WM? 4 char inst */ +if ((op == OP_B) && (t == BCD_BLANK)) goto CHECK_LENGTH; /* BR + space? */ +D = t; /* could be D char */ +BS = hun_table[t]; /* could be B addr */ +PP (IS); + +if ((t = M[IS]) & WM) { /* WM? 5 char inst */ + BS = BS | BA; /* BSTAR bad */ + goto CHECK_LENGTH; } +BS = BS + ten_table[t]; /* build B addr */ +PP (IS); + +if ((t = M[IS]) & WM) { /* WM? 6 char inst */ + BS = BS | BA; /* BSTAR bad */ + goto CHECK_LENGTH; } +BS = BS + one_table[t]; /* finish B addr */ +xa = (BS >> V_INDEX) & M_INDEX; /* get index reg */ +if (xa && (cpu_unit.flags & XSA)) { /* indexed? */ + BS = BS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] + + one_table[M[xa + 2] & CHAR]; + BS = (BS & INDEXMASK) % MAXMEMSIZE; } +PP (IS); + +if ((M[IS] & WM) || (flags & NOWM)) goto CHECK_LENGTH; /* WM? 7 chr */ +D = M[IS]; /* last char is D */ +do { PP (IS); } while ((M[IS] & WM) == 0); /* find word mark */ + +CHECK_LENGTH: +ilnt = IS - saved_IS; /* get lnt */ +if (((flags & len_table [(ilnt <= 8)? ilnt: 8]) == 0) && /* valid lnt? */ + ((flags & HNOP) == 0)) reason = STOP_INVL; +if ((flags & BREQ) && ADDR_ERR (BS)) reason = STOP_INVB; /* valid A? */ +if ((flags & AREQ) && ADDR_ERR (AS)) reason = STOP_INVA; /* valid B? */ +if (reason) break; /* error in fetch? */ +switch (op) { /* case on opcode */ + +/* Move instructions A check B check + + MCW: copy A to B, preserving B WM, here fetch + until either A or B WM + LCA: copy A to B, overwriting B WM, here fetch + until A WM + MCM: copy A to B, preserving B WM, fetch fetch + until record or group mark + MSZ: copy A to B, clearing B WM, until A WM; fetch fetch + reverse scan and suppress leading zeroes + MN: copy A char digit to B char digit, fetch fetch + preserving B zone and WM + MZ: copy A char zone to B char zone, fetch fetch + preserving B digit and WM +*/ + +case OP_MCW: /* move char */ + if (ilnt >= 8) { /* I/O form? */ + reason = iodisp (dev, unit, MD_NORM, D); + break; } + if (ADDR_ERR (AS)) { /* check A addr */ + reason = STOP_INVA; + break; } + do { M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */ + wm = M[AS] | M[BS]; + MM (AS); MM (BS); } /* decr pointers */ + while ((wm & WM) == 0); /* stop on A,B WM */ + break; +case OP_LCA: /* load char */ + if (ilnt >= 8) { /* I/O form? */ + reason = iodisp (dev, unit, MD_WM, D); + break; } + if (ADDR_ERR (AS)) { /* check A addr */ + reason = STOP_INVA; + break; } + do { wm = M[BS] = M[AS]; /* move char + wmark */ + MM (AS); MM (BS); } /* decr pointers */ + while ((wm & WM) == 0); /* stop on A WM */ + break; +case OP_MCM: /* move to rec/group */ + do { M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */ + t = M[AS]; + PP (AS); PP (BS); } /* incr pointers */ + while (((t & CHAR) != BCD_RECMRK) && (t != (BCD_GRPMRK + WM))); + break; +case OP_MSZ: /* move suppress zero */ + bsave = BS; /* save B start */ + qzero = 1; /* set suppress */ + do { M[BS] = M[AS] & ((BS != bsave)? CHAR: DIGIT); /* copy char */ + wm = M[AS]; + MM (AS); MM (BS); } /* decr pointers */ + while ((wm & WM) == 0); /* stop on A WM */ + do { PP (BS); /* adv B */ + t = M[BS]; /* get B, cant be WM */ + if ((t == BCD_ZERO) || (t == BCD_COMMA)) { + if (qzero) M[BS] = 0; } + else if ((t == BCD_BLANK) || (t == BCD_MINUS)) ; + else if (((t == BCD_DECIMAL) && (cpu_unit.flags & EPE)) || + (t <= BCD_NINE)) qzero = 0; + else qzero = 1; } + while (BS <= bsave); + break; +case OP_MN: /* move numeric */ + M[BS] = (M[BS] & ~DIGIT) | (M[AS] & DIGIT); /* move digit */ + MM (AS); MM (BS); /* decr pointers */ + break; +case OP_MZ: /* move zone */ + M[BS] = (M[BS] & ~ZONE) | (M[AS] & ZONE); /* move high bits */ + MM (AS); MM (BS); /* decr pointers */ + break; + +/* Compare + + A and B are checked in fetch +*/ + +case OP_C: /* compare */ + if (ilnt != 1) { /* if not chained */ + ind[IN_EQU] = 1; /* clear indicators */ + ind[IN_UNQ] = ind[IN_HGH] = ind[IN_LOW] = 0; } + do { a = M[AS]; /* get characters */ + b = M[BS]; + wm = a | b; /* get word marks */ + if ((a & CHAR) != (b & CHAR)) { /* unequal? */ + ind[IN_EQU] = 0; /* set indicators */ + ind[IN_UNQ] = 1; + ind[IN_HGH] = col_table[b & CHAR] > col_table [a & CHAR]; + ind[IN_LOW] = ind[IN_HGH] ^ 1; } + MM (AS); MM (BS); } /* decr pointers */ + while ((wm & WM) == 0); /* stop on A, B WM */ + if ((a & WM) && !(b & WM)) { /* short A field? */ + ind[IN_EQU] = ind[IN_LOW] = 0; + ind[IN_UNQ] = ind[IN_HGH] = 1; } + if (!(cpu_unit.flags & HLE)) /* no HLE? */ + ind[IN_EQU] = ind[IN_LOW] = ind[IN_HGH] = 0; + break; + +/* Branch instructions A check B check + + B 8 char: branch if B char equals d if branch here + B 5 char: branch if indicator[d] is set if branch + B 4 char: unconditional branch if branch + BWZ: branch if (d<0>: B char WM) if branch here + (d<1>: B char zone = d zone) + BBE: branch if B char & d non-zero if branch here +*/ + +case OP_B: /* branch */ + if (ilnt <= 4) { BRANCH; } /* uncond branch? */ + else if (ilnt == 5) { /* branch on ind? */ + if (ind[D]) { BRANCH; } /* test indicator */ + if (ind_table[D]) ind[D] = 0; } /* reset if needed */ + else { if (ADDR_ERR (BS)) { /* branch char eq */ + reason = STOP_INVB; /* validate B addr */ + break; } + if ((M[BS] & CHAR) == D) { BRANCH; } /* char equal? */ + else { MM (BS); } } + break; +case OP_BWZ: /* branch wm or zone */ + if (((D & 1) && (M[BS] & WM)) || /* d1? test wm */ + ((D & 2) && ((M[BS] & ZONE) == (D & ZONE)))) /* d2? test zone */ + { BRANCH; } + else { MM (BS); } /* decr pointer */ + break; +case OP_BBE: /* branch if bit eq */ + if (M[BS] & D & CHAR) { BRANCH; } /* any bits set? */ + else { MM (BS); } /* decr pointer */ + break; + +/* Arithmetic instructions A check B check + + ZA: move A to B, normalizing A sign, fetch fetch + preserving B WM, until B WM + ZS: move A to B, complementing A sign, fetch fetch + preserving B WM, until B WM + A: add A to B fetch fetch + S: subtract A from B fetch fetch +*/ + +case OP_ZA: case OP_ZS: /* zero and add/sub */ + a = i = 0; /* clear flags */ + do { if (a & WM) wm = M[BS] = (M[BS] & WM) | BCD_ZERO; + else { a = M[AS]; /* get A char */ + t = (a & CHAR)? bin_to_bcd[a & DIGIT]: 0; + wm = M[BS] = (M[BS] & WM) | t; /* move digit */ + MM (AS); } + if (i == 0) i = M[BS] = M[BS] | + ((((a & ZONE) == BBIT) ^ (op == OP_ZS))? BBIT: ZONE); + MM (BS); } + while ((wm & WM) == 0); /* stop on B WM */ + break; +case OP_A: case OP_S: /* add/sub */ + bsave = BS; /* save sign pos */ + a = M[AS]; /* get A digit/sign */ + b = M[BS]; /* get B digit/sign */ + MM (AS); + qsign = ((a & ZONE) == BBIT) ^ ((b & ZONE) == BBIT) ^ (op == OP_S); + t = bcd_to_bin[a & DIGIT]; /* get A binary */ + t = bcd_to_bin[b & DIGIT] + (qsign? 10 - t: t); /* sum A + B */ + carry = (t >= 10); /* get carry */ + b = (b & ~DIGIT) | sum_table[t]; /* get result */ + if (qsign && ((b & BBIT) == 0)) b = b | ZONE; /* normalize sign */ + M[BS] = b; /* store result */ + MM (BS); + if (b & WM) { /* b wm? done */ + if (qsign && (carry == 0)) M[bsave] = /* compl, no carry? */ + WM + ((b & ZONE) ^ ABIT) + sum_table[10 - t]; + break; } + do { if (a & WM) a = WM; /* A WM? char = 0 */ + else { a = M[AS]; /* else get A */ + MM (AS); } + b = M[BS]; /* get B */ + t = bcd_to_bin[a & DIGIT]; /* get A binary */ + t = bcd_to_bin[b & DIGIT] + (qsign? 9 - t: t) + carry; + carry = (t >= 10); /* get carry */ + if ((b & WM) && (qsign == 0)) { /* last, no recomp? */ + M[BS] = WM + sum_table[t] + /* zone add */ + (((a & ZONE) + b + (carry? ABIT: 0)) & ZONE); + ind[IN_OVF] = carry; } /* ovflo if carry */ + else M[BS] = (b & WM) + sum_table[t]; /* normal add */ + MM (BS); } + while ((b & WM) == 0); /* stop on B WM */ + if (qsign && (carry == 0)) { /* recompl, no carry? */ + M[bsave] = M[bsave] ^ ABIT; /* XOR sign */ + for (carry = 1; bsave != BS; --bsave) { /* rescan */ + t = 9 - bcd_to_bin[M[bsave] & DIGIT] + carry; + carry = (t >= 10); + M[bsave] = (M[bsave] & ~DIGIT) | sum_table[t]; } } + break; + +/* I/O instructions A check B check + + R: read a card if branch + W: write to line printer if branch + WR: write and read if branch + P: punch a card if branch + RP: read and punch if branch + WP: write and punch if branch + WRP: write read and punch if branch + RF: read feed (nop) + PF: punch feed (nop) + SS: select stacker if branch + CC: carriage control if branch + MTF: magtape functions +*/ + +case OP_R: /* read */ + if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */ + reason = read_card (ilnt, D); /* read card */ + BS = CDR_BUF + CDR_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + break; +case OP_W: /* write */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + BS = LPT_BUF + LPT_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + break; +case OP_P: /* punch */ + if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */ + reason = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + break; +case OP_WR: /* write and read */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + r1 = read_card (ilnt, D); /* read card */ + BS = CDR_BUF + CDR_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = r1; /* merge errors */ + break; +case OP_WP: /* write and punch */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + r1 = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = r1; /* merge errors */ + break; +case OP_RP: /* read and punch */ + if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */ + reason = read_card (ilnt, D); /* read card */ + r1 = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = r1; /* merge errors */ + break; +case OP_WRP: /* write, read, punch */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + r1 = read_card (ilnt, D); /* read card */ + r2 = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if (ilnt >= 4) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = (r1 == SCPE_OK)? r2: r1; + break; +case OP_SS: /* select stacker */ + if (reason = iomod (ilnt, D, ss_mod)) break; /* valid modifier? */ + if (reason = select_stack (D)) break; /* sel stack, error? */ + if (ilnt >= 4) { BRANCH; } /* check for branch */ + break; +case OP_CC: /* carriage control */ + if (reason = carriage_control (D)) break; /* car ctrl, error? */ + if (ilnt >= 4) { BRANCH; } /* check for branch */ + break; +case OP_MTF: /* magtape function */ + if (reason = iomod (ilnt, D, mtf_mod)) break; /* valid modifier? */ + if (reason = mt_func (unit, D)) break; /* mt func, error? */ + break; /* can't branch */ +case OP_RF: case OP_PF: /* read, punch feed */ + break; /* nop's */ + +/* Move character and edit + + Control flags + qsign sign of A field (0 = +, 1 = minus) + qawm A field WM seen and processed + qzero zero suppression enabled + qbody in body (copying A field characters) + qdollar EPE only; $ seen in body + qaster EPE only; * seen in body + qdecimal EPE only; . seen on first rescan +*/ + +case OP_MCE: /* edit */ + a = M[AS]; /* get A char */ + b = M[BS]; /* get B char */ + if (a & WM) { /* one char A field? */ + reason = STOP_MCE1; + break; } + if (b & WM) { /* one char B field? */ + reason = STOP_MCE2; + break; } + t = a & DIGIT; MM (AS); /* get A digit */ + qsign = ((a & ZONE) == BBIT); /* get A field sign */ + qawm = qzero = qbody = 0; /* clear other flags */ + qdollar = qaster = qdecimal = 0; /* clear EPE flags */ + +/* Edit pass 1 - from right to left, under B field control */ + + do { b = M[BS]; /* get B char */ + M[BS] = M[BS] & ~WM; /* clr WM */ + switch (b & CHAR) { /* case on B char */ + case BCD_ASTER: /* * */ + if (!qbody || qdollar || !(cpu_unit.flags & EPE)) break; + qaster = 1; /* flag */ + goto A_CYCLE; /* take A cycle */ + case BCD_DOLLAR: /* $ */ + if (!qbody || qaster || !(cpu_unit.flags & EPE)) break; + qdollar = 1; /* flag */ + goto A_CYCLE; /* take A cycle */ + case BCD_ZERO: /* 0 */ + if (qawm && !qzero && !(b & WM)) { + M[BS] = BCD_ZERO + WM; /* mark with WM */ + qzero = 1; /* flag supress */ + break; } + if (!qzero) t = t | WM; /* first? set WM */ + qzero = 1; /* flag suppress */ + /* fall through */ + case BCD_BLANK: /* blank */ + if (qawm) break; /* any A left? */ + A_CYCLE: + M[BS] = t; /* copy char */ + if (a & WM) { /* end of A field? */ + qbody = 0; /* end body */ + qawm = 1; } + else { qbody = 1; /* in body */ + a = M[AS]; MM (AS); /* next A */ + t = a & DIGIT; } + break; + case BCD_C: case BCD_R: case BCD_MINUS: /* C, R, - */ + if (!qsign && !qbody) M[BS] = BCD_BLANK; + break; + case BCD_COMMA: /* , */ + if (!qbody) M[BS] = BCD_BLANK; /* bl if status */ + break; + case BCD_AMPER: /* & */ + M[BS] = BCD_BLANK; /* blank B field */ + break; } /* end switch */ + MM (BS); } /* decr B pointer */ + while ((b & WM) == 0); /* stop on B WM */ + + if (!qawm || !qzero) { /* rescan? */ + if (qdollar) reason = STOP_MCE3; /* error if $ */ + break; } + +/* Edit pass 2 - from left to right, suppressing zeroes */ + + do { b = M[++BS]; /* get B char */ + switch (b & CHAR) { /* case on B char */ + case BCD_ONE: case BCD_TWO: case BCD_THREE: + case BCD_FOUR: case BCD_FIVE: case BCD_SIX: + case BCD_SEVEN: case BCD_EIGHT: case BCD_NINE: + qzero = 0; /* turn off supr */ + break; + case BCD_ZERO: case BCD_COMMA: /* 0 or , */ + if (qzero && !qdecimal) /* if supr, blank */ + M[BS] = qaster? BCD_ASTER: BCD_BLANK; + break; + case BCD_BLANK: /* blank */ + if (qaster) M[BS] = BCD_ASTER; /* if EPE *, repl */ + break; + case BCD_DECIMAL: /* . */ + if (qzero && (cpu_unit.flags & EPE)) + qdecimal = 1; /* flag for EPE */ + case BCD_PERCNT: case BCD_WM: case BCD_BS: + case BCD_TS: case BCD_MINUS: + break; /* ignore */ + default: /* other */ + qzero = 1; /* restart supr */ + break; } } /* end case, do */ + while ((b & WM) == 0); + + M[BS] = M[BS] & ~WM; /* clear B WM */ + if (!qdollar && !(qdecimal && qzero)) break; /* rescan again? */ + if (qdecimal && qzero) qdollar = 0; /* no digits? clr $ */ + +/* Edit pass 3 (extended print only) - from right to left */ + + for (;; ) { /* until chars */ + b = M[BS]; /* get B char */ + if ((b == BCD_BLANK) && qdollar) { /* blank & flt $? */ + M[BS] = BCD_DOLLAR; /* insert $ */ + break; } /* exit for */ + if (b == BCD_DECIMAL) { /* decimal? */ + M[BS] = qaster? BCD_ASTER: BCD_BLANK; + break; } /* exit for */ + if ((b == BCD_ZERO) && !qdollar) /* 0 & ~flt $ */ + M[BS] = qaster? BCD_ASTER: BCD_BLANK; + BS--; } /* end for */ + break; /* done at last! */ + +/* Miscellaneous instructions A check B check + + SWM: set WM on A char and B char fetch fetch + CWM: clear WM on A char and B char fetch fetch + CS: clear from B down to nearest hundreds address if branch fetch + MA: add A addr and B addr, store at B addr fetch fetch + SAR: store A* at A addr fetch + SBR: store B* at A addr fetch + NOP: no operation + H: halt +*/ + +case OP_SWM: /* set word mark */ + M[BS] = M[BS] | WM; /* set A field mark */ + M[AS] = M[AS] | WM; /* set B field mark */ + MM (AS); MM (BS); /* decr pointers */ + break; +case OP_CWM: /* clear word mark */ + M[BS] = M[BS] & ~WM; /* clear A field mark */ + M[AS] = M[AS] & ~WM; /* clear B field mark */ + MM (AS); MM (BS); /* decr pointers */ + break; +case OP_CS: /* clear storage */ + t = (BS / 100) * 100; /* lower bound */ + while (BS >= t) M[BS--] = 0; /* clear region */ + if (BS < 0) BS = BS + MEMSIZE; /* wrap if needed */ + if (ilnt >= 7) { BRANCH; } /* branch variant? */ + break; +case OP_MA: /* modify address */ + a = one_table[M[AS] & CHAR]; MM (AS); /* get A address */ + a = a + ten_table[M[AS] & CHAR]; MM (AS); + a = a + hun_table[M[AS] & CHAR]; MM (AS); + b = one_table[M[BS] & CHAR]; MM (BS); /* get B address */ + b = b + ten_table[M[BS] & CHAR]; MM (BS); + b = b + hun_table[M[BS] & CHAR]; MM (BS); + t = ((a + b) & INDEXMASK) % MAXMEMSIZE; /* compute sum */ + M[BS + 3] = (M[BS + 3] & WM) | store_addr_u (t); + M[BS + 2] = (M[BS + 2] & (WM + ZONE)) | store_addr_t (t); + M[BS + 1] = (M[BS + 1] & WM) | store_addr_h (t); + if (((a % 4000) + (b % 4000)) >= 4000) BS = BS + 2; /* carry? */ + break; +case OP_SAR: case OP_SBR: /* store A, B reg */ + M[AS] = (M[AS] & WM) | store_addr_u (BS); MM (AS); + M[AS] = (M[AS] & WM) | store_addr_t (BS); MM (AS); + M[AS] = (M[AS] & WM) | store_addr_h (BS); MM (AS); + break; +case OP_NOP: /* nop */ + break; +case OP_H: /* halt */ + if (ilnt >= 4) { BRANCH; } /* branch if called */ + reason = STOP_HALT; /* stop simulator */ + saved_IS = IS; /* commit instruction */ + break; +default: + reason = STOP_NXI; /* unimplemented */ + break; } /* end switch */ +} /* end while */ + +/* Simulation halted */ + +as_err = (AS > ADDRMASK); +bs_err = (BS > ADDRMASK); +return reason; +} /* end sim_instr */ + +/* store addr_x - convert address to BCD character in x position + + Inputs: + addr = address to convert + Outputs: + char = converted address character +*/ + +int32 store_addr_h (int32 addr) +{ +int32 thous; + +thous = (addr / 1000) & 03; +return bin_to_bcd[(addr % 1000) / 100] | (thous << V_ZONE); +} + +int32 store_addr_t (int32 addr) +{ +return bin_to_bcd[(addr % 100) / 10]; +} + +int32 store_addr_u (int32 addr) +{ +int32 thous; + +thous = (addr / 1000) & 014; +return bin_to_bcd[addr % 10] | (thous << (V_ZONE - 2)); +} + +/* iomod - check on I/O modifiers + + Inputs: + ilnt = instruction length + mod = modifier character + tptr = pointer to table of modifiers, end is -1 + Output: + status = SCPE_OK if ok, STOP_INVM if invalid +*/ + +t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr) +{ +if ((ilnt != 2) && (ilnt != 5) && (ilnt < 8)) return SCPE_OK; +if (tptr == NULL) return STOP_INVM; +do { if (mod == *tptr++) return SCPE_OK; } +while (*tptr >= 0); +return STOP_INVM; +} + +/* iodisp - dispatch load or move to I/O routine + + Inputs: + dev = device number + unit = unit number + flag = move (MD_NORM) vs load (MD_WM) + mod = modifier +*/ + +t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod) +{ +if (dev == IO_INQ) return inq_io (flag, mod); /* inq terminal? */ +if (dev == IO_MT) return mt_io (unit, flag, mod); /* magtape? */ +if (dev == IO_MTB) { /* binary? */ + if (flag == MD_WM) return STOP_INVM; /* invalid */ + return mt_io (unit, MD_BIN, mod); } +return STOP_NXD; /* not implemented */ +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < 64; i++) ind[i] = 0; +ind[IN_UNC] = 1; +AS = 0; as_err = 1; +BS = 0; bs_err = 1; +return cpu_svc (&cpu_unit); +} + +/* Breakpoint service */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr - ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & (WM + CHAR); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & (WM + CHAR); +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value % 1000) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +if (MEMSIZE > 4000) cpu_unit.flags = cpu_unit.flags | MA; +else cpu_unit.flags = cpu_unit.flags & ~MA; +return SCPE_OK; +} diff --git a/i1401_defs.h b/i1401_defs.h new file mode 100644 index 00000000..15be2922 --- /dev/null +++ b/i1401_defs.h @@ -0,0 +1,262 @@ +/* i1401_defs.h: IBM 1401 simulator definitions + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Converted t_addr to unsigned + + This simulator is based on the 1401 simulator written by Len Fehskens + with assistance from Sarah Lee Harris and Bob Supnik. This one's for + you, Len. +*/ + +#include "sim_defs.h" + +/* Simulator stop codes */ + +#define STOP_NXI 1 /* unimpl instr */ +#define STOP_NXM 2 /* non-exist mem */ +#define STOP_NXD 3 /* non-exist dev */ +#define STOP_NOWM 4 /* no WM under op */ +#define STOP_INVA 5 /* invalid A addr */ +#define STOP_INVB 6 /* invalid B addr */ +#define STOP_INVL 7 /* invalid length */ +#define STOP_INVM 8 /* invalid modifier */ +#define STOP_INVBR 9 /* invalid branch */ +#define STOP_IBKPT 10 /* breakpoint */ +#define STOP_HALT 11 /* halt */ +#define STOP_INVMTU 12 /* invalid MT unit */ +#define STOP_MTZ 13 /* MT zero lnt rec */ +#define STOP_MTL 14 /* MT write lock */ +#define STOP_CCT 15 /* inv CCT channel */ +#define STOP_NOCD 16 /* no cards left */ +#define STOP_WRAP 17 /* AS, BS mem wrap */ +#define STOP_MCE1 18 /* MCE short A field */ +#define STOP_MCE2 19 /* MCE short B field */ +#define STOP_MCE3 20 /* MCE hanging $ */ +#define STOP_IOC 21 /* I/O check */ + +/* Memory and devices */ + +#define MAXMEMSIZE 16000 /* max memory */ +#define MEMSIZE (cpu_unit.capac) /* current memory */ +#define CDR_BUF 1 /* card rdr buffer */ +#define CDR_WIDTH 80 /* card rdr width */ +#define CDP_BUF 101 /* card punch buffer */ +#define CDP_WIDTH 80 /* card punch width */ +#define LPT_BUF 201 /* line print buffer */ +#define LPT_WIDTH 132 /* line print width */ +#define CCT_LNT 132 /* car ctrl length */ +#define INQ_WIDTH 80 /* inq term width */ +#define ADDR_ERR(x) (((t_addr) (x)) >= MEMSIZE) + +/* Binary address format + + <14:0> address, with index added in + <23:16> index register memory address + <25:24> address error bits +*/ + +#define ADDRMASK 037777 /* addr mask */ +#define INDEXMASK 077777 /* addr + index mask */ +#define V_INDEX 16 +#define M_INDEX 0177 +#define V_ADDRERR 24 +#define BA (1 << V_ADDRERR) /* bad addr digit */ +#define X1 (87 << V_INDEX) /* index reg 1 */ +#define X2 (92 << V_INDEX) /* index reg 2 */ +#define X3 (97 << V_INDEX) /* index reg 3 */ + +/* CPU instruction control flags. The flag definitions must be harmonized + with the UNIT flag definitions used by the simulator. */ + +/* Lengths */ + +#define L1 0001 /* 1: op */ +#define L2 0002 /* 2: op d */ +#define L4 0004 /* 4: op aaa */ +#define L5 0010 /* 5: op aaa d */ +#define L7 0020 /* 7: op aaa bbb */ +#define L8 0040 /* 8: op aaa bbb d */ + +/* CPU options, stored in cpu_unit.flags */ + +#define MDV (1 << (UNIT_V_UF + 0)) /* multiply/divide */ +#define MR (1 << (UNIT_V_UF + 1)) /* move record */ +#define XSA (1 << (UNIT_V_UF + 2)) /* index, store addr */ +#define EPE (1 << (UNIT_V_UF + 3)) /* expanded edit */ +#define MA (1 << (UNIT_V_UF + 4)) /* modify address */ +#define BBE (1 << (UNIT_V_UF + 5)) /* br bit equal */ +#define HLE (1 << (UNIT_V_UF + 6)) /* high/low/equal */ +#define UNIT_MSIZE (1 << (UNIT_V_UF + 7)) /* fake flag */ +#define ALLOPT (MDV + MR + XSA + EPE + MA + BBE + HLE) +#define STDOPT (MR + XSA + EPE + MA + BBE + HLE) + +/* Fetch control */ + +#define AREQ (1 << (UNIT_V_UF + 8)) /* validate A */ +#define BREQ (1 << (UNIT_V_UF + 9)) /* validate B */ +#define MLS (1 << (UNIT_V_UF + 10)) /* move load store */ +#define NOWM (1 << (UNIT_V_UF + 11)) /* no WM at end */ +#define HNOP (1 << (UNIT_V_UF + 12)) /* halt or nop */ +#define IO (1 << (UNIT_V_UF + 13)) /* IO */ +#define UNIT_BCD (1 << (UNIT_V_UF + 14)) /* BCD strings */ + +#if (UNIT_V_UF < 6) || ((UNIT_V_UF + 14) > 31) + Definition error: flags overlap +#endif + +/* BCD memory character format */ + +#define WM 0100 /* word mark */ +#define ZONE 0060 /* zone */ +#define BBIT 0040 /* 1 in valid sign */ +#define ABIT 0020 /* sign (1 = +) */ +#define DIGIT 0017 /* digit */ +#define CHAR 0077 /* character */ + +#define V_WM 6 +#define V_ZONE 4 +#define V_DIGIT 0 + +/* Interesting BCD characters */ + +#define BCD_BLANK 000 +#define BCD_ONE 001 +#define BCD_TWO 002 +#define BCD_THREE 003 +#define BCD_FOUR 004 +#define BCD_FIVE 005 +#define BCD_SIX 006 +#define BCD_SEVEN 007 +#define BCD_EIGHT 010 +#define BCD_NINE 011 +#define BCD_ZERO 012 +#define BCD_ALT 020 +#define BCD_S 022 +#define BCD_U 024 +#define BCD_W 026 +#define BCD_RECMRK 032 +#define BCD_COMMA 033 +#define BCD_PERCNT 034 +#define BCD_WM 035 +#define BCD_BS 036 +#define BCD_TS 037 +#define BCD_MINUS 040 +#define BCD_M 044 +#define BCD_R 051 +#define BCD_DOLLAR 053 +#define BCD_ASTER 054 +#define BCD_AMPER 060 +#define BCD_B 062 +#define BCD_C 063 +#define BCD_E 065 +#define BCD_DECIMAL 073 +#define BCD_SQUARE 074 +#define BCD_GRPMRK 077 + +/* Opcodes */ + +#define OP_R 001 /* read */ +#define OP_W 002 /* write */ +#define OP_WR 003 /* write and read */ +#define OP_P 004 /* punch */ +#define OP_RP 005 /* read and punch */ +#define OP_WP 006 /* write and punch */ +#define OP_WRP 007 /* write read punch */ +#define OP_RF 010 /* reader feed */ +#define OP_PF 011 /* punch feed */ +#define OP_MA 013 /* modify address */ +#define OP_MUL 014 /* multiply */ +#define OP_CS 021 /* clear storage */ +#define OP_S 022 /* subtract */ +#define OP_MTF 024 /* magtape function */ +#define OP_BWZ 025 /* branch wm or zone */ +#define OP_BBE 026 /* branch bit equal */ +#define OP_MZ 030 /* move zone */ +#define OP_MSZ 031 /* move suppr zeroes */ +#define OP_SWM 033 /* set word mark */ +#define OP_DIV 034 /* divide */ +#define OP_SS 042 /* select stacker */ +#define OP_LCA 043 /* load characters */ +#define OP_MCW 044 /* move characters */ +#define OP_NOP 045 /* no op */ +#define OP_MCM 047 /* move to rec/grp mk */ +#define OP_SAR 050 /* store A register */ +#define OP_ZS 052 /* zero and subtract */ +#define OP_A 061 /* add */ +#define OP_B 062 /* branch */ +#define OP_C 063 /* compare */ +#define OP_MN 064 /* move numeric */ +#define OP_MCE 065 /* move char and edit */ +#define OP_CC 066 /* carriage control */ +#define OP_SBR 070 /* store B register */ +#define OP_ZA 072 /* zero and add */ +#define OP_H 073 /* halt */ +#define OP_CWM 074 /* clear word mark */ + +/* I/O addresses */ + +#define IO_INQ 023 /* inquiry terminal */ +#define IO_MT 024 /* magtape */ +#define IO_MTB 062 /* binary magtape */ +#define IO_DP 066 /* 1311 diskpack */ + +/* I/O modes */ + +#define MD_NORM 0 /* normal (move) */ +#define MD_WM 1 /* word mark (load) */ +#define MD_BIN 2 /* binary */ + +/* Indicator characters */ + +#define IN_UNC 000 /* unconditional */ +#define IN_CC9 011 /* carr ctrl chan 9 */ +#define IN_CC12 014 /* carr ctrl chan 12 */ +#define IN_UNQ 021 /* unequal */ +#define IN_EQU 022 /* equal */ +#define IN_LOW 023 /* low */ +#define IN_HGH 024 /* high */ +#define IN_PAR 025 /* parity check */ +#define IN_LNG 026 /* wrong lnt record */ +#define IN_UNA 027 /* unequal addr cmp */ +#define IN_DSK 030 /* disk error */ +#define IN_OVF 031 /* overflow */ +#define IN_LPT 032 /* printer error */ +#define IN_PRO 034 /* process check */ +#define IN_END 042 /* end indicator */ +#define IN_TAP 043 /* tape error */ +#define IN_ACC 045 /* access error */ +#define IN_BSY 047 /* printer busy */ +#define IN_INR 050 /* inquiry request */ +#define IN_PCB 051 /* printer carr busy */ +#define IN_PNCH 052 /* punch error */ +#define IN_INC 054 /* inquiry clear */ +#define IN_LST 061 /* last card */ +#define IN_SSB 062 /* sense switch B */ +#define IN_SSC 063 /* sense switch C */ +#define IN_SSD 064 /* sense switch D */ +#define IN_SSE 065 /* sense switch E */ +#define IN_SSF 066 /* sense switch F */ +#define IN_SSG 067 /* sense switch G */ +#define IN_READ 072 /* reader error */ diff --git a/i1401_iq.c b/i1401_iq.c new file mode 100644 index 00000000..a55c4ebb --- /dev/null +++ b/i1401_iq.c @@ -0,0 +1,151 @@ +/* i1401_iq.c: IBM 1407 inquiry terminal + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned +*/ + +#include "i1401_defs.h" +#include + +extern volatile int32 stop_cpu; +extern unsigned char M[]; +extern int32 BS, iochk, ind[64]; +extern char ascii_to_bcd[128], bcd_to_ascii[64]; +extern UNIT cpu_unit; +int32 inq_char = 033; /* request inq */ +t_stat inq_svc (UNIT *uptr); +t_stat inq_reset (DEVICE *dptr); +void puts_tty (char *cptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* INQ data structures + + inq_dev INQ device descriptor + inq_unit INQ unit descriptor + inq_reg INQ register list +*/ + +UNIT inq_unit = { UDATA (&inq_svc, 0, 0), KBD_POLL_WAIT }; + +REG inq_reg[] = { + { ORDATA (INQC, inq_char, 7) }, + { FLDATA (INR, ind[IN_INR], 0) }, + { FLDATA (INC, ind[IN_INC], 0) }, + { DRDATA (TIME, inq_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE inq_dev = { + "INQ", &inq_unit, inq_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &inq_reset, + NULL, NULL, NULL }; + +/* Terminal I/O + + Modifiers have not been checked; legal modifiers are R and W +*/ + +t_stat inq_io (int32 flag, int32 mod) +{ +int32 i, t, wm_seen = 0; + +ind[IN_INC] = 0; /* clear inq clear */ +switch (mod) { /* case on mod */ +case BCD_R: /* input */ +/* if (ind[IN_INR] == 0) return SCPE_OK; /* return if no req */ + ind[IN_INR] = 0; /* clear req */ + puts_tty ("[Enter]\r\n"); /* prompt */ + for (i = 0; M[BS] != (BCD_GRPMRK + WM); i++) { /* until GM + WM */ + while ((t = sim_poll_kbd ()) == SCPE_OK) + if (stop_cpu) return SCPE_STOP; /* interrupt? */ + if (t < SCPE_KFLAG) return t; /* if not char, err */ + t = t & 0177; + if ((t == '\r') || (t == '\n')) break; + if (t == inq_char) { /* cancel? */ + ind[IN_INC] = 1; /* set indicator */ + puts_tty ("\r\n[Canceled]\r\n"); + return SCPE_OK; } + if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); + sim_putchar (t); /* echo */ + if (flag == MD_WM) { /* word mark mode? */ + if ((t == '~') && (wm_seen == 0)) wm_seen = WM; + else { M[BS] = wm_seen | ascii_to_bcd[t]; + wm_seen = 0; } } + else M[BS] = (M[BS] & WM) | ascii_to_bcd[t]; + if (!wm_seen) BS++; + if (ADDR_ERR (BS)) { + BS = BA | (BS % MAXMEMSIZE); + return STOP_NXM; } } + puts_tty ("\r\n"); + M[BS++] = BCD_GRPMRK + WM; + return SCPE_OK; +case BCD_W: /* output */ + for (i = 0; (t = M[BS++]) != (BCD_GRPMRK + WM); i++) { + if ((flag == MD_WM) && (t & WM)) { + if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); + sim_putchar ('~'); } + if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); + sim_putchar (bcd_to_ascii[t & CHAR]); + if (ADDR_ERR (BS)) { + BS = BA | (BS % MAXMEMSIZE); + return STOP_NXM; } } + puts_tty ("\r\n"); + return SCPE_OK; +default: + return STOP_INVM; } /* invalid mod */ +return SCPE_OK; +} + +/* Unit service - polls for WRU or inquiry request */ + +t_stat inq_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&inq_unit, inq_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +if ((temp & 0177) == inq_char) ind[IN_INR] = 1; /* set indicator */ +return SCPE_OK; +} + +/* Output multiple characters */ + +void puts_tty (char *cptr) +{ +if (cptr == NULL) return; +while (*cptr != 0) sim_putchar (*cptr++); +return; +} + +/* Reset routine */ + +t_stat inq_reset (DEVICE *dptr) +{ +ind[IN_INR] = ind[IN_INC] = 0; /* clear indicators */ +sim_activate (&inq_unit, inq_unit.wait); /* activate poll */ +return SCPE_OK; +} diff --git a/i1401_lp.c b/i1401_lp.c new file mode 100644 index 00000000..3ad38b8e --- /dev/null +++ b/i1401_lp.c @@ -0,0 +1,224 @@ +/* i1401_lp.c: IBM 1403 line printer simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include "i1401_defs.h" + +extern unsigned char M[]; +extern char bcd_to_ascii[64]; +extern int32 iochk, ind[64]; +int32 cct[CCT_LNT] = { 03 }; +int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat space (int32 lines, int32 lflag); +char bcd_to_pca[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '#', '@', ' ', ' ', ' ', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', ',', '%', ' ', ' ', ' ', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '&', '.', ')', ' ', ' ', ' ' }; +char bcd_to_pch[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ' ', ' ', ' ', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', ',', '(', ' ', ' ', ' ', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '&', '.', ')', ' ', ' ', ' ' }; +char *pch_table[4] = { + bcd_to_ascii, bcd_to_pca, bcd_to_pch, bcd_to_ascii }; + +#define UNIT_V_PCHAIN (UNIT_V_UF + 0) +#define UNIT_M_PCHAIN 03 +#define M_PCF 00 /* full */ +#define M_PCA 01 /* business */ +#define M_PCH 02 /* Fortran */ +#define UNIT_PCHAIN (UNIT_M_PCHAIN << UNIT_V_PCHAIN) +#define PCF (M_PCF << UNIT_V_PCHAIN) +#define PCA (M_PCA << UNIT_V_PCHAIN) +#define PCH (M_PCH << UNIT_V_PCHAIN) +#define GET_PCHAIN(x) (((x) >> UNIT_V_PCHAIN) & UNIT_M_PCHAIN) +#define CHP(ch,val) ((val) & (1 << (ch))) + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; + +REG lpt_reg[] = { + { FLDATA (ERR, ind[IN_LPT], 0) }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { BRDATA (*CCT, cct, 8, 32, CCT_LNT), REG_HRO }, + { DRDATA (LINES, lines, 8), PV_LEFT }, + { DRDATA (CCTP, cctptr, 8), PV_LEFT }, + { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, + { GRDATA (CHAIN, lpt_unit.flags, 10, 2, UNIT_V_PCHAIN), REG_HRO }, + { NULL } }; + +MTAB lpt_mod[] = { + { UNIT_PCHAIN, PCF, "F chain", "PCF", NULL }, + { UNIT_PCHAIN, PCA, "A chain", "PCA", NULL }, + { UNIT_PCHAIN, PCH, "H chain", "PCH", NULL }, + { 0 } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, NULL }; + +/* Print routine + + Modifiers have been checked by the caller + SQUARE = word mark mode + S = suppress automatic newline +*/ + +t_stat write_line (int32 ilnt, int32 mod) +{ +int32 i, t, wm, sup; +char *pch; +static char lbuf[LPT_WIDTH + 1]; /* + null */ + +if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +wm = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_SQUARE); +sup = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_S); +ind[IN_LPT] = 0; /* clear error */ +pch = pch_table[GET_PCHAIN (lpt_unit.flags)]; /* get print chain */ +for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ + t = M[LPT_BUF + i]; + if (wm) lbuf[i] = (t & WM)? '1': ' '; /* wmarks -> 1 or sp */ + else lbuf[i] = pch[t & CHAR]; } /* normal */ +M[LPT_BUF + 1] = 0; /* trailing null */ +for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; +fputs (lbuf, lpt_unit.fileref); /* write line */ +if (lines) space (lines, lflag); /* cc action? do it */ +else if (sup == 0) space (1, FALSE); /* default? 1 line */ +else { fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ + lpt_unit.pos = ftell (lpt_unit.fileref); } /* update position */ +lines = lflag = 0; /* clear cc action */ +if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("Line printer I/O error"); + clearerr (lpt_unit.fileref); + if (iochk) return SCPE_IOERR; + ind[IN_LPT] = 1; } +return SCPE_OK; +} + +/* Carriage control routine + + The modifier has not been checked, its format is + <5:4> = 00, skip to channel now + = 01, space lines after + = 10, space lines now + = 11, skip to channel after + <3:0> = number of lines or channel number +*/ + +t_stat carriage_control (int32 mod) +{ +int32 i, action; + +action = (mod & ZONE) >> V_ZONE; /* get mod type */ +mod = mod & DIGIT; /* isolate value */ +switch (action) { +case 0: /* to channel now */ + if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) + return space (i, TRUE); } + return STOP_CCT; /* runaway channel */ +case 1: /* space after */ + if (mod <= 3) { + lines = mod; /* save # lines */ + lflag = FALSE; /* flag spacing */ + ind[IN_CC9] = ind[IN_CC12] = 0; } + return SCPE_OK; +case 2: /* space now */ + if (mod <= 3) return space (mod, FALSE); + return SCPE_OK; +case 3: /* to channel after */ + if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */ + ind[IN_CC9] = ind[IN_CC12] = 0; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) { + lines = i; /* save # lines */ + lflag = TRUE; /* flag skipping */ + return SCPE_OK; } } + return STOP_CCT; } /* runaway channel */ +return SCPE_OK; +} + +/* Space routine - space or skip n lines + + Inputs: + count = number of lines to space or skip + sflag = skip (TRUE) or space (FALSE) +*/ + +t_stat space (int32 count, int32 sflag) +{ +int32 i; + +if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; +cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ +if (sflag && CHP (0, cct[cctptr])) /* skip, top of form? */ + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ +else { for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); } +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +ind[IN_CC9] = CHP (9, cct[cctptr]) != 0; /* set indicators */ +ind[IN_CC12] = CHP (12, cct[cctptr]) != 0; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = lflag = 0; /* no cc action */ +ind[IN_LPT] = 0; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = 0; /* no cc action */ +ind[IN_LPT] = 0; +return attach_unit (uptr, cptr); +} diff --git a/i1401_mt.c b/i1401_mt.c new file mode 100644 index 00000000..0e78b759 --- /dev/null +++ b/i1401_mt.c @@ -0,0 +1,268 @@ +/* IBM 1401 magnetic tape simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + 04-Oct-98 RMS V2.4 magtape format + + Magnetic tapes are represented as a series of variable 16b records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "i1401_defs.h" + +#define MT_NUMDR 7 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_W_UF 2 /* #save flags */ + +extern unsigned char M[]; /* memory */ +extern int32 ind[64]; +extern int32 BS, iochk; +extern UNIT cpu_unit; +unsigned int8 dbuf[MAXMEMSIZE * 2]; /* tape buffer */ +t_stat mt_reset (DEVICE *dptr); +UNIT *get_unit (int32 unit); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +UNIT mt_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, /* doesn't exist */ + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + UNIT_BCD, 0) } }; + +REG mt_reg[] = { + { FLDATA (END, ind[IN_END], 0) }, + { FLDATA (ERR, ind[IN_TAP], 0) }, + { FLDATA (PAR, ind[IN_PAR], 0) }, + { DRDATA (POS1, mt_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, mt_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, mt_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, mt_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, mt_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, mt_unit[6].pos, 31), PV_LEFT + REG_RO }, + { GRDATA (FLG1, mt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, mt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, mt_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, mt_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, mt_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, mt_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { NULL } }; + +MTAB mt_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 6, + NULL, NULL, &mt_reset, + NULL, NULL, NULL }; + +/* Function routine + + Inputs: + unit = unit character + mod = modifier character + Outputs: + status = status +*/ + +t_stat mt_func (int32 unit, int32 mod) +{ +int32 err; +t_mtrlnt tbc; +UNIT *uptr; +static t_mtrlnt bceof = { 0 }; + +if ((uptr = get_unit (unit)) == NULL) return STOP_INVMTU; /* valid unit? */ +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +switch (mod) { /* case on modifier */ +case BCD_B: /* backspace */ + ind[IN_END] = 0; /* clear end of reel */ + if (uptr -> pos == 0) return SCPE_OK; /* at bot? */ + fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), + SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || + (feof (uptr -> fileref))) break; + if (tbc == 0) /* file mark? */ + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + else uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); + break; /* end case */ +case BCD_E: /* erase = nop */ + if (uptr -> flags & UNIT_WLK) return STOP_MTL; + return SCPE_OK; +case BCD_M: /* write tapemark */ + if (uptr -> flags & UNIT_WLK) return STOP_MTL; + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; +case BCD_R: /* rewind */ + uptr -> pos = 0; /* update position */ + return SCPE_OK; +case BCD_U: /* unload */ + uptr -> pos = 0; /* update position */ + return detach_unit (uptr); /* detach */ +default: + return STOP_INVM; } +if (err != 0) { /* I/O error */ + perror ("MT I/O error"); + clearerr (uptr -> fileref); + if (iochk) return SCPE_IOERR; + ind[IN_TAP] = 1; } +return SCPE_OK; +} + +/* Read and write routines + + Inputs: + unit = unit character + flag = normal, word mark, or binary mode + mod = modifier character + Outputs: + status = status +*/ + +t_stat mt_io (int32 unit, int32 flag, int32 mod) +{ +int32 err, i, t, wm_seen; +t_mtrlnt tbc; +UNIT *uptr; + +if ((uptr = get_unit (unit)) == NULL) return STOP_INVMTU; /* valid unit? */ +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +switch (mod) { +case BCD_R: /* read */ + ind[IN_TAP] = ind[IN_END] = 0; /* clear error */ + wm_seen = 0; /* no word mk seen */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || (feof (uptr -> fileref))) { + ind[IN_END] = 1; /* err or eof? */ + break; } + if (tbc == 0) { /* tape mark? */ + ind[IN_END] = 1; /* set end mark */ + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + tbc = MTRL (tbc); /* ignore error flag */ + i = fxread (dbuf, sizeof (int8), tbc, uptr -> fileref); + for ( ; i < tbc; i++) dbuf[i] = 0; /* fill with 0's */ + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + for (i = 0; (i < tbc) && (M[BS] != (BCD_GRPMRK + WM)); i++) { + t = dbuf[i]; /* get char */ + if ((flag != MD_BIN) && (t == BCD_ALT)) t = BCD_BLANK; + if (flag == MD_WM) { /* word mk mode? */ + if ((t == BCD_WM) && (wm_seen == 0)) wm_seen = WM; + else { M[BS] = wm_seen | (t & CHAR); + wm_seen = 0; } } + else M[BS] = (M[BS] & WM) | (t & CHAR); + if (!wm_seen) BS++; + if (ADDR_ERR (BS)) { + BS = BA | (BS % MAXMEMSIZE); + return STOP_NXM; } } + M[BS++] = BCD_GRPMRK + WM; /* end of record */ + break; + +case BCD_W: + if (uptr -> flags & UNIT_WLK) return STOP_MTL; /* locked? */ + if (M[BS] == (BCD_GRPMRK + WM)) return STOP_MTZ; /* eor? */ + ind[IN_TAP] = ind[IN_END] = 0; /* clear error */ + for (tbc = 0; (t = M[BS++]) != (BCD_GRPMRK + WM); ) { + if ((t & WM) && (flag == MD_WM)) dbuf[tbc++] = BCD_WM; + if (((t & CHAR) == BCD_BLANK) && (flag != MD_BIN)) + dbuf[tbc++] = BCD_ALT; + else dbuf[tbc++] = t & CHAR; + if (ADDR_ERR (BS)) { + BS = BA | (BS % MAXMEMSIZE); + return STOP_NXM; } } + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + fxwrite (dbuf, sizeof (int8), (tbc + 1) & ~1, uptr -> fileref); + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + break; +default: + return STOP_INVM; } + +if (err != 0) { /* I/O error */ + perror ("MT I/O error"); + clearerr (uptr -> fileref); + if (iochk) return SCPE_IOERR; + ind[IN_TAP] = 1; } /* flag error */ +return SCPE_OK; +} + +/* Get unit pointer from unit number */ + +UNIT *get_unit (int32 unit) +{ +if ((unit <= 0) || (unit >= MT_NUMDR)) return NULL; +else return mt_dev.units + unit; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +ind[IN_END] = ind[IN_PAR] = ind[IN_TAP] = 0; /* clear indicators */ +return SCPE_OK; +} diff --git a/i1401_sys.c b/i1401_sys.c new file mode 100644 index 00000000..57d91c77 --- /dev/null +++ b/i1401_sys.c @@ -0,0 +1,306 @@ +/* i1401_sys.c: IBM 1401 simulator interface + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface +*/ + +#include "i1401_defs.h" +#include + +#define LINE_LNT 80 +extern DEVICE cpu_dev, inq_dev, lpt_dev; +extern DEVICE cdr_dev, cdp_dev, stack_dev; +extern DEVICE mt_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern unsigned char M[]; +extern char bcd_to_ascii[64], ascii_to_bcd[128]; +extern char *get_glyph (char *cptr, char *gbuf, char term); +extern int32 store_addr_h (int32 addr); +extern int32 store_addr_t (int32 addr); +extern int32 store_addr_u (int32 addr); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 1401"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = LINE_LNT; + +DEVICE *sim_devices[] = { &cpu_dev, &inq_dev, + &cdr_dev, &cdp_dev, &stack_dev, &lpt_dev, + &mt_dev, NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "Non-existent memory", + "Non-existent device", + "No WM at instruction start", + "Invalid A address", + "Invalid B address", + "Invalid instruction length", + "Invalid modifer", + "Invalid branch address", + "Breakpoint", + "HALT instruction", + "Invalid MT unit number", + "Invalid MT record length", + "Write to locked MT unit", + "Skip to unpunched CCT channel", + "Card reader empty", + "Address register wrap", + "MCE data field too short", + "MCE control field too short", + "MCE EPE hanging $", + "I/O check" }; + +/* Binary loader -- load carriage control tape + + A carriage control tape consists of entries of the form + + (repeat count) column number,column number,column number,... + + The CCT entries are stored in cct[0:lnt-1], cctlnt contains the + number of entries +*/ + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; +t_stat r; +extern int32 cctlnt, cctptr, cct[CCT_LNT]; +char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +ptr = 0; +for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ + mask = 0; + if (*cptr == '(') { /* repeat count? */ + cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ + rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ + if (r != SCPE_OK) return SCPE_FMT; } + else rpt = 1; + while (*cptr != 0) { /* get col no's */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + col = get_uint (gbuf, 10, 12, &r); /* column number */ + if (r != SCPE_OK) return SCPE_FMT; + mask = mask | (1 << col); } /* set bit */ + for ( ; rpt > 0; rpt--) { /* store vals */ + if (ptr >= CCT_LNT) return SCPE_FMT; + cctbuf[ptr++] = mask; } } +if (ptr == 0) return SCPE_FMT; +cctlnt = ptr; +cctptr = 0; +for (rpt = 0; rpt < cctlnt; rpt++) cct[rpt] = cctbuf[rpt]; +return SCPE_OK; +} + +/* Symbol table */ + +const char *opcode[64] = { + NULL, "R", "W", "WR", "P", "RP", "WP", "WRP", + "RF", "WF", NULL, "MA", "MUL", NULL, NULL, NULL, + NULL, "CS", "S", NULL, "MTF", "BWZ", "BBE", NULL, + "MZ", "MSZ", NULL, "SWM", "DIV", NULL, NULL, NULL, + NULL, NULL, "SS", "LCA", "MCW", "NOP", NULL, "MCM", + "SAR", NULL, "ZS", NULL, NULL, NULL, NULL, NULL, + NULL, "A", "B", "C", "MN", "MCE", "CC", NULL, + "SBR", NULL, "ZA", "H", "CWM", NULL, NULL, NULL }; + +/* Print an address from three characters */ + +void fprint_addr (FILE *of, t_value *dig) +{ +int32 addr, xa; +extern int32 hun_table[64], ten_table[64], one_table[64]; + +addr = hun_table[dig[0]] + ten_table[dig[1]] + one_table[dig[2]]; +xa = (addr >> V_INDEX) & M_INDEX; +if (xa) fprintf (of, " %d,%d", addr & ADDRMASK, ((xa - (X1 >> V_INDEX)) / 5) + 1); +else if (addr >= MAXMEMSIZE) fprintf (of, " %d*", addr & ADDRMASK); +else fprintf (of, " %d", addr); +return; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current address + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 op, flags, ilnt, i, t; +extern int32 op_table[64], len_table[9]; + +if (sw & SWMASK ('C')) { /* character? */ + t = val[0]; + if (uptr -> flags & UNIT_BCD) + fprintf (of, (t & WM)? "~%c": "%c", bcd_to_ascii[t & CHAR]); + else fprintf (of, FMTASC (t & 0177)); + return SCPE_OK; } +if ((uptr != NULL) && (uptr != &cpu_unit)) return SCPE_ARG; /* CPU? */ +if (sw & SWMASK ('S')) { /* string? */ + i = 0; + do { t = val[i++]; + fprintf (of, (t & WM)? "~%c": "%c", bcd_to_ascii[t & CHAR]); } + while ((i < LINE_LNT) && ((val[i] & WM) == 0)); + return -(i - 1); } +if ((sw & SWMASK ('M')) == 0) return SCPE_ARG; + +if ((val[0] & WM) == 0) return STOP_NOWM; /* WM under op? */ +flags = op_table[val[0] & CHAR]; /* get flags */ +for (ilnt = 1; ilnt < sim_emax; ilnt++) if (val[ilnt] & WM) break; +if (flags & HNOP) ilnt = 1; /* halt, nop? */ +else if ((flags & NOWM) && (ilnt > 7)) ilnt = 7; /* cs, swm? */ +else if ((op == OP_B) && (ilnt > 4) && (val[4] == BCD_BLANK)) ilnt = 4; +else if (ilnt > 8) ilnt = 8; /* cap length */ +if ((flags & len_table[ilnt]) == 0) return STOP_INVL; /* legal length? */ +fprintf (of, "%s",opcode[val[0] & CHAR]); /* print opcode */ +if (ilnt > 2) { /* A address? */ + if ((flags & IO) && (val[1] == BCD_PERCNT)) fprintf (of, " %%%c%c", + bcd_to_ascii[val[2]], bcd_to_ascii[val[3]]); + else fprint_addr (of, &val[1]); } +if (ilnt > 5) fprint_addr (of, &val[4]); /* B address? */ +if ((ilnt == 2) || (ilnt == 5) || (ilnt == 8)) /* d character? */ + fprintf (of, " '%c", bcd_to_ascii[val[ilnt - 1]]); +return -(ilnt - 1); /* return # chars */ +} + +/* get_addr - get address + index pair */ + +t_stat get_addr (char *cptr, t_value *val) +{ +int32 addr, index; +t_stat r; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, ','); /* get address */ +addr = get_uint (gbuf, 10, MAXMEMSIZE, &r); +if (r != SCPE_OK) return SCPE_ARG; +if (*cptr != 0) { /* more? */ + cptr = get_glyph (cptr, gbuf, ' '); + index = get_uint (gbuf, 10, 3, &r); + if ((r != SCPE_OK) || (index == 0)) return SCPE_ARG; } +else index = 0; +if (*cptr != 0) return SCPE_ARG; +val[0] = store_addr_h (addr); +val[1] = store_addr_t (addr) | (index << V_ZONE); +val[2] = store_addr_u (addr); +return SCPE_OK; +} + +/* get_io - get I/O address */ + +t_stat get_io (char *cptr, t_value *val) +{ +if ((cptr[0] != '%') || (cptr[3] != 0) || !isalnum (cptr[1]) || + !isalnum (cptr[2])) return SCPE_ARG; +val[0] = BCD_PERCNT; +val[1] = ascii_to_bcd[cptr[1]]; +val[2] = ascii_to_bcd[cptr[2]]; +return SCPE_OK; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, op, ilnt, t, cflag, wm_seen; +extern int32 op_table[64], len_table[9]; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('C')) || (sw & SWMASK ('S')) || (*cptr == '~') || + ((*cptr == '\'') && cptr++) || ((*cptr == '"') && cptr++)) { + wm_seen = 0; + for (i = 0; (i < sim_emax) && (*cptr != 0); ) { + t = *cptr++; /* get character */ + if (cflag && (wm_seen == 0) && (t == '~')) wm_seen = WM; + else if (uptr -> flags & UNIT_BCD) { + if (t < 040) return SCPE_ARG; + val[i++] = ascii_to_bcd[t] | wm_seen; + wm_seen = 0; } + else val[i++] = t; } + if ((i == 0) || wm_seen) return SCPE_ARG; + return -(i-1); } + +if (cflag == 0) return SCPE_ARG; /* CPU only */ +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (op = 0; op < 64; op++) /* look it up */ + if (opcode[op] && strcmp (gbuf, opcode[op]) == 0) break; +if (op >= 64) return SCPE_ARG; /* successful? */ +val[0] = op | WM; /* store opcode */ +cptr = get_glyph (cptr, gbuf, ' '); /* get addr or d */ +if (((op_table[op] && IO) && (get_io (gbuf, &val[1]) == SCPE_OK)) || + (get_addr (gbuf, &val[1]) == SCPE_OK)) { + cptr = get_glyph (cptr, gbuf, ' '); /* get addr or d */ + if (get_addr (gbuf, &val[4]) == SCPE_OK) { + cptr = get_glyph (cptr, gbuf, ' '); /* get d */ + ilnt = 7; } /* a and b addresses */ + else ilnt = 4; } /* a address */ +else ilnt = 1; /* no addresses */ +if ((gbuf[0] == '\'') || (gbuf[0] == '"')) { /* d character? */ + t = gbuf[1]; + if ((gbuf[2] != 0) || (*cptr != 0) || (t < 040)) + return SCPE_ARG; /* end and legal? */ + val[ilnt] = ascii_to_bcd[t]; /* save D char */ + ilnt = ilnt + 1; } +if ((op_table[op] & len_table[ilnt]) == 0) return STOP_INVL; +return -(ilnt - 1); +} diff --git a/id4_cpu.c b/id4_cpu.c new file mode 100644 index 00000000..d1e5f854 --- /dev/null +++ b/id4_cpu.c @@ -0,0 +1,769 @@ +/* id4_cpu.c: Interdata 4 CPU simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 07-Oct-00 RMS Overhauled I/O subsystem + 14-Apr-99 RMS Changed t_addr to unsigned + + The register state for the Interdata 4 CPU is: + + R[0:F]<0:15> general registers + F[0:7]<0:31> floating point registers + PSW<0:31> processor status word, including + STAT<0:11> status flags + CC<0:3> condition codes + PC<0:15> program counter + int_req[8]<0:31> interrupt requests + int_enb[8]<0:31> interrupt enables + + The Interdata 4 has three instruction formats: register to register, + register to memory, and register to storage. The formats are: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | R2 | register-register + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-memory + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-storage + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + For register-memory and register-storage instructions, an effective + address is calculated as follows: + + effective addr = address + RX (if RX > 0) + + Register-memory instructions can access an address space of 65K bytes. +*/ + +/* This routine is the instruction decode routine for the Interdata 4. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + wait state and no I/O outstanding + invalid instruction + I/O error in I/O simulator + + 2. Interrupts. Each device has an interrupt request flag and an + interrupt enabled flag. To facilitate evaluation, all interrupt + requests are kept in int_req, and all enables in int_enb. If + external interrupts are enabled in the PSW, and an external request + is pending, an interrupt occurs. + + 3. Non-existent memory. On the Interdata 4, reads to non-existent + memory return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + id4_defs.h add a definition for the device mnemonic + id4_cpu.c add dispatch routine to table dev_tab + id4_sys.c add pointer to data structures to sim_devices +*/ + +#include "id4_defs.h" + +#define ILL_ADR_FLAG (MAXMEMSIZE) +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_MSIZE (UNIT_V_UF) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define SIGN_EXT(x) (((x) & SIGN)? (x) | ~MAGMASK: (x)) +#define CC_GL(x) if ((x) & SIGN) CC = CC_L; \ + else if (x) CC = CC_G; \ + else CC = 0 +#define CC_GL_C(x) if ((x) & SIGN) CC = CC_L; \ + else if (x) CC = CC_G; \ + else CC = 0 +/* else CC = CC & (CC_G | CC_L) */ +#define BUILD_PSW ((PSW & ~CC_MASK) | CC) +#define PSW_SWAP(o,n) WriteW ((o), BUILD_PSW); \ + WriteW ((o) + 2, PC); \ + PSW = ReadW (n); \ + PC = ReadW ((n) + 2); \ + CC = PSW & CC_MASK + +unsigned int16 M[MAXMEMSIZE >> 1] = { 0 }; /* memory */ +int32 R[16] = { 0 }; /* general registers */ +unsigned int32 F[8] = { 0 }; /* fp registers */ +int32 PSW = 0; /* processor status word */ +int32 saved_PC = 0; /* program counter */ +int32 SR = 0; /* switch register */ +int32 DR = 0; /* display register */ +int32 drmod = 0; /* mode */ +int32 srpos = 0; /* switch register pos */ +int32 drpos = 0; /* display register pos */ +int32 int_req[INTSZ] = { 0 }; /* interrupt requests */ +int32 int_enb[INTSZ] = { 0 }; /* interrupt enables */ +t_bool qanyin = FALSE; /* interrupt outstanding */ +int32 stop_inst = 0; /* stop on ill inst */ +int32 ibkpt_addr = ILL_ADR_FLAG | AMASK; /* breakpoint addr */ +int32 old_PC = 0; /* previous PC */ + +extern int32 sim_int_char; +extern UNIT *sim_clock_queue; +t_bool int_eval (void); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +extern int32 le (int32 op, int32 r1, int32 r2, int32 ea); +extern int32 ce (int32 op, int32 r1, int32 r2, int32 ea); +extern int32 ase (int32 op, int32 r1, int32 r2, int32 ea); +extern int32 me (int32 op, int32 r1, int32 r2, int32 ea); +extern int32 de (int32 op, int32 r1, int32 r2, int32 ea); +extern int32 display (int32 op, int32 datout); +extern int32 tt (int32 op, int32 datout); +extern int32 pt (int32 op, int32 datout); + +int32 (*dev_tab[DEVNO])(int32 op, int32 datout) = { + NULL, &display, &tt, &pt, NULL, NULL, NULL, NULL }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK, + MAXMEMSIZE) }; + +REG cpu_reg[] = { + { HRDATA (PC, saved_PC, 16) }, + { HRDATA (R0, R[0], 16) }, + { HRDATA (R1, R[1], 16) }, + { HRDATA (R2, R[2], 16) }, + { HRDATA (R3, R[3], 16) }, + { HRDATA (R4, R[4], 16) }, + { HRDATA (R5, R[5], 16) }, + { HRDATA (R6, R[6], 16) }, + { HRDATA (R7, R[7], 16) }, + { HRDATA (R8, R[8], 16) }, + { HRDATA (R9, R[9], 16) }, + { HRDATA (RA, R[10], 16) }, + { HRDATA (RB, R[11], 16) }, + { HRDATA (RC, R[12], 16) }, + { HRDATA (RD, R[13], 16) }, + { HRDATA (RE, R[14], 16) }, + { HRDATA (RF, R[15], 16) }, + { HRDATA (F0, F[0], 32) }, + { HRDATA (F2, F[1], 32) }, + { HRDATA (F4, F[2], 32) }, + { HRDATA (F6, F[3], 32) }, + { HRDATA (F8, F[4], 32) }, + { HRDATA (FA, F[5], 32) }, + { HRDATA (FC, F[6], 32) }, + { HRDATA (FE, F[7], 32) }, + { HRDATA (PSW, PSW, 16) }, + { HRDATA (CC, PSW, 4) }, + { HRDATA (SR, SR, 16) }, + { HRDATA (DR, DR, 16) }, + { GRDATA (DR1, DR, 16, 16, 16) }, + { FLDATA (DRMOD, drmod, 0) }, + { FLDATA (SRPOS, srpos, 0) }, + { HRDATA (DRPOS, drpos, 2) }, + { HRDATA (IRQ0, int_req[0], 32) }, + { HRDATA (IRQ1, int_req[1], 32) }, + { HRDATA (IRQ2, int_req[2], 32) }, + { HRDATA (IRQ3, int_req[3], 32) }, + { HRDATA (IRQ4, int_req[4], 32) }, + { HRDATA (IRQ5, int_req[5], 32) }, + { HRDATA (IRQ6, int_req[6], 32) }, + { HRDATA (IRQ7, int_req[7], 32) }, + { HRDATA (IEN0, int_enb[0], 32) }, + { HRDATA (IEN1, int_enb[1], 32) }, + { HRDATA (IEN2, int_enb[2], 32) }, + { HRDATA (IEN3, int_enb[3], 32) }, + { HRDATA (IEN4, int_enb[4], 32) }, + { HRDATA (IEN5, int_enb[5], 32) }, + { HRDATA (IEN6, int_enb[6], 32) }, + { HRDATA (IEN7, int_enb[7], 32) }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { HRDATA (OLDPC, old_PC, 16), REG_RO }, + { HRDATA (BREAK, ibkpt_addr, 17) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size}, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size}, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 16, 2, 16, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 dev, i, j, r, t; +register int32 PC, OP, R1, R2, EA, CC; +int32 inc, lim; +register t_stat reason; + +/* Restore register state */ + +PC = saved_PC & AMASK; +CC = PSW & CC_MASK; /* isolate cond codes */ +qanyin = int_eval (); /* eval interrupts */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + qanyin = int_eval (); } + +if ((PSW & PSW_EXI) && qanyin) { /* interrupt? */ + PSW_SWAP (EXOPSW, EXNPSW); /* swap PSW */ + continue; } + +if (PSW & PSW_WAIT) { /* wait state? */ + if (sim_clock_queue != NULL) sim_interval = 0; /* force check */ + else reason = STOP_WAIT; + continue; } + +if (PC == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save address */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + +sim_interval = sim_interval - 1; +t = ReadW (PC); /* fetch instr */ +PC = (PC + 2) & AMASK; /* increment PC */ +OP = (t >> 8) & 0xFF; /* isolate op, R1, R2 */ +R1 = (t >> 4) & 0xF; +R2 = t & 0xF; +if (OP & OP_4B) { /* RX or RS? */ + EA = ReadW (PC); /* fetch address */ + PC = (PC + 2) & AMASK; /* increment PC */ + if (R2) EA = (EA + R[R2]) & AMASK; /* index calculation */ + } +else EA = R[R2]; /* RR? "EA" = reg content */ +switch (OP) { /* case on opcode */ + +/* Load/store instructions */ + +case 0x48: /* LH */ + EA = ReadW (EA); /* fetch operand */ +case 0x08: /* LHR */ +case 0xC8: /* LHI */ + R[R1] = EA; /* load operand */ + CC_GL (R[R1]); /* set G,L */ + break; + +case 0x40: /* STH */ + WriteW (EA, R[R1]); /* store register */ + break; + +case 0xD1: /* LM */ + for ( ; R1 <= 0xF; R1++) { /* loop thru reg */ + R[R1] = ReadW (EA); /* load register */ + EA = (EA + 2) & AMASK; } /* incr mem addr */ + break; + +case 0xD0: /* STM */ + for ( ; R1 <= 0xF; R1++) { /* loop thru reg */ + WriteW (EA, R[R1]); /* store register */ + EA = (EA + 2) & AMASK; } /* incr mem addr */ + break; + +case 0x93: /* LDBR */ + R[R1] = R[R2] & 0xFF; /* load byte */ + break; +case 0xD3: /* LDB */ + R[R1] = ReadB (EA); /* load byte */ + break; + +case 0x92: /* STBR */ + R[R2] = (R[R2] & ~0xFF) | (R[R1] & 0xFF); /* store byte */ + break; +case 0xD2: /* STB */ + WriteB (EA, R[R1] & 0xFF); /* store byte */ + break; + +/* Control instructions */ + +case 0x01: /* BALR */ +case 0x41: /* BAL */ + old_PC = R[R1] = PC; /* save cur PC */ + PC = EA; /* branch */ + break; + +case 0x02: /* BTCR */ +case 0x42: /* BTC */ + if (CC & R1) { /* test CC's */ + old_PC = PC; /* branch if true */ + PC = EA; } + break; + +case 0x03: /* BFCR */ +case 0x43: /* BFC */ + if ((CC & R1) == 0) { /* test CC's */ + old_PC = PC; /* branch if false */ + PC = EA; } + break; + +case 0xC0: /* BXH */ + inc = R[(R1 + 1) & 0xF]; /* inc = R1 + 1 */ + lim = R[(R1 + 2) & 0xF]; /* lim = R1 + 2 */ + R[R1] = (R[R1] + inc) & DMASK; /* or -? */ + if (R[R1] > lim) { /* if R1 > lim */ + old_PC = PC; /* branch */ + PC = EA; } + break; + +case 0xC1: /* BXLE */ + inc = R[(R1 + 1) & 0xF]; /* inc = R1 + 1 */ + lim = R[(R1 + 2) & 0xF]; /* lim = R1 + 2 */ + R[R1] = (R[R1] + inc) & DMASK; /* R1 = R1 + inc */ + if (R[R1] <= lim) { /* if R1 <= lim */ + old_PC = PC; /* branch */ + PC = EA; } + break; + +case 0xC2: /* LPSW */ + old_PC = PC; /* effective branch */ + PSW = ReadW (EA); /* read PSW/CC */ + CC = PSW & CC_MASK; /* separate CC */ + PC = ReadW ((EA + 2) & AMASK); /* read PC */ + break; + +/* Logical and shift instructions */ + +case 0x44: /* NH */ + EA = ReadW (EA); /* fetch operand */ +case 0x04: /* NHR */ +case 0xC4: /* NHI */ + R[R1] = R[R1] & EA; /* result */ + CC_GL (R[R1]); /* set G,L */ + break; + +case 0x46: /* OH */ + EA = ReadW (EA); /* fetch operand */ +case 0x06: /* OHR */ +case 0xC6: /* OHI */ + R[R1] = R[R1] | EA; /* result */ + CC_GL (R[R1]); /* set G,L */ + break; + +case 0x47: /* XH */ + EA = ReadW (EA); /* fetch operand */ +case 0x07: /* XHR */ +case 0xC7: /* XHI */ + R[R1] = R[R1] ^ EA; /* result */ + CC_GL (R[R1]); /* set G,L */ + break; + +case 0xCC: /* SRHL */ + t = EA & 0xF; /* shift count */ + r = R[R1] >> t; /* result */ + CC_GL (r); /* set G,L */ + if (t && ((R[R1] >> (t - 1)) & 1)) CC = CC | CC_C; + R[R1] = r; /* store result */ + break; + +case 0xCD: /* SLHL */ + t = EA & 0xF; /* shift count */ + r = R[R1] << t; /* result */ + R[R1] = r & DMASK; /* store masked result */ + CC_GL (R[R1]); /* set G,L */ + if (t && (r & 0x10000)) CC = CC_C; /* set C if shft out */ + break; + +case 0xCE: /* SRHA */ + t = EA & 0xF; /* shift count */ + r = (SIGN_EXT (R[R1]) >> t) & DMASK; /* result */ + CC_GL (r); /* set G,L */ + if (t && ((R[R1] >> (t - 1)) & 1)) CC = CC | CC_C; + R[R1] = r; /* store result */ + break; + +case 0xCF: /* SLHA */ + t = EA & 0xF; /* shift count */ + r = R[R1] << t; /* raw result */ + R[R1] = (R[R1] & SIGN) | (r & MAGMASK); /* arith result */ + CC_GL (R[R1]); /* set G,L */ + if (t && (r & SIGN)) CC = CC | CC_C; /* set C if shft out */ + break; + +/* Arithmetic instructions */ + +case 0x45: /* CLH */ + EA = ReadW (EA); /* fetch operand */ +case 0x05: /* CLHR */ +case 0xC5: /* CLHI */ + r = (R[R1] - EA) & DMASK; /* result */ + CC_GL (r); /* set G,L */ + if (R[R1] < EA) CC = CC | CC_C; /* set C if borrow */ + if (((R[R1] ^ EA) & (~R[R1] ^ r)) & SIGN) CC = CC | CC_V; + break; + +case 0x4A: /* AH */ + EA = ReadW (EA); /* fetch operand */ +case 0x0A: /* AHR */ +case 0xCA: /* AHI */ + r = (R[R1] + EA) & DMASK; /* result */ + CC_GL (r); /* set G,L */ + if (r < EA) CC = CC | CC_C; /* set C if carry */ + if (((~R[R1] ^ EA) & (R[R1] ^ r)) & SIGN) CC = CC | CC_V; + R[R1] = r; + break; + +case 0x4B: /* SH */ + EA = ReadW (EA); /* fetch operand */ +case 0x0B: /* SHR */ +case 0xCB: /* SHI */ + r = (R[R1] - EA) & DMASK; /* result */ + CC_GL (r); /* set G,L */ + if (R[R1] < EA) CC = CC | CC_C; /* set C if borrow */ + if (((R[R1] ^ EA) & (~R[R1] ^ r)) & SIGN) CC = CC | CC_V; + R[R1] = r; + break; + +case 0x4C: /* MH */ + EA = ReadW (EA); /* fetch operand */ +case 0x0C: /* MHR */ + r = SIGN_EXT (R[R1 | 1]) * SIGN_EXT (EA); /* multiply */ + R[R1] = (r >> 16) & DMASK; /* high result */ + R[R1 | 1] = r & DMASK; /* low result */ + break; + +case 0x4D: /* DH */ + EA = ReadW (EA); /* fetch operand */ +case 0x0D: /* DHR */ + r = (SIGN_EXT (R[R1]) << 16) | R[R1 | 1]; /* form 32b divd */ + if (EA) { /* if divisor != 0 */ + t = r / SIGN_EXT (EA); /* quotient */ + r = r % SIGN_EXT (EA); } /* remainder */ + if (EA && ((t < 0x8000) || (t >= -0x8000))) { /* if quo fits */ + R[R1] = r; /* store remainder */ + R[R1 | 1] = t; } /* store quotient */ + else if (PSW & PSW_DFI) { /* div fault enabled? */ + PSW_SWAP (IDOPSW, IDNPSW); } /* swap PSW */ + break; + +case 0x4E: /* ACH */ + EA = ReadW (EA); /* fetch operand */ +case 0x0E: /* ACHR */ + t = R[R1] + EA + ((CC & CC_C) != 0); /* raw result */ + r = t & 0xFFFF; /* masked result */ + CC_GL_C (r); /* set G,L */ + if (t > DMASK) CC = CC | CC_C; /* set C if carry */ + if (((~R[R1] ^ EA) & (R[R1] ^ r)) & SIGN) CC = CC | CC_V; + R[R1] = r; /* store result */ + break; + +case 0x4F: /* SCH */ + EA = ReadW (EA); /* fetch operand */ +case 0x0F: /* SCHR */ + t = R[R1] - EA - ((CC & CC_C) != 0); /* raw result */ + r = t & 0xFFFF; /* masked result */ + CC_GL_C (r); /* set G,L */ + if (t < 0) CC = CC | CC_C; /* set C if borrow */ + if (((R[R1] ^ EA) & (~R[R1] ^ t)) & 0x8000) CC = CC | CC_V; + R[R1] = r; /* store result */ + break; + +/* Floating point instructions */ + +case 0x68: /* LE */ +case 0x28: /* LER */ + CC = le (OP, R1, R2, EA); + break; + +case 0x69: /* CE */ +case 0x29: /* CER */ + CC = ce (OP, R1, R2, EA); + break; + +case 0x6A: /* AE */ +case 0x6B: /* SE */ +case 0x2A: /* AER */ +case 0x2B: /* SER */ + CC = ase (OP, R1, R2, EA); + break; + +case 0x6C: /* ME */ +case 0x2C: /* MER */ + CC = me (OP, R1, R2, EA); + break; + +case 0x6D: /* DE */ +case 0x2D: /* DER */ + t = de (OP, R1, R2, EA); /* perform divide */ + if (t >= 0) CC = t; /* if ok, set CC */ + else if (PSW & PSW_FDI) { /* else fault */ + PSW_SWAP (FDOPSW, FDNPSW); } /* swap PSW */ + break; + +case 0x60: /* STE */ + WriteW ((F[R1 >> 1] >> 16) & DMASK, EA); + WriteW (F[R1 >> 1] & DMASK, ((EA + 2) & AMASK)); + break; + +/* I/O instructions */ + +case 0xDE: /* OC */ + EA = ReadW (EA); /* fetch operand */ +case 0x9E: /* OCR */ + dev = R[R1] & DEV_MAX; + if (dev_tab[dev]) { + dev_tab[dev] (IO_ADR, EA); /* select */ + t = dev_tab[dev] (IO_OC, EA); /* send command */ + qanyin = int_eval (); /* re-eval intr */ + if (t & IOT_EXM) CC = CC_V; /* set V if err */ + else CC = 0; + reason = t >> IOT_V_REASON; } + else CC = CC_V; + break; + +case 0xDA: /* WD */ + EA = ReadW (EA); /* fetch operand */ +case 0x9A: /* WDR */ + dev = R[R1] & DEV_MAX; + if (dev_tab[dev]) { + dev_tab[dev] (IO_ADR, EA); /* select */ + t = dev_tab[dev] (IO_WD, EA); /* send data */ + qanyin = int_eval (); /* re-eval intr */ + if (t & IOT_EXM) CC = CC_V; /* set V if err */ + else CC = 0; + reason = t >> IOT_V_REASON; } + else CC = CC_V; + break; + +case 0xD6: /* WB */ +case 0x96: /* WBR */ + dev = R[R1] & DEV_MAX; + if (OP & OP_4B) { + EA = ReadW (EA); /* start */ + lim = ReadW ((EA + 2) & 0xFFFF); } /* end */ + else lim = R[(R2 + 1) & 0xF]; + if (dev_tab[dev]) { + dev_tab[dev] (IO_ADR, EA); /* select */ + for ( ; EA <= lim; EA = ((EA + 1) & 0xFFFF)) { + t = dev_tab[dev] (IO_WD, ReadB (EA)); + if (reason = t >> IOT_V_REASON) break; + t = dev_tab[dev] (IO_SS, 0); + if (CC = t & 0xF) break; } + qanyin = int_eval (); } /* re-eval intr */ + else CC = CC_V; + break; + +case 0xDB: /* RD */ +case 0x9B: /* RDR */ + dev = R[R1] & DEV_MAX; + if (dev_tab[dev]) { + dev_tab[dev] (IO_ADR, EA); /* select */ + t = dev_tab[dev] (IO_RD, 0); /* get data */ + qanyin = int_eval (); /* re-eval intr */ + if (OP & OP_4B) { /* RX or RR? */ + WriteB (EA, t & 0xFF); } + else R[R2] = t & 0xFF; + if (t & IOT_EXM) CC = CC_V; /* set V if err */ + else CC = 0; + reason = t >> IOT_V_REASON; } + else CC = CC_V; + break; + +case 0xD7: /* RB */ +case 0x97: /* RBR */ + dev = R[R1] & DEV_MAX; + if (OP & OP_4B) { + EA = ReadW (EA); /* start */ + lim = ReadW ((EA + 2) & 0xFFFF); } /* end */ + else lim = R[(R2 + 1) & 0xF]; + if (dev_tab[dev]) { + dev_tab[dev] (IO_ADR, EA); /* select */ + for ( ; EA <= lim; EA = ((EA + 1) & 0xFFFF)) { + t = dev_tab[dev] (IO_RD, 0); + WriteB (EA, t & 0xFF); + if (reason = t >> IOT_V_REASON) break; + t = dev_tab[dev] (IO_SS, 0); + if (CC = t & 0xF) break; } + qanyin = int_eval (); } /* re-eval intr */ + else CC = CC_V; + break; + +case 0xDF: /* AI */ +case 0x9F: /* AIR */ + for (i = t = 0; i < INTSZ; i++) { /* loop thru array */ + register unsigned int32 temp; + if (temp = int_req[i] & int_enb[i]) { /* loop thru word */ + for (j = 0; j < 32; j++) { + if (temp & INT_V(j)) break; + t = t + 1; } } + else t = t + 32; } + R[R1] = t & DEV_MAX; + CLR_INT (t & DEV_MAX); /* clear int req */ + /* fall through */ +case 0xDD: /* SS */ +case 0x9D: /* SSR */ + dev = R[R1] & DEV_MAX; + if (dev_tab[dev]) { + dev_tab[dev] (IO_ADR, EA); /* select */ + t = dev_tab[dev] (IO_SS, 0); /* get status */ + qanyin = int_eval (); /* re-eval intr */ + if (OP & OP_4B) { /* RR or RX? */ + WriteB (EA, t & 0xFF); } + else R[R2] = t & 0xFF; + CC = t & 0xF; + reason = t >> IOT_V_REASON; } + else CC = CC_V; + break; + +default: /* undefined */ + PC = (PC - ((OP & OP_4B)? 4: 2)) & AMASK; + if (reason = stop_inst) break; /* stop on undef? */ + PSW_SWAP (ILOPSW, ILNPSW); /* swap PSW */ + break; } /* end switch */ + } /* end while */ + +/* Simulation halted */ + +PSW = BUILD_PSW; +saved_PC = PC & AMASK; +return reason; +} + +/* Evaluate interrupt */ + +t_bool int_eval (void) +{ +int i; + +for (i = 0; i < INTSZ; i++) + if (int_req[i] & int_enb[i]) return TRUE; +return FALSE; +} + +/* Display register device */ + +int32 display (int32 op, int32 dat) +{ +int t; + +switch (op) { +case IO_ADR: /* select */ + drpos = srpos = 0; /* clear counters */ + break; +case IO_OC: /* command */ + op = op & 0xC0; + if (op == 0x40) drmod = 1; /* x40 = inc */ + else if (op == 0x80) drmod = 0; /* x80 = norm */ + else if (op == 0xC0) drmod = drmod ^ 1; /* xC0 = flip */ + break; +case IO_WD: /* write */ + DR = (DR & ~(0xFF << (drpos * 8))) | (dat << (drpos * 8)); + if (drmod) drpos = (drpos + 1) & 0x3; + break; +case IO_RD: /* read */ + t = (SR >> (srpos * 8)) & 0xFF; + srpos = srpos ^ 1; + return t; +case IO_SS: /* status */ + return 0x80; } +return 0; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +PSW = 0; +DR = 0; +drmod = 0; +return cpu_svc (&cpu_unit); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = ReadW (addr); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +WriteW (addr, val); +return SCPE_OK; +} + +/* Breakpoint service */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 0xFFF) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} diff --git a/id4_defs.h b/id4_defs.h new file mode 100644 index 00000000..c7b85475 --- /dev/null +++ b/id4_defs.h @@ -0,0 +1,143 @@ +/* id4_defs.h: Interdata 4 simulator definitions + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 07-Oct-00 RMS Overhauled I/O subsystem + 14-Apr-99 RMS Changed t_addr to unsigned + + The author gratefully acknowledges the help of Carl Friend, who provided + key documents about the Interdata 4. Questions answered to date: + + 1. Do device interrupt enables mask interrupt requests or prevent + interrupt requests? A: Mask interrupt requests. + 2. Does SLHA set C from shift out of bit <0> or bit <1>? A: From <1>. + 3. What is the limit on device numbers? A: 256. How big must the + interrupt request and enable arrays be? A: 8 x 32b. + 4. Does BXH subtract or add the second argument? + 5. Do BXH and BXLE do a logical or arithmetic compare? A: Logical. + 6. Do ACH and SCH produce normal GL codes, or do they take into account + prior GL codes? +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Memory */ + +#define MAXMEMSIZE 65536 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define AMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE) +#define ReadW(x) M[(x) >> 1] +#define WriteW(x,d) if (MEM_ADDR_OK (x)) M[(x) >> 1] = d +#define ReadB(x) ((M[(x) >> 1] >> (((x) & 1)? 0: 8)) & 0xFF) +#define WriteB(x,d) if (MEM_ADDR_OK (x)) M[(x) >> 1] = \ + (((x) & 1)? ((M[(x) >> 1] & ~0xFF) | (d)): \ + ((M[(x) >> 1] & 0xFF) | ((d) << 8))) + +/* Architectural constants */ + +#define SIGN 0x8000 /* sign bit */ +#define DMASK 0xFFFF /* data mask */ +#define MAGMASK 0x7FFF /* magnitude mask */ + +#define OP_4B 0x40 /* 2 byte vs 4 byte */ + +#define CC_C 0x8 /* carry */ +#define CC_V 0x4 /* overflow */ +#define CC_G 0x2 /* greater than */ +#define CC_L 0x1 /* less than */ +#define CC_MASK (CC_C | CC_V | CC_G | CC_L) + +#define PSW_WAIT 0x8000 /* wait */ +#define PSW_EXI 0x4000 /* ext intr enable */ +#define PSW_MCI 0x2000 /* machine check enable */ +#define PSW_DFI 0x1000 /* divide fault enable */ +#define PSW_FDI 0x0400 /* flt divide fault enable */ + +#define FDOPSW 0x28 /* flt div fault old PSW */ +#define FDNPSW 0x2C /* flt div fault new PSW */ +#define ILOPSW 0x30 /* illegal op old PSW */ +#define ILNPSW 0x34 /* illegal op new PSW */ +#define MCOPSW 0x38 /* machine check old PSW */ +#define MCNPSW 0x3C /* machine check new PSW */ +#define EXOPSW 0x40 /* external intr old PSW */ +#define EXNPSW 0x44 /* external intr new PSW */ +#define IDOPSW 0x48 /* int div fault old PSW */ +#define IDNPSW 0x4C /* int div fault new PSW */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_WAIT 4 /* wait */ + +/* I/O operations */ + +#define IO_ADR 0x0 /* address select */ +#define IO_RD 0x1 /* read */ +#define IO_WD 0x2 /* write */ +#define IO_OC 0x3 /* output command */ +#define IO_SS 0x5 /* sense status */ + +/* Device return codes: data byte is <7:0> */ + +#define IOT_V_EXM 8 /* set V flag */ +#define IOT_EXM (1u << IOT_V_EXM) +#define IOT_V_REASON 9 /* set reason */ + +/* Device command byte */ + +#define CMD_V_INT 6 /* interrupt control */ +#define CMD_M_INT 0x3 +#define CMD_IENB 1 /* enable */ +#define CMD_IDIS 2 /* disable */ +#define CMD_ICOM 3 /* complement */ +#define CMD_GETINT(x) (((x) >> CMD_V_INT) & CMD_M_INT) + +/* Device status byte */ + +#define STA_BSY 0x8 /* busy */ +#define STA_EX 0x4 /* examine status */ +#define STA_EOM 0x2 /* end of medium */ +#define STA_DU 0x1 /* device unavailable */ + +/* Range of implemented device numbers */ + +#define DEV_LOW 0x01 /* lowest intr dev */ +#define DEV_MAX 0xFF /* highest intr dev */ +#define DEVNO (DEV_MAX + 1) /* number of devices */ +#define INTSZ ((DEVNO + 31) / 32) /* number of interrupt words */ +#define DS 0x01 /* display and switches */ +#define TT 0x02 /* teletype */ +#define PT 0x03 /* paper tape */ +#define CD 0x04 /* card reader */ +#define INT_V(d) (1u << ((d) & 0x1F)) +#define SET_INT(d) int_req[(d)/32] = int_req[(d)/32] | INT_V (d) +#define CLR_INT(d) int_req[(d)/32] = int_req[(d)/32] & ~INT_V (d) +#define SET_ENB(d) int_enb[(d)/32] = int_enb[(d)/32] | INT_V (d) +#define COM_ENB(d) int_enb[(d)/32] = int_enb[(d)/32] ^ INT_V (d) +#define CLR_ENB(d) int_enb[(d)/32] = int_enb[(d)/32] & ~INT_V (d) + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ diff --git a/id4_fp.c b/id4_fp.c new file mode 100644 index 00000000..b2dac670 --- /dev/null +++ b/id4_fp.c @@ -0,0 +1,272 @@ +/* id4_fp.c: Interdata 4 floating point instructions + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The Interdata 4 uses IBM 360 single precision floating point format: + + 0 7 8 15 23 31 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |S| exponent | fraction | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + where S = 0 for plus, 1 for minus + exponent = 16**n, in excess 64 code + fraction = .hhhhhh, seen as 6 hexadecimal digits + + Numbers could be normalized or unnormalized but are always normalized + when loaded. + + The Interdata 4 has 8 floating point registers, F0, F2, ... , FE. + In floating point instructions, the low order bit of the register + specification is ignored. + + On floating point overflow, the exponent and fraction are set to 1's. + On floating point underflow, the exponent and fraction are set to 0's. +*/ + +#include "id4_defs.h" + +struct ufp { /* unpacked fp */ + int32 sign; /* sign */ + int32 exp; /* unbiased exp */ + unsigned int32 frh; /* fr high */ + unsigned int32 frl; }; /* fr low */ + +#define FP_V_SIGN 31 /* sign */ +#define FP_M_SIGN 0x1 +#define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN) +#define FP_V_EXP 24 /* exponent */ +#define FP_M_EXP 0x7F +#define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP) +#define FP_V_FRH 0 /* fraction */ +#define FP_M_FRH 0xFFFFFF +#define FP_GETFRH(x) (((x) >> FP_V_FRH) & FP_M_FRH) + +#define FP_BIAS 0x40 /* exp bias */ +#define FP_CARRY (1 << FP_V_EXP ) /* carry out */ +#define FP_NORM (0xF << (FP_V_EXP - 4)) /* normalized */ +#define FP_ROUND 0x80000000 +#define FP_DMASK 0xFFFFFFFF + +#define FP_SHFR(v,s) if ((s) < 32) { \ + v.frl = ((v.frl >> (s)) | \ + (v.frh << (32 - (s)))) & FP_DMASK; \ + v.frh = (v.frh >> (s)) & FP_DMASK; } \ + else { v.frl = v.frh >> ((s) - 32); \ + v.frh = 0; } + +extern int32 R[16]; +extern unsigned int32 F[8]; +extern unsigned int16 M[]; +void ReadFP2 (struct ufp *fop, int32 op, int32 r2, int32 ea); +void UnpackFP (struct ufp *fop, unsigned int32 val); +void NormFP (struct ufp *fop); +int32 StoreFP (struct ufp *fop, int32 r1); + +/* Floating point load */ + +int32 le (op, r1, r2, ea) +{ +struct ufp fop2; + +ReadFP2 (&fop2, op, r2, ea); /* get op, normalize */ +return StoreFP (&fop2, r1); /* store, chk unflo */ +} + +/* Floating point compare */ + +int32 ce (op, r1, r2, ea) +{ +struct ufp fop1, fop2; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, normalize */ +UnpackFP (&fop1, F[r1 >> 1]); /* get op1 */ +if (fop1.sign ^ fop2.sign) /* signs differ? */ + return (fop2.sign? CC_G: CC_L); +if (fop1.exp != fop2.exp) /* exps differ? */ + return (((fop1.exp > fop2.exp) ^ fop1.sign)? CC_G: CC_L); +if (fop1.frh != fop2.frh) /* fracs differ? */ + return (((fop1.frh > fop2.frh) ^ fop1.sign)? CC_G: CC_L); +return 0; +} + +/* Floating point add/subtract */ + +int32 ase (op, r1, r2, ea) +{ +struct ufp fop1, fop2, t; +int32 ediff; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, normalize */ +UnpackFP (&fop1, F[r1 >> 1]); /* get op1 */ +if (op & 1) fop2.sign = fop2.sign ^ 1; /* if sub, inv sign2 */ +if (fop1.frh == 0) fop1 = fop2; /* if op1 = 0, res = op2 */ +else if (fop2.frh != 0) { /* if op2 = 0, no add */ + if ((fop1.exp < fop2.exp) || ((fop1.exp == fop2.exp) && + (fop1.frh < fop2.frh))) { /* |op1| < |op2|? */ + t = fop2; /* swap operands */ + fop2 = fop1; + fop1 = t; } + if (ediff = fop1.exp - fop2.exp) { /* exp differ? */ + if (ediff > 14) fop2.frh = 0; /* limit shift */ + else { FP_SHFR (fop2, ediff * 4); } } + if (fop1.sign ^ fop2.sign) { /* eff subtract */ + fop1.frl = 0 - fop2.frl; /* sub fractions */ + fop1.frh = fop1.frh - fop2.frh - (fop1.frl != 0); + NormFP (&fop1); } /* normalize result */ + else { fop1.frl = fop2.frl; /* add fractions */ + fop1.frh = fop1.frh + fop2.frh; + if (fop1.frh & FP_CARRY) { /* carry out? */ + FP_SHFR (fop1, 4); /* renormalize */ + fop1.exp = fop1.exp + 1; } } /* incr exp */ + } /* end if fop2 */ +return StoreFP (&fop1, r1); /* store result */ +} + +/* Floating point multiply + + Note that the 24b * 24b multiply yields 2 extra hex digits of 0, + which are accounted for by biasing the normalize count + */ + +int32 me (op, r1, r2, ea) +{ +struct ufp fop1, fop2; +unsigned int32 hi1, hi2, mid, lo1, lo2; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ +UnpackFP (&fop1, F[r1 >> 1]); /* get op1 */ +if (fop1.frh && fop2.frh) { /* if both != 0 */ + fop1.sign = fop1.sign ^ fop2.sign; /* sign = diff */ + fop1.exp = fop1.exp + fop2.exp - FP_BIAS + 2; /* exp = sum */ + hi1 = (fop1.frh >> 16) & 0xFF; /* 24b * 24b */ + hi2 = (fop2.frh >> 16) & 0xFF; /* yields 48b */ + lo1 = fop1.frh & 0xFFFF; /* 32b */ + lo2 = fop2.frh & 0xFFFF; /* 16b */ + fop1.frl = lo1 * lo2; /* 25b */ + mid = (hi1 * lo2) + (lo1 * hi2); + fop1.frh = hi1 * hi2; + fop1.frl = fop1.frl + ((mid << 16) & FP_DMASK); + fop1.frh = fop1.frh + (mid >> 8) + (fop1.frl < ((mid << 16) & FP_DMASK)); + NormFP (&fop1); /* normalize */ + return StoreFP (&fop1, r1); } /* store result */ +else F[r1 >> 0] = 0; /* result = 0 */ +return 0; +} + +/* Floating point divide */ + +int32 de (op, r1, r2, ea) +{ +struct ufp fop1, fop2; +int32 i; +unsigned int32 divd; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ +UnpackFP (&fop1, F[r1 >> 1]); /* get op1 */ +if (fop2.frh == 0) return -1; /* div by zero? */ +if (fop1.frh) { /* dvd != 0? */ + fop1.sign = fop1.sign ^ fop2.sign; /* sign = diff */ + fop1.exp = fop1.exp - fop2.exp + FP_BIAS; /* exp = diff */ + if (fop1.frh >= fop2.frh) divd = fop1.frh; /* 1st sub ok? */ + else { divd = fop1.frh << 4; /* ensure success */ + fop1.exp = fop1.exp - 1; } + for (i = fop1.frh = 0; i < 6; i++) { /* 6 hex digits */ + fop1.frh = fop1.frh << 4; /* shift quotient */ + while (divd >= fop2.frh) { /* while sub works */ + divd = divd - fop2.frh; /* decrement */ + fop1.frh = fop1.frh + 1; } /* add quo bit */ + divd = divd << 4; } /* shift divd */ + if (divd >= (fop2.frh << 3)) fop1.frl = FP_ROUND; + /* don't need to normalize */ + } /* end if fop1.frh */ +return StoreFP (&fop1, r1); /* store result */ +} + +/* Utility routines */ + +/* Unpack floating point number */ + +void UnpackFP (struct ufp *fop, unsigned int32 val) +{ +fop -> frl = 0; /* low frac zero */ +if (fop -> frh = FP_GETFRH (val)) { /* any fraction? */ + fop -> sign = FP_GETSIGN (val); /* get sign */ + fop -> exp = FP_GETEXP (val); /* get exp */ + NormFP (fop); } /* normalize */ +else fop -> sign = fop -> exp = 0; /* clean zero */ +return; +} + +/* Read memory operand */ + +void ReadFP2 (struct ufp *fop, int32 op, int32 r2, int32 ea) +{ +unsigned int32 t; + +if (op & OP_4B) /* RX? */ + t = (ReadW (ea) << 16) | ReadW ((ea + 2) & AMASK); +else t = F[r2 >> 1]; /* RR */ +UnpackFP (fop, t); /* unpack op */ +return; +} + +/* Normalize unpacked floating point number */ + +void NormFP (struct ufp *fop) +{ +if (fop -> frh || fop -> frl) { /* any fraction? */ + while ((fop -> frh & FP_NORM) == 0) { /* until norm */ + fop -> frh = (fop -> frh << 4) | ((fop -> frl >> 28) & 0xF); + fop -> frl = fop -> frl << 4; + fop -> exp = fop -> exp - 1; } } +else fop -> sign = fop -> exp = 0; /* clean 0 */ +return; +} + +/* Round fp number, store, generate condition codes */ + +int32 StoreFP (struct ufp *fop, int32 r1) +{ +if (fop -> frl & FP_ROUND) { /* round bit set? */ + fop -> frh = fop -> frh + 1; /* add 1 to frac */ + if (fop -> frh & FP_CARRY) { /* carry out? */ + fop -> frh = fop -> frh >> 4; /* renormalize */ + fop -> exp = fop -> exp + 1; } } /* incr exp */ +if (fop -> frh == 0) { /* result 0? */ + F[r1 >> 1] = 0; /* store clean 0 */ + return 0; } +if (fop -> exp <= 0) { /* underflow? */ + F[r1 >> 1] = 0; /* store clean 0 */ + return CC_V; } +if (fop -> exp > FP_M_EXP) { /* overflow? */ + F[r1 >> 1] = (fop -> sign)? 0xFFFFFFFF: 0x3FFFFFFF; + return (CC_V | ((fop -> sign)? CC_L: CC_G)); } +F[r1 >> 1] = ((fop -> sign & FP_M_SIGN) << FP_V_SIGN) | /* pack result */ + ((fop -> exp & FP_M_EXP) << FP_V_EXP) | + ((fop -> frh & FP_M_FRH) << FP_V_FRH); +if (fop -> sign) return CC_L; /* generate cc's */ +if (F[r1 >> 1]) return CC_G; +return 0; +} \ No newline at end of file diff --git a/id4_stddev.c b/id4_stddev.c new file mode 100644 index 00000000..581150ea --- /dev/null +++ b/id4_stddev.c @@ -0,0 +1,407 @@ +/* id4_stddev.c: Interdata 4 standard devices + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + pt paper tape reader and punch + tt console + cdr card reader +*/ + +#include "id4_defs.h" +#include +#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ +#define UNIT_UC (1 << UNIT_V_UC) + +extern unsigned int16 M[]; +extern int32 int_req[INTSZ], int_enb[INTSZ]; +int32 pt_run = 0, pt_slew = 0; /* ptr modes */ +int pt_rw = 0, pt_busy = 0; /* pt state */ +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ +int32 tt_hdpx = 0; /* tt mode */ +int32 tt_rw = 0, tt_busy = 0; /* tt state */ +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat pt_boot (int32 unitno); +t_stat pt_reset (DEVICE *dptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tt_reset (DEVICE *dptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* Device definitions */ + +#define PTR 0 +#define PTP 1 +#define PT_STA_OVFL 0x80 /* overflow */ +#define PT_STA_NMTN 0x10 /* no motion */ + +#define PT_V_RUN 4 /* run/stop */ +#define PT_M_RUN 0x3 +#define PT_RUN 1 +#define PT_STOP 2 +#define PT_CRS 3 +#define PT_GETRUN(x) (((x) >> PT_V_RUN) & PT_M_RUN) + +#define PT_V_SLEW 2 /* slew/step */ +#define PT_M_SLEW 0x3 +#define PT_SLEW 1 +#define PT_STEP 2 +#define PT_CSLEW 3 +#define PT_GETSLEW(x) (((x) >> PT_V_SLEW) & PT_M_SLEW) + +#define PT_V_RW 0 /* read/write */ +#define PT_M_RW 0x3 +#define PT_RD 1 +#define PT_WD 2 +#define PT_CRW 3 +#define PT_GETRW(x) (((x) >> PT_V_RW) & PT_M_RW) + +#define TTI 0 +#define TTO 1 +#define TT_V_DPX 4 /* half/full duplex */ +#define TT_M_DPX 0x3 +#define TT_FDPX 1 +#define TT_HDPX 2 +#define TT_CDPX 3 +#define TT_GETDPX(x) (((x) >> TT_V_DPX) & TT_M_DPX) + +#define TT_V_RW 2 /* read/write */ +#define TT_M_RW 0x3 +#define TT_RD 1 +#define TT_WD 2 +#define TT_CRW 3 +#define TT_GETRW(x) (((x) >> TT_V_RW) & TT_M_RW) + +/* PT data structures + + pt_dev PT device descriptor + pt_unit PT unit descriptors + pt_reg PT register list +*/ + +UNIT pt_unit[] = { + { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }, + { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT } +}; + +REG pt_reg[] = { + { HRDATA (RBUF, pt_unit[PTR].buf, 8) }, + { DRDATA (RPOS, pt_unit[PTR].pos, 31), PV_LEFT }, + { DRDATA (RTIME, pt_unit[PTR].wait, 24), PV_LEFT }, + { FLDATA (RSTOP_IOE, ptr_stopioe, 0) }, + { HRDATA (PBUF, pt_unit[PTP].buf, 8) }, + { DRDATA (PPOS, pt_unit[PTP].pos, 31), PV_LEFT }, + { DRDATA (PTIME, pt_unit[PTP].wait, 24), PV_LEFT }, + { FLDATA (PSTOP_IOE, ptp_stopioe, 0) }, + { FLDATA (IREQ, int_req[PT/32], PT & 0x1F) }, + { FLDATA (IENB, int_enb[PT/32], PT & 0x1F) }, + { FLDATA (RUN, pt_run, 0) }, + { FLDATA (SLEW, pt_slew, 0) }, + { FLDATA (BUSY, pt_busy, 0) }, + { FLDATA (RW, pt_rw, 0) }, + { NULL } }; + +DEVICE pt_dev = { + "PT", pt_unit, pt_reg, NULL, + 2, 10, 31, 1, 8, 8, + NULL, NULL, &pt_reset, + &pt_boot, NULL, NULL }; + +/* TT data structures + + tt_dev TT device descriptor + tt_unit TT unit descriptors + tt_reg TT register list + tt_mod TT modifiers list +*/ + +UNIT tt_unit[] = { + { UDATA (&tti_svc, UNIT_UC, 0), KBD_POLL_WAIT }, + { UDATA (&tto_svc, UNIT_UC, 0), SERIAL_OUT_WAIT } +}; + +REG tt_reg[] = { + { HRDATA (KBUF, tt_unit[TTI].buf, 8) }, + { DRDATA (KPOS, tt_unit[TTI].pos, 31), PV_LEFT }, + { DRDATA (KTIME, tt_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, + { HRDATA (TBUF, tt_unit[TTO].buf, 8) }, + { DRDATA (TPOS, tt_unit[TTO].pos, 31), PV_LEFT }, + { DRDATA (TTIME, tt_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (IREQ, int_req[TT/32], TT & 0x1F) }, + { FLDATA (IENB, int_enb[TT/32], TT & 0x1F) }, + { FLDATA (HDPX, tt_hdpx, 0) }, + { FLDATA (BUSY, tt_busy, 0) }, + { FLDATA (RW, tt_rw, 0) }, + { FLDATA (UC, tt_unit[TTI].flags, UNIT_V_UC), REG_HRO }, + { NULL } }; + +MTAB tt_mod[] = { + { UNIT_UC, 0, "lower case", "LC", NULL }, + { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, + { 0 } }; + +DEVICE tt_dev = { + "TT", tt_unit, tt_reg, tt_mod, + 2, 10, 31, 1, 8, 8, + NULL, NULL, tt_reset, + NULL, NULL, NULL }; + +/* Paper tape: IO routine */ + +int32 pt (int32 op, int32 dat) +{ +int32 t; + +switch (op) { /* case IO op */ +case IO_ADR: /* select */ + break; +case IO_OC: /* command */ + t = CMD_GETINT (dat); /* get enable */ + if (t == CMD_IENB) SET_ENB (PT); /* process */ + else if (t == CMD_IDIS) CLR_ENB (PT); + else if (t == CMD_ICOM) COM_ENB (PT); + t = PT_GETRUN (dat); /* run/stop */ + if (t == PT_RUN) pt_run = 1; /* process */ + else if (t == PT_STOP) pt_run = 0; + else if (t == PT_CRS) pt_run = pt_run ^ 1; + t = PT_GETSLEW (dat); /* slew/step */ + if (t == PT_SLEW) pt_slew = 1; /* process */ + else if (t == PT_STEP) pt_slew = 0; + else if (t == PT_CSLEW) pt_slew = pt_slew ^ 1; + t = PT_GETRW (dat); /* read/write */ + if (t == PT_RD) pt_rw = 0; /* process */ + else if (t == PT_WD) pt_rw = 1; + else if (t == PT_CRW) pt_rw = pt_rw ^ 1; + if ((pt_rw == 0) && pt_run) /* read + run? */ + sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); + if (sim_is_active (&pt_unit[pt_rw? PTP: PTR])) pt_busy = 1; + else { if (pt_busy) SET_INT (PT); + pt_busy = 0; } + break; +case IO_RD: /* read */ + if (pt_run && !pt_slew) + sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); + if (pt_rw == 0) pt_busy = 1; + return (pt_unit[PTR].buf & 0xFF); +case IO_WD: /* write */ + pt_unit[PTP].buf = dat & 0xFF; + sim_activate (&pt_unit[PTP], pt_unit[PTP].wait); + if (pt_rw) pt_busy = 1; + break; +case IO_SS: /* status */ + t = pt_busy? STA_BSY: 0; + if ((pt_unit[PTR].flags & UNIT_ATT) == 0) t = t | STA_DU; + if (!sim_is_active (&pt_unit[PTR])) t = t | PT_STA_NMTN | STA_EX; + return t; } +return 0; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((pt_unit[PTR].flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if (pt_rw == 0) { /* read mode? */ + if (pt_busy) SET_INT (PT); /* if busy, intr */ + pt_busy = 0; } /* not busy */ +if (pt_slew) sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); /* slew? */ +if ((temp = getc (pt_unit[PTR].fileref)) == EOF) { + if (feof (pt_unit[PTR].fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (pt_unit[PTR].fileref); + return SCPE_IOERR; } +pt_unit[PTR].buf = temp & 0xFF; +pt_unit[PTR].pos = pt_unit[PTR].pos + 1; +return SCPE_OK; +} + +t_stat ptp_svc (UNIT *uptr) +{ +if ((pt_unit[PTP].flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (pt_rw) { /* write mode? */ + if (pt_busy) SET_INT (PT); /* if busy, intr */ + pt_busy = 0; } /* not busy */ +if (putc (pt_unit[PTP].buf, pt_unit[PTP].fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (pt_unit[PTP].fileref); + return SCPE_IOERR; } +pt_unit[PTP].pos = pt_unit[PTP].pos + 1; +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 0x3E +#define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned int16)) + +static const unsigned int16 boot_rom[] = { + 0xC820, /* START: LHI 2,80 */ + 0x0080, + 0xC830, /* LHI 3,1 */ + 0x0001, + 0xC840, /* LHI 4,CF */ + 0x00CF, + 0xD3A0, /* LB A,78 */ + 0x0078, + 0xDEA0, /* OC A,79 */ + 0x0079, + 0x9DAE, /* LOOP: SSR A,E */ + 0x42F0, /* BTC F,LOOP */ + 0x0052, + 0x9BAE, /* RDR A,E */ + 0x08EE, /* LHR E,E */ + 0x4330, /* BZ LOOP */ + 0x0052, + 0x4300, /* BR STORE */ + 0x006C, + 0x9DAE, /* LOOP1: SSR A,E */ + 0x42F0, /* BTC F,LOOP1 */ + 0x0064, + 0x9BAE, /* RDR A,E */ + 0xD2E2, /* STORE: STB E,0(2) */ + 0x0000, + 0xC120, /* BXLE 2,LOOP1 */ + 0x0064, + 0x4300, /* BR 80 */ + 0x0080, + 0x0395, /* HS PAPER TAPE INPUT */ + 0x039A, /* HS PAPER TAPE OUTPUT */ + 0x0420, /* CARD INPUT TO ASSEMBLER */ + 0x0298 /* TELEPRINTER OUTPUT FOR ASSEMBLER */ +}; + +t_stat pt_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat pt_reset (DEVICE *dptr) +{ +sim_cancel (&pt_unit[PTR]); /* deactivate units */ +sim_cancel (&pt_unit[PTP]); +pt_busy = pt_run = pt_slew = pt_rw = 0; +return SCPE_OK; +} + +/* Terminal: IO routine */ + +int32 tt (int32 op, int32 dat) +{ +int32 t, old_tt_rw; + +switch (op) { /* case IO op */ +case IO_ADR: /* select */ + break; +case IO_OC: /* command */ + t = CMD_GETINT (dat); /* get enable */ + if (t == CMD_IENB) SET_ENB (TT); /* process */ + else if (t == CMD_IDIS) CLR_ENB (TT); + else if (t == CMD_ICOM) COM_ENB (TT); + t = TT_GETDPX (dat); /* get duplex */ + if (t == TT_FDPX) tt_hdpx = 0; /* process */ + else if (t == TT_HDPX) tt_hdpx = 1; + else if (t == TT_CDPX) tt_hdpx = tt_hdpx ^ 1; + t = TT_GETRW (dat); /* read/write */ + old_tt_rw = tt_rw; + if (t == TT_RD) tt_rw = 0; /* process */ + else if (t == TT_WD) tt_rw = 1; + else if (t == TT_CRW) tt_rw = tt_rw ^ 1; + if (tt_rw == 0) { /* read? */ + if (old_tt_rw != 0) tt_busy = 1; } /* chg? set busy */ + else { if (sim_is_active (&tt_unit[TTO])) tt_busy = 1; + else { if (tt_busy) SET_INT (TT); + tt_busy = 0; } } + break; +case IO_RD: /* read */ + if (tt_rw == 0) tt_busy = 1; + return (tt_unit[TTI].buf & 0xFF); +case IO_WD: /* write */ + tt_unit[TTO].buf = dat & 0xFF; + sim_activate (&tt_unit[TTO], tt_unit[TTO].wait); + if (pt_rw) tt_busy = 1; + break; +case IO_SS: /* status */ + t = tt_busy? STA_BSY: 0; + return t; } +return 0; +} + +/* Unit service routines */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +if (tt_rw == 0) { /* read mode? */ + if (tt_busy) SET_INT (TT); /* if busy, intr */ + tt_busy = 0; } /* not busy */ +temp = temp & 0x7F; +if ((tt_unit[TTI].flags & UNIT_UC) && islower (temp)) + temp = toupper (temp); +tt_unit[TTI].buf = temp | 0x80; /* got char */ +tt_unit[TTI].pos = tt_unit[TTI].pos + 1; +if (tt_hdpx) { /* half duplex? */ + sim_putchar (temp); /* write char */ + tt_unit[TTO].pos = tt_unit[TTO].pos + 1; } +return SCPE_OK; +} + +t_stat tto_svc (UNIT *uptr) +{ +int32 temp; + +if (tt_rw) { /* write mode? */ + if (tt_busy) SET_INT (TT); /* if was busy, intr */ + tt_busy = 0; } /* not busy */ +if ((temp = sim_putchar (tt_unit[TTO].buf & 0177)) != SCPE_OK) return temp; +tt_unit[TTO].pos = tt_unit[TTO].pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tt_reset (DEVICE *dptr) +{ +sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* activate input */ +sim_cancel (&tt_unit[TTO]); /* cancel output */ +tt_hdpx = tt_rw = 0; +tt_busy = 1; /* no kbd input yet */ +return SCPE_OK; +} diff --git a/id4_sys.c b/id4_sys.c new file mode 100644 index 00000000..d27233ce --- /dev/null +++ b/id4_sys.c @@ -0,0 +1,317 @@ +/* id4_sys.c: Interdata 4 simulator interface + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface +*/ + +#include "id4_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE pt_dev, tt_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern unsigned int16 *M; +extern int32 saved_PC; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "Interdata 4"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 2; + +DEVICE *sim_devices[] = { &cpu_dev, + &pt_dev, &tt_dev, + NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Reserved instruction", + "HALT instruction", + "Breakpoint", + "Wait state" }; + +/* Binary loader. + + To be specified +*/ + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +return SCPE_FMT; /* unexpected eof */ +} + +/* Symbol tables */ + +#define I_V_FL 16 /* class bits */ +#define I_M_FL 07 /* class mask */ +#define I_V_MR 0 /* mask-register */ +#define I_V_RR 1 /* register-register */ +#define I_V_R 2 /* register */ +#define I_V_MX 3 /* mask-memory */ +#define I_V_RX 4 /* register-memory */ +#define I_V_X 5 /* memory */ +#define I_V_FF 6 /* float reg-reg */ +#define I_V_FX 7 /* float reg-mem */ +#define I_MR (I_V_MR << I_V_FL) +#define I_RR (I_V_RR << I_V_FL) +#define I_R (I_V_R << I_V_FL) +#define I_MX (I_V_MX << I_V_FL) +#define I_RX (I_V_RX << I_V_FL) +#define I_X (I_V_X << I_V_FL) +#define I_FF (I_V_FF << I_V_FL) +#define I_FX (I_V_FX << I_V_FL) + +static const int32 masks[] = +{ 0xFFF0, 0xFF00, 0xFFF0, 0xFF00, + 0xFF00, 0xFFF0, 0xFF00, 0xFF00 }; + +static const char *opcode[] = { +"BZ", "BNZ", "BE", "BNE", +"BP", "BNP", "BL", "BNL", +"BM", "BNM", "BO", "BC", +"B", "BR", + "BALR","BTCR","BFCR", +"NHR", "CLHR","OHR", "XHR", +"LHR", "AHR", "SHR", +"MHR", "DHR", "ACHR","SCHR", +"LER", "CER", "AER", "SER", +"MER", "DER", + "BAL", "BTC", "BFC", +"NH", "CLH", "OH", "XH", +"LH", "AH", "SH", +"MH", "DH", "ACH", "SCH", +"STE", +"LE", "CE", "AE", "SE", +"ME", "DE", + "STBR","LBR", + "WBR", "RBR", + "WDR", "RDR", + "SSR", "OCR", "AIR", +"BXH", "BXLE","LPSW", +"NHI", "CLHI","OHI", "XHI", +"LHI", "AHI", "SHI", +"SRHL","SLHL","SRHA","SLHA", +"STM", "LM", "STB", "LB", + "AL", "WB", "RB", + "WD", "RD", + "SS", "OC", "AI", +NULL }; + +static const int32 opc_val[] = { +0x4330+I_X, 0x4230+I_X, 0x4330+I_X, 0x4230+I_X, +0x4220+I_X, 0x4320+I_X, 0x4280+I_X, 0x4380+I_X, +0x4210+I_X, 0x4310+I_X, 0x4240+I_X, 0x4280+I_X, +0x4300+I_X, 0x0300+I_R, + 0x0100+I_RR, 0x0200+I_MR, 0x0300+I_MR, +0x0400+I_RR, 0x0500+I_RR, 0x0600+I_RR, 0x0700+I_RR, +0x0800+I_RR, 0x0A00+I_RR, 0x0B00+I_RR, +0x0C00+I_RR, 0x0D00+I_RR, 0x0E00+I_RR, 0x0F00+I_RR, +0x2800+I_FF, 0x2900+I_FF, 0x2A00+I_FF, 0x2B00+I_FF, +0x2C00+I_FF, 0x2D00+I_FF, + 0x4100+I_RX, 0x4200+I_MX, 0x4300+I_MX, +0x4400+I_RX, 0x4500+I_RX, 0x4600+I_RX, 0x4700+I_RX, +0x4800+I_RX, 0x4A00+I_RX, 0x4B00+I_RX, +0x4C00+I_RX, 0x4D00+I_RX, 0x4E00+I_RX, 0x4F00+I_RX, +0x6000+I_FX, +0x6800+I_FX, 0x6900+I_FX, 0x6A00+I_FX, 0x6B00+I_FX, +0x6C00+I_FX, 0x6D00+I_FX, + 0x9200+I_RR, 0x9300+I_RR, + 0x9600+I_RR, 0x9700+I_RR, + 0x9A00+I_RR, 0x9B00+I_RR, + 0x9D00+I_RR, 0x9E00+I_RR, 0x9F00+I_RR, +0xC000+I_RX, 0xC100+I_RX, 0xC200+I_RX, +0xC400+I_RX, 0xC500+I_RX, 0xC600+I_RX, 0xC700+I_RX, +0xC800+I_RX, 0xCA00+I_RX, 0xCB00+I_RX, +0xCC00+I_RX, 0xCD00+I_RX, 0xCE00+I_RX, 0xCF00+I_RX, +0xD000+I_RX, 0xD100+I_RX, 0xD200+I_RX, 0xD300+I_RX, + 0xD500+I_RX, 0xD600+I_RX, 0xD700+I_RX, + 0xDA00+I_RX, 0xDB00+I_RX, + 0xDD00+I_RX, 0xDE00+I_RX, 0xDF00+I_RX, +-1 }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, j, c1, c2, inst, r1, r2; + +c1 = (val[0] >> 8) & 0x3F; /* big endian */ +c2 = val[0] & 0177; +if (sw & SWMASK ('A')) { /* ASCII? */ + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +inst = val[0]; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0xFFFF) == (inst & masks[j])) { /* match? */ + r1 = (val[0] >> 4) & 0xF; + r2 = val[0] & 0xF; + switch (j) { /* case on class */ + case I_V_MR: /* mask-register */ + fprintf (of, "%s %-X,R%-X", opcode[i], r1, r2); + return SCPE_OK; + case I_V_RR: /* register-register */ + case I_V_FF: /* floating-floating */ + fprintf (of, "%s R%-X,R%-X", opcode[i], r1, r2); + return SCPE_OK; + case I_V_R: /* register */ + fprintf (of, "%s R%-X", opcode[i], r2); + return SCPE_OK; + case I_V_MX: /* mask-memory */ + fprintf (of, "%s %-X,%-X", opcode[i], r1, val[1]); + break; + case I_V_RX: /* register-memory */ + case I_V_FX: /* floating-memory */ + fprintf (of, "%s R%-X,%-X", opcode[i], r1, val[1]); + break; + case I_V_X: /* memory */ + fprintf (of, "%s %-X", opcode[i], val[1]); + break; } /* end case */ + if (r2) fprintf (of, "(R%-X)", r2); + return -1; } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +/* Register number + + Inputs: + *cptr = pointer to input string + mchar = match character + regflt = false for integer, true for floating + Outputs: + rnum = output register number, -1 if error +*/ + +int32 get_reg (char *cptr, char mchar, t_bool regflt) +{ +int32 reg = -1; + +if ((*cptr == 'R') || (*cptr == 'r')) cptr++; +if (*(cptr + 1) != mchar) return reg; +if ((*cptr >= '0') && (*cptr <= '9')) reg = *cptr - '0'; +else if ((*cptr >= 'a') && (*cptr <= 'f')) reg = (*cptr - 'a') + 10; +else if ((*cptr >= 'A') && (*cptr <= 'F')) reg = (*cptr - 'A') + 10; +if (regflt && (reg & 1)) return -1; +return reg; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, j, r1, r2; +t_bool regflt; +char *tptr, gbuf[CBUFSIZE]; + +regflt = FALSE; /* assume int reg */ +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((t_value) cptr[0] << 8) + (t_value) cptr[1]; + return SCPE_OK; } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0xFFFF; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ +switch (j) { /* case on class */ + +case I_V_FF: /* float-float */ + regflt = TRUE; /* fall thru */ +case I_V_MR: case I_V_RR: /* mask/reg-register */ + cptr = get_glyph (cptr, gbuf, ','); /* get register 1 */ + if ((r1 = get_reg (gbuf, 0, regflt)) < 0) return SCPE_ARG; + val[0] = val[0] | (r1 << 4); /* fall thru for reg 2 */ + +case I_V_R: /* register */ + cptr = get_glyph (cptr, gbuf, 0); /* get r2 */ + if ((r2 = get_reg (gbuf, 0, regflt)) < 0) return SCPE_ARG; + val[0] = val[0] | r2; + if (*cptr != 0) return SCPE_ARG; + return SCPE_OK; + +case I_V_FX: /* float-memory */ + regflt = TRUE; /* fall thru */ +case I_V_MX: case I_V_RX: /* mask/reg-memory */ + cptr = get_glyph (cptr, gbuf, ','); /* get register 1 */ + if ((r1 = get_reg (gbuf, 0, regflt)) < 0) return SCPE_ARG; + val[0] = val[0] | (r1 << 4); /* fall thru for memory */ + +case I_V_X: /* memory */ + val[1] = strtoul (cptr, &tptr, 16); + if (cptr == tptr) return SCPE_ARG; + if (*tptr == 0) return -1; + if ((*tptr != '(') || (*(tptr + 4) != 0)) return SCPE_ARG; + if ((r2 = get_reg (tptr + 1, ')', FALSE)) < 0) return SCPE_ARG; + val[0] = val[0] | r2; + return -1; } /* end case */ +} diff --git a/mtcvtv23.c b/mtcvtv23.c new file mode 100644 index 00000000..c57d9610 --- /dev/null +++ b/mtcvtv23.c @@ -0,0 +1,81 @@ +/* This program converts a pre V2.3 simulated magtape to a V2.3 magtape + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include +#include +#include +#include +#define FLPSIZ 65536 +int main (int argc, char *argv[]) +{ +int i, k, wc, fc, rc; +unsigned char bc[4] = { 0 }; +unsigned char buf[FLPSIZ]; +char *ppos, oname[256]; +FILE *ifile, *ofile; + +if ((argc < 2) || (argv[0] == NULL)) { + printf ("Usage is: verb file [file...]\n"); + exit (0); } + +for (i = 1; i < argc; i++) { + strcpy (oname, argv[i]); + if (ppos = strrchr (oname, '.')) strcpy (ppos, ".new"); + else strcat (oname, ".new"); + ifile = fopen (argv[i], "rb"); + if (ifile == NULL) { + printf ("Error opening file: %s\n", argv[i]); + exit (0); } + ofile = fopen (oname, "wb"); + if (ofile == NULL) { + printf ("Error opening file: %s\n", oname); + exit (0); } + + printf ("Processing file %s\n", argv[i]); + fc = 1; rc = 0; + for (;;) { + k = fread (bc, sizeof (char), 2, ifile); + if (k == 0) break; + wc = ((unsigned int) bc[1] << 8) | (unsigned int) bc[0]; + wc = (wc + 1) & ~1; + fwrite (bc, sizeof (char), 4, ofile); + if (wc) { + k = fread (buf, sizeof (char), wc, ifile); + for ( ; k < wc; k++) buf[k] =0; + fwrite (buf, sizeof (char), wc, ofile); + fwrite (bc, sizeof (char), 4, ofile); + rc++; } + else { if (rc) printf ("End of file %d, record count = %d\n", fc, rc); + else printf ("End of tape\n"); + fc++; + rc = 0; } + } + fclose (ifile); + fclose (ofile); + } + +return 0; +} diff --git a/nova_clk.c b/nova_clk.c new file mode 100644 index 00000000..e779f00d --- /dev/null +++ b/nova_clk.c @@ -0,0 +1,110 @@ +/* nova_clk.c: NOVA real-time clock simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 24-Sep-97 RMS Fixed bug in unit service (found by Dutch Owen) + + clk real-time clock +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable; +int32 clk_sel = 0; /* selected freq */ +int32 clk_alt_time[4] = { 16000, 100000, 10000, 1000 }; /* freq table */ +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; + +REG clk_reg[] = { + { ORDATA (SELECT, clk_sel, 2) }, + { FLDATA (BUSY, dev_busy, INT_V_CLK) }, + { FLDATA (DONE, dev_done, INT_V_CLK) }, + { FLDATA (DISABLE, dev_disable, INT_V_CLK) }, + { FLDATA (INT, int_req, INT_V_CLK) }, + { DRDATA (TIME0, clk_alt_time[0], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME1, clk_alt_time[1], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME2, clk_alt_time[2], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME3, clk_alt_time[3], 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL }; + +/* IOT routine */ + +int32 clk (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) clk_sel = AC & 3; +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_CLK; /* set busy */ + dev_done = dev_done & ~INT_CLK; /* clear done, int */ + int_req = int_req & ~INT_CLK; + sim_activate (&clk_unit, clk_alt_time[clk_sel]); /* activate unit */ + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_CLK; /* clear busy */ + dev_done = dev_done & ~INT_CLK; /* clear done, int */ + int_req = int_req & ~INT_CLK; + sim_cancel (&clk_unit); /* deactivate unit */ + break; } /* end switch */ +return 0; +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_CLK; /* set done */ +dev_busy = dev_busy & ~INT_CLK; /* clear busy */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +sim_activate (&clk_unit, clk_alt_time[clk_sel]); /* reactivate unit */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +clk_sel = 0; +dev_busy = dev_busy & ~INT_CLK; /* clear busy */ +dev_done = dev_done & ~INT_CLK; /* clear done, int */ +int_req = int_req & ~INT_CLK; +sim_cancel (&clk_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/nova_cpu.c b/nova_cpu.c new file mode 100644 index 00000000..faa349f9 --- /dev/null +++ b/nova_cpu.c @@ -0,0 +1,789 @@ +/* nova_cpu.c: NOVA CPU simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-Oct-00 RMS Fixed bug in MDV test, added stack, byte, trap instructions + 14-Apr-98 RMS Changed t_addr to unsigned + 15-Sep-97 RMS Added read and write breakpoints + + The register state for the NOVA CPU is: + + AC[0:3]<0:15> general registers + C carry flag + PC<0:14> program counter + + The NOVA has three instruction formats: memory reference, I/O transfer, + and operate. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0| op | AC |in| mode| displacement | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 00000 JMP PC = MA + 00001 JMS AC3 = PC, PC = MA + 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0 + 001'n LDA ACn = M[MA] + 010'n STA M[MA] = ACn + + <5:7> mode action + + 000 page zero direct MA = zext (IR<8:15>) + 001 PC relative direct MA = PC + sext (IR<8:15>) + 010 AC2 relative direct MA = AC2 + sext (IR<8:15>) + 011 AC3 relative direct MA = AC3 + sext (IR<8:15>) + 100 page zero indirect MA = M[zext (IR<8:15>)] + 101 PC relative dinirect MA = M[PC + sext (IR<8:15>)] + 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)] + 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)] + + Memory reference instructions can access an address space of 32K words. + An instruction can directly reference the first 256 words of memory + (called page zero), as well as 256 words relative to the PC, AC2, or + AC3; it can indirectly access all 32K words. If an indirect address + is in locations 00020-00027, the indirect address is incremented and + rewritten to memory before use; if in 00030-00037, decremented and + rewritten. +*/ + +/* The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 1 1| AC | opcode |pulse| device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IOT instruction sends the opcode, pulse, and specified AC to the + specified I/O device. The device may accept data, provide data, + initiate or cancel operations, or skip on status. + + The operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + \______/ \___/ \___/ | | | | + | | | | | | +--- reverse skip sense + | | | | | +--- skip if C == 0 + | | | | +--- skip if result == 0 + | | | +--- don't load result + | | +--- carry in (load as is, + | | set to Zero, + | | set to One, + | | load Complement) + | +--- shift (none, + | left one, + | right one, + | byte swap) + +--- operation (complement, + negate, + move, + increment, + add complement, + subtract, + add, + and) + + The operate instruction can be microprogrammed to perform operations + on the source and destination AC's and the Carry flag. +*/ + +/* This routine is the instruction decode routine for the NOVA. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + infinite indirection loop + unknown I/O device and STOP_DEV flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by four parallel variables: + + dev_done device done flags + dev_disable device interrupt disable flags + dev_busy device busy flags + int_req interrupt requests + + In addition, int_req contains the interrupt enable and ION pending + flags. If ION and ION pending are set, and at least one interrupt + request is pending, then an interrupt occurs. Note that the 16b PIO + mask must be mapped to the simulator's device bit mapping. + + 3. Non-existent memory. On the NOVA, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + nova_defs.h add interrupt request definition + nova_cpu.c add IOT mask, PI mask, and routine to dev_table + nova_sys.c add pointer to data structures to sim_devices +*/ + +#include "nova_defs.h" + +#define INCA(x) (((x) + 1) & AMASK) +#define DECA(x) (((x) - 1) & AMASK) +#define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): (x)) +#define STK_CHECK(x,y) if (((x) & 0377) < (y)) int_req = int_req | INT_STK +#define IND_STEP(x) M[x] & A_IND; \ + if (((x) & 077770) == AUTO_INC) \ + M[x] = (M[x] + 1) & 0177777; \ + else if (((x) & 077770) == AUTO_DEC) \ + M[x] = (M[x] - 1) & 0177777; \ + x = M[x] & AMASK + +#define ILL_ADR_FLAG A_IND +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_MDV (UNIT_V_UF) /* MDV present */ +#define UNIT_MDV (1 << UNIT_V_MDV) +#define UNIT_V_STK (UNIT_V_UF+1) /* stack instr */ +#define UNIT_STK (1 << UNIT_V_STK) +#define UNIT_V_BYT (UNIT_V_UF+2) /* byte instr */ +#define UNIT_BYT (1 << UNIT_V_BYT) +#define UNIT_IOPT (UNIT_MDV | UNIT_STK | UNIT_BYT) +#define UNIT_NOVA3 (UNIT_MDV | UNIT_STK) +#define UNIT_NOVA4 (UNIT_MDV | UNIT_STK | UNIT_BYT) +#define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +unsigned int16 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC[4] = { 0 }; /* accumulators */ +int32 C = 0; /* carry flag */ +int32 saved_PC = 0; /* program counter */ +int32 SP = 0; /* stack pointer */ +int32 FP = 0; /* frame pointer */ +int32 SR = 0; /* switch register */ +int32 dev_done = 0; /* device done flags */ +int32 dev_busy = 0; /* device busy flags */ +int32 dev_disable = 0; /* int disable flags */ +int32 int_req = 0; /* interrupt requests */ +int32 pimask = 0; /* priority int mask */ +int32 pwr_low = 0; /* power fail flag */ +int32 ind_max = 16; /* iadr nest limit */ +int32 stop_dev = 0; /* stop on ill dev */ +int32 ibkpt_addr = ILL_ADR_FLAG | AMASK; /* ibreakpoint addr */ +int32 old_PC = 0; /* previous PC */ + +extern int32 sim_int_char; +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +extern int32 ptr (int32 pulse, int32 code, int32 AC); +extern int32 ptp (int32 pulse, int32 code, int32 AC); +extern int32 tti (int32 pulse, int32 code, int32 AC); +extern int32 tto (int32 pulse, int32 code, int32 AC); +extern int32 clk (int32 pulse, int32 code, int32 AC); +extern int32 lpt (int32 pulse, int32 code, int32 AC); +extern int32 dsk (int32 pulse, int32 code, int32 AC); +extern int32 dkp (int32 pulse, int32 code, int32 AC); +extern int32 mta (int32 pulse, int32 code, int32 AC); +int32 nulldev (int32 pulse, int32 code, int32 AC); +extern t_stat sim_activate (UNIT *uptr, int32 delay); + +/* IOT dispatch table */ + +struct ndev dev_table[64] = { + { 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 0 - 7 */ + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { INT_TTI, PI_TTI, &tti }, { INT_TTO, PI_TTO, &tto }, /* 10 - 17 */ + { INT_PTR, PI_PTR, &ptr }, { INT_PTP, PI_PTP, &ptp }, + { INT_CLK, PI_CLK, &clk }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { INT_LPT, PI_LPT, &lpt }, + { INT_DSK, PI_DSK, &dsk }, { 0, 0, &nulldev }, /* 20 - 27 */ + { INT_MTA, PI_MTA, &mta }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 30 - 37 */ + { 0, 0, &nulldev }, {INT_DKP, PI_DKP, &dkp }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 40 - 47 */ + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 50 - 57 */ + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 60 - 67 */ + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 70 - 77 */ + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev }, + { 0, 0, &nulldev }, { 0, 0, &nulldev } }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK + UNIT_MDV, + MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 15) }, + { ORDATA (AC0, AC[0], 16) }, + { ORDATA (AC1, AC[1], 16) }, + { ORDATA (AC2, AC[2], 16) }, + { ORDATA (AC3, AC[3], 16) }, + { FLDATA (C, C, 16) }, + { ORDATA (SP, SP, 16) }, + { ORDATA (FP, FP, 16) }, + { ORDATA (SR, SR, 16) }, + { ORDATA (PI, pimask, 16) }, + { FLDATA (ION, int_req, INT_V_ION) }, + { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, + { FLDATA (STKOVF, int_req, INT_V_STK) }, + { FLDATA (PWR, pwr_low, 0) }, + { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, + { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO }, + { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO }, + { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO }, + { FLDATA (STOP_DEV, stop_dev, 0) }, + { FLDATA (MDV, cpu_unit.flags, UNIT_V_MDV), REG_HRO }, + { FLDATA (ISTK, cpu_unit.flags, UNIT_V_STK), REG_HRO }, + { FLDATA (IBYT, cpu_unit.flags, UNIT_V_BYT), REG_HRO }, + { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, + { ORDATA (OLDPC, old_PC, 15), REG_RO }, + { ORDATA (BREAK, ibkpt_addr, 16) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_IOPT, UNIT_NOVA4, "NOVA4", "NOVA4", NULL }, + { UNIT_IOPT, UNIT_NOVA3, "NOVA3", "NOVA3", NULL }, + { UNIT_IOPT, UNIT_MDV, "MDV", "MDV", NULL }, + { UNIT_IOPT, 0, "none", "NONE", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 PC, IR, i; +register t_stat reason; +void mask_out (int32 mask); + +/* Restore register state */ + +PC = saved_PC & AMASK; /* load local PC */ +C = C & CBIT; +mask_out (pimask); /* reset int system */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; } + +if (int_req > INT_PENDING) { /* interrupt? */ + register int32 MA, indf; + int_req = int_req & ~INT_ION; + old_PC = M[INT_SAV] = PC; + if (int_req & INT_STK) { /* stack overflow? */ + int_req = int_req & ~INT_STK; /* clear */ + MA = STK_JMP; } /* jmp @3 */ + else MA = INT_JMP; /* intr: jmp @1 */ + for (i = 0, indf = 1; indf && (i < ind_max); i++) { + indf = IND_STEP (MA); } /* indirect loop */ + if (i >= ind_max) { + reason = STOP_IND_INT; + break; } + PC = MA; } /* end interrupt */ + +if (PC == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save address */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + +IR = M[PC]; /* fetch instr */ +PC = (PC + 1) & AMASK; +int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ +sim_interval = sim_interval - 1; + +/* Operate instruction */ + +if (IR & I_OPR) { /* operate? */ + register int32 src, srcAC, dstAC; + srcAC = I_GETSRC (IR); /* get reg decodes */ + dstAC = I_GETDST (IR); + switch (I_GETCRY (IR)) { /* decode carry */ + case 0: /* load */ + src = AC[srcAC] | C; + break; + case 1: /* clear */ + src = AC[srcAC]; + break; + case 2: /* set */ + src = AC[srcAC] | CBIT; + break; + case 3: /* complement */ + src = AC[srcAC] | (C ^ CBIT); + break; } /* end switch carry */ + switch (I_GETALU (IR)) { /* decode ALU */ + case 0: /* COM */ + src = src ^ DMASK; + break; + case 1: /* NEG */ + src = ((src ^ DMASK) + 1) & CMASK; + break; + case 2: /* MOV */ + break; + case 3: /* INC */ + src = (src + 1) & CMASK; + break; + case 4: /* ADC */ + src = ((src ^ DMASK) + AC[dstAC]) & CMASK; + break; + case 5: /* SUB */ + src = ((src ^ DMASK) + AC[dstAC] + 1) & CMASK; + break; + case 6: /* ADD */ + src = (src + AC[dstAC]) & CMASK; + break; + case 7: /* AND */ + src = src & (AC[dstAC] | CBIT); + break; } /* end switch oper */ + +/* Operate, continued */ + + switch (I_GETSHF (IR)) { /* decode shift */ + case 0: /* nop */ + break; + case 1: /* L */ + src = ((src << 1) | (src >> 16)) & CMASK; + break; + case 2: /* R */ + src = ((src >> 1) | (src << 16)) & CMASK; + break; + case 3: /* S */ + src = ((src & 0377) << 8) | ((src >> 8) & 0377) | + (src & CBIT); + break; } /* end switch shift */ + switch (I_GETSKP (IR)) { /* decode skip */ + case 0: /* nop */ + if ((IR & I_NLD) && (cpu_unit.flags & UNIT_BYT)) { + register int32 indf, MA; /* Nova 4 trap */ + old_PC = M[TRP_SAV] = (PC - 1) & AMASK; + MA = TRP_JMP; /* jmp @47 */ + for (i = 0, indf = 1; indf && (i < ind_max); i++) { + indf = IND_STEP (MA); } /* resolve ind */ + if (i >= ind_max) { /* indirect loop? */ + reason = STOP_IND_TRP; + break; } + PC = MA; /* new PC */ + break; } + break; + case 1: /* SKP */ + PC = (PC + 1) & AMASK; + break; + case 2: /* SZC */ + if (src < CBIT) PC = (PC + 1) & AMASK; + break; + case 3: /* SNC */ + if (src >= CBIT) PC = (PC + 1) & AMASK; + break; + case 4: /* SZR */ + if ((src & DMASK) == 0) PC = (PC + 1) & AMASK; + break; + case 5: /* SNR */ + if ((src & DMASK) != 0) PC = (PC + 1) & AMASK; + break; + case 6: /* SEZ */ + if (src <= CBIT) PC = (PC + 1) & AMASK; + break; + case 7: /* SBN */ + if (src > CBIT) PC = (PC + 1) & AMASK; + break; } /* end switch skip */ + if ((IR & I_NLD) == 0) { /* load? */ + AC[dstAC] = src & DMASK; + C = src & CBIT; } /* end if load */ + } /* end if operate */ + +/* Memory reference instructions */ + +else if (IR < 060000) { /* mem ref? */ + register int32 src, MA, indf; + MA = I_GETDISP (IR); /* get disp */ + switch (I_GETMODE (IR)) { /* decode mode */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + if (MA & DISPSIGN) MA = 077400 | MA; + MA = (MA + PC - 1) & AMASK; + break; + case 2: /* AC2 relative */ + if (MA & DISPSIGN) MA = 077400 | MA; + MA = (MA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + if (MA & DISPSIGN) MA = 077400 | MA; + MA = (MA + AC[3]) & AMASK; + break; } /* end switch mode */ + + if (indf = IR & I_IND) { /* indirect? */ + for (i = 0; indf && (i < ind_max); i++) { /* count */ + indf = IND_STEP (MA); } /* resolve indirect */ + if (i >= ind_max) { /* too many? */ + reason = STOP_IND; + break; } } + +/* Memory reference, continued */ + + switch (I_GETOPAC (IR)) { /* decode op + AC */ + case 001: /* JSR */ + AC[3] = PC; + case 000: /* JMP */ + old_PC = PC; + PC = MA; + break; + case 002: /* ISZ */ + src = (M[MA] + 1) & DMASK; + if (MEM_ADDR_OK (MA)) M[MA] = src; + if (src == 0) PC = (PC + 1) & AMASK; + break; + case 003: /* DSZ */ + src = (M[MA] - 1) & DMASK; + if (MEM_ADDR_OK (MA)) M[MA] = src; + if (src == 0) PC = (PC + 1) & AMASK; + break; + case 004: /* LDA 0 */ + AC[0] = M[MA]; + break; + case 005: /* LDA 1 */ + AC[1] = M[MA]; + break; + case 006: /* LDA 2 */ + AC[2] = M[MA]; + break; + case 007: /* LDA 3 */ + AC[3] = M[MA]; + break; + case 010: /* STA 0 */ + if (MEM_ADDR_OK (MA)) M[MA] = AC[0]; + break; + case 011: /* STA 1 */ + if (MEM_ADDR_OK (MA)) M[MA] = AC[1]; + break; + case 012: /* STA 2 */ + if (MEM_ADDR_OK (MA)) M[MA] = AC[2]; + break; + case 013: /* STA 3 */ + if (MEM_ADDR_OK (MA)) M[MA] = AC[3]; + break; } /* end switch */ + } /* end mem ref */ + +/* IOT instruction */ + +else { /* IOT */ + register int32 dstAC, pulse, code, device, iodata; + dstAC = I_GETDST (IR); /* decode fields */ + code = I_GETIOT (IR); + pulse = I_GETPULSE (IR); + device = I_GETDEV (IR); + if (code == ioSKP) { /* IO skip? */ + switch (pulse) { /* decode IR<8:9> */ + case 0: /* skip if busy */ + if ((device == DEV_CPU)? (int_req & INT_ION) != 0: + (dev_busy & dev_table[device].mask) != 0) + PC = (PC + 1) & AMASK; + break; + case 1: /* skip if not busy */ + if ((device == DEV_CPU)? (int_req & INT_ION) == 0: + (dev_busy & dev_table[device].mask) == 0) + PC = (PC + 1) & AMASK; + break; + case 2: /* skip if done */ + if ((device == DEV_CPU)? pwr_low != 0: + (dev_done & dev_table[device].mask) != 0) + PC = (PC + 1) & AMASK; + break; + case 3: /* skip if not done */ + if ((device == DEV_CPU)? pwr_low == 0: + (dev_done & dev_table[device].mask) == 0) + PC = (PC + 1) & AMASK; + break; } /* end switch */ + } /* end IO skip */ + +/* IOT, continued */ + + else if (device == DEV_MDV) { + switch (code) { /* case on opcode */ + case ioNIO: /* frame ptr */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) FP = AC[dstAC] & AMASK; + if (pulse == iopC) AC[dstAC] = FP; } + break; + case ioDIA: /* load byte */ + if (cpu_unit.flags & UNIT_BYT) + AC[dstAC] = (M[AC[pulse] >> 1] >> + ((AC[pulse] & 1)? 0: 8)) & 0377; + break; + case ioDOA: /* stack ptr */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) SP = AC[dstAC] & AMASK; + if (pulse == iopC) AC[dstAC] = SP; } + break; + case ioDIB: /* push, pop */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) { + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; + STK_CHECK (SP, 1); } + if (pulse == iopC) { + AC[dstAC] = M[SP]; + SP = DECA (SP); } } + break; + case ioDOB: /* store byte */ + if (cpu_unit.flags & UNIT_BYT) { + register int32 MA, val; + MA = AC[pulse] >> 1; + val = AC[dstAC] & 0377; + if (MEM_ADDR_OK (MA)) M[MA] = (AC[pulse] & 1)? + ((M[MA] & ~0377) | val): + ((M[MA] & 0377) | (val << 8)); } + break; + case ioDIC: /* save, return */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) { + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = FP; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | + (AC[3] & AMASK); + AC[3] = FP = SP; + STK_CHECK (SP, 5); } + if (pulse == iopC) { + old_PC = PC; + C = (M[SP] << 1) & CBIT; + PC = M[SP] & AMASK; + SP = DECA (SP); + AC[3] = M[SP]; + SP = DECA (SP); + AC[2] = M[SP]; + SP = DECA (SP); + AC[1] = M[SP]; + SP = DECA (SP); + AC[0] = M[SP]; + SP = DECA (SP); + FP = AC[3] & AMASK; } } + break; + case ioDOC: + if ((dstAC == 2) && (cpu_unit.flags & UNIT_MDV)) { + register unsigned int32 mddata, uAC0, uAC1, uAC2; + uAC0 = (unsigned int32) AC[0]; + uAC1 = (unsigned int32) AC[1]; + uAC2 = (unsigned int32) AC[2]; + if (pulse == iopP) { /* mul */ + mddata = (uAC1 * uAC2) + uAC0; + AC[0] = (mddata >> 16) & DMASK; + AC[1] = mddata & DMASK; } + if (pulse == iopS) { /* div */ + if (uAC0 >= uAC2) C = CBIT; + else { C = 0; + mddata = (uAC0 << 16) | uAC1; + AC[1] = mddata / uAC2; + AC[0] = mddata % uAC2; } } } + if ((dstAC == 3) && (cpu_unit.flags & UNIT_BYT)) { + register int32 mddata; + if (pulse == iopC) { /* muls */ + mddata = (SEXT (AC[1]) * SEXT (AC[2])) + + SEXT (AC[0]); + AC[0] = (mddata >> 16) & DMASK; + AC[1] = mddata & DMASK; } + if (pulse == iopN) { /* divs */ + if (AC[0] == 0) C = CBIT; + else { mddata = (SEXT (AC[0]) << 16) | AC[1]; + AC[1] = mddata / SEXT (AC[2]); + AC[0] = mddata % SEXT (AC[2]); + if ((AC[1] > 077777) || (AC[1] < -0100000)) + C = CBIT; + else C = 0; + AC[0] = AC[0] & DMASK; } } } + break; } /* end case code */ + } /* end if mul/div */ + +/* IOT, continued */ + + else if (device == DEV_CPU) { /* CPU control */ + switch (code) { /* decode IR<5:7> */ + case ioDIA: /* read switches */ + AC[dstAC] = SR; + break; + case ioDIB: /* int ack */ + AC[dstAC] = 0; + int_req = (int_req & ~INT_DEV) | + (dev_done & ~dev_disable); + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + AC[dstAC] = i; break; } } + break; + case ioDOB: /* mask out */ + mask_out (pimask = AC[dstAC]); + break; + case ioDIC: /* io reset */ + reset_all (0); /* reset devices */ + break; + case ioDOC: /* halt */ + reason = STOP_HALT; + break; } /* end switch code */ + switch (pulse) { /* decode IR<8:9> */ + case iopS: /* ion */ + int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; + break; + case iopC: /* iof */ + int_req = int_req & ~INT_ION; + break; } /* end switch pulse */ + } /* end CPU control */ + else { /* normal device */ + iodata = dev_table[device].routine (pulse, code, AC[dstAC]); + reason = iodata >> IOT_V_REASON; + if (code & 1) AC[dstAC] = iodata & 0177777; } + } /* end if IOT */ +} /* end while */ + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* Null device */ + +int32 nulldev (int32 pulse, int32 code, int32 AC) +{ +return stop_dev << IOT_V_REASON; +} + +/* New priority mask out */ + +void mask_out (int32 newmask) +{ +int32 i; + +dev_disable = 0; +for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (newmask & dev_table[i].pi) + dev_disable = dev_disable | dev_table[i].mask; } +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +C = 0; +int_req = int_req & ~(INT_ION | INT_STK); +pimask = 0; +dev_disable = 0; +pwr_low = 0; +return cpu_svc (&cpu_unit); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Breakpoint service */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} diff --git a/nova_defs.h b/nova_defs.h new file mode 100644 index 00000000..187c5d4d --- /dev/null +++ b/nova_defs.h @@ -0,0 +1,213 @@ +/* nova_defs.h: NOVA simulator definitions + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-Oct-00 RMS Added stack, byte, trap instructions + 14-Apr-99 RMS Changed t_addr to unsigned + 16-Mar-95 RMS Added dynamic memory size + 06-Dec-95 RMS Added magnetic tape + + The author gratefully acknowledges the help of Tom West, Diana + Engelbart, and Carl Friend in resolving questions about the NOVA. +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define AMASK (MAXMEMSIZE - 1) /* address mask */ +#define A_V_IND 15 /* ind: indirect */ +#define A_IND (1 << A_V_IND) +#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE) + +/* Architectural constants */ + +#define SIGN 0100000 /* sign */ +#define DMASK 0177777 /* data mask */ +#define CBIT (DMASK + 1) /* carry bit */ +#define CMASK (CBIT | DMASK) /* carry + data */ + +#define I_OPR 0100000 /* operate */ +#define I_M_SRC 03 /* OPR: src AC */ +#define I_V_SRC 13 +#define I_GETSRC(x) (((x) >> I_V_SRC) & I_M_SRC) +#define I_M_DST 03 /* dst AC */ +#define I_V_DST 11 +#define I_GETDST(x) (((x) >> I_V_DST) & I_M_DST) +#define I_M_ALU 07 /* OPR: ALU op */ +#define I_V_ALU 8 +#define I_GETALU(x) (((x) >> I_V_ALU) & I_M_ALU) +#define I_M_SHF 03 /* OPR: shift */ +#define I_V_SHF 6 +#define I_GETSHF(x) (((x) >> I_V_SHF) & I_M_SHF) +#define I_M_CRY 03 /* OPR: carry */ +#define I_V_CRY 4 +#define I_GETCRY(x) (((x) >> I_V_CRY) & I_M_CRY) +#define I_V_NLD 3 /* OPR: no load */ +#define I_NLD (1 << I_V_NLD) +#define I_M_SKP 07 /* OPR: skip */ +#define I_V_SKP 0 +#define I_GETSKP(x) (((x) >> I_V_SKP) & I_M_SKP) + +#define I_M_OPAC 017 /* MRF: opcode + AC */ +#define I_V_OPAC 11 +#define I_GETOPAC(x) (((x) >> I_V_OPAC) & I_M_OPAC) +#define I_V_IND 10 /* MRF: indirect */ +#define I_IND (1 << I_V_IND) +#define I_M_MODE 03 /* MRF: mode */ +#define I_V_MODE 8 +#define I_GETMODE(x) (((x) >> I_V_MODE) & I_M_MODE) +#define I_M_DISP 0377 /* MRF: disp */ +#define I_V_DISP 0 +#define I_GETDISP(x) (((x) >> I_V_DISP) & I_M_DISP) +#define DISPSIZE (I_M_DISP + 1) /* page size */ +#define DISPSIGN (DISPSIZE >> 1) /* page sign */ + +#define I_M_IOT 07 /* IOT: code */ +#define I_V_IOT 8 +#define I_GETIOT(x) (((x) >> I_V_IOT) & I_M_IOT) +#define I_M_PULSE 03 /* IOT pulse */ +#define I_V_PULSE 6 +#define I_GETPULSE(x) (((x) >> I_V_PULSE) & I_M_PULSE) +#define I_M_DEV 077 /* IOT: device */ +#define I_V_DEV 0 +#define I_GETDEV(x) (((x) >> I_V_DEV) & I_M_DEV) + +/* Special memory locations */ + +#define INT_SAV 0 /* intr saved PC */ +#define INT_JMP 1 /* intr jmp @ */ +#define STK_JMP 3 /* stack jmp @ */ +#define TRP_SAV 046 /* trap saved PC */ +#define TRP_JMP 047 /* trap jmp @ */ +#define AUTO_INC 020 /* start autoinc */ +#define AUTO_DEC 030 /* start autodec */ + +/* Instruction format */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_IND 4 /* indirect loop */ +#define STOP_IND_INT 5 /* ind loop, intr */ +#define STOP_IND_TRP 6 /* ind loop, trap */ + +/* IOT return codes */ + +#define IOT_V_REASON 16 /* set reason */ + +/* IOT fields */ + +#define ioNIO 0 /* opcode field */ +#define ioDIA 1 +#define ioDOA 2 +#define ioDIB 3 +#define ioDOB 4 +#define ioDIC 5 +#define ioDOC 6 +#define ioSKP 7 + +#define iopN 0 /* pulse field */ +#define iopS 1 +#define iopC 2 +#define iopP 3 + +/* Range of implemented device numbers */ + +#define DEV_LOW 010 /* lowest intr dev */ +#define DEV_HIGH 033 /* highest intr dev */ +#define DEV_MDV 001 /* multiply/divide */ +#define DEV_CPU 077 /* CPU control */ + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* I/O structure + + The NOVA I/O structure is tied together by dev_table, indexed by + the device number. Each entry in dev_table consists of + + mask device mask for busy, done (simulator representation) + pi pi disable bit (hardware representation) + routine IOT action routine +*/ + +struct ndev { + int mask; /* done/busy mask */ + int pi; /* assigned pi bit */ + int (*routine)(); /* dispatch routine */ + }; + +/* Device flags (simulator representation) + + Priority (for INTA) runs from low numbers to high +*/ + +#define INT_V_DKP 3 /* moving head disk */ +#define INT_V_DSK 4 /* fixed head disk */ +#define INT_V_MTA 5 /* magnetic tape */ +#define INT_V_LPT 6 /* line printer */ +#define INT_V_CLK 7 /* clock */ +#define INT_V_PTR 8 /* paper tape reader */ +#define INT_V_PTP 9 /* paper tape punch */ +#define INT_V_TTI 10 /* keyboard */ +#define INT_V_TTO 11 /* terminal */ +#define INT_V_STK 13 /* stack overflow */ +#define INT_V_NO_ION_PENDING 14 /* ion delay */ +#define INT_V_ION 15 /* interrupts on */ + +#define INT_DKP (1 << INT_V_DKP) +#define INT_DSK (1 << INT_V_DSK) +#define INT_MTA (1 << INT_V_MTA) +#define INT_LPT (1 << INT_V_LPT) +#define INT_CLK (1 << INT_V_CLK) +#define INT_PTR (1 << INT_V_PTR) +#define INT_PTP (1 << INT_V_PTP) +#define INT_TTI (1 << INT_V_TTI) +#define INT_TTO (1 << INT_V_TTO) +#define INT_STK (1 << INT_V_STK) +#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) +#define INT_ION (1 << INT_V_ION) +#define INT_DEV ((1 << INT_V_STK) - 1) /* device ints */ +#define INT_PENDING INT_ION+INT_NO_ION_PENDING + +/* PI disable bits */ + +#define PI_DKP 0000400 +#define PI_DSK 0000100 +#define PI_MTA 0000040 +#define PI_LPT 0000010 +#define PI_CLK 0000004 +#define PI_PTR 0000020 +#define PI_PTP 0000004 +#define PI_TTI 0000002 +#define PI_TTO 0000001 +/* #define PI_CDR 0000040 */ +/* #define PI_DCM 0100000 */ +/* #define PI_CAS 0000040 */ +/* #define PI_PLT 0000010 */ +/* #define PI_ADCV 0000002 */ diff --git a/nova_dkp.c b/nova_dkp.c new file mode 100644 index 00000000..14b4192c --- /dev/null +++ b/nova_dkp.c @@ -0,0 +1,738 @@ +/* nova_dkp.c: NOVA moving head disk simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dkp moving head disk + + 15-Oct-00 RMS Editorial changes + 14-Apr-99 RMS Changed t_addr to unsigned + 15-Sep-97 RMS Fixed bug in DIB/DOB for new disks + 15-Sep-97 RMS Fixed bug in cylinder extraction (found by Dutch Owen) + 10-Sep-97 RMS Fixed bug in error reporting (found by Dutch Owen) + 25-Nov-96 RMS Defaults to autosize + 29-Jun-96 RMS Added unit disable support +*/ + +#include "nova_defs.h" + +#define DKP_NUMDR 4 /* #drives */ +#define DKP_NUMWD 256 /* words/sector */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 017 +#define UNIT_V_AUTO (UNIT_V_UF + 5) /* autosize */ +#define UNIT_W_UF 7 /* saved flag width */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define FUNC u3 /* function */ +#define CYL u4 /* on cylinder */ + +/* Unit, surface, sector, count register + + Original format: 2b, 6b, 4b, 4b + Revised format: 2b, 5b, 5b, 4b +*/ + +#define USSC_V_COUNT 0 /* count */ +#define USSC_M_COUNT 017 +#define USSC_V_OSECTOR 4 /* old: sector */ +#define USSC_M_OSECTOR 017 +#define USSC_V_OSURFACE 8 /* old: surface */ +#define USSC_M_OSURFACE 077 +#define USSC_V_NSECTOR 4 /* new: sector */ +#define USSC_M_NSECTOR 037 +#define USSC_V_NSURFACE 9 /* new: surface */ +#define USSC_M_NSURFACE 037 +#define USSC_V_UNIT 14 /* unit */ +#define USSC_M_UNIT 03 +#define USSC_UNIT (USSC_M_UNIT << USSC_V_UNIT) +#define GET_COUNT(x) (((x) >> USSC_V_COUNT) & USSC_M_COUNT) +#define GET_SECT(x,dt) ((drv_tab[dt].new)? \ + (((x) >> USSC_V_NSECTOR) & USSC_M_NSECTOR): \ + (((x) >> USSC_V_OSECTOR) & USSC_M_OSECTOR) ) +#define GET_SURF(x,dt) ((drv_tab[dt].new)? \ + (((x) >> USSC_V_NSURFACE) & USSC_M_NSURFACE): \ + (((x) >> USSC_V_OSURFACE) & USSC_M_OSURFACE) ) +#define GET_UNIT(x) (((x) >> USSC_V_UNIT) & USSC_M_UNIT) + +/* Flags, command, cylinder register + + Original format: 5b, 2b, 1b + 8b (surrounding command) + Revised format: 5b, 2b, 9b +*/ + +#define FCCY_V_OCYL 0 /* old: cylinder */ +#define FCCY_M_OCYL 0377 +#define FCCY_V_OCMD 8 /* old: command */ +#define FCCY_M_OCMD 3 +#define FCCY_V_OCEX 10 /* old: cyl extend */ +#define FCCY_OCEX (1 << FCCY_V_OCEX) +#define FCCY_V_NCYL 0 /* new: cylinder */ +#define FCCY_M_NCYL 0777 +#define FCCY_V_NCMD 9 /* new: command */ +#define FCCY_M_NCMD 3 +#define FCCY_READ 0 +#define FCCY_WRITE 1 +#define FCCY_SEEK 2 +#define FCCY_RECAL 3 +#define FCCY_FLAGS 0174000 /* flags */ +#define GET_CMD(x,dt) ((drv_tab[dt].new)? \ + (((x) >> FCCY_V_NCMD) & FCCY_M_NCMD): \ + (((x) >> FCCY_V_OCMD) & FCCY_M_OCMD) ) +#define GET_CYL(x,dt) ((drv_tab[dt].new)? \ + (((x) >> FCCY_V_NCYL) & FCCY_M_NCYL): \ + ((((x) >> FCCY_V_OCYL) & FCCY_M_OCYL) | \ + ((dt != TYPE_D44)? 0: \ + (((x) & FCCY_OCEX) >> (FCCY_V_OCEX - FCCY_V_OCMD)))) ) + +/* Status */ + +#define STA_ERR 0000001 /* error */ +#define STA_DLT 0000002 /* data late */ +#define STA_CRC 0000004 /* crc error */ +#define STA_UNS 0000010 /* unsafe */ +#define STA_XCY 0000020 /* cross cylinder */ +#define STA_CYL 0000040 /* nx cylinder */ +#define STA_DRDY 0000100 /* drive ready */ +#define STA_SEEK3 0000200 /* seeking unit 3 */ +#define STA_SEEK2 0000400 /* seeking unit 2 */ +#define STA_SEEK1 0001000 /* seeking unit 1 */ +#define STA_SEEK0 0002000 /* seeking unit 0 */ +#define STA_SKDN3 0004000 /* seek done unit 3 */ +#define STA_SKDN2 0010000 /* seek done unit 2 */ +#define STA_SKDN1 0020000 /* seek done unit 1 */ +#define STA_SKDN0 0040000 /* seek done unit 0 */ +#define STA_DONE 0100000 /* operation done */ + +#define STA_DYN (STA_DRDY | STA_CYL) /* set from unit */ +#define STA_EFLGS (STA_ERR | STA_DLT | STA_CRC | STA_UNS | \ + STA_XCY | STA_CYL) /* error flags */ +#define STA_DFLGS (STA_DONE | STA_SKDN0 | STA_SKDN1 | \ + STA_SKDN2 | STA_SKDN3) /* done flags */ + +#define GET_SA(cy,sf,sc,t) (((((cy)*drv_tab[t].surf)+(sf))* \ + drv_tab[t].sect)+(sc)) + +/* This controller supports many different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ new format? + surface cylinder drive + + floppy 8 1 77 no + DS/DD floppy 16 2 77 yes + Diablo 31 12 2 203 no + 6225 20 2 245 yes + Century 111 6 10 203 no + Diablo 44 12 4 408 no + 6099 32 4 192 yes + 6227 20 6 245 yes + 6070 24 4 408 yes + Century 114 12 20 203 no + 6103 32 8 192 yes + 4231 23 19 411 yes + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. +*/ + +#define TYPE_FLP 0 +#define SECT_FLP 8 +#define SURF_FLP 1 +#define CYL_FLP 77 +#define SIZE_FLP (SECT_FLP * SURF_FLP * CYL_FLP * DKP_NUMWD) +#define NFMT_FLP FALSE + +#define TYPE_DSDD 1 +#define SECT_DSDD 16 +#define SURF_DSDD 2 +#define CYL_DSDD 77 +#define SIZE_DSDD (SECT_DSDD * SURF_DSDD * CYL_DSDD * DKP_NUMWD) +#define NFMT_DSDD TRUE + +#define TYPE_D31 2 +#define SECT_D31 12 +#define SURF_D31 2 +#define CYL_D31 203 +#define SIZE_D31 (SECT_D31 * SURF_D31 * CYL_D31 * DKP_NUMWD) +#define NFMT_D31 FALSE + +#define TYPE_6225 3 +#define SECT_6225 20 +#define SURF_6225 2 +#define CYL_6225 245 +#define SIZE_6225 (SECT_6225 * SURF_6225 * CYL_6225 * DKP_NUMWD) +#define NFMT_6225 TRUE + +#define TYPE_C111 4 +#define SECT_C111 6 +#define SURF_C111 10 +#define CYL_C111 203 +#define SIZE_C111 (SECT_C111 * SURF_C111 * CYL_C111 * DKP_NUMWD) +#define NFMT_C111 FALSE + +#define TYPE_D44 5 +#define SECT_D44 12 +#define SURF_D44 4 +#define CYL_D44 408 +#define SIZE_D44 (SECT_D44 * SURF_D44 * CYL_D44 * DKP_NUMWD) +#define NFMT_D44 FALSE + +#define TYPE_6099 6 +#define SECT_6099 32 +#define SURF_6099 4 +#define CYL_6099 192 +#define SIZE_6099 (SECT_6099 * SURF_6099 * CYL_6099 * DKP_NUMWD) +#define NFMT_6099 TRUE + +#define TYPE_6227 7 +#define SECT_6227 20 +#define SURF_6227 6 +#define CYL_6227 245 +#define SIZE_6227 (SECT_6227 * SURF_6227 * CYL_6227 * DKP_NUMWD) +#define NFMT_6227 TRUE + +#define TYPE_6070 8 +#define SECT_6070 24 +#define SURF_6070 4 +#define CYL_6070 408 +#define SIZE_6070 (SECT_6070 * SURF_6070 * CYL_6070 * DKP_NUMWD) +#define NFMT_6070 TRUE + +#define TYPE_C114 9 +#define SECT_C114 12 +#define SURF_C114 20 +#define CYL_C114 203 +#define SIZE_C114 (SECT_C114 * SURF_C114 * CYL_C114 * DKP_NUMWD) +#define NFMT_C114 FALSE + +#define TYPE_6103 10 +#define SECT_6103 32 +#define SURF_6103 8 +#define CYL_6103 192 +#define SIZE_6103 (SECT_6103 * SURF_6103 * CYL_6103 * DKP_NUMWD) +#define NFMT_6103 TRUE + +#define TYPE_4231 11 +#define SECT_4231 23 +#define SURF_4231 19 +#define CYL_4231 411 +#define SIZE_4231 (SECT_4231 * SURF_4231 * CYL_4231 * DKP_NUMWD) +#define NFMT_4231 TRUE + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 size; /* #blocks */ + int32 new; /* new format flag */ +}; + +struct drvtyp drv_tab[] = { + { SECT_FLP, SURF_FLP, CYL_FLP, SIZE_FLP, NFMT_FLP }, + { SECT_DSDD, SURF_DSDD, CYL_DSDD, SIZE_DSDD, NFMT_DSDD }, + { SECT_D31, SURF_D31, CYL_D31, SIZE_D31, NFMT_D31 }, + { SECT_6225, SURF_6225, CYL_6225, SIZE_6225, NFMT_6225 }, + { SECT_C111, SURF_C111, CYL_C111, SIZE_C111, NFMT_C111 }, + { SECT_D44, SURF_D44, CYL_D44, SIZE_D44, NFMT_D44 }, + { SECT_6099, SURF_6099, CYL_6099, SIZE_6099, NFMT_6099 }, + { SECT_6227, SURF_6227, CYL_6227, SIZE_6227, NFMT_6227 }, + { SECT_6070, SURF_6070, CYL_6070, SIZE_6070, NFMT_6070 }, + { SECT_C114, SURF_C114, CYL_C114, SIZE_C114, NFMT_C114 }, + { SECT_6103, SURF_6103, CYL_6103, SIZE_6103, NFMT_6103 }, + { SECT_4231, SURF_4231, CYL_4231, SIZE_4231, NFMT_4231 }, + { 0 } }; + +extern unsigned int16 M[]; +extern UNIT cpu_unit; +extern int32 int_req, dev_busy, dev_done, dev_disable; +int32 dkp_ma = 0; /* memory address */ +int32 dkp_ussc = 0; /* unit/sf/sc/cnt */ +int32 dkp_fccy = 0; /* flags/cylinder */ +int32 dkp_sta = 0; /* status register */ +int32 dkp_swait = 100; /* seek latency */ +int32 dkp_rwait = 100; /* rotate latency */ +t_stat dkp_svc (UNIT *uptr); +t_stat dkp_reset (DEVICE *dptr); +t_stat dkp_boot (int32 unitno); +t_stat dkp_attach (UNIT *uptr, char *cptr); +t_stat dkp_go (void); +t_stat dkp_set_size (UNIT *uptr, int32 value); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* DKP data structures + + dkp_dev DKP device descriptor + dkp_unit DKP unit list + dkp_reg DKP register list + dkp_mod DKP modifier list +*/ + +UNIT dkp_unit[] = { + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) } }; + +REG dkp_reg[] = { + { ORDATA (FCCY, dkp_fccy, 16) }, + { ORDATA (USSC, dkp_ussc, 16) }, + { ORDATA (STA, dkp_sta, 16) }, + { ORDATA (MA, dkp_ma, 16) }, + { FLDATA (INT, int_req, INT_V_DKP) }, + { FLDATA (BUSY, dev_busy, INT_V_DKP) }, + { FLDATA (DONE, dev_done, INT_V_DKP) }, + { FLDATA (DISABLE, dev_disable, INT_V_DKP) }, + { DRDATA (STIME, dkp_swait, 24), PV_LEFT }, + { DRDATA (RTIME, dkp_rwait, 24), PV_LEFT }, + { GRDATA (FLG0, dkp_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, dkp_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, dkp_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, dkp_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { DRDATA (CAPAC0, dkp_unit[0].capac, 32), PV_LEFT + REG_HRO }, + { DRDATA (CAPAC1, dkp_unit[1].capac, 32), PV_LEFT + REG_HRO }, + { DRDATA (CAPAC2, dkp_unit[2].capac, 32), PV_LEFT + REG_HRO }, + { DRDATA (CAPAC3, dkp_unit[3].capac, 32), PV_LEFT + REG_HRO }, + { NULL } }; + +MTAB dkp_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE) + UNIT_ATT, + "6030 (floppy)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE) + UNIT_ATT, + "6097 (DS/DD floppy)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE) + UNIT_ATT, + "4047 (Diablo 31)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE) + UNIT_ATT, + "4234/6045 (Diablo 44)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE) + UNIT_ATT, + "4048 (Century 111)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE) + UNIT_ATT, + "2314/4057 (Century 114)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE) + UNIT_ATT, + "6225", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE) + UNIT_ATT, + "6227", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE) + UNIT_ATT, + "6099", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE) + UNIT_ATT, + "6103", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE) + UNIT_ATT, + "6070", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE) + UNIT_ATT, + "4231/3330", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE), + "6030 (floppy)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE), + "6097 (DS/DD floppy)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE), + "4047 (Diablo 31)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE), + "4234/6045 (Diablo 44)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE), + "4048 (Century 111)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE), + "2314/4057 (Century 114)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE), + "6225", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE), + "6227", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE), + "6099", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE), + "6103", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE), + "6070", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE), + "4231/3330", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE), + NULL, "FLOPPY", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE), + NULL, "6030", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE), + NULL, "DSDDFLOPPY", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE), + NULL, "6097", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE), + NULL, "D31", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE), + NULL, "4047", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), + NULL, "D44", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), + NULL, "4234", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), + NULL, "6045", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE), + NULL, "C111", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE), + NULL, "4048", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), + NULL, "C114", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), + NULL, "2314", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), + NULL, "4057", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6225 << UNIT_V_DTYPE), + NULL, "6225", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6227 << UNIT_V_DTYPE), + NULL, "6227", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6099 << UNIT_V_DTYPE), + NULL, "6099", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6103 << UNIT_V_DTYPE), + NULL, "6103", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6070 << UNIT_V_DTYPE), + NULL, "6070", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE), + NULL, "4231", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE), + NULL, "3330", &dkp_set_size }, + { 0 } }; + +DEVICE dkp_dev = { + "DP", dkp_unit, dkp_reg, dkp_mod, + DKP_NUMDR, 8, 30, 1, 8, 16, + NULL, NULL, &dkp_reset, + &dkp_boot, &dkp_attach, NULL }; + +/* IOT routine */ + +int32 dkp (int32 pulse, int32 code, int32 AC) +{ +UNIT *uptr; +int32 u, rval, dtype; + +rval = 0; +uptr = dkp_dev.units + GET_UNIT (dkp_ussc); /* select unit */ +dtype = GET_DTYPE (uptr -> flags); /* get drive type */ +switch (code) { /* decode IR<5:7> */ +case ioDIA: /* DIA */ + dkp_sta = dkp_sta & ~STA_DYN; /* clear dynamic */ + if (uptr -> flags & UNIT_ATT) dkp_sta = dkp_sta | STA_DRDY; + if (uptr -> CYL >= drv_tab[dtype].cyl) + dkp_sta = dkp_sta | STA_CYL; /* bad cylinder? */ + if (dkp_sta & STA_EFLGS) dkp_sta = dkp_sta | STA_ERR; + rval = dkp_sta; + break; +case ioDOA: /* DOA */ + if ((dev_busy & INT_DKP) == 0) { + dkp_fccy = AC; /* save cmd, cyl */ + dkp_sta = dkp_sta & ~(AC & FCCY_FLAGS); } + break; +case ioDIB: /* DIB */ + rval = dkp_ma; /* return buf addr */ + break; +case ioDOB: /* DOB */ + if ((dev_busy & INT_DKP) == 0) dkp_ma = + AC & (drv_tab[dtype].new? DMASK: AMASK); + break; +case ioDIC: /* DIC */ + rval = dkp_ussc; /* return unit, sect */ + break; +case ioDOC: /* DOC */ + if ((dev_busy & INT_DKP) == 0) dkp_ussc = AC; /* save unit, sect */ + break; } /* end switch code */ + +/* IOT, continued */ + +u = GET_UNIT(dkp_ussc); /* select unit */ +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_DKP; /* set busy */ + dev_done = dev_done & ~INT_DKP; /* clear done */ + int_req = int_req & ~INT_DKP; /* clear int */ + if (dkp_go ()) break; /* new cmd, error? */ + dev_busy = dev_busy & ~INT_DKP; /* clear busy */ + dev_done = dev_done | INT_DKP; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + dkp_sta = dkp_sta | STA_DONE; + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_DKP; /* clear busy */ + dev_done = dev_done & ~INT_DKP; /* clear done */ + int_req = int_req & ~INT_DKP; /* clear int */ + dkp_sta = dkp_sta & ~(STA_DFLGS + STA_EFLGS); + if (dkp_unit[u].FUNC != FCCY_SEEK) sim_cancel (&dkp_unit[u]); + break; +case iopP: /* pulse */ + dev_done = dev_done & ~INT_DKP; /* clear done */ + if (dkp_go ()) break; /* new seek command */ + dev_done = dev_done | INT_DKP; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + dkp_sta = dkp_sta | (STA_SKDN0 >> u); /* set seek done */ + break; } /* end case pulse */ +return rval; +} + +/* New command, start vs pulse handled externally + Returns true if command ok, false if error +*/ + +t_stat dkp_go (void) +{ +UNIT *uptr; +int32 newcyl, func, u, dtype; + +dkp_sta = dkp_sta & ~STA_EFLGS; /* clear errors */ +u = GET_UNIT (dkp_ussc); /* get unit number */ +uptr = dkp_dev.units + u; /* get unit */ +if (((uptr -> flags & UNIT_ATT) == 0) || sim_is_active (uptr)) { + dkp_sta = dkp_sta | STA_ERR; /* attached or busy? */ + return FALSE; } +dtype = GET_DTYPE (uptr -> flags); /* get drive type */ +func = GET_CMD (dkp_fccy, dtype); /* get function */ +newcyl = GET_CYL (dkp_fccy, dtype); /* get cylinder */ +switch (func) { /* decode command */ +case FCCY_READ: case FCCY_WRITE: + sim_activate (uptr, dkp_rwait); /* schedule */ + break; +case FCCY_RECAL: /* recalibrate */ + newcyl = 0; + func = FCCY_SEEK; +case FCCY_SEEK: /* seek */ + sim_activate (uptr, dkp_swait * abs (newcyl - uptr -> CYL)); + dkp_sta = dkp_sta | (STA_SEEK0 >> u); /* set seeking */ + uptr -> CYL = newcyl; /* on cylinder */ + break; } /* end case command */ +uptr -> FUNC = func; /* save command */ +return TRUE; /* no error */ +} + +/* Unit service + + If seek done, put on cylinder; + else, do read or write + If controller was busy, clear busy, set done, interrupt + + NXM calculation: this takes advantage of the 4KW granularity of + memory, versus the 4KW maximum transfer size. The end address + is calculated as an absolute value; thus it may be ok, non- + existant, or greater than 32KW (wraps to first bank). + + start addr end addr wc wc1 + ok ok unchanged 0 + ok nxm MEMSIZE-dkp_ma < 0 + ok wrapped MEMSIZE-dkp_ma dkp_ma+wc mod 32K + nxm ok impossible + nxm nxm < 0 < 0 + nxm wrapped < 0 dkp-ma+wc mod 32K +*/ + +t_stat dkp_svc (uptr) +UNIT *uptr; +{ +int32 sc, sa, xcsa, wc, wc1, awc, bda; +int32 dtype, u, err, newsect, newsurf; +t_stat rval; + +rval = SCPE_OK; +dtype = GET_DTYPE (uptr -> flags); /* get drive type */ +if (uptr -> FUNC == FCCY_SEEK) { /* seek? */ + if (uptr -> CYL >= drv_tab[dtype].cyl) /* bad cylinder? */ + dkp_sta = dkp_sta | STA_ERR | STA_CYL; + dev_done = dev_done | INT_DKP; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + u = uptr - dkp_dev.units; /* get unit number */ + dkp_sta = (dkp_sta | (STA_SKDN0 >> u)) /* set seek done */ + & ~(STA_SEEK0 >> u); /* clear seeking */ + return SCPE_OK; } + +if (((uptr -> flags & UNIT_ATT) == 0) || /* not attached? */ + ((uptr -> flags & UNIT_WLK) && (uptr -> FUNC == FCCY_WRITE))) + dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ + +else if ((uptr -> CYL >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SURF (dkp_ussc, dtype) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SECT (dkp_ussc, dtype) >= drv_tab[dtype].sect)) /* or bad sector? */ + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; + +else if (GET_CYL (dkp_fccy, dtype) != uptr -> CYL) /* address error? */ + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; + +else { sc = 16 - GET_COUNT (dkp_ussc); /* get sector count */ + sa = GET_SA (uptr -> CYL, GET_SURF (dkp_ussc, dtype), + GET_SECT (dkp_ussc, dtype), dtype); /* get disk block */ + xcsa = GET_SA (uptr -> CYL + 1, 0, 0, dtype); /* get next cyl addr */ + if ((sa + sc) > xcsa ) { /* across cylinder? */ + sc = xcsa - sa; /* limit transfer */ + dkp_sta = dkp_sta | STA_XCY; } /* xcyl error */ + wc = sc * DKP_NUMWD; /* convert blocks */ + bda = sa * DKP_NUMWD * sizeof (short); /* to words, bytes */ + if (((t_addr) (dkp_ma + wc)) <= MEMSIZE) wc1 = 0; /* xfer fit? */ + else { wc = MEMSIZE - dkp_ma; /* calculate xfer */ + wc1 = (dkp_ma + wc) - MAXMEMSIZE; } /* calculate wrap */ + + err = fseek (uptr -> fileref, bda, SEEK_SET); /* position drive */ + + if (uptr -> FUNC == FCCY_READ) { /* read? */ + if ((wc > 0) && (err == 0)) { /* start in memory? */ + awc = fxread (&M[dkp_ma], sizeof (int16), wc, uptr -> fileref); + for ( ; awc < wc; awc++) M[(dkp_ma + awc) & AMASK] = 0; + err = ferror (uptr -> fileref); + dkp_ma = (dkp_ma + wc) & AMASK; } + if ((wc1 > 0) && (err == 0)) { /* memory wrap? */ + awc = fxread (&M[0], sizeof (int16), wc1, uptr -> fileref); + for ( ; awc < wc1; awc++) M[0 + awc] = 0; + err = ferror (uptr -> fileref); + dkp_ma = (dkp_ma + wc1) & AMASK; } } + + if (uptr -> FUNC == FCCY_WRITE) { /* write? */ + if ((wc > 0) && (err == 0)) { /* start in memory? */ + fxwrite (&M[dkp_ma], sizeof (int16), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + dkp_ma = (dkp_ma + wc + 2) & AMASK; } + if ((wc1 > 0) && (err == 0)) { /* memory wrap? */ + fxwrite (&M[0], sizeof (int16), wc1, uptr -> fileref); + err = ferror (uptr -> fileref); + dkp_ma = (dkp_ma + wc1) & AMASK; } } + + if (err != 0) { + perror ("DKP I/O error"); + rval = SCPE_IOERR; } + clearerr (uptr -> fileref); + + sa = sa + sc; /* update sector addr */ + newsect = sa % drv_tab[dtype].sect; + newsurf = (sa / drv_tab[dtype].sect) % drv_tab[dtype].surf; + dkp_ussc = (dkp_ussc & USSC_UNIT) | ((dkp_ussc + sc) & USSC_M_COUNT) | + ((drv_tab[dtype].new)? + ((newsurf << USSC_V_NSURFACE) | (newsect << USSC_V_NSECTOR)): + ((newsurf << USSC_V_OSURFACE) | (newsect << USSC_V_OSECTOR)) ); + dkp_sta = dkp_sta | STA_DONE; } /* set status */ + +dev_busy = dev_busy & ~INT_DKP; /* clear busy */ +dev_done = dev_done | INT_DKP; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +return rval; +} + +/* Reset routine */ + +t_stat dkp_reset (dptr) +DEVICE *dptr; +{ +int32 u; +UNIT *uptr; + +dev_busy = dev_busy & ~INT_DKP; /* clear busy */ +dev_done = dev_done & ~INT_DKP; /* clear done, int */ +int_req = int_req & ~INT_DKP; +dkp_fccy = dkp_ussc = dkp_ma = dkp_sta = 0; /* clear registers */ +for (u = 0; u < DKP_NUMDR; u++) { /* loop thru units */ + uptr = dkp_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + uptr -> CYL = uptr -> FUNC = 0; } +return SCPE_OK; +} + +/* Attach routine (with optional autosizing) */ + +t_stat dkp_attach (UNIT *uptr, char *cptr) +{ +int32 i, p; +t_stat r; + +uptr -> capac = drv_tab[GET_DTYPE (uptr -> flags)].size; +r = attach_unit (uptr, cptr); +if ((r != SCPE_OK) || ((uptr -> flags & UNIT_AUTO) == 0)) return r; +if (fseek (uptr -> fileref, 0, SEEK_END)) return SCPE_OK; +if ((p = ftell (uptr -> fileref)) == 0) return SCPE_OK; +for (i = 0; drv_tab[i].sect != 0; i++) { + if (p <= (drv_tab[i].size * (int) sizeof (short))) { + uptr -> flags = (uptr -> flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr -> capac = drv_tab[i].size; + return SCPE_OK; } } +return SCPE_OK; +} + +/* Set size command validation routine */ + +t_stat dkp_set_size (UNIT *uptr, int32 value) +{ +if (uptr -> flags & UNIT_ATT) return SCPE_ALATT; +uptr -> capac = drv_tab[GET_DTYPE (value)].size; +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 02000 +#define BOOT_UNIT 02021 +#define BOOT_SEEK 02022 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 060233, /* NIOC 0,DKP ; clear disk */ + 020420, /* LDA 0,USSC ; unit, sfc, sec, cnt */ + 063033, /* DOC 0,DKP ; select disk */ + 020417, /* LDA 0,SEKCMD ; command, cylinder */ + 061333, /* DOAP 0,DKP ; start seek */ + 024415, /* LDA 1,SEKDN */ + 060433, /* DIA 0,DKP ; get status */ + 0123415, /* AND# 1,0,SZR ; skip if done */ + 000776, /* JMP .-2 */ + 0102400, /* SUB 0,0 ; mem addr = 0 */ + 062033, /* DOB 0,DKP */ + 020411, /* LDA 0,REDCMD ; command, cylinder */ + 061133, /* DOAS 0,DKP ; start read */ + 060433, /* DIA 0, DKP ; get status */ + 0101113, /* MOVL# 0,0,SNC ; skip if done */ + 000776, /* JMP .-2 */ + 000377, /* JMP 377 */ + 000016, /* USSC: 0.B1+0.B7+0.B11+16 */ + 0175000, /* SEKCMD: 175000 */ + 074000, /* SEKDN: 074000 */ + 0174000 /* REDCMD: 174000 */ +}; + +t_stat dkp_boot (int32 unitno) +{ +int32 i, dtype; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +unitno = unitno & USSC_M_UNIT; +dtype = GET_DTYPE (dkp_unit[unitno].flags); +M[BOOT_UNIT] = M[BOOT_UNIT] | (unitno << USSC_V_UNIT); +if (drv_tab[dtype].new) M[BOOT_SEEK] = 0176000; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/nova_dsk.c b/nova_dsk.c new file mode 100644 index 00000000..b291c45c --- /dev/null +++ b/nova_dsk.c @@ -0,0 +1,236 @@ +/* nova_dsk.c: 4019 fixed head disk simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dsk fixed head disk + + 15-Oct-00 RMS Editorial changes + 14-Apr-99 RMS Changed t_addr to unsigned + + The 4019 is a head-per-track disk. To minimize overhead, the entire disk + is buffered in memory. +*/ + +#include "nova_defs.h" +#include + +/* Constants */ + +#define DSK_NUMWD 256 /* words/sector */ +#define DSK_NUMSC 8 /* sectors/track */ +#define DSK_NUMTR 128 /* tracks/disk */ +#define DSK_NUMDK 8 /* disks/controller */ +#define DSK_SCSIZE (DSK_NUMDK*DSK_NUMTR*DSK_NUMSC) /* sectors/drive */ +#define DSK_AMASK (DSK_SCSIZE - 1) /* address mask */ +#define DSK_SIZE (DSK_SCSIZE * DSK_NUMWD) /* words/drive */ +#define GET_DISK(x) (((x) / (DSK_NUMSC * DSK_NUMTR)) & (DSK_NUMDK - 1)) + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ + +/* Status register */ + +#define DSKS_WLS 020 /* write lock status */ +#define DSKS_DLT 010 /* data late error */ +#define DSKS_NSD 004 /* non-existent disk */ +#define DSKS_CRC 002 /* parity error */ +#define DSKS_ERR 001 /* error summary */ +#define DSKS_ALLERR (DSKS_WLS | DSKS_DLT | DSKS_NSD | DSKS_CRC | DSKS_ERR) + +/* Map logical sector numbers to physical sector numbers + (indexed by track<2:0>'sector) +*/ + +static const int32 sector_map[] = { + 0, 2, 4, 6, 1, 3, 5, 7, 1, 3, 5, 7, 2, 4, 6, 0, + 2, 4, 6, 0, 3, 5, 7, 1, 3, 5, 7, 1, 4, 6, 0, 2, + 4, 6, 0, 2, 5, 7, 1, 3, 5, 7, 1, 3, 6, 0, 2, 4, + 6, 0, 2, 4, 7, 1, 3, 5, 7, 1, 3, 5, 0, 2, 4, 6 }; + +#define DSK_MMASK 077 +#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DSK_NUMSC))) + +extern unsigned int16 M[]; +extern UNIT cpu_unit; +extern int32 int_req, dev_busy, dev_done, dev_disable; +int32 dsk_stat = 0; /* status register */ +int32 dsk_da = 0; /* disk address */ +int32 dsk_ma = 0; /* memory address */ +int32 dsk_wlk = 0; /* wrt lock switches */ +int32 dsk_stopioe = 1; /* stop on error */ +int32 dsk_time = 100; /* time per sector */ +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_reset (DEVICE *dptr); +t_stat dsk_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* DSK data structures + + dsk_dev device descriptor + dsk_unit unit descriptor + dsk_reg register list +*/ + +UNIT dsk_unit = + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DSK_SIZE) }; + +REG dsk_reg[] = { + { ORDATA (STAT, dsk_stat, 16) }, + { ORDATA (DA, dsk_da, 16) }, + { ORDATA (MA, dsk_ma, 16) }, + { FLDATA (BUSY, dev_busy, INT_V_DSK) }, + { FLDATA (DONE, dev_done, INT_V_DSK) }, + { FLDATA (DISABLE, dev_disable, INT_V_DSK) }, + { FLDATA (INT, int_req, INT_V_DSK) }, + { ORDATA (WLK, dsk_wlk, 8) }, + { DRDATA (TIME, dsk_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, dsk_stopioe, 0) }, + { NULL } }; + +DEVICE dsk_dev = { + "DK", &dsk_unit, dsk_reg, NULL, + 1, 8, 21, 1, 8, 16, + NULL, NULL, &dsk_reset, + &dsk_boot, NULL, NULL }; + +/* IOT routine */ + +int32 dsk (int32 pulse, int32 code, int32 AC) +{ +int32 t, rval; + +rval = 0; +switch (code) { /* decode IR<5:7> */ +case ioDIA: /* DIA */ + rval = dsk_stat & DSKS_ALLERR; /* read status */ + break; +case ioDOA: /* DOA */ + dsk_da = AC & DSK_AMASK; /* save disk addr */ + break; +case ioDIB: /* DIB */ + rval = dsk_ma & AMASK; /* read mem addr */ + break; +case ioDOB: /* DOB */ + dsk_ma = AC & AMASK; /* save mem addr */ + break; } /* end switch code */ + +if (pulse) { /* any pulse? */ + dev_busy = dev_busy & ~INT_DSK; /* clear busy */ + dev_done = dev_done & ~INT_DSK; /* clear done */ + int_req = int_req & ~INT_DSK; /* clear int */ + dsk_stat = 0; /* clear status */ + sim_cancel (&dsk_unit); } /* stop I/O */ + +if ((pulse == iopP) && ((dsk_wlk >> GET_DISK (dsk_da)) & 1)) { /* wrt lock? */ + dev_done = dev_done | INT_DSK; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + dsk_stat = DSKS_ERR + DSKS_WLS; /* set status */ + return rval; } + +if (pulse & 1) { /* read or write? */ + if (((t_addr) (dsk_da * DSK_NUMWD)) >= dsk_unit.capac) { /* invalid sector? */ + dev_done = dev_done | INT_DSK; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */ + return rval; } /* done */ + dsk_unit.FUNC = pulse; /* save command */ + dev_busy = dev_busy | INT_DSK; /* set busy */ + t = sector_map[dsk_da & DSK_MMASK] - GET_SECTOR (dsk_time); + if (t < 0) t = t + DSK_NUMSC; + sim_activate (&dsk_unit, t * dsk_time); } /* activate */ +return rval; +} + +/* Unit service */ + +t_stat dsk_svc (UNIT *uptr) +{ +int32 i, da; +t_addr j; + +dev_busy = dev_busy & ~INT_DSK; /* clear busy */ +dev_done = dev_done | INT_DSK; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + +if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ + dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */ + return IORETURN (dsk_stopioe, SCPE_UNATT); } + +da = dsk_da * DSK_NUMWD; /* calc disk addr */ +if (uptr -> FUNC == iopS) { /* read? */ + for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */ + j = (dsk_ma + i) & AMASK; + if (MEM_ADDR_OK (j)) + M[j] = *(((int16 *) uptr -> filebuf) + da + i); } + dsk_ma = (dsk_ma + DSK_NUMWD) & AMASK; } +if (uptr -> FUNC == iopP) { /* write? */ + for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */ + *(((int16 *) uptr -> filebuf) + da + i) = + M[(dsk_ma + i) & AMASK]; } + dsk_ma = (dsk_ma + DSK_NUMWD + 3) & AMASK; } + +dsk_stat = 0; /* set status */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat dsk_reset (DEVICE *dptr) +{ +dsk_stat = dsk_da = dsk_ma = 0; +dev_busy = dev_busy & ~INT_DSK; /* clear busy */ +dev_done = dev_done & ~INT_DSK; /* clear done */ +int_req = int_req & ~INT_DSK; /* clear int */ +sim_cancel (&dsk_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 2000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 060220, /* NIOC DSK ; clear disk */ + 0102400, /* SUB 0,0 ; addr = 0 */ + 061020, /* DOA 0,DSK ; set disk addr */ + 062120, /* DOBS 0,DSK ; set mem addr, rd */ + 063620, /* SKPDN DSK ; done? */ + 000776, /* JMP .-2 */ + 000377, /* JMP 377 */ +}; + +t_stat dsk_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/nova_lp.c b/nova_lp.c new file mode 100644 index 00000000..ca581236 --- /dev/null +++ b/nova_lp.c @@ -0,0 +1,117 @@ +/* nova_lp.c: NOVA line printer simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt line printer +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable; +int32 lpt_stopioe = 0; /* stop on error */ +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_LPT) }, + { FLDATA (DONE, dev_done, INT_V_LPT) }, + { FLDATA (DISABLE, dev_disable, INT_V_LPT) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL }; + +/* IOT routine */ + +int32 lpt (int32 pulse, int32 code, int32 AC) +{ + +if (code == ioDOA) lpt_unit.buf = AC & 0177; +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_LPT; /* set busy */ + dev_done = dev_done & ~INT_LPT; /* clear done, int */ + int_req = int_req & ~INT_LPT; + if ((lpt_unit.buf != 015) && (lpt_unit.buf != 014) && + (lpt_unit.buf != 012)) + return (lpt_svc (&lpt_unit) << IOT_V_REASON); + sim_activate (&lpt_unit, lpt_unit.wait); + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_LPT; /* clear busy */ + dev_done = dev_done & ~INT_LPT; /* clear done, int */ + int_req = int_req & ~INT_LPT; + sim_cancel (&lpt_unit); /* deactivate unit */ + break; } /* end switch */ +return 0; +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +dev_busy = dev_busy & ~INT_LPT; /* clear busy */ +dev_done = dev_done | INT_LPT; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); +if (putc (lpt_unit.buf, lpt_unit.fileref) == EOF) { + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } +lpt_unit.pos = ftell (lpt_unit.fileref); +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; +dev_busy = dev_busy & ~INT_LPT; /* clear busy */ +dev_done = dev_done & ~INT_LPT; /* clear done, int */ +int_req = int_req & ~INT_LPT; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/nova_mta.c b/nova_mta.c new file mode 100644 index 00000000..32e4fb8b --- /dev/null +++ b/nova_mta.c @@ -0,0 +1,596 @@ +/* nova_mta.c: NOVA magnetic tape simulator + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mta magnetic tape + + 15-Oct-00 RMS Editorial changes + 04-Oct-98 RMS V2.4 magtape format + 18-Jan-97 RMS V2.3 magtape format + 29-Jun-96 RMS Added unit disable support + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count byte count is little endian + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0 and are + not duplicated; end of tape by end of file. +*/ + +#include "nova_defs.h" + +#define MTA_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_W_UF 2 /* saved flags width */ +#define UNIT_WLK 1 << UNIT_V_WLK +#define USTAT u3 /* unit status */ +#define UNUM u4 /* unit number */ +#define DTSIZE (1 << 14) /* max data xfer */ +#define DTMASK (DTSIZE - 1) + +/* Command/unit */ + +#define CU_CI 0100000 /* clear interrupt */ +#define CU_EP 0002000 /* poll enable */ +#define CU_DE 0001000 /* disable erase */ +#define CU_DA 0000400 /* disable autoretry */ +#define CU_PE 0000400 /* PE mode */ +#define CU_V_CMD 3 /* command */ +#define CU_M_CMD 027 +#define CU_READ 000 +#define CU_REWIND 001 +#define CU_CMODE 002 +#define CU_SPACEF 003 +#define CU_SPACER 004 +#define CU_WRITE 005 +#define CU_WREOF 006 +#define CU_ERASE 007 +#define CU_READNS 020 +#define CU_UNLOAD 021 +#define CU_DMODE 022 +#define CU_V_UNIT 0 /* unit */ +#define CU_M_UNIT 07 +#define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD) +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) + +/* Status 1 - stored in mta_sta<31:16> or (*) uptr -> USTAT<31:16> */ + +#define STA_ERR1 (0100000u << 16) /* error */ +#define STA_DLT (0040000 << 16) /* data late */ +#define STA_REW (0020000 << 16) /* *rewinding */ +#define STA_ILL (0010000 << 16) /* illegal */ +#define STA_HDN (0004000 << 16) /* high density */ +#define STA_DAE (0002000 << 16) /* data error */ +#define STA_EOT (0001000 << 16) /* *end of tape */ +#define STA_EOF (0000400 << 16) /* *end of file */ +#define STA_BOT (0000200 << 16) /* *start of tape */ +#define STA_9TK (0000100 << 16) /* nine track */ +#define STA_BAT (0000040 << 16) /* bad tape */ +#define STA_CHG (0000010 << 16) /* status change */ +#define STA_WLK (0000004 << 16) /* *write lock */ +#define STA_ODD (0000002 << 16) /* odd character */ +#define STA_RDY (0000001 << 16) /* *drive ready */ + +/* Status 2 - stored in mta_sta<15:0> or (*) uptr -> USTAT<15:0> */ + +#define STA_ERR2 0100000 /* error */ +#define STA_RWY 0040000 /* runaway tape */ +#define STA_FGP 0020000 /* false gap */ +#define STA_CDL 0004000 /* corrected dlt */ +#define STA_V_UNIT 8 +#define STA_M_UNIT 07 /* unit */ +#define STA_WCO 0000200 /* word count ovflo */ +#define STA_BDS 0000100 /* bad signal */ +#define STA_OVS 0000040 /* overskew */ +#define STA_CRC 0000020 /* check error */ +#define STA_STE 0000010 /* single trk error */ +#define STA_FPR 0000004 /* false preamble */ +#define STA_FMT 0000002 /* format error */ +#define STA_PEM 0000001 /* *PE mode */ + +#define STA_EFLGS1 (STA_DLT | STA_ILL | STA_DAE | STA_EOT | \ + STA_EOF | STA_BOT | STA_BAT | STA_ODD) +#define STA_EFLGS2 (STA_FGP | STA_CDL | STA_BDS | STA_OVS | \ + STA_CRC | STA_FPR | STA_FPR) /* set error 2 */ +#define STA_CLR ((020 << 16) | 0010000) /* always clear */ +#define STA_SET (STA_HDN | STA_9TK) /* always set */ +#define STA_DYN (STA_REW | STA_EOT | STA_EOF | STA_BOT | \ + STA_WLK | STA_RDY | STA_PEM) /* kept in USTAT */ +#define STA_MON (STA_REW | STA_BOT | STA_WLK | STA_RDY | \ + STA_PEM) /* set status chg */ + +extern unsigned int16 M[]; +extern UNIT cpu_unit; +extern int32 int_req, dev_busy, dev_done, dev_disable; +int32 mta_ma = 0; /* memory address */ +int32 mta_wc = 0; /* word count */ +int32 mta_cu = 0; /* command/unit */ +int32 mta_sta = 0; /* status register */ +int32 mta_ep = 0; /* enable polling */ +int32 mta_cwait = 100; /* command latency */ +int32 mta_rwait = 100; /* record latency */ +t_stat mta_svc (UNIT *uptr); +t_stat mta_reset (DEVICE *dptr); +t_stat mta_boot (int32 unitno); +t_stat mta_attach (UNIT *uptr, char *cptr); +t_stat mta_detach (UNIT *uptr); +int32 mta_updcsta (UNIT *uptr); +void mta_upddsta (UNIT *uptr, int32 newsta); +t_stat mta_vlock (UNIT *uptr, int32 val); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +static const int ctype[32] = { /* c vs r timing */ + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 }; + +/* MTA data structures + + mta_dev MTA device descriptor + mta_unit MTA unit list + mta_reg MTA register list + mta_mod MTA modifier list +*/ + +UNIT mta_unit[] = { + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } }; + +REG mta_reg[] = { + { ORDATA (CU, mta_cu, 16) }, + { ORDATA (MA, mta_ma, 16) }, + { ORDATA (WC, mta_wc, 16) }, + { GRDATA (STA1, mta_sta, 8, 16, 16) }, + { ORDATA (STA2, mta_sta, 16) }, + { FLDATA (EP, mta_ep, 0) }, + { FLDATA (BUSY, dev_busy, INT_V_MTA) }, + { FLDATA (DONE, dev_done, INT_V_MTA) }, + { FLDATA (DISABLE, dev_disable, INT_V_MTA) }, + { FLDATA (INT, int_req, INT_V_MTA) }, + { DRDATA (CTIME, mta_cwait, 24), PV_LEFT }, + { DRDATA (RTIME, mta_rwait, 24), PV_LEFT }, + { ORDATA (UST0, mta_unit[0].USTAT, 32) }, + { ORDATA (UST1, mta_unit[1].USTAT, 32) }, + { ORDATA (UST2, mta_unit[2].USTAT, 32) }, + { ORDATA (UST3, mta_unit[3].USTAT, 32) }, + { ORDATA (UST4, mta_unit[4].USTAT, 32) }, + { ORDATA (UST5, mta_unit[5].USTAT, 32) }, + { ORDATA (UST6, mta_unit[6].USTAT, 32) }, + { ORDATA (UST7, mta_unit[7].USTAT, 32) }, + { DRDATA (POS0, mta_unit[0].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS1, mta_unit[1].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS2, mta_unit[2].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS3, mta_unit[3].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS4, mta_unit[4].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS5, mta_unit[5].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS6, mta_unit[6].pos, 31), REG_RO + PV_LEFT }, + { DRDATA (POS7, mta_unit[7].pos, 31), REG_RO + PV_LEFT }, + { GRDATA (FLG0, mta_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, mta_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, mta_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, mta_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, mta_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, mta_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, mta_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, mta_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { NULL } }; + +MTAB mta_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", &mta_vlock }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &mta_vlock }, + { 0 } }; + +DEVICE mta_dev = { + "MT", mta_unit, mta_reg, mta_mod, + MTA_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mta_reset, + &mta_boot, &mta_attach, &mta_detach }; + +/* IOT routine */ + +int32 mta (int32 pulse, int32 code, int32 AC) +{ +UNIT *uptr; +int32 u, c, rval; + +rval = 0; +uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */ +switch (code) { /* decode IR<5:7> */ +case ioDIA: /* DIA */ + rval = (mta_updcsta (uptr) >> 16) & DMASK; /* return status 1 */ + break; +case ioDOA: /* DOA */ +/* if (AC & CU_CI) ... clear ep int */ + mta_cu = AC; /* save cmd/unit */ + uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */ + mta_updcsta (uptr); /* update status */ + break; +case ioDIB: /* DIB */ + rval = mta_ma & AMASK; /* return ma */ + break; +case ioDOB: /* DOB */ + mta_ma = AC & AMASK; /* save ma */ + break; +case ioDIC: /* DIC */ + rval = mta_updcsta (uptr) & DMASK; /* return status 2 */ + break; +case ioDOC: /* DOC */ + mta_wc = ((AC & 040000) << 1) | (AC & 077777); /* save wc */ + break; } /* end switch code */ + +/* IOT, continued */ + +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + c = GET_CMD (mta_cu); /* get command */ + if (dev_busy & INT_MTA) break; /* ignore if busy */ + if ((uptr -> USTAT & STA_RDY) == 0) { /* drive not ready? */ + mta_sta = mta_sta | STA_ILL; /* illegal op */ + dev_busy = dev_busy & ~INT_MTA; /* clear busy */ + dev_done = dev_done | INT_MTA; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); } + else if ((c == CU_REWIND) || (c == CU_UNLOAD)) { /* rewind, unload? */ + mta_upddsta (uptr, (uptr -> USTAT & /* update status */ + ~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)) | STA_REW); + sim_activate (uptr, mta_rwait); /* start IO */ + if (c == CU_UNLOAD) detach_unit (uptr); } + else { mta_sta = 0; /* clear errors */ + dev_busy = dev_busy | INT_MTA; /* set busy */ + dev_done = dev_done & ~INT_MTA; /* clear done */ + int_req = int_req & ~INT_MTA; /* clear int */ + if (ctype[c]) sim_activate (uptr, mta_cwait); + else { mta_upddsta (uptr, uptr -> USTAT & + ~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)); + sim_activate (uptr, mta_rwait); } } + mta_updcsta (uptr); /* update status */ + break; +case iopC: /* clear */ + for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */ + uptr = mta_dev.units + u; /* cancel IO */ + if (sim_is_active (uptr) && !(uptr -> USTAT & STA_REW)) { + mta_upddsta (uptr, uptr -> USTAT | STA_RDY); + sim_cancel (uptr); } } + dev_busy = dev_busy & ~INT_MTA; /* clear busy */ + dev_done = dev_done & ~INT_MTA; /* clear done */ + int_req = int_req & ~INT_MTA; /* clear int */ + mta_sta = mta_cu = mta_ma = 0; /* clear registers */ + mta_updcsta (&mta_unit[0]); /* update status */ + break; } /* end case pulse */ +return rval; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, clear busy, set done, interrupt +*/ + +t_stat mta_svc (UNIT *uptr) +{ +int32 c, i, p, u, err; +t_stat rval; +t_mtrlnt cbc, tbc, wc; +unsigned int16 c1, c2; +unsigned int8 dbuf[2 * DTSIZE]; +static t_mtrlnt bceof = { 0 }; + +rval = SCPE_OK; +u = uptr -> UNUM; /* get unit number */ +if (uptr -> USTAT & STA_REW) { /* rewind? */ + uptr -> pos = 0; /* update position */ + mta_upddsta (uptr, (uptr -> USTAT & ~STA_REW) | STA_BOT | STA_RDY); + if (u == GET_UNIT (mta_cu)) mta_updcsta (uptr); + return rval; } + +err = 0; +c = GET_CMD (mta_cu); /* command */ +wc = DTSIZE - (mta_wc & DTMASK); /* io wc */ + +if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */ + mta_upddsta (uptr, 0); /* unit off line */ + mta_sta = mta_sta | STA_ILL; } /* illegal operation */ + +else if ((uptr -> flags & UNIT_WLK) && /* write locked? */ + ((c == CU_WRITE) || (c == CU_WREOF) || (c == CU_ERASE))) { + mta_upddsta (uptr, uptr -> USTAT | STA_WLK | STA_RDY); + mta_sta = mta_sta | STA_ILL; } /* illegal operation */ + +else switch (c) { /* case on command */ +case CU_CMODE: /* controller mode */ + mta_ep = mta_cu & CU_EP; + break; +case CU_DMODE: /* drive mode */ + if (uptr -> pos) mta_sta = mta_sta | STA_ILL; /* must be BOT */ + else mta_upddsta (uptr, (mta_cu & CU_PE)? /* update drv status */ + uptr -> USTAT | STA_PEM: uptr -> USTAT & ~ STA_PEM); + break; + +/* Unit service, continued */ + +case CU_READ: /* read */ +case CU_READNS: /* read non-stop */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); /* read byte count */ + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + (feof (uptr -> fileref))) { + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOT); + break; } + if (tbc == 0) { /* tape mark? */ + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOF); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + tbc = MTRL (tbc); /* ignore error flag */ + cbc = wc * 2; /* expected bc */ + if (tbc & 1) mta_sta = mta_sta | STA_ODD; /* odd byte count? */ + if (tbc > cbc) mta_sta = mta_sta | STA_WCO; /* too big? */ + else { cbc = tbc; /* no, use it */ + wc = (cbc + 1) / 2; } /* adjust wc */ + i = fxread (dbuf, sizeof (int8), cbc, uptr -> fileref); + for ( ; i < cbc; i++) dbuf[i] = 0; + err = ferror (uptr -> fileref); + for (i = p = 0; i < wc; i++) { /* copy buf to mem */ + c1 = dbuf[p++]; + c2 = dbuf[p++]; + if (MEM_ADDR_OK (mta_ma)) M[mta_ma] = (c1 << 8) | c2; + mta_ma = (mta_ma + 1) & AMASK; } + mta_wc = (mta_wc + wc) & DMASK; + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + mta_upddsta (uptr, uptr -> USTAT | STA_RDY); + break; +case CU_WRITE: /* write */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + tbc = wc * 2; /* io byte count */ + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + for (i = p = 0; i < wc; i++) { /* copy to buffer */ + dbuf[p++] = (M[mta_ma] >> 8) & 0377; + dbuf[p++] = M[mta_ma] & 0377; + mta_ma = (mta_ma + 1) & AMASK; } + fxwrite (dbuf, sizeof (int8), tbc, uptr -> fileref); + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + mta_wc = 0; + uptr -> pos = uptr -> pos + tbc + (2 * sizeof (t_mtrlnt)); + mta_upddsta (uptr, uptr -> USTAT | STA_RDY); + break; +case CU_WREOF: /* write eof */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + mta_upddsta (uptr, uptr -> USTAT | STA_EOF | STA_RDY); + break; +case CU_ERASE: /* erase */ + mta_upddsta (uptr, uptr -> USTAT | STA_RDY); + break; + +/* Unit service, continued */ + +case CU_SPACEF: /* space forward */ + do { mta_wc = (mta_wc + 1) & DMASK; /* incr wc */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + (feof (uptr -> fileref))) { + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOT); + break; } + if (tbc == 0) { /* zero bc? */ + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOF); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); } + while (mta_wc != 0); + mta_upddsta (uptr, uptr -> USTAT | STA_RDY); + break; +case CU_SPACER: /* space reverse */ + if (uptr -> pos == 0) { /* at BOT? */ + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_BOT); + break; } + do { mta_wc = (mta_wc + 1) & DMASK; /* incr wc */ + fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), + SEEK_SET); /* bs to reclnt */ + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + (feof (uptr -> fileref))) { + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_BOT); + uptr -> pos = 0; + break; } + if (tbc == 0) { /* zero bc? */ + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOF); + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); + if (uptr -> pos == 0) { /* start of tape? */ + mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_BOT); + break; } } + while (mta_wc != 0); + mta_upddsta (uptr, uptr -> USTAT | STA_RDY); + break; +default: /* reserved */ + mta_sta = mta_sta | STA_ILL; + break; } /* end case */ + +/* Unit service, continued */ + +if (err != 0) { /* I/O error */ + mta_sta = mta_sta | STA_DAE; /* flag error */ + perror ("MTA I/O error"); + rval = SCPE_IOERR; + clearerr (uptr -> fileref); } +mta_updcsta (uptr); /* update status */ +dev_busy = dev_busy & ~INT_MTA; /* clear busy */ +dev_done = dev_done | INT_MTA; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +return rval; +} + +/* Update controller status */ + +int32 mta_updcsta (UNIT *uptr) /* update ctrl */ +{ +mta_sta = (mta_sta & ~(STA_DYN | STA_CLR | STA_ERR1 | STA_ERR2)) | + (uptr -> USTAT & STA_DYN) | STA_SET; +if (mta_sta & STA_EFLGS1) mta_sta = mta_sta | STA_ERR1; +if (mta_sta & STA_EFLGS2) mta_sta = mta_sta | STA_ERR2; +return mta_sta; +} + +/* Update drive status */ + +void mta_upddsta (UNIT *uptr, int32 newsta) /* drive status */ +{ +int32 change; + +if ((uptr -> flags & UNIT_ATT) == 0) newsta = 0; /* offline? */ +change = (uptr -> USTAT ^ newsta) & STA_MON; /* changes? */ +uptr -> USTAT = newsta & STA_DYN; /* update status */ +if (change) { +/* if (mta_ep) { /* if polling */ +/* u = uptr -> UNUM; /* unit num */ +/* mta_sta = (mta_sta & ~STA_UNIT) | (u << STA_V_UNIT); +/* set polling interupt... } */ + mta_sta = mta_sta | STA_CHG; } /* flag change */ +return; +} + +/* Reset routine */ + +t_stat mta_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +dev_busy = dev_busy & ~INT_MTA; /* clear busy */ +dev_done = dev_done & ~INT_MTA; /* clear done, int */ +int_req = int_req & ~INT_MTA; +mta_cu = mta_wc = mta_ma = mta_sta = 0; /* clear registers */ +mta_ep = 0; +for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */ + uptr = mta_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + uptr -> UNUM = u; + if (uptr -> flags & UNIT_ATT) uptr -> USTAT = STA_RDY | + (uptr -> USTAT & STA_PEM) | + ((uptr -> flags & UNIT_WLK)? STA_WLK: 0) | + ((uptr -> pos)? 0: STA_BOT); + else uptr -> USTAT = 0; } +mta_updcsta (&mta_unit[0]); /* update status */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mta_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +if (!sim_is_active (uptr)) mta_upddsta (uptr, STA_RDY | STA_BOT | STA_PEM | + ((uptr -> flags & UNIT_WLK)? STA_WLK: 0)); +return r; +} + +/* Detach routine */ + +t_stat mta_detach (UNIT* uptr) +{ +if (!sim_is_active (uptr)) mta_upddsta (uptr, 0); +return detach_unit (uptr); +} + +/* Write lock/unlock validate routine */ + +t_stat mta_vlock (UNIT *uptr, int32 val) +{ +if ((uptr -> flags & UNIT_ATT) && val) + mta_upddsta (uptr, uptr -> USTAT | STA_WLK); +else mta_upddsta (uptr, uptr -> USTAT & ~STA_WLK); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 02000 +#define BOOT_UNIT 02020 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 060222, /* NIOC 0,MTA ; clear disk */ + 020417, /* LDA 0,UNIT ; unit */ + 024417, /* LDA 1,REWIND ; cmd */ + 0107000, /* ADD 0,1 ; cmd + unit */ + 065122, /* DOAS 1,MTA ; start rewind */ + 070422, /* DIA 2,MTA ; get status */ + 0151213, /* MOVR# 2,2,SNC ; skip if done */ + 000776, /* JMP .-2 */ + 0126400, /* SUB 1,1 ; ma, wc = 0 */ + 066022, /* DOB 1,MTA */ + 067022, /* DOC 1,MTA */ + 061122, /* DOAS 0,MTA ; start read */ + 070422, /* DIA 2,MTA ; get status */ + 0151213, /* MOVR# 2,2,SNC ; skip if done */ + 000776, /* JMP .-2 */ + 000377, /* JMP 377 */ + 000000, /* UNIT: */ + 000010 /* REWIND: 10 */ +}; + +t_stat mta_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +M[BOOT_UNIT] = (unitno & CU_M_UNIT) << CU_V_UNIT; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/nova_pt.c b/nova_pt.c new file mode 100644 index 00000000..b5a36a3e --- /dev/null +++ b/nova_pt.c @@ -0,0 +1,201 @@ +/* nova_pt.c: NOVA paper tape read/punch simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable; +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_PTR) }, + { FLDATA (DONE, dev_done, INT_V_PTR) }, + { FLDATA (DISABLE, dev_disable, INT_V_PTR) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { DRDATA (POS, ptr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + NULL, NULL, NULL }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_PTP) }, + { FLDATA (DONE, dev_done, INT_V_PTP) }, + { FLDATA (DISABLE, dev_disable, INT_V_PTP) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { DRDATA (POS, ptp_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL }; + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 pulse, int32 code, int32 AC) +{ +int32 iodata; + +iodata = (code == ioDIA)? ptr_unit.buf & 0377: 0; +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_PTR; /* set busy */ + dev_done = dev_done & ~INT_PTR; /* clear done, int */ + int_req = int_req & ~INT_PTR; + sim_activate (&ptr_unit, ptr_unit.wait); /* activate unit */ + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_PTR; /* clear busy */ + dev_done = dev_done & ~INT_PTR; /* clear done, int */ + int_req = int_req & ~INT_PTR; + sim_cancel (&ptr_unit); /* deactivate unit */ + break; } /* end switch */ +return iodata; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; } +dev_busy = dev_busy & ~INT_PTR; /* clear busy */ +dev_done = dev_done | INT_PTR; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +ptr_unit.buf = temp & 0377; +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; +dev_busy = dev_busy & ~INT_PTR; /* clear busy */ +dev_done = dev_done & ~INT_PTR; /* clear done, int */ +int_req = int_req & ~INT_PTR; +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) ptp_unit.buf = AC & 0377; +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_PTP; /* set busy */ + dev_done = dev_done & ~INT_PTP; /* clear done, int */ + int_req = int_req & ~INT_PTP; + sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_PTP; /* clear busy */ + dev_done = dev_done & ~INT_PTP; /* clear done, int */ + int_req = int_req & ~INT_PTP; + sim_cancel (&ptp_unit); /* deactivate unit */ + break; } /* end switch */ +return 0; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +dev_busy = dev_busy & ~INT_PTP; /* clear busy */ +dev_done = dev_done | INT_PTP; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +dev_busy = dev_busy & ~INT_PTP; /* clear busy */ +dev_done = dev_done & ~INT_PTP; /* clear done, int */ +int_req = int_req & ~INT_PTP; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/nova_sys.c b/nova_sys.c new file mode 100644 index 00000000..cf6a816b --- /dev/null +++ b/nova_sys.c @@ -0,0 +1,678 @@ +/* nova_sys.c: NOVA simulator interface + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 15-Oct-00 RMS Added stack, byte, trap instructions + 14-Apr-99 RMS Changed t_addr to unsigned + 27-Oct-98 RMS V2.4 load interface + 24-Sep-97 RMS Fixed bug in device name table (found by Dutch Owen) +*/ + +#include "nova_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE clk_dev, lpt_dev; +extern DEVICE dkp_dev, dsk_dev; +extern DEVICE mta_dev; +extern REG cpu_reg[]; +extern unsigned int16 M[]; +extern int32 saved_PC; + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "NOVA"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { &cpu_dev, + &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, + &clk_dev, &lpt_dev, &dsk_dev, &dkp_dev, + &mta_dev, NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O instruction", + "HALT instruction", + "Breakpoint", + "Nested indirect address limit exceeded", + "Nested indirect interrupt address limit exceeded", + "Nested indirect trap address limit exceeded" }; + +/* Binary loader + + Loader format consists of blocks, optionally preceded, separated, and + followed by zeroes. Each block consists of: + + lo_count + hi_count + lo_origin + hi_origin + lo_checksum + hi_checksum + lo_data byte --- + hi_data byte | + : > -count words + lo_data byte | + hi_data byte --- + + If the word count is [0,-20], then the block is normal data. + If the word count is [-21,-n], then the block is repeated data. + If the word count is 1, the block is the start address. + If the word count is >1, the block is an error block. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 data, csum, count, state, i; +t_addr origin; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +state = 0; +while ((i = getc (fileref)) != EOF) { + switch (state) { + case 0: /* leader */ + count = i; + state = (count != 0); + break; + case 1: /* high count */ + csum = count = (i << 8) | count; + state = 2; + break; + case 2: /* low origin */ + origin = i; + state = 3; + break; + case 3: /* high origin */ + origin = (i << 8) | origin; + csum = csum + origin; + state = 4; + break; + case 4: /* low checksum */ + csum = csum + i; + state = 5; + break; + case 5: /* high checksum */ + csum = csum + (i << 8); + if (count == 1) saved_PC = origin; /* count = 1? */ + if (count <= 1) { /* count = 0/1? */ + if (csum & 0177777) return SCPE_CSUM; + state = 0; + break; } + if (count < 0100000) { /* count > 1 */ + state = 8; + break; } + count = 0200000 - count; + state = 6; + break; + case 6: /* low data */ + data = i; + state = 7; + break; + case 7: /* high data */ + data = (i << 8) | data; + csum = csum + data; + if (count > 20) { /* large block */ + for (count = count - 1; count == 1; count--) { + if (origin >= MEMSIZE) return SCPE_NXM; + M[origin] = data; + origin = origin + 1; } } + if (origin >= MEMSIZE) return SCPE_NXM; + M[origin] = data; + origin = origin + 1; + count = count - 1; + if (count == 0) { + if (csum & 0177777) return SCPE_CSUM; + state = 0; + break; } + state = 6; + break; + case 8: /* error block */ + if (i == 0377) state = 0; + break; } /* end switch */ + } /* end while */ + +/* Ok to find end of tape between blocks or in error state */ + +return ((state == 0) || (state == 8))? SCPE_OK: SCPE_FMT; +} + +/* Symbol tables */ + +#define I_V_FL 18 /* flag bits */ +#define I_M_FL 07 /* flag width */ +#define I_V_NPN 0 /* no operands */ +#define I_V_R 1 /* reg */ +#define I_V_D 2 /* device */ +#define I_V_RD 3 /* reg,device */ +#define I_V_M 4 /* mem addr */ +#define I_V_RM 5 /* reg, mem addr */ +#define I_V_RR 6 /* operate */ +#define I_V_BY 7 /* byte pointer */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_R (I_V_R << I_V_FL) +#define I_D (I_V_D << I_V_FL) +#define I_RD (I_V_RD << I_V_FL) +#define I_M (I_V_M << I_V_FL) +#define I_RM (I_V_RM << I_V_FL) +#define I_RR (I_V_RR << I_V_FL) +#define I_BY (I_V_BY << I_V_FL) + +static const int32 masks[] = { +0177777, 0163777, 0177700, 0163700, +0174000, 0160000, 0103770, 0163477 }; + +static const char *opcode[] = { + "JMP", "JSR", "ISZ", "DSZ", + "LDA", "STA", + "COM", "COMZ", "COMO", "COMC", + "COML", "COMZL", "COMOL", "COMCL", + "COMR", "COMZR", "COMOR", "COMCR", + "COMS", "COMZS", "COMOS", "COMCS", + "COM#", "COMZ#", "COMO#", "COMC#", + "COML#", "COMZL#", "COMOL#", "COMCL#", + "COMR#", "COMZR#", "COMOR#", "COMCR#", + "COMS#", "COMZS#", "COMOS#", "COMCS#", + "NEG", "NEGZ", "NEGO", "NEGC", + "NEGL", "NEGZL", "NEGOL", "NEGCL", + "NEGR", "NEGZR", "NEGOR", "NEGCR", + "NEGS", "NEGZS", "NEGOS", "NEGCS", + "NEG#", "NEGZ#", "NEGO#", "NEGC#", + "NEGL#", "NEGZL#", "NEGOL#", "NEGCL#", + "NEGR#", "NEGZR#", "NEGOR#", "NEGCR#", + "NEGS#", "NEGZS#", "NEGOS#", "NEGCS#", + "MOV", "MOVZ", "MOVO", "MOVC", + "MOVL", "MOVZL", "MOVOL", "MOVCL", + "MOVR", "MOVZR", "MOVOR", "MOVCR", + "MOVS", "MOVZS", "MOVOS", "MOVCS", + "MOV#", "MOVZ#", "MOVO#", "MOVC#", + "MOVL#", "MOVZL#", "MOVOL#", "MOVCL#", + "MOVR#", "MOVZR#", "MOVOR#", "MOVCR#", + "MOVS#", "MOVZS#", "MOVOS#", "MOVCS#", + "INC", "INCZ", "INCO", "INCC", + "INCL", "INCZL", "INCOL", "INCCL", + "INCR", "INCZR", "INCOR", "INCCR", + "INCS", "INCZS", "INCOS", "INCCS", + "INC#", "INCZ#", "INCO#", "INCC#", + "INCL#", "INCZL#", "INCOL#", "INCCL#", + "INCR#", "INCZR#", "INCOR#", "INCCR#", + "INCS#", "INCZS#", "INCOS#", "INCCS#", + "ADC", "ADCZ", "ADCO", "ADCC", + "ADCL", "ADCZL", "ADCOL", "ADCCL", + "ADCR", "ADCZR", "ADCOR", "ADCCR", + "ADCS", "ADCZS", "ADCOS", "ADCCS", + "ADC#", "ADCZ#", "ADCO#", "ADCC#", + "ADCL#", "ADCZL#", "ADCOL#", "ADCCL#", + "ADCR#", "ADCZR#", "ADCOR#", "ADCCR#", + "ADCS#", "ADCZS#", "ADCOS#", "ADCCS#", + "SUB", "SUBZ", "SUBO", "SUBC", + "SUBL", "SUBZL", "SUBOL", "SUBCL", + "SUBR", "SUBZR", "SUBOR", "SUBCR", + "SUBS", "SUBZS", "SUBOS", "SUBCS", + "SUB#", "SUBZ#", "SUBO#", "SUBC#", + "SUBL#", "SUBZL#", "SUBOL#", "SUBCL#", + "SUBR#", "SUBZR#", "SUBOR#", "SUBCR#", + "SUBS#", "SUBZS#", "SUBOS#", "SUBCS#", + "ADD", "ADDZ", "ADDO", "ADDC", + "ADDL", "ADDZL", "ADDOL", "ADDCL", + "ADDR", "ADDZR", "ADDOR", "ADDCR", + "ADDS", "ADDZS", "ADDOS", "ADDCS", + "ADD#", "ADDZ#", "ADDO#", "ADDC#", + "ADDL#", "ADDZL#", "ADDOL#", "ADDCL#", + "ADDR#", "ADDZR#", "ADDOR#", "ADDCR#", + "ADDS#", "ADDZS#", "ADDOS#", "ADDCS#", + "AND", "ANDZ", "ANDO", "ANDC", + "ANDL", "ANDZL", "ANDOL", "ANDCL", + "ANDR", "ANDZR", "ANDOR", "ANDCR", + "ANDS", "ANDZS", "ANDOS", "ANDCS", + "AND#", "ANDZ#", "ANDO#", "ANDC#", + "ANDL#", "ANDZL#", "ANDOL#", "ANDCL#", + "ANDR#", "ANDZR#", "ANDOR#", "ANDCR#", + "ANDS#", "ANDZS#", "ANDOS#", "ANDCS#", + "ION", "IOF", + "RDSW", "INTA", "MSKO", "IORST", "HALT", + "MUL", "DIV", "MULS", "DIVS", + "PSHA", "POPA", "SAV", "RET", + "MTSP", "MTFP", "MFSP", "MFFP", + "LDB", "STB", + "NIO", "NIOS", "NIOC", "NIOP", + "DIA", "DIAS", "DIAC", "DIAP", + "DOA", "DOAS", "DOAC", "DOAP", + "DIB", "DIBS", "DIBC", "DIBP", + "DOB", "DOBS", "DOBC", "DOBP", + "DIC", "DICS", "DICC", "DICP", + "DOC", "DOCS", "DOCC", "DOCP", + "SKPBN", "SKPBZ", "SKPDN", "SKPDZ", + NULL }; + +static const opc_val[] = { + 0000000+I_M, 0004000+I_M, 0010000+I_M, 0014000+I_M, + 0020000+I_RM, 0040000+I_RM, + 0100000+I_RR, 0100020+I_RR, 0100040+I_RR, 0100060+I_RR, + 0100100+I_RR, 0100120+I_RR, 0100140+I_RR, 0100160+I_RR, + 0100200+I_RR, 0100220+I_RR, 0100240+I_RR, 0100260+I_RR, + 0100300+I_RR, 0100320+I_RR, 0100340+I_RR, 0100360+I_RR, + 0100010+I_RR, 0100030+I_RR, 0100050+I_RR, 0100070+I_RR, + 0100110+I_RR, 0100130+I_RR, 0100150+I_RR, 0100170+I_RR, + 0100210+I_RR, 0100230+I_RR, 0100250+I_RR, 0100270+I_RR, + 0100310+I_RR, 0100330+I_RR, 0100350+I_RR, 0100370+I_RR, + 0100400+I_RR, 0100420+I_RR, 0100440+I_RR, 0100460+I_RR, + 0100500+I_RR, 0100520+I_RR, 0100540+I_RR, 0100560+I_RR, + 0100600+I_RR, 0100620+I_RR, 0100640+I_RR, 0100660+I_RR, + 0100700+I_RR, 0100720+I_RR, 0100740+I_RR, 0100760+I_RR, + 0100410+I_RR, 0100430+I_RR, 0100450+I_RR, 0100470+I_RR, + 0100510+I_RR, 0100530+I_RR, 0100550+I_RR, 0100570+I_RR, + 0100610+I_RR, 0100630+I_RR, 0100650+I_RR, 0100670+I_RR, + 0100710+I_RR, 0100730+I_RR, 0100750+I_RR, 0100770+I_RR, + 0101000+I_RR, 0101020+I_RR, 0101040+I_RR, 0101060+I_RR, + 0101100+I_RR, 0101120+I_RR, 0101140+I_RR, 0101160+I_RR, + 0101200+I_RR, 0101220+I_RR, 0101240+I_RR, 0101260+I_RR, + 0101300+I_RR, 0101320+I_RR, 0101340+I_RR, 0101360+I_RR, + 0101010+I_RR, 0101030+I_RR, 0101050+I_RR, 0101070+I_RR, + 0101110+I_RR, 0101130+I_RR, 0101150+I_RR, 0101170+I_RR, + 0101210+I_RR, 0101230+I_RR, 0101250+I_RR, 0101270+I_RR, + 0101310+I_RR, 0101330+I_RR, 0101350+I_RR, 0101370+I_RR, + 0101400+I_RR, 0101420+I_RR, 0101440+I_RR, 0101460+I_RR, + 0101500+I_RR, 0101520+I_RR, 0101540+I_RR, 0101560+I_RR, + 0101600+I_RR, 0101620+I_RR, 0101640+I_RR, 0101660+I_RR, + 0101700+I_RR, 0101720+I_RR, 0101740+I_RR, 0101760+I_RR, + 0101410+I_RR, 0101430+I_RR, 0101450+I_RR, 0101470+I_RR, + 0101510+I_RR, 0101530+I_RR, 0101550+I_RR, 0101570+I_RR, + 0101610+I_RR, 0101630+I_RR, 0101650+I_RR, 0101670+I_RR, + 0101710+I_RR, 0101730+I_RR, 0101750+I_RR, 0101770+I_RR, + 0102000+I_RR, 0102020+I_RR, 0102040+I_RR, 0102060+I_RR, + 0102100+I_RR, 0102120+I_RR, 0102140+I_RR, 0102160+I_RR, + 0102200+I_RR, 0102220+I_RR, 0102240+I_RR, 0102260+I_RR, + 0102300+I_RR, 0102320+I_RR, 0102340+I_RR, 0102360+I_RR, + 0102010+I_RR, 0102030+I_RR, 0102050+I_RR, 0102070+I_RR, + 0102110+I_RR, 0102130+I_RR, 0102150+I_RR, 0102170+I_RR, + 0102210+I_RR, 0102230+I_RR, 0102250+I_RR, 0102270+I_RR, + 0102310+I_RR, 0102330+I_RR, 0102350+I_RR, 0102370+I_RR, + 0102400+I_RR, 0102420+I_RR, 0102440+I_RR, 0102460+I_RR, + 0102500+I_RR, 0102520+I_RR, 0102540+I_RR, 0102560+I_RR, + 0102600+I_RR, 0102620+I_RR, 0102640+I_RR, 0102660+I_RR, + 0102700+I_RR, 0102720+I_RR, 0102740+I_RR, 0102760+I_RR, + 0102410+I_RR, 0102430+I_RR, 0102450+I_RR, 0102470+I_RR, + 0102510+I_RR, 0102530+I_RR, 0102550+I_RR, 0102570+I_RR, + 0102610+I_RR, 0102630+I_RR, 0102650+I_RR, 0102670+I_RR, + 0102710+I_RR, 0102730+I_RR, 0102750+I_RR, 0102770+I_RR, + 0103000+I_RR, 0103020+I_RR, 0103040+I_RR, 0103060+I_RR, + 0103100+I_RR, 0103120+I_RR, 0103140+I_RR, 0103160+I_RR, + 0103200+I_RR, 0103220+I_RR, 0103240+I_RR, 0103260+I_RR, + 0103300+I_RR, 0103320+I_RR, 0103340+I_RR, 0103360+I_RR, + 0103010+I_RR, 0103030+I_RR, 0103050+I_RR, 0103070+I_RR, + 0103110+I_RR, 0103130+I_RR, 0103150+I_RR, 0103170+I_RR, + 0103210+I_RR, 0103230+I_RR, 0103250+I_RR, 0103270+I_RR, + 0103310+I_RR, 0103330+I_RR, 0103350+I_RR, 0103370+I_RR, + 0103400+I_RR, 0103420+I_RR, 0103440+I_RR, 0103460+I_RR, + 0103500+I_RR, 0103520+I_RR, 0103540+I_RR, 0103560+I_RR, + 0103600+I_RR, 0103620+I_RR, 0103640+I_RR, 0103660+I_RR, + 0103700+I_RR, 0103720+I_RR, 0103740+I_RR, 0103760+I_RR, + 0103410+I_RR, 0103430+I_RR, 0103450+I_RR, 0103470+I_RR, + 0103510+I_RR, 0103530+I_RR, 0103550+I_RR, 0103570+I_RR, + 0103610+I_RR, 0103630+I_RR, 0103650+I_RR, 0103670+I_RR, + 0103710+I_RR, 0103730+I_RR, 0103750+I_RR, 0103770+I_RR, + 0060177+I_NPN, 0060277+I_NPN, + 0060477+I_R, 0061477+I_R, 0062077+I_R, 0062677+I_NPN, 0063077+I_NPN, + 0073301+I_NPN, 0073101+I_NPN, 0077201+I_NPN, 0077001+I_NPN, + 0061401+I_R, 0061601+I_R, 0062401+I_NPN, 0062601+I_NPN, + 0061001+I_R, 0060001+I_R, 0061201+I_R, 0060201+I_R, + 0060401+I_BY, 0062001+I_BY, + 0060000+I_D, 0060100+I_D, 0060200+I_D, 0060300+I_D, + 0060400+I_RD, 0060500+I_RD, 0060600+I_RD, 0060700+I_RD, + 0061000+I_RD, 0061100+I_RD, 0061200+I_RD, 0061300+I_RD, + 0061400+I_RD, 0061500+I_RD, 0061600+I_RD, 0061700+I_RD, + 0062000+I_RD, 0062100+I_RD, 0062200+I_RD, 0062300+I_RD, + 0062400+I_RD, 0062500+I_RD, 0062600+I_RD, 0062700+I_RD, + 0063000+I_RD, 0063100+I_RD, 0063200+I_RD, 0063300+I_RD, + 0063400+I_D, 0063500+I_D, 0063600+I_D, 0063700+I_D, + -1 }; + +static const char *skip[] = { + "SKP", "SZC", "SNC", "SZR", "SNR", "SEZ", "SBN", + NULL }; + +static const char *device[] = { + "TTI", "TTO", "PTR", "PTP", "RTC", "PLT", "CDR", "LPT", + "DSK", "MTA", "DCM", "ADCV", "DKP", "CAS", "CPU", + NULL }; + +static const int32 dev_val[] = { + 010, 011, 012, 013, 014, 015, 016, 017, + 020, 022, 024, 030, 033, 034, 077, + -1 }; + +/* Address decode + + Inputs: + *of = output stream + addr = current PC + inst = instruction to decode + cflag = true if decoding for CPU + Outputs: + return = error code +*/ + +t_stat fprint_addr (FILE *of, t_addr addr, int32 inst, int32 cflag) +{ +int32 disp; + +if (inst & I_IND) fprintf (of, "@"); /* indirect? */ +disp = I_GETDISP (inst); /* displacement */ +switch (I_GETMODE (inst)) { /* mode */ +case 0: /* page zero */ + fprintf (of, "%-o", disp); + break; +case 1: /* PC rel */ + if (disp & DISPSIGN) { + if (cflag) fprintf (of, "%-o", (addr + 0177400 + disp) & AMASK); + else fprintf (of, ".-%-o", 0400 - disp); } + else { if (cflag) fprintf (of, "%-o", (addr + disp) & AMASK); + else fprintf (of, ".+%-o", disp); } + break; +case 2: /* AC2 rel */ + if (disp & DISPSIGN) fprintf (of, "-%-o,2", 0400 - disp); + else fprintf (of, "%-o,2", disp); + break; +case 3: /* AC3 rel */ + if (disp & DISPSIGN) fprintf (of, "-%-o,3", 0400 - disp); + else fprintf (of, "%-o,3", disp); + break; } /* end switch */ +return SCPE_OK; +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, c1, c2, inst, dv, src, dst, skp, dev, byac; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +c1 = (val[0] >> 8) & 0177; +c2 = val[0] & 0177; +if (sw & SWMASK ('A')) { /* ASCII? */ + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* mnemonic? */ + +/* Instruction decode */ + +inst = val[0]; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */ + src = I_GETSRC (inst); /* opr fields */ + dst = I_GETDST (inst); + skp = I_GETSKP (inst); + dev = I_GETDEV (inst); /* IOT fields */ + for (dv = 0; (dev_val[dv] >= 0) && (dev_val[dv] != dev); dv++) ; + + switch (j) { /* switch on class */ + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + case I_V_R: /* reg only */ + fprintf (of, "%s %-o", opcode[i], dst); + break; + case I_V_D: /* dev only */ + if (dev_val[dv] >= 0) + fprintf (of, "%s %s", opcode[i], device[dv]); + else fprintf (of, "%s %-o", opcode[i], dev); + break; + case I_V_RD: /* reg, dev */ + if (dev_val[dv] >= 0) + fprintf (of, "%s %-o,%s", opcode[i], dst, device[dv]); + else fprintf (of, "%s %-o,%-o", opcode[i], dst, dev); + break; + case I_V_M: /* addr only */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, addr, inst, cflag); + break; + case I_V_RM: /* reg, addr */ + fprintf (of, "%s %-o,", opcode[i], dst); + fprint_addr (of, addr, inst, cflag); + break; + case I_V_RR: /* operate */ + fprintf (of, "%s %-o,%-o", opcode[i], src, dst); + if (skp) fprintf (of, ",%s", skip[skp-1]); + break; + case I_V_BY: /* byte */ + byac = I_GETPULSE (inst); /* src = pulse */ + fprintf (of, "%s %-o,%-o", opcode[i], byac, dst); + break; } /* end case */ + return SCPE_OK; } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Address parse + + Inputs: + *cptr = pointer to input string + addr = current PC + cflag = true if parsing for CPU + *val = pointer to output value + Outputs: + optr = pointer to next char in input string + NULL if error +*/ + +#define A_FL 001 /* CPU flag */ +#define A_NX 002 /* index seen */ +#define A_PER 004 /* period seen */ +#define A_NUM 010 /* number seen */ +#define A_SI 020 /* sign seen */ +#define A_MI 040 /* - seen */ + +char *get_addr (char *cptr, t_addr addr, int32 cflag, int32 *val) +{ +int32 d, r, x, pflag; +t_addr sd; +char gbuf[CBUFSIZE]; + +*val = 0; /* clear result */ +d = 0; /* default no num */ +x = 1; /* default PC rel */ + +pflag = cflag & A_FL; /* isolate flag */ +if (*cptr == '@') { /* indirect? */ + *val = I_IND; + cptr++; } +if (*cptr == '.') { /* relative? */ + pflag = pflag | A_PER; + cptr++; } +if (*cptr == '+') { /* + sign? */ + pflag = pflag | A_SI; + cptr++; } +else if (*cptr == '-') { /* - sign? */ + pflag = pflag | A_MI | A_SI; + cptr++; } +if (*cptr != 0) { /* number? */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + d = get_uint (gbuf, 8, AMASK, &r); + if (r != SCPE_OK) return NULL; + pflag = pflag | A_NUM; + sd = (pflag & A_MI)? -d: d; } +if (*cptr != 0) { /* index? */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + x = get_uint (gbuf, 8, I_M_DST, &r); + if ((r != SCPE_OK) || (x < 2)) return NULL; + pflag = pflag | A_NX; } + +/* Address parse, continued */ + +switch (pflag & ~A_MI) { /* case on flags */ +case A_NUM: case A_NUM+A_SI: /* ~CPU, (+/-) num */ + if (sd <= I_M_DISP) *val = *val + sd; + else return NULL; + break; +case A_NUM+A_FL: case A_NUM+A_SI+A_FL: /* CPU, (+/-) num */ + if (sd <= I_M_DISP) *val = *val + sd; + else if (((sd >= ((addr - 0200) & AMASK)) && + (sd <= ((addr + 0177) & AMASK))) || + (sd >= (addr + 077600))) + *val = *val + 0400 + ((sd - addr) & I_M_DISP); + else return NULL; + break; +case A_PER: case A_PER+A_FL: /* .+/- num */ +case A_PER+A_SI+A_NUM: case A_PER+A_SI+A_NUM+A_FL: +case A_NX+A_NUM: case A_NX+A_NUM+A_FL: /* (+/-) num, ndx */ +case A_NX+A_SI+A_NUM: case A_NX+A_SI+A_NUM+A_FL: + if (((pflag & A_MI) == 0) && (d <= 0177)) *val = *val + (x << 8) + d; + else if ((pflag & A_MI) && (d <= 0200)) + *val = *val + (x << 8) + 0400 - d; + else return NULL; + break; +default: + return NULL; } /* end case */ +return cptr; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((t_value) cptr[0] << 8) + (t_value) cptr[1]; + return SCPE_OK; } + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0177777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ +case I_V_NPN: /* no operand */ + break; +case I_V_R: /* IOT reg */ + cptr = get_glyph (cptr, gbuf, 0); /* get register */ + d = get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + break; +case I_V_RD: /* IOT reg,dev */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ +case I_V_D: /* IOT dev */ + cptr = get_glyph (cptr, gbuf, 0); /* get device */ + for (i = 0; (device[i] != NULL) && (strcmp (device[i], gbuf) != 0); + i++); + if (device[i] != NULL) val[0] = val[0] | dev_val[i]; + else { d = get_uint (gbuf, 8, I_M_DEV, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DEV); } + break; +case I_V_RM: /* mem reg,addr */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ +case I_V_M: + if ((cptr = get_addr (cptr, addr, cflag, &d)) == NULL) return SCPE_ARG; + val[0] = val[0] | d; + break; +case I_V_RR: /* operate */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = get_uint (gbuf, 8, I_M_SRC, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_SRC); /* put in place */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + if (*cptr != 0) { /* skip? */ + cptr = get_glyph (cptr, gbuf, 0); /* get skip */ + for (i = 0; (skip[i] != NULL) && + (strcmp (skip[i], gbuf) != 0); i++) ; + if (skip[i] == NULL) return SCPE_ARG; + val[0] = val[0] | (i + 1); } /* end for */ + break; +case I_V_BY: /* byte */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = get_uint (gbuf, 8, I_M_PULSE, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_PULSE); /* put in place */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + break; } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* any leftovers? */ +return SCPE_OK; +} diff --git a/nova_tt.c b/nova_tt.c new file mode 100644 index 00000000..5c16dfa1 --- /dev/null +++ b/nova_tt.c @@ -0,0 +1,208 @@ +/* nova_tt.c: NOVA console terminal simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti terminal input + tto terminal output +*/ + +#include "nova_defs.h" + +#define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */ +#define UNIT_DASHER (1 << UNIT_V_DASHER) +extern int32 int_req, dev_busy, dev_done, dev_disable; +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat ttx_setmod (UNIT *uptr, int32 value); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list + ttx_mod TTI/TTO modifiers list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_TTI) }, + { FLDATA (DONE, dev_done, INT_V_TTI) }, + { FLDATA (DISABLE, dev_disable, INT_V_TTI) }, + { FLDATA (INT, int_req, INT_V_TTI) }, + { DRDATA (POS, tti_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (MODE, tti_unit.flags, UNIT_V_DASHER), REG_HRO }, + { NULL } }; + +MTAB ttx_mod[] = { + { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx_setmod }, + { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx_setmod }, + { 0 } }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, ttx_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_TTO) }, + { FLDATA (DONE, dev_done, INT_V_TTO) }, + { FLDATA (DISABLE, dev_disable, INT_V_TTO) }, + { FLDATA (INT, int_req, INT_V_TTO) }, + { DRDATA (POS, tto_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { FLDATA (MODE, tto_unit.flags, UNIT_V_DASHER), REG_HRO }, + { NULL } }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, ttx_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL }; + +/* Terminal input: IOT routine */ + +int32 tti (int32 pulse, int32 code, int32 AC) +{ +int32 iodata; + +iodata = (code == ioDIA)? tti_unit.buf & 0377: 0; +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_TTI; /* set busy */ + dev_done = dev_done & ~INT_TTI; /* clear done, int */ + int_req = int_req & ~INT_TTI; + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_TTI; /* clear busy */ + dev_done = dev_done & ~INT_TTI; /* clear done, int */ + int_req = int_req & ~INT_TTI; + break; } /* end switch */ +return iodata; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +tti_unit.buf = temp & 0177; +if ((tti_unit.flags & UNIT_DASHER) && (tti_unit.buf == '\r')) + tti_unit.buf = '\n'; /* Dasher: cr -> nl */ +dev_busy = dev_busy & ~INT_TTI; /* clear busy */ +dev_done = dev_done | INT_TTI; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +tti_unit.pos = tti_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +dev_busy = dev_busy & ~INT_TTI; /* clear busy */ +dev_done = dev_done & ~INT_TTI; /* clear done, int */ +int_req = int_req & ~INT_TTI; +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) tto_unit.buf = AC & 0377; +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + dev_busy = dev_busy | INT_TTO; /* set busy */ + dev_done = dev_done & ~INT_TTO; /* clear done, int */ + int_req = int_req & ~INT_TTO; + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_TTO; /* clear busy */ + dev_done = dev_done & ~INT_TTO; /* clear done, int */ + int_req = int_req & ~INT_TTO; + sim_cancel (&tto_unit); /* deactivate unit */ + break; } /* end switch */ +return 0; +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c, temp; + +dev_busy = dev_busy & ~INT_TTO; /* clear busy */ +dev_done = dev_done | INT_TTO; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +c = tto_unit.buf & 0177; +if ((tto_unit.flags & UNIT_DASHER) && (c == 031)) c = '\b'; +if ((temp = sim_putchar (c)) != SCPE_OK) return temp; +tto_unit.pos = tto_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +dev_busy = dev_busy & ~INT_TTO; /* clear busy */ +dev_done = dev_done & ~INT_TTO; /* clear done, int */ +int_req = int_req & ~INT_TTO; +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat ttx_setmod (UNIT *uptr, int32 value) +{ +tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | value; +tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | value; +return SCPE_OK; +} diff --git a/pdp11_cis.c b/pdp11_cis.c new file mode 100644 index 00000000..622fae8b --- /dev/null +++ b/pdp11_cis.c @@ -0,0 +1,1259 @@ +/* pdp11_cis.c: PDP-11 CIS optional instruction set simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module simulates the PDP-11 commercial instruction set (CIS). + + The commercial instruction set consists of three instruction formats: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register operands + | 0 1 1 1 1 1| 0 0 0 0| opcode | 076030:076057 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076070:076077 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ inline operands + | 0 1 1 1 1 1| 0 0 0 1| opcode | 076130:076157 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076170:076177 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ load descriptors + | 0 1 1 1 1 1| 0 0 0 0|op| 1 0| reg | 076020:076027 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076060:076067 + + The CIS instructions operate on character strings, packed (decimal) + strings, and numeric (decimal) strings. Strings are described by + a two word descriptor: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | length in bytes | char string + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor + | starting byte address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | |str type| | length | decimal string + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor + | starting byte address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + Decimal string types are: + + <14:12> data type bytes occupied by n digits + 0 signed zoned n + 1 unsigned zone n + 2 trailing overpunch n + 3 leading overpunch n + 4 trailing separate n+1 + 5 leading separate n+1 + 6 signed packed n/2 +1 + 7 unsigned packed n/2 +1 + + Zero length character strings occupy no memory; zero length decimal strings + require either zero bytes (zoned, overpunch) or one byte (separate, packed). + + CIS instructions can run for a very long time, so they are interruptible + and restartable. In the simulator, all instructions run to completion. + The code is unoptimized. +*/ + +#include "pdp11_defs.h" + +/* Opcode bits */ + +#define INLINE 0100 /* inline */ +#define PACKED 0020 /* packed */ +#define NUMERIC 0000 /* numeric */ + +/* Operand type definitions */ + +#define R0_DESC 1 /* descr in R0:R1 */ +#define R2_DESC 2 /* descr in R2:R3 */ +#define R4_DESC 3 /* descr in R4:R5 */ +#define R4_ARG 4 /* argument in R4 */ +#define IN_DESC 5 /* inline descriptor */ +#define IN_ARG 6 /* inline argument */ +#define IN_DESC_R0 7 /* inline descr to R0:R1 */ +#define MAXOPN 4 /* max # operands */ + +/* Decimal data type definitions */ + +#define XZ 0 /* signed zoned */ +#define UZ 1 /* unsigned zoned */ +#define TO 2 /* trailing overpunch */ +#define LO 3 /* leading overpunch */ +#define TS 4 /* trailing separate */ +#define LS 5 /* leading separate */ +#define XP 6 /* signed packed */ +#define UP 7 /* unsigned packed */ + +/* Decimal descriptor definitions */ + +#define DTYP_M 07 /* type mask */ +#define DTYP_V 12 /* type position */ +#define DLNT_M 037 /* length mask */ +#define DLNT_V 0 /* length position */ +#define GET_DTYP(x) (((x) >> DTYP_V) & DTYP_M) +#define GET_DLNT(x) (((x) >> DLNT_V) & DLNT_M) + +/* Shift operand definitions */ + +#define ASHRND_M 017 /* round digit mask */ +#define ASHRND_V 8 /* round digit pos */ +#define ASHLNT_M 0377 /* shift count mask */ +#define ASHLNT_V 0 /* shift length pos */ +#define ASHSGN 0200 /* shift sign */ +#define GET_ASHRND(x) (((x) >> ASHRND_V) & ASHRND_M) +#define GET_ASHLNT(x) (((x) >> ASHLNT_V) & ASHLNT_M) + +/* Operand array aliases */ + +#define A1LNT arg[0] +#define A1ADR arg[1] +#define A2LNT arg[2] +#define A2ADR arg[3] +#define A3LNT arg[4] +#define A3ADR arg[5] +#define A1 &arg[0] +#define A2 &arg[2] +#define A3 &arg[4] + +/* Condition code macros */ + +#define GET_BIT(ir,n) (((ir) >> n) & 1) +#define GET_SIGN_L(ir) GET_BIT((ir), 31) +#define GET_SIGN_W(ir) GET_BIT((ir), 15) +#define GET_SIGN_B(ir) GET_BIT((ir), 7) +#define GET_Z(ir) (ir == 0) + +/* Decimal string structure */ + +#define DSTRLNT 4 +#define DSTRMAX (DSTRLNT - 1) +#define MAXDVAL 429496730 /* 2^32 / 10 */ + +struct dstr { + unsigned int32 sign; + unsigned int32 val[DSTRLNT]; }; + +typedef struct dstr DSTR; + +static DSTR Dstr0 = { 0, 0, 0, 0, 0 }; + +extern int32 isenable, dsenable; +extern int32 N, Z, V, C; +extern int32 R[8], trap_req; +extern int32 ReadW (int32 addr); +extern void WriteW (int32 data, int32 addr); +extern int32 ReadB (int32 addr); +extern void WriteB (int32 data, int32 addr); +int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag); +void WriteDstr (int32 *dscr, DSTR *dec, int32 flag); +int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin); +void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); +int32 CmpDstr (DSTR *src1, DSTR *src2); +int32 TestDstr (DSTR *dsrc); +int32 LntDstr (DSTR *dsrc, int32 nz); +unsigned int32 NibbleLshift (DSTR *dsrc, int32 sc, unsigned int32 cin); +unsigned int32 NibbleRshift (DSTR *dsrc, int32 sc, unsigned int32 cin); +int32 WordLshift (DSTR *dsrc, int32 sc); +void WordRshift (DSTR *dsrc, int32 sc); +void CreateTable (DSTR *dsrc, DSTR mtable[10]); + +/* Table of instruction operands */ + +static int32 opntab[128][MAXOPN] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 007 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 010 - 017 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* LD2R */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + R0_DESC, R2_DESC, R4_ARG, 0, /* MOVC */ + R0_DESC, R2_DESC, R4_ARG, 0, /* MOVRC */ + R0_DESC, R2_DESC, R4_DESC, 0, /* MOVTC */ + 0, 0, 0, 0, /* 033 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 034 - 037 */ + 0, 0, 0, 0, 0, 0, 0, 0, + R4_ARG, 0, 0, 0, /* LOCC */ + R4_ARG, 0, 0, 0, /* SKPC */ + R4_DESC, 0, 0, 0, /* SCANC */ + R4_DESC, 0, 0, 0, /* SPANC */ + R0_DESC, R2_DESC, R4_ARG, 0, /* CMPC */ + R2_DESC, 0, 0, 0, /* MATC */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 046 - 047 */ + R0_DESC, R2_DESC, R4_DESC, 0, /* ADDN */ + R0_DESC, R2_DESC, R4_DESC, 0, /* SUBN */ + R0_DESC, R2_DESC, 0, 0, /* CMPN */ + R0_DESC, 0, 0, 0, /* CVTNL */ + R0_DESC, R2_DESC, 0, 0, /* CVTPN */ + R0_DESC, R2_DESC, 0, 0, /* CVTNP */ + R0_DESC, R2_DESC, R4_ARG, 0, /* ASHN */ + R0_DESC, 0, 0, 0, /* CVTLN */ + 0, 0, 0, 0, 0, 0, 0, 0, /* LD3R */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + R0_DESC, R2_DESC, R4_DESC, 0, /* ADDP */ + R0_DESC, R2_DESC, R4_DESC, 0, /* SUBP */ + R0_DESC, R2_DESC, 0, 0, /* CMPP */ + R0_DESC, 0, 0, 0, /* CVTPL */ + R0_DESC, R2_DESC, R4_DESC, 0, /* MULP */ + R0_DESC, R2_DESC, R4_DESC, 0, /* DIVP */ + R0_DESC, R2_DESC, R4_ARG, 0, /* ASHP */ + R0_DESC, 0, 0, 0, /* CVTLP */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 100 - 107 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 110 - 117 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 120 - 127 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + IN_DESC, IN_DESC, IN_ARG, 0, /* MOVCI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* MOVRCI */ + IN_DESC, IN_DESC, IN_ARG, IN_ARG, /* MOVTCI */ + 0, 0, 0, 0, /* 133 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 134 - 137 */ + 0, 0, 0, 0, 0, 0, 0, 0, + IN_DESC_R0, IN_ARG, 0, 0, /* LOCCI */ + IN_DESC_R0, IN_ARG, 0, 0, /* SKPCI */ + IN_DESC_R0, IN_DESC, 0, 0, /* SCANCI */ + IN_DESC_R0, IN_DESC, 0, 0, /* SPANCI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* CMPCI */ + IN_DESC_R0, IN_DESC, 0, 0, /* MATCI */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 146 - 147 */ + IN_DESC, IN_DESC, IN_DESC, 0, /* ADDNI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* SUBNI */ + IN_DESC, IN_DESC, 0, 0, /* CMPNI */ + IN_DESC, IN_ARG, 0, 0, /* CVTNLI */ + IN_DESC, IN_DESC, 0, 0, /* CVTPNI */ + IN_DESC, IN_DESC, 0, 0, /* CVTNPI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* ASHNI */ + IN_DESC, IN_DESC, 0, 0, /* CVTLNI */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 167 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + IN_DESC, IN_DESC, IN_DESC, 0, /* ADDPI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* SUBPI */ + IN_DESC, IN_DESC, 0, 0, /* CMPPI */ + IN_DESC, 0, 0, 0, /* CVTPLI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* MULPI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* DIVPI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* ASHPI */ + IN_DESC, IN_DESC, 0, 0 /* CVTLPI */ +}; + +/* ASCII to overpunch table: sign is <7>, digit is <4:0> */ + +static int32 overbin[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 037 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x80, 0, 0, 0, 0, 0, 0, /* 040 - 077 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0x80, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, /* 100 - 137 */ + 8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 140 - 177 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x80, 0, 0 +}; + +/* Overpunch to ASCII table: indexed by sign and digit */ + +static int32 binover[2][16] = { + '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + '0', '0', '0', '0', '0', '0', + '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + '0', '0', '0', '0', '0', '0' +}; + +static unsigned char movbuf[65536]; + +/* CIS emulator */ + +void cis11 (int32 IR) +{ +int32 c, i, j, k, t, op, rn, addr; +int32 fill, mask, match, limit, mvlnt, shift; +int32 spc, ldivd, ldivr; +int32 arg[6]; /* operands */ +unsigned int32 nc, digit, result; +static DSTR accum, src1, src2, dst; +static DSTR mptable[10]; +static DSTR Dstr1 = { 0, 0x10, 0, 0, 0 }; + +op = IR & 0177; /* IR <6:0> */ +for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) { /* parse operands */ + switch (opntab[op][i]) { /* case on op type */ + case R0_DESC: + arg[j++] = R[0]; + arg[j++] = R[1]; + break; + case R2_DESC: + arg[j++] = R[2]; + arg[j++] = R[3]; + break; + case R4_DESC: + arg[j++] = R[4]; + arg[j++] = R[5]; + break; + case R4_ARG: + arg[j++] = R[4]; + break; + case IN_DESC: + addr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + arg[j++] = ReadW (addr | dsenable); + arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable); + break; + case IN_DESC_R0: + addr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + R[0] = ReadW (addr | dsenable); + R[1] = ReadW (((addr + 2) & 0177777) | dsenable); + break; + case IN_ARG: + arg[j++] = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + break; } /* end case */ + } /* end for */ +switch (op) { /* case on opcode */ +default: + setTRAP (TRAP_ILL); + return; + +/* MOVC, MOVTC, MOVCI, MOVTCI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = dest string descriptor + A3LNT<7:0> = fill character + A3ADR = translation table address (MOVTC, MOVTCI only) + + Condition codes: + NZVC = set from src.lnt - dst.lnt + + Registers (MOVC, MOVTC only) + R0 = max (0, src.len - dst.len) + R1:R3 = 0 + R4:R5 = unchanged + + Notes: + - To avoid overlap problems, the entire source string is + buffered in movbuf. On a modern microprocessor, for most + string sizes, this will be handled in the on chip cache. + - If either the source or destination lengths are zero, + the move loops exit immediately. + - If the source length does not exceed the destination + length, the fill loop exits immediately. +*/ + +case 030: case 032: case 0130: case 0132: + mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; /* calc move lnt */ + for (i = 0; i < mvlnt; i++) { + movbuf[i] = ReadB (((A1ADR + i) & 0177777) | dsenable); } + for (i = 0; i < mvlnt; i++) { + t = movbuf[i]; + if (op & 2) t = ReadB (((A3ADR + t) & 0177777) | dsenable); + WriteB (t, ((A2ADR + i) & 0177777) | dsenable); } + fill = A3LNT & 0377; /* do fill, if any */ + for (i = mvlnt; i < A2LNT; i++) { + WriteB (fill, ((A2ADR + i) & 0177777) | dsenable); } + t = A1LNT - A2LNT; /* src.lnt - dst.lnt */ + N = GET_SIGN_W (t); /* set cc's from diff */ + Z = GET_Z (t); + V = GET_SIGN_W ((A1LNT ^ A2LNT) & (~A2LNT ^ t)); + C = (A1LNT < A2LNT); + if ((op & INLINE) == 0) { /* if reg, set reg */ + R[0] = C? 0: t & 0177777; + R[1] = R[2] = R[3] = 0; + R[4] = R[4] & 0377; } + return; + +/* MOVRC, MOVRCI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = dest string descriptor + A3LNT<7:0> = fill character + + Condition codes: + NZVC = set from src.lnt - dst.lnt + + Registers (MOVC, MOVTC only) + R0 = max (0, src.len - dst.len) + R1:R3 = 0 + R4:R5 = unchanged + + Notes: see MOVC, MOVCI +*/ + +case 031: case 0131: + mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; /* calc move lnt */ + addr = A1ADR + A1LNT - mvlnt; + for (i = 0; i < mvlnt; i++) { + movbuf[i] = ReadB (((addr + i) & 0177777) | dsenable); } + addr = A2ADR + A2LNT - mvlnt; + for (i = 0; i < mvlnt; i++) { + WriteB (movbuf[i], ((addr + i) & 0177777) | dsenable); } + fill = A3LNT & 0377; /* do fill, if any */ + for (i = mvlnt, j = 0; i < A2LNT; i++, j++) { + WriteB (fill, ((A2ADR + j) & 0177777) | dsenable); } + t = A1LNT - A2LNT; /* src.lnt - dst.lnt */ + N = GET_SIGN_W (t); /* set cc's from diff */ + Z = GET_Z (t); + V = GET_SIGN_W ((A1LNT ^ A2LNT) & (~A2LNT ^ t)); + C = (A1LNT < A2LNT); + if ((op & INLINE) == 0) { /* if reg, set reg */ + R[0] = C? 0: t & 0177777; + R[1] = R[2] = R[3] = 0; + R[4] = R[4] & 0377; } + return; + +/* Load descriptors - no operands */ + +case 020: case 021: case 022: case 023: +case 024: case 025: case 026: case 027: +case 060: case 061: case 062: case 063: +case 064: case 065: case 066: case 067: + limit = (op & 040)? 6: 4; + rn = IR & 07; /* get register */ + t = R[rn]; + spc = (rn == 7)? isenable: dsenable; + for (j = 0; j < limit; j = j + 2) { /* loop for 2,3 dscr */ + addr = ReadW (((t + j) & 0177777) | spc); + R[j] = ReadW (addr | dsenable); + R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable); } + if (rn >= limit) R[rn] = (R[rn] + limit) & 0177777; + return; + +/* LOCC, SKPC, LOCCI, SKPCI + + Operands: + R0, R1 = source string descriptor + A1LNT<7:0> = match character + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0:R1 = substring descriptor where operation terminated +*/ + +case 040: case 041: case 0140: case 0141: + match = A1LNT & 0377; /* match character */ + for ( ; R[0] != 0; R[0]--) { /* loop */ + c = ReadB (R[1] | dsenable); /* get char */ + if ((c == match) ^ (op & 1)) break; /* = + LOC, != + SKP? */ + R[1] = (R[1] + 1) & 0177777; } + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = C = 0; + if ((op & INLINE) == 0) R[4] = R[4] & 0377; /* if reg, set reg */ + return; + +/* SCANC, SPANC, SCANCI, SPANCI + + Operands: + R0, R1 = source string descriptor + A1LNT<7:0> = mask + A1ADR = table address + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0:R1 = substring descriptor where operation terminated +*/ + +case 042: case 043: case 0142: case 0143: + mask = A1LNT & 0377; /* mask character */ + for (; R[0] != 0; R[0]--) { /* loop */ + t = ReadB (R[1] | dsenable); /* get char as index */ + c = ReadB (((A1ADR + t) & 0177777) | dsenable); + if (((c & mask) != 0) ^ (op & 1)) break; /* != + SCN, = + SPN? */ + R[1] = (R[1] + 1) & 0177777; } + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = C = 0; + if ((op & INLINE) == 0) R[4] = R[4] & 0377; /* if reg, set reg */ + return; + +/* CMPC, CMPCI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + A3LNT<7:0> = fill character + + Condition codes: + NZVC = set from src1 - src2 at mismatch, or + = 0100 if equal + + Registers (CMPC only): + R0:R1 = unmatched source1 substring descriptor + R2:R3 = unmatched source2 substring descriptor +*/ + +case 044: case 0144: + c = t = 0; + for (i = 0; i < ((A1LNT > A2LNT)? A1LNT: A2LNT); i++) { + if (i < A1LNT) c = ReadB (((A1ADR + i) & 0177777) | dsenable); + else c = A3LNT & 0377; + if (i < A2LNT) t = ReadB (((A2ADR + i) & 0177777) | dsenable); + else t = A3LNT & 0377; + if (c != t) break; } + j = c - t; /* last chars read */ + N = GET_SIGN_B (j); /* set cc's */ + Z = GET_Z (j); + V = GET_SIGN_B ((c ^ t) & (~t ^ j)); + C = (c < t); + if ((op & INLINE) == 0) { /* if reg, set reg */ + j = (i > A1LNT)? A1LNT: i; /* #src1 chars used */ + k = (i > A2LNT)? A2LNT: i; /* #src2 chars used */ + R[0] = A1LNT - j; + R[1] = (A1ADR + j) & 0177777; + R[2] = A2LNT - k; + R[3] = (A2ADR + k) & 0177777; + R[4] = R[4] & 0377; } + return; + +/* MATC, MATCI + + Operands: + R0, R1 = source string descriptor + A1LNT, A1ADR = substring descriptor + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0:R1 = source substring descriptor for match + + Notes: + - If the string is zero length, and the substring is not, + the outer loop exits immediately, and the result is + "no match" + - If the substring is zero length, the inner loop always + exits immediately, and the result is a "match" + - If the string is zero length, and the substring is as + well, the outer loop executes, the inner loop exits + immediately, and the result is a match, but the result + is the length of the string (zero), or "no match" +*/ + +case 0045: case 0145: + for (match = 0; R[0] >= A1LNT; R[0]--) { /* loop thru string */ + for (i = 0, match = 1; match && (i < A1LNT); i++) { + c = ReadB (((R[1] + i) & 0177777) | dsenable); + t = ReadB (((A1ADR + i) & 0177777) | dsenable); + match = (c == t); } /* end for substring */ + if (match) break; /* exit if match */ + R[1] = (R[1] + 1) & 0177777; } /* end for string */ + if (!match) { /* if no match */ + R[1] = (R[1] + R[0]) & 0177777; + R[0] = 0; } + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = C = 0; + return; + +/* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + A3LNT, A3ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (ADDN, ADDP, SUBN, SUBP only): + R0:R3 = 0 +*/ + +case 050: case 051: case 070: case 071: +case 0150: case 0151: case 0170: case 0171: + ReadDstr (A1, &src1, op); /* get source1 */ + ReadDstr (A2, &src2, op); /* get source2 */ + if (op & 1) src1.sign = src1.sign ^ 1; /* sub? invert sign */ + if (src1.sign ^ src2.sign) { /* opp signs? sub */ + if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ + SubDstr (&src1, &src2, &dst); /* src2 - src1 */ + dst.sign = src2.sign; } /* sign = src2 */ + else { SubDstr (&src2, &src1, &dst); /* src1 - src2 */ + dst.sign = src1.sign; } /* sign = src1 */ + V = 0; } /* can't carry */ + else { /* addition */ + V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ + dst.sign = src1.sign; } /* set result sign */ + C = 0; + WriteDstr (A3, &dst, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return; + +/* MULP, MULPI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + A3LNT, A3ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (MULP only): + R0:R3 = 0 +*/ + +case 074: case 0174: + dst = Dstr0; /* clear result */ + if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) { + dst.sign = src1.sign ^ src2.sign; /* sign of result */ + accum = Dstr0; /* clear accum */ + NibbleRshift (&src1, 1, 0); /* shift out sign */ + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ + digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (digit > 0) /* add in digit*mpcnd */ + AddDstr (&mptable[digit], &accum, &accum, 0); + nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ + NibbleRshift (&dst, 1, nc); } /* result right 4 */ + V = TestDstr (&accum) != 0; } /* if ovflo, set V */ + else V = 0; /* result = 0 */ + C = 0; /* C = 0 */ + WriteDstr (A3, &dst, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return; + +/* DIVP, DIVPI + + Operands: + A1LNT, A1ADR = divisor string descriptor + A2LNT, A2ADR = dividend string descriptor + A3LNT, A3ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = set if divide by zero + + Registers (DIVP only): + R0:R3 = 0 +*/ + +case 075: case 0175: + ldivr = ReadDstr (A1, &src1, op); /* get divisor */ + if (ldivr == 0) { /* divisor = 0? */ + V = C = 1; /* set cc's */ + return; } + ldivr = LntDstr (&src1, ldivr); /* get exact length */ + ldivd = ReadDstr (A2, &src2, op); /* get dividend */ + ldivd = LntDstr (&src2, ldivd); /* get exact length */ + dst = Dstr0; /* clear dest */ + NibbleRshift (&src1, 1, 0); /* right justify ops */ + NibbleRshift (&src2, 1, 0); + if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ + WordLshift (&src1, t / 8); /* align divr to divd */ + NibbleLshift (&src1, t % 8, 0); + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 0; i <= t; i++) { /* divide loop */ + for (digit = 9; digit > 0; digit--) { /* find digit */ + if (CmpDstr (&src2, &mptable[digit]) >= 0) { + SubDstr (&mptable[digit], &src2, &src2); + dst.val[0] = dst.val[0] | digit; + break; } } /* end if, for */ + NibbleLshift (&src2, 1, 0); /* shift dividend */ + NibbleLshift (&dst, 1, 0); /* shift quotient */ + } /* end divide loop */ + dst.sign = src1.sign ^ src2.sign; /* calculate sign */ + } /* end if */ + V = C = 0; + WriteDstr (A3, &dst, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return; + +/* CMPN, CMPP, CMPNI, CMPPI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + + Condition codes: + NZ = set from comparison + VC = 0 + + Registers (CMPN, CMPP only): + R0:R3 = 0 +*/ + +case 052: case 072: case 0152: case 0172: + ReadDstr (A1, &src1, op); /* get source1 */ + ReadDstr (A2, &src2, op); /* get source2 */ + N = Z = V = C = 0; + if (src1.sign != src2.sign) N = src1.sign; + else { + t = CmpDstr (&src1, &src2); /* compare strings */ + if (t < 0) N = 1; + else if (t == 0) Z = 1; } + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return; + +/* ASHN, ASHP, ASHNI, ASHPI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = destination string descriptor + A3LNT<11:8> = rounding digit + A3LNT<7:0> = shift count + + Condition codes: + NZV = set from result + C = 0 + + Registers (ASHN, ASHP only): + R0:R1, R4 = 0 +*/ + +case 056: case 076: case 0156: case 0176: + ReadDstr (A1, &src1, op); /* get source */ + V = C = 0; /* init cc's */ + shift = GET_ASHLNT (A3LNT); /* get shift count */ + if (shift & ASHSGN) { /* right shift? */ + shift = (ASHLNT_M + 1 - shift); /* !shift! */ + WordRshift (&src1, shift / 8); /* do word shifts */ + NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ + t = GET_ASHRND (A3LNT); /* get rounding digit */ + if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ + AddDstr (&src1, &Dstr1, &src1, 0); /* round */ + src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ + } /* end right shift */ + else if (shift) { /* left shift? */ + if (WordLshift (&src1, shift / 8)) V = 1; /* do word shifts */ + if (NibbleLshift (&src1, shift % 8, 0)) V = 1; + } /* end left shift */ + WriteDstr (A2, &src1, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[4] = 0; + return; + +/* CVTPN, CVTPNI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (CVTPN only): + R0:R1 = 0 +*/ + +case 054: case 0154: + ReadDstr (A1, &src1, PACKED); /* get source */ + V = C = 0; /* init cc's */ + WriteDstr (A2, &src1, NUMERIC); /* write dest */ + if ((op & INLINE) == 0) R[0] = R[1] = 0; /* if reg, clr reg */ + return; + +/* CVTNP, CVTNPI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (CVTNP only): + R0:R1 = 0 +*/ + +case 055: case 0155: + ReadDstr (A1, &src1, NUMERIC); /* get source */ + V = C = 0; /* init cc's */ + WriteDstr (A2, &src1, PACKED); /* write dest */ + if ((op & INLINE) == 0) R[0] = R[1] = 0; /* if reg, clr reg */ + return; + +/* CVTNL, CVTPL, CVTNLI, CVTPLI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT = destination address (inline only) + + Condition codes: + NZV = set from result + C = source < 0 and result != 0 + + Registers (CVTNL, CVTPL only): + R0:R1 = 0 + R2:R3 = result +*/ + +case 053: case 073: case 0153: case 0173: + ReadDstr (A1, &src1, op); /* get source */ + V = result = 0; /* clear V, result */ + for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ + digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (digit || result || V) { /* skip initial 0's */ + if (result >= MAXDVAL) V = 1; + result = (result * 10) + digit; + if (result < digit) V = 1; } /* end if */ + } /* end for */ + if (src1.sign) result = (~result + 1) & 0xFFFFFFFF; + N = GET_SIGN_L (result); + Z = GET_Z (result); + V = V | (N ^ src1.sign); /* overflow if +2**31 */ + C = src1.sign && (Z == 0); /* set C based on std */ + if (op & INLINE) { /* inline? */ + WriteW (result & 0177777, A2LNT | dsenable); + WriteW ((result >> 16) & 0177777, + ((A2LNT + 2) & 0177777) | dsenable); } + else { R[0] = R[1] = 0; + R[2] = (result >> 16) & 0177777; + R[3] = result & 0177777; } + return; + +/* CVTLN, CVTLP, CVTLNI, CVTLPI + + Operands: + A1LNT, A1ADR = destination string descriptor + A2LNT, A2ADR = source long (CVTLNI, CVTLPI) - VAX format + R2:R3 = source long (CVTLN, CVTLP) - EIS format + + Condition codes: + NZV = set from result + C = 0 + + Registers (CVTLN, CVTLP only) + R2:R3 = 0 +*/ + +case 057: case 077: + result = (R[2] << 16) | R[3]; /* op in EIS format */ + R[2] = R[3] = 0; /* clear registers */ + goto CVTLx; /* join common code */ +case 0157: case 0177: + result = (A2ADR << 16) | A2LNT; /* op in VAX format */ +CVTLx: + dst = Dstr0; /* clear result */ + if (dst.sign = GET_SIGN_L (result)) result = (~result + 1) & 0xFFFFFFFF; + for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { + digit = result % 10; + result = result / 10; + dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4)); } + V = C = 0; + WriteDstr (A1, &dst, op); /* write result */ + return; } +return; +} /* end cis */ + +/* Get decimal string + + Arguments: + dscr = decimal string descriptor + src = decimal string structure + flag = numeric/packed flag + + The routine returns the length in int32's of the non-zero part of + the string. + + This routine plays fast and loose with operand checking, as did the + original 11/23 microcode (half of which I wrote). In particular, + + - If the flag specifies packed, the type is not checked at all. + The sign of an unsigned string is assumed to be 0xF (an + alternative for +). + - If the flag specifies numeric, packed types will be treated + as unsigned zoned. + - For separate, only the '-' sign is checked, not the '+'. + + However, to simplify the code elsewhere, digits are range checked, + and bad digits are replaced with 0's. +*/ + +int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag) +{ +int32 c, i, end, lnt, type, t; + +*src = Dstr0; /* clear result */ +type = GET_DTYP (dscr[0]); /* get type */ +lnt = GET_DLNT (dscr[0]); /* get string length */ +if (flag & PACKED) { /* packed? */ + end = lnt / 2; /* last byte */ + for (i = 0; i <= end; i++) { /* loop thru string */ + c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable); + if (i == 0) t = c & 0xF; /* save sign */ + if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF; + if (c >= 0xA0) c = c & 0xF; /* check hi digit */ + if ((c & 0xF) >= 0xA) c = c & 0xF0; /* check lo digit */ + src -> val[i / 4] = src -> val[i / 4] | (c << ((i % 4) * 8)); + } /* end for */ + if ((t == 0xB) || (t == 0xD)) src -> sign = 1; /* if -, set sign */ + src -> val[0] = src -> val[0] & ~0xF; /* clear sign */ + } /* end packed */ +else { /* numeric */ + if (type >= TS) src -> sign = (ReadB ((((type == TS)? + dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-'); + for (i = 1; i <= lnt; i++) { /* loop thru string */ + c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable); + if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70)) + src -> sign = 1; /* signed zoned */ + else if (((i == 1) && (type == TO)) || + ((i == lnt) && (type == LO))) { + c = overbin[c & 0177]; /* get sign and digit */ + src -> sign = c >> 7; } /* set sign */ + c = c & 0xF; /* get digit */ + if (c > 9) c = 0; /* range check */ + src -> val[i / 8] = src -> val[i / 8] | (c << ((i % 8) * 4)); + } /* end for */ + } /* end numeric */ +return TestDstr (src); /* clean -0 */ +} + +/* Store decimal string + + Arguments: + dsrc = decimal string descriptor + src = decimal string structure + flag = numeric/packed flag + PSW.NZ are also set to their proper values + PSW.V will be set on overflow; it must be initialized elsewhere + (to allow for external overflow calculations) + + The rules for the stored sign and the PSW sign are: + - Stored sign is negative if input is negative, string type + is signed, and the result is non-zero or there was overflow + - PSW sign is negative if input is negative, string type is + signed, and the result is non-zero + Thus, the stored sign and the PSW sign will differ in one case: + a negative zero generated by overflow is stored with a negative + sign, but PSW.N is clear +*/ + +void WriteDstr (int32 *dscr, DSTR *dst, int32 flag) +{ +int32 c, i, mask, limit, end, type, lnt; +static int32 masktab[8] = { + 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, + 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 }; +static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 }; + +type = GET_DTYP (dscr[0]); /* get type */ +lnt = GET_DLNT (dscr[0]); /* get string length */ +mask = 0; /* can't ovflo */ +Z = 1; /* assume all 0's */ +limit = lnt / 8; /* limit for test */ +for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ + if (i == limit) mask = masktab[lnt % 8]; /* at limit, get mask */ + else if (i > limit) mask = 0xFFFFFFFF; /* beyond, all ovflo */ + if (dst -> val[i] & mask) V = 1; /* test for ovflo */ + if (dst -> val[i] = dst -> val[i] & ~mask) Z = 0; } /* test nz */ +dst -> sign = dst -> sign & ~unsignedtab[type] & ~(Z & ~V); +N = dst -> sign & ~Z; /* N = sign, if ~zero */ + +if (flag & PACKED) { /* packed? */ + end = lnt / 2; /* end of string */ + if (type == UP) dst -> val[0] = dst -> val[0] | 0xF; + else dst -> val[0] = dst -> val[0] | 0xC | dst -> sign; + for (i = 0; i <= end; i++) { /* store string */ + c = (dst -> val[i / 4] >> ((i % 4) * 8)) & 0xFF; + WriteB (c, ((dscr[1] + end - i) & 0177777)); + } /* end for */ + } /* end packed */ +else { + if (type >= TS) WriteB (dst -> sign? '-': '+', (((type == TS)? + dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable); + for (i = 1; i <= lnt; i++) { /* store string */ + c = (dst -> val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ + if ((i == 1) && (type == XZ) && dst -> sign) + c = c | 0x70; /* signed zoned */ + else if (((i == 1) && (type == TO)) || + ((i == lnt) && (type == LO))) + c = binover[dst -> sign][c]; /* get sign and digit */ + else c = c | 0x30; /* default */ + WriteB (c, ((dscr[1] + lnt - i) & 0177777)); + } /* end for */ + } /* end numeric */ +return; +} + +/* Add decimal string magnitudes + + Arguments: + s1 = source1 decimal string + s2 = source2 decimal string + ds = destination decimal string + cy = carry in + Output = 1 if carry, 0 if no carry + + This algorithm courtesy Anton Chernoff, circa 1992 or even earlier + + We trace the history of a pair of adjacent digits to see how the + carry is fixed; each parenthesized item is a 4b digit. + + Assume we are adding: + (a)(b) I + + (x)(y) J + + First compute I^J: + (a^x)(b^y) TMP + + Note that the low bit of each digit is the same as the low bit of + the sum of the digits, ignoring the cary, since the low bit of the + sum is the xor of the bits. + + Now compute I+J+66 to get decimal addition with carry forced left + one digit: + (a+x+6+carry mod 16)(b+y+6 mod 16) SUM + + Note that if there was a carry from b+y+6, then the low bit of the + left digit is different from the expected low bit from the xor. + If we xor this SUM into TMP, then the low bit of each digit is 1 + if there was a carry, and 0 if not. We need to subtract 6 from each + digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift + it right 4 to the digits that are affected, and subtract 6*adjustment + (actually, shift it right 3 and subtract 3*adjustment). +*/ + +int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy) +{ +int32 i; +unsigned int32 sm1, sm2, tm1, tm2, tm3, tm4; + +for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ + tm1 = s1 -> val[i] ^ (s2 -> val[i] + cy); /* xor operands */ + sm1 = s1 -> val[i] + (s2 -> val[i] + cy); /* sum operands */ + sm2 = sm1 + 0x66666666; /* force carry out */ + cy = ((sm1 < s1 -> val[i]) || (sm2 < sm1)); /* check for overflow */ + tm2 = tm1 ^ sm2; /* get carry flags */ + tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ + tm4 = 0x22222222 & ~tm3; /* clear where carry */ + ds -> val[i] = sm2 - (3 * tm4); } /* final result */ +return cy; +} + +/* Subtract decimal string magnitudes + + Arguments: + s1 = source1 decimal string + s2 = source2 decimal string + ds = destination decimal string + Outputs: s2 - s1 in ds + + Note: the routine assumes that s1 <= s2 + +*/ + +void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) +{ +int32 i; +DSTR compl; + +for (i = 0; i < DSTRLNT; i++) compl.val[i] = 0x99999999 - s1 -> val[i]; +AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ +return; +} + +/* Compare decimal string magnitudes + + Arguments: + s1 = source1 decimal string + s2 = source2 decimal string + Output = 1 if >, 0 if =, -1 if < +*/ + +int32 CmpDstr (DSTR *s1, DSTR *s2) +{ +int32 i; + +for (i = DSTRMAX; i >=0; i--) { + if (s1 -> val[i] > s2 -> val[i]) return 1; + if (s1 -> val[i] < s2 -> val[i]) return -1; } +return 0; +} + +/* Test decimal string for zero + + Arguments: + dsrc = decimal string structure + Returns the non-zero length of the string, in int32 units + If the string is zero, the sign is cleared +*/ + +int32 TestDstr (DSTR *dsrc) +{ +int32 i; + +for (i = DSTRMAX; i >= 0; i--) if (dsrc -> val[i]) return (i + 1); +dsrc -> sign = 0; +return 0; +} + +/* Get exact length of decimal string + + Arguments: + dsrc = decimal string structure + nz = result from TestDstr +*/ + +int32 LntDstr (DSTR *dsrc, int32 nz) +{ +int32 i; + +for (i = 7; i > 0; i--) { + if ((dsrc -> val[nz - 1] >> (i * 4)) & 0xF) break; } +return ((nz - 1) * 8) + i; +} + +/* Create table of multiples + + Arguments: + dsrc = base decimal string structure + mtable[10] = array of decimal string structures + + Note that dsrc has a high order zero nibble; this + guarantees that the largest multiple won't overflow + Also note that mtable[0] is not filled in +*/ + +void CreateTable (DSTR *dsrc, DSTR mtable[10]) +{ +int32 (i); + +mtable[1] = *dsrc; +for (i = 2; i < 10; i++) AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); +return; +} + +/* Word shift right + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +void WordRshift (DSTR *dsrc, int32 sc) +{ +int32 i; + +if (sc) { + for (i = 0; i < DSTRLNT; i++) { + if ((i + sc) < DSTRLNT) dsrc -> val[i] = dsrc -> val[i + sc]; + else dsrc -> val[i] = 0; } } +return; +} + +/* Word shift left + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +int32 WordLshift (DSTR *dsrc, int32 sc) +{ +int32 i, c; + +c = 0; +if (sc) { + for (i = DSTRMAX; i >= 0; i--) { + if (i > (DSTRMAX - sc)) c = c | dsrc -> val[i]; + if ((i - sc) >= 0) dsrc -> val[i] = dsrc -> val[i - sc]; + else dsrc -> val[i] = 0; } } +return c; +} + +/* Nibble shift decimal string right + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +unsigned int32 NibbleRshift (DSTR *dsrc, int32 sc, unsigned int32 cin) +{ +int32 i, s, rs, nc; + +if (s = sc * 4) { + rs = 32 - s; + for (i = DSTRMAX; i >= 0; i--) { + nc = dsrc -> val[i]; + dsrc -> val[i] = ((dsrc -> val[i] >> s) | + (cin << rs)) & 0xFFFFFFFF; + cin = nc; } + return cin; } +return 0; +} + +/* Nibble shift decimal string left + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +unsigned int32 NibbleLshift (DSTR *dsrc, int32 sc, unsigned int32 cin) +{ +int32 i, s, rs, nc; + +if (s = sc * 4) { + rs = 32 - s; + for (i = 0; i < DSTRLNT; i++) { + nc = dsrc -> val[i]; + dsrc -> val[i] = ((dsrc -> val[i] << s) | + (cin >> rs)) & 0xFFFFFFFF; + cin = nc; } + return cin; } +return 0; +} diff --git a/pdp11_cpu.c b/pdp11_cpu.c new file mode 100644 index 00000000..eab9ca8f --- /dev/null +++ b/pdp11_cpu.c @@ -0,0 +1,2373 @@ +/* pdp11_cpu.c: PDP-11 CPU simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + 18-Aug-98 RMS Added CIS support + 09-May-98 RMS Fixed bug in DIV overflow test + 19-Jan-97 RMS Added RP/RM support + 06-Apr-96 RMS Added dynamic memory sizing + 29-Feb-96 RMS Added TM11 support + 17-Jul-94 RMS Corrected updating of MMR1 if MMR0 locked + + The register state for the PDP-11 is: + + REGFILE[0:5][0] general register set + REGFILE[0:5][1] alternate general register set + STACKFILE[4] stack pointers for kernel, supervisor, unused, user + PC program counter + PSW processor status word + <15:14> = CM current processor mode + <13:12> = PM previous processor mode + <11> = RS register set select + <7:5> = IPL interrupt priority level + <4> = TBIT trace trap enable + <3:0> = NZVC condition codes + FR[0:5] floating point accumulators + FPS floating point status register + FEC floating exception code + FEA floating exception address + MMR0,1,2,3 memory management control registers + APRFILE[0:63] memory management relocation registers for + kernel, supervisor, unused, user + <31:16> = PAR processor address registers + <15:0> = PDR processor data registers + PIRQ processor interrupt request register + CPUERR CPU error register + MEMERR memory system error register + CCR cache control register + MAINT maintenance register + HITMISS cache status register + SR switch register + DR display register +*/ + +/* The PDP-11 has many instruction formats: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ double operand + | opcode | source spec | dest spec | 010000:067777 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 110000:167777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand + | opcode | src reg| dest spec | 004000:004777 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 070000:077777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single operand + | opcode | dest spec | 000100:000177 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000300:000377 + 005000:007777 + 105000:107777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single register + | opcode |dest reg| 000200:000207 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000230:000237 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand + | opcode | 000000:000007 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ branch + | opcode | branch displacement | 000400:003477 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 100000:103477 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ EMT/TRAP + | opcode | trap code | 104000:104777 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ cond code operator + | opcode | immediate | 000240:000277 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + An operand specifier consists of an addressing mode and a register. + The addressing modes are: + + 0 register direct R op = R + 1 register deferred (R) op = M[R] + 2 autoincrement (R)+ op = M[R]; R = R + length + 3 autoincrement deferred @(R)+ op = M[M[R]]; R = R + 2 + 4 autodecrement -(R) R = R - length; op = M[R] + 5 autodecrement deferred @-(R) R = R - 2; op = M[M[R]] + 6 displacement d(R) op = M[R + disp] + 7 displacement deferred @d(R) op = M[M[R + disp]] + + There are eight general registers, R0-R7. R6 is the stack pointer, + R7 the PC. The combination of addressing modes with R7 yields: + + 27 immediate #n op = M[PC]; PC = PC + 2 + 37 absolute @#n op = M[M[PC]]; PC = PC + 2 + 67 relative d(PC) op = M[PC + disp] + 77 relative deferred @d(PC) op = M[M[PC + disp]] +*/ + +/* This routine is the instruction decode routine for the PDP-11. It + is called from the simulator control program to execute instructions + in simulated memory, starting at the simulated PC. It runs until an + enabled exception is encountered. + + General notes: + + 1. Virtual address format. PDP-11 memory management uses the 16b + virtual address, the type of reference (instruction or data), and + the current mode, to construct the 22b physical address. To + package this conveniently, the simulator uses a 19b pseudo virtual + address, consisting of the 16b virtual address prefixed with the + current mode and ispace/dspace indicator. These are precalculated + as isenable and dsenable for ispace and dspace, respectively, and + must be recalculated whenever MMR0, MMR3, or PSW changes. + + 2. Traps and interrupts. Variable trap_req bit-encodes all possible + traps. In addition, an interrupt pending bit is encoded as the + lowest priority trap. Traps are processed by trap_vec and trap_clear, + which provide the vector and subordinate traps to clear, respectively. + + Variable int_req bit encodes all possible interrupts. It is masked + under the interrupt masks, int_mask[ipl]. If any interrupt request + is not masked, the interrupt bit is set in trap_req. While most + interrupts are handled centrally, a device can supply an interrupt + acknowledge routine. + + 3. PSW handling. The PSW is kept as components, for easier access. + Because the PSW can be explicitly written as address 17777776, + all instructions must update PSW before executing their last write. + + 4. Adding I/O devices. This requires modifications to three modules: + + pdp11_defs.h add interrupt request definitions + pdp11_cpu.c add I/O page linkages + pdp11_sys.c add to sim_devices +*/ + +/* Definitions */ + +#include "pdp11_defs.h" +#include + +#define calc_is(md) ((md) << VA_V_MODE) +#define calc_ds(md) (calc_is((md)) | ((MMR3 & dsmask[(md)])? VA_DS: 0)) +#define calc_MMR1(val) (MMR1 = MMR1? ((val) << 8) | MMR1: (val)) +#define calc_ints(lv,rq,tr) (((rq) & int_mask[(lv)])? \ + ((tr) | TRAP_INT) : ((tr) & ~TRAP_INT)) +#define GET_SIGN_W(v) ((v) >> 15) +#define GET_SIGN_B(v) ((v) >> 7) +#define GET_Z(v) ((v) == 0) +#define JMP_PC(x) old_PC = PC; PC = (x) +#define BRANCH_F(x) old_PC = PC; PC = (PC + (((x) + (x)) & 0377)) & 0177777 +#define BRANCH_B(x) old_PC = PC; PC = (PC + (((x) + (x)) | 0177400)) & 0177777 +#define ILL_ADR_FLAG 0200000 +#define save_ibkpt (cpu_unit.u3) /* will be SAVEd */ +#define last_pa (cpu_unit.u4) /* and RESTOREd */ +#define UNIT_V_18B (UNIT_V_UF) /* force 18b addr */ +#define UNIT_18B (1u << UNIT_V_18B) +#define UNIT_V_CIS (UNIT_V_UF + 1) /* CIS present */ +#define UNIT_CIS (1u << UNIT_V_CIS) +#define UNIT_V_MSIZE (UNIT_V_UF + 2) /* dummy */ +#define UNIT_MSIZE (1u << UNIT_V_MSIZE) + +/* Global state */ + +unsigned int16 *M = NULL; /* address of memory */ +int32 REGFILE[6][2] = { 0 }; /* R0-R5, two sets */ +int32 STACKFILE[4] = { 0 }; /* SP, four modes */ +int32 saved_PC = 0; /* program counter */ +int32 R[8] = { 0 }; /* working registers */ +int32 PSW = 0; /* PSW */ + int32 cm = 0; /* current mode */ + int32 pm = 0; /* previous mode */ + int32 rs = 0; /* register set */ + int32 ipl = 0; /* int pri level */ + int32 tbit = 0; /* trace flag */ + int32 N = 0, Z = 0, V = 0, C = 0; /* condition codes */ +int32 wait_state = 0; /* wait state */ +int32 trap_req = 0; /* trap requests */ +int32 int_req = 0; /* interrupt requests */ +int32 PIRQ = 0; /* programmed int req */ +int32 SR = 0; /* switch register */ +int32 DR = 0; /* display register */ +fpac_t FR[6] = { 0 }; /* fp accumulators */ +int32 FPS = 0; /* fp status */ +int32 FEC = 0; /* fp exception code */ +int32 FEA = 0; /* fp exception addr */ +int32 APRFILE[64] = { 0 }; /* PARs/PDRs */ +int32 MMR0 = 0; /* MMR0 - status */ +int32 MMR1 = 0; /* MMR1 - R+/-R */ +int32 MMR2 = 0; /* MMR2 - saved PC */ +int32 MMR3 = 0; /* MMR3 - 22b status */ +int32 isenable = 0, dsenable = 0; /* i, d space flags */ +int32 CPUERR = 0; /* CPU error reg */ +int32 MEMERR = 0; /* memory error reg */ +int32 CCR = 0; /* cache control reg */ +int32 HITMISS = 0; /* hit/miss reg */ +int32 MAINT = (0 << 9) + (0 << 8) + (4 << 4); /* maint bit<9> = Q/U */ + /* <8> = hwre FP */ + /* <6:4> = sys type */ +int32 stop_trap = 1; /* stop on trap */ +int32 stop_vecabort = 1; /* stop on vec abort */ +int32 stop_spabort = 1; /* stop on SP abort */ +int32 wait_enable = 0; /* wait state enable */ +int32 ibkpt_addr = ILL_ADR_FLAG | VAMASK; /* breakpoint addr */ +int32 old_PC = 0; /* previous PC */ +jmp_buf save_env; /* abort handler */ +int32 dsmask[4] = { MMR3_KDS, MMR3_SDS, 0, MMR3_UDS }; /* dspace enables */ +int32 int_mask[8] = { INT_IPL0, INT_IPL1, INT_IPL2, /* interrupt masks */ + INT_IPL3, INT_IPL4, INT_IPL5, INT_IPL6, INT_IPL7 }; +extern int32 sim_int_char; + +/* Function declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +int32 GeteaB (int32 spec); +int32 GeteaW (int32 spec); +int32 relocR (int32 addr); +int32 relocW (int32 addr); +int32 ReadW (int32 addr); +int32 ReadB (int32 addr); +int32 ReadMW (int32 addr); +int32 ReadMB (int32 addr); +void WriteW (int32 data, int32 addr); +void WriteB (int32 data, int32 addr); +void PWriteW (int32 data, int32 addr); +void PWriteB (int32 data, int32 addr); +t_stat iopageR (int32 *data, int32 addr, int32 access); +t_stat iopageW (int32 data, int32 addr, int32 access); + +t_stat CPU_rd (int32 *data, int32 addr, int32 access); +t_stat CPU_wr (int32 data, int32 addr, int32 access); +t_stat APR_rd (int32 *data, int32 addr, int32 access); +t_stat APR_wr (int32 data, int32 addr, int32 access); +t_stat SR_MMR012_rd (int32 *data, int32 addr, int32 access); +t_stat SR_MMR012_wr (int32 data, int32 addr, int32 access); +t_stat MMR3_rd (int32 *data, int32 addr, int32 access); +t_stat MMR3_wr (int32 data, int32 addr, int32 access); +extern t_stat std_rd (int32 *data, int32 addr, int32 access); +extern t_stat std_wr (int32 data, int32 addr, int32 access); +extern t_stat lpt_rd (int32 *data, int32 addr, int32 access); +extern t_stat lpt_wr (int32 data, int32 addr, int32 access); +extern t_stat rk_rd (int32 *data, int32 addr, int32 access); +extern t_stat rk_wr (int32 data, int32 addr, int32 access); +extern int32 rk_inta (void); +extern t_stat rl_rd (int32 *data, int32 addr, int32 access); +extern t_stat rl_wr (int32 data, int32 addr, int32 access); +extern t_stat rp_rd (int32 *data, int32 addr, int32 access); +extern t_stat rp_wr (int32 data, int32 addr, int32 access); +extern int32 rp_inta (void); +extern t_stat rx_rd (int32 *data, int32 addr, int32 access); +extern t_stat rx_wr (int32 data, int32 addr, int32 access); +extern t_stat tm_rd (int32 *data, int32 addr, int32 access); +extern t_stat tm_wr (int32 data, int32 addr, int32 access); +extern t_stat sim_activate (UNIT *uptr, int32 delay); + +/* Auxiliary data structures */ + +struct iolink { /* I/O page linkage */ + int32 low; /* low I/O addr */ + int32 high; /* high I/O addr */ + t_stat (*read)(); /* read routine */ + t_stat (*write)(); }; /* write routine */ + +struct iolink iotable[] = { + { 017777740, 017777777, &CPU_rd, &CPU_wr }, + { 017777546, 017777567, &std_rd, &std_wr }, + { 017777514, 017777517, &lpt_rd, &lpt_wr }, + { 017777400, 017777417, &rk_rd, &rk_wr }, + { 017774400, 017774411, &rl_rd, &rl_wr }, + { 017776700, 017776753, &rp_rd, &rp_wr }, + { 017777170, 017777173, &rx_rd, &rx_wr }, + { 017772520, 017772533, &tm_rd, &tm_wr }, + { 017777600, 017777677, &APR_rd, &APR_wr }, + { 017772200, 017772377, &APR_rd, &APR_wr }, + { 017777570, 017777577, &SR_MMR012_rd, &SR_MMR012_wr }, + { 017772516, 017772517, &MMR3_rd, &MMR3_wr }, + { 0, 0, NULL } }; + +int32 int_vec[32] = { /* int req to vector */ + 0, 0, 0, VEC_PIRQ, VEC_CLK, 0, 0, VEC_PIRQ, + VEC_RK, VEC_RL, VEC_RX, VEC_TM, VEC_RP, 0, 0, VEC_PIRQ, + VEC_TTI, VEC_TTO, VEC_PTR, VEC_PTP, VEC_LPT, 0, 0, 0, + 0, 0, 0, 0, VEC_PIRQ, VEC_PIRQ, VEC_PIRQ, VEC_PIRQ }; + +int32 (*int_ack[32])() = { /* int ack routines */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &rk_inta, NULL, NULL, NULL, &rp_inta, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + +int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */ + VEC_RED, VEC_ODD, VEC_MME, VEC_NXM, + VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT, + VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC, + VEC_YEL, VEC_PWRFL, VEC_FPE }; + +int32 trap_clear[TRAP_V_MAX] = { /* trap clears */ + TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC, + TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC, + TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC, + TRAP_TRAP+TRAP_TRC, TRAP_TRC, + TRAP_YEL, TRAP_PWRFL, TRAP_FPE }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK, INIMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 16) }, + { ORDATA (R0, REGFILE[0][0], 16) }, + { ORDATA (R1, REGFILE[1][0], 16) }, + { ORDATA (R2, REGFILE[2][0], 16) }, + { ORDATA (R3, REGFILE[3][0], 16) }, + { ORDATA (R4, REGFILE[4][0], 16) }, + { ORDATA (R5, REGFILE[5][0], 16) }, + { ORDATA (R10, REGFILE[0][1], 16) }, + { ORDATA (R11, REGFILE[1][1], 16) }, + { ORDATA (R12, REGFILE[2][1], 16) }, + { ORDATA (R13, REGFILE[3][1], 16) }, + { ORDATA (R14, REGFILE[4][1], 16) }, + { ORDATA (R15, REGFILE[5][1], 16) }, + { ORDATA (KSP, STACKFILE[KERNEL], 16) }, + { ORDATA (SSP, STACKFILE[SUPER], 16) }, + { ORDATA (USP, STACKFILE[USER], 16) }, + { ORDATA (PSW, PSW, 16) }, + { GRDATA (CM, PSW, 8, 2, PSW_V_CM) }, + { GRDATA (PM, PSW, 8, 2, PSW_V_PM) }, + { FLDATA (RS, PSW, PSW_V_RS) }, + { GRDATA (IPL, PSW, 8, 3, PSW_V_IPL) }, + { FLDATA (T, PSW, PSW_V_TBIT) }, + { FLDATA (N, PSW, PSW_V_N) }, + { FLDATA (Z, PSW, PSW_V_Z) }, + { FLDATA (V, PSW, PSW_V_V) }, + { FLDATA (C, PSW, PSW_V_C) }, + { ORDATA (SR, SR, 16) }, + { ORDATA (DR, DR, 16) }, + { ORDATA (MEMERR, MEMERR, 16) }, + { ORDATA (CCR, CCR, 16) }, + { ORDATA (MAINT, MAINT, 16) }, + { ORDATA (HITMISS, HITMISS, 16) }, + { ORDATA (CPUERR, CPUERR, 16) }, + { ORDATA (INT, int_req, 32), REG_RO }, + { ORDATA (TRAPS, trap_req, TRAP_V_MAX) }, + { ORDATA (PIRQ, PIRQ, 16) }, + { FLDATA (WAIT, wait_state, 0) }, + { FLDATA (WAIT_ENABLE, wait_enable, 0) }, + { ORDATA (STOP_TRAPS, stop_trap, TRAP_V_MAX) }, + { FLDATA (STOP_VECA, stop_vecabort, 0) }, + { FLDATA (STOP_SPA, stop_spabort, 0) }, + { ORDATA (FAC0H, FR[0].h, 32) }, + { ORDATA (FAC0L, FR[0].l, 32) }, + { ORDATA (FAC1H, FR[1].h, 32) }, + { ORDATA (FAC1L, FR[1].l, 32) }, + { ORDATA (FAC2H, FR[2].h, 32) }, + { ORDATA (FAC2L, FR[2].l, 32) }, + { ORDATA (FAC3H, FR[3].h, 32) }, + { ORDATA (FAC3L, FR[3].l, 32) }, + { ORDATA (FAC4H, FR[4].h, 32) }, + { ORDATA (FAC4L, FR[4].l, 32) }, + { ORDATA (FAC5H, FR[5].h, 32) }, + { ORDATA (FAC5L, FR[5].l, 32) }, + { ORDATA (FPS, FPS, 16) }, + { ORDATA (FEA, FEA, 16) }, + { ORDATA (FEC, FEC, 4) }, + { ORDATA (MMR0, MMR0, 16) }, + { ORDATA (MMR1, MMR1, 16) }, + { ORDATA (MMR2, MMR2, 16) }, + { ORDATA (MMR3, MMR3, 16) }, + { GRDATA (KIPAR0, APRFILE[000], 8, 16, 16) }, + { GRDATA (KIPDR0, APRFILE[000], 8, 16, 0) }, + { GRDATA (KIPAR1, APRFILE[001], 8, 16, 16) }, + { GRDATA (KIPDR1, APRFILE[001], 8, 16, 0) }, + { GRDATA (KIPAR2, APRFILE[002], 8, 16, 16) }, + { GRDATA (KIPDR2, APRFILE[002], 8, 16, 0) }, + { GRDATA (KIPAR3, APRFILE[003], 8, 16, 16) }, + { GRDATA (KIPDR3, APRFILE[003], 8, 16, 0) }, + { GRDATA (KIPAR4, APRFILE[004], 8, 16, 16) }, + { GRDATA (KIPDR4, APRFILE[004], 8, 16, 0) }, + { GRDATA (KIPAR5, APRFILE[005], 8, 16, 16) }, + { GRDATA (KIPDR5, APRFILE[005], 8, 16, 0) }, + { GRDATA (KIPAR6, APRFILE[006], 8, 16, 16) }, + { GRDATA (KIPDR6, APRFILE[006], 8, 16, 0) }, + { GRDATA (KIPAR7, APRFILE[007], 8, 16, 16) }, + { GRDATA (KIPDR7, APRFILE[007], 8, 16, 0) }, + { GRDATA (KDPAR0, APRFILE[010], 8, 16, 16) }, + { GRDATA (KDPDR0, APRFILE[010], 8, 16, 0) }, + { GRDATA (KDPAR1, APRFILE[011], 8, 16, 16) }, + { GRDATA (KDPDR1, APRFILE[011], 8, 16, 0) }, + { GRDATA (KDPAR2, APRFILE[012], 8, 16, 16) }, + { GRDATA (KDPDR2, APRFILE[012], 8, 16, 0) }, + { GRDATA (KDPAR3, APRFILE[013], 8, 16, 16) }, + { GRDATA (KDPDR3, APRFILE[013], 8, 16, 0) }, + { GRDATA (KDPAR4, APRFILE[014], 8, 16, 16) }, + { GRDATA (KDPDR4, APRFILE[014], 8, 16, 0) }, + { GRDATA (KDPAR5, APRFILE[015], 8, 16, 16) }, + { GRDATA (KDPDR5, APRFILE[015], 8, 16, 0) }, + { GRDATA (KDPAR6, APRFILE[016], 8, 16, 16) }, + { GRDATA (KDPDR6, APRFILE[016], 8, 16, 0) }, + { GRDATA (KDPAR7, APRFILE[017], 8, 16, 16) }, + { GRDATA (KDPDR7, APRFILE[017], 8, 16, 0) }, + { GRDATA (SIPAR0, APRFILE[020], 8, 16, 16) }, + { GRDATA (SIPDR0, APRFILE[020], 8, 16, 0) }, + { GRDATA (SIPAR1, APRFILE[021], 8, 16, 16) }, + { GRDATA (SIPDR1, APRFILE[021], 8, 16, 0) }, + { GRDATA (SIPAR2, APRFILE[022], 8, 16, 16) }, + { GRDATA (SIPDR2, APRFILE[022], 8, 16, 0) }, + { GRDATA (SIPAR3, APRFILE[023], 8, 16, 16) }, + { GRDATA (SIPDR3, APRFILE[023], 8, 16, 0) }, + { GRDATA (SIPAR4, APRFILE[024], 8, 16, 16) }, + { GRDATA (SIPDR4, APRFILE[024], 8, 16, 0) }, + { GRDATA (SIPAR5, APRFILE[025], 8, 16, 16) }, + { GRDATA (SIPDR5, APRFILE[025], 8, 16, 0) }, + { GRDATA (SIPAR6, APRFILE[026], 8, 16, 16) }, + { GRDATA (SIPDR6, APRFILE[026], 8, 16, 0) }, + { GRDATA (SIPAR7, APRFILE[027], 8, 16, 16) }, + { GRDATA (SIPDR7, APRFILE[027], 8, 16, 0) }, + { GRDATA (SDPAR0, APRFILE[030], 8, 16, 16) }, + { GRDATA (SDPDR0, APRFILE[030], 8, 16, 0) }, + { GRDATA (SDPAR1, APRFILE[031], 8, 16, 16) }, + { GRDATA (SDPDR1, APRFILE[031], 8, 16, 0) }, + { GRDATA (SDPAR2, APRFILE[032], 8, 16, 16) }, + { GRDATA (SDPDR2, APRFILE[032], 8, 16, 0) }, + { GRDATA (SDPAR3, APRFILE[033], 8, 16, 16) }, + { GRDATA (SDPDR3, APRFILE[033], 8, 16, 0) }, + { GRDATA (SDPAR4, APRFILE[034], 8, 16, 16) }, + { GRDATA (SDPDR4, APRFILE[034], 8, 16, 0) }, + { GRDATA (SDPAR5, APRFILE[035], 8, 16, 16) }, + { GRDATA (SDPDR5, APRFILE[035], 8, 16, 0) }, + { GRDATA (SDPAR6, APRFILE[036], 8, 16, 16) }, + { GRDATA (SDPDR6, APRFILE[036], 8, 16, 0) }, + { GRDATA (SDPAR7, APRFILE[037], 8, 16, 16) }, + { GRDATA (SDPDR7, APRFILE[037], 8, 16, 0) }, + { GRDATA (UIPAR0, APRFILE[060], 8, 16, 16) }, + { GRDATA (UIPDR0, APRFILE[060], 8, 16, 0) }, + { GRDATA (UIPAR1, APRFILE[061], 8, 16, 16) }, + { GRDATA (UIPDR1, APRFILE[061], 8, 16, 0) }, + { GRDATA (UIPAR2, APRFILE[062], 8, 16, 16) }, + { GRDATA (UIPDR2, APRFILE[062], 8, 16, 0) }, + { GRDATA (UIPAR3, APRFILE[063], 8, 16, 16) }, + { GRDATA (UIPDR3, APRFILE[063], 8, 16, 0) }, + { GRDATA (UIPAR4, APRFILE[064], 8, 16, 16) }, + { GRDATA (UIPDR4, APRFILE[064], 8, 16, 0) }, + { GRDATA (UIPAR5, APRFILE[065], 8, 16, 16) }, + { GRDATA (UIPDR5, APRFILE[065], 8, 16, 0) }, + { GRDATA (UIPAR6, APRFILE[066], 8, 16, 16) }, + { GRDATA (UIPDR6, APRFILE[066], 8, 16, 0) }, + { GRDATA (UIPAR7, APRFILE[067], 8, 16, 16) }, + { GRDATA (UIPDR7, APRFILE[067], 8, 16, 0) }, + { GRDATA (UDPAR0, APRFILE[070], 8, 16, 16) }, + { GRDATA (UDPDR0, APRFILE[070], 8, 16, 0) }, + { GRDATA (UDPAR1, APRFILE[071], 8, 16, 16) }, + { GRDATA (UDPDR1, APRFILE[071], 8, 16, 0) }, + { GRDATA (UDPAR2, APRFILE[072], 8, 16, 16) }, + { GRDATA (UDPDR2, APRFILE[072], 8, 16, 0) }, + { GRDATA (UDPAR3, APRFILE[073], 8, 16, 16) }, + { GRDATA (UDPDR3, APRFILE[073], 8, 16, 0) }, + { GRDATA (UDPAR4, APRFILE[074], 8, 16, 16) }, + { GRDATA (UDPDR4, APRFILE[074], 8, 16, 0) }, + { GRDATA (UDPAR5, APRFILE[075], 8, 16, 16) }, + { GRDATA (UDPDR5, APRFILE[075], 8, 16, 0) }, + { GRDATA (UDPAR6, APRFILE[076], 8, 16, 16) }, + { GRDATA (UDPDR6, APRFILE[076], 8, 16, 0) }, + { GRDATA (UDPAR7, APRFILE[077], 8, 16, 16) }, + { GRDATA (UDPDR7, APRFILE[077], 8, 16, 0) }, + { FLDATA (18B_ADDR, cpu_unit.flags, UNIT_V_18B), REG_HRO }, + { FLDATA (CIS, cpu_unit.flags, UNIT_V_CIS), REG_HRO }, + { ORDATA (OLDPC, old_PC, 16), REG_RO }, + { ORDATA (BREAK, ibkpt_addr, 17) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL} }; + +MTAB cpu_mod[] = { + { UNIT_18B, UNIT_18B, "18b addressing", "18B", NULL }, + { UNIT_18B, 0, NULL, "22B", NULL }, + { UNIT_CIS, UNIT_CIS, "CIS", "CIS", NULL }, + { UNIT_CIS, 0, "No CIS", "NOCIS", NULL }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size}, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size}, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size}, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size}, + { UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size}, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size}, + { UNIT_MSIZE, 229376, NULL, "192K", &cpu_set_size}, + { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size}, + { UNIT_MSIZE, 393216, NULL, "384K", &cpu_set_size}, + { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size}, + { UNIT_MSIZE, 786432, NULL, "768K", &cpu_set_size}, + { UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size}, + { UNIT_MSIZE, 2097152, NULL, "2048K", &cpu_set_size}, + { UNIT_MSIZE, 3145728, NULL, "3072K", &cpu_set_size}, + { UNIT_MSIZE, 4194304, NULL, "4096K", &cpu_set_size}, + { UNIT_MSIZE, 1048576, NULL, "1M", &cpu_set_size}, + { UNIT_MSIZE, 2097152, NULL, "2M", &cpu_set_size}, + { UNIT_MSIZE, 3145728, NULL, "3M", &cpu_set_size}, + { UNIT_MSIZE, 4194304, NULL, "4M", &cpu_set_size}, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 22, 2, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +extern UNIT *sim_clock_queue; +register int32 IR, srcspec, srcreg, dstspec, dstreg; +register int32 src, src2, dst; +register int32 i, t, sign, oldrs, trapnum; +int32 abortval; +volatile int32 trapea; +t_stat reason; +void fp11 (int32 IR); +void cis11 (int32 IR); + +/* Restore register state + + 1. PSW components + 2. Active register file based on PSW + 3. Active stack pointer based on PSW + 4. Memory management control flags + 5. Interrupt system +*/ + +cm = (PSW >> PSW_V_CM) & 03; /* call calc_is,ds */ +pm = (PSW >> PSW_V_PM) & 03; +rs = (PSW >> PSW_V_RS) & 01; +ipl = (PSW >> PSW_V_IPL) & 07; /* call calc_ints */ +tbit = (PSW >> PSW_V_TBIT) & 01; +N = (PSW >> PSW_V_N) & 01; +Z = (PSW >> PSW_V_Z) & 01; +V = (PSW >> PSW_V_V) & 01; +C = (PSW >> PSW_V_C) & 01; + +for (i = 0; i < 6; i++) R[i] = REGFILE[i][rs]; +SP = STACKFILE[cm]; +PC = saved_PC; + +isenable = calc_is (cm); +dsenable = calc_ds (cm); + +CPU_wr (PIRQ, 017777772, WRITE); /* rewrite PIRQ */ +trap_req = calc_ints (ipl, int_req, trap_req); +trapea = 0; +reason = 0; + +/* Abort handling + + If an abort occurs in memory management or memory access, the lower + level routine executes a longjmp to this area OUTSIDE the main + simulation loop. The longjmp specifies a trap mask which is OR'd + into the trap_req register. Simulation then resumes at the fetch + phase, and the trap is sprung. + + Aborts which occur within a trap sequence (trapea != 0) require + special handling. If the abort occured on the stack pushes, and + the mode (encoded in trapea) is kernel, an "emergency" kernel + stack is created at 4, and a red zone stack trap taken. +*/ + +abortval = setjmp (save_env); /* set abort hdlr */ +if (abortval != 0) { + trap_req = trap_req | abortval; /* or in trap flag */ + if ((trapea > 0) && (stop_vecabort)) reason = STOP_VECABORT; + if ((trapea < 0) && (stop_spabort)) reason = STOP_SPABORT; + if (trapea == ~KERNEL) { /* kernel stk abort? */ + setTRAP (TRAP_RED); + setCPUERR (CPUE_RED); + STACKFILE[KERNEL] = 4; + if (cm == KERNEL) SP = 4; } } + +/* Main instruction fetch/decode loop + + Check for traps or interrupts. If trap, locate the vector and check + for stop condition. If interrupt, locate the vector. +*/ + +while (reason == 0) { +if (sim_interval <= 0) { /* check clock queue */ + reason = sim_process_event (); + trap_req = calc_ints (ipl, int_req, trap_req); + continue; } +if (trap_req) { /* check traps, ints */ + trapea = 0; /* assume srch fails */ + if (t = trap_req & TRAP_ALL) { /* if a trap */ + for (trapnum = 0; trapnum < TRAP_V_MAX; trapnum++) { + if ((t >> trapnum) & 1) { + trapea = trap_vec[trapnum]; + trap_req = trap_req & ~trap_clear[trapnum]; + if ((stop_trap >> trapnum) & 1) + reason = trapnum + 1; + break; } } } + else if (t = int_req & int_mask[ipl]) { /* if an interrupt */ + for (i = 0; i < 32; i++) { + if ((t >> i) & 1) { + int_req = int_req & ~(1u << i); + if (int_ack[i]) trapea = int_ack[i](); + else trapea = int_vec[i]; + trapnum = TRAP_V_MAX; + break; } } } + if (trapea == 0) { /* nothing to do? */ + trap_req = calc_ints (ipl, int_req, 0); /* recalculate */ + continue; } /* back to fetch */ + +/* Process a trap or interrupt + + 1. Exit wait state + 2. Save the current SP and PSW + 3. Read the new PC, new PSW from trapea, kernel data space + 4. Get the mode and stack selected by the new PSW + 5. Push the old PC and PSW on the new stack + 6. Update SP, PSW, and PC + 7. If not stack overflow, check for stack overflow +*/ + + wait_state = 0; /* exit wait state */ + STACKFILE[cm] = SP; + PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) | + (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | + (N << PSW_V_N) | (Z << PSW_V_Z) | + (V << PSW_V_V) | (C << PSW_V_C); + oldrs = rs; + src = ReadW (trapea | calc_ds (KERNEL)); + src2 = ReadW ((trapea + 2) | calc_ds (KERNEL)); + t = (src2 >> PSW_V_CM) & 03; + trapea = ~t; /* flag pushes */ + WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t)); + WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t)); + trapea = 0; /* clear trap flag */ + pm = cm; + cm = t; /* call calc_is,ds */ + rs = (src2 >> PSW_V_RS) & 01; + ipl = (src2 >> PSW_V_IPL) & 07; /* call calc_ints */ + tbit = (src2 >> PSW_V_TBIT) & 01; + N = (src2 >> PSW_V_N) & 01; + Z = (src2 >> PSW_V_Z) & 01; + V = (src2 >> PSW_V_V) & 01; + C = (src2 >> PSW_V_C) & 01; + if (rs != oldrs) { /* if rs chg, swap */ + for (i = 0; i < 6; i++) { + REGFILE[i][oldrs] = R[i]; + R[i] = REGFILE[i][rs]; } } + SP = (STACKFILE[cm] - 4) & 0177777; /* update SP, PC */ + JMP_PC (src); + isenable = calc_is (cm); + dsenable = calc_ds (cm); + trap_req = calc_ints (ipl, int_req, trap_req); + if ((SP < STKLIM) && (cm == KERNEL) && + (trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + continue; } /* end if traps */ + +/* Fetch and decode next instruction */ + +if (tbit) setTRAP (TRAP_TRC); +if (wait_state) { /* wait state? */ + if (sim_clock_queue != NULL) sim_interval = 0; /* force check */ + else reason = STOP_WAIT; + continue; } + +if (PC == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save bkpt */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + continue; } + +if (update_MM) { /* if mm not frozen */ + MMR1 = 0; + MMR2 = PC; } +IR = ReadW (PC | isenable); /* fetch instruction */ +PC = (PC + 2) & 0177777; /* incr PC, mod 65k */ +sim_interval = sim_interval - 1; +srcspec = (IR >> 6) & 077; /* src, dst specs */ +dstspec = IR & 077; +srcreg = (srcspec <= 07); /* src, dst = rmode? */ +dstreg = (dstspec <= 07); +switch ((IR >> 12) & 017) { /* decode IR<15:12> */ + +/* Opcode 0: no operands, specials, branches, JSR, SOPs */ + +case 000: + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: /* no operand */ + if (IR > 000010) { /* 000010 - 000077 */ + setTRAP (TRAP_ILL); /* illegal */ + break; } + switch (IR) { /* decode IR<2:0> */ + case 0: /* HALT */ + if (cm == KERNEL) reason = STOP_HALT; + else { setTRAP (TRAP_PRV); + setCPUERR (CPUE_HALT); } + break; + case 1: /* WAIT */ + if (cm == KERNEL && wait_enable) wait_state = 1; + break; + case 3: /* BPT */ + setTRAP (TRAP_BPT); + break; + case 4: /* IOT */ + setTRAP (TRAP_IOT); + break; + case 5: /* RESET */ + if (cm == KERNEL) { + reset_all (1); + PIRQ = 0; + int_req = 0; + MMR0 = MMR0 & ~(MMR0_MME | MMR0_FREEZE); + MMR3 = 0; + trap_req = trap_req & ~TRAP_INT; + dsenable = calc_ds (cm); } + break; + +/* Opcode 0: specials, continued */ + + case 2: /* RTI */ + case 6: /* RTT */ + src = ReadW (SP | dsenable); + src2 = ReadW (((SP + 2) & 0177777) | dsenable); + STACKFILE[cm] = SP = (SP + 4) & 0177777; + oldrs = rs; + if (cm == KERNEL) { + cm = (src2 >> PSW_V_CM) & 03; + pm = (src2 >> PSW_V_PM) & 03; + rs = (src2 >> PSW_V_RS) & 01; + ipl = (src2 >> PSW_V_IPL) & 07; } + else { cm = cm | ((src2 >> PSW_V_CM) & 03); + pm = pm | ((src2 >> PSW_V_PM) & 03); + rs = rs | ((src2 >> PSW_V_RS) & 01); } + tbit = (src2 >> PSW_V_TBIT) & 01; + N = (src2 >> PSW_V_N) & 01; + Z = (src2 >> PSW_V_Z) & 01; + V = (src2 >> PSW_V_V) & 01; + C = (src2 >> PSW_V_C) & 01; + trap_req = calc_ints (ipl, int_req, trap_req); + isenable = calc_is (cm); + dsenable = calc_ds (cm); + if (rs != oldrs) { + for (i = 0; i < 6; i++) { + REGFILE[i][oldrs] = R[i]; + R[i] = REGFILE[i][rs]; } } + SP = STACKFILE[cm]; + JMP_PC (src); + if ((IR == 000002) && tbit) setTRAP (TRAP_TRC); + break; + case 7: /* MFPT */ + R[0] = 5; /* report J-11 */ + break; } /* end switch no ops */ + break; /* end case no ops */ + +/* Opcode 0: specials, continued */ + + case 001: /* JMP */ + if (dstreg) setTRAP (TRAP_ILL); + else { JMP_PC (GeteaW (dstspec) & 0177777); } + break; /* end JMP */ + case 002: /* RTS et al*/ + if (IR < 000210) { /* RTS */ + dstspec = dstspec & 07; + JMP_PC (R[dstspec]); + R[dstspec] = ReadW (SP | dsenable); + SP = (SP + 2) & 0177777; + break; } /* end if RTS */ + if (IR < 000230) { + setTRAP (TRAP_ILL); + break; } + if (IR < 000240) { /* SPL */ + if (cm == KERNEL) ipl = IR & 07; + trap_req = calc_ints (ipl, int_req, trap_req); + break; } /* end if SPL */ + if (IR < 000260) { /* clear CC */ + if (IR & 010) N = 0; + if (IR & 004) Z = 0; + if (IR & 002) V = 0; + if (IR & 001) C = 0; + break; } /* end if clear CCs */ + if (IR & 010) N = 1; /* set CC */ + if (IR & 004) Z = 1; + if (IR & 002) V = 1; + if (IR & 001) C = 1; + break; /* end case RTS et al */ + case 003: /* SWAB */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = ((dst & 0377) << 8) | ((dst >> 8) & 0377); + N = GET_SIGN_B (dst & 0377); + Z = GET_Z (dst & 0377); + V = C = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; /* end SWAB */ + +/* Opcode 0: branches, JSR */ + + case 004: case 005: /* BR */ + BRANCH_F (IR); + break; + case 006: case 007: /* BR */ + BRANCH_B (IR); + break; + case 010: case 011: /* BNE */ + if (Z == 0) { BRANCH_F (IR); } + break; + case 012: case 013: /* BNE */ + if (Z == 0) { BRANCH_B (IR); } + break; + case 014: case 015: /* BEQ */ + if (Z) { BRANCH_F (IR); } + break; + case 016: case 017: /* BEQ */ + if (Z) { BRANCH_B (IR); } + break; + case 020: case 021: /* BGE */ + if ((N ^ V) == 0) { BRANCH_F (IR); } + break; + case 022: case 023: /* BGE */ + if ((N ^ V) == 0) { BRANCH_B (IR); } + break; + case 024: case 025: /* BLT */ + if (N ^ V) { BRANCH_F (IR); } + break; + case 026: case 027: /* BLT */ + if (N ^ V) { BRANCH_B (IR); } + break; + case 030: case 031: /* BGT */ + if ((Z | (N ^ V)) == 0) { BRANCH_F (IR); } + break; + case 032: case 033: /* BGT */ + if ((Z | (N ^ V)) == 0) { BRANCH_B (IR); } + break; + case 034: case 035: /* BLE */ + if (Z | (N ^ V)) { BRANCH_F (IR); } + break; + case 036: case 037: /* BLE */ + if (Z | (N ^ V)) { BRANCH_B (IR); } + break; + case 040: case 041: case 042: case 043: /* JSR */ + case 044: case 045: case 046: case 047: + if (dstreg) setTRAP (TRAP_ILL); + else { srcspec = srcspec & 07; + dst = GeteaW (dstspec); + SP = (SP - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0366); + WriteW (R[srcspec], SP | dsenable); + if ((SP < STKLIM) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + R[srcspec] = PC; + JMP_PC (dst & 0177777); } + break; /* end JSR */ + +/* Opcode 0: SOPs */ + + case 050: /* CLR */ + N = V = C = 0; + Z = 1; + if (dstreg) R[dstspec] = 0; + else WriteW (0, GeteaW (dstspec)); + break; + case 051: /* COM */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = dst ^ 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + C = 1; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 052: /* INC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst + 1) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (dst == 0100000); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 053: /* DEC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst - 1) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (dst == 077777); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 054: /* NEG */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (-dst) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (dst == 0100000); + C = Z ^ 1; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 055: /* ADC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst + C) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (C && (dst == 0100000)); + C = C & Z; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Opcode 0: SOPs, continued */ + + case 056: /* SBC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst - C) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (C && (dst == 077777)); + C = (C && (dst == 0177777)); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 057: /* TST */ + dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = C = 0; + break; + case 060: /* ROR */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src >> 1) | (C << 15); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 061: /* ROL */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = ((src << 1) | C) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = GET_SIGN_W (src); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 062: /* ASR */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src >> 1) | (src & 0100000); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 063: /* ASL */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src << 1) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = GET_SIGN_W (src); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Opcode 0: SOPS, continued + + Notes: + - MxPI must mask GeteaW returned address to force ispace + - MxPI must set MMR1 for SP recovery in case of fault +*/ + + case 064: /* MARK */ + i = (PC + dstspec + dstspec) & 0177777; + JMP_PC (R[5]); + R[5] = ReadW (i | dsenable); + SP = (i + 2) & 0177777; + break; + case 065: /* MFPI */ + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm]; + else dst = R[dstspec]; } + else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_is (pm)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0366); + WriteW (dst, SP | dsenable); + if ((cm == KERNEL) && (SP < STKLIM)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + break; + case 066: /* MTPI */ + dst = ReadW (SP | dsenable); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP + 2) & 0177777; + if (update_MM) MMR1 = 026; + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst; + else R[dstspec] = dst; } + else { i = ((cm == pm) && (cm == USER))? + calc_ds (pm): calc_is (pm); + WriteW (dst, (GeteaW (dstspec) & 0177777) | i); } + break; + case 067: /* SXT */ + dst = N? 0177777: 0; + Z = N ^ 1; + V = 0; + if (dstreg) R[dstspec] = dst; + else WriteW (dst, GeteaW (dstspec)); + break; + +/* Opcode 0: SOPs, continued */ + + case 070: /* CSM */ + if (((MMR3 & MMR3_CSM) == 0) || (cm == KERNEL)) + setTRAP (TRAP_ILL); + else { dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | + (rs << PSW_V_RS) | (ipl << PSW_V_IPL) | + (tbit << PSW_V_TBIT); + STACKFILE[cm] = SP; + WriteW (PSW, ((SP - 2) & 0177777) | calc_ds (SUPER)); + WriteW (PC, ((SP - 4) & 0177777) | calc_ds (SUPER)); + WriteW (dst, ((SP - 6) & 0177777) | calc_ds (SUPER)); + SP = (SP - 6) & 0177777; + pm = cm; + cm = SUPER; + tbit = 0; + isenable = calc_is (cm); + dsenable = calc_ds (cm); + PC = ReadW (010 | isenable); } + break; + case 072: /* TSTSET */ + if (dstreg) setTRAP (TRAP_ILL); + else { dst = ReadMW (GeteaW (dstspec)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + C = (dst & 1); + PWriteW (R[0] | 1, last_pa); + R[0] = dst; } + break; + case 073: /* WRTLCK */ + if (dstreg) setTRAP (TRAP_ILL); + else { N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = 0; + WriteW (R[0], GeteaW (dstspec)); } + break; + default: + setTRAP (TRAP_ILL); + break; } /* end switch SOPs */ + break; /* end case 000 */ + +/* Opcodes 01 - 06: double operand word instructions + + Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)] + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] +*/ + +case 001: /* MOV */ + dst = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else WriteW (dst, GeteaW (dstspec)); + break; +case 002: /* CMP */ + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + dst = (src - src2) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = GET_SIGN_W ((src ^ src2) & (~src2 ^ dst)); + C = (src < src2); + break; +case 003: /* BIT */ + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + dst = src2 & src; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + break; +case 004: /* BIC */ + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = src2 & ~src; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; +case 005: /* BIS */ + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = src2 | src; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; +case 006: /* ADD */ + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src2 + src) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = GET_SIGN_W ((~src ^ src2) & (src ^ dst)); + C = (dst < src); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Opcode 07: EIS, FIS (not implemented), CIS + + Notes: + - The code assumes that the host int length is at least 32 bits. + - MUL carry: C is set if the (signed) result doesn't fit in 16 bits. + - Divide has three error cases: + 1. Divide by zero. + 2. Divide largest negative number by -1. + 3. (Signed) quotient doesn't fit in 16 bits. + Cases 1 and 2 must be tested in advance, to avoid C runtime errors. + - ASHx left: overflow if the bits shifted out do not equal the sign + of the result (convert shift out to 1/0, xor against sign). + - ASHx right: if right shift sign extends, then the shift and + conditional or of shifted -1 is redundant. If right shift zero + extends, then the shift and conditional or does sign extension. +*/ + +case 007: + srcspec = srcspec & 07; + switch ((IR >> 9) & 07) { /* decode IR<11:9> */ + case 0: /* MUL */ + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src = R[srcspec]; + if (GET_SIGN_W (src2)) src2 = src2 | ~077777; + if (GET_SIGN_W (src)) src = src | ~077777; + dst = src * src2; + R[srcspec] = (dst >> 16) & 0177777; + R[srcspec | 1] = dst & 0177777; + N = (dst < 0); + Z = GET_Z (dst); + V = 0; + C = ((dst > 077777) || (dst < -0100000)); + break; + case 1: /* DIV */ + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src = (R[srcspec] << 16) | R[srcspec | 1]; + if (src2 == 0) { + V = C = 1; + break; } + if ((src == 020000000000) && (src2 == 0177777)) { + V = 1; + C = 0; + break; } + if (GET_SIGN_W (src2)) src2 = src2 | ~077777; + if (GET_SIGN_W (R[srcspec])) src = src | ~017777777777; + dst = src / src2; + if ((dst > 077777) || (dst < -0100000)) { + V = 1; + C = 0; + break; } + R[srcspec] = dst & 0177777; + R[srcspec | 1] = (src - (src2 * dst)) & 0177777; + N = (dst < 0); + Z = GET_Z (dst); + V = C = 0; + break; + +/* Opcode 7: EIS, continued */ + + case 2: /* ASH */ + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src2 = src2 & 077; + sign = GET_SIGN_W (R[srcspec]); + src = sign? R[srcspec] | ~077777: R[srcspec]; + if (src2 == 0) { /* [0] */ + dst = src; + V = C = 0; } + else if (src2 <= 15) { /* [1,15] */ + dst = src << src2; + i = (src >> (16 - src2)) & 0177777; + V = (i != ((dst & 0100000)? 0177777: 0)); + C = (i & 1); } + else if (src2 <= 31) { /* [16,31] */ + dst = 0; + V = (src != 0); + C = (src << (src2 - 16)) & 1; } + else { /* [-32,-1] */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + V = 0; + C = ((src >> (63 - src2)) & 1); } + dst = R[srcspec] = dst & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + break; + case 3: /* ASHC */ + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src2 = src2 & 077; + sign = GET_SIGN_W (R[srcspec]); + src = (R[srcspec] << 16) | R[srcspec | 1]; + if (src2 == 0) { /* [0] */ + dst = src; + V = C = 0; } + else if (src2 <= 31) { /* [1,31] */ + dst = src << src2; + i = (src >> (32 - src2)) | (-sign << src2); + V = (i != ((dst & 020000000000)? -1: 0)); + C = (i & 1); } + else { /* [-32,-1] */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + V = 0; + C = ((src >> (63 - src2)) & 1); } + i = R[srcspec] = (dst >> 16) & 0177777; + dst = R[srcspec | 1] = dst & 0177777; + N = GET_SIGN_W (i); + Z = GET_Z (dst | i); + break; + +/* Opcode 7: EIS, continued */ + + case 4: /* XOR */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = dst ^ R[srcspec]; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + case 5: /* FIS - not impl */ + setTRAP (TRAP_ILL); + break; + case 6: /* CIS - not impl */ + if (cpu_unit.flags & UNIT_CIS) cis11 (IR); + else setTRAP (TRAP_ILL); + break; + case 7: /* SOB */ + R[srcspec] = (R[srcspec] - 1) & 0177777; + if (R[srcspec]) { + JMP_PC ((PC - dstspec - dstspec) & 0177777); } + break; } /* end switch EIS */ + break; /* end case 007 */ + +/* Opcode 10: branches, traps, SOPs */ + +case 010: + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: case 001: /* BPL */ + if (N == 0) { BRANCH_F (IR); } + break; + case 002: case 003: /* BPL */ + if (N == 0) { BRANCH_B (IR); } + break; + case 004: case 005: /* BMI */ + if (N) { BRANCH_F (IR); } + break; + case 006: case 007: /* BMI */ + if (N) { BRANCH_B (IR); } + break; + case 010: case 011: /* BHI */ + if ((C | Z) == 0) { BRANCH_F (IR); } + break; + case 012: case 013: /* BHI */ + if ((C | Z) == 0) { BRANCH_B (IR); } + break; + case 014: case 015: /* BLOS */ + if (C | Z) { BRANCH_F (IR); } + break; + case 016: case 017: /* BLOS */ + if (C | Z) { BRANCH_B (IR); } + break; + case 020: case 021: /* BVC */ + if (V == 0) { BRANCH_F (IR); } + break; + case 022: case 023: /* BVC */ + if (V == 0) { BRANCH_B (IR); } + break; + case 024: case 025: /* BVS */ + if (V) { BRANCH_F (IR); } + break; + case 026: case 027: /* BVS */ + if (V) { BRANCH_B (IR); } + break; + case 030: case 031: /* BCC */ + if (C == 0) { BRANCH_F (IR); } + break; + case 032: case 033: /* BCC */ + if (C == 0) { BRANCH_B (IR); } + break; + case 034: case 035: /* BCS */ + if (C) { BRANCH_F (IR); } + break; + case 036: case 037: /* BCS */ + if (C) { BRANCH_B (IR); } + break; + case 040: case 041: case 042: case 043: /* EMT */ + setTRAP (TRAP_EMT); + break; + case 044: case 045: case 046: case 047: /* TRAP */ + setTRAP (TRAP_TRAP); + break; + +/* Opcode 10, continued: SOPs */ + + case 050: /* CLRB */ + N = V = C = 0; + Z = 1; + if (dstreg) R[dstspec] = R[dstspec] & 0177400; + else WriteB (0, GeteaB (dstspec)); + break; + case 051: /* COMB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst ^ 0377) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + C = 1; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 052: /* INCB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst + 1) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (dst == 0200); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 053: /* DECB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst - 1) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (dst == 0177); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 054: /* NEGB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (-dst) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (dst == 0200); + C = (Z ^ 1); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 055: /* ADCB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst + C) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (C && (dst == 0200)); + C = C & Z; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + +/* Opcode 10: SOPs, continued */ + + case 056: /* SBCB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst - C) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (C && (dst == 0177)); + C = (C && (dst == 0377)); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 057: /* TSTB */ + dst = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = C = 0; + break; + case 060: /* RORB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = ((src & 0377) >> 1) | (C << 7); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 061: /* ROLB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = ((src << 1) | C) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = GET_SIGN_B (src & 0377); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 062: /* ASRB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = ((src & 0377) >> 1) | (src & 0200); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + case 063: /* ASLB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (src << 1) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = GET_SIGN_B (src & 0377); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + +/* Opcode 10: SOPs, continued + + Notes: + - MTPS cannot alter the T bit + - MxPD must mask GeteaW returned address, dspace is from cm not pm + - MxPD must set MMR1 for SP recovery in case of fault +*/ + + case 064: /* MTPS */ + dst = dstreg? R[dstspec]: ReadB (GeteaB (dstspec)); + if (cm == KERNEL) { + ipl = (dst >> PSW_V_IPL) & 07; + trap_req = calc_ints (ipl, int_req, trap_req); } + N = (dst >> PSW_V_N) & 01; + Z = (dst >> PSW_V_Z) & 01; + V = (dst >> PSW_V_V) & 01; + C = (dst >> PSW_V_C) & 01; + break; + case 065: /* MFPD */ + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm]; + else dst = R[dstspec]; } + else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_ds (pm)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0366); + WriteW (dst, SP | dsenable); + if ((cm == KERNEL) && (SP < STKLIM)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + break; + case 066: /* MTPD */ + dst = ReadW (SP | dsenable); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP + 2) & 0177777; + if (update_MM) MMR1 = 026; + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst; + else R[dstspec] = dst; } + else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_ds (pm)); + break; + case 067: /* MFPS */ + dst = (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | + (N << PSW_V_N) | (Z << PSW_V_Z) | + (V << PSW_V_V) | (C << PSW_V_C); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst; + else WriteB (dst, GeteaB (dstspec)); + break; + default: + setTRAP (TRAP_ILL); + break; } /* end switch SOPs */ + break; /* end case 010 */ + +/* Opcodes 11 - 16: double operand byte instructions + + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] + Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)] +*/ + +case 011: /* MOVB */ + dst = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst; + else WriteB (dst, GeteaB (dstspec)); + break; +case 012: /* CMPB */ + src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); + dst = (src - src2) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = GET_SIGN_B ((src ^ src2) & (~src2 ^ dst)); + C = (src < src2); + break; +case 013: /* BITB */ + src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec]: ReadB (GeteaB (dstspec)); + dst = (src2 & src) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + break; +case 014: /* BICB */ + src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (src2 & ~src) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; +case 015: /* BISB */ + src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (src2 | src) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; +case 016: /* SUB */ + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src2 - src) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = GET_SIGN_W ((src ^ src2) & (~src ^ dst)); + C = (src2 < src); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Opcode 17: floating point */ + +case 017: + fp11 (IR); /* floating point */ + break; /* end case 017 */ + } /* end switch op */ +} /* end main loop */ + +/* Simulation halted */ + +PSW = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) | + (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | + (N << PSW_V_N) | (Z << PSW_V_Z) | (V << PSW_V_V) | (C << PSW_V_C); +for (i = 0; i < 6; i++) REGFILE[i][rs] = R[i]; +STACKFILE[cm] = SP; +saved_PC = PC & 0177777; +return reason; +} + +/* Effective address calculations + + Inputs: + spec = specifier <5:0> + Outputs: + ea = effective address + <15:0> = virtual address + <16> = instruction/data data space + <18:17> = mode + + Data space calculation: the PDP-11 features both instruction and data + spaces. Instruction space contains the instruction and any sequential + add ons (eg, immediates, absolute addresses). Data space contains all + data operands and indirect addresses. If data space is enabled, then + memory references are directed according to these rules: + + Mode Index ref Indirect ref Direct ref + 10..16 na na data + 17 na na instruction + 20..26 na na data + 27 na na instruction + 30..36 na data data + 37 na instruction (absolute) data + 40..46 na na data + 47 na na instruction + 50..56 na data data + 57 na instruction data + 60..67 instruction na data + 70..77 instruction data data + + According to the PDP-11 Architecture Handbook, MMR1 records all + autoincrement and autodecrement operations, including those which + explicitly reference the PC. For the J-11, this is only true for + autodecrement operands, autodecrement deferred operands, and + autoincrement destination operands that involve a write to memory. + The simulator follows the Handbook, for simplicity. + + Notes: + + - dsenable will direct a reference to data space if data space is enabled + - ds will direct a reference to data space if data space is enabled AND if + the specifier register is not PC; this is used for 17, 27, 37, 47, 57 + - Modes 2x, 3x, 4x, and 5x must update MMR1 if updating enabled + - Modes 46 and 56 must check for stack overflow if kernel mode +*/ + +/* Effective address calculation for words */ + +int32 GeteaW (int32 spec) +{ +int32 adr, reg, ds; + +reg = spec & 07; /* register number */ +ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ +switch (spec >> 3) { /* decode spec<5:3> */ +default: /* can't get here */ +case 1: /* (R) */ + return (R[reg] | ds); +case 2: /* (R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (020 | reg); + return (adr | ds); +case 3: /* @(R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (020 | reg); + adr = ReadW (adr | ds); + return (adr | dsenable); +case 4: /* -(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0360 | reg); + if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + return (adr | ds); +case 5: /* @-(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0360 | reg); + if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + adr = ReadW (adr | ds); + return (adr | dsenable); +case 6: /* d(r) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + return (((R[reg] + adr) & 0177777) | dsenable); +case 7: /* @d(R) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); + return (adr | dsenable); + } /* end switch */ +} + +/* Effective address calculation for bytes */ + +int32 GeteaB (int32 spec) +{ +int32 adr, reg, ds, delta; + +reg = spec & 07; /* reg number */ +ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ +switch (spec >> 3) { /* decode spec<5:3> */ +default: /* can't get here */ +case 1: /* (R) */ + return (R[reg] | ds); +case 2: /* (R)+ */ + delta = 1 + (reg >= 6); /* 2 if R6, PC */ + R[reg] = ((adr = R[reg]) + delta) & 0177777; + if (update_MM) MMR1 = calc_MMR1 ((delta << 3) | reg); + return (adr | ds); +case 3: /* @(R)+ */ + adr = R[reg]; + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (020 | reg); + adr = ReadW (adr | ds); + return (adr | dsenable); +case 4: /* -(R) */ + delta = 1 + (reg >= 6); /* 2 if R6, PC */ + adr = R[reg] = (R[reg] - delta) & 0177777; + if (update_MM) MMR1 = calc_MMR1 ((((-delta) & 037) << 3) | reg); + if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + return (adr | ds); +case 5: /* @-(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0360 | reg); + if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + adr = ReadW (adr | ds); + return (adr | dsenable); +case 6: /* d(r) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + return (((R[reg] + adr) & 0177777) | dsenable); +case 7: /* @d(R) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); + return (adr | dsenable); + } /* end switch */ +} + +/* Read byte and word routines, read only and read-modify-write versions + + Inputs: + va = virtual address, <18:16> = mode, I/D space + Outputs: + data = data read from memory or I/O space +*/ + +int32 ReadW (int32 va) +{ +int32 pa, data; + +if (va & 1) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); } +pa = relocR (va); /* relocate */ +if (ADDR_IS_MEM (pa)) return (M[pa >> 1]); /* memory address? */ +if (pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return data; +} + +int32 ReadB (int32 va) +{ +int32 pa, data; + +pa = relocR (va); /* relocate */ +if (ADDR_IS_MEM (pa)) return (va & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377; +if (pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return ((va & 1)? data >> 8: data) & 0377; +} + +int32 ReadMW (int32 va) +{ +int32 data; + +if (va & 1) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); } +last_pa = relocW (va); /* reloc, wrt chk */ +if (ADDR_IS_MEM (last_pa)) return (M[last_pa >> 1]); /* memory address? */ +if (last_pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return data; +} + +int32 ReadMB (int32 va) +{ +int32 data; + +last_pa = relocW (va); /* reloc, wrt chk */ +if (ADDR_IS_MEM (last_pa)) + return (va & 1? M[last_pa >> 1] >> 8: M[last_pa >> 1]) & 0377; +if (last_pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return ((va & 1)? data >> 8: data) & 0377; +} + +/* Write byte and word routines + + Inputs: + data = data to be written + va = virtual address, <18:16> = mode, I/D space, or + pa = physical address + Outputs: none +*/ + +void WriteW (int32 data, int32 va) +{ +int32 pa; + +if (va & 1) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); } +pa = relocW (va); /* relocate */ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + M[pa >> 1] = data; + return; } +if (pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return; +} + +void WriteB (int32 data, int32 va) +{ +int32 pa; + +pa = relocW (va); /* relocate */ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + if (va & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); + else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; + return; } +if (pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return; +} + +void PWriteW (int32 data, int32 pa) +{ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + M[pa >> 1] = data; + return; } +if (pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return; +} + +void PWriteB (int32 data, int32 pa) +{ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + if (pa & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); + else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; + return; } +if (pa < IOPAGEBASE) { /* I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); } +if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); } +return; +} + +/* Relocate virtual address, read access + + Inputs: + va = virtual address, <18:16> = mode, I/D space + Outputs: + pa = physical address + On aborts, this routine aborts back to the top level simulator + with an appropriate trap code. + + Notes: + - APRFILE[UNUSED] is all zeroes, forcing non-resident abort + - Aborts must update MMR0<15:13,6:1> if updating is enabled +*/ + +int32 relocR (int32 va) +{ +int32 dbn, plf, apridx, apr, pa; + +if (MMR0 & MMR0_MME) { /* if mmgt */ + apridx = (va >> VA_V_APF) & 077; /* index into APR */ + apr = APRFILE[apridx]; /* with va<18:13> */ + dbn = va & VA_BN; /* extr block num */ + plf = (apr & PDR_PLF) >> 2; /* extr page length */ + if ((apr & PDR_NR) == 0) { /* if non-resident */ + if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_NR; + ABORT (TRAP_MME); } /* abort ref */ + if ((apr & PDR_ED)? dbn < plf: dbn > plf) { /* if pg lnt error */ + if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_PL; + ABORT (TRAP_MME); } /* abort ref */ + pa = (va & VA_DF) + ((apr >> 10) & 017777700); + if ((MMR3 & MMR3_M22E) == 0) { + pa = pa & 0777777; + if (pa >= 0760000) pa = 017000000 | pa; } } +else { pa = va & 0177777; /* mmgt off */ + if (pa >= 0160000) pa = 017600000 | pa; } +return pa; +} + +/* Relocate virtual address, write access + + Inputs: + va = virtual address, <18:16> = mode, I/D space + Outputs: + pa = physical address + On aborts, this routine aborts back to the top level simulator + with an appropriate trap code. + + Notes: + - APRFILE[UNUSED] is all zeroes, forcing non-resident abort + - Aborts must update MMR0<15:13,6:1> if updating is enabled +*/ + +int32 relocW (int32 va) +{ +int32 dbn, plf, apridx, apr, pa; + +if (MMR0 & MMR0_MME) { /* if mmgt */ + apridx = (va >> VA_V_APF) & 077; /* index into APR */ + apr = APRFILE[apridx]; /* with va<18:13> */ + dbn = va & VA_BN; /* extr block num */ + plf = (apr & PDR_PLF) >> 2; /* extr page length */ + if ((apr & PDR_NR) == 0) { /* if non-resident */ + if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_NR; + ABORT (TRAP_MME); } /* abort ref */ + if ((apr & PDR_ED)? dbn < plf: dbn > plf) { /* if pg lnt error */ + if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_PL; + ABORT (TRAP_MME); } /* abort ref */ + if ((apr & PDR_RW) == 0) { /* if rd only error */ + if (update_MM) MMR0 = MMR0 | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_RO; + ABORT (TRAP_MME); } /* abort ref */ + APRFILE[apridx] = apr | PDR_W; /* set W */ + pa = (va & VA_DF) + ((apr >> 10) & 017777700); + if ((MMR3 & MMR3_M22E) == 0) { + pa = pa & 0777777; + if (pa >= 0760000) pa = 017000000 | pa; } } +else { pa = va & 0177777; /* mmgt off */ + if (pa >= 0160000) pa = 017600000 | pa; } +return pa; +} + +/* Relocate virtual address, console access + + Inputs: + va = virtual address + sw = switches + Outputs: + pa = physical address + On aborts, this routine returns -1 +*/ + +int32 relocC (int32 va, int32 sw) +{ +int32 mode, dbn, plf, apridx, apr, pa; + +if (MMR0 & MMR0_MME) { /* if mmgt */ + if (sw & SWMASK ('K')) mode = KERNEL; + else if (sw & SWMASK ('S')) mode = SUPER; + else if (sw & SWMASK ('U')) mode = USER; + else if (sw & SWMASK ('P')) mode = (PSW >> PSW_V_PM) & 03; + else mode = (PSW >> PSW_V_CM) & 03; + va = va | ((sw & SWMASK ('D'))? calc_ds (mode): calc_is (mode)); + apridx = (va >> VA_V_APF) & 077; /* index into APR */ + apr = APRFILE[apridx]; /* with va<18:13> */ + dbn = va & VA_BN; /* extr block num */ + plf = (apr & PDR_PLF) >> 2; /* extr page length */ + if ((apr & PDR_NR) == 0) return -1; + if ((apr & PDR_ED)? dbn < plf: dbn > plf) return -1; + pa = (va & VA_DF) + ((apr >> 10) & 017777700); + if ((MMR3 & MMR3_M22E) == 0) { + pa = pa & 0777777; + if (pa >= 0760000) pa = 017000000 | pa; } } +else { pa = va & 0177777; /* mmgt off */ + if (pa >= 0160000) pa = 017600000 | pa; } +return pa; +} + +/* I/O page lookup and linkage routines + + Inputs: + *data = pointer to data to read, if READ + data = data to store, if WRITE or WRITEB + pa = address + access = READ, WRITE, or WRITEB + Outputs: + status = SCPE_OK or SCPE_NXM +*/ + +t_stat iopageR (int32 *data, int32 pa, int32 access) +{ +t_stat stat; +struct iolink *p; + +for (p = &iotable[0]; p -> low != 0; p++ ) { + if ((pa >= p -> low) && (pa <= p -> high)) { + stat = p -> read (data, pa, access); + trap_req = calc_ints (ipl, int_req, trap_req); + return stat; } } +return SCPE_NXM; +} + +t_stat iopageW (int32 data, int32 pa, int32 access) +{ +t_stat stat; +struct iolink *p; + +for (p = &iotable[0]; p -> low != 0; p++ ) { + if ((pa >= p -> low) && (pa <= p -> high)) { + stat = p -> write (data, pa, access); + trap_req = calc_ints (ipl, int_req, trap_req); + return stat; } } +return SCPE_NXM; +} + +/* I/O page routines for CPU registers + + Switch register and memory management registers + + SR 17777570 read only + MMR0 17777572 read/write, certain bits unimplemented or read only + MMR1 17777574 read only + MMR2 17777576 read only + MMR3 17777516 read/write, certain bits unimplemented +*/ + +t_stat SR_MMR012_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 3) { /* decode pa<2:1> */ +case 0: /* SR */ + *data = SR; + return SCPE_OK; +case 1: /* MMR0 */ + *data = MMR0 & MMR0_IMP; + return SCPE_OK; +case 2: /* MMR1 */ + *data = MMR1; + return SCPE_OK; +case 3: /* MMR2 */ + *data = MMR2; + return SCPE_OK; } /* end switch pa */ +} + +t_stat SR_MMR012_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 3) { /* decode pa<2:1> */ +case 0: /* DR */ + DR = data; + return SCPE_OK; +case 1: /* MMR0 */ + if (access == WRITEB) data = (pa & 1)? + (MMR0 & 0377) | (data << 8): (MMR0 & ~0377) | data; + MMR0 = (MMR0 & ~MMR0_RW) | (data & MMR0_RW); + return SCPE_OK; +default: /* MMR1, MMR2 */ + return SCPE_OK; } /* end switch pa */ +} + +t_stat MMR3_rd (int32 *data, int32 pa, int32 access) /* MMR3 */ +{ +*data = MMR3 & MMR3_IMP; +return SCPE_OK; +} + +t_stat MMR3_wr (int32 data, int32 pa, int32 access) /* MMR3 */ +{ +if (pa & 1) return SCPE_OK; +MMR3 = data & MMR3_RW; +if (cpu_unit.flags & UNIT_18B) + MMR3 = MMR3 & ~(MMR3_BME + MMR3_M22E); /* for UNIX V6 */ +dsenable = calc_ds (cm); +return SCPE_OK; +} + +/* PARs and PDRs. These are grouped in I/O space as follows: + + 17772200 - 17772276 supervisor block + 17772300 - 17772376 kernel block + 17777600 - 17777676 user block + + Within each block, the subblocks are I PDR's, D PDR's, I PAR's, D PAR's + + Thus, the algorithm for converting between I/O space addresses and + APRFILE indices is as follows: + + idx<3:0> = dspace'page = pa<4:1> + par = PDR vs PAR = pa<5> + idx<5:4> = ker/sup/user = pa<8>'~pa<6> + + Note that the W bit is read only; it is cleared by any write to an APR +*/ + +t_stat APR_rd (int32 *data, int32 pa, int32 access) +{ +t_stat left, idx; + +idx = (pa >> 1) & 017; /* dspace'page */ +left = (pa >> 5) & 1; /* PDR vs PAR */ +if ((pa & 0100) == 0) idx = idx | 020; /* 1 for super, user */ +if (pa & 0400) idx = idx | 040; /* 1 for user only */ +*data = left? (APRFILE[idx] >> 16) & 0177777: APRFILE[idx] & PDR_IMP; +return SCPE_OK; +} + +t_stat APR_wr (int32 data, int32 pa, int32 access) +{ +int32 left, idx, curr; + +idx = (pa >> 1) & 017; /* dspace'page */ +left = (pa >> 5) & 1; /* PDR vs PAR */ +if ((pa & 0100) == 0) idx = idx | 020; /* 1 for super, user */ +if (pa & 0400) idx = idx | 040; /* 1 for user only */ +curr = left? (APRFILE[idx] >> 16) & 0177777: APRFILE[idx] & PDR_IMP; +if (access == WRITEB) data = (pa & 1)? + (curr & 0377) | (data << 8): (curr & ~0377) | data; +if (left) APRFILE[idx] = + ((APRFILE[idx] & 0177777) | (data << 16)) & ~PDR_W; +else APRFILE[idx] = + ((APRFILE[idx] & ~PDR_RW) | (data & PDR_RW)) & ~PDR_W; +return SCPE_OK; +} + +/* CPU control registers + + MEMERR 17777744 read only, clear on write + CCR 17777746 read/write + MAINT 17777750 read only + HITMISS 17777752 read only + CPUERR 17777766 read only, clear on write + PIRQ 17777772 read/write, with side effects + PSW 17777776 read/write, with side effects +*/ + +t_stat CPU_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ +case 2: /* MEMERR */ + *data = MEMERR; + MEMERR = 0; + return SCPE_OK; +case 3: /* CCR */ + *data = CCR; + return SCPE_OK; +case 4: /* MAint */ + *data = MAINT; + return SCPE_OK; +case 5: /* Hit/miss */ + *data = HITMISS; + return SCPE_OK; +case 013: /* CPUERR */ + *data = CPUERR & CPUE_IMP; + CPUERR = 0; + return SCPE_OK; +case 015: /* PIRQ */ + *data = PIRQ; + return SCPE_OK; +case 017: /* PSW */ + if (access == READC) *data = PSW; + else *data = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) | + (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | + (N << PSW_V_N) | (Z << PSW_V_Z) | + (V << PSW_V_V) | (C << PSW_V_C); + return SCPE_OK; } /* end switch PA */ +return SCPE_NXM; /* unimplemented */ +} + +/* CPU control registers, continued */ + +t_stat CPU_wr (int32 data, int32 pa, int32 access) +{ +int32 i, pl, curr, oldrs; + +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ +case 2: /* MEMERR */ + MEMERR = 0; + return SCPE_OK; +case 3: /* CCR */ + if (access == WRITEB) data = (pa & 1)? + (CCR & 0377) | (data << 8): (CCR & ~0377) | data; + CCR = data; + return SCPE_OK; +case 4: /* MAINT */ + return SCPE_OK; +case 5: /* Hit/miss */ + return SCPE_OK; +case 013: /* CPUERR */ + CPUERR = 0; + return SCPE_OK; +case 015: /* PIRQ */ + if (access == WRITEB) { + if (pa & 1) data = data << 8; + else return SCPE_OK; } + int_req = int_req & ~(INT_PIR7 + INT_PIR6 + INT_PIR5 + INT_PIR4 + + INT_PIR3 + INT_PIR2 + INT_PIR1); + PIRQ = data & PIRQ_RW; + pl = 0; + if (PIRQ & PIRQ_PIR1) { int_req = int_req | INT_PIR1; pl = 0042; } + if (PIRQ & PIRQ_PIR2) { int_req = int_req | INT_PIR2; pl = 0104; } + if (PIRQ & PIRQ_PIR3) { int_req = int_req | INT_PIR3; pl = 0146; } + if (PIRQ & PIRQ_PIR4) { int_req = int_req | INT_PIR4; pl = 0210; } + if (PIRQ & PIRQ_PIR5) { int_req = int_req | INT_PIR5; pl = 0252; } + if (PIRQ & PIRQ_PIR6) { int_req = int_req | INT_PIR6; pl = 0314; } + if (PIRQ & PIRQ_PIR7) { int_req = int_req | INT_PIR7; pl = 0356; } + PIRQ = PIRQ | pl; + return SCPE_OK; + +/* CPU control registers, continued + + Note: Explicit writes to the PSW do not modify the T bit +*/ + +case 017: /* PSW */ + if (access == WRITEC) { /* console access? */ + PSW = data & PSW_RW; + return SCPE_OK; } + curr = (cm << PSW_V_CM) | (pm << PSW_V_PM) | (rs << PSW_V_RS) | + (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | + (N << PSW_V_N) | (Z << PSW_V_Z) | + (V << PSW_V_V) | (C << PSW_V_C); + STACKFILE[cm] = SP; + if (access == WRITEB) data = (pa & 1)? + (curr & 0377) | (data << 8): (curr & ~0377) | data; + curr = (curr & ~PSW_RW) | (data & PSW_RW); + oldrs = rs; + cm = (curr >> PSW_V_CM) & 03; /* call calc_is,ds */ + pm = (curr >> PSW_V_PM) & 03; + rs = (curr >> PSW_V_RS) & 01; + ipl = (curr >> PSW_V_IPL) & 07; + N = (curr >> PSW_V_N) & 01; + Z = (curr >> PSW_V_Z) & 01; + V = (curr >> PSW_V_V) & 01; + C = (curr >> PSW_V_C) & 01; + if (rs != oldrs) { + for (i = 0; i < 6; i++) { + REGFILE[i][oldrs] = R[i]; + R[i] = REGFILE[i][rs]; } } + SP = STACKFILE[cm]; + isenable = calc_is (cm); + dsenable = calc_ds (cm); + return SCPE_OK; } /* end switch pa */ +return SCPE_NXM; /* unimplemented */ +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +PIRQ = MMR0 = MMR1 = MMR2 = MMR3 = 0; +DR = CPUERR = MEMERR = CCR = HITMISS = 0; +PSW = 000340; +trap_req = 0; +wait_state = 0; +if (M == NULL) M = calloc (MEMSIZE >> 1, sizeof (unsigned int16)); +if (M == NULL) return SCPE_MEM; +return cpu_svc (&cpu_unit); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 iodata; +t_stat stat; + +if (vptr == NULL) return SCPE_ARG; +if (sw & SWMASK ('V')) { /* -v */ + if (addr >= VASIZE) return SCPE_NXM; + addr = relocC (addr, sw); /* relocate */ + if (addr < 0) return SCPE_REL; } +if (addr < MEMSIZE) { + *vptr = M[addr >> 1] & 0177777; + return SCPE_OK; } +if (addr < IOPAGEBASE) return SCPE_NXM; +stat = iopageR (&iodata, addr, READC); +*vptr = iodata; +return stat; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (sw & SWMASK ('V')) { /* -v */ + if (addr >= VASIZE) return SCPE_NXM; + addr = relocC (addr, sw); /* relocate */ + if (addr < 0) return SCPE_REL; } +if (addr < MEMSIZE) { + M[addr >> 1] = val & 0177777; + return SCPE_OK; } +if (addr < IOPAGEBASE) return SCPE_NXM; +return iopageW ((int32) val, addr, WRITEC); +} + +/* Breakpoint service */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +/* Memory allocation */ + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i, clim; +unsigned int16 *nM = NULL; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i = i + 2) mc = mc | M[i >> 1]; +if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; +nM = calloc (value >> 1, sizeof (unsigned int16)); +if (nM == NULL) return SCPE_MEM; +clim = (((t_addr) value) < MEMSIZE)? value: MEMSIZE; +for (i = 0; i < clim; i = i + 2) nM[i >> 1] = M[i >> 1]; +free (M); +M = nM; +MEMSIZE = value; +return SCPE_OK; } diff --git a/pdp11_defs.h b/pdp11_defs.h new file mode 100644 index 00000000..fda91741 --- /dev/null +++ b/pdp11_defs.h @@ -0,0 +1,312 @@ +/* pdp11_defs.h: PDP-11 simulator definitions + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The author gratefully acknowledges the help of Max Burnet, Megan Gentry, + and John Wilson in resolving questions about the PDP-11 +*/ + +#include "sim_defs.h" /* simulator defns */ +#include + +/* Constants */ + +#define STKLIM 0400 /* stack limit */ +#define VASIZE 0200000 /* 2**16 */ +#define VAMASK (VASIZE - 1) /* 2**16 - 1 */ +#define INIMEMSIZE 001000000 /* 2**18 */ +#define IOPAGEBASE 017760000 /* 2**22 - 2**13 */ +#define MAXMEMSIZE 020000000 /* 2**22 */ +#define MEMSIZE (cpu_unit.capac) +#define ADDR_IS_MEM(x) (((t_addr) (x)) < MEMSIZE) + +/* Protection modes */ + +#define KERNEL 0 +#define SUPER 1 +#define UNUSED 2 +#define USER 3 + +/* I/O access modes */ + +#define READ 0 +#define READC 1 /* read console */ +#define WRITE 2 +#define WRITEC 3 /* write console */ +#define WRITEB 4 + +/* PSW */ + +#define PSW_V_C 0 /* condition codes */ +#define PSW_V_V 1 +#define PSW_V_Z 2 +#define PSW_V_N 3 +#define PSW_V_TBIT 4 /* trace trap */ +#define PSW_V_IPL 5 /* int priority */ +#define PSW_V_RS 11 /* register set */ +#define PSW_V_PM 12 /* previous mode */ +#define PSW_V_CM 14 /* current mode */ +#define PSW_RW 0174357 /* read/write bits */ + +/* FPS */ + +#define FPS_V_C 0 /* condition codes */ +#define FPS_V_V 1 +#define FPS_V_Z 2 +#define FPS_V_N 3 +#define FPS_V_T 5 /* truncate */ +#define FPS_V_L 6 /* long */ +#define FPS_V_D 7 /* double */ +#define FPS_V_IC 8 /* ic err int */ +#define FPS_V_IV 9 /* overflo err int */ +#define FPS_V_IU 10 /* underflo err int */ +#define FPS_V_IUV 11 /* undef var err int */ +#define FPS_V_ID 14 /* int disable */ +#define FPS_V_ER 15 /* error */ + +/* PIRQ */ + +#define PIRQ_PIR1 0001000 +#define PIRQ_PIR2 0002000 +#define PIRQ_PIR3 0004000 +#define PIRQ_PIR4 0010000 +#define PIRQ_PIR5 0020000 +#define PIRQ_PIR6 0040000 +#define PIRQ_PIR7 0100000 +#define PIRQ_IMP 0177356 /* implemented bits */ +#define PIRQ_RW 0177000 /* read/write bits */ + +/* MMR0 */ + +#define MMR0_MME 0000001 /* mem mgt enable */ +#define MMR0_V_PAGE 1 /* offset to pageno */ +#define MMR0_RO 0020000 /* read only error */ +#define MMR0_PL 0040000 /* page lnt error */ +#define MMR0_NR 0100000 /* no access error */ +#define MMR0_FREEZE 0160000 /* if set, no update */ +#define MMR0_IMP 0160177 /* implemented bits */ +#define MMR0_RW 0160001 /* read/write bits */ + +/* MMR3 */ + +#define MMR3_UDS 001 /* user dspace enbl */ +#define MMR3_SDS 002 /* super dspace enbl */ +#define MMR3_KDS 004 /* krnl dspace enbl */ +#define MMR3_CSM 010 /* CSM enable */ +#define MMR3_M22E 020 /* 22b mem mgt enbl */ +#define MMR3_BME 040 /* DMA bus map enbl */ +#define MMR3_IMP 077 /* implemented bits */ +#define MMR3_RW 077 /* read/write bits */ + +/* PDR */ + +#define PDR_NR 0000002 /* non-resident ACF */ +#define PDR_ED 0000010 /* expansion dir */ +#define PDR_W 0000100 /* written flag */ +#define PDR_PLF 0077400 /* page lnt field */ +#define PDR_IMP 0177516 /* implemented bits */ +#define PDR_RW 0177416 /* read/write bits */ + +/* Virtual address */ + +#define VA_DF 0017777 /* displacement */ +#define VA_BN 0017700 /* block number */ +#define VA_V_APF 13 /* offset to APF */ +#define VA_V_DS 16 /* offset to space */ +#define VA_V_MODE 17 /* offset to mode */ +#define VA_DS (1u << VA_V_DS) /* data space flag */ + +/* CPUERR */ + +#define CPUE_RED 0004 /* red stack */ +#define CPUE_YEL 0010 /* yellow stack */ +#define CPUE_TMO 0020 /* IO page nxm */ +#define CPUE_NXM 0040 /* memory nxm */ +#define CPUE_ODD 0100 /* odd address */ +#define CPUE_HALT 0200 /* HALT not kernel */ +#define CPUE_IMP 0374 /* implemented bits */ + +/* Floating point accumulators */ + +struct fpac { + unsigned int32 l; /* low 32b */ + unsigned int32 h; /* high 32b */ +}; +typedef struct fpac fpac_t; + +/* Device CSRs */ + +#define CSR_V_GO 0 /* go */ +#define CSR_V_IE 6 /* interrupt enable */ +#define CSR_V_DONE 7 /* done */ +#define CSR_V_BUSY 11 /* busy */ +#define CSR_V_ERR 15 /* error */ +#define CSR_GO (1u << CSR_V_GO) +#define CSR_IE (1u << CSR_V_IE) +#define CSR_DONE (1u << CSR_V_DONE) +#define CSR_BUSY (1u << CSR_V_BUSY) +#define CSR_ERR (1u << CSR_V_ERR) + +/* Simulator stop codes; codes 1:TRAP_V_MAX correspond to traps 0:TRAPMAX-1 */ + +#define STOP_HALT TRAP_V_MAX + 1 /* HALT instruction */ +#define STOP_IBKPT TRAP_V_MAX + 2 /* instruction bkpt */ +#define STOP_WAIT TRAP_V_MAX + 3 /* wait, no events */ +#define STOP_VECABORT TRAP_V_MAX + 4 /* abort vector read */ +#define STOP_SPABORT TRAP_V_MAX + 5 /* abort trap push */ +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +/* Trap masks, descending priority order, following J-11 + An interrupt summary bit is kept with traps, to minimize overhead +*/ + +#define TRAP_V_RED 0 /* red stk abort 4 */ +#define TRAP_V_ODD 1 /* odd address 4 */ +#define TRAP_V_MME 2 /* mem mgt 250 */ +#define TRAP_V_NXM 3 /* nx memory 4 */ +#define TRAP_V_PAR 4 /* parity err 114 */ +#define TRAP_V_PRV 5 /* priv inst 4 */ +#define TRAP_V_ILL 6 /* illegal inst 10 */ +#define TRAP_V_BPT 7 /* BPT 14 */ +#define TRAP_V_IOT 8 /* IOT 20 */ +#define TRAP_V_EMT 9 /* EMT 30 */ +#define TRAP_V_TRAP 10 /* TRAP 34 */ +#define TRAP_V_TRC 11 /* T bit 14 */ +#define TRAP_V_YEL 12 /* stack 4 */ +#define TRAP_V_PWRFL 13 /* power fail 24 */ +#define TRAP_V_FPE 14 /* fpe 244 */ +#define TRAP_V_MAX 15 /* intr = max trp # */ +#define TRAP_RED (1u << TRAP_V_RED) +#define TRAP_ODD (1u << TRAP_V_ODD) +#define TRAP_MME (1u << TRAP_V_MME) +#define TRAP_NXM (1u << TRAP_V_NXM) +#define TRAP_PAR (1u << TRAP_V_PAR) +#define TRAP_PRV (1u << TRAP_V_PRV) +#define TRAP_ILL (1u << TRAP_V_ILL) +#define TRAP_BPT (1u << TRAP_V_BPT) +#define TRAP_IOT (1u << TRAP_V_IOT) +#define TRAP_EMT (1u << TRAP_V_EMT) +#define TRAP_TRAP (1u << TRAP_V_TRAP) +#define TRAP_TRC (1u << TRAP_V_TRC) +#define TRAP_YEL (1u << TRAP_V_YEL) +#define TRAP_PWRFL (1u << TRAP_V_PWRFL) +#define TRAP_FPE (1u << TRAP_V_FPE) +#define TRAP_INT (1u << TRAP_V_MAX) +#define TRAP_ALL ((1u << TRAP_V_MAX) - 1) /* all traps */ + +#define VEC_RED 0004 /* trap vectors */ +#define VEC_ODD 0004 +#define VEC_MME 0250 +#define VEC_NXM 0004 +#define VEC_PAR 0114 +#define VEC_PRV 0004 +#define VEC_ILL 0010 +#define VEC_BPT 0014 +#define VEC_IOT 0020 +#define VEC_EMT 0030 +#define VEC_TRAP 0034 +#define VEC_TRC 0014 +#define VEC_YEL 0004 +#define VEC_PWRFL 0024 +#define VEC_FPE 0244 + +/* Interrupt assignments, priority is right to left + + <3:0> = BR7, <3> = PIR7 + <7:4> = BR6, <7> = PIR6 + <15:8> = BR5, <15> = PIR5 + <28:16> = BR4, <28> = PIR4 + <29> = PIR3 + <30> = PIR2 + <31> = PIR1 +*/ + +#define INT_V_PIR7 3 +#define INT_V_CLK 4 +#define INT_V_PIR6 7 +#define INT_V_RK 8 +#define INT_V_RL 9 +#define INT_V_RX 10 +#define INT_V_TM 11 +#define INT_V_RP 12 +#define INT_V_PIR5 15 +#define INT_V_TTI 16 +#define INT_V_TTO 17 +#define INT_V_PTR 18 +#define INT_V_PTP 19 +#define INT_V_LPT 20 +#define INT_V_PIR4 28 +#define INT_V_PIR3 29 +#define INT_V_PIR2 30 +#define INT_V_PIR1 31 + +#define INT_PIR7 (1u << INT_V_PIR7) +#define INT_CLK (1u << INT_V_CLK) +#define INT_PIR6 (1u << INT_V_PIR6) +#define INT_RK (1u << INT_V_RK) +#define INT_RL (1u << INT_V_RL) +#define INT_RX (1u << INT_V_RX) +#define INT_TM (1u << INT_V_TM) +#define INT_RP (1u << INT_V_RP) +#define INT_PIR5 (1u << INT_V_PIR5) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_TTI (1u << INT_V_TTI) +#define INT_TTO (1u << INT_V_TTO) +#define INT_LPT (1u << INT_V_LPT) +#define INT_PIR4 (1u << INT_V_PIR4) +#define INT_PIR3 (1u << INT_V_PIR3) +#define INT_PIR2 (1u << INT_V_PIR2) +#define INT_PIR1 (1u << INT_V_PIR1) + +#define INT_IPL7 0x00000000 /* int level masks */ +#define INT_IPL6 0x0000000F +#define INT_IPL5 0x000000FF +#define INT_IPL4 0x0000FFFF +#define INT_IPL3 0x1FFFFFFF +#define INT_IPL2 0x3FFFFFFF +#define INT_IPL1 0x7FFFFFFF +#define INT_IPL0 0xFFFFFFFF + +#define VEC_PIRQ 0240 /* interrupt vectors */ +#define VEC_TTI 0060 +#define VEC_TTO 0064 +#define VEC_PTR 0070 +#define VEC_PTP 0074 +#define VEC_CLK 0100 +#define VEC_LPT 0200 +#define VEC_RK 0220 +#define VEC_RL 0160 +#define VEC_RX 0264 +#define VEC_TM 0224 +#define VEC_RP 0254 + +/* CPU and FPU macros */ + +#define update_MM ((MMR0 & (MMR0_FREEZE + MMR0_MME)) == MMR0_MME) +#define setTRAP(name) trap_req = trap_req | (name) +#define setCPUERR(name) CPUERR = CPUERR | (name) +#define ABORT(val) longjmp (save_env, (val)) +#define SP R[6] +#define PC R[7] diff --git a/pdp11_fp.c b/pdp11_fp.c new file mode 100644 index 00000000..044e9a38 --- /dev/null +++ b/pdp11_fp.c @@ -0,0 +1,997 @@ +/* pdp11_fp.c: PDP-11 floating point simulator (32b version) + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 05-Jun-98 RMS Fixed implementation specific shift bugs + 20-Apr-98 RMS Fixed bug in MODf integer truncation + 17-Apr-98 RMS Fixed bug in STCfi range check + 16-Apr-98 RMS Fixed bugs in STEXP, STCfi, round/pack + 9-Apr-98 RMS Fixed bug in LDEXP + 4-Apr-98 RMS Fixed bug in MODf condition codes + + This module simulates the PDP-11 floating point unit (FP11 series). + It is called from the instruction decoder for opcodes 170000:177777. + + The floating point unit recognizes three instruction formats: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand + | 1 1 1 1| 0 0 0 0 0 0| opcode | 170000: + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170077 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ one operand + | 1 1 1 1| 0 0 0| opcode | dest spec | 170100: + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand + | 1 1 1 1| opcode | fac | dest spec | 171000: + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 177777 + + The instruction space is further extended through use of the floating + point status register (FPS) mode bits. Three mode bits affect how + instructions are interpreted: + + FPS_D if 0, floating registers are single precision + if 1, floating registers are double precision + + FPS_L if 0, integer operands are word + if 1, integer operands are longword + + FPS_T if 0, floating operations are rounded + if 1, floating operations are truncated + + FPS also contains the condition codes for the floating point unit, + and exception enable bits for individual error conditions. Exceptions + cause a trap through 0244, unless the individual exception, or all + exceptions, are disabled. Illegal address mode, undefined variable, + and divide by zero abort the current instruction; all other exceptions + permit the instruction to complete. (Aborts are implemented as traps + that request an "interrupt" trap. If an interrupt is pending, it is + serviced; if not, trap_req is updated and processing continues.) + + Floating point specifiers are similar to integer specifiers, with + the length of the operand being up to 8 bytes. In two specific cases, + the floating point unit reads or writes only two bytes, rather than + the length specified by the operand type: + + register for integers, only 16b are accessed; if the + operand is 32b, these are the high order 16b + of the operand + + immediate for integers or floating point, only 16b are + accessed; if the operand is 32b or 64b, these + are the high order 16b of the operand +*/ + +#include "pdp11_defs.h" + +/* Floating point status register */ + +#define FPS_ER (1u << FPS_V_ER) /* error */ +#define FPS_ID (1u << FPS_V_ID) /* interrupt disable */ +#define FPS_IUV (1u << FPS_V_IUV) /* int on undef var */ +#define FPS_IU (1u << FPS_V_IU) /* int on underflow */ +#define FPS_IV (1u << FPS_V_IV) /* int on overflow */ +#define FPS_IC (1u << FPS_V_IC) /* int on conv error */ +#define FPS_D (1u << FPS_V_D) /* single/double */ +#define FPS_L (1u << FPS_V_L) /* word/long */ +#define FPS_T (1u << FPS_V_T) /* round/truncate */ +#define FPS_N (1u << FPS_V_N) +#define FPS_Z (1u << FPS_V_Z) +#define FPS_V (1u << FPS_V_V) +#define FPS_C (1u << FPS_V_C) +#define FPS_CC (FPS_N + FPS_Z + FPS_V + FPS_C) +#define FPS_RW (FPS_ER + FPS_ID + FPS_IUV + FPS_IU + FPS_IV + \ + FPS_IC + FPS_D + FPS_L + FPS_T + FPS_CC) + +/* Floating point exception codes */ + +#define FEC_OP 2 /* illegal op/mode */ +#define FEC_DZRO 4 /* divide by zero */ +#define FEC_ICVT 6 /* conversion error */ +#define FEC_OVFLO 8 /* overflow */ +#define FEC_UNFLO 10 /* underflow */ +#define FEC_UNDFV 12 /* undef variable */ + +/* Floating point format, all assignments 32b relative */ + +#define FP_V_SIGN (63 - 32) /* high lw: sign */ +#define FP_V_EXP (55 - 32) /* exponent */ +#define FP_V_HB FP_V_EXP /* hidden bit */ +#define FP_V_F0 (48 - 32) /* fraction 0 */ +#define FP_V_F1 (32 - 32) /* fraction 1 */ +#define FP_V_FROUND (31 - 32) /* f round point */ +#define FP_V_F2 16 /* low lw: fraction 2 */ +#define FP_V_F3 0 /* fraction 3 */ +#define FP_V_DROUND (-1) /* d round point */ +#define FP_M_EXP 0377 +#define FP_SIGN (1u << FP_V_SIGN) +#define FP_EXP (FP_M_EXP << FP_V_EXP) +#define FP_HB (1u << FP_V_HB) +#define FP_FRACH ((1u << FP_V_HB) - 1) +#define FP_FRACL 0xFFFFFFFF +#define FP_BIAS 0200 /* exponent bias */ +#define FP_GUARD 3 /* guard bits */ + +/* Data lengths */ + +#define WORD 2 +#define LONG 4 +#define QUAD 8 + +/* Double precision operations on 64b quantities */ + +#define F_LOAD(qd,ac,ds) ds.h = ac.h; ds.l = (qd)? ac.l: 0 +#define F_LOAD_P(qd,ac,ds) ds -> h = ac.h; ds -> l = (qd)? ac.l: 0 +#define F_LOAD_FRAC(qd,ac,ds) ds.h = (ac.h & FP_FRACH) | FP_HB; \ + ds.l = (qd)? ac.l: 0 +#define F_STORE(qd,sr,ac) ac.h = sr.h; if ((qd)) ac.l = sr.l +#define F_STORE_P(qd,sr,ac) ac.h = sr -> h; if ((qd)) ac.l = sr -> l +#define F_GET_FRAC_P(sr,ds) ds.l = sr -> l; \ + ds.h = (sr -> h & FP_FRACH) | FP_HB +#define F_ADD(s2,s1,ds) ds.l = (s1.l + s2.l) & 0xFFFFFFFF; \ + ds.h = (s1.h + s2.h + (ds.l < s2.l)) & 0xFFFFFFFF +#define F_SUB(s2,s1,ds) ds.h = (s1.h - s2.h - (s1.l < s2.l)) & 0xFFFFFFFF; \ + ds.l = (s1.l - s2.l) & 0xFFFFFFFF +#define F_LT(x,y) ((x.h < y.h) || ((x.h == y.h) && (x.l < y.l))) +#define F_LT_AP(x,y) (((x -> h & ~FP_SIGN) < (y -> h & ~FP_SIGN)) || \ + (((x -> h & ~FP_SIGN) == (y -> h & ~FP_SIGN)) && (x -> l < y -> l))) +#define F_LSH_V(sr,n,ds) \ + ds.h = (((n) >= 32)? (sr.l << ((n) - 32)): \ + (sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \ + & 0xFFFFFFFF; \ + ds.l = ((n) >= 32)? 0: (sr.l << (n)) & 0xFFFFFFFF +#define F_RSH_V(sr,n,ds) \ + ds.l = (((n) >= 32)? (sr.h >> ((n) - 32)) & and_mask[64 - (n)]: \ + ((sr.l >> (n)) & and_mask[32 - (n)]) | \ + (sr.h << (32 - (n)))) & 0xFFFFFFFF; \ + ds.h = ((n) >= 32)? 0: \ + ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF + +/* For the constant shift macro, arguments must in the range [2,31] */ + +#define F_LSH_1(ds) ds.h = ((ds.h << 1) | ((ds.l >> 31) & 1)) & 0xFFFFFFFF; \ + ds.l = (ds.l << 1) & 0xFFFFFFFF +#define F_RSH_1(ds) ds.l = ((ds.l >> 1) & 0x7FFFFFFF) | ((ds.h & 1) << 31); \ + ds.h = ((ds.h >> 1) & 0x7FFFFFFF) +#define F_LSH_K(sr,n,ds) \ + ds.h = ((sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \ + & 0xFFFFFFFF; \ + ds.l = (sr.l << (n)) & 0xFFFFFFFF +#define F_RSH_K(sr,n,ds) \ + ds.l = (((sr.l >> (n)) & and_mask[32 - (n)]) | \ + (sr.h << (32 - (n)))) & 0xFFFFFFFF; \ + ds.h = ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF +#define F_LSH_GUARD(ds) F_LSH_K(ds,FP_GUARD,ds) +#define F_RSH_GUARD(ds) F_RSH_K(ds,FP_GUARD,ds) + +#define GET_BIT(ir,n) (((ir) >> n) & 1) +#define GET_SIGN(ir) GET_BIT((ir), FP_V_SIGN) +#define GET_EXP(ir) (((ir) >> FP_V_EXP) & FP_M_EXP) +#define GET_SIGN_L(ir) GET_BIT((ir), 31) +#define GET_SIGN_W(ir) GET_BIT((ir), 15) + +extern jmp_buf save_env; +extern int32 FEC, FEA, FPS; +extern int32 CPUERR, trap_req; +extern int32 N, Z, V, C; +extern int32 R[8]; +extern fpac_t FR[6]; +extern int32 GeteaW (int32 spec); +extern int32 ReadW (int32 addr); +extern void WriteW (int32 data, int32 addr); +fpac_t zero_fac = { 0, 0 }; +fpac_t one_fac = { 1, 0 }; +fpac_t fround_fac = { (1u << (FP_V_FROUND + 32)), 0 }; +fpac_t fround_guard_fac = { 0, (1u << (FP_V_FROUND + FP_GUARD)) }; +fpac_t dround_guard_fac = { (1u << (FP_V_DROUND + FP_GUARD)), 0 }; +fpac_t fmask_fac = { 0xFFFFFFFF, (1u << (FP_V_HB + FP_GUARD + 1)) - 1 }; +static const unsigned int32 and_mask[33] = { 0, + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF }; +int32 backup_PC; +int32 fpnotrap (int32 code); +int32 GeteaFP (int32 spec, int32 len); +unsigned int32 ReadI (int32 addr, int32 spec, int32 len); +void ReadFP (fpac_t *fac, int32 addr, int32 spec, int32 len); +void WriteI (int32 data, int32 addr, int32 spec, int32 len); +void WriteFP (fpac_t *data, int32 addr, int32 spec, int32 len); +int32 setfcc (int32 old_status, int32 result_high, int32 newV); +int32 addfp11 (fpac_t *src1, fpac_t *src2); +int32 mulfp11 (fpac_t *src1, fpac_t *src2); +int32 divfp11 (fpac_t *src1, fpac_t *src2); +int32 modfp11 (fpac_t *src1, fpac_t *src2, fpac_t *frac); +void frac_mulfp11 (fpac_t *src1, fpac_t *src2); +int32 roundfp11 (fpac_t *src); +int32 round_and_pack (fpac_t *fac, int32 exp, fpac_t *frac, int r); + +/* Set up for instruction decode and execution */ + +void fp11 (int32 IR) +{ +int32 dst, ea, ac, dstspec; +int32 i, qdouble, lenf, leni; +int32 newV, exp, sign; +fpac_t fac, fsrc, modfrac; +static const unsigned int32 i_limit[2][2] = + { { 0x80000000, 0x80010000 }, { 0x80000000, 0x80000001 } }; + +backup_PC = PC; /* save PC for FEA */ +ac = (IR >> 6) & 03; /* fac is IR<7:6> */ +dstspec = IR & 077; +qdouble = FPS & FPS_D; +lenf = qdouble? QUAD: LONG; +switch ((IR >> 8) & 017) { /* decode IR<11:8> */ +case 0: + switch (ac) { /* decode IR<7:6> */ + case 0: /* specials */ + if (IR == 0170000) { /* CFCC */ + N = (FPS >> PSW_V_N) & 1; + Z = (FPS >> PSW_V_Z) & 1; + V = (FPS >> PSW_V_V) & 1; + C = (FPS >> PSW_V_C) & 1; } + else if (IR == 0170001) /* SETF */ + FPS = FPS & ~FPS_D; + else if (IR == 0170002) /* SETI */ + FPS = FPS & ~FPS_L; + else if (IR == 0170011) /* SETD */ + FPS = FPS | FPS_D; + else if (IR == 0170012) /* SETL */ + FPS = FPS | FPS_L; + else fpnotrap (FEC_OP); + break; + case 1: /* LDFPS */ + dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec)); + FPS = dst & FPS_RW; + break; + case 2: /* STFPS */ + FPS = FPS & FPS_RW; + if (dstspec <= 07) R[dstspec] = FPS; + else WriteW (FPS, GeteaW (dstspec)); + break; + case 3: /* STST */ + if (dstspec <= 07) R[dstspec] = FEC; + else WriteI ((FEC << 16) | FEA, GeteaFP (dstspec, LONG), + dstspec, LONG); + break; } /* end switch <7:6> */ + break; /* end case 0 */ + +/* "Easy" instructions */ + +case 1: + switch (ac) { /* decode IR<7:6> */ + case 0: /* CLRf */ + WriteFP (&zero_fac, GeteaFP (dstspec, lenf), dstspec, lenf); + FPS = (FPS & ~FPS_CC) | FPS_Z; + break; + case 1: /* TSTf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + FPS = setfcc (FPS, fsrc.h, 0); + break; + case 2: /* ABSf */ + ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + else fsrc.h = fsrc.h & ~FP_SIGN; + WriteFP (&fsrc, ea, dstspec, lenf); + FPS = setfcc (FPS, fsrc.h, 0); + break; + case 3: /* NEGf */ + ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + else fsrc.h = fsrc.h ^ FP_SIGN; + WriteFP (&fsrc, ea, dstspec, lenf); + FPS = setfcc (FPS, fsrc.h, 0); + break; } /* end switch <7:6> */ + break; /* end case 1 */ +case 5: /* LDf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_STORE (qdouble, fsrc, FR[ac]); + FPS = setfcc (FPS, fsrc.h, 0); + break; +case 010: /* STf */ + F_LOAD (qdouble, FR[ac], fac); + WriteFP (&fac, GeteaFP (dstspec, lenf), dstspec, lenf); + break; +case 017: /* LDCff' */ + ReadFP (&fsrc, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + if ((FPS & (FPS_D + FPS_T)) == 0) newV = roundfp11 (&fsrc); + else newV = 0; + F_STORE (qdouble, fsrc, FR[ac]); + FPS = setfcc (FPS, fsrc.h, newV); + break; +case 014: /* STCff' */ + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fac.h) == 0) fac = zero_fac; + if ((FPS & (FPS_D + FPS_T)) == FPS_D) newV = roundfp11 (&fac); + else newV = 0; + WriteFP (&fac, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf); + FPS = setfcc (FPS, fac.h, newV); + break; + +/* Compare instruction */ + +case 7: /* CMPf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + if (GET_EXP (fac.h) == 0) fac = zero_fac; + if ((fsrc.h == fac.h) && (fsrc.l == fac.l)) { /* equal? */ + FPS = (FPS & ~FPS_CC) | FPS_Z; + if ((fsrc.h | fsrc.l) == 0) { /* zero? */ + F_STORE (qdouble, zero_fac, FR[ac]); } + break; } + FPS = (FPS & ~FPS_CC) | ((fsrc.h >> (FP_V_SIGN - PSW_V_N)) & FPS_N); + if ((GET_SIGN (fsrc.h ^ fac.h) == 0) && (fac.h != 0) && + F_LT (fsrc, fac)) FPS = FPS ^ FPS_N; + break; + +/* Load and store exponent instructions */ + +case 015: /* LDEXP */ + dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec)); + F_LOAD (qdouble, FR[ac], fac); + fac.h = (fac.h & ~FP_EXP) | (((dst + FP_BIAS) & FP_M_EXP) << FP_V_EXP); + newV = 0; + if ((dst > 0177) && (dst <= 0177600)) { + if (dst < 0100000) { + if (fpnotrap (FEC_OVFLO)) fac = zero_fac; + newV = FPS_V; } + else { if (fpnotrap (FEC_UNFLO)) fac = zero_fac; } } + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; +case 012: /* STEXP */ + dst = (GET_EXP (FR[ac].h) - FP_BIAS) & 0177777; + N = GET_SIGN_W (dst); + Z = (dst == 0); + V = 0; + C = 0; + FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | (Z << PSW_V_Z); + if (dstspec <= 07) R[dstspec] = dst; + else WriteW (dst, GeteaW (dstspec)); + break; + +/* Integer convert instructions */ + +case 016: /* LDCif */ + leni = FPS & FPS_L? LONG: WORD; + if (dstspec <= 07) fac.l = R[dstspec] << 16; + else fac.l = ReadI (GeteaFP (dstspec, leni), dstspec, leni); + fac.h = 0; + if (fac.l) { + if (sign = GET_SIGN_L (fac.l)) fac.l = (fac.l ^ 0xFFFFFFFF) + 1; + for (i = 0; GET_SIGN_L (fac.l) == 0; i++) fac.l = fac.l << 1; + exp = ((FPS & FPS_L)? FP_BIAS + 32: FP_BIAS + 16) - i; + fac.h = (sign << FP_V_SIGN) | (exp << FP_V_EXP) | + ((fac.l >> (31 - FP_V_HB)) & FP_FRACH); + fac.l = (fac.l << (FP_V_HB + 1)) & FP_FRACL; + if ((FPS & (FPS_D + FPS_T)) == 0) roundfp11 (&fac); } + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, 0); + break; +case 013: /* STCfi */ + sign = GET_SIGN (FR[ac].h); /* get sign, */ + exp = GET_EXP (FR[ac].h); /* exponent, */ + F_LOAD_FRAC (qdouble, FR[ac], fac); /* fraction */ + if (FPS & FPS_L) { + leni = LONG; + i = FP_BIAS + 32; } + else { leni = WORD; + i = FP_BIAS + 16; } + C = 0; + if (exp <= FP_BIAS) dst = 0; + else if (exp > i) { + dst = 0; + C = 1; } + else { F_RSH_V (fac, FP_V_HB + 1 + i - exp, fsrc); + if (leni == WORD) fsrc.l = fsrc.l & ~0177777; + if (fsrc.l >= i_limit[leni == LONG][sign]) { + dst = 0; + C = 1; } + else { dst = fsrc.l; + if (sign) dst = -dst; } } + N = GET_SIGN_L (dst); + Z = (dst == 0); + V = 0; + if (C) fpnotrap (FEC_ICVT); + FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | + (Z << PSW_V_Z) | (C << PSW_V_C); + if (dstspec <= 07) R[dstspec] = (dst >> 16) & 0177777; + else WriteI (dst, GeteaFP (dstspec, leni), dstspec, leni); + break; + +/* Calculation instructions */ + +case 2: /* MULf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = mulfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; +case 3: /* MODf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = modfp11 (&fac, &fsrc, &modfrac); + F_STORE (qdouble, fac, FR[ac | 1]); + F_STORE (qdouble, modfrac, FR[ac]); + FPS = setfcc (FPS, modfrac.h, newV); + break; +case 4: /* ADDf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = addfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; +case 6: /* SUBf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fsrc.h) != 0) fsrc.h = fsrc.h ^ FP_SIGN; + newV = addfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; +case 011: /* DIVf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = divfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; } /* end switch fop */ +return; +} + +/* Effective address calculation for fp operands + + Inputs: + spec = specifier + len = length + Outputs: + VA = virtual address + + Warnings: + - Do not call this routine for integer mode 0 operands + - Do not call this routine more than once per instruction +*/ + +int32 GeteaFP (int32 spec, int32 len) +{ +int32 adr, reg, ds; +extern int32 cm, isenable, dsenable, MMR0, MMR1; + +reg = spec & 07; /* reg number */ +ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ +switch (spec >> 3) { /* case on spec */ +case 0: /* floating AC */ + if (reg >= 06) { fpnotrap (FEC_OP); ABORT (TRAP_INT); } + return 0; +case 1: /* (R) */ + return (R[reg] | ds); +case 2: /* (R)+ */ + if (reg == 7) len = 2; + R[reg] = ((adr = R[reg]) + len) & 0177777; + if (update_MM) MMR1 = (len << 3) | reg; + return (adr | ds); +case 3: /* @(R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = 020 | reg; + adr = ReadW (adr | ds); + return (adr | dsenable); +case 4: /* -(R) */ + adr = R[reg] = (R[reg] - len) & 0177777; + if (update_MM) MMR1 = (((-len) & 037) << 3) | reg; + if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + return (adr | ds); +case 5: /* @-(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = 0360 | reg; + if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) { + setTRAP (TRAP_YEL); + setCPUERR (CPUE_YEL); } + adr = ReadW (adr | ds); + return (adr | dsenable); +case 6: /* d(r) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + return (((R[reg] + adr) & 0177777) | dsenable); +case 7: /* @d(R) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); + return (adr | dsenable); } /* end switch */ +} + +/* Read integer operand + + Inputs: + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length (2/4 bytes) + Outputs: + data = data read from memory or I/O space +*/ + +unsigned int32 ReadI (int32 VA, int32 spec, int32 len) +{ +if ((len == WORD) || (spec == 027)) return (ReadW (VA) << 16); +return ((ReadW (VA) << 16) | ReadW ((VA & ~0177777) | ((VA + 2) & 0177777))); +} + +/* Read floating operand + + Inputs: + fptr = pointer to output + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length (4/8 bytes) +*/ + +void ReadFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) +{ +int32 exta; + +if (spec <= 07) { + F_LOAD_P (len == QUAD, FR[spec], fptr); + return; } +if (spec == 027) { + fptr -> h = (ReadW (VA) << FP_V_F0); + fptr -> l = 0; } +else { exta = VA & ~0177777; + fptr -> h = (ReadW (VA) << FP_V_F0) | + (ReadW (exta | ((VA + 2) & 0177777)) << FP_V_F1); + if (len == QUAD) fptr -> l = + (ReadW (exta | ((VA + 4) & 0177777)) << FP_V_F2) | + (ReadW (exta | ((VA + 6) & 0177777)) << FP_V_F3); + else fptr -> l = 0; } +if ((GET_SIGN (fptr -> h) != 0) && (GET_EXP (fptr -> h) == 0) && + (fpnotrap (FEC_UNDFV) == 0)) ABORT (TRAP_INT); +return; +} + +/* Write integer result + + Inputs: + data = data to be written + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length + Outputs: none +*/ + +void WriteI (int32 data, int32 VA, int32 spec, int32 len) +{ +WriteW ((data >> 16) & 0177777, VA); +if ((len == WORD) || (spec == 027)) return; +WriteW (data & 0177777, (VA & ~0177777) | ((VA + 2) & 0177777)); +return; +} + +/* Write floating result + + Inputs: + fptr = pointer to data to be written + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length + Outputs: none +*/ + +void WriteFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) +{ +int32 exta; + +if (spec <= 07) { + F_STORE_P (len == QUAD, fptr, FR[spec]); + return; } +WriteW ((fptr -> h >> FP_V_F0) & 0177777, VA); +if (spec == 027) return; +exta = VA & ~0177777; +WriteW ((fptr -> h >> FP_V_F1) & 0177777, exta | ((VA + 2) & 0177777)); +if (len == LONG) return; +WriteW ((fptr -> l >> FP_V_F2) & 0177777, exta | ((VA + 4) & 0177777)); +WriteW ((fptr -> l >> FP_V_F3) & 0177777, exta | ((VA + 6) & 0177777)); +return; +} + +/* Floating point add + + Inputs: + facp = pointer to src1 (output) + fsrcp = pointer to src2 + Outputs: + ovflo = overflow variable +*/ + +int32 addfp11 (fpac_t *facp, fpac_t *fsrcp) +{ +int32 facexp, fsrcexp, ediff; +fpac_t facfrac, fsrcfrac; + +if (F_LT_AP (facp, fsrcp)) { /* if !fac! < !fsrc! */ + facfrac = *facp; + *facp = *fsrcp; /* swap operands */ + *fsrcp = facfrac; } +facexp = GET_EXP (facp -> h); /* get exponents */ +fsrcexp = GET_EXP (fsrcp -> h); +if (facexp == 0) { /* fac = 0? */ + *facp = fsrcexp? *fsrcp: zero_fac; /* result fsrc or 0 */ + return 0; } +if (fsrcexp == 0) return 0; /* fsrc = 0? no op */ +ediff = facexp - fsrcexp; /* exponent diff */ +if (ediff >= 60) return 0; /* too big? no op */ +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +F_LSH_GUARD (facfrac); /* guard fractions */ +F_LSH_GUARD (fsrcfrac); +if (GET_SIGN (facp -> h) != GET_SIGN (fsrcp -> h)) { /* signs different? */ + if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); } /* sub, shf fsrc */ + F_SUB (fsrcfrac, facfrac, facfrac); /* sub fsrc from fac */ + if ((facfrac.h | facfrac.l) == 0) { /* result zero? */ + *facp = zero_fac; /* no overflow */ + return 0; } + if (ediff <= 1) { /* big normalize? */ + if ((facfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) { + F_LSH_K (facfrac, 24, facfrac); + facexp = facexp - 24; } + if ((facfrac.h & (0x00FFF000 << FP_GUARD)) == 0) { + F_LSH_K (facfrac, 12, facfrac); + facexp = facexp - 12; } + if ((facfrac.h & (0x00FC0000 << FP_GUARD)) == 0) { + F_LSH_K (facfrac, 6, facfrac); + facexp = facexp - 6; } } + while (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (facfrac); + facexp = facexp - 1; } } +else { if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); } /* add, shf fsrc */ + F_ADD (fsrcfrac, facfrac, facfrac); /* add fsrc to fac */ + if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD + 1)) { + F_RSH_1 (facfrac); /* carry out, shift */ + facexp = facexp + 1; } } +return round_and_pack (facp, facexp, &facfrac, 1); +} + +/* Floating point multiply + + Inputs: + facp = pointer to src1 (output) + fsrcp = pointer to src2 + Outputs: + ovflo = overflow indicator +*/ + +int32 mulfp11 (fpac_t *facp, fpac_t *fsrcp) +{ +int32 facexp, fsrcexp; +fpac_t facfrac, fsrcfrac; + +facexp = GET_EXP (facp -> h); /* get exponents */ +fsrcexp = GET_EXP (fsrcp -> h); +if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */ + *facp = zero_fac; + return 0; } +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */ +facp -> h = facp -> h ^ fsrcp -> h; /* calculate sign */ +frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */ + +/* Multiplying two numbers in the range [.5,1) produces a result in the + range [.25,1). Therefore, at most one bit of normalization is required + to bring the result back to the range [.5,1). +*/ + +if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (facfrac); + facexp = facexp - 1; } +return round_and_pack (facp, facexp, &facfrac, 1); +} + +/* Floating point mod + + Inputs: + facp = pointer to src1 (integer result) + fsrcp = pointer to src2 + fracp = pointer to fractional result + Outputs: + ovflo = overflow indicator + + See notes on multiply for initial operation +*/ + +int32 modfp11 (fpac_t *facp, fpac_t *fsrcp, fpac_t *fracp) +{ +int32 facexp, fsrcexp; +fpac_t facfrac, fsrcfrac, fmask; + +facexp = GET_EXP (facp -> h); /* get exponents */ +fsrcexp = GET_EXP (fsrcp -> h); +if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */ + *fracp = zero_fac; + *facp = zero_fac; + return 0; } +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */ +fracp -> h = facp -> h = facp -> h ^ fsrcp -> h; /* calculate sign */ +frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */ + +/* Multiplying two numbers in the range [.5,1) produces a result in the + range [.25,1). Therefore, at most one bit of normalization is required + to bring the result back to the range [.5,1). +*/ + +if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (facfrac); + facexp = facexp - 1; } + +/* There are three major cases of MODf: + + 1. Exp <= FP_BIAS (all fraction). Return 0 as integer, product as + fraction. Underflow can occur. + 2. Exp > FP_BIAS + #fraction bits (all integer). Return product as + integer, 0 as fraction. Overflow can occur. + 3. FP_BIAS < exp <= FP_BIAS + #fraction bits. Separate integer and + fraction and return both. Neither overflow nor underflow can occur. +*/ + + if (facexp <= FP_BIAS) { /* case 1 */ + *facp = zero_fac; + return round_and_pack (fracp, facexp, &facfrac, 1); } + if (facexp > ((FPS & FPS_D)? FP_BIAS + 56: FP_BIAS + 24)) { + *fracp = zero_fac; /* case 2 */ + return round_and_pack (facp, facexp, &facfrac, 0); } + F_RSH_V (fmask_fac, facexp - FP_BIAS, fmask); /* shift mask */ + fsrcfrac.l = facfrac.l & fmask.l; /* extract fraction */ + fsrcfrac.h = facfrac.h & fmask.h; + if ((fsrcfrac.h | fsrcfrac.l) == 0) *fracp = zero_fac; + else { F_LSH_V (fsrcfrac, facexp - FP_BIAS, fsrcfrac); + fsrcexp = FP_BIAS; + if ((fsrcfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) { + F_LSH_K (fsrcfrac, 24, fsrcfrac); + fsrcexp = fsrcexp - 24; } + if ((fsrcfrac.h & (0x00FFF000 << FP_GUARD)) == 0) { + F_LSH_K (fsrcfrac, 12, fsrcfrac); + fsrcexp = fsrcexp - 12; } + if ((fsrcfrac.h & (0x00FC0000 << FP_GUARD)) == 0) { + F_LSH_K (fsrcfrac, 6, fsrcfrac); + fsrcexp = fsrcexp - 6; } + while (GET_BIT (fsrcfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (fsrcfrac); + fsrcexp = fsrcexp - 1; } + round_and_pack (fracp, fsrcexp, &fsrcfrac, 1); } + facfrac.l = facfrac.l & ~fmask.l; + facfrac.h = facfrac.h & ~fmask.h; + return round_and_pack (facp, facexp, &facfrac, 0); +} + +/* Fraction multiply + + Inputs: + f1p = pointer to multiplier (output) + f2p = pointer to multiplicand fraction + + Note: the inputs are unguarded; the output is guarded. + + This routine performs a classic shift-and-add multiply. The low + order bit of the multiplier is tested; if 1, the multiplicand is + added into the high part of the double precision result. The + result and the multiplier are both shifted right 1. + + For the 24b x 24b case, this routine develops 48b of result. + For the 56b x 56b case, this routine only develops the top 64b + of the the result. Because the inputs are normalized fractions, + the interesting part of the result is the high 56+guard bits. + Everything shifted off to the right, beyond 64b, plays no part + in rounding or the result. + + There are many possible optimizations in this routine: scanning + for groups of zeroes, particularly in the 56b x 56b case; using + "extended multiply" capability if available in the hardware. +*/ + +void frac_mulfp11 (fpac_t *f1p, fpac_t *f2p) +{ +fpac_t result, mpy, mpc; +int32 i; + +result = zero_fac; /* clear result */ +mpy = *f1p; /* get operands */ +mpc = *f2p; +F_LSH_GUARD (mpc); /* guard multipicand */ +if ((mpy.l | mpc.l) == 0) { /* 24b x 24b? */ + for (i = 0; i < 24; i++) { + if (mpy.h & 1) result.h = result.h + mpc.h; + F_RSH_1 (result); + mpy.h = mpy.h >> 1; } } +else { if (mpy.l != 0) { /* 24b x 56b? */ + for (i = 0; i < 32; i++) { + if (mpy.l & 1) { F_ADD (mpc, result, result); } + F_RSH_1 (result); + mpy.l = mpy.l >> 1; } } + for (i = 0; i < 24; i++) { + if (mpy.h & 1) { F_ADD (mpc, result, result); } + F_RSH_1 (result); + mpy.h = mpy.h >> 1; } } +*f1p = result; +return; +} + +/* Floating point divide + + Inputs: + facp = pointer to dividend (output) + fsrcp = pointer to divisor + Outputs: + ovflo = overflow indicator +*/ + +int32 divfp11 (fpac_t *facp, fpac_t *fsrcp) +{ +int32 facexp, fsrcexp, i, count, qd; +fpac_t facfrac, fsrcfrac, quo; + +fsrcexp = GET_EXP (fsrcp -> h); /* get divisor exp */ +if (fsrcexp == 0) { /* divide by zero? */ + fpnotrap (FEC_DZRO); + ABORT (TRAP_INT); } +facexp = GET_EXP (facp -> h); /* get dividend exp */ +if (facexp == 0) { /* test for zero */ + *facp = zero_fac; /* result zero */ + return 0; } +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +F_LSH_GUARD (facfrac); /* guard fractions */ +F_LSH_GUARD (fsrcfrac); +facexp = facexp - fsrcexp + FP_BIAS + 1; /* calculate exp */ +facp -> h = facp -> h ^ fsrcp -> h; /* calculate sign */ +qd = FPS & FPS_D; +count = FP_V_HB + FP_GUARD + (qd? 33: 1); /* count = 56b/24b */ + +quo = zero_fac; +for (i = count; (i > 0) && ((facfrac.h | facfrac.l) != 0); i--) { + F_LSH_1 (quo); /* shift quotient */ + if (!F_LT (facfrac, fsrcfrac)) { /* divd >= divr? */ + F_SUB (fsrcfrac, facfrac, facfrac); /* divd - divr */ + if (qd) quo.l = quo.l | 1; /* double or single? */ + else quo.h = quo.h | 1; } + F_LSH_1 (facfrac); } /* shift divd */ +if (i > 0) { F_LSH_V (quo, i, quo); } /* early exit? */ + +/* Dividing two numbers in the range [.5,1) produces a result in the + range [.5,2). Therefore, at most one bit of normalization is required + to bring the result back to the range [.5,1). The choice of counts + and quotient bit positions makes this work correctly. +*/ + +if (GET_BIT (quo.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (quo); + facexp = facexp - 1; } +return round_and_pack (facp, facexp, &quo, 1); +} + +/* Update floating condition codes + Note that FC is only set by STCfi via the integer condition codes + + Inputs: + oldst = current status + result = high result + newV = new V + Outputs: + newst = new status +*/ + +int32 setfcc (int32 oldst, int32 result, int32 newV) +{ +oldst = (oldst & ~FPS_CC) | newV; +if (GET_SIGN (result)) oldst = oldst | FPS_N; +if (GET_EXP (result) == 0) oldst = oldst | FPS_Z; +return oldst; +} + +/* Round (in place) floating point number to f_floating + + Inputs: + fptr = pointer to floating number + Outputs: + ovflow = overflow +*/ + +int32 roundfp11 (fpac_t *fptr) +{ +fpac_t outf; + +outf = *fptr; /* get argument */ +F_ADD (fround_fac, outf, outf); /* round */ +if (GET_SIGN (outf.h ^ fptr -> h)) { /* flipped sign? */ + outf.h = (outf.h ^ FP_SIGN) & 0xFFFFFFFF; /* restore sign */ + if (fpnotrap (FEC_OVFLO)) *fptr = zero_fac; /* if no int, clear */ + else *fptr = outf; /* return rounded */ + return FPS_V; } /* overflow */ +else { *fptr = outf; /* round was ok */ + return 0; } /* no overflow */ +} + +/* Round result of calculation, test overflow, pack + + Input: + facp = pointer to result, sign in place + exp = result exponent, right justified + fracp = pointer to result fraction, right justified with + guard bits + r = round (1) or truncate (0) + Outputs: + ovflo = overflow indicator +*/ + +int32 round_and_pack (fpac_t *facp, int32 exp, fpac_t *fracp, int r) +{ +fpac_t frac; + +frac = *fracp; /* get fraction */ +if (r && ((FPS & FPS_T) == 0)) { + if (FPS & FPS_D) { F_ADD (dround_guard_fac, frac, frac); } + else { F_ADD (fround_guard_fac, frac, frac); } + if (GET_BIT (frac.h, FP_V_HB + FP_GUARD + 1)) { + F_RSH_1 (frac); + exp = exp + 1; } } +F_RSH_GUARD (frac); +facp -> l = frac.l & FP_FRACL; +facp -> h = (facp -> h & FP_SIGN) | ((exp & FP_M_EXP) << FP_V_EXP) | + (frac.h & FP_FRACH); +if (exp > 0377) { + if (fpnotrap (FEC_OVFLO)) *facp = zero_fac; + return FPS_V; } +if ((exp <= 0) && (fpnotrap (FEC_UNFLO))) *facp = zero_fac; +return 0; +} + +/* Process floating point exception + + Inputs: + code = exception code + Outputs: + int = FALSE if interrupt enabled, TRUE if disabled +*/ + +int32 fpnotrap (int32 code) +{ +static const int32 test_code[] = { 0, 0, 0, FPS_IC, FPS_IV, FPS_IU, FPS_IUV }; + +if ((code >= FEC_ICVT) && (code <= FEC_UNDFV) && + ((FPS & test_code[code >> 1]) == 0)) return TRUE; +FPS = FPS | FPS_ER; +FEC = code; +FEA = (backup_PC - 2) & 0177777; +if ((FPS & FPS_ID) == 0) setTRAP (TRAP_FPE); +return FALSE; +} diff --git a/pdp11_lp.c b/pdp11_lp.c new file mode 100644 index 00000000..5ca11485 --- /dev/null +++ b/pdp11_lp.c @@ -0,0 +1,147 @@ +/* pdp11_lp.c: PDP-11 line printer simulator + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt line printer + + 30-Oct-00 RMS Standardized register naming +*/ + +#include "pdp11_defs.h" + +#define LPTCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* implemented */ +#define LPTCSR_RW (CSR_IE) /* read/write */ + +extern int32 int_req; +int32 lpt_csr = 0; /* control/status */ +int32 lpt_stopioe = 0; /* stop on error */ +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *ptr); +t_stat lpt_detach (UNIT *uptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { ORDATA (CSR, lpt_csr, 16) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { FLDATA (ERR, lpt_csr, CSR_V_ERR) }, + { FLDATA (DONE, lpt_csr, CSR_V_DONE) }, + { FLDATA (IE, lpt_csr, CSR_V_IE) }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach }; + +/* Line printer routines + + lpt_rd I/O page read + lpt_wr I/O page write + lpt_svc process event (printer ready) + lpt_reset process reset + lpt_attach process attach + lpt_detach process detach +*/ + +t_stat lpt_rd (int32 *data, int32 PA, int32 access) +{ +if ((PA & 02) == 0) *data = lpt_csr & LPTCSR_IMP; /* csr */ +else *data = lpt_unit.buf; /* buffer */ +return SCPE_OK; +} + +t_stat lpt_wr (int32 data, int32 PA, int32 access) +{ +if ((PA & 02) == 0) { /* csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_LPT; + else if ((lpt_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + int_req = int_req | INT_LPT; + lpt_csr = (lpt_csr & ~LPTCSR_RW) | (data & LPTCSR_RW); } +else { if ((PA & 1) == 0) lpt_unit.buf = data & 0177; /* buffer */ + lpt_csr = lpt_csr & ~CSR_DONE; + int_req = int_req & ~INT_LPT; + if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || + (lpt_unit.buf == 012)) sim_activate (&lpt_unit, lpt_unit.wait); + else sim_activate (&lpt_unit, 0); } +return SCPE_OK; +} + +t_stat lpt_svc (UNIT *uptr) +{ +lpt_csr = lpt_csr | CSR_ERR | CSR_DONE; +if (lpt_csr & CSR_IE) int_req = int_req | INT_LPT; +if ((lpt_unit.flags & UNIT_ATT) == 0) + return IORETURN (lpt_stopioe, SCPE_UNATT); +if (putc (lpt_unit.buf & 0177, lpt_unit.fileref) == EOF) { + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } +lpt_csr = lpt_csr & ~CSR_ERR; +lpt_unit.pos = ftell (lpt_unit.fileref); +return SCPE_OK; +} + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; +lpt_csr = CSR_DONE; +if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR; +int_req = int_req & ~INT_LPT; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +lpt_csr = lpt_csr & ~CSR_ERR; +reason = attach_unit (uptr, cptr); +if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR; +return reason; +} + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_csr = lpt_csr | CSR_ERR; +return detach_unit (uptr); +} diff --git a/pdp11_rk.c b/pdp11_rk.c new file mode 100644 index 00000000..2d5543bf --- /dev/null +++ b/pdp11_rk.c @@ -0,0 +1,589 @@ +/* RK11 cartridge disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 29-Jun-96 RMS Added unit disable support. + + The RK11 is an eight drive cartridge disk subsystem. An RK05 drive + consists of 203 cylinders, each with 2 surfaces containing 12 sectors + of 512 bytes. + + The most complicated part of the RK11 controller is the concept of + interrupt "polling". While only one read or write can occur at a + time, the controller supports multiple seeks. When a seek completes, + if done is set the drive attempts to interrupt. If an interrupt is + already pending, the interrupt is "queued" until it can be processed. + When an interrupt occurs, RKDS<15:13> is loaded with the number of the + interrupting drive. + + To implement this structure, and to assure that read/write interrupts + take priority over seek interrupts, the controller contains an + interrupt queue, rkintq, with a bit for a controller interrupt and + then one for each drive. In addition, the drive number of the last + non-seeking drive is recorded in last_drv. +*/ + +#include "pdp11_defs.h" + +/* Constants */ + +#define RK_NUMWD 256 /* words/sector */ +#define RK_NUMSC 12 /* sectors/surface */ +#define RK_NUMSF 2 /* surfaces/cylinder */ +#define RK_NUMCY 203 /* cylinders/drive */ +#define RK_NUMTR (RK_NUMCY * RK_NUMSF) /* tracks/drive */ +#define RK_NUMDR 8 /* drives/controller */ +#define RK_M_NUMDR 07 +#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */ +#define RK_MAXMEM ((int32) (MEMSIZE / sizeof (int16))) /* words/memory */ +#define RK_CTLI 1 /* controller int */ +#define RK_SCPI(x) (2u << (x)) /* drive int */ + +/* Flags in the unit flags word */ + +#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ +#define UNIT_W_UF 3 /* user flags width */ +#define UNIT_HWLK (1u << UNIT_V_HWLK) +#define UNIT_SWLK (1u << UNIT_V_SWLK) + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* RKDS */ + +#define RKDS_SC 0000017 /* sector counter */ +#define RKDS_ON_SC 0000020 /* on sector */ +#define RKDS_WLK 0000040 /* write locked */ +#define RKDS_RWS 0000100 /* rd/wr/seek ready */ +#define RKDS_RDY 0000200 /* drive ready */ +#define RKDS_SC_OK 0000400 /* SC valid */ +#define RKDS_INC 0001000 /* seek incomplete */ +#define RKDS_UNSAFE 0002000 /* unsafe */ +#define RKDS_RK05 0004000 /* RK05 */ +#define RKDS_PWR 0010000 /* power low */ +#define RKDS_ID 0160000 /* drive ID */ +#define RKDS_V_ID 13 + +/* RKER */ + +#define RKER_WCE 0000001 /* write check */ +#define RKER_CSE 0000002 /* checksum */ +#define RKER_NXS 0000040 /* nx sector */ +#define RKER_NXC 0000100 /* nx cylinder */ +#define RKER_NXD 0000200 /* nx drive */ +#define RKER_TE 0000400 /* timing error */ +#define RKER_DLT 0001000 /* data late */ +#define RKER_NXM 0002000 /* nx memory */ +#define RKER_PGE 0004000 /* programming error */ +#define RKER_SKE 0010000 /* seek error */ +#define RKER_WLK 0020000 /* write lock */ +#define RKER_OVR 0040000 /* overrun */ +#define RKER_DRE 0100000 /* drive error */ +#define RKER_IMP 0177743 /* implemented */ +#define RKER_SOFT (RKER_WCE+RKER_CSE) /* soft errors */ +#define RKER_HARD 0177740 /* hard errors */ + +/* RKCS */ + +#define RKCS_M_FUNC 0000007 /* function */ +#define RKCS_CTLRESET 0 +#define RKCS_WRITE 1 +#define RKCS_READ 2 +#define RKCS_WCHK 3 +#define RKCS_SEEK 4 +#define RKCS_RCHK 5 +#define RKCS_DRVRESET 6 +#define RKCS_WLK 7 +#define RKCS_V_FUNC 1 +#define RKCS_MEX 0000060 /* memory extension */ +#define RKCS_V_MEX 4 +#define RKCS_SSE 0000400 /* stop on soft err */ +#define RKCS_FMT 0002000 /* format */ +#define RKCS_INH 0004000 /* inhibit increment */ +#define RKCS_SCP 0020000 /* search complete */ +#define RKCS_HERR 0040000 /* hard error */ +#define RKCS_ERR 0100000 /* error */ +#define RKCS_REAL 0026776 /* kept here */ +#define RKCS_RW 0006576 /* read/write */ +#define GET_FUNC(x) (((x) >> RKCS_V_FUNC) & RKCS_M_FUNC) + +/* RKDA */ + +#define RKDA_V_SECT 0 /* sector */ +#define RKDA_M_SECT 017 +#define RKDA_V_TRACK 4 /* track */ +#define RKDA_M_TRACK 0777 +#define RKDA_V_CYL 5 /* cylinder */ +#define RKDA_M_CYL 0377 +#define RKDA_V_DRIVE 13 /* drive */ +#define RKDA_M_DRIVE 07 +#define RKDA_DRIVE (RKDA_M_DRIVE << RKDA_V_DRIVE) +#define GET_SECT(x) (((x) >> RKDA_V_SECT) & RKDA_M_SECT) +#define GET_CYL(x) (((x) >> RKDA_V_CYL) & RKDA_M_CYL) +#define GET_TRACK(x) (((x) >> RKDA_V_TRACK) & RKDA_M_TRACK) +#define GET_DRIVE(x) (((x) >> RKDA_V_DRIVE) & RKDA_M_DRIVE) +#define GET_DA(x) ((GET_TRACK (x) * RK_NUMSC) + GET_SECT (x)) + +/* RKBA */ + +#define RKBA_IMP 0177776 /* implemented */ + +#define RK_MIN 10 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern int32 int_req; +extern unsigned int16 *M; /* memory */ +extern UNIT cpu_unit; +int32 rkcs = 0; /* control/status */ +int32 rkds = 0; /* drive status */ +int32 rkba = 0; /* memory address */ +int32 rkda = 0; /* disk address */ +int32 rker = 0; /* error status */ +int32 rkwc = 0; /* word count */ +int32 rkintq = 0; /* interrupt queue */ +int32 last_drv = 0; /* last r/w drive */ +int32 rk_stopioe = 1; /* stop on error */ +int32 rk_swait = 10; /* seek time */ +int32 rk_rwait = 10; /* rotate time */ +t_stat rk_svc (UNIT *uptr); +t_stat rk_reset (DEVICE *dptr); +void rk_go (void); +void rk_set_done (int32 error); +void rk_clr_done (void); +t_stat rk_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* RK11 data structures + + rk_dev RK device descriptor + rk_unit RK unit list + rk_reg RK register list + rk_mod RK modifier list +*/ + +UNIT rk_unit[] = { + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) } }; + +REG rk_reg[] = { + { ORDATA (RKCS, rkcs, 16) }, + { ORDATA (RKDA, rkda, 16) }, + { ORDATA (RKBA, rkba, 16) }, + { ORDATA (RKWC, rkwc, 16) }, + { ORDATA (RKDS, rkds, 16) }, + { ORDATA (RKER, rker, 16) }, + { ORDATA (INTQ, rkintq, 9) }, + { ORDATA (DRVN, last_drv, 3) }, + { FLDATA (INT, int_req, INT_V_RK) }, + { FLDATA (ERR, rkcs, CSR_V_ERR) }, + { FLDATA (DONE, rkcs, CSR_V_DONE) }, + { FLDATA (IE, rkcs, CSR_V_IE) }, + { DRDATA (STIME, rk_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rk_rwait, 24), PV_LEFT }, + { GRDATA (FLG0, rk_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, rk_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, rk_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, rk_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, rk_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, rk_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, rk_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, rk_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { FLDATA (STOP_IOE, rk_stopioe, 0) }, + { NULL } }; + +MTAB rk_mod[] = { + { (UNIT_HWLK+UNIT_SWLK), 0, "write enabled", "ENABLED", NULL }, + { (UNIT_HWLK+UNIT_SWLK), UNIT_HWLK, "write locked", "LOCKED", NULL }, + { (UNIT_HWLK+UNIT_SWLK), UNIT_SWLK, "write locked", NULL, NULL }, + { (UNIT_HWLK+UNIT_SWLK), (UNIT_HWLK+UNIT_SWLK), "write locked", + NULL, NULL }, + { 0 } }; + +DEVICE rk_dev = { + "RK", rk_unit, rk_reg, rk_mod, + RK_NUMDR, 8, 24, 1, 8, 16, + NULL, NULL, &rk_reset, + &rk_boot, NULL, NULL }; + +/* I/O dispatch routine, I/O addresses 17777400 - 17777416 + + 17777400 RKDS read only, constructed from "id'd drive" + plus current drive status flags + 17777402 RKER read only, set as operations progress, + cleared by INIT or CONTROL RESET + 17777404 RKCS read/write + 17777406 RKWC read/write + 17777410 RKBA read/write + 17777412 RKDA read/write + 17777414 RKMR read/write, unimplemented + 17777416 RKDB read only, unimplemented +*/ + +t_stat rk_rd (int32 *data, int32 PA, int32 access) +{ +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ +case 0: /* RKDS: read only */ + rkds = (rkds & RKDS_ID) | RKDS_RK05 | RKDS_SC_OK | + (rand () % RK_NUMSC); /* random sector */ + uptr = rk_dev.units + GET_DRIVE (rkda); /* selected unit */ + if (uptr -> flags & UNIT_ATT) rkds = rkds | RKDS_RDY; /* attached? */ + if (!sim_is_active (uptr)) rkds = rkds | RKDS_RWS; /* idle? */ + if (uptr -> flags & (UNIT_HWLK + UNIT_SWLK)) rkds = rkds | RKDS_WLK; + if (GET_SECT (rkda) == (rkds & RKDS_SC)) rkds = rkds | RKDS_ON_SC; + *data = rkds; + return SCPE_OK; +case 1: /* RKER: read only */ + *data = rker & RKER_IMP; + return SCPE_OK; +case 2: /* RKCS */ + rkcs = rkcs & RKCS_REAL; + if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */ + if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; + *data = rkcs; + return SCPE_OK; +case 3: /* RKWC */ + *data = rkwc; + return SCPE_OK; +case 4: /* RKBA */ + *data = rkba & RKBA_IMP; + return SCPE_OK; +case 5: /* RKDA */ + *data = rkda; + return SCPE_OK; +default: + *data = 0; + return SCPE_OK; } /* end switch */ +} + +t_stat rk_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ +case 0: /* RKDS: read only */ + return SCPE_OK; +case 1: /* RKER: read only */ + return SCPE_OK; +case 2: /* RKCS */ + rkcs = rkcs & RKCS_REAL; + if (access == WRITEB) data = (PA & 1)? + (rkcs & 0377) | (data << 8): (rkcs & ~0377) | data; + if ((data & CSR_IE) == 0) { /* int disable? */ + rkintq = 0; /* clr int queue */ + int_req = int_req & ~INT_RK; } /* clr int request */ + else if ((rkcs & (CSR_DONE + CSR_IE)) == CSR_DONE) { + rkintq = rkintq | RK_CTLI; /* queue ctrl int */ + int_req = int_req | INT_RK; } /* set int request */ + rkcs = (rkcs & ~RKCS_RW) | (data & RKCS_RW); + if ((rkcs & CSR_DONE) && (data & CSR_GO)) rk_go (); /* new function? */ + return SCPE_OK; +case 3: /* RKWC */ + if (access == WRITEB) data = (PA & 1)? + (rkwc & 0377) | (data << 8): (rkwc & ~0377) | data; + rkwc = data; + return SCPE_OK; +case 4: /* RKBA */ + if (access == WRITEB) data = (PA & 1)? + (rkba & 0377) | (data << 8): (rkba & ~0377) | data; + rkba = data & RKBA_IMP; + return SCPE_OK; +case 5: /* RKDA */ + if ((rkcs & CSR_DONE) == 0) return SCPE_OK; + if (access == WRITEB) data = (PA & 1)? + (rkda & 0377) | (data << 8): (rkda & ~0377) | data; + rkda = data; + return SCPE_OK; +default: + return SCPE_OK; } /* end switch */ +} + +/* Initiate new function */ + +void rk_go (void) +{ +int32 i, sect, cyl, func; +UNIT *uptr; + +func = GET_FUNC (rkcs); /* get function */ +if (func == RKCS_CTLRESET) { /* control reset? */ + rker = 0; /* clear errors */ + rkda = 0; + rkba = 0; + rkcs = CSR_DONE; + rkintq = 0; /* clr int queue */ + int_req = int_req & ~INT_RK; /* clr int request */ + return; } +rker = rker & ~RKER_SOFT; /* clear soft errors */ +if (rker == 0) rkcs = rkcs & ~RKCS_ERR; /* redo summary */ +rkcs = rkcs & ~RKCS_SCP; /* clear sch compl*/ +rk_clr_done (); /* clear done */ +last_drv = GET_DRIVE (rkda); /* get drive no */ +uptr = rk_dev.units + last_drv; /* select unit */ +if (uptr -> flags & UNIT_DIS) { /* not present? */ + rk_set_done (RKER_NXD); + return; } +if (((uptr -> flags & UNIT_ATT) == 0) || sim_is_active (uptr)) { + rk_set_done (RKER_DRE); /* not att or busy */ + return; } +if (rkcs & (RKCS_INH + RKCS_FMT)) { /* format? */ + rk_set_done (RKER_PGE); + return; } +if ((func == RKCS_WRITE) && (uptr -> flags & (UNIT_HWLK + UNIT_SWLK))) { + rk_set_done (RKER_WLK); /* write and locked? */ + return; } +if (func == RKCS_WLK) { /* write lock? */ + uptr -> flags = uptr -> flags | UNIT_SWLK; + rk_set_done (0); + return; } +if (func == RKCS_DRVRESET) { /* drive reset? */ + uptr -> flags = uptr -> flags & ~UNIT_SWLK; + cyl = sect = 0; + func = RKCS_SEEK; } +else { sect = GET_SECT (rkda); + cyl = GET_CYL (rkda); } +if (sect >= RK_NUMSC) { /* bad sector? */ + rk_set_done (RKER_NXS); + return; } +if (cyl >= RK_NUMCY) { /* bad cyl? */ + rk_set_done (RKER_NXC); + return; } +i = abs (cyl - uptr -> CYL) * rk_swait; /* seek time */ +if (func == RKCS_SEEK) { /* seek? */ + rk_set_done (0); /* set done */ + sim_activate (uptr, MAX (RK_MIN, i)); } /* schedule */ +else sim_activate (uptr, i + rk_rwait); +uptr -> FUNC = func; /* save func */ +uptr -> CYL = cyl; /* put on cylinder */ +return; +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and disk address for + the current command. +*/ + +static unsigned int16 fill[RK_NUMWD] = { 0 }; +t_stat rk_svc (UNIT *uptr) +{ +int32 comp, drv, err, awc, twc, wc; +int32 pa, da, fillc, track, sect; + +drv = uptr - rk_dev.units; /* get drv number */ +if (uptr -> FUNC == RKCS_SEEK) { /* seek */ + rkcs = rkcs | RKCS_SCP; /* set seek done */ + if (rkcs & CSR_IE) { /* ints enabled? */ + rkintq = rkintq | RK_SCPI (drv); /* queue request */ + if (rkcs & CSR_DONE) int_req = int_req | INT_RK; } + else { rkintq = 0; /* clear queue */ + int_req = int_req & ~INT_RK; } /* clear interrupt */ + return SCPE_OK; } + +if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */ + rk_set_done (RKER_DRE); + return IORETURN (rk_stopioe, SCPE_UNATT); } +pa = (((rkcs & RKCS_MEX) << (16 - RKCS_V_MEX)) | rkba) >> 1; +da = GET_DA (rkda) * RK_NUMWD; /* get disk addr */ +twc = 0200000 - rkwc; /* get true wc */ +if ((pa + twc) > RK_MAXMEM) { /* mem overrun? */ + rker = rker | RKER_NXM; + wc = (RK_MAXMEM - pa); } +else wc = twc; +if (wc < 0) { /* abort transfer? */ + rk_set_done (0); + return SCPE_OK; } +if ((da + twc) > RK_SIZE) { /* disk overrun? */ + rker = rker | RKER_OVR; + if (wc > (RK_SIZE - da)) wc = RK_SIZE - da; } + +err = fseek (uptr -> fileref, da * sizeof (int16), SEEK_SET); + +if ((uptr -> FUNC == RKCS_READ) && (err == 0)) { /* read? */ + awc = fxread (&M[pa], sizeof (int16), wc, uptr -> fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; + err = ferror (uptr -> fileref); } + +if ((uptr -> FUNC == RKCS_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int16), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + if ((err == 0) && (fillc = (wc & (RK_NUMWD - 1)))) { + fxwrite (fill, sizeof (int16), fillc, uptr -> fileref); + err = ferror (uptr -> fileref); } } + +if ((uptr -> FUNC == RKCS_WCHK) && (err == 0)) { /* write check? */ + twc = wc; /* xfer length */ + for (wc = 0; (err == 0) && (wc < twc); wc++) { + awc = fxread (&comp, sizeof (int16), 1, uptr -> fileref); + if (awc == 0) comp = 0; + if (comp != M[pa + wc]) { + rker = rker | RKER_WCE; + if (rkcs & RKCS_SSE) break; } } + err = ferror (uptr -> fileref); } + +rkwc = (rkwc + wc) & 0177777; /* final word count */ +pa = (pa + wc) << 1; /* final byte addr */ +rkba = pa & RKBA_IMP; /* lower 16b */ +rkcs = (rkcs & ~RKCS_MEX) | ((pa >> (16 - RKCS_V_MEX)) & RKCS_MEX); +da = da + wc + (RK_NUMWD - 1); +track = (da / RK_NUMWD) / RK_NUMSC; +sect = (da / RK_NUMWD) % RK_NUMSC; +rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT); +rk_set_done (0); + +if (err != 0) { /* error? */ + perror ("RK I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; } +return SCPE_OK; +} + +/* Interrupt state change routines + + rk_set_done set done and possibly errors + rk_clr_done clear done + rk_inta acknowledge intererupt +*/ + +void rk_set_done (int32 error) +{ + rkcs = rkcs | CSR_DONE; /* set done */ + if (error != 0) { + rker = rker | error; /* update error */ + if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */ + if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; } + if (rkcs & CSR_IE) { /* int enable? */ + rkintq = rkintq | RK_CTLI; /* set ctrl int */ + int_req = int_req | INT_RK; } /* request int */ + else { rkintq = 0; /* clear queue */ + int_req = int_req & ~INT_RK; } + return; +} + +void rk_clr_done (void) +{ + rkcs = rkcs & ~CSR_DONE; /* clear done */ + rkintq = rkintq & ~RK_CTLI; /* clear ctl int */ + int_req = int_req & ~INT_RK; /* clear int req */ + return; +} + +int32 rk_inta (void) +{ +int32 i; + +for (i = 0; i <= RK_NUMDR; i++) { /* loop thru intq */ + if (rkintq & (1u << i)) { /* bit i set? */ + rkintq = rkintq & ~(1u << i); /* clear bit i */ + if (rkintq) int_req = int_req | INT_RK; /* queue next */ + rkds = (rkds & ~RKDS_ID) | /* id drive */ + (((i == 0)? last_drv: i - 1) << RKDS_V_ID); + return VEC_RK; } } /* return vector */ +rkintq = 0; /* clear queue */ +return 0; /* passive release */ +} + +/* Device reset */ + +t_stat rk_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rkcs = CSR_DONE; +rkda = rkba = rker = rkds = 0; +rkintq = last_drv = 0; +int_req = int_req & ~INT_RK; +for (i = 0; i < RK_NUMDR; i++) { + uptr = rk_dev.units + i; + sim_cancel (uptr); + uptr -> CYL = uptr -> FUNC = 0; + uptr -> flags = uptr -> flags & ~UNIT_SWLK; } +return SCPE_OK; +} + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_UNIT 02006 /* where to store unit number */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0012706, 0002000, /* MOV #2000, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0000303, /* SWAB R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0012701, 0177412, /* MOV #RKDA, R1 ; csr */ + 0010311, /* MOV R3, (R1) ; load da */ + 0005041, /* CLR -(R1) ; clear ba */ + 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */ + 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0005004, /* CLR R4 */ + 0012705, 0062153, /* MOV #"DK, R5 */ + 0105711, /* TSTB (R1) */ + 0100376, /* BPL .-2 */ + 0105011, /* CLRB (R1) */ + 0005007 /* CLR PC */ +}; + +t_stat rk_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RK_M_NUMDR; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp11_rl.c b/pdp11_rl.c new file mode 100644 index 00000000..7f30e412 --- /dev/null +++ b/pdp11_rl.c @@ -0,0 +1,574 @@ +/* RL11 (RLV12) cartridge disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 12-Nov-97 RMS Added bad block table command. + 25-Nov-96 RMS Default units to autosize. + 29-Jun-96 RMS Added unit disable support. + + The RL11 is a four drive cartridge disk subsystem. An RL01 drive + consists of 256 cylinders, each with 2 surfaces containing 40 sectors + of 256 bytes. An RL02 drive has 512 cylinders. The RLV12 is a + controller variant which supports 22b direct addressing. + + The most complicated part of the RL11 controller is the way it does + seeks. Seeking is relative to the current disk address; this requires + keeping accurate track of the current cylinder. The RL01 will not + switch heads or cross cylinders during transfers. +*/ + +#include "pdp11_defs.h" + +/* Constants */ + +#define RL_NUMWD 128 /* words/sector */ +#define RL_NUMSC 40 /* sectors/surface */ +#define RL_NUMSF 2 /* surfaces/cylinder */ +#define RL_NUMCY 256 /* cylinders/drive */ +#define RL_NUMDR 4 /* drives/controller */ +#define RL01_SIZE (RL_NUMCY * RL_NUMSF * RL_NUMSC * RL_NUMWD) /* words/drive */ +#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ +#define RL_MAXMEM ((int) (MEMSIZE / sizeof (int16))) /* words/memory */ + +/* Flags in the unit flags word */ + +#define UNIT_V_HWLK (UNIT_V_UF) /* hwre write lock */ +#define UNIT_V_RL02 (UNIT_V_UF+1) /* RL01 vs RL02 */ +#define UNIT_V_AUTO (UNIT_V_UF+2) /* autosize enable */ +#define UNIT_W_UF 4 /* saved flags width */ +#define UNIT_V_DUMMY (UNIT_V_UF + UNIT_W_UF) /* dummy flag */ +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define UNIT_HWLK (1u << UNIT_V_HWLK) +#define UNIT_RL02 (1u << UNIT_V_RL02) +#define UNIT_AUTO (1u << UNIT_V_AUTO) + +/* Parameters in the unit descriptor */ + +#define TRK u3 /* current track */ +#define STAT u4 /* status */ + +/* RLDS */ + +#define RLDS_LOAD 0 /* no cartridge */ +#define RLDS_LOCK 5 /* lock on */ +#define RLDS_BHO 0000010 /* brushes home */ +#define RLDS_HDO 0000020 /* heads out */ +#define RLDS_CVO 0000040 /* cover open */ +#define RLDS_HD 0000100 /* head select */ +#define RLDS_DSE 0000400 /* drive select err */ +#define RLDS_RL02 0000200 /* RL02 */ +#define RLDS_VCK 0001000 /* volume check */ +#define RLDS_WGE 0002000 /* write gate err */ +#define RLDS_SPE 0004000 /* spin err */ +#define RLDS_STO 0010000 /* seek time out */ +#define RLDS_WLK 0020000 /* write locked */ +#define RLDS_HCE 0040000 /* head current err */ +#define RLDS_WDE 0100000 /* write data err */ +#define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* attached status */ +#define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unattached status */ +#define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ + RLDS_VCK+RLDS_DSE) /* errors bits */ + +/* RLCS */ + +#define RLCS_DRDY 0000001 /* drive ready */ +#define RLCS_M_FUNC 0000007 /* function */ +#define RLCS_NOP 0 +#define RLCS_WCHK 1 +#define RLCS_GSTA 2 +#define RLCS_SEEK 3 +#define RLCS_RHDR 4 +#define RLCS_WRITE 5 +#define RLCS_READ 6 +#define RLCS_RNOHDR 7 +#define RLCS_V_FUNC 1 +#define RLCS_M_MEX 03 /* memory extension */ +#define RLCS_V_MEX 4 +#define RLCS_MEX (RLCS_M_MEX << RLCS_V_MEX) +#define RLCS_M_DRIVE 03 +#define RLCS_V_DRIVE 8 +#define RLCS_INCMP 0002000 /* incomplete */ +#define RLCS_CRC 0004000 /* CRC error */ +#define RLCS_HDE 0010000 /* header error */ +#define RLCS_NXM 0020000 /* non-exist memory */ +#define RLCS_DRE 0040000 /* drive error */ +#define RLCS_ERR 0100000 /* error summary */ +#define RLCS_ALLERR (RLCS_ERR+RLCS_DRE+RLCS_NXM+RLCS_HDE+RLCS_CRC+RLCS_INCMP) +#define RLCS_RW 0001776 /* read/write */ +#define GET_FUNC(x) (((x) >> RLCS_V_FUNC) & RLCS_M_FUNC) +#define GET_DRIVE(x) (((x) >> RLCS_V_DRIVE) & RLCS_M_DRIVE) + +/* RLDA */ + +#define RLDA_SK_DIR 0000004 /* direction */ +#define RLDA_GS_CLR 0000010 /* clear errors */ +#define RLDA_SK_HD 0000020 /* head select */ + +#define RLDA_V_SECT 0 /* sector */ +#define RLDA_M_SECT 077 +#define RLDA_V_TRACK 6 /* track */ +#define RLDA_M_TRACK 01777 +#define RLDA_HD0 (0 << RLDA_V_TRACK) +#define RLDA_HD1 (1u << RLDA_V_TRACK) +#define RLDA_V_CYL 7 /* cylinder */ +#define RLDA_M_CYL 0777 +#define RLDA_TRACK (RLDA_M_TRACK << RLDA_V_TRACK) +#define RLDA_CYL (RLDA_M_CYL << RLDA_V_CYL) +#define GET_SECT(x) (((x) >> RLDA_V_SECT) & RLDA_M_SECT) +#define GET_CYL(x) (((x) >> RLDA_V_CYL) & RLDA_M_CYL) +#define GET_TRACK(x) (((x) >> RLDA_V_TRACK) & RLDA_M_TRACK) +#define GET_DA(x) ((GET_TRACK (x) * RL_NUMSC) + GET_SECT (x)) + +/* RLBA */ + +#define RLBA_IMP 0177776 /* implemented */ + +/* RLBAE */ + +#define RLBAE_IMP 0000077 /* implemented */ + +extern int32 int_req; +extern unsigned int16 *M; /* memory */ +extern UNIT cpu_unit; +int32 rlcs = 0; /* control/status */ +int32 rlba = 0; /* memory address */ +int32 rlbae = 0; /* mem addr extension */ +int32 rlda = 0; /* disk addr */ +int32 rlmp = 0, rlmp1 = 0, rlmp2 = 0; /* mp register queue */ +int32 rl_swait = 10; /* seek wait */ +int32 rl_rwait = 10; /* rotate wait */ +int32 rl_stopioe = 1; /* stop on error */ +t_stat rl_svc (UNIT *uptr); +t_stat rl_reset (DEVICE *dptr); +void rl_set_done (int32 error); +t_stat rl_boot (int32 unitno); +t_stat rl_attach (UNIT *uptr, char *cptr); +t_stat rl_set_size (UNIT *uptr, int32 value); +t_stat rl_set_bad (UNIT *uptr, int32 value); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); +extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); + +/* RL11 data structures + + rl_dev RL device descriptor + rl_unit RL unit list + rl_reg RL register list + rl_mod RL modifier list +*/ + +UNIT rl_unit[] = { + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO, + RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO, + RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO, + RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO, + RL01_SIZE) } }; + +REG rl_reg[] = { + { ORDATA (RLCS, rlcs, 16) }, + { ORDATA (RLDA, rlda, 16) }, + { ORDATA (RLBA, rlba, 16) }, + { ORDATA (RLBAE, rlbae, 6) }, + { ORDATA (RLMP, rlmp, 16) }, + { ORDATA (RLMP1, rlmp1, 16) }, + { ORDATA (RLMP2, rlmp2, 16) }, + { FLDATA (INT, int_req, INT_V_RL) }, + { FLDATA (ERR, rlcs, CSR_V_ERR) }, + { FLDATA (DONE, rlcs, CSR_V_DONE) }, + { FLDATA (IE, rlcs, CSR_V_IE) }, + { DRDATA (STIME, rl_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rl_rwait, 24), PV_LEFT }, + { GRDATA (FLG0, rl_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, rl_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, rl_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, rl_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { DRDATA (CAPAC0, rl_unit[0].capac, 32), PV_LEFT + REG_HRO }, + { DRDATA (CAPAC1, rl_unit[1].capac, 32), PV_LEFT + REG_HRO }, + { DRDATA (CAPAC2, rl_unit[2].capac, 32), PV_LEFT + REG_HRO }, + { DRDATA (CAPAC3, rl_unit[3].capac, 32), PV_LEFT + REG_HRO }, + { FLDATA (STOP_IOE, rl_stopioe, 0) }, + { NULL } }; + +MTAB rl_mod[] = { + { UNIT_HWLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, + { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, + { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, + { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, + { 0 } }; + +DEVICE rl_dev = { + "RL", rl_unit, rl_reg, rl_mod, + RL_NUMDR, 8, 24, 1, 8, 16, + NULL, NULL, &rl_reset, + &rl_boot, &rl_attach, NULL }; + +/* I/O dispatch routine, I/O addresses 17774400 - 17774407 + + 17774400 RLCS read/write + 17774402 RLBA read/write + 17774404 RLDA read/write + 17774406 RLMP read/write + 17774410 RLBAE read/write +*/ + +t_stat rl_rd (int32 *data, int32 PA, int32 access) +{ +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<2:1> */ +case 0: /* RLCS */ + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR; + uptr = rl_dev.units + GET_DRIVE (rlcs); + if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY; + else rlcs = rlcs | RLCS_DRDY; /* see if ready */ + *data = rlcs; + return SCPE_OK; +case 1: /* RLBA */ + *data = rlba & RLBA_IMP; + return SCPE_OK; +case 2: /* RLDA */ + *data = rlda; + return SCPE_OK; +case 3: /* RLMP */ + *data = rlmp; + rlmp = rlmp1; /* ripple data */ + rlmp1 = rlmp2; + return SCPE_OK; +case 4: /* RLBAE */ + *data = rlbae & RLBAE_IMP; + return SCPE_OK; } /* end switch */ +} + +t_stat rl_wr (int32 data, int32 PA, int32 access) +{ +int32 curr, offs, newc, maxc; +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<2:1> */ +case 0: /* RLCS */ + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR; + uptr = rl_dev.units + GET_DRIVE (data); /* get new drive */ + if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY; + else rlcs = rlcs | RLCS_DRDY; /* see if ready */ + + if (access == WRITEB) data = (PA & 1)? + (rlcs & 0377) | (data << 8): (rlcs & ~0377) | data; + rlcs = (rlcs & ~RLCS_RW) | (data & RLCS_RW); + rlbae = (rlbae & ~RLCS_M_MEX) | ((rlcs >> RLCS_V_MEX) & RLCS_M_MEX); + if (data & CSR_DONE) { /* ready set? */ + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_RL; + else if ((rlcs & (CSR_DONE + CSR_IE)) == CSR_DONE) + int_req = int_req | INT_RL; + return SCPE_OK; } + + int_req = int_req & ~INT_RL; /* clear interrupt */ + rlcs = rlcs & ~RLCS_ALLERR; /* clear errors */ + switch (GET_FUNC (rlcs)) { /* case on RLCS<3:1> */ + case RLCS_NOP: /* nop */ + rl_set_done (0); + return SCPE_OK; + case RLCS_SEEK: /* seek */ + curr = GET_CYL (uptr -> TRK); /* current cylinder */ + offs = GET_CYL (rlda); /* offset */ + if (rlda & RLDA_SK_DIR) { /* in or out? */ + newc = curr + offs; /* out */ + maxc = (uptr -> flags & UNIT_RL02)? + RL_NUMCY * 2: RL_NUMCY; + if (newc >= maxc) newc = maxc - 1; } + else { newc = curr - offs; /* in */ + if (newc < 0) newc = 0; } + uptr -> TRK = (newc << RLDA_V_CYL) | /* put on track */ + ((rlda & RLDA_SK_HD)? RLDA_HD1: RLDA_HD0); + sim_activate (uptr, rl_swait * abs (newc - curr)); + return SCPE_OK; + default: /* data transfer */ + sim_activate (uptr, rl_swait); /* activate unit */ + return SCPE_OK; } /* end switch func */ + return SCPE_OK; /* end case RLCS */ + +case 1: /* RLBA */ + if (access == WRITEB) data = (PA & 1)? + (rlba & 0377) | (data << 8): (rlba & ~0377) | data; + rlba = data & RLBA_IMP; + return SCPE_OK; +case 2: /* RLDA */ + if (access == WRITEB) data = (PA & 1)? + (rlda & 0377) | (data << 8): (rlda & ~0377) | data; + rlda = data; + return SCPE_OK; +case 3: /* RLMP */ + if (access == WRITEB) data = (PA & 1)? + (rlmp & 0377) | (data << 8): (rlmp & ~0377) | data; + rlmp = rlmp1 = rlmp2 = data; + return SCPE_OK; +case 4: /* RLBAE */ + if (PA & 1) return SCPE_OK; + rlbae = data & RLBAE_IMP; + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + return SCPE_OK; } /* end switch */ +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +static unsigned int16 fill[RL_NUMWD] = { 0 }; +t_stat rl_svc (UNIT *uptr) +{ +int32 comp, err, awc, wc, maxwc; +int32 func, pa, da, fillc; + +func = GET_FUNC (rlcs); /* get function */ +if (func == RLCS_GSTA) { /* get status */ + rlmp = uptr -> STAT | (uptr -> TRK & RLDS_HD) | + ((uptr -> flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); + if (rlda & RLDA_GS_CLR) rlmp = rlmp & ~RLDS_ERR; + if (uptr -> flags & UNIT_RL02) rlmp = rlmp | RLDS_RL02; + if (uptr -> flags & UNIT_HWLK) rlmp = rlmp | RLDS_WLK; + uptr -> STAT = rlmp2 = rlmp1 = rlmp; + rl_set_done (0); /* done */ + return SCPE_OK; } + +if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */ + rlcs = rlcs & ~RLCS_DRDY; /* clear drive ready */ + rl_set_done (RLCS_ERR | RLCS_INCMP); /* flag error */ + return IORETURN (rl_stopioe, SCPE_UNATT); } + +if ((func == RLCS_WRITE) && (uptr -> flags & UNIT_HWLK)) { + uptr -> STAT = uptr -> STAT | RLDS_WGE; /* write and locked */ + rl_set_done (RLCS_ERR | RLCS_DRE); + return SCPE_OK; } + +if (func == RLCS_SEEK) { /* seek? */ + rl_set_done (0); /* done */ + return SCPE_OK; } + +if (func == RLCS_RHDR) { /* read header? */ + rlmp = (uptr -> TRK & RLDA_TRACK) | GET_SECT (rlda); + rlmp1 = 0; + rl_set_done (0); /* done */ + return SCPE_OK; } + +if (((func != RLCS_RNOHDR) && ((uptr -> TRK & RLDA_CYL) != (rlda & RLDA_CYL))) + || (GET_SECT (rlda) >= RL_NUMSC)) { /* bad cyl or sector? */ + rl_set_done (RLCS_ERR | RLCS_HDE | RLCS_INCMP); /* wrong cylinder? */ + return SCPE_OK; } + +pa = ((rlbae << 16) | rlba) >> 1; /* form phys addr */ +da = GET_DA (rlda) * RL_NUMWD; /* get disk addr */ +wc = 0200000 - rlmp; /* get true wc */ + +maxwc = (RL_NUMSC - GET_SECT (rlda)) * RL_NUMWD; /* max transfer */ +if (wc > maxwc) wc = maxwc; /* track overrun? */ +if ((pa + wc) > RL_MAXMEM) { /* mem overrun? */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; + wc = (RL_MAXMEM - pa); } +if (wc < 0) { /* abort transfer? */ + rl_set_done (RLCS_INCMP); + return SCPE_OK; } + +err = fseek (uptr -> fileref, da * sizeof (int16), SEEK_SET); +if ((func >= RLCS_READ) && (err == 0)) { /* read (no hdr)? */ + awc = fxread (&M[pa], sizeof (int16), wc, uptr -> fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; + err = ferror (uptr -> fileref); } + +if ((func == RLCS_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int16), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + if ((err == 0) && (fillc = (wc & (RL_NUMWD - 1)))) { + fxwrite (fill, sizeof (int16), fillc, uptr -> fileref); + err = ferror (uptr -> fileref); } } + +if ((func == RLCS_WCHK) && (err == 0)) { /* write check? */ + fillc = wc; /* xfer length */ + for (wc = 0; (err == 0) && (wc < fillc); wc++) { + awc = fxread (&comp, sizeof (int16), 1, uptr -> fileref); + if (awc == 0) comp = 0; + if (comp != M[pa + wc]) rlcs = rlcs | RLCS_ERR | RLCS_CRC; } + err = ferror (uptr -> fileref); } + +rlmp = (rlmp + wc) & 0177777; /* final word count */ +if (rlmp != 0) rlcs = rlcs | RLCS_ERR | RLCS_INCMP; /* completed? */ +pa = (pa + wc) << 1; /* final byte addr */ +rlbae = (pa >> 16) & RLBAE_IMP; /* upper 6b */ +rlba = pa & RLBA_IMP; /* lower 16b */ +rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); +rlda = rlda + ((wc + (RL_NUMWD - 1)) / RL_NUMWD); +rl_set_done (0); + +if (err != 0) { /* error? */ + perror ("RL I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; } +return SCPE_OK; +} + +/* Set done and possibly errors */ + +void rl_set_done (int32 status) +{ + rlcs = rlcs | status | CSR_DONE; /* set done */ + if (rlcs & CSR_IE) int_req = int_req | INT_RL; + else int_req = int_req & ~INT_RL; + return; +} + +/* Device reset + + Note that the RL11 does NOT recalibrate its drives on RESET +*/ + +t_stat rl_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rlcs = CSR_DONE; +rlda = rlba = rlbae = rlmp = rlmp1 = rlmp2 = 0; +int_req = int_req & ~INT_RL; +for (i = 0; i < RL_NUMDR; i++) { + uptr = rl_dev.units + i; + sim_cancel (uptr); + uptr -> STAT = 0; } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rl_attach (UNIT *uptr, char *cptr) +{ +int32 p; +t_stat r; + +uptr -> capac = (uptr -> flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +r = attach_unit (uptr, cptr); +if ((r != SCPE_OK) || ((uptr -> flags & UNIT_AUTO) == 0)) return r; +if (fseek (uptr -> fileref, 0, SEEK_END)) return SCPE_OK; +if ((p = ftell (uptr -> fileref)) == 0) return SCPE_OK; +if (p > (RL01_SIZE * sizeof (int16))) { + uptr -> flags = uptr -> flags | UNIT_RL02; + uptr -> capac = RL02_SIZE; } +else { uptr -> flags = uptr -> flags & ~UNIT_RL02; + uptr -> capac = RL01_SIZE; } +return SCPE_OK; +} + +/* Set size routine */ + +t_stat rl_set_size (UNIT *uptr, int32 value) +{ +if (uptr -> flags & UNIT_ATT) return SCPE_ALATT; +uptr -> capac = (value & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +return SCPE_OK; +} + +/* Set bad block routine */ + +t_stat rl_set_bad (UNIT *uptr, int32 value) +{ +return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); +} + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_UNIT 02006 /* where to store unit number */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0012706, 0002000, /* MOV #2000, SP */ + 0012700, 0000000, /* MOV #UNIT, R0 */ + 0010003, /* MOV R0, R3 */ + 0000303, /* SWAB R3 */ + 0012701, 0174400, /* MOV #RLCS, R1 ; csr */ + 0012761, 0000013, 0000004, /* MOV #13, 4(R1) ; clr err */ + 0052703, 0000004, /* BIS #4, R3 ; unit+gstat */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0105003, /* CLRB R3 */ + 0052703, 0000010, /* BIS #10, R3 ; unit+rdhdr */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0016102, 0000006, /* MOV 6(R1), R2 ; get hdr */ + 0042702, 0000077, /* BIC #77, R2 ; clr sector */ + 0005202, /* INC R2 ; magic bit */ + 0010261, 0000004, /* MOV R2, 4(R1) ; seek to 0 */ + 0105003, /* CLRB R3 */ + 0052703, 0000006, /* BIS #6, R3 ; unit+seek */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0005061, 0000002, /* CLR 2(R1) ; clr ba */ + 0005061, 0000004, /* CLR 4(R1) ; clr da */ + 0012761, 0177000, 0000006, /* MOV #-512., 6(R1) ; set wc */ + 0105003, /* CLRB R3 */ + 0052703, 0000014, /* BIS #14, R3 ; unit+read */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0042711, 0000377, /* BIC #377, (R1) */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0005004, /* CLR R4 */ + 0012705, 0062154, /* MOV "DL, R5 */ + 0005007 /* CLR PC */ +}; + +t_stat rl_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RLCS_M_DRIVE; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp11_rp.c b/pdp11_rp.c new file mode 100644 index 00000000..9d5a35c5 --- /dev/null +++ b/pdp11_rp.c @@ -0,0 +1,1035 @@ +/* pdp11_rp.c - RP04/05/06/07 RM02/03/05/80 "Massbus style" disk controller + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + 05-Oct-98 RMS Fixed bug, failing to interrupt on go error + 04-Oct-98 RMS Changed names to allow coexistence with RH/TU77 + 12-Nov-97 RMS Added bad block table command. + 10-Aug-97 RMS Fixed bugs in interrupt handling. + + The "Massbus style" disks consisted of several different large + capacity drives interfaced through a reasonably common (but not + 100% compatible) family of interfaces into a 22b direct addressing + port. On the PDP-11/70, this was the Massbus; but on 22b Qbus + systems, this was through many different third party controllers + which emulated the Massbus interface. + + WARNING: This controller is somewhat abstract. It is intended to run + the operating system drivers for the PDP-11 operating systems and + nothing more. Most error and all diagnostic functions have been + omitted. In addition, the controller conflates the RP04/05/06 series + controllers with the RM02/03/05/80 series controllers and with the + RP07 controller. There are actually significant differences, which + have been highlighted where noticed. +*/ + +#include "pdp11_defs.h" +#include + +#define RP_NUMDR 8 /* #drives */ +#define RP_NUMWD 256 /* words/sector */ +#define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) drv_tab[d].sect))) + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 7 +#define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_W_UF 6 /* user flags width */ +#define UNIT_V_DUMMY (UNIT_V_UF + UNIT_W_UF) /* dummy flag */ +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* RPCS1 - 176700 - control/status 1 */ + +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define CS1_FNC (CS1_M_FNC << CS1_V_FNC) +#define FNC_NOP 000 /* no operation */ +#define FNC_UNLOAD 001 /* unload */ +#define FNC_SEEK 002 /* seek */ +#define FNC_RECAL 003 /* recalibrate */ +#define FNC_DCLR 004 /* drive clear */ +#define FNC_RELEASE 005 /* port release */ +#define FNC_OFFSET 006 /* offset */ +#define FNC_RETURN 007 /* return to center */ +#define FNC_PRESET 010 /* read-in preset */ +#define FNC_PACK 011 /* pack acknowledge */ +#define FNC_SEARCH 014 /* search */ +#define FNC_WCHK 024 /* write check */ +#define FNC_WRITE 030 /* write */ +#define FNC_READ 034 /* read */ +#define CS1_IE CSR_IE /* int enable */ +#define CS1_DONE CSR_DONE /* ready */ +#define CS1_V_UAE 8 /* Unibus addr ext */ +#define CS1_M_UAE 03 +#define CS1_UAE (CS1_M_UAE << CS1_V_UAE) +#define CS1_DVA 0004000 /* drive avail NI */ +#define CS1_MCPE 0020000 /* Mbus par err NI */ +#define CS1_TRE 0040000 /* transfer err */ +#define CS1_SC 0100000 /* special cond */ +#define CS1_MBZ 0012000 +#define CS1_RW (CS1_FNC | CS1_IE | CS1_UAE) +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) + +/* RPWC - 176702 - word count */ + +/* RPBA - 176704 - base address */ + +#define BA_MBZ 0000001 /* must be zero */ + +/* RPDA - 176706 - sector/track */ + +#define DA_V_SC 0 /* sector pos */ +#define DA_M_SC 077 /* sector mask */ +#define DA_V_SF 8 /* track pos */ +#define DA_M_SF 077 /* track mask */ +#define DA_MBZ 0140300 +#define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) + +/* RPCS2 - 176710 - control/status 2 */ + +#define CS2_V_UNIT 0 /* unit pos */ +#define CS2_M_UNIT 07 /* unit mask */ +#define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) +#define CS2_UAI 0000010 /* addr inhibit NI */ +#define CS2_PAT 0000020 /* parity test NI */ +#define CS2_CLR 0000040 /* controller clear */ +#define CS2_IR 0000100 /* input ready */ +#define CS2_OR 0000200 /* output ready */ +#define CS2_MDPE 0000400 /* Mbus par err NI */ +#define CS2_MXF 0001000 /* missed xfer NI */ +#define CS2_PGE 0002000 /* program err */ +#define CS2_NEM 0004000 /* nx mem err */ +#define CS2_NED 0010000 /* nx drive err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write check err */ +#define CS2_DLT 0100000 /* data late NI */ +#define CS2_MBZ (CS2_CLR) +#define CS2_RW (CS2_UNIT | CS2_UAI | CS2_PAT) +#define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ + CS2_NED | CS2_PE | CS2_WCE | CS2_DLT ) +#define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) + +/* RPDS - 176712 - drive status */ + +#define DS_OF 0000001 /* offset mode */ +#define DS_VV 0000100 /* volume valid */ +#define DS_RDY 0000200 /* drive ready */ +#define DS_DPR 0000400 /* drive present */ +#define DS_PGM 0001000 /* programable NI */ +#define DS_LST 0002000 /* last sector */ +#define DS_WRL 0004000 /* write locked */ +#define DS_MOL 0010000 /* medium online */ +#define DS_PIP 0020000 /* pos in progress */ +#define DS_ERR 0040000 /* error */ +#define DS_ATA 0100000 /* attention active */ +#define DS_MBZ 0000076 + +/* RPER1 - 176714 - error status 1 */ + +#define ER1_ILF 0000001 /* illegal func */ +#define ER1_ILR 0000002 /* illegal register */ +#define ER1_RMR 0000004 /* reg mod refused */ +#define ER1_PAR 0000010 /* parity err */ +#define ER1_FER 0000020 /* format err NI */ +#define ER1_WCF 0000040 /* write clk fail NI */ +#define ER1_ECH 0000100 /* ECC hard err NI */ +#define ER1_HCE 0000200 /* hdr comp err NI */ +#define ER1_HCR 0000400 /* hdr CRC err NI */ +#define ER1_AOE 0001000 /* addr ovflo err */ +#define ER1_IAE 0002000 /* invalid addr err */ +#define ER1_WLE 0004000 /* write lock err */ +#define ER1_DTE 0010000 /* drive time err NI */ +#define ER1_OPI 0020000 /* op incomplete */ +#define ER1_UNS 0040000 /* drive unsafe */ +#define ER1_DCK 0100000 /* data check NI */ + +/* RPAS - 176716 - attention summary */ + +#define AS_U0 0000001 /* unit 0 flag */ + +/* RPLA - 176720 - look ahead register */ + +#define LA_V_SC 6 /* sector pos */ + +/* RPDB - 176722 - data buffer */ +/* RPMR - 176724 - maintenace register */ +/* RPDT - 176726 - drive type */ +/* RPSN - 176730 - serial number */ + +/* RPOF - 176732 - offset register */ + +#define OF_HCI 0002000 /* hdr cmp inh NI */ +#define OF_ECI 0004000 /* ECC inhibit NI */ +#define OF_F22 0010000 /* format NI */ +#define OF_MBZ 0161400 + +/* RPDC - 176734 - desired cylinder */ + +#define DC_V_CY 0 /* cylinder pos */ +#define DC_M_CY 01777 /* cylinder mask */ +#define DC_MBZ 0176000 +#define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) +#define GET_DA(c,fs,d) ((((GET_CY (c) * drv_tab[d].surf) + \ + GET_SF (fs)) * drv_tab[d].sect) + GET_SC (fs)) + +/* RPCC - 176736 - current cylinder - unimplemented */ +/* RPER2 - 176740 - error status 2 - drive unsafe conditions - unimplemented */ +/* RPER3 - 176742 - error status 3 - more unsafe conditions - unimplemented */ +/* RPEC1 - 176744 - ECC status 1 - unimplemented */ +/* RPEC2 - 176746 - ECC status 2 - unimplemented */ + +/* RPBAE - 176750 - bus address extension */ + +#define AE_M_MAE 0 /* addr ext pos */ +#define AE_V_MAE 077 /* addr ext mask */ +#define AE_MBZ 0177700 + +/* RPCS3 - 176752 - control/status 3 - unused except for duplicate IE */ + +#define CS3_MBZ 0177660 + +/* This controller supports many different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + RM02/3 32 5 823 =67MB + RP04/5 22 19 411 =88MB + RM80 31 14 559 =124MB + RP06 22 19 815 =176MB + RM05 32 19 823 =256MB + RP07 50 32 630 =516MB + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. +*/ + +#define RM03_DTYPE 0 +#define RM03_SECT 32 +#define RM03_SURF 5 +#define RM03_CYL 823 +#define RM03_DEV 020024 +#define RM03_SIZE (RM03_SECT * RM03_SURF * RM03_CYL * RP_NUMWD) + +#define RP04_DTYPE 1 +#define RP04_SECT 22 +#define RP04_SURF 19 +#define RP04_CYL 411 +#define RP04_DEV 020020 +#define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD) + +#define RM80_DTYPE 2 +#define RM80_SECT 31 +#define RM80_SURF 14 +#define RM80_CYL 559 +#define RM80_DEV 020026 +#define RM80_SIZE (RM80_SECT * RM80_SURF * RM80_CYL * RP_NUMWD) + +#define RP06_DTYPE 3 +#define RP06_SECT 22 +#define RP06_SURF 19 +#define RP06_CYL 815 +#define RP06_DEV 020022 +#define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD) + +#define RM05_DTYPE 4 +#define RM05_SECT 32 +#define RM05_SURF 19 +#define RM05_CYL 823 +#define RM05_DEV 020027 +#define RM05_SIZE (RM05_SECT * RM05_SURF * RM05_CYL * RP_NUMWD) + +#define RP07_DTYPE 5 +#define RP07_SECT 50 +#define RP07_SURF 32 +#define RP07_CYL 630 +#define RP07_DEV 020042 +#define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD) + +struct drvtyp { + int sect; /* sectors */ + int surf; /* surfaces */ + int cyl; /* cylinders */ + int size; /* #blocks */ + int devtype; /* device type */ +}; + +struct drvtyp drv_tab[] = { + { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV }, + { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV }, + { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV }, + { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV }, + { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV }, + { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV }, + { 0 } }; + +extern int32 int_req; +extern unsigned int16 *M; /* memory */ +extern UNIT cpu_unit; +int32 rpcs1 = 0; /* control/status 1 */ +int32 rpwc = 0; /* word count */ +int32 rpba = 0; /* bus address */ +int32 rpda = 0; /* track/sector */ +int32 rpcs2 = 0; /* control/status 2 */ +int32 rpds[RP_NUMDR] = { 0 }; /* drive status */ +int32 rper1[RP_NUMDR] = { 0 }; /* error status 1 */ +int32 rpdb = 0; /* data buffer */ +int32 rpmr = 0; /* maint register */ +int32 rpof = 0; /* offset */ +int32 rpdc = 0; /* cylinder */ +int32 rper2 = 0; /* error status 2 */ +int32 rper3 = 0; /* error status 3 */ +int32 rpec1 = 0; /* ECC correction 1 */ +int32 rpec2 = 0; /* ECC correction 2 */ +int32 rpbae = 0; /* bus address ext */ +int32 rpcs3 = 0; /* control/status 3 */ +int32 rp_stopioe = 1; /* stop on error */ +int32 rp_swait = 10; /* seek time */ +int32 rp_rwait = 10; /* rotate time */ +int reg_in_drive[32] = { + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +void update_rpcs (int32 flags, int32 drv); +void rp_go (int32 drv); +t_stat rp_set_size (UNIT *uptr, int32 value); +t_stat rp_set_bad (UNIT *uptr, int32 value); +t_stat rp_svc (UNIT *uptr); +t_stat rp_reset (DEVICE *dptr); +t_stat rp_boot (int32 unitno); +t_stat rp_attach (UNIT *uptr, char *cptr); +t_stat rp_detach (UNIT *uptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); +extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); + +/* RP data structures + + rp_dev RP device descriptor + rp_unit RP unit list + rp_reg RP register list + rp_mod RP modifier list +*/ + +UNIT rp_unit[] = { + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RM03_DTYPE << UNIT_V_DTYPE), RM03_SIZE) } }; + +REG rp_reg[] = { + { ORDATA (RPCS1, rpcs1, 16) }, + { ORDATA (RPWC, rpwc, 16) }, + { ORDATA (RPBA, rpba, 16) }, + { ORDATA (RPDA, rpda, 16) }, + { ORDATA (RPCS2, rpcs2, 16) }, + { ORDATA (RPOF, rpof, 16) }, + { ORDATA (RPDC, rpdc, 16) }, + { ORDATA (RPER2, rper2, 16) }, + { ORDATA (RPER3, rper3, 16) }, + { ORDATA (RPEC1, rpec1, 16) }, + { ORDATA (RPEC2, rpec2, 16) }, + { ORDATA (RPMR, rpmr, 16) }, + { ORDATA (RPDB, rpdb, 16) }, + { ORDATA (RPBAE, rpbae, 6) }, + { ORDATA (RPCS3, rpcs3, 16) }, + { FLDATA (INT, int_req, INT_V_RP) }, + { FLDATA (SC, rpcs1, CSR_V_ERR) }, + { FLDATA (DONE, rpcs1, CSR_V_DONE) }, + { FLDATA (IE, rpcs1, CSR_V_IE) }, + { DRDATA (STIME, rp_swait, 24), REG_NZ + PV_LEFT }, + { DRDATA (RTIME, rp_rwait, 24), REG_NZ + PV_LEFT }, + { ORDATA (RPDS0, rpds[0], 16) }, + { ORDATA (RPDS1, rpds[1], 16) }, + { ORDATA (RPDS2, rpds[2], 16) }, + { ORDATA (RPDS3, rpds[3], 16) }, + { ORDATA (RPDS4, rpds[4], 16) }, + { ORDATA (RPDS5, rpds[5], 16) }, + { ORDATA (RPDS6, rpds[6], 16) }, + { ORDATA (RPDS7, rpds[7], 16) }, + { ORDATA (RPDE0, rper1[0], 16) }, + { ORDATA (RPDE1, rper1[1], 16) }, + { ORDATA (RPDE2, rper1[2], 16) }, + { ORDATA (RPDE3, rper1[3], 16) }, + { ORDATA (RPDE4, rper1[4], 16) }, + { ORDATA (RPDE5, rper1[5], 16) }, + { ORDATA (RPDE6, rper1[6], 16) }, + { ORDATA (RPDE7, rper1[7], 16) }, + { GRDATA (FLG0, rp_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, rp_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, rp_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, rp_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, rp_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, rp_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, rp_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, rp_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { FLDATA (STOP_IOE, rp_stopioe, 0) }, + { NULL } }; + +MTAB rp_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rp_set_bad }, + { (UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM03", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP04", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM80", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP06", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM05", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP07", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE), + "RM03", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE), + "RP04", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE), + "RM80", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE), + "RP06", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE), + "RM05", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE), + "RP07", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (RM03_DTYPE << UNIT_V_DTYPE), + NULL, "RM03", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP04_DTYPE << UNIT_V_DTYPE), + NULL, "RP04", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RM80_DTYPE << UNIT_V_DTYPE), + NULL, "RM80", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP06_DTYPE << UNIT_V_DTYPE), + NULL, "RP06", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RM05_DTYPE << UNIT_V_DTYPE), + NULL, "RM05", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP07_DTYPE << UNIT_V_DTYPE), + NULL, "RP07", &rp_set_size }, + { 0 } }; + +DEVICE rp_dev = { + "RP", rp_unit, rp_reg, rp_mod, + RP_NUMDR, 8, 30, 1, 8, 16, + NULL, NULL, &rp_reset, + &rp_boot, &rp_attach, &rp_detach }; + +/* I/O dispatch routines, I/O addresses 17776700 - 17776776 */ + +t_stat rp_rd (int32 *data, int32 PA, int32 access) +{ +int32 drv, dtype, i, j; + +drv = GET_UNIT (rpcs2); /* get current unit */ +dtype = GET_DTYPE (rp_unit[drv].flags); /* get drive type */ +j = (PA >> 1) & 037; /* get reg offset */ +if (reg_in_drive[j] && (rp_unit[drv].flags & UNIT_DIS)) { /* nx disk */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + *data = 0; + return SCPE_OK; } + +update_rpcs (0, drv); /* update status */ +switch (j) { /* decode PA<5:1> */ +case 000: /* RPCS1 */ + *data = rpcs1; + break; +case 001: /* RPWC */ + *data = rpwc; + break; +case 002: /* RPBA */ + *data = rpba = rpba & ~BA_MBZ; + break; +case 003: /* RPDA */ + *data = rpda = rpda & ~DA_MBZ; + break; +case 004: /* RPCS2 */ + *data = rpcs2 = (rpcs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; + break; +case 005: /* RPDS */ + *data = rpds[drv]; + break; +case 006: /* RPER1 */ + *data = rper1[drv]; + break; +case 007: /* RPAS */ + *data = 0; + for (i = 0; i < RP_NUMDR; i++) + if (rpds[i] & DS_ATA) *data = *data | (AS_U0 << i); + break; +case 010: /* RPLA */ + *data = GET_SECTOR (rp_rwait, dtype) << LA_V_SC; + break; +case 011: /* RPDB */ + *data = rpdb; + break; +case 012: /* RPMR */ + *data = rpmr; + break; +case 013: /* RPDT */ + *data = drv_tab[dtype].devtype; + break; +case 014: /* RPSN */ + *data = 020 | (drv + 1); + break; +case 015: /* RPOF */ + *data = rpof = rpof & ~OF_MBZ; + break; +case 016: /* RPDC */ + *data = rpdc = rpdc & ~DC_MBZ; + break; +case 017: /* RPCC, RMHR */ + *data = rp_unit[drv].CYL; + break; +case 020: /* RPER2, RMMR2 */ + *data = rper2; + break; +case 021: /* RPER3, RMER2 */ + *data = rper3; + break; +case 022: /* RPEC1 */ + *data = rpec1; + break; +case 023: /* RPEC2 */ + *data = rpec2; + break; +case 024: /* RPBAE */ + *data = rpbae = rpbae & ~AE_MBZ; + break; +case 025: /* RPCS3 */ + *data = rpcs3 = (rpcs3 & ~(CS1_IE | CS3_MBZ)) | (rpcs1 & CS1_IE); + break; +default: /* all others */ + rper1[drv] = rper1[drv] | ER1_ILR; + update_rpcs (0, drv); + break; } +return SCPE_OK; +} + +t_stat rp_wr (int32 data, int32 PA, int32 access) +{ +int32 drv, i, j; + +drv = GET_UNIT (rpcs2); /* get current unit */ +j = (PA >> 1) & 037; /* get reg offset */ +if (reg_in_drive[j] && (rp_unit[drv].flags & UNIT_DIS)) { /* nx disk */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return SCPE_OK; } +if (reg_in_drive[j] && sim_is_active (&rp_unit[drv])) { /* unit busy? */ + rper1[drv] = rper1[drv] | ER1_RMR; /* won't write */ + update_rpcs (0, drv); + return SCPE_OK; } + +switch (j) { /* decode PA<5:1> */ +case 000: /* RPCS1 */ + if (access == WRITEB) data = (PA & 1)? + (rpcs1 & 0377) | (data << 8): (rpcs1 & ~0377) | data; + if ((data & CS1_IE) == 0) int_req = int_req & ~INT_RP; + else if ((((rpcs1 & CS1_IE) == 0) && (rpcs1 & CS1_DONE)) || + (data & CS1_DONE)) int_req = int_req | INT_RP; + rpcs1 = (rpcs1 & ~CS1_RW) | (data & CS1_RW); + rpbae = (rpbae & ~CS1_M_UAE) | ((rpcs1 >> CS1_V_UAE) & CS1_M_UAE); + rpcs3 = (rpcs3 & ~CS1_IE) | (rpcs1 & CS1_IE); + if (data & CS1_GO) { /* new command? */ + if (rpcs1 & CS1_DONE) rp_go (drv); /* start if not busy */ + else rpcs2 = rpcs2 | CS2_PGE; } /* else prog error */ + break; +case 001: /* RPWC */ + if (access == WRITEB) data = (PA & 1)? + (rpwc & 0377) | (data << 8): (rpwc & ~0377) | data; + rpwc = data; + break; +case 002: /* RPBA */ + if (access == WRITEB) data = (PA & 1)? + (rpba & 0377) | (data << 8): (rpba & ~0377) | data; + rpba = data & ~BA_MBZ; + break; +case 003: /* RPDA */ + if (access == WRITEB) data = (PA & 1)? + (rpda & 0377) | (data << 8): (rpda & ~0377) | data; + rpda = data & ~DA_MBZ; + break; +case 004: /* RPCS2 */ + if (access == WRITEB) data = (PA & 1)? + (rpcs2 & 0377) | (data << 8): (rpcs2 & ~0377) | data; + if (data & CS2_CLR) rp_reset (&rp_dev); + else rpcs2 = (rpcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; + drv = GET_UNIT (rpcs2); + break; +case 006: /* RPER1 */ + if (access == WRITEB) break; + rper1[drv] = rper1[drv] & data; + break; +case 007: /* RPAS */ + if (PA & 1) break; + for (i = 0; i < RP_NUMDR; i++) + if (data & (AS_U0 << i)) rpds[i] = rpds[i] & ~DS_ATA; + break; +case 011: /* RPDB */ + if (access == WRITEB) data = (PA & 1)? + (rpdb & 0377) | (data << 8): (rpdb & ~0377) | data; + rpdb = data; + break; +case 012: /* RPMR */ + if (access == WRITEB) data = (PA & 1)? + (rpmr & 0377) | (data << 8): (rpmr & ~0377) | data; + rpmr = data; + break; +case 015: /* RPOF */ + if (access == WRITEB) data = (PA & 1)? + (rpof & 0377) | (data << 8): (rpof & ~0377) | data; + rpof = data & ~OF_MBZ; + break; +case 016: /* RPDC */ + if (access == WRITEB) data = (PA & 1)? + (rpdc & 0377) | (data << 8): (rpdc & ~0377) | data; + rpdc = data & ~DC_MBZ; + break; +case 024: /* RPBAE */ + if (PA & 1) break; + rpbae = data & ~AE_MBZ; + rpcs1 = (rpcs1 & ~CS1_UAE) | ((rpbae << CS1_V_UAE) & CS1_UAE); + break; +case 025: /* RPCS3 */ + if (PA & 1) break; + rpcs3 = data & ~CS3_MBZ; + if ((data & CS1_IE) == 0) int_req = int_req & ~INT_RP; + else if (((rpcs1 & CS1_IE) == 0) && (rpcs1 & CS1_DONE)) + int_req = int_req | INT_RP; + rpcs1 = (rpcs1 & ~CS1_IE) | (rpcs3 & CS1_IE); + break; +case 005: /* RPDS */ +case 010: /* RPLA */ +case 013: /* RPDT */ +case 014: /* RPSN */ +case 017: /* RPDC, RMHR */ +case 020: /* RPER2, RMMN2 */ +case 021: /* RPER3, RMER2 */ +case 022: /* RPEC1 */ +case 023: /* RPEC2 */ + break; /* read only */ +default: /* all others */ + rper1[drv] = rper1[drv] | ER1_ILR; + break; } /* end switch */ +update_rpcs (0, drv); /* update status */ +return SCPE_OK; +} + +/* Initiate operation */ + +void rp_go (int32 drv) +{ + +int32 dc, dtype, fnc; +UNIT *uptr; + +fnc = GET_FNC (rpcs1); /* get function */ +uptr = rp_dev.units + drv; /* get unit */ +if (uptr -> flags & UNIT_DIS) { /* nx unit? */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return; } +if (((fnc != FNC_DCLR) && (rpds[drv] & DS_ERR)) || /* not clear & err? */ + ((rpds[drv] & DS_RDY) == 0)) { /* not ready? */ + rpcs2 = rpcs2 | CS2_PGE; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return; } +dtype = GET_DTYPE (uptr -> flags); /* get drive type */ +rpds[drv] = rpds[drv] & ~DS_ATA; /* clear attention */ +dc = rpdc; /* assume seek, sch */ + +switch (fnc) { /* case on function */ +case FNC_DCLR: /* drive clear */ + rpda = 0; /* clear disk addr */ + rper1[drv] = rper2 = rper3 = 0; /* clear errors */ +case FNC_NOP: /* no operation */ +case FNC_RELEASE: /* port release */ + return; + +case FNC_PRESET: /* read-in preset */ + rpdc = 0; /* clear disk addr */ + rpda = 0; + rpof = 0; /* clear offset */ +case FNC_PACK: /* pack acknowledge */ + rpds[drv] = rpds[drv] | DS_VV; /* set volume valid */ + return; + +case FNC_OFFSET: /* offset mode */ +case FNC_RETURN: + uptr -> FUNC = fnc; /* save function */ + rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ + sim_activate (uptr, rp_swait); /* time operation */ + return; + +case FNC_UNLOAD: /* unload */ +case FNC_RECAL: /* recalibrate */ + dc = 0; /* seek to 0 */ +case FNC_SEEK: /* seek */ +case FNC_SEARCH: /* search */ + if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SF (rpda) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SC (rpda) >= drv_tab[dtype].sect)) { /* or bad sector? */ + rper1[drv] = rper1[drv] | ER1_IAE; + break; } + rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ + sim_activate (uptr, rp_swait * abs (dc - uptr -> CYL)); + uptr -> FUNC = fnc; /* save function */ + uptr -> CYL = dc; /* save cylinder */ + return; + +case FNC_WRITE: /* write */ +case FNC_WCHK: /* write check */ +case FNC_READ: /* read */ + rpcs2 = rpcs2 & ~CS2_ERR; /* clear errors */ + rpcs1 = rpcs1 & ~(CS1_TRE | CS1_MCPE | CS1_DONE); + if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SF (rpda) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SC (rpda) >= drv_tab[dtype].sect)) { /* or bad sector? */ + rper1[drv] = rper1[drv] | ER1_IAE; + update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ + return; } + rpds[drv] = rpds[drv] & ~DS_RDY; /* clear drive rdy */ + sim_activate (uptr, rp_rwait + (rp_swait * abs (dc - uptr -> CYL))); + uptr -> FUNC = fnc; /* save function */ + uptr -> CYL = dc; /* save cylinder */ + return; + +default: /* all others */ + rper1[drv] = rper1[drv] | ER1_ILF; /* not supported */ + break; } +update_rpcs (CS1_SC, drv); /* error, req intr */ +return; +} + +/* Service unit timeout + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations +*/ + +static unsigned int16 fill[RP_NUMWD] = { 0 }; +t_stat rp_svc (UNIT *uptr) +{ +int32 dtype, drv, err; +int32 pa, wc, awc, twc, da, fillc; + +dtype = GET_DTYPE (uptr -> flags); /* get drive type */ +drv = uptr - rp_dev.units; /* get drv number */ +rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ + +switch (uptr -> FUNC) { /* case on function */ +case FNC_OFFSET: /* offset */ + rpds[drv] = rpds[drv] | DS_OF | DS_ATA; /* set offset, attention */ + update_rpcs (CS1_SC, drv); + return SCPE_OK; +case FNC_RETURN: /* return to centerline */ + rpds[drv] = (rpds[drv] & ~DS_OF) | DS_ATA; /* clear offset, set attn */ + update_rpcs (CS1_SC, drv); + return SCPE_OK; +case FNC_UNLOAD: /* unload */ + rp_detach (uptr); /* detach unit */ + return SCPE_OK; +case FNC_RECAL: /* recalibrate */ +case FNC_SEARCH: /* search */ +case FNC_SEEK: /* seek */ + rpds[drv] = rpds[drv] | DS_ATA; /* set attention */ + update_rpcs (CS1_SC, drv); + return SCPE_OK; + +case FNC_WRITE: /* write */ + if (uptr -> flags & UNIT_WLK) { /* write locked? */ + rper1[drv] = rper1[drv] | ER1_WLE; /* set drive error */ + update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ + return SCPE_OK; } +case FNC_WCHK: /* write check */ +case FNC_READ: /* read */ + pa = ((rpbae << 16) | rpba) >> 1; /* get mem addr */ + da = GET_DA (rpdc, rpda, dtype) * RP_NUMWD; /* get disk addr */ + twc = 0200000 - rpwc; /* get true wc */ + if (((t_addr) (pa + twc)) > (MEMSIZE / 2)) { /* mem overrun? */ + rpcs2 = rpcs2 | CS2_NEM; + wc = ((MEMSIZE / 2) - pa); + if (wc < 0) { /* abort transfer? */ + update_rpcs (CS1_DONE, drv); /* set done */ + return SCPE_OK; } } + else wc = twc; + if ((da + twc) > drv_tab[dtype].size) { /* disk overrun? */ + rper1[drv] = rper1[drv] | ER1_AOE; + if (wc > (drv_tab[dtype].size - da)) + wc = drv_tab[dtype].size - da; } + + err = fseek (uptr -> fileref, da * sizeof (int16), SEEK_SET); + + if ((uptr -> FUNC == FNC_READ) && (err == 0)) { /* read? */ + awc = fxread (&M[pa], sizeof (int16), wc, uptr -> fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; + err = ferror (uptr -> fileref); } + + if ((uptr -> FUNC == FNC_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int16), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + if ((err == 0) && (fillc = (wc & (RP_NUMWD - 1)))) { + fxwrite (fill, sizeof (int16), fillc, uptr -> fileref); + err = ferror (uptr -> fileref); } } + + if ((uptr -> FUNC == FNC_WCHK) && (err == 0)) { /* wcheck? */ + twc = wc; /* xfer length */ + for (wc = 0; (err == 0) && (wc < twc); wc++) { + awc = fxread (&rpdb, sizeof (int16), 1, uptr -> fileref); + if (awc == 0) rpdb = 0; + if (rpdb != M[pa + wc]) { + rpcs2 = rpcs2 | CS2_WCE; + break; } } + err = ferror (uptr -> fileref); } + + rpwc = (rpwc + wc) & 0177777; /* final word count */ + pa = (pa + wc) << 1; /* final byte addr */ + rpba = (pa & 0177777) & ~BA_MBZ; /* lower 16b */ + rpbae = (pa >> 16) & ~AE_MBZ; /* upper 6b */ + rpcs1 = (rpcs1 & ~ CS1_UAE) | ((rpbae << CS1_V_UAE) & CS1_UAE); + da = da + wc + (RP_NUMWD - 1); + if (da >= drv_tab[dtype].size) rpds[drv] = rpds[drv] | DS_LST; + da = da / RP_NUMWD; + rpda = da % drv_tab[dtype].sect; + da = da / drv_tab[dtype].sect; + rpda = rpda | ((da % drv_tab[dtype].surf) << DA_V_SF); + rpdc = da / drv_tab[dtype].surf; + + if (err != 0) { /* error? */ + rper1[drv] = rper1[drv] | ER1_PAR; /* set drive error */ + update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ + perror ("RP I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; } + update_rpcs (CS1_DONE, drv); /* set done */ + return SCPE_OK; } /* end case function */ +return SCPE_OK; +} + +/* Controller status update + First update drive status, then update RPCS1 + If optional argument, request interrupt +*/ + +void update_rpcs (int32 flag, int32 drv) +{ +int32 i; + +if (rp_unit[drv].flags & UNIT_DIS) rpds[drv] = rper1[drv] = 0; +else rpds[drv] = (rpds[drv] | DS_DPR) & ~DS_PGM; +if (rp_unit[drv].flags & UNIT_ATT) rpds[drv] = rpds[drv] | DS_MOL; +else rpds[drv] = rpds[drv] & ~(DS_MOL | DS_VV | DS_RDY); +if (rper1[drv] | rper2 | rper3) rpds[drv] = rpds[drv] | DS_ERR; +else rpds[drv] = rpds[drv] & ~DS_ERR; + +rpcs1 = (rpcs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ)) | CS1_DVA | flag; +if (rpcs2 & CS2_ERR) rpcs1 = rpcs1 | CS1_TRE | CS1_SC; +for (i = 0; i < RP_NUMDR; i++) + if (rpds[i] & DS_ATA) rpcs1 = rpcs1 | CS1_SC; +if (((rpcs1 & CS1_IE) == 0) || ((rpcs1 & CS1_DONE) == 0)) + int_req = int_req & ~INT_RP; +else if (flag) int_req = int_req | INT_RP; +return; +} + +/* Interrupt acknowledge */ + +int32 rp_inta (void) +{ +/* rpcs1 = rpcs1 & ~CS1_IE; /* clear int enable */ +/* rpcs3 = rpcs3 & ~CS1_IE; /* in both registers */ +return VEC_RP; /* acknowledge */ +} + +/* Device reset */ + +t_stat rp_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rpcs1 = CS1_DVA | CS1_DONE; +rpcs2 = CS2_IR | CS2_OR; +rpba = rpda = 0; +rpof = rpdc = 0; +rper2 = rper3 = 0; +rpec1 = rpec2 = 0; +rpbae = rpcs3 = 0; +int_req = int_req & ~INT_RP; +for (i = 0; i < RP_NUMDR; i++) { + uptr = rp_dev.units + i; + sim_cancel (uptr); + uptr -> CYL = uptr -> FUNC = 0; + if (uptr -> flags & UNIT_ATT) rpds[i] = (rpds[i] & DS_VV) | + DS_DPR | DS_RDY | DS_MOL | ((uptr -> flags & UNIT_WLK)? DS_WRL: 0); + else if (uptr -> flags & UNIT_DIS) rpds[i] = 0; + else rpds[i] = DS_DPR; + rper1[i] = 0; } +return SCPE_OK; +} + +/* Device attach */ + +t_stat rp_attach (UNIT *uptr, char *cptr) +{ +int drv, i, p; +t_stat r; + +uptr -> capac = drv_tab[GET_DTYPE (uptr -> flags)].size; +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +drv = uptr - rp_dev.units; /* get drv number */ +rpds[drv] = DS_ATA | DS_MOL | DS_RDY | DS_DPR | + ((uptr -> flags & UNIT_WLK)? DS_WRL: 0); +rper1[drv] = 0; +update_rpcs (CS1_SC, drv); + +if ((uptr -> flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +if (fseek (uptr -> fileref, 0, SEEK_END)) return SCPE_OK; +if ((p = ftell (uptr -> fileref)) == 0) return SCPE_OK; +for (i = 0; drv_tab[i].sect != 0; i++) { + if (p <= (drv_tab[i].size * (int) sizeof (int16))) { + uptr -> flags = (uptr -> flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr -> capac = drv_tab[i].size; + return SCPE_OK; } } +return SCPE_OK; +} + +/* Device detach */ + +t_stat rp_detach (UNIT *uptr) +{ +int32 drv; + +drv = uptr - rp_dev.units; /* get drv number */ +rpds[drv] = (rpds[drv] & ~(DS_MOL | DS_RDY | DS_WRL | DS_VV | DS_OF)) | + DS_ATA; +if (sim_is_active (uptr)) { /* unit active? */ + sim_cancel (uptr); /* cancel operation */ + rper1[drv] = rper1[drv] | ER1_OPI; /* set drive error */ + if (uptr -> FUNC >= FNC_WCHK) /* data transfer? */ + rpcs1 = rpcs1 | CS1_DONE | CS1_TRE; } /* set done, err */ +update_rpcs (CS1_SC, drv); /* request intr */ +return detach_unit (uptr); +} + +/* Set size command validation routine */ + +t_stat rp_set_size (UNIT *uptr, int32 value) +{ +if (uptr -> flags & UNIT_ATT) return SCPE_ALATT; +uptr -> capac = drv_tab[GET_DTYPE (value)].size; +return SCPE_OK; +} + +/* Set bad block routine */ + +t_stat rp_set_bad (UNIT *uptr, int32 value) +{ +return pdp11_bad_block (uptr, drv_tab[GET_DTYPE (uptr -> flags)].sect, RP_NUMWD); +} + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_UNIT 02006 /* where to store unit number */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0012706, 0002000, /* mov #2000, sp */ + 0012700, 0000000, /* mov #unit, r0 */ + 0012701, 0176700, /* mov #RPCS1, r1 */ + 0012737, 0000040, 0176710, /* mov #CS2_CLR, RPCS2 */ + 0010037, 0176710, /* mov r0, RPCS2 */ + 0012711, 0000021, /* mov #RIP+GO, (R1) */ + 0012737, 0010000, 0176732, /* mov #FMT16B, RPOF */ + 0005037, 0176750, /* clr RPBAE */ + 0005037, 0176704, /* clr RPBA */ + 0005037, 0176734, /* clr RPDC */ + 0005037, 0176706, /* clr RPDA */ + 0012737, 0177000, 0176702, /* mov #-512., RPWC */ + 0012711, 0000071, /* mov #READ+GO, (R1) */ + 0005002, /* clr R2 */ + 0005003, /* clr R3 */ + 0005004, /* clr R4 */ + 0012705, 0042120, /* mov #"DP, r5 */ + 0105711, /* tstb (R1) */ + 0100376, /* bpl .-2 */ + 0105011, /* clrb (R1) */ + 0005007 /* clr PC */ +}; + +t_stat rp_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & CS2_M_UNIT; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp11_rx.c b/pdp11_rx.c new file mode 100644 index 00000000..ba3f4511 --- /dev/null +++ b/pdp11_rx.c @@ -0,0 +1,425 @@ +/* pdp11_rx.c: RX11/RX01 floppy disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rx RX11 disk controller + + 14-Apr-99 RMS Changed t_addr to unsigned + + An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. + Tracks are numbered 0-76, sectors 1-26. +*/ + +#include "pdp11_defs.h" + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 +#define RX_NUMBY 128 /* bytes/sector */ +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_WLK (1u << UNIT_V_UF) + +#define IDLE 0 /* idle state */ +#define RWDS 1 /* rw, sect next */ +#define RWDT 2 /* rw, track next */ +#define FILL 3 /* fill buffer */ +#define EMPTY 4 /* empty buffer */ +#define CMD_COMPLETE 5 /* set done next */ +#define INIT_COMPLETE 6 /* init compl next */ + +#define RXCS_V_FUNC 1 /* function */ +#define RXCS_M_FUNC 7 +#define RXCS_FILL 0 /* fill buffer */ +#define RXCS_EMPTY 1 /* empty buffer */ +#define RXCS_WRITE 2 /* write sector */ +#define RXCS_READ 3 /* read sector */ +#define RXCS_RXES 5 /* read status */ +#define RXCS_WRDEL 6 /* write del data */ +#define RXCS_ECODE 7 /* read error code */ +#define RXCS_V_DRV 4 /* drive select */ +#define RXCS_V_DONE 5 /* done */ +#define RXCS_V_TR 7 /* xfer request */ +#define RXCS_V_INIT 14 /* init */ +#define RXCS_FUNC (RXCS_M_FUNC << RXCS_V_FUNC) +#define RXCS_DRV (1u << RXCS_V_DRV) +#define RXCS_DONE (1u << RXCS_V_DONE) +#define RXCS_TR (1u << RXCS_V_TR) +#define RXCS_INIT (1u << RXCS_V_INIT) +#define RXCS_ROUT (CSR_ERR+RXCS_TR+CSR_IE+RXCS_DONE) +#define RXCS_IMP (RXCS_ROUT+RXCS_DRV+RXCS_FUNC) +#define RXCS_RW (CSR_IE) /* read/write */ + +#define RXES_CRC 0001 /* CRC error */ +#define RXES_PAR 0002 /* parity error */ +#define RXES_ID 0004 /* init done */ +#define RXES_WLK 0010 /* write protect */ +#define RXES_DD 0100 /* deleted data */ +#define RXES_DRDY 0200 /* drive ready */ + +#define TRACK u3 /* current track */ +#define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY + +extern int32 int_req; +int32 rx_csr = 0; /* control/status */ +int32 rx_dbr = 0; /* data buffer */ +int32 rx_esr = 0; /* error status */ +int32 rx_ecode = 0; /* error code */ +int32 rx_track = 0; /* desired track */ +int32 rx_sector = 0; /* desired sector */ +int32 rx_state = IDLE; /* controller state */ +int32 rx_stopioe = 1; /* stop on error */ +int32 rx_cwait = 100; /* command time */ +int32 rx_swait = 10; /* seek, per track */ +int32 rx_xwait = 1; /* tr set time */ +unsigned int8 buf[RX_NUMBY] = { 0 }; /* sector buffer */ +int32 bptr = 0; /* buffer pointer */ +t_stat rx_svc (UNIT *uptr); +t_stat rx_reset (DEVICE *dptr); +t_stat rx_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* RX11 data structures + + rx_dev RX device descriptor + rx_unit RX unit list + rx_reg RX register list + rx_mod RX modifier list +*/ + +UNIT rx_unit[] = { + { UDATA (&rx_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) }, + { UDATA (&rx_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) } }; + +REG rx_reg[] = { + { ORDATA (RXCS, rx_csr, 16) }, + { ORDATA (RXDB, rx_dbr, 8) }, + { ORDATA (RXES, rx_esr, 8) }, + { ORDATA (RXERR, rx_ecode, 8) }, + { ORDATA (RXTA, rx_track, 8) }, + { ORDATA (RXSA, rx_sector, 8) }, + { ORDATA (STAPTR, rx_state, 3), REG_RO }, + { ORDATA (BUFPTR, bptr, 7) }, + { FLDATA (INT, int_req, INT_V_RX) }, + { FLDATA (ERR, rx_csr, CSR_V_ERR) }, + { FLDATA (TR, rx_csr, RXCS_V_TR) }, + { FLDATA (IE, rx_csr, CSR_V_IE) }, + { FLDATA (DONE, rx_csr, RXCS_V_DONE) }, + { DRDATA (CTIME, rx_cwait, 24), PV_LEFT }, + { DRDATA (STIME, rx_swait, 24), PV_LEFT }, + { DRDATA (XTIME, rx_xwait, 24), PV_LEFT }, + { FLDATA (FLG0, rx_unit[0].flags, UNIT_V_WLK), REG_HRO }, + { FLDATA (FLG1, rx_unit[1].flags, UNIT_V_WLK), REG_HRO }, + { FLDATA (STOP_IOE, rx_stopioe, 0) }, + { BRDATA (**BUF, buf, 8, 8, RX_NUMBY), REG_HRO }, + { NULL } }; + +MTAB rx_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE rx_dev = { + "RX", rx_unit, rx_reg, rx_mod, + RX_NUMDR, 8, 20, 1, 8, 8, + NULL, NULL, &rx_reset, + &rx_boot, NULL, NULL }; + +/* I/O dispatch routine, I/O addresses 17777170 - 17777172 + + 17777170 floppy CSR + 17777172 floppy data register +*/ + +t_stat rx_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 1) { /* decode PA<1> */ +case 0: /* RXCS */ + rx_csr = rx_csr & RXCS_IMP; /* clear junk */ + *data = rx_csr & RXCS_ROUT; + return SCPE_OK; +case 1: /* RXDB */ + if (rx_state == EMPTY) { /* empty? */ + sim_activate (&rx_unit[0], rx_xwait); + rx_csr = rx_csr & ~RXCS_TR; } /* clear xfer */ + *data = rx_dbr; /* return data */ + return SCPE_OK; } /* end switch PA */ +} + +t_stat rx_wr (int32 data, int32 PA, int32 access) +{ +int32 drv; + +switch ((PA >> 1) & 1) { /* decode PA<1> */ + +/* Writing RXCS, three cases: + 1. Writing INIT, reset device + 2. Idle and writing new function + - clear error, done, transfer ready, int req + - save int enable, function, drive + - start new function + 3. Otherwise, write IE and update interrupts +*/ + +case 0: /* RXCS */ + rx_csr = rx_csr & RXCS_IMP; /* clear junk */ + if (access == WRITEB) data = (PA & 1)? /* write byte? */ + (rx_csr & 0377) | (data << 8): (rx_csr & ~0377) | data; + if (data & RXCS_INIT) { /* initialize? */ + rx_reset (&rx_dev); /* reset device */ + return SCPE_OK; } /* end if init */ + if ((data & CSR_GO) && (rx_state == IDLE)) { /* new function? */ + rx_csr = data & (CSR_IE + RXCS_DRV + RXCS_FUNC); + bptr = 0; /* clear buf pointer */ + switch ((data >> RXCS_V_FUNC) & RXCS_M_FUNC) { + case RXCS_FILL: + rx_state = FILL; /* state = fill */ + rx_csr = rx_csr | RXCS_TR; /* xfer is ready */ + break; + case RXCS_EMPTY: + rx_state = EMPTY; /* state = empty */ + sim_activate (&rx_unit[0], rx_xwait); + break; + case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: + rx_state = RWDS; /* state = get sector */ + rx_csr = rx_csr | RXCS_TR; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + default: + rx_state = CMD_COMPLETE; /* state = cmd compl */ + drv = (data & RXCS_DRV) > 0; /* get drive number */ + sim_activate (&rx_unit[drv], rx_cwait); + break; } /* end switch func */ + return SCPE_OK; } /* end if GO */ + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_RX; + else if ((rx_csr & (RXCS_DONE + CSR_IE)) == RXCS_DONE) + int_req = int_req | INT_RX; + rx_csr = (rx_csr & ~RXCS_RW) | (data & RXCS_RW); + return SCPE_OK; /* end case RXCS */ + +/* Accessing RXDB, two cases: + 1. Write idle, write + 2. Write not idle and TR set, state dependent +*/ + +case 1: /* RXDB */ + if ((PA & 1) || ((rx_state != IDLE) && ((rx_csr & RXCS_TR) == 0))) + return SCPE_OK; /* if ~IDLE, need tr */ + rx_dbr = data & 0377; /* save data */ + if ((rx_state == FILL) || (rx_state == RWDS)) { /* fill or sector? */ + sim_activate (&rx_unit[0], rx_xwait); /* sched event */ + rx_csr = rx_csr & ~RXCS_TR; } /* clear xfer */ + if (rx_state == RWDT) { /* track? */ + drv = (rx_csr & RXCS_DRV) > 0; /* get drive number */ + sim_activate (&rx_unit[drv], /* sched done */ + rx_swait * abs (rx_track - rx_unit[drv].TRACK)); + rx_csr = rx_csr & ~RXCS_TR; } /* clear xfer */ + return SCPE_OK; /* end case RXDB */ + } /* end switch PA */ +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here, treat as unknown command + RWDS Just transferred sector, wait for track, set tr + RWDT Just transferred track, do read or write, finish command + FILL copy ir to buf[bptr], advance ptr + if bptr > max, finish command, else set tr + EMPTY if bptr > max, finish command, else + copy buf[bptr] to ir, advance ptr, set tr + CMD_COMPLETE copy requested data to ir, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command + + For RWDT and CMD_COMPLETE, the input argument is the selected drive; + otherwise, it is drive 0. +*/ + +t_stat rx_svc (UNIT *uptr) +{ +int32 i, func; +t_addr da; +t_stat rval; +void rx_done (int new_dbr, int new_ecode); + +rval = SCPE_OK; /* assume ok */ +func = (rx_csr >> RXCS_V_FUNC) & RXCS_M_FUNC; /* get function */ +switch (rx_state) { /* case on state */ +case IDLE: /* idle */ + rx_done (rx_esr, 0); /* done */ + break; +case EMPTY: /* empty buffer */ + if (bptr >= RX_NUMBY) rx_done (rx_esr, 0); /* done all? */ + else { rx_dbr = buf[bptr]; /* get next */ + bptr = bptr + 1; + rx_csr = rx_csr | RXCS_TR; } /* set xfer */ + break; +case FILL: /* fill buffer */ + buf[bptr] = rx_dbr; /* write next */ + bptr = bptr + 1; + if (bptr < RX_NUMBY) rx_csr = rx_csr | RXCS_TR; /* if more, set xfer */ + else rx_done (rx_esr, 0); /* else done */ + break; +case RWDS: /* wait for sector */ + rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */ + rx_csr = rx_csr | RXCS_TR; /* set xfer */ + rx_state = RWDT; /* advance state */ + break; +case RWDT: /* wait for track */ + rx_track = rx_dbr & RX_M_TRACK; /* save track */ + if (rx_track >= RX_NUMTR) { /* bad track? */ + rx_done (rx_esr, 0040); /* done, error */ + break; } + uptr -> TRACK = rx_track; /* now on track */ + if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ + rx_done (rx_esr, 0070); /* done, error */ + break; } + if ((uptr -> flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (rx_esr, 0110); /* done, error */ + rval = SCPE_UNATT; /* return error */ + break; } + da = CALC_DA (rx_track, rx_sector); /* get disk address */ + if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */ + if (func == RXCS_READ) { /* read? */ + for (i = 0; i < RX_NUMBY; i++) + buf[i] = *(((int8 *) uptr -> filebuf) + da + i); } + else { if (uptr -> flags & UNIT_WLK) { /* write and locked? */ + rx_esr = rx_esr | RXES_WLK; /* flag error */ + rx_done (rx_esr, 0100); /* done, error */ + break; } + for (i = 0; i < RX_NUMBY; i++) /* write */ + *(((int8 *) uptr -> filebuf) + da + i) = buf[i]; + da = da + RX_NUMBY; + if (da > uptr -> hwmark) uptr -> hwmark = da; } + rx_done (rx_esr, 0); /* done */ + break; +case CMD_COMPLETE: /* command complete */ + if (func == RXCS_ECODE) rx_done (rx_ecode, 0); + else if (uptr -> flags & UNIT_ATT) rx_done (rx_esr | RXES_DRDY, 0); + else rx_done (rx_esr, 0); + break; +case INIT_COMPLETE: /* init complete */ + rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ + rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ + if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (rx_esr | RXES_ID, 0010); /* init done, error */ + break; } + da = CALC_DA (1, 1); /* track 1, sector 1 */ + for (i = 0; i < RX_NUMBY; i++) /* read sector */ + buf[i] = *(((int8 *) uptr -> filebuf) + da + i); + rx_done (rx_esr | RXES_ID | RXES_DRDY, 0); /* set done */ + if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020; + break; } /* end case state */ +return IORETURN (rx_stopioe, rval); +} + +/* Command complete. Set done and put final value in interface register, + request interrupt if needed, return to IDLE state. +*/ + +void rx_done (int32 new_dbr, int32 new_ecode) +{ +rx_csr = rx_csr | RXCS_DONE; /* set done */ +if (rx_csr & CSR_IE) int_req = int_req | INT_RX; /* if ie, intr */ +rx_dbr = new_dbr; /* update RXDB */ +if (new_ecode != 0) { /* test for error */ + rx_ecode = new_ecode; + rx_csr = rx_csr | CSR_ERR; } +rx_state = IDLE; /* now idle */ +return; +} + +/* Device initialization. The RX is one of the few devices that schedules + an I/O transfer as part of its initialization. +*/ + +t_stat rx_reset (DEVICE *dptr) +{ +rx_csr = rx_dbr = 0; /* clear regs */ +rx_esr = rx_ecode = 0; /* clear error */ +rx_state = INIT_COMPLETE; /* set state */ +int_req = int_req & ~INT_RX; /* clear int req */ +sim_cancel (&rx_unit[1]); /* cancel drive 1 */ +sim_activate (&rx_unit[0], /* start drive 0 */ + rx_swait * abs (1 - rx_unit[0].TRACK)); +return SCPE_OK; +} + +/* Device bootstrap */ + +#define BOOT_START 02000 +#define BOOT_UNIT 02006 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0012706, 0002000, /* MOV #2000, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0012701, 0177170, /* MOV #RXCS, R1 ; csr */ + 0032711, 0000040, /* BITB #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0052703, 0000007, /* BIS #READ+GO, R3 */ + 0010311, /* MOV R3, (R1) ; read & go */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; sector */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */ + 0005003, /* CLR R3 */ + 0032711, 0000040, /* BITB #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0012711, 0000003, /* MOV #EMPTY+GO, (R1) ; empty & go */ + 0105711, /* TSTB (R1) ; xfr, done? */ + 0001776, /* BEQ .-2 */ + 0100003, /* BPL .+010 */ + 0116123, 0000002, /* MOVB 2(R1), (R3)+ ; move byte */ + 0000772, /* BR .-012 */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0005004, /* CLR R4 */ + 0012705, 0062170, /* MOV #"DX, R5 */ + 0005007 /* CLR R7 */ +}; + +t_stat rx_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; +extern unsigned short *M; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp11_stddev.c b/pdp11_stddev.c new file mode 100644 index 00000000..6a5bd7ec --- /dev/null +++ b/pdp11_stddev.c @@ -0,0 +1,502 @@ +/* pdp11_stddev.c: PDP-11 standard I/O devices simulator + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + tti terminal input + tto terminal output + clk line frequency clock + + 30-Oct-00 RMS Standardized register order + 25-Jun-98 RMS Fixed bugs in paper tape error handling +*/ + +#include "pdp11_defs.h" + +#define PTRCSR_IMP (CSR_ERR+CSR_BUSY+CSR_DONE+CSR_IE) /* paper tape reader */ +#define PTRCSR_RW (CSR_IE) +#define PTPCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* paper tape punch */ +#define PTPCSR_RW (CSR_IE) +#define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ +#define TTICSR_RW (CSR_IE) +#define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ +#define TTOCSR_RW (CSR_IE) +#define CLKCSR_IMP (CSR_IE) /* real-time clock */ +#define CLKCSR_RW (CSR_IE) + +extern int32 int_req; +int32 ptr_csr = 0; /* control/status */ +int32 ptr_stopioe = 0; /* stop on error */ +int32 ptp_csr = 0; /* control/status */ +int32 ptp_stopioe = 0; /* stop on error */ +int32 tti_csr = 0; /* control/status */ +int32 tto_csr = 0; /* control/status */ +int32 clk_csr = 0; /* control/status */ +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat clk_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat clk_reset (DEVICE *dptr); +t_stat ptr_attach (UNIT *uptr, char *ptr); +t_stat ptr_detach (UNIT *uptr); +t_stat ptp_attach (UNIT *uptr, char *ptr); +t_stat ptp_detach (UNIT *uptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { ORDATA (CSR, ptr_csr, 16) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { FLDATA (ERR, ptr_csr, CSR_V_ERR) }, + { FLDATA (BUSY, ptr_csr, CSR_V_BUSY) }, + { FLDATA (DONE, ptr_csr, CSR_V_DONE) }, + { FLDATA (IE, ptr_csr, CSR_V_IE) }, + { DRDATA (POS, ptr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + NULL, &ptr_attach, &ptr_detach }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { ORDATA (CSR, ptp_csr, 16) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { FLDATA (ERR, ptp_csr, CSR_V_ERR) }, + { FLDATA (DONE, ptp_csr, CSR_V_DONE) }, + { FLDATA (IE, ptp_csr, CSR_V_IE) }, + { DRDATA (POS, ptp_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, &ptp_attach, &ptp_detach }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { ORDATA (CSR, tti_csr, 16) }, + { FLDATA (INT, int_req, INT_V_TTI) }, + { FLDATA (ERR, tti_csr, CSR_V_ERR) }, + { FLDATA (DONE, tti_csr, CSR_V_DONE) }, + { FLDATA (IE, tti_csr, CSR_V_IE) }, + { DRDATA (POS, tti_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { ORDATA (CSR, tto_csr, 16) }, + { FLDATA (INT, int_req, INT_V_TTO) }, + { FLDATA (ERR, tto_csr, CSR_V_ERR) }, + { FLDATA (DONE, tto_csr, CSR_V_DONE) }, + { FLDATA (IE, tto_csr, CSR_V_IE) }, + { DRDATA (POS, tto_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 8000 }; + +REG clk_reg[] = { + { ORDATA (CSR, clk_csr, 16) }, + { FLDATA (INT, int_req, INT_V_CLK) }, + { FLDATA (DONE, clk_csr, CSR_V_DONE) }, + { FLDATA (IE, clk_csr, CSR_V_IE) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL }; + +/* Standard I/O dispatch routine, I/O addresses 17777546-17777567 + + 17777546 clock CSR + 17777550 ptr CSR + 17777552 ptr buffer + 17777554 ptp CSR + 17777556 ptp buffer + 17777560 tti CSR + 17777562 tti buffer + 17777564 tto CSR + 17777566 tto buffer + + Note: Word access routines filter out odd addresses. Thus, + an odd address implies an (odd) byte access. +*/ + +t_stat std_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 017) { /* decode PA<4:1> */ +case 03: /* clk csr */ + *data = clk_csr & CLKCSR_IMP; + return SCPE_OK; +case 04: /* ptr csr */ + *data = ptr_csr & PTRCSR_IMP; + return SCPE_OK; +case 05: /* ptr buf */ + ptr_csr = ptr_csr & ~CSR_DONE; + int_req = int_req & ~INT_PTR; + *data = ptr_unit.buf & 0377; + return SCPE_OK; +case 06: /* ptp csr */ + *data = ptp_csr & PTPCSR_IMP; + return SCPE_OK; +case 07: /* ptp buf */ + *data = ptp_unit.buf; + return SCPE_OK; +case 010: /* tti csr */ + *data = tti_csr & TTICSR_IMP; + return SCPE_OK; +case 011: /* tti buf */ + tti_csr = tti_csr & ~CSR_DONE; + int_req = int_req & ~INT_TTI; + *data = tti_unit.buf & 0377; + return SCPE_OK; +case 012: /* tto csr */ + *data = tto_csr & TTOCSR_IMP; + return SCPE_OK; +case 013: /* tto buf */ + *data = tto_unit.buf; + return SCPE_OK; } /* end switch PA */ +return SCPE_NXM; /* can't get here */ +} + +t_stat std_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 017) { /* decode PA<4:1> */ +case 03: /* clk csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_CLK; + clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); + sim_activate (&clk_unit, clk_unit.wait); + return SCPE_OK; +case 04: /* ptr csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_PTR; + else if (((ptr_csr & CSR_IE) == 0) && (ptr_csr & (CSR_ERR | CSR_DONE))) + int_req = int_req | INT_PTR; + if (data & CSR_GO) { + ptr_csr = (ptr_csr & ~CSR_DONE) | CSR_BUSY; + int_req = int_req & ~INT_PTR; + if (ptr_unit.flags & UNIT_ATT) /* data to read? */ + sim_activate (&ptr_unit, ptr_unit.wait); + else sim_activate (&ptr_unit, 0); } /* error if not */ + ptr_csr = (ptr_csr & ~PTRCSR_RW) | (data & PTRCSR_RW); + return SCPE_OK; +case 05: /* ptr buf */ + return SCPE_OK; +case 06: /* ptp csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_PTP; + else if (((ptp_csr & CSR_IE) == 0) && (ptp_csr & (CSR_ERR | CSR_DONE))) + int_req = int_req | INT_PTP; + ptp_csr = (ptp_csr & ~PTPCSR_RW) | (data & PTPCSR_RW); + return SCPE_OK; +case 07: /* ptp buf */ + if ((PA & 1) == 0) ptp_unit.buf = data & 0377; + ptp_csr = ptp_csr & ~CSR_DONE; + int_req = int_req & ~INT_PTP; + if (ptp_unit.flags & UNIT_ATT) /* file to write? */ + sim_activate (&ptp_unit, ptp_unit.wait); + else sim_activate (&ptp_unit, 0); /* error if not */ + return SCPE_OK; +case 010: /* tti csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_TTI; + else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + int_req = int_req | INT_TTI; + tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW); + return SCPE_OK; +case 011: /* tti buf */ + return SCPE_OK; +case 012: /* tto csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_TTO; + else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + int_req = int_req | INT_TTO; + tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW); + return SCPE_OK; +case 013: /* tto buf */ + if ((PA & 1) == 0) tto_unit.buf = data & 0377; + tto_csr = tto_csr & ~CSR_DONE; + int_req = int_req & ~INT_TTO; + sim_activate (&tto_unit, tto_unit.wait); + return SCPE_OK; } /* end switch PA */ +return SCPE_NXM; /* can't get here */ +} + +/* Paper tape reader routines + + ptr_svc process event (character ready) + ptr_reset process reset + ptr_attach process attach + ptr_detach process detach +*/ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +ptr_csr = (ptr_csr | CSR_ERR) & ~CSR_BUSY; +if (ptr_csr & CSR_IE) int_req = int_req | INT_PTR; +if ((ptr_unit.flags & UNIT_ATT) == 0) + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; } +ptr_csr = (ptr_csr | CSR_DONE) & ~CSR_ERR; +ptr_unit.buf = temp & 0377; +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; +ptr_csr = 0; +if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR; +int_req = int_req & ~INT_PTR; +sim_cancel (&ptr_unit); +return SCPE_OK; +} + +t_stat ptr_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR; +else ptr_csr = ptr_csr & ~CSR_ERR; +return reason; +} + +t_stat ptr_detach (UNIT *uptr) +{ +ptr_csr = ptr_csr | CSR_ERR; +return detach_unit (uptr); +} + +/* Paper tape punch routines + + ptp_svc process event (character punched) + ptp_reset process reset + ptp_attach process attach + ptp_detach process detach +*/ + +t_stat ptp_svc (UNIT *uptr) +{ +ptp_csr = ptp_csr | CSR_ERR | CSR_DONE; +if (ptp_csr & CSR_IE) int_req = int_req | INT_PTP; +if ((ptp_unit.flags & UNIT_ATT) == 0) + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; } +ptp_csr = ptp_csr & ~CSR_ERR; +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +ptp_csr = CSR_DONE; +if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR; +int_req = int_req & ~INT_PTP; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat ptp_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR; +else ptp_csr = ptp_csr & ~CSR_ERR; +return reason; +} + +t_stat ptp_detach (UNIT *uptr) +{ +ptp_csr = ptp_csr | CSR_ERR; +return detach_unit (uptr); +} + +/* Terminal input routines + + tti_svc process event (character ready) + tti_reset process reset +*/ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +tti_unit.buf = temp & 0377; +tti_unit.pos = tti_unit.pos + 1; +tti_csr = tti_csr | CSR_DONE; +if (tti_csr & CSR_IE) int_req = int_req | INT_TTI; +return SCPE_OK; +} + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +tti_csr = 0; +int_req = int_req & ~INT_TTI; +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output routines + + tto_svc process event (character typed) + tto_reset process reset +*/ + +t_stat tto_svc (UNIT *uptr) +{ +int32 temp; + +tto_csr = tto_csr | CSR_DONE; +if (tto_csr & CSR_IE) int_req = int_req | INT_TTO; +if ((temp = sim_putchar (tto_unit.buf & 0177)) != SCPE_OK) return temp; +tto_unit.pos = tto_unit.pos + 1; +return SCPE_OK; +} + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +tto_csr = CSR_DONE; +int_req = int_req & ~INT_TTO; +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Clock routines + + clk_svc process event (clock tick) + clk_reset process reset +*/ + +t_stat clk_svc (UNIT *uptr) +{ +if (clk_csr & CSR_IE) int_req = int_req | INT_CLK; +sim_activate (&clk_unit, clk_unit.wait); /* reactivate unit */ +return SCPE_OK; +} + +t_stat clk_reset (DEVICE *dptr) +{ +clk_csr = 0; +int_req = int_req & ~INT_CLK; +sim_activate (&clk_unit, clk_unit.wait); /* activate unit */ +return SCPE_OK; +} diff --git a/pdp11_sys.c b/pdp11_sys.c new file mode 100644 index 00000000..22a0af23 --- /dev/null +++ b/pdp11_sys.c @@ -0,0 +1,838 @@ +/* pdp11_sys.c: PDP-11 simulator interface + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 14-Apr-99 RMS Changed t_addr to unsigned + 09-Nov-98 RMS Fixed assignments of ROR/ROL (John Wilson). + 27-Oct-98 RMS V2.4 load interface. + 08-Oct-98 RMS Fixed bug in bad block routine. + 30-Mar-98 RMS Fixed bug in floating point display. + 12-Nov-97 RMS Added bad block table routine. +*/ + +#include "pdp11_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE lpt_dev, clk_dev; +extern DEVICE rk_dev, rx_dev; +extern DEVICE rl_dev, rp_dev; +extern DEVICE tm_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern unsigned int16 *M; +extern int32 saved_PC; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-11"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { &cpu_dev, + &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, + &lpt_dev, &clk_dev, &rk_dev, &rl_dev, + &rp_dev, &rx_dev, &tm_dev, NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Red stack trap", + "Odd address trap", + "Memory management trap", + "Non-existent memory trap", + "Parity error trap", + "Privilege trap", + "Illegal instruction trap", + "BPT trap", + "IOT trap", + "EMT trap", + "TRAP trap", + "Trace trap", + "Yellow stack trap", + "Powerfail trap", + "Floating point exception", + "HALT instruction", + "Breakpoint", + "Wait state", + "Trap vector fetch abort", + "Trap stack push abort" }; + +/* Binary loader. + + Loader format consists of blocks, optionally preceded, separated, and + followed by zeroes. Each block consists of: + + 001 --- + xxx | + lo_count | + hi_count | + lo_origin > count bytes + hi_origin | + data byte | + : | + data byte --- + checksum + + If the byte count is exactly six, the block is the last on the tape, and + there is no checksum. If the origin is not 000001, then the origin is + the PC at which to start the program. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 csum, count, state, i; +t_addr origin; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +state = csum = 0; +while ((i = getc (fileref)) != EOF) { + csum = csum + i; /* add into chksum */ + switch (state) { + case 0: /* leader */ + if (i == 1) state = 1; + else csum = 0; + break; + case 1: /* ignore after 001 */ + state = 2; + break; + case 2: /* low count */ + count = i; + state = 3; + break; + case 3: /* high count */ + count = (i << 8) | count; + state = 4; + break; + case 4: /* low origin */ + origin = i; + state = 5; + break; + case 5: /* high origin */ + origin = (i << 8) | origin; + if (count == 6) { + if (origin != 1) saved_PC = origin & 0177776; + return SCPE_OK; } + count = count - 6; + state = 6; + break; + case 6: /* data */ + if (origin >= MEMSIZE) return SCPE_NXM; + M[origin >> 1] = (origin & 1)? + (M[origin >> 1] & 0377) | (i << 8): + (M[origin >> 1] & 0177400) | i; + origin = origin + 1; + count = count - 1; + state = state + (count == 0); + break; + case 7: /* checksum */ + if (csum & 0377) return SCPE_CSUM; + csum = state = 0; + break; } /* end switch */ + } /* end while */ +return SCPE_FMT; /* unexpected eof */ +} + +/* Factory bad block table creation routine + + This routine writes a DEC standard 044 compliant bad block table on the + last track of the specified unit. The bad block table consists of 10 + repetitions of the same table, formatted as follows: + + words 0-1 pack id number + words 2-3 cylinder/sector/surface specifications + : + words n-n+1 end of table (-1,-1) + + Inputs: + uptr = pointer to unit + sec = number of sectors per surface + wds = number of words per sector + Outputs: + sta = status code +*/ + +t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds) +{ +int32 i, da; +int16 *buf; + +if ((sec < 2) || (wds < 16)) return SCPE_ARG; +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_UNATT; +if (!get_yn ("Overwrite last track? [N]", FALSE)) return SCPE_OK; +da = (uptr -> capac - (sec * wds)) * sizeof (int16); +if (fseek (uptr -> fileref, da, SEEK_SET)) return SCPE_IOERR; +if ((buf = malloc (wds * sizeof (int16))) == NULL) return SCPE_MEM; +buf[0] = buf[1] = 012345u; +buf[2] = buf[3] = 0; +for (i = 4; i < wds; i++) buf[i] = 0177777u; +for (i = 0; (i < sec) && (i < 10); i++) + fxwrite (buf, sizeof (int16), wds, uptr -> fileref); +free (buf); +if (ferror (uptr -> fileref)) return SCPE_IOERR; +return SCPE_OK; +} + +/* Symbol tables */ + +#define I_V_L 16 /* long mode */ +#define I_V_D 17 /* double mode */ +#define I_L (1 << I_V_L) +#define I_D (1 << I_V_D) + +/* Warning: for literals, the class number MUST equal the field width!! */ + +#define I_V_CL 18 /* class bits */ +#define I_M_CL 017 /* class mask */ +#define I_V_NPN 0 /* no operands */ +#define I_V_REG 1 /* reg */ +#define I_V_SOP 2 /* operand */ +#define I_V_3B 3 /* 3b literal */ +#define I_V_FOP 4 /* flt operand */ +#define I_V_AFOP 5 /* fac, flt operand */ +#define I_V_6B 6 /* 6b literal */ +#define I_V_BR 7 /* cond branch */ +#define I_V_8B 8 /* 8b literal */ +#define I_V_SOB 9 /* reg, disp */ +#define I_V_RSOP 10 /* reg, operand */ +#define I_V_ASOP 11 /* fac, operand */ +#define I_V_ASMD 12 /* fac, moded int op */ +#define I_V_DOP 13 /* double operand */ +#define I_V_CCC 14 /* CC clear */ +#define I_V_CCS 15 /* CC set */ +#define I_NPN (I_V_NPN << I_V_CL) +#define I_REG (I_V_REG << I_V_CL) +#define I_3B (I_V_3B << I_V_CL) +#define I_SOP (I_V_SOP << I_V_CL) +#define I_FOP (I_V_FOP << I_V_CL) +#define I_6B (I_V_6B << I_V_CL) +#define I_BR (I_V_BR << I_V_CL) +#define I_8B (I_V_8B << I_V_CL) +#define I_AFOP (I_V_AFOP << I_V_CL) +#define I_ASOP (I_V_ASOP << I_V_CL) +#define I_RSOP (I_V_RSOP << I_V_CL) +#define I_SOB (I_V_SOB << I_V_CL) +#define I_ASMD (I_V_ASMD << I_V_CL) +#define I_DOP (I_V_DOP << I_V_CL) +#define I_CCC (I_V_CCC << I_V_CL) +#define I_CCS (I_V_CCS << I_V_CL) + +static const int32 masks[] = { +0177777, 0177770, 0177700, 0177770, +0177700+I_D, 0177400+I_D, 0177700, 0177400, +0177400, 0177000, 0177000, 0177400, +0177400+I_D+I_L, 0170000, 0177777, 0177777 }; + +static const char *opcode[] = { +"HALT","WAIT","RTI","BPT", +"IOT","RESET","RTT","MFPT", +"JMP","RTS","SPL", +"NOP","CLC","CLV","CLV CLC", +"CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC", +"CLN","CLN CLC","CLN CLV","CLN CLV CLC", +"CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC", +"NOP","SEC","SEV","SEV SEC", +"SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC", +"SEN","SEN SEC","SEN SEV","SEN SEV SEC", +"SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC", +"SWAB","BR","BNE","BEQ", +"BGE","BLT","BGT","BLE", +"JSR", +"CLR","COM","INC","DEC", +"NEG","ADC","SBC","TST", +"ROR","ROL","ASR","ASL", +"MARK","MFPI","MTPI","SXT", +"CSM", "TSTSET","WRTLCK", +"MOV","CMP","BIT","BIC", +"BIS","ADD", +"MUL","DIV","ASH","ASHC", +"XOR", +"FADD","FSUB","FMUL","FDIV", +"L2DR", +"MOVC","MOVRC","MOVTC", +"LOCC","SKPC","SCANC","SPANC", +"CMPC","MATC", +"ADDN","SUBN","CMPN","CVTNL", +"CVTPN","CVTNP","ASHN","CVTLN", +"L3DR", +"ADDP","SUBP","CMPP","CVTPL", +"MULP","DIVP","ASHP","CVTLP", +"MOVCI","MOVRCI","MOVTCI", +"LOCCI","SKPCI","SCANCI","SPANCI", +"CMPCI","MATCI", +"ADDNI","SUBNI","CMPNI","CVTNLI", +"CVTPNI","CVTNPI","ASHNI","CVTLNI", +"ADDPI","SUBPI","CMPPI","CVTPLI", +"MULPI","DIVPI","ASHPI","CVTLPI", +"SOB", +"BPL","BMI","BHI","BLOS", +"BVC","BVS","BCC","BCS", +"BHIS","BLO", /* encode only */ +"EMT","TRAP", +"CLRB","COMB","INCB","DECB", +"NEGB","ADCB","SBCB","TSTB", +"RORB","ROLB","ASRB","ASLB", +"MTPS","MFPD","MTPD","MFPS", +"MOVB","CMPB","BITB","BICB", +"BISB","SUB", +"CFCC","SETF","SETI","SETD","SETL", +"LDFPS","STFPS","STST", +"CLRF","CLRD","TSTF","TSTD", +"ABSF","ABSD","NEGF","NEGD", +"MULF","MULD","MODF","MODD", +"ADDF","ADDD","LDF","LDD", +"SUBF","SUBD","CMPF","CMPD", +"STF","STD","DIVF","DIVD", +"STEXP", +"STCFI","STCDI","STCFL","STCDL", +"STCFD","STCDF", +"LDEXP", +"LDCIF","LDCID","LDCLF","LDCLD", +"LDCFD","LDCDF", +NULL }; + +static const int32 opc_val[] = { +0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN, +0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN, +0000100+I_SOP, 0000200+I_REG, 0000230+I_3B, +0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN, +0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN, +0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN, +0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC, +0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN, +0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN, +0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN, +0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS, +0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR, +0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR, +0004000+I_RSOP, +0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP, +0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP, +0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP, +0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP, +0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP, +0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP, +0050000+I_DOP, 0060000+I_DOP, +0070000+I_RSOP, 0071000+I_RSOP, 0072000+I_RSOP, 0073000+I_RSOP, +0074000+I_RSOP, +0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG, +0076020+I_REG, +0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN, +0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN, +0076044+I_NPN, 0076045+I_NPN, +0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN, +0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN, +0076060+I_REG, +0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN, +0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN, +0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN, +0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN, +0076144+I_NPN, 0076145+I_NPN, +0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN, +0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN, +0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN, +0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN, +0077000+I_SOB, +0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR, +0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR, +0103000+I_BR, 0103400+I_BR, +0104000+I_8B, 0104400+I_8B, +0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP, +0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP, +0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP, +0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP, +0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP, +0150000+I_DOP, 0160000+I_DOP, +0170000+I_NPN, 0170001+I_NPN, 0170002+I_NPN, 0170011+I_NPN, 0170012+I_NPN, +0170100+I_SOP, 0170200+I_SOP, 0170300+I_SOP, +0170400+I_FOP, 0170400+I_FOP+I_D, 0170500+I_FOP, 0170500+I_FOP+I_D, +0170600+I_FOP, 0170600+I_FOP+I_D, 0170700+I_FOP, 0170700+I_FOP+I_D, +0171000+I_AFOP, 0171000+I_AFOP+I_D, 0171400+I_AFOP, 0171400+I_AFOP+I_D, +0172000+I_AFOP, 0172000+I_AFOP+I_D, 0172400+I_AFOP, 0172400+I_AFOP+I_D, +0173000+I_AFOP, 0173000+I_AFOP+I_D, 0173400+I_AFOP, 0173400+I_AFOP+I_D, +0174000+I_AFOP, 0174000+I_AFOP+I_D, 0174400+I_AFOP, 0174400+I_AFOP+I_D, +0175000+I_ASOP, +0175400+I_ASMD, 0175400+I_ASMD+I_D, 0175400+I_ASMD+I_L, 0175400+I_ASMD+I_D+I_L, +0176000+I_AFOP, 0176000+I_AFOP+I_D, +0176400+I_ASOP, +0177000+I_ASMD, 0177000+I_ASMD+I_D, 0177000+I_ASMD+I_L, 0177000+I_ASMD+I_D+I_L, +0177400+I_AFOP, 0177400+I_AFOP+I_D, +-1 }; + +static const char *rname [] = +{ "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" }; + +static const char *fname [] = +{ "F0", "F1", "F2", "F3", "F4", "F5", "?6", "?7" }; + +/* Specifier decode + + Inputs: + *of = output stream + addr = current PC + spec = specifier + nval = next word + flag = TRUE if decoding for CPU + iflag = TRUE if decoding integer instruction + Outputs: + count = -number of extra words retired +*/ + +int32 fprint_spec (FILE *of, t_addr addr, int32 spec, t_value nval, + int32 flag, int32 iflag) +{ +int32 reg; + +reg = spec & 07; +switch ((spec >> 3) & 07) { +case 0: + if (iflag) fprintf (of, "%s", rname[reg]); + else fprintf (of, "%s", fname[reg]); + return 0; +case 1: + fprintf (of, "(%s)", rname[reg]); + return 0; +case 2: + if (reg != 7) { + fprintf (of, "(%s)+", rname[reg]); + return 0; } + else { fprintf (of, "#%-o", nval); + return -1; } +case 3: + if (reg != 7) { + fprintf (of, "@(%s)+", rname[reg]); + return 0; } + else { fprintf (of, "@#%-o", nval); + return -1; } +case 4: + fprintf (of, "-(%s)", rname[reg]); + return 0; +case 5: + fprintf (of, "@-(%s)", rname[reg]); + return 0; +case 6: + if ((reg != 7) || !flag) fprintf (of, "%-o(%s)", nval, rname[reg]); + else fprintf (of, "%-o", (nval + addr + 4) & 0177777); + return -1; +case 7: + if ((reg != 7) || !flag) fprintf (of, "@%-o(%s)", nval, rname[reg]); + else fprintf (of, "@%-o", (nval + addr + 4) & 0177777); + return -1; } /* end case */ +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, c1, c2, inst, fac, srcm, srcr, dstm, dstr; +int32 l8b, brdisp, wd1, wd2; +extern int32 FPS; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +c1 = val[0] & 0177; +c2 = (val[0] >> 8) & 0177; +if (sw & SWMASK ('A')) { /* ASCII? */ + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +inst = val[0] | ((FPS << (I_V_L - FPS_V_L)) & I_L) | + ((FPS << (I_V_D - FPS_V_D)) & I_D); /* inst + fp mode */ +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + if ((opc_val[i] & 0777777) == (inst & masks[j])) { /* match? */ + srcm = (inst >> 6) & 077; /* opr fields */ + srcr = srcm & 07; + fac = srcm & 03; + dstm = inst & 077; + dstr = dstm & 07; + l8b = inst & 0377; + +/* Instruction decode */ + + switch (j) { /* case on class */ + case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */ + fprintf (of, "%s", opcode[i]); + return SCPE_OK; + case I_V_REG: /* reg */ + fprintf (of, "%s %-s", opcode[i], rname[dstr]); + return SCPE_OK; + case I_V_SOP: /* sop */ + fprintf (of, "%s ", opcode[i]); + return fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + case I_V_3B: /* 3b */ + fprintf (of, "%s %-o", opcode[i], dstr); + return SCPE_OK; + case I_V_FOP: /* fop */ + fprintf (of, "%s ", opcode[i]); + return fprint_spec (of, addr, dstm, val[1], cflag, FALSE); + case I_V_AFOP: /* afop */ + fprintf (of, "%s %s,", opcode[i], fname[fac]); + return fprint_spec (of, addr, dstm, val[1], cflag, FALSE); + case I_V_6B: /* 6b */ + fprintf (of, "%s %-o", opcode[i], dstm); + return SCPE_OK; + case I_V_BR: /* cond branch */ + fprintf (of, "%s ", opcode[i]); + brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777; + if (cflag) fprintf (of, "%-o", (addr + brdisp) & 0177777); + else if (brdisp < 01000) fprintf (of, ".+%-o", brdisp); + else fprintf (of, ".-%-o", 0200000 - brdisp); + return SCPE_OK; + case I_V_8B: /* 8b */ + fprintf (of, "%s %-o", opcode[i], l8b); + return SCPE_OK; + case I_V_SOB: /* sob */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + brdisp = (dstm * 2) - 2; + if (cflag) fprintf (of, "%-o", (addr - brdisp) & 0177777); + else if (brdisp <= 0) fprintf (of, ".+%-o", -brdisp); + else fprintf (of, ".-%-o", brdisp); + return SCPE_OK; + case I_V_RSOP: /* rsop */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + return fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + case I_V_ASOP: case I_V_ASMD: /* asop, asmd */ + fprintf (of, "%s %s,", opcode[i], fname[fac]); + return fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + case I_V_DOP: /* dop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, srcm, val[1], cflag, TRUE); + fprintf (of, ","); + wd2 = fprint_spec (of, addr - wd1 - wd1, dstm, + val[1 - wd1], cflag, TRUE); + return wd1 + wd2; } /* end case */ + } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +#define A_PND 100 /* # seen */ +#define A_MIN 040 /* -( seen */ +#define A_PAR 020 /* (Rn) seen */ +#define A_REG 010 /* Rn seen */ +#define A_PLS 004 /* + seen */ +#define A_NUM 002 /* number seen */ +#define A_REL 001 /* relative addr seen */ + +/* Register number + + Inputs: + *cptr = pointer to input string + *strings = pointer to register names + mchar = character to match after register name + Outputs: + rnum = 0..7 if a legitimate register + < 0 if error +*/ + +int32 get_reg (char *cptr, const char *strings[], char mchar) +{ +int32 i; + +if (*(cptr + 2) != mchar) return -1; +for (i = 0; i < 8; i++) { + if (strncmp (cptr, strings[i], 2) == 0) return i; } +return -1; +} + +/* Number or memory address + + Inputs: + *cptr = pointer to input string + *dptr = pointer to output displacement + *pflag = pointer to accumulating flags + Outputs: + cptr = pointer to next character in input string + NULL if parsing error + + Flags: 0 (no result), A_NUM (number), A_REL (relative) +*/ + +char *get_addr (char *cptr, int32 *dptr, int32 *pflag) +{ +int32 val, minus; +char *tptr; + +minus = 0; + +if (*cptr == '.') { /* relative? */ + *pflag = *pflag | A_REL; + cptr++; } +if (*cptr == '+') { /* +? */ + *pflag = *pflag | A_NUM; + cptr++; } +if (*cptr == '-') { /* -? */ + *pflag = *pflag | A_NUM; + minus = 1; + cptr++; } +errno = 0; +val = strtoul (cptr, &tptr, 8); +if (cptr == tptr) { /* no number? */ + if (*pflag != (A_REL + A_NUM)) return cptr; + else return NULL; } +if (errno || (*pflag == A_REL)) return NULL; /* .n? */ +*dptr = (minus? -val: val) & 0177777; +*pflag = *pflag | A_NUM; +return tptr; +} + +/* Specifier decode + + Inputs: + *cptr = pointer to input string + addr = current PC + n1 = 0 if no extra word used + -1 if extra word used in prior decode + *sptr = pointer to output specifier + *dptr = pointer to output displacement + cflag = true if parsing for the CPU + iflag = true if integer specifier + Outputs: + status = = -1 extra word decoded + = 0 ok + = +1 error +*/ + +t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr, + int32 cflag, int32 iflag) +{ +int32 reg, indir, pflag, disp; + +indir = 0; /* no indirect */ +pflag = 0; + +if (*cptr == '@') { /* indirect? */ + indir = 010; + cptr++; } +if (*cptr == '#') { /* literal? */ + pflag = pflag | A_PND; + cptr++; } +if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */ + pflag = pflag | A_MIN; + cptr++; } +else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) return 1; +if (*cptr == '(') { /* register index? */ + pflag = pflag | A_PAR; + if ((reg = get_reg (cptr + 1, rname, ')')) < 0) return 1; + cptr = cptr + 4; + if (*cptr == '+') { /* autoincrement? */ + pflag = pflag | A_PLS; + cptr++; } } +else if ((reg = get_reg (cptr, iflag? rname: fname, 0)) >= 0) { + pflag = pflag | A_REG; + cptr = cptr + 2; } +if (*cptr != 0) return 1; /* all done? */ + +/* Specifier decode, continued */ + +switch (pflag) { /* case on syntax */ +case A_REG: /* Rn, @Rn */ + *sptr = indir + reg; + return 0; +case A_PAR: /* (Rn), @(Rn) */ + if (indir) { /* @(Rn) = @0(Rn) */ + *sptr = 070 + reg; + *dptr = 0; + return -1; } + else *sptr = 010 + reg; + return 0; +case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */ + *sptr = 020 + indir + reg; + return 0; +case A_MIN+A_PAR: /* -(Rn), @-(Rn) */ + *sptr = 040 + indir + reg; + return 0; +case A_NUM+A_PAR: /* d(Rn), @d(Rn) */ + *sptr = 060 + indir + reg; + *dptr = disp; + return -1; +case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */ + if (!cflag) return 1; + disp = (disp + addr) & 0177777; /* fall through */ +case A_PND+A_NUM: /* #n, @#n */ + *sptr = 027 + indir; + *dptr = disp; + return -1; +case A_REL: case A_REL+A_NUM: /* .+n, @.+n */ + *sptr = 067 + indir; + *dptr = (disp - 4 + (2 * n1)) & 0177777; + return -1; +case A_NUM: /* n, @n */ + if (cflag) { /* CPU - use rel */ + *sptr = 067 + indir; + *dptr = (disp - addr - 4 + (2 * n1)) & 0177777; } + else { if (indir) return 1; /* other - use abs */ + *sptr = 037; + *dptr = disp; } + return -1; +default: + return 1; } /* end case */ +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, reg, spec, n1, n2, disp, pflag; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((t_value) cptr[1] << 8) + (t_value) cptr[0]; + return SCPE_OK; } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +n1 = n2 = pflag = 0; +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0177777; /* get value */ +j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + +switch (j) { /* case on class */ +case I_V_NPN: /* no operand */ + break; +case I_V_REG: /* register */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | reg; + break; +case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */ + cptr = get_glyph (cptr, gbuf, 0); /* get literal */ + d = get_uint (gbuf, 8, (1 << j) - 1, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; /* put in place */ + break; +case I_V_BR: /* cond br */ + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + if ((cptr = get_addr (gbuf, &disp, &pflag)) == NULL) return SCPE_ARG; + if ((pflag & A_REL) == 0) { + if (cflag) disp = (disp - addr) & 0177777; + else return SCPE_ARG; } + if ((disp & 1) || (disp > 0400) && (disp < 0177402)) return SCPE_ARG; + val[0] = val[0] | (((disp - 2) >> 1) & 0377); + break; +case I_V_SOB: /* sob */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | (reg << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + if ((cptr = get_addr (gbuf, &disp, &pflag)) == NULL) return SCPE_ARG; + if ((pflag & A_REL) == 0) { + if (cflag) disp = (disp - addr) & 0177777; + else return SCPE_ARG; } + if ((disp & 1) || ((disp > 2) && (disp < 0177604))) return SCPE_ARG; + val[0] = val[0] | (((2 - disp) >> 1) & 077); + break; +case I_V_RSOP: /* reg, sop */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | (reg << 6); /* fall through */ +case I_V_SOP: /* sop */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; +case I_V_AFOP: case I_V_ASOP: case I_V_ASMD: /* fac, (s)fop */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, fname, 0)) < 0) return SCPE_ARG; + if (reg > 3) return SCPE_ARG; + val[0] = val[0] | (reg << 6); /* fall through */ +case I_V_FOP: /* fop */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, + (j == I_V_ASOP) || (j == I_V_ASMD))) > 0) return SCPE_ARG; + val[0] = val[0] | spec; + break; +case I_V_DOP: /* double op */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) + return SCPE_ARG; + val[0] = val[0] | (spec << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n2 = get_spec (gbuf, addr, n1, &spec, &val[1 - n1], + cflag, TRUE)) > 0) return SCPE_ARG; + val[0] = val[0] | spec; + break; +case I_V_CCC: case I_V_CCS: /* cond code oper */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) || + (opcode[i] == NULL)) return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 0177777); } + break; +default: + return SCPE_ARG; } +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return n1 + n2; +} diff --git a/pdp11_tm.c b/pdp11_tm.c new file mode 100644 index 00000000..8e2a7aeb --- /dev/null +++ b/pdp11_tm.c @@ -0,0 +1,646 @@ +/* PDP-11 magnetic tape simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tm TM11/TU10 magtape + + 14-Apr-99 RMS Changed t_addr to unsigned + 04-Oct-98 RMS V2.4 magtape format + 10-May-98 RMS Fixed bug with non-zero unit operation (from Steven Schultz) + 09-May-98 RMS Fixed problems in bootstrap (from Steven Schultz) + 10-Apr-98 RMS Added 2nd block bootstrap (from John Holden, + University of Sydney) + 31-Jul-97 RMS Added bootstrap (from Ethan Dicks, Ohio State) + 22-Jan-97 RMS V2.3 magtape format + 18-Jan-97 RMS Fixed double interrupt, error flag bugs + 29-Jun-96 RMS Added unit disable support + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. +*/ + +#include "pdp11_defs.h" + +#define TM_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK 1 << UNIT_V_WLK +#define UNIT_W_UF 2 /* saved user flags */ +#define USTAT u3 /* unit status */ +#define UNUM u4 /* unit number */ +#define DBSIZE (1 << 16) /* max data buf */ +#define DBMASK (SBSIZE - 1) + +/* Command - tm_cmd */ + +#define MTC_ERR (1 << CSR_V_ERR) /* error */ +#define MTC_V_DEN 13 /* density */ +#define MTC_M_DEN 03 +#define MTC_DEN (MTC_M_DEN << MTC_V_DEN) +#define MTC_INIT 0010000 /* init */ +#define MTC_LPAR 0004000 /* parity select */ +#define MTC_V_UNIT 8 /* unit */ +#define MTC_M_UNIT 07 +#define MTC_UNIT (MTC_M_UNIT << MTC_V_UNIT) +#define MTC_DONE (1 << CSR_V_DONE) /* done */ +#define MTC_IE (1 << CSR_V_IE) /* interrupt enable */ +#define MTC_V_EMA 4 /* ext mem address */ +#define MTC_M_EMA 03 +#define MTC_EMA (MTC_M_EMA << MTC_V_EMA) +#define MTC_V_FNC 1 /* function */ +#define MTC_M_FNC 07 +#define MTC_UNLOAD 00 +#define MTC_READ 01 +#define MTC_WRITE 02 +#define MTC_WREOF 03 +#define MTC_SPACEF 04 +#define MTC_SPACER 05 +#define MTC_WREXT 06 +#define MTC_REWIND 07 +#define MTC_FNC (MTC_M_FNC << MTC_V_FNC) +#define MTC_GO (1 << CSR_V_GO) /* go */ +#define MTC_RW (MTC_DEN | MTC_LPAR | MTC_UNIT | MTC_IE | \ + MTC_EMA | MTC_FNC) +#define GET_EMA(x) (((x) & MTC_EMA) << (16 - MTC_V_EMA)) +#define GET_UNIT(x) (((x) >> MTC_V_UNIT) & MTC_M_UNIT) +#define GET_FNC(x) (((x) >> MTC_V_FNC) & MTC_M_FNC) + +/* Status - stored in tm_sta or (*) uptr -> USTAT or (+) calculated */ + +#define STA_ILL 0100000 /* illegal */ +#define STA_EOF 0040000 /* *end of file */ +#define STA_CRC 0020000 /* CRC error */ +#define STA_PAR 0010000 /* parity error */ +#define STA_DLT 0004000 /* data late */ +#define STA_EOT 0002000 /* *end of tape */ +#define STA_RLE 0001000 /* rec lnt error */ +#define STA_BAD 0000400 /* bad tape error */ +#define STA_NXM 0000200 /* non-existent mem */ +#define STA_ONL 0000100 /* *online */ +#define STA_BOT 0000040 /* *start of tape */ +#define STA_7TK 0000020 /* 7 track */ +#define STA_SDN 0000010 /* settle down */ +#define STA_WLK 0000004 /* *write locked */ +#define STA_REW 0000002 /* *rewinding */ +#define STA_TUR 0000001 /* +unit ready */ + +#define STA_CLR (STA_7TK | STA_SDN) /* always clear */ +#define STA_DYN (STA_EOF | STA_EOT | STA_ONL | STA_BOT | \ + STA_WLK | STA_REW | STA_TUR) /* kept in USTAT */ +#define STA_EFLGS (STA_ILL | STA_EOF | STA_CRC | STA_PAR | \ + STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM) + /* set error */ + +extern unsigned int16 *M; /* memory */ +extern int32 int_req; +extern UNIT cpu_unit; +int32 tm_sta = 0; /* status register */ +int32 tm_cmd = 0; /* command register */ +int32 tm_ca = 0; /* current address */ +int32 tm_bc = 0; /* byte count */ +int32 tm_db = 0; /* data buffer */ +int32 tm_time = 10; /* record latency */ +int32 tm_stopioe = 1; /* stop on error */ +t_stat tm_svc (UNIT *uptr); +t_stat tm_reset (DEVICE *dptr); +t_stat tm_attach (UNIT *uptr, char *cptr); +t_stat tm_detach (UNIT *uptr); +t_stat tm_boot (int32 unitno); +void tm_go (UNIT *uptr); +int32 tm_updcsta (UNIT *uptr); +void tm_set_done (void); +t_stat tm_vlock (UNIT *uptr, int32 val); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* MT data structures + + tm_dev MT device descriptor + tm_unit MT unit list + tm_reg MT register list + tm_mod MT modifier list +*/ + +UNIT tm_unit[] = { + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } }; + +REG tm_reg[] = { + { ORDATA (MTS, tm_sta, 16) }, + { ORDATA (MTC, tm_cmd, 16) }, + { ORDATA (MTBRC, tm_bc, 16) }, + { ORDATA (MTCMA, tm_ca, 16) }, + { ORDATA (MTD, tm_db, 8) }, + { FLDATA (INT, int_req, INT_V_TM) }, + { FLDATA (ERR, tm_cmd, CSR_V_ERR) }, + { FLDATA (DONE, tm_cmd, CSR_V_DONE) }, + { FLDATA (IE, tm_cmd, CSR_V_IE) }, + { FLDATA (STOP_IOE, tm_stopioe, 0) }, + { DRDATA (TIME, tm_time, 24), PV_LEFT }, + { ORDATA (UST0, tm_unit[0].USTAT, 16) }, + { ORDATA (UST1, tm_unit[1].USTAT, 16) }, + { ORDATA (UST2, tm_unit[2].USTAT, 16) }, + { ORDATA (UST3, tm_unit[3].USTAT, 16) }, + { ORDATA (UST4, tm_unit[4].USTAT, 16) }, + { ORDATA (UST5, tm_unit[5].USTAT, 16) }, + { ORDATA (UST6, tm_unit[6].USTAT, 16) }, + { ORDATA (UST7, tm_unit[7].USTAT, 16) }, + { DRDATA (POS0, tm_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, tm_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, tm_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, tm_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, tm_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, tm_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, tm_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, tm_unit[7].pos, 31), PV_LEFT + REG_RO }, + { GRDATA (FLG0, tm_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, tm_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, tm_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, tm_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, tm_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, tm_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, tm_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, tm_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { NULL } }; + +MTAB tm_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", &tm_vlock }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &tm_vlock }, + { 0 } }; + +DEVICE tm_dev = { + "TM", tm_unit, tm_reg, tm_mod, + TM_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &tm_reset, + &tm_boot, &tm_attach, &tm_detach }; + +/* I/O dispatch routine, I/O addresses 17772520 - 17772532 + + 17772520 MTS read only, constructed from tm_sta + plus current drive status flags + 17772522 MTC read/write + 17772524 MTBRC read/write + 17772526 MTCMA read/write + 17772530 MTD read/write + 17772532 MTRD unimplemented +*/ + +t_stat tm_rd (int32 *data, int32 PA, int32 access) +{ +UNIT *uptr; + +uptr = tm_dev.units + GET_UNIT (tm_cmd); /* get unit */ +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ +case 0: /* MTS */ + *data = tm_updcsta (uptr); /* update status */ + return SCPE_OK; +case 1: /* MTC */ + tm_updcsta (uptr); /* update status */ + *data = tm_cmd; /* return command */ + return SCPE_OK; +case 2: /* MTBRC */ + *data = tm_bc; /* return byte count */ + return SCPE_OK; +case 3: /* MTCMA */ + *data = tm_ca; /* return mem addr */ + return SCPE_OK; +case 4: /* MTD */ + *data = tm_db; /* return data buffer */ + return SCPE_OK; +default: /* unimplemented */ + *data = 0; + return SCPE_OK; } +} + +t_stat tm_wr (int32 data, int32 PA, int32 access) +{ +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ +case 0: /* MTS: read only */ + return SCPE_OK; +case 1: /* MTC */ + uptr = tm_dev.units + GET_UNIT (tm_cmd); /* select unit */ + if ((tm_cmd & MTC_DONE) == 0) tm_sta = tm_sta | STA_ILL; + else { if (access == WRITEB) data = (PA & 1)? + (tm_cmd & 0377) | (data << 8): + (tm_cmd & ~0377) | data; + if (data & MTC_INIT) { /* init? */ + tm_reset (&tm_dev); /* reset device */ + return SCPE_OK; } + if ((data & MTC_IE) == 0) /* int disable? */ + int_req = int_req & ~INT_TM; /* clr int request */ + else if ((tm_cmd & (MTC_ERR + MTC_DONE)) && !(tm_cmd & MTC_IE)) + int_req = int_req | INT_TM; /* set int request */ + tm_cmd = (tm_cmd & ~MTC_RW) | (data & MTC_RW); + uptr = tm_dev.units + GET_UNIT (tm_cmd); /* new unit */ + if (data & MTC_GO) tm_go (uptr); } /* new function? */ + tm_updcsta (uptr); /* update status */ + return SCPE_OK; +case 2: /* MTBRC */ + if (access == WRITEB) data = (PA & 1)? + (tm_bc & 0377) | (data << 8): (tm_bc & ~0377) | data; + tm_bc = data; + return SCPE_OK; +case 3: /* MTCMA */ + if (access == WRITEB) data = (PA & 1)? + (tm_ca & 0377) | (data << 8): (tm_ca & ~0377) | data; + tm_ca = data; + return SCPE_OK; +case 4: /* MTD */ + if ((access == WRITEB) && (PA & 1)) return SCPE_OK; + tm_db = data & 0377; + return SCPE_OK; +default: + return SCPE_OK; } /* end switch */ +} + +/* New magtape command */ + +void tm_go (UNIT *uptr) +{ +int32 f; + +f = GET_FNC (tm_cmd); /* get function */ +if (((uptr -> flags & UNIT_ATT) == 0) || /* not attached? */ + sim_is_active (uptr) || /* busy? */ + (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) && + (uptr -> flags & UNIT_WLK))) { /* write locked? */ + tm_sta = tm_sta | STA_ILL; /* illegal */ + tm_set_done (); /* set done */ + return; } +uptr -> USTAT = uptr -> USTAT & (STA_WLK | STA_ONL); /* clear status */ +tm_sta = 0; /* clear errors */ +if (f == MTC_UNLOAD) { /* unload? */ + uptr -> USTAT = (uptr -> USTAT | STA_REW) & ~STA_ONL; + detach_unit (uptr); } /* set offline */ +else if (f == MTC_REWIND) /* rewind */ + uptr -> USTAT = uptr -> USTAT | STA_REW; /* rewinding */ +/* else /* uncomment this else if rewind/unload don't set done */ +tm_cmd = tm_cmd & ~MTC_DONE; /* clear done */ +int_req = int_req & ~INT_TM; /* clear int */ +sim_activate (uptr, tm_time); /* start io */ +return; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat tm_svc (UNIT *uptr) +{ +int f, i, p, err; +t_addr xma; +t_stat rval; +t_mtrlnt tbc, cbc; +unsigned int16 c; +unsigned int8 dbuf[DBSIZE]; +static t_mtrlnt bceof = { 0 }; + +if (uptr -> USTAT & STA_REW) { /* rewind? */ + uptr -> pos = 0; /* update position */ + if (uptr -> flags & UNIT_ATT) /* still on line? */ + uptr -> USTAT = STA_ONL | STA_BOT | + ((uptr -> flags & UNIT_WLK)? STA_WLK: 0); + else uptr -> USTAT = 0; + if (uptr -> UNUM == GET_UNIT (tm_cmd)) { /* selected? */ + tm_set_done (); /* set done */ + tm_updcsta (uptr); } /* update status */ + return SCPE_OK; } + +if ((uptr -> flags & UNIT_ATT) == 0) { /* if not attached */ + uptr -> USTAT = 0; /* unit off line */ + tm_sta = tm_sta | STA_ILL; /* illegal operation */ + tm_set_done (); /* set done */ + tm_updcsta (uptr); /* update status */ + return IORETURN (tm_stopioe, SCPE_UNATT); } + +f = GET_FNC (tm_cmd); /* get command */ +if (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) && + (uptr -> flags & UNIT_WLK)) { /* write and locked? */ + tm_sta = tm_sta | STA_ILL; /* illegal operation */ + tm_set_done (); /* set done */ + tm_updcsta (uptr); /* update status */ + return SCPE_OK; } + +err = 0; +rval = SCPE_OK; +xma = GET_EMA (tm_cmd) | tm_ca; /* get mem addr */ +cbc = 0200000 - tm_bc; /* get bc */ +switch (f) { /* case on function */ + +/* Unit service, continued */ + +case MTC_READ: /* read */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + (feof (uptr -> fileref))) { + uptr -> USTAT = uptr -> USTAT | STA_EOT; + break; } + if (tbc == 0) { /* tape mark? */ + uptr -> USTAT = uptr -> USTAT | STA_EOF; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + tbc = MTRL (tbc); /* ignore error flag */ + if (tbc > cbc) tm_sta = tm_sta | STA_RLE; /* wrong size? */ + if (tbc < cbc) cbc = tbc; /* use smaller */ + i = fxread (dbuf, sizeof (int8), cbc, uptr -> fileref); + for ( ; i < tbc; i++) dbuf[i] = 0; /* fill with 0's */ + err = ferror (uptr -> fileref); + for (i = 0; i < cbc; i++) { /* copy buffer */ + if (xma >= MEMSIZE) { /* nx memory? */ + tm_sta = tm_sta | STA_NXM; + break; } + p = xma >> 1; /* word address */ + c = dbuf[i]; /* character */ + if (i & 1) M[p] = (M[p] & 0377) | (c << 8); + else M[p] = (M[p] & 0177400) | c; + xma = (xma + 1) & 0777777; + tm_bc = (tm_bc + 1) & 0177777; } + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + break; +case MTC_WRITE: /* write */ +case MTC_WREXT: /* write ext gap */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&cbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + for (i = 0; i < cbc; i++) { /* copy buf to tape */ + if (xma >= MEMSIZE) { /* nx memory? */ + tm_sta = tm_sta | STA_NXM; + break; } + if (i & 1) dbuf[i] = (M[xma >> 1] >> 8) & 0377; + else dbuf[i] = M[xma >> 1] & 0377; + xma = (xma + 1) & 0777777; + tm_bc = (tm_bc + 1) & 0177777; } + fxwrite (dbuf, sizeof (int8), cbc, uptr -> fileref); + fxwrite (&cbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + ((cbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + break; +case MTC_WREOF: + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update position */ + break; + +/* Unit service, continued */ + +case MTC_SPACEF: /* space forward */ + do { tm_bc = (tm_bc + 1) & 0177777; /* incr wc */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + feof (uptr -> fileref)) { + uptr -> USTAT = uptr -> USTAT | STA_EOT; + break; } + if (tbc == 0) { /* zero bc? */ + uptr -> USTAT = uptr -> USTAT | STA_EOF; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); } + while (tm_bc != 0); + break; +case MTC_SPACER: /* space reverse */ + if (uptr -> pos == 0) { /* at BOT? */ + uptr -> USTAT = uptr -> USTAT | STA_BOT; + break; } + do { tm_bc = (tm_bc + 1) & 0177777; /* incr wc */ + fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), + SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + feof (uptr -> fileref)) { + uptr -> USTAT = uptr -> USTAT | STA_BOT; + uptr -> pos = 0; + break; } + if (tbc == 0) { /* start of prv file? */ + uptr -> USTAT = uptr -> USTAT | STA_EOF; + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); + if (uptr -> pos == 0) { /* start of tape? */ + uptr -> USTAT = uptr -> USTAT | STA_BOT; + break; } } + while (tm_bc != 0); + break; } /* end case */ + +/* Unit service, continued */ + +if (err != 0) { /* I/O error */ + tm_sta = tm_sta | STA_PAR | STA_CRC; /* flag error */ + perror ("MT I/O error"); + rval = SCPE_IOERR; + clearerr (uptr -> fileref); } +tm_cmd = (tm_cmd & ~MTC_EMA) | ((xma >> (16 - MTC_V_EMA)) & MTC_EMA); +tm_ca = xma & 0177777; /* update mem addr */ +tm_set_done (); /* set done */ +tm_updcsta (uptr); /* update status */ +return IORETURN (tm_stopioe, rval); +} + +/* Update controller status */ + +int32 tm_updcsta (UNIT *uptr) +{ +tm_sta = (tm_sta & ~(STA_DYN | STA_CLR)) | (uptr -> USTAT & STA_DYN); +if (sim_is_active (uptr)) tm_sta = tm_sta & ~STA_TUR; +else tm_sta = tm_sta | STA_TUR; +if (tm_sta & STA_EFLGS) tm_cmd = tm_cmd | MTC_ERR; +else tm_cmd = tm_cmd & ~MTC_ERR; +if ((tm_cmd & MTC_IE) == 0) int_req = int_req & ~INT_TM; +return tm_sta; +} + +/* Set done */ + +void tm_set_done (void) +{ +tm_cmd = tm_cmd | MTC_DONE; +if (tm_cmd & MTC_IE) int_req = int_req | INT_TM; +return; +} + +/* Reset routine */ + +t_stat tm_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +tm_cmd = MTC_DONE; /* set done */ +tm_bc = tm_ca = tm_db = tm_sta = 0; +int_req = int_req & ~INT_TM; /* clear interrupt */ +for (u = 0; u < TM_NUMDR; u++) { /* loop thru units */ + uptr = tm_dev.units + u; + uptr -> UNUM = u; /* init drive number */ + sim_cancel (uptr); /* cancel activity */ + if (uptr -> flags & UNIT_ATT) uptr -> USTAT = STA_ONL | + ((uptr -> pos)? 0: STA_BOT) | + ((uptr -> flags & UNIT_WLK)? STA_WLK: 0); + else uptr -> USTAT = 0; } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat tm_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +uptr -> USTAT = STA_ONL | STA_BOT | ((uptr -> flags & UNIT_WLK)? STA_WLK: 0); +if (uptr -> UNUM == GET_UNIT (tm_cmd)) tm_updcsta (uptr); +return r; +} + +/* Detach routine */ + +t_stat tm_detach (UNIT* uptr) +{ +if (!sim_is_active (uptr)) uptr -> USTAT = 0; +if (uptr -> UNUM == GET_UNIT (tm_cmd)) tm_updcsta (uptr); +return detach_unit (uptr); +} + +/* Write lock/enable routine */ + +t_stat tm_vlock (UNIT *uptr, int32 val) +{ +if ((uptr -> flags & UNIT_ATT) && val) uptr -> USTAT = uptr -> USTAT | STA_WLK; +else uptr -> USTAT = uptr -> USTAT & ~STA_WLK; +if (uptr -> UNUM == GET_UNIT (tm_cmd)) tm_updcsta (uptr); +return SCPE_OK; +} + +/* Device bootstrap + + Magtape boot format changed over time. Originally, a boot tape + contained a boot loader in the first block. Eventually, the first + block was reserved for a tape label, and the second block was + expected to contain a boot loader. BSD and DEC operating systems + use the second block scheme, so it is the default. + + To boot from the first block, use boot -o (old). + */ + +#define BOOT_START 040000 +#define BOOT_UNIT (BOOT_START + 6) +#define BOOT1_LEN (sizeof (boot1_rom) / sizeof (int32)) +#define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int32)) + +static const int32 boot1_rom[] = { + 0012706, 0040000, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit_num, r0 */ + 0012701, 0172526, /* mov #172526, r1 ; mtcma */ + 0005011, /* clr (r1) */ + 0011041, /* mov r1, -(r1) ; mtbrc */ + 0010002, /* mov r0,r2 */ + 0000302, /* swab r2 */ + 0062702, 0060003, /* add #60003, r2 */ + 0010241, /* mov r2, -(r1) ; read + go */ + 0105711, /* tstb (r1) ; mtc */ + 0100376, /* bpl .-2 */ + 0005002, /* clr r2 */ + 0005003, /* clr r3 */ + 0005004, /* clr r4 */ + 0012705, 0052115, /* mov #MT, r5 */ + 0005007 /* clr r7 */ +}; + +static const int32 boot2_rom[] = { + 0012706, 0040000, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit_num, r0 */ + 0012701, 0172526, /* mov #172526, r1 ; mtcma */ + 0005011, /* clr (r1) */ + 0012741, 0177777, /* mov #-1, -(r1) ; mtbrc */ + 0010002, /* mov r0,r2 */ + 0000302, /* swab r2 */ + 0062702, 0060011, /* add #60011, r2 */ + 0010241, /* mov r2, -(r1) ; space + go */ + 0105711, /* tstb (r1) ; mtc */ + 0100376, /* bpl .-2 */ + 0010002, /* mov r0,r2 */ + 0000302, /* swab r2 */ + 0062702, 0060003, /* add #60003, r2 */ + 0010211, /* mov r2, (r1) ; read + go */ + 0105711, /* tstb (r1) ; mtc */ + 0100376, /* bpl .-2 */ + 0005002, /* clr r2 */ + 0005003, /* clr r3 */ + 0005004, /* clr r4 */ + 0012705, 0052115, /* mov #MT, r5 */ + 0005007 /* clr r7 */ +}; + +t_stat tm_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; +extern int32 sim_switches; + +if (sim_switches & SWMASK ('O')) { + for (i = 0; i < BOOT1_LEN; i++) + M[(BOOT_START >> 1) + i] = boot1_rom[i]; } +else { for (i = 0; i < BOOT2_LEN; i++) + M[(BOOT_START >> 1) + i] = boot2_rom[i]; } +M[BOOT_UNIT >> 1] = unitno; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp15.c b/pdp15.c new file mode 100644 index 00000000..d6778bb2 --- /dev/null +++ b/pdp15.c @@ -0,0 +1 @@ +#define PDP15 0 diff --git a/pdp18b_cpu.c b/pdp18b_cpu.c new file mode 100644 index 00000000..29b0c0b5 --- /dev/null +++ b/pdp18b_cpu.c @@ -0,0 +1,1421 @@ +/* pdp18b_cpu.c: 18b PDP CPU simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + + The 18b PDP family has five distinct architectural variants: PDP-1, + PDP-4, PDP-7, PDP-9, and PDP-15. Of these, the PDP-1 is so unique + as to require a different simulator. The PDP-4, PDP-7, PDP-9, and + PDP-15 are "upward compatible", with each new variant adding + distinct architectural features and incompatibilities. + + The register state for the 18b PDP's is: + + all AC<0:17> accumulator + all MQ<0:17> multiplier-quotient + all L link flag + all PC<0:x> program counter + all IORS I/O status register + PDP-7, PDP-9 EXTM extend mode + PDP-15 BANKM bank mode + PDP-7 TRAPM trap mode + PDP-9, PDP-15 USMD user mode + PDP-9, PDP-15 BR bounds register + PDP-15 XR index register + PDP-15 LR limit register +*/ + +/* The PDP-4, PDP-7, and PDP-9 have five instruction formats: memory + reference, load immediate, I/O transfer, EAE, and operate. The PDP-15 + adds a sixth, index operate, and a seventh, floating point. The memory + reference format for the PDP-4, PDP-7, and PDP-9, and for the PDP-15 + in bank mode, is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in| address | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The PDP-15 in page mode trades an address bit for indexing capability: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in| X| address | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:3> mnemonic action + + 00 CAL JMS with MA = 20 + 04 DAC M[MA] = AC + 10 JMS M[MA] = L'mem'user'PC, PC = MA + 1 + 14 DZM M[MA] = 0 + 20 LAC AC = M[MA] + 24 XOR AC = AC ^ M[MA] + 30 ADD L'AC = AC + M[MA] one's complement + 34 TAD L'AC = AC + M[MA] + 40 XCT M[MA] is executed as an instruction + 44 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 50 AND AC = AC & M[MA] + 54 SAD skip if AC != M[MA] + 60 JMP PC = MA + + On the PDP-4, PDP-7, and PDP-9, and the PDP-15 in bank mode, memory + reference instructions can access an address space of 32K words. The + address space is divided into four 8K word fields. An instruction can + directly address, via its 13b address, the entire current field. On the + PDP-4, PDP-7, and PDP-9, if extend mode is off, indirect addresses access + the current field; if on (or a PDP-15), they can access all 32K. + + On the PDP-15 in page mode, memory reference instructions can access + an address space of 128K words. The address is divided into four 32K + word blocks, each of which consists of eight 4K pages. An instruction + can directly address, via its 12b address, the current page. Indirect + addresses can access the current block. Indexed and autoincrement + addresses can access all 128K. + + On the PDP-4 and PDP-7, if an indirect address in in locations 00010- + 00017 of any field, the indirect address is incremented and rewritten + to memory before use. On the PDP-9 and PDP-15, only locations 00010- + 00017 of field zero autoincrement; special logic will redirect indirect + references to 00010-00017 to field zero, even if (on the PDP-9) extend + mode is off. +*/ + +/* The EAE format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 1| | | | | | | | | | | | | | | EAE + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | | | | | + | | | | | | | | | | | | | +- or SC (3) + | | | | | | | | | | | | +---- or MQ (3) + | | | | | | | | | | | +------- compl MQ (3) + | | | | | | | | \______________/ + | | | | | | | | | + | | | | | \_____/ +--------- shift count + | | | | | | + | | | | | +---------------------- EAE command (3) + | | | | +---------------------------- clear AC (2) + | | | +------------------------------- or AC (2) + | | +---------------------------------- load EAE sign (1) + | +------------------------------------- clear MQ (1) + +---------------------------------------- load link (1) + + The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0 0| device | sdv |cl| pulse | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified pulse to the + specified I/O device and sub-device. The I/O device may take data + from the AC, return data to the AC, initiate or cancel operations, + or skip on status. On the PDP-4, PDP-7, and PDP-9, bits <4:5> + were designated as subdevice bits but were never used; the PDP-15 + requires them to be zero. + + On the PDP-15, the floating point format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0 1| subopcode | floating point + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + Indirection is always single level. +*/ + +/* On the PDP-15, the index operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 1| subopcode | immediate | index operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The index operate instructions provide various operations on the + index and limit registers. + + The operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 0| | | | | | | | | | | | | | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | | | | + | | | | | | | | | | | | +- CMA (3) + | | | | | | | | | | | +---- CML (3) + | | | | | | | | | | +------- OAS (3) + | | | | | | | | | +---------- RAL (3) + | | | | | | | | +------------- RAR (3) + | | | | | | | +---------------- HLT (4) + | | | | | | +------------------- SMA (1) + | | | | | +---------------------- SZA (1) + | | | | +------------------------- SNL (1) + | | | +---------------------------- invert skip (1) + | | +------------------------------- rotate twice (2) + | +---------------------------------- CLL (2) + +------------------------------------- CLA (2) + + The operate instruction can be microprogrammed to perform operations + on the AC and link. + + The load immediate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 1| immediate | LAW + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 76 LAW AC = IR +*/ + +/* This routine is the instruction decode routine for the 18b PDP's. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and STOP_INST flag set + nested XCT's + I/O error in I/O simulator + + 2. Interrupts. Interrupt requests are maintained in the int_req + register. If interrupts are on and not deferred, and at least + one interrupt request is set, a program interrupt occurs. + + 3. Arithmetic. The 18b PDP's implements both 1's and 2's complement + arithmetic for signed numbers. In 1's complement arithmetic, a + negative number is represented by the complement (XOR 0777777) of + its absolute value. Addition of 1's complement numbers requires + propagating the carry out of the high order bit back to the low + order bit. + + 4. Adding I/O devices. Three modules must be modified: + + pdp18b_defs.h add interrupt request definition + pdp18b_cpu.c add IOT and IORS dispatches + pdp18b_sys.c add pointer to data structures to sim_devices +*/ + +#include "pdp18b_defs.h" + +#define ILL_ADR_FLAG (1 << ADDRSIZE) +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ +#define UNIT_NOEAE (1 << UNIT_V_NOEAE) +#define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#if defined (PDP4) +#define EAE_DFLT UNIT_NOEAE +#else +#define EAE_DFLT 0 +#endif + +int32 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 saved_LAC = 0; /* link'AC */ +int32 saved_MQ = 0; /* MQ */ +int32 saved_PC = 0; /* PC */ +int32 int_req = 0; /* int requests */ +int32 iors = 0; /* IORS */ +int32 ion = 0; /* int on */ +int32 ion_defer = 0; /* int defer */ +int32 memm = 0; /* ext mem mode */ +int32 usmd = 0; /* user mode */ +int32 usmdbuf = 0; /* user mode buffer */ +int32 trap_pending = 0; /* trap pending */ +int32 emir_pending = 0; /* emir pending */ +int32 rest_pending = 0; /* restore pending */ +int32 BR = 0; /* mem mgt bounds */ +int32 nexm = 0; /* nx mem flag */ +int32 prvn = 0; /* priv viol flag */ +int32 SC = 0; /* shift count */ +int32 eae_ac_sign = 0; /* EAE AC sign */ +int32 SR = 0; /* switch register */ +int32 XR = 0; /* index register */ +int32 LR = 0; /* limit register */ +int32 stop_inst = 0; /* stop on rsrv inst */ +int32 xct_max = 16; /* nested XCT limit */ +int32 old_PC = 0; /* old PC */ +int32 ibkpt_addr = ILL_ADR_FLAG | ADDRMASK; /* breakpoint addr */ +extern int32 sim_int_char; +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +int32 upd_iors (void); +extern t_stat sim_activate (UNIT *uptr, int32 delay); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK + EAE_DFLT, + MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, ADDRSIZE) }, + { ORDATA (AC, saved_LAC, 18) }, + { FLDATA (L, saved_LAC, 18) }, +#if !defined (PDP4) + { ORDATA (MQ, saved_MQ, 18) }, + { ORDATA (SC, SC, 6) }, + { FLDATA (EAE_AC_SIGN, eae_ac_sign, 18) }, +#endif + { ORDATA (SR, SR, 18) }, + { ORDATA (IORS, iors, 18), REG_RO }, + { ORDATA (INT, int_req, 32), REG_RO }, + { FLDATA (ION, ion, 0) }, + { ORDATA (ION_DELAY, ion_defer, 2) }, +#if defined (PDP7) + { FLDATA (TRAPM, usmd, 0) }, + { FLDATA (TRAPP, trap_pending, 0) }, + { FLDATA (EXTM, memm, 0) }, + { FLDATA (EMIRP, emir_pending, 0) }, +#elif defined (PDP9) + { ORDATA (BR, BR, ADDRSIZE) }, + { FLDATA (USMD, usmd, 0) }, + { FLDATA (USMDBUF, usmdbuf, 0) }, + { FLDATA (NEXM, nexm, 0) }, + { FLDATA (PRVN, prvn, 0) }, + { FLDATA (TRAPP, trap_pending, 0) }, + { FLDATA (EXTM, memm, 0) }, + { FLDATA (EMIRP, emir_pending, 0) }, + { FLDATA (RESTP, rest_pending, 0) }, + { FLDATA (PWRFL, int_req, INT_V_PWRFL) }, +#elif defined (PDP15) + { ORDATA (XR, XR, 18) }, + { ORDATA (LR, LR, 18) }, + { ORDATA (BR, BR, ADDRSIZE) }, + { FLDATA (NEXM, nexm, 0) }, + { FLDATA (PRVN, prvn, 0) }, + { FLDATA (TRAPP, trap_pending, 0) }, + { FLDATA (USMD, usmd, 0) }, + { FLDATA (USMDBUF, usmdbuf, 0) }, + { FLDATA (BANKM, memm, 0) }, + { FLDATA (RESP, rest_pending, 0) }, + { FLDATA (PWRFL, int_req, INT_V_PWRFL) }, +#endif + { ORDATA (OLDPC, old_PC, ADDRSIZE), REG_RO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (NOEAE, cpu_unit.flags, UNIT_V_NOEAE), REG_HRO }, + { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (BREAK, ibkpt_addr, ADDRSIZE + 1) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { +#if !defined (PDP4) + { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, + { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, +#endif + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, +#if (MAXMEMSIZE > 8192) + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, +#endif +#if (MAXMEMSIZE > 32768) + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MSIZE, 81920, NULL, "80K", &cpu_set_size }, + { UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size }, + { UNIT_MSIZE, 114688, NULL, "112K", &cpu_set_size }, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, +#endif + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, ADDRSIZE, 1, 8, 18, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 PC, LAC, MQ; +int32 iot_data, device, pulse; +t_stat reason; +extern int32 tti (int32 pulse, int32 AC); +extern int32 tto (int32 pulse, int32 AC); +extern int32 ptr (int32 pulse, int32 AC); +extern int32 ptp (int32 pulse, int32 AC); +extern int32 clk (int32 pulse, int32 AC); +extern int32 lpt65 (int32 pulse, int32 AC); +extern int32 lpt66 (int32 pulse, int32 AC); +#if defined (DRM) +extern int32 drm60 (int32 pulse, int32 AC); +extern int32 drm61 (int32 pulse, int32 AC); +extern int32 drm62 (int32 pulse, int32 AC); +#endif +#if defined (RF) +extern int32 rf70 (int32 pulse, int32 AC); +extern int32 rf72 (int32 pulse, int32 AC); +#endif +#if defined (RP) +extern int32 rp63 (int32 pulse, int32 AC); +extern int32 rp64 (int32 pulse, int32 AC); +#endif +#if defined (MTA) +extern int32 mt (int32 pulse, int32 AC); +#endif + +#define JMS_WORD(t) (((LAC & 01000000) >> 1) | ((memm & 1) << 16) | \ + (((t) & 1) << 15) | ((PC) & 077777)) +#define INCR_ADDR(x) (((x) & epcmask) | (((x) + 1) & damask)) +#define SEXT(x) ((int) (((x) & 0400000)? (x) | ~0777777: (x) & 0777777)) + +/* Restore register state */ + +#if defined (PDP15) +register int32 epcmask, damask; + +damask = memm? 017777: 07777; /* set dir addr mask */ +epcmask = ADDRMASK & ~damask; /* extended PC mask */ + +#else +#define damask 017777 /* direct addr mask */ +#define epcmask (ADDRMASK & ~damask) /* extended PC mask */ +#endif + +PC = saved_PC & ADDRMASK; /* load local copies */ +LAC = saved_LAC & 01777777; +MQ = saved_MQ & 0777777; +reason = 0; + +/* Main instruction fetch/decode loop: check trap and interrupt */ + +while (reason == 0) { /* loop until halted */ +register int32 IR, MA, t, xct_count; +register int32 link_init, fill; + +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; } + +/* Protection traps work like interrupts, with these quirks: + + PDP-7 extend mode forced on, M[0] = PC, PC = 2 + PDP-9, PDP-15 extend/bank mode forced off, M[0/20] = PC, PC = 0/21 +*/ + +#if defined (PDP7) +if (trap_pending) { /* trap pending? */ + old_PC = PC; /* save old PC */ + M[0] = JMS_WORD (1); /* save state */ + PC = 2; /* fetch next from 2 */ + ion = 0; /* interrupts off */ + memm = 1; /* extend on */ + emir_pending = trap_pending = 0; /* emir, trap off */ + usmd = 0; } /* protect off */ +#endif + +#if defined (PDP9) || defined (PDP15) +if (trap_pending) { /* trap pending? */ + old_PC = PC; /* save old PC */ + MA = ion? 0: 020; /* save in 0 or 20 */ + M[MA] = JMS_WORD (1); /* save state */ + PC = MA + 1; /* fetch next */ + ion = 0; /* interrupts off */ + memm = 0; /* extend/bank off */ + emir_pending = rest_pending = trap_pending = 0; /* emir,rest,trap off */ + usmd = 0; } /* protect off */ +#endif + +if (ion && !ion_defer && int_req) { /* interrupt? */ + old_PC = PC; /* save old PC */ + M[0] = JMS_WORD (usmd); /* save state */ + PC = 1; /* fetch next from 1 */ + ion = 0; /* interrupts off */ + memm = 0; /* extend/bank off */ + emir_pending = rest_pending = 0; /* emir, restore off */ + usmd = 0; } /* protect off */ + +if (PC == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save ibkpt */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + +/* The following macros implement addressing. They account for autoincrement + addressing, extended addressing, and memory protection, if it exists. + + CHECK_AUTO_INC check auto increment + INDIRECT indirect addressing + CHECK_INDEX check indexing + CHECK_ADDR_R check address for read + CHECK_ADDR_W check address for write + + On the PDP-4 and PDP-7, + There are autoincrement locations in every field. If a field + does not exist, it is impossible to generate an + autoincrement reference (all instructions are CAL). + Indirect addressing range is determined by extend mode. + There is no indexing. + There is no memory protection, nxm reads zero and ignores writes. +*/ + +#if defined (PDP4) || defined (PDP7) +#define CHECK_AUTO_INC \ + if ((IR & 017770) == 010) M[MA] = (M[MA] + 1) & 0777777 +#define INDIRECT \ + MA = memm? M[MA] & IAMASK: (MA & epcmask) | (M[MA] & damask) +#define CHECK_INDEX /* no indexing capability */ +#define CHECK_ADDR_R(x) /* no read protection */ +#define CHECK_ADDR_W(x) \ + if (!MEM_ADDR_OK (x)) break +#endif + +/* On the PDP-9, + The autoincrement registers are in field zero only. Regardless + of extend mode, indirect addressing through 00010-00017 + will access absolute locations 00010-00017. + Indirect addressing range is determined by extend mode. If + extend mode is off, and autoincrementing is used, the + resolved address is in bank 0 (KG09B maintenance manual). + There is no indexing. + Memory protection is implemented for foreground/background operation. +*/ + +#if defined (PDP9) +#define CHECK_AUTO_INC \ + if ((IR & 017770) == 010) { \ + MA = MA & 017; \ + M[MA] = (M[MA] + 1) & 0777777; } +#define INDIRECT \ + MA = memm? M[MA] & IAMASK: (MA & epcmask) | (M[MA] & damask) +#define CHECK_ADDR_R(x) \ + if (usmd) { \ + if (!MEM_ADDR_OK (x)) { \ + nexm = prvn = trap_pending = 1; \ + break; } \ + if ((x) >= BR) { \ + prvn = trap_pending = 1; \ + break; } } \ + if (!MEM_ADDR_OK (x)) nexm = 1 +#define CHECK_INDEX /* no indexing capability */ +#define CHECK_ADDR_W(x) \ + CHECK_ADDR_R (x); \ + if (!MEM_ADDR_OK (x)) break +#endif + +/* On the PDP-15, + The autoincrement registers are in page zero only. Regardless + of bank mode, indirect addressing through 00010-00017 + will access absolute locations 00010-00017. + Indirect addressing range is determined by autoincrementing. + Indexing is available if bank mode is off. + Memory protection is implemented for foreground/background operation. +*/ + +#if defined (PDP15) +#define CHECK_AUTO_INC \ + if ((IR & damask & ~07) == 00010) { \ + MA = MA & 017; \ + M[MA] = (M[MA] + 1) & 0777777; } +#define INDIRECT \ + if (rest_pending) { \ + rest_pending = 0; \ + LAC = ((M[MA] << 1) & 01000000) | (LAC & 0777777); \ + memm = (M[MA] >> 16) & 1; \ + usmd = (M[MA] >> 15) & 1; } \ + MA = ((IR & damask & ~07) != 00010)? \ + (PC & BLKMASK) | (M[MA] & IAMASK): (M[MA] & ADDRMASK) +#define CHECK_INDEX \ + if ((IR & 0010000) && (memm == 0)) MA = (MA + XR) & ADDRMASK +#define CHECK_ADDR_R(x) \ + if (usmd) { \ + if (!MEM_ADDR_OK (x)) { \ + nexm = prvn = trap_pending = 1; \ + break; } \ + if ((x) >= BR) { \ + prvn = trap_pending = 1; \ + break; } } \ + if (!MEM_ADDR_OK (x)) nexm = 1 +#define CHECK_ADDR_W(x) \ + CHECK_ADDR_R (x); \ + if (!MEM_ADDR_OK (x)) break +#endif + +/* Fetch, decode instruction */ + +CHECK_ADDR_R (PC); /* validate PC */ +IR = M[PC]; /* fetch instruction */ +PC = INCR_ADDR (PC); /* increment PC */ +#if defined (PDP9) || defined (PDP15) +if (!ion_defer) usmd = usmdbuf; /* no IOT? load usmd */ +#endif +if (ion_defer) ion_defer = ion_defer - 1; /* count down defer */ +xct_count = 0; /* track nested XCT's */ +sim_interval = sim_interval - 1; + +xct_instr: /* label for XCT */ +MA = (PC & epcmask) | (IR & damask); /* effective address */ +switch ((IR >> 13) & 037) { /* decode IR<0:4> */ + +/* LAC: opcode 20 */ + +case 011: /* LAC, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 010: /* LAC, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + LAC = (LAC & 01000000) | M[MA]; + break; + +/* DAC: opcode 04 */ + +case 003: /* DAC, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 002: /* DAC, dir */ + CHECK_INDEX; + CHECK_ADDR_W (MA); + M[MA] = LAC & 0777777; + break; + +/* DZM: opcode 14 */ + +case 007: /* DZM, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 006: /* DZM, direct */ + CHECK_INDEX; + CHECK_ADDR_W (MA); + M[MA] = 0; + break; + +/* AND: opcode 50 */ + +case 025: /* AND, ind */ + CHECK_AUTO_INC; + INDIRECT; +case 024: /* AND, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + LAC = LAC & (M[MA] | 01000000); + break; + +/* XOR: opcode 24 */ + +case 013: /* XOR, ind */ + CHECK_AUTO_INC; + INDIRECT; +case 012: /* XOR, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + LAC = LAC ^ M[MA]; + break; + +/* ADD: opcode 30 */ + +case 015: /* ADD, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 014: /* ADD, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + t = (LAC & 0777777) + M[MA]; + if (t > 0777777) t = (t + 1) & 0777777; /* end around carry */ + if (((~LAC ^ M[MA]) & (LAC ^ t)) & 0400000) /* overflow? */ + LAC = 01000000 | t; /* set link */ + else LAC = (LAC & 01000000) | t; + break; + +/* TAD: opcode 34 */ + +case 017: /* TAD, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 016: /* TAD, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + LAC = (LAC + M[MA]) & 01777777; + break; + +/* ISZ: opcode 44 */ + +case 023: /* ISZ, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 022: /* ISZ, dir */ + CHECK_INDEX; + CHECK_ADDR_W (MA); + M[MA] = (M[MA] + 1) & 0777777; + if (M[MA] == 0) PC = INCR_ADDR (PC); + break; + +/* SAD: opcode 54 */ + +case 027: /* SAD, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 026: /* SAD, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + if ((LAC & 0777777) != M[MA]) PC = INCR_ADDR (PC); + break; + +/* XCT: opcode 40 */ + +case 021: /* XCT, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 020: /* XCT, dir */ + CHECK_INDEX; + CHECK_ADDR_R (MA); + if (usmd && (xct_count != 0)) { /* trap and chained? */ + prvn = trap_pending = 1; + break; } + if (xct_count >= xct_max) { /* too many XCT's? */ + reason = STOP_XCT; + break; } + xct_count = xct_count + 1; /* count XCT's */ +#if defined (PDP9) + ion_defer = 1; /* defer intr */ +#endif + IR = M[MA]; /* get instruction */ + goto xct_instr; /* go execute */ + +/* CAL: opcode 00 + + On the PDP-4 and PDP-7, CAL (I) is exactly the same as JMS (I) 20 + On the PDP-9, CAL clears user mode + On the PDP-15, CAL goes to absolute 20, regardless of mode +*/ + +case 001: case 000: /* CAL */ + t = usmd; +#if defined (PDP15) + MA = 020; +#else + MA = (memm? 0: PC & epcmask) | 020; /* MA = 20 */ +#endif +#if defined (PDP9) || defined (PDP15) + usmd = 0; /* clear user mode */ +#endif + if (IR & 0020000) { INDIRECT; } /* indirect? */ + CHECK_ADDR_W (MA); + old_PC = PC; + M[MA] = JMS_WORD (t); /* save state */ + PC = INCR_ADDR (MA); + break; + +/* JMS: opcode 010 */ + +case 005: /* JMS, indir */ + CHECK_AUTO_INC; + INDIRECT; +case 004: /* JMS, dir */ + CHECK_INDEX; + CHECK_ADDR_W (MA); + old_PC = PC; + M[MA] = JMS_WORD (usmd); /* save state */ + PC = INCR_ADDR (MA); + break; + +/* JMP: opcode 60 + + Restore quirks: + On the PDP-7 and PDP-9, EMIR can only clear extend + On the PDP-15, any I triggers restore, but JMP I is conventional +*/ + +case 031: /* JMP, indir */ + CHECK_AUTO_INC; /* check auto inc */ +#if defined (PDP7) || defined (PDP9) + if (emir_pending && (((M[MA] >> 16) & 1) == 0)) memm = 0; +#elif defined (PDP9) + if (rest_pending) { /* restore pending? */ + LAC = ((M[MA] << 1) & 01000000) | (LAC & 0777777); + memm = (M[MA] >> 16) & 1; + usmd = (M[MA] >> 15) & 1); } +#endif + INDIRECT; /* complete indirect */ + emir_pending = rest_pending = 0; +case 030: /* JMP, dir */ + CHECK_INDEX; + old_PC = PC; /* save old PC */ + PC = MA; + break; + +/* OPR: opcode 74 */ + +case 037: /* OPR, indir */ + LAC = (LAC & 01000000) | IR; /* LAW */ + break; + +case 036: /* OPR, dir */ + switch ((IR >> 6) & 017) { /* decode IR<8:11> */ + case 0: /* nop */ + break; + case 1: /* SMA */ + if ((LAC & 0400000) != 0) PC = INCR_ADDR (PC); + break; + case 2: /* SZA */ + if ((LAC & 0777777) == 0) PC = INCR_ADDR (PC); + break; + case 3: /* SZA | SMA */ + if (((LAC & 0777777) == 0) || ((LAC & 0400000) != 0)) + PC = INCR_ADDR (PC); + break; + case 4: /* SNL */ + if (LAC >= 01000000) PC = INCR_ADDR (PC); + break; + case 5: /* SNL | SMA */ + if (LAC >= 0400000) PC = INCR_ADDR (PC); + break; + case 6: /* SNL | SZA */ + if ((LAC >= 01000000) || (LAC == 0)) PC = INCR_ADDR (PC); + break; + case 7: /* SNL | SZA | SMA */ + if ((LAC >= 0400000) || (LAC == 0)) PC = INCR_ADDR (PC); + break; + case 010: /* SKP */ + PC = INCR_ADDR (PC); + break; + case 011: /* SPA */ + if ((LAC & 0400000) == 0) PC = INCR_ADDR (PC); + break; + case 012: /* SNA */ + if ((LAC & 0777777) != 0) PC = INCR_ADDR (PC); + break; + case 013: /* SNA & SPA */ + if (((LAC & 0777777) != 0) && ((LAC & 0400000) == 0)) + PC = INCR_ADDR (PC); + break; + case 014: /* SZL */ + if (LAC < 01000000) PC = INCR_ADDR (PC); + break; + case 015: /* SZL & SPA */ + if (LAC < 0400000) PC = INCR_ADDR (PC); + break; + case 016: /* SZL & SNA */ + if ((LAC < 01000000) && (LAC != 0)) PC = INCR_ADDR (PC); + break; + case 017: /* SZL & SNA & SPA */ + if ((LAC < 0400000) && (LAC != 0)) PC = INCR_ADDR (PC); + break; } /* end switch skips */ + +/* OPR, continued */ + + switch (((IR >> 9) & 014) | (IR & 03)) { /* IR<5:6,16:17> */ + case 0: /* NOP */ + break; + case 1: /* CMA */ + LAC = LAC ^ 0777777; + break; + case 2: /* CML */ + LAC = LAC ^ 01000000; + break; + case 3: /* CML CMA */ + LAC = LAC ^ 01777777; + break; + case 4: /* CLL */ + LAC = LAC & 0777777; + break; + case 5: /* CLL CMA */ + LAC = (LAC & 0777777) ^ 0777777; + break; + case 6: /* CLL CML = STL */ + LAC = LAC | 01000000; + break; + case 7: /* CLL CML CMA */ + LAC = (LAC | 01000000) ^ 0777777; + break; + case 010: /* CLA */ + LAC = LAC & 01000000; + break; + case 011: /* CLA CMA = STA */ + LAC = LAC | 0777777; + break; + case 012: /* CLA CML */ + LAC = (LAC & 01000000) ^ 01000000; + break; + case 013: /* CLA CML CMA */ + LAC = (LAC | 0777777) ^ 01000000; + break; + case 014: /* CLA CLL */ + LAC = 0; + break; + case 015: /* CLA CLL CMA */ + LAC = 0777777; + break; + case 016: /* CLA CLL CML */ + LAC = 01000000; + break; + case 017: /* CLA CLL CML CMA */ + LAC = 01777777; + break; } /* end decode */ + +/* OPR, continued */ + + if (IR & 0000004) { /* OAS */ +#if defined (PDP9) || defined (PDP15) + if (usmd) prvn = trap_pending = 1; + else +#endif + LAC = LAC | SR; } + + switch (((IR >> 8) & 04) | ((IR >> 3) & 03)) { /* decode IR<7,13:14> */ + case 1: /* RAL */ + LAC = ((LAC << 1) | (LAC >> 18)) & 01777777; + break; + case 2: /* RAR */ + LAC = ((LAC >> 1) | (LAC << 18)) & 01777777; + break; + case 3: /* RAL RAR */ +#if defined (PDP15) /* PDP-15 */ + LAC = (LAC + 1) & 01777777; /* IAC */ +#else /* PDP-4,-7,-9 */ + reason = stop_inst; /* undefined */ +#endif + break; + case 5: /* RTL */ + LAC = ((LAC << 2) | (LAC >> 17)) & 01777777; + break; + case 6: /* RTR */ + LAC = ((LAC >> 2) | (LAC << 17)) & 01777777; + break; + case 7: /* RTL RTR */ +#if defined (PDP15) /* PDP-15 */ + LAC = ((LAC >> 9) & 0777) | ((LAC & 0777) << 9) | + (LAC & 01000000); /* BSW */ +#else /* PDP-4,-7,-9 */ + reason = stop_inst; /* undefined */ +#endif + break; } /* end switch rotate */ + + if (IR & 0000040) { /* HLT */ + if (usmd) prvn = trap_pending = 1; + else reason = STOP_HALT; } + break; /* end OPR */ + +/* EAE: opcode 64 + + The EAE is microprogrammed to execute variable length signed and + unsigned shift, multiply, divide, and normalize. Most commands are + controlled by a six bit step counter (SC). In the hardware, the step + counter is complemented on load and then counted up to zero; timing + guarantees an initial increment, which completes the two's complement + load. In the simulator, the SC is loaded normally and then counted + down to zero; the read SC command compensates. +*/ + +case 033: case 032: /* EAE */ + if (cpu_unit.flags & UNIT_NOEAE) break; /* disabled? */ + if (IR & 0020000) /* IR<4>? AC0 to L */ + LAC = ((LAC << 1) & 01000000) | (LAC & 0777777); + if (IR & 0010000) MQ = 0; /* IR<5>? clear MQ */ + if ((IR & 0004000) && (LAC & 0400000)) /* IR<6> and minus? */ + eae_ac_sign = 01000000; /* set eae_ac_sign */ + else eae_ac_sign = 0; /* if not, unsigned */ + if (IR & 0002000) MQ = (MQ | LAC) & 0777777; /* IR<7>? or AC */ + else if (eae_ac_sign) LAC = LAC ^ 0777777; /* if not, |AC| */ + if (IR & 0001000) LAC = LAC & 01000000; /* IR<8>? clear AC */ + link_init = LAC & 01000000; /* link temporary */ + fill = link_init? 0777777: 0; /* fill = link */ + + switch ((IR >> 6) & 07) { /* case on IR<9:11> */ + case 0: /* setup */ + if (IR & 04) LAC = LAC ^ 0777777; /* IR<15>? ~AC */ + if (IR & 02) LAC = LAC | MQ; /* IR<16>? or MQ */ + if (IR & 01) LAC = LAC | ((-SC) & 077); /* IR<17>? or SC */ + break; + + case 1: /* multiply */ + CHECK_ADDR_R (PC); /* validate PC */ + MA = M[PC]; /* get next word */ + PC = INCR_ADDR (PC); /* increment PC */ + if (eae_ac_sign) MQ = MQ ^ 0777777; /* EAE AC sign? ~MQ */ + LAC = LAC & 0777777; /* clear link */ + for (SC = IR & 077; SC != 0; SC--) { /* loop per step cnt */ + if (MQ & 1) LAC = LAC + MA; /* MQ<17>? add */ + MQ = (MQ >> 1) | ((LAC & 1) << 17); + LAC = LAC >> 1; } /* shift AC'MQ right */ + if (eae_ac_sign ^ link_init) { /* result negative? */ + LAC = LAC ^ 0777777; + MQ = MQ ^ 0777777; } + break; + +/* EAE, continued + + Divide uses a non-restoring divide. This code duplicates the PDP-7 + algorithm, except for its use of two's complement arithmetic instead + of 1's complement. + + The quotient is generated in one's complement form; therefore, the + quotient is complemented if the input operands had the same sign + (that is, if the quotient is positive). +*/ + + case 3: /* divide */ + CHECK_ADDR_R (PC); /* validate PC */ + MA = M[PC]; /* get next word */ + PC = INCR_ADDR (PC); /* increment PC */ + if (eae_ac_sign) MQ = MQ ^ 0777777; /* EAE AC sign? ~MQ */ + if ((LAC & 0777777) >= MA) { /* overflow? */ + LAC = (LAC - MA) | 01000000; /* set link */ + break; } + LAC = LAC & 0777777; /* clear link */ + t = 0; /* init loop */ + for (SC = IR & 077; SC != 0; SC--) { + if (t) LAC = (LAC + MA) & 01777777; + else LAC = (LAC - MA) & 01777777; + t = (LAC >> 18) & 1; /* quotient bit */ + if (SC > 1) LAC = /* skip if last */ + ((LAC << 1) | (MQ >> 17)) & 01777777; + MQ = ((MQ << 1) | t) & 0777777; } + if (t) LAC = (LAC + MA) & 01777777; + if (eae_ac_sign) LAC = LAC ^ 0777777; /* sgn rem = sgn divd */ + if (eae_ac_sign ^ link_init ^ 1) MQ = MQ ^ 0777777; + break; + +/* EAE, continued + + EAE shifts, whether left or right, fill from the link. If the + operand sign has been copied to the link, this provides correct + sign extension for one's complement numbers. +*/ + + case 4: /* normalize */ +#if defined (PDP15) + if (!usmd) ion_defer = 2; /* free cycles */ +#endif + for (SC = IR & 077; ((LAC & 0400000) == + ((LAC << 1) & 0400000)) && (SC != 0); SC--) { + LAC = (LAC << 1) | ((MQ >> 17) & 1); + MQ = (MQ << 1) | (link_init >> 18); } + LAC = link_init | (LAC & 0777777); /* trim AC, restore L */ + MQ = MQ & 0777777; /* trim MQ */ + break; + case 5: /* long right shift */ + t = IR & 077; /* get shift count */ + if (t < 18) { + MQ = ((LAC << (18 - t)) | (MQ >> t)) & 0777777; + LAC = ((fill << (18 - t)) | (LAC >> t)) & 01777777; } + else { if (t < 36) MQ = + ((fill << (36 - t)) | (LAC >> (t - 18))) & 0777777; + else MQ = fill; + LAC = link_init | fill; } + SC = 0; /* clear step count */ + break; + case 6: /* long left shift */ + t = IR & 077; /* get shift count */ + if (t < 18) { + LAC = link_init | + (((LAC << t) | (MQ >> (18 - t))) & 0777777); + MQ = ((MQ << t) | (fill >> (18 - t))) & 0777777; } + else { if (t < 36) LAC = link_init | + (((MQ << (t - 18)) | (fill >> (36 - t))) & 0777777); + else LAC = link_init | fill; + MQ = fill; } + SC = 0; /* clear step count */ + break; + case 7: /* AC left shift */ + t = IR & 077; /* get shift count */ + if (t < 18) LAC = link_init | + (((LAC << t) | (fill >> (18 - t))) & 0777777); + else LAC = link_init | fill; + SC = 0; /* clear step count */ + break; } /* end switch IR */ + break; /* end case EAE */ + +/* PDP-15 index operates: opcode 72 */ + +case 035: /* index operates */ +#if defined (PDP15) + t = (IR & 0400)? IR | 0777000: IR & 0377; /* sext immediate */ + switch ((IR >> 9) & 017) { /* case on IR<5:8> */ + case 001: /* PAX */ + XR = LAC & 0777777; + break; + case 002: /* PAL */ + LR = LAC & 0777777; + break; + case 003: + LAC = (LAC & 01000000) | ((LAC + t) & 0777777); /* AAC */ + break; + case 004: /* PXA */ + LAC = (LAC & 01000000) | XR; + break; + case 005: /* AXS */ + XR = (XR + t) & 0777777; + if (SEXT (XR) >= SEXT (LR)) PC = INCR_ADDR (PC); + break; + case 006: /* PXL */ + LR = XR; + break; + case 010: /* PLA */ + LAC = (LAC & 01000000) | LR; + break; + case 011: /* PLX */ + XR = LR; + break; + case 015: /* CLX */ + XR = 0; + break; + case 016: /* CLL */ + LR = 0; + break; + case 017: /* AXR */ + XR = (XR + t) & 0777777; + break; } /* end switch IR */ + break; /* end case */ +#endif + +/* IOT: opcode 70 + + The 18b PDP's have different definitions of various control IOT's. + + IOT PDP-4 PDP-7 PDP-9 PDP-15 + + 700002 IOF IOF IOF IOF + 700042 ION ION ION ION + 700062 undefined ITON undefined undefined + 701701 undefined undefined MPSK MPSK + 701741 undefined undefined MPSNE MPSNE + 701702 undefined undefined MPCV MPCV + 701742 undefined undefined MPEU MPEU + 701704 undefined undefined MPLD MPLD + 701744 undefined undefined MPCNE MPCNE + 703201 undefined undefined PFSF PFSF + 703301 undefined TTS TTS TTS + 703341 undefined SKP7 SKP7 SPCO + 703302 undefined CAF CAF CAF + 703344 undefined undefined DBR DBR + 707701 undefined SEM SEM undefined + 707741 undefined undefined undefined SKP15 + 707761 undefined undefined undefined SBA + 707702 undefined EEM EEM undefined + 707742 undefined EMIR EMIR RES + 707762 undefined undefined undefined DBA + 707704 undefined LEM LEM undefined + 707764 undefined undefined undefined EBA +*/ + +case 034: /* IOT */ +#if defined (PDP15) + if (IR & 0010000) { /* floating point? */ +/* PC = fp15 (PC, IR); /* process */ + break; } +#endif + if (usmd) { /* user mode? */ + prvn = trap_pending = 1; /* trap */ + break; } + device = (IR >> 6) & 077; /* device = IR<6:11> */ + pulse = IR & 067; /* pulse = IR<12:17> */ + if (IR & 0000010) LAC = LAC & 01000000; /* clear AC? */ + iot_data = LAC & 0777777; /* AC unchanged */ + +/* PDP-4 system IOT's */ + +#if defined (PDP4) + switch (device) { /* decode IR<6:11> */ + case 0: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = ion_defer = 1; /* ION */ + else iot_data = clk (pulse, iot_data); + break; + +/* PDP-7 system IOT's */ + +#elif defined (PDP7) + switch (device) { /* decode IR<6:11> */ + case 0: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = ion_defer = 1; /* ION */ + else if (pulse == 062) /* ITON */ + usmd = ion = ion_defer = 1; + else iot_data = clk (pulse, iot_data); + break; + case 033: /* CPU control */ + if ((pulse == 001) || (pulse == 041)) PC = INCR_ADDR (PC); + else if (pulse == 002) reset_all (0); /* CAF */ + break; + case 077: /* extended memory */ + if ((pulse == 001) && memm) PC = INCR_ADDR (PC); + else if (pulse == 002) memm = 1; /* EEM */ + else if (pulse == 042) /* EMIR */ + memm = emir_pending = 1; /* ext on, restore */ + else if (pulse == 004) memm = 0; /* LEM */ + break; + +/* PDP-9 system IOT's */ + +#elif defined (PDP9) + ion_defer = 1; /* delay interrupts */ + switch (device) { /* decode IR<6:11> */ + case 000: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = 1; /* ION */ + else iot_data = clk (pulse, iot_data); + break; + case 017: /* mem protection */ + if ((pulse == 001) && prvn) PC = INCR_ADDR (PC); + else if ((pulse == 041) && nexm) PC = INCR_ADDR (PC); + else if (pulse == 002) prvn = 0; + else if (pulse == 042) usmdbuf = 1; + else if (pulse == 004) BR = LAC & 076000; + else if (pulse == 044) nexm = 0; + break; + case 032: /* power fail */ + if ((pulse == 001) && (int_req & INT_PWRFL)) + PC = INCR_ADDR (PC); + break; + case 033: /* CPU control */ + if ((pulse == 001) || (pulse == 041)) PC = INCR_ADDR (PC); + else if (pulse == 002) reset_all (0); /* CAF */ + else if (pulse == 044) rest_pending = 1; /* DBR */ + break; + case 077: /* extended memory */ + if ((pulse == 001) && memm) PC = INCR_ADDR (PC); + else if (pulse == 002) memm = 1; /* EEM */ + else if (pulse == 042) /* EMIR */ + memm = emir_pending = 1; /* ext on, restore */ + else if (pulse == 004) memm = 0; /* LEM */ + break; + +/* PDP-15 system IOT's */ + +#elif defined (PDP15) + ion_defer = 1; /* delay interrupts */ + switch (device) { /* decode IR<6:11> */ + case 000: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = 1; /* ION */ + else iot_data = clk (pulse, iot_data); + break; + case 017: /* mem protection */ + if ((pulse == 001) && prvn) PC = INCR_ADDR (PC); + else if ((pulse == 041) && nexm) PC = INCR_ADDR (PC); + else if (pulse == 002) prvn = 0; + else if (pulse == 042) usmdbuf = 1; + else if (pulse == 004) BR = LAC & 0377400; + else if (pulse == 044) nexm = 0; + break; + case 032: /* power fail */ + if ((pulse == 001) && (int_req & INT_PWRFL)) + PC = INCR_ADDR (PC); + break; + case 033: /* CPU control */ + if ((pulse == 001) || (pulse == 041)) PC = INCR_ADDR (PC); + else if (pulse == 002) reset_all (0); /* CAF */ + else if (pulse == 044) rest_pending = 1; /* DBR */ + break; + case 077: /* bank addressing */ + if ((pulse == 041) || ((pulse == 061) && memm)) + PC = INCR_ADDR (PC); /* SKP15, SBA */ + else if (pulse == 042) rest_pending = 1; /* RES */ + else if (pulse == 062) memm = 0; /* DBA */ + else if (pulse == 064) memm = 1; /* EBA */ + break; +#endif + +/* IOT, continued */ + + case 1: /* PTR */ + iot_data = ptr (pulse, iot_data); + break; + case 2: /* PTP */ + iot_data = ptp (pulse, iot_data); + break; + case 3: /* TTI */ + if (pulse == 004) iot_data = upd_iors (); + else iot_data = tti (pulse, iot_data); + break; + case 4: /* TTO */ + iot_data = tto (pulse, iot_data); + break; +#if defined (DRM) + case 060: /* drum */ + iot_data = drm60 (pulse, iot_data); + break; + case 061: + iot_data = drm61 (pulse, iot_data); + break; + case 062: + iot_data = drm62 (pulse, iot_data); + break; +#endif +#if defined (RP) + case 063: /* RP15 */ + iot_data = rp63 (pulse, iot_data); + break; + case 064: + iot_data = rp64 (pulse, iot_data); + break; +#endif + case 065: /* LPT */ + iot_data = lpt65 (pulse, iot_data); + break; + case 066: + iot_data = lpt66 (pulse, iot_data); + break; +#if defined (RF) + case 070: /* RF09 */ + iot_data = rf70 (pulse, iot_data); + break; + case 072: + iot_data = rf72 (pulse, iot_data); + break; +#endif +#if defined (MTA) + case 073: /* TC59 */ + iot_data = mt (pulse, iot_data); + break; +#endif + default: /* unknown device */ + reason = stop_inst; /* stop on flag */ + break; } /* end switch device */ + LAC = LAC | (iot_data & 0777777); + if (iot_data & IOT_SKP) PC = INCR_ADDR (PC); + if (iot_data >= IOT_REASON) reason = iot_data >> IOT_V_REASON; + break; /* end case IOT */ + } /* end switch opcode */ +} /* end while */ + +/* Simulation halted */ + +saved_PC = PC & ADDRMASK; /* save copies */ +saved_LAC = LAC & 01777777; +saved_MQ = MQ & 0777777; +iors = upd_iors (); /* get IORS */ +return reason; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +SC = 0; +eae_ac_sign = 0; +ion = ion_defer = 0; +int_req = int_req & ~INT_PWRFL; +BR = 0; +memm = usmd = usmdbuf = 0; +nexm = prvn = trap_pending = 0; +emir_pending = rest_pending = 0; +return cpu_svc (&cpu_unit); +} + +/* Process IORS instruction */ + +int32 upd_iors (void) +{ +extern int32 std_iors (void); +extern int32 lpt_iors (void); +#if defined (DRM) +extern int32 drm_iors (void); +#endif +#if defined (RF) +extern int32 rf_iors (void); +#endif +#if defined (RP) +extern int32 rp_iors (void); +#endif +#if defined (MTA) +extern int32 mt_iors (void); +#endif + +return (ion? IOS_ION: 0) | +#if defined (DRM) + drm_iors () | +#endif +#if defined (RP) + rp_iors () | +#endif +#if defined (RF) + rf_iors () | +#endif +#if defined (MTA) + mt_iors () | +#endif + std_iors () | lpt_iors (); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 0777777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & 0777777; +return SCPE_OK; +} + +/* Service breakpoint */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} diff --git a/pdp18b_defs.h b/pdp18b_defs.h new file mode 100644 index 00000000..44dd219b --- /dev/null +++ b/pdp18b_defs.h @@ -0,0 +1,230 @@ +/* pdp18b_defs.h: 18b PDP simulator definitions + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + 02-Jan-96 RMS Added fixed head and moving head disks + 31-Dec-95 RMS Added memory management + 19-Mar-95 RMS Added dynamic memory size + + The author gratefully acknowledges the help of Craig St. Clair and + Deb Tevonian in locating archival material about the 18b PDP's +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Models: only one should be defined + + model memory CPU options I/O options + + PDP4 8K none Type 65 KSR-28 Teletype (Baudot) + integral paper tape reader + Type 75 paper tape punch + integral real time clock + Type 62 line printer (Hollerith) + + PDP7 32K Type 177 EAE Type 649 KSR-33 Teletype + Type 148 mem extension Type 444 paper tape reader + Type 75 paper tape punch + integral real time clock + Type 647B line printer (sixbit) + Type 24 serial drum + + PDP9 32K KE09A EAE KSR-33 Teletype + KG09B mem extension PC09A paper tape reader and punch + KP09A power detection integral real time clock + KX09A mem protection Type 647D/E line printer (sixbit) + RF09/RS09 fixed head disk + TC59 magnetic tape + + PDP15 128K KE15 EAE KSR-35 Teletype + KF15 power detection PC15 paper tape reader and punch + KM15 mem protection KW15 real time clock + ??KT15 mem relocation LP15 line printer + RP15 disk pack + RF15/RF09 fixed head disk + TC59D magnetic tape + + ??Indicates not implemented. The PDP-4 manual refers to both an EAE + ??and a memory extension control; there is no documentation on either. +*/ + +#if !defined (PDP4) && !defined (PDP7) && !defined (PDP9) && !defined (PDP15) +#define PDP9 0 /* default to PDP-9 */ +#endif + +/* Memory and peripheral configuration */ + +#if defined (PDP4) +#define ADDRSIZE 13 +#define KSR28 0 /* Baudot terminal */ +#define TYPE62 0 /* Hollerith printer */ +#elif defined (PDP7) +#define ADDRSIZE 15 +#define TYPE647 0 /* sixbit printer */ +#define DRM 0 /* drum */ +#elif defined (PDP9) +#define ADDRSIZE 15 +#define TYPE647 0 /* sixbit printer */ +#define RF 0 /* fixed head disk */ +#define MTA 0 /* magtape */ +#elif defined (PDP15) +#define ADDRSIZE 17 +#define LP15 0 /* ASCII printer */ +#define RF 0 /* fixed head disk */ +#define RP 0 /* disk pack */ +#define MTA 0 /* magtape */ +#define BLKMASK 0300000 /* block mask */ +#endif + +#define ADDRMASK ((1 << ADDRSIZE) - 1) /* address mask */ +#define IAMASK 077777 /* ind address mask */ +#define MAXMEMSIZE (1 << ADDRSIZE) /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE) + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_XCT 4 /* nested XCT's */ + +/* IOT subroutine return codes */ + +#define IOT_V_SKP 18 /* skip */ +#define IOT_V_REASON 19 /* reason */ +#define IOT_SKP (1 << IOT_V_SKP) +#define IOT_REASON (1 << IOT_V_REASON) + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Interrupt system + + The interrupt system can be modelled on either the flag driven system + of the PDP-4 and PDP-7 or the API driven system of the PDP-9 and PDP-15. + If flag based, API is hard to implement; if API based, IORS requires + extra code for implementation. I've chosen an API based model. + + Interrupt system, priority is left to right. + + <30:28> = priority 0 + <27:20> = priority 1 + <19:14> = priority 2 + <13:10> = priority 3 + <9:4> = PI only + <3> = priority 4 (software) + <2> = priority 5 (software) + <1> = priority 6 (software) + <0> = priority 7 (software) +*/ + +#define INT_V_PWRFL 30 /* powerfail */ +#define INT_V_DTA 27 /* DECtape */ +#define INT_V_MTA 26 /* magtape */ +#define INT_V_DRM 25 /* drum */ +#define INT_V_RF 24 /* fixed head disk */ +#define INT_V_RP 23 /* disk pack */ +#define INT_V_PTR 19 /* paper tape reader */ +#define INT_V_LPT 18 /* line printer */ +#define INT_V_LPTSPC 17 /* line printer spc */ +#define INT_V_CLK 13 /* clock */ +#define INT_V_TTI 9 /* keyboard */ +#define INT_V_TTO 8 /* terminal */ +#define INT_V_PTP 7 /* paper tape punch */ +#define INT_V_SW4 3 /* software 4 */ +#define INT_V_SW5 2 /* software 5 */ +#define INT_V_SW6 1 /* software 6 */ +#define INT_V_SW7 0 /* software 7 */ + +#define INT_PWRFL (1 << INT_V_PWRFL) +#define INT_DTA (1 << INT_V_DTA) +#define INT_MTA (1 << INT_V_MTA) +#define INT_DRM (1 << INT_V_DRM) +#define INT_RF (1 << INT_V_RF) +#define INT_RP (1 << INT_V_RP) +#define INT_PTR (1 << INT_V_PTR) +#define INT_LPT (1 << INT_V_LPT) +#define INT_LPTSPC (1 << INT_V_LPTSPC) +#define INT_CLK (1 << INT_V_CLK) +#define INT_TTI (1 << INT_V_TTI) +#define INT_TTO (1 << INT_V_TTO) +#define INT_PTP (1 << INT_V_PTP) +#define INT_SW4 (1 << INT_V_SW4) +#define INT_SW5 (1 << INT_V_SW5) +#define INT_SW6 (1 << INT_V_SW6) +#define INT_SW7 (1 << INT_V_SW7) + +/* I/O status flags for the IORS instruction + + bit PDP-4 PDP-7 PDP-9 PDP-15 + + 0 intr on intr on intr on intr on + 1 tape rdr flag* tape rdr flag* tape rdr flag* tape rdr flag* + 2 tape pun flag* tape pun flag* tape pun flag* tape pun flag* + 3 keyboard flag* keyboard flag* keyboard flag* keyboard flag* + 4 type out flag* type out flag* type out flag* type out flag* + 5 display flag* display flag* light pen flag* light pen flag* + 6 clk ovflo flag* clk ovflo flag* clk ovflo flag* clk ovflo flag* + 7 clk enable flag clk enable flag clk enable flag clk enable flag + 8 mag tape flag* mag tape flag* tape rdr empty* tape rdr empty* + 9 card rdr col* * tape pun empty tape pun empty + 10 card rdr ~busy DECtape flag* DECtape flag* + 11 card rdr error magtape flag* magtape flag* + 12 card rdr EOF disk pack flag* + 13 card pun row* DECdisk flag* DECdisk flag* + 14 card pun error lpt flag* + 15 lpt flag* lpt flag* lpt flag* + 16 lpt space flag* lpt error flag lpt error flag + 17 drum flag* drum flag* +*/ + +#define IOS_ION 0400000 /* interrupts on */ +#define IOS_PTR 0200000 /* tape reader */ +#define IOS_PTP 0100000 /* tape punch */ +#define IOS_TTI 0040000 /* keyboard */ +#define IOS_TTO 0020000 /* terminal */ +#define IOS_LPEN 0010000 /* light pen */ +#define IOS_CLK 0004000 /* clock */ +#define IOS_CLKON 0002000 /* clock enable */ +#define IOS_DTA 0000200 /* DECtape */ +#define IOS_RP 0000040 /* disk pack */ +#define IOS_RF 0000020 /* fixed head disk */ +#define IOS_DRM 0000001 /* drum */ +#if defined (PDP4) || defined (PDP7) +#define IOS_MTA 0001000 /* magtape */ +#define IOS_LPT 0000004 /* line printer */ +#define IOS_LPT1 0000002 /* line printer stat */ +#elif defined (PDP9) +#define IOS_PTRERR 0001000 /* reader empty */ +#define IOS_PTPERR 0000400 /* punch empty */ +#define IOS_MTA 0000100 /* magtape */ +#define IOS_LPT 0000004 /* line printer */ +#define IOS_LPT1 0000002 /* line printer stat */ +#elif defined (PDP15) +#define IOS_PTRERR 0001000 /* reader empty */ +#define IOS_PTPERR 0000400 /* punch empty */ +#define IOS_MTA 0000100 /* magtape */ +#define IOS_LPT 0000010 /* line printer */ +#endif diff --git a/pdp18b_drm.c b/pdp18b_drm.c new file mode 100644 index 00000000..a2126121 --- /dev/null +++ b/pdp18b_drm.c @@ -0,0 +1,208 @@ +/* pdp18b_drm.c: drum/fixed head disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + drm (PDP-7) Type 24 serial drum + (PDP-9) RM09 serial drum + + 14-Apr-99 RMS Changed t_addr to unsigned +*/ + +#include "pdp18b_defs.h" +#include + +/* Constants */ + +#define DRM_NUMWDS 256 /* words/sector */ +#define DRM_NUMSC 2 /* sectors/track */ +#define DRM_NUMTR 256 /* tracks/drum */ +#define DRM_NUMDK 1 /* drum/controller */ +#define DRM_NUMWDT (DRM_NUMWDS * DRM_NUMSC) /* words/track */ +#define DRM_SIZE (DRM_NUMDK * DRM_NUMTR * DRM_NUMWDT) /* words/drum */ +#define DRM_SMASK ((DRM_NUMTR * DRM_NUMSC) - 1) /* sector mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define DRM_READ 000 /* read */ +#define DRM_WRITE 040 /* write */ + +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DRM_NUMWDT))) + +extern int32 M[]; +extern int32 int_req; +extern UNIT cpu_unit; +int32 drm_da = 0; /* track address */ +int32 drm_ma = 0; /* memory address */ +int32 drm_err = 0; /* error flag */ +int32 drm_wlk = 0; /* write lock */ +int32 drm_time = 10; /* inter-word time */ +int32 drm_stopioe = 1; /* stop on error */ +t_stat drm_svc (UNIT *uptr); +t_stat drm_reset (DEVICE *dptr); +t_stat drm_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* DRM data structures + + drm_dev DRM device descriptor + drm_unit DRM unit descriptor + drm_reg DRM register list +*/ + +UNIT drm_unit = + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DRM_SIZE) }; + +REG drm_reg[] = { + { ORDATA (DA, drm_da, 9) }, + { ORDATA (MA, drm_ma, 15) }, + { FLDATA (INT, int_req, INT_V_DRM) }, + { FLDATA (DONE, int_req, INT_V_DRM) }, + { FLDATA (ERR, drm_err, 0) }, + { ORDATA (WLK, drm_wlk, 32) }, + { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, drm_stopioe, 0) }, + { NULL } }; + +DEVICE drm_dev = { + "DRM", &drm_unit, drm_reg, NULL, + 1, 8, 20, 1, 8, 18, + NULL, NULL, &drm_reset, + &drm_boot, NULL, NULL }; + +/* IOT routines */ + +int32 drm60 (int32 pulse, int32 AC) +{ +if ((pulse & 027) == 06) { /* DRLR, DRLW */ + drm_ma = AC & ADDRMASK; /* load mem addr */ + drm_unit.FUNC = pulse & 040; } /* save function */ +return AC; +} + +int32 drm61 (int32 pulse, int32 AC) +{ +int32 t; + +if (pulse == 001) return (int_req & INT_DRM)? IOT_SKP + AC: AC; /* DRSF */ +if (pulse == 002) { /* DRCF */ + int_req = int_req & ~INT_DRM; /* clear done */ + drm_err = 0; } /* clear error */ +if (pulse == 006) { /* DRSS */ + drm_da = AC & DRM_SMASK; /* load sector # */ + int_req = int_req & ~INT_DRM; /* clear done */ + drm_err = 0; /* clear error */ + t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); + if (t < 0) t = t + DRM_NUMWDT; /* wrap around? */ + sim_activate (&drm_unit, t * drm_time); } /* schedule op */ +return AC; +} + +int32 drm62 (int32 pulse, int32 AC) +{ +int32 t; + +if (pulse == 001) return (drm_err)? AC: IOT_SKP + AC; /* DRSN */ +if (pulse == 004) { /* DRCS */ + int_req = int_req & ~INT_DRM; /* clear done */ + drm_err = 0; /* clear error */ + t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); + if (t < 0) t = t + DRM_NUMWDT; /* wrap around? */ + sim_activate (&drm_unit, t * drm_time); } /* schedule op */ +return AC; +} + +/* Unit service + + This code assumes the entire drum is buffered. +*/ + +t_stat drm_svc (UNIT *uptr) +{ +int32 i; +t_addr da; + +if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ + drm_err = 1; /* set error */ + int_req = int_req | INT_DRM; /* set done */ + return IORETURN (drm_stopioe, SCPE_UNATT); } + +da = drm_da * DRM_NUMWDS; /* compute dev addr */ +for (i = 0; i < DRM_NUMWDS; i++, da++) { /* do transfer */ + if (uptr -> FUNC == DRM_READ) { + if (MEM_ADDR_OK (drm_ma)) /* read, check nxm */ + M[drm_ma] = *(((int32 *) uptr -> filebuf) + da); } + else { if ((drm_wlk >> (drm_da >> 4)) & 1) drm_err = 1; + else { *(((int32 *) uptr -> filebuf) + da) = M[drm_ma]; + if (da >= uptr -> hwmark) + uptr -> hwmark = da + 1; } } + drm_ma = (drm_ma + 1) & ADDRMASK; } /* incr mem addr */ +drm_da = (drm_da + 1) & DRM_SMASK; /* incr dev addr */ +int_req = int_req | INT_DRM; /* set done */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat drm_reset (DEVICE *dptr) +{ +drm_ma = drm_ma = drm_err = 0; +int_req = int_req & ~INT_DRM; +sim_cancel (&drm_unit); +return SCPE_OK; +} + +/* IORS routine */ + +int32 drm_iors (void) +{ +return ((int_req & INT_DRM)? IOS_DRM: 0); +} + +/* Bootstrap routine */ + +#define BOOT_START 02000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 0750000, /* CLA ; dev, mem addr */ + 0706006, /* DRLR ; load ma */ + 0706106, /* DRSS ; load da, start */ + 0706101, /* DRSF ; wait for done */ + 0602003, /* JMP .-1 + 0600000 /* JMP 0 ; enter boot */ +}; + +t_stat drm_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp18b_lp.c b/pdp18b_lp.c new file mode 100644 index 00000000..414ea98f --- /dev/null +++ b/pdp18b_lp.c @@ -0,0 +1,576 @@ +/* pdp18b_lp.c: 18b PDP's line printer simulator + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Standardized register naming + 20-Aug-98 RMS Fixed compilation problem in BeOS + 03-Jan-97 RMS Fixed bug in Type 62 state handling + + lpt Type 62 line printer for the PDP-4 + Type 647 line printer for the PDP-7 and PDP-9 + LP15 line printer for the PDP-15 +*/ + +#include "pdp18b_defs.h" + +#if defined (TYPE62) + +#define BPTR_MAX 40 /* pointer max */ +#define LPT_BSIZE 120 /* line size */ +#define BPTR_MASK 077 /* buf ptr max */ +extern int32 int_req; +int32 lpt_iot = 0, lpt_stopioe = 0, bptr = 0; +char lpt_buf[LPT_BSIZE + 1] = { 0 }; + +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* Type 62 LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { FLDATA (DONE, int_req, INT_V_LPT) }, + { FLDATA (SPC, int_req, INT_V_LPTSPC) }, + { DRDATA (BPTR, bptr, 6) }, + { ORDATA (STATE, lpt_iot, 6), REG_HRO }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { BRDATA (**BUF, lpt_buf, 8, 8, LPT_BSIZE), REG_HRO }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL }; + +/* Type 62 line printer: IOT routines */ + +int32 lpt65 (int32 pulse, int32 AC) +{ +int32 i; + +static const char lpt_trans[64] = { + ' ','1','2','3','4','5','6','7','8','9','\'','~','#','V','^','<', + '0','/','S','T','U','V','W','X','Y','Z','"',',','>','^','-','?', + 'o','J','K','L','M','N','O','P','Q','R','$','=','-',')','-','(', + '_','A','B','C','D','E','F','G','H','I','*','.','+',']','|','[' }; + +if (pulse == 001) return (int_req & INT_LPT)? IOT_SKP + AC: AC; /* LPSF */ +if (pulse == 002) int_req = int_req & ~INT_LPT; /* LPCF */ +else if (pulse == 042) { /* LPLD */ + if (bptr < BPTR_MAX) { /* limit test ptr */ + i = bptr * 3; /* cvt to chr ptr */ + lpt_buf[i++] = lpt_trans[(AC >> 12) & 077]; + lpt_buf[i++] = lpt_trans[(AC >> 6) & 077]; + lpt_buf[i++] = lpt_trans[AC & 077]; } + bptr = (bptr + 1) & BPTR_MASK; } +else if (pulse == 006) { /* LPSE */ + int_req = int_req & ~INT_LPT; /* clear flag */ + sim_activate (&lpt_unit, lpt_unit.wait); } /* activate */ +return AC; +} + +int32 lpt66 (int32 pulse, int32 AC) +{ +if (pulse == 001) return (int_req & INT_LPTSPC)? IOT_SKP + AC: AC; /* LSSF */ +if (pulse & 002) int_req = int_req & ~INT_LPTSPC; /* LSCF */ +if (pulse & 004) { /* LSPR */ + int_req = int_req & ~INT_LPTSPC; /* clear flag */ + lpt_iot = 020 | (AC & 07); /* space, no print */ + sim_activate (&lpt_unit, lpt_unit.wait); } /* activate */ +return AC; +} + +/* Unit service, printer is in one of three states + + lpt_iot = 0 write buffer to file, set state to + lpt_iot = 10 write cr, then write buffer to file + lpt_iot = 2x space command x, then set state to 0 +*/ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i; +static const char *lpt_cc[] = { + "\n", + "\n\n", + "\n\n\n", + "\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f" }; + +if (lpt_iot & 020) { /* space? */ + int_req = int_req | INT_LPTSPC; /* set flag */ + if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); + fputs (lpt_cc[lpt_iot & 07], lpt_unit.fileref); /* print cctl */ + if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } + lpt_iot = 0; } /* clear state */ +else { int_req = int_req | INT_LPT; /* print */ + if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); + if (lpt_iot & 010) fputc ('\r', lpt_unit.fileref); + fputs (lpt_buf, lpt_unit.fileref); /* print buffer */ + if (ferror (lpt_unit.fileref)) { /* test error */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } + bptr = 0; + for (i = 0; i <= LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ + lpt_iot = 010; } /* set state */ +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +int32 i; + +int_req = int_req & ~(INT_LPT + INT_LPTSPC); /* clear flag, space */ +sim_cancel (&lpt_unit); /* deactivate unit */ +bptr = 0; /* clear buffer ptr */ +for (i = 0; i <= LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ +lpt_iot = 0; /* clear state */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lpt_iors (void) +{ +return ((int_req & INT_LPT)? IOS_LPT: 0) | + ((int_req & INT_LPTSPC)? IOS_LPT1: 0); +} + +#elif defined (TYPE647) + +#define LPT_BSIZE 120 /* line size */ +extern int32 int_req; +int32 lpt_done = 0, lpt_ie = 1, lpt_err = 0; +int32 lpt_iot = 0, lpt_stopioe = 0, bptr = 0; +char lpt_buf[LPT_BSIZE] = { 0 }; + +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat lpt_detach (UNIT *uptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* Type 647 LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { FLDATA (DONE, lpt_done, 0) }, +#if defined (PDP9) + { FLDATA (ENABLE, lpt_ie, 0) }, +#endif + { FLDATA (ERR, lpt_err, 0) }, + { DRDATA (BPTR, bptr, 7) }, + { ORDATA (SCMD, lpt_iot, 6), REG_HRO }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { BRDATA (**BUF, lpt_buf, 8, 8, LPT_BSIZE), REG_HRO }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach }; + +/* Type 647 line printer: IOT routines */ + +int32 lpt65 (int32 pulse, int32 AC) +{ +int32 i; + +if (pulse == 001) return (int_req & INT_LPT)? IOT_SKP + AC: AC; /* LPSF */ +if (pulse & 002) { /* pulse 02 */ + lpt_done = 0; /* clear done */ + int_req = int_req & ~INT_LPT; } /* clear int req */ +if (pulse == 002) { /* LPCB */ + for (i = 0; i < LPT_BSIZE; i++) lpt_buf[i] = 0; + bptr = 0; /* reset buf ptr */ + lpt_done = 1; /* set done */ + if (lpt_ie) int_req = int_req | INT_LPT; } /* set int */ +#if defined (PDP9) +if (pulse == 004) { /* LPDI */ + lpt_ie = 0; /* clear int enable */ + int_req = int_req & ~INT_LPT; } /* clear int req */ +#endif +if ((pulse == 046) && (bptr < LPT_BSIZE)) { /* LPB3 */ + lpt_buf[bptr] = lpt_buf[bptr] | ((AC >> 12) & 077); + bptr = bptr + 1; } +if (((pulse == 046) || (pulse == 026)) && (bptr < LPT_BSIZE)) { + lpt_buf[bptr] = lpt_buf[bptr] | ((AC >> 6) & 077); + bptr = bptr + 1; } +if ((pulse == 046) || (pulse == 026) || (pulse == 066)) { + if (bptr < LPT_BSIZE) { + lpt_buf[bptr] = lpt_buf[bptr] | (AC & 077); + bptr = bptr + 1; } + lpt_done = 1; /* set done */ + if (lpt_ie) int_req = int_req | INT_LPT; } /* set int */ +return AC; +} + +int32 lpt66 (int32 pulse, int32 AC) +{ +if (pulse == 001) return lpt_err? IOT_SKP + AC: AC; /* LPSE */ +if (pulse & 002) { /* LPCF */ + lpt_done = 0; /* clear done, int */ + int_req = int_req & ~INT_LPT; } +if (((pulse & 060) < 060) && (pulse & 004)) { /* LPLS, LPPB, LPPS */ + lpt_iot = (pulse & 060) | (AC & 07); /* save parameters */ + sim_activate (&lpt_unit, lpt_unit.wait); } /* activate */ +#if defined (PDP9) +if (pulse == 064) { /* LPEI */ + lpt_ie = 1; /* set int enable */ + if (lpt_done) int_req = int_req | INT_LPT; } +#endif +return AC; +} + +/* Unit service. lpt_iot specifies the action to be taken + + lpt_iot = 0x print only + lpt_iot = 2x space only, x is spacing command + lpt_iot = 4x print then space, x is spacing command +*/ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i; +char pbuf[LPT_BSIZE + 1]; +static const char *lpt_cc[] = { + "\n", + "\n\n", + "\n\n\n", + "\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f" }; + +lpt_done = 1; +if (lpt_ie) int_req = int_req | INT_LPT; /* set flag */ +if ((lpt_unit.flags & UNIT_ATT) == 0) { /* not attached? */ + lpt_err = 1; /* set error */ + return IORETURN (lpt_stopioe, SCPE_UNATT); } +if ((lpt_iot & 020) == 0) { /* print? */ + for (i = 0; i < bptr; i++) /* translate buffer */ + pbuf[i] = lpt_buf[i] | ((lpt_buf[i] >= 040)? 0: 0100); + if ((lpt_iot & 060) == 0) pbuf[bptr++] = '\r'; + for (i = 0; i < LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ + fwrite (pbuf, 1, bptr, lpt_unit.fileref); /* print buffer */ + if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + bptr = 0; + return SCPE_IOERR; } + bptr = 0; } /* clear buffer ptr */ +if (lpt_iot & 060) { /* space? */ + fputs (lpt_cc[lpt_iot & 07], lpt_unit.fileref); /* write cctl */ + if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } } +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +int32 i; + +lpt_done = 0; /* clear done */ +lpt_err = (lpt_unit.flags & UNIT_ATT)? 0: 1; /* compute error */ +lpt_ie = 1; /* set enable */ +int_req = int_req & ~INT_LPT; /* clear int */ +sim_cancel (&lpt_unit); /* deactivate unit */ +bptr = 0; /* clear buffer ptr */ +lpt_iot = 0; /* clear state */ +for (i = 0; i < LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lpt_iors (void) +{ +return (lpt_done? IOS_LPT: 0) | (lpt_err? IOS_LPT1: 0); +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +lpt_err = (lpt_unit.flags & UNIT_ATT)? 0: 1; /* compute error */ +return reason; +} + +/* Detach routine */ + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_err = 1; +return detach_unit (uptr); +} + +#elif defined (LP15) + +#define LPT_BSIZE 132 /* line size */ +#define LPT_WC 034 /* word count */ +#define LPT_MA 035 /* mem address */ + +/* Status register */ + +#define STA_ERR 0400000 /* error */ +#define STA_ALM 0200000 /* alarm */ +#define STA_OVF 0100000 /* line overflow */ +#define STA_IHT 0040000 /* illegal HT */ +#define STA_BUSY 0020000 /* busy */ +#define STA_DON 0010000 /* done */ +#define STA_ILK 0004000 /* interlock */ +#define STA_EFLGS (STA_ALM | STA_OVF | STA_IHT | STA_ILK) +#define STA_CLR 0003777 /* always clear */ + +extern int32 M[]; +extern int32 int_req; +int32 lpt_sta = 0, lpt_ie = 1, lpt_stopioe = 0; +int32 mode = 0, lcnt = 0, bptr = 0; +char lpt_buf[LPT_BSIZE] = { 0 }; + +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat lpt_detach (UNIT *uptr); +int32 lpt_updsta (int32 new); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); + +/* LP15 LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (STA, lpt_sta, 18) }, + { ORDATA (MA, M[LPT_MA], 18) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { FLDATA (ENABLE, lpt_ie, 0) }, + { DRDATA (LCNT, lcnt, 9) }, + { DRDATA (BPTR, bptr, 8) }, + { FLDATA (MODE, mode, 0) }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { BRDATA (**BUF, lpt_buf, 8, 8, LPT_BSIZE), REG_HRO }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach }; + +/* LP15 line printer: IOT routines */ + +int32 lpt65 (int32 pulse, int32 AC) +{ +int32 header; + +if (pulse == 001) /* LPSF */ + return (lpt_sta & (STA_ERR | STA_DON))? IOT_SKP + AC: AC; +if ((pulse == 021) || (pulse == 041)) { /* LPP1, LPPM */ + header = M[(M[LPT_MA] + 1) & ADDRMASK]; /* get first word */ + M[LPT_MA] = (M[LPT_MA] + 2) & 0777777; + mode = header & 1; /* mode */ + if (pulse == 041) lcnt = 1; /* line count */ + else lcnt = (header >> 9) & 0377; + if (lcnt == 0) lcnt = 256; + bptr = 0; /* reset buf ptr */ + sim_activate (&lpt_unit, lpt_unit.wait); } /* activate */ +if (pulse == 061) lpt_ie = 0; /* LPDI */ +if (pulse == 042) return lpt_updsta (0); /* LPOS, LPRS */ +if (pulse == 044) lpt_ie = 1; /* LPEI */ +lpt_updsta (0); /* update status */ +return AC; +} + +int32 lpt66 (int32 pulse, int32 AC) +{ +if (pulse == 021) lpt_sta = lpt_sta & ~STA_DON; /* LPCD */ +if (pulse == 041) lpt_sta = lpt_sta & STA_ALM; /* LPCF */ +lpt_updsta (0); /* update status */ +return AC; +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i, ccnt, more, w0, w1; +char c[5]; +static const char *ctrl[040] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f", "\r", NULL, NULL, + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n", "\n\n\n", "\n", + "\n\n\n\n\n\n\n\n\n\n", NULL, NULL, NULL, + NULL, NULL, NULL, "\r", NULL, NULL, NULL, NULL }; + +if ((lpt_unit.flags & UNIT_ATT) == 0) { /* not attached? */ + lpt_updsta (STA_DON | STA_ALM); /* set done, err */ + return IORETURN (lpt_stopioe, SCPE_UNATT); } + +for (more = 1; more != 0; ) { /* loop until ctrl */ + w0 = M[(M[LPT_MA] + 1) & ADDRMASK]; /* get first word */ + w1 = M[(M[LPT_MA] + 2) & ADDRMASK]; /* get second word */ + M[LPT_MA] = (M[LPT_MA] + 2) & 0777777; /* advance mem addr */ + if (mode) { /* unpacked? */ + c[0] = w0 & 0177; + c[1] = w1 & 0177; + ccnt = 2; } + else { c[0] = (w0 >> 11) & 0177; /* packed */ + c[1] = (w0 >> 4) & 0177; + c[2] = (((w0 << 3) | (w1 >> 15))) & 0177; + c[3] = (w1 >> 8) & 0177; + c[4] = (w1 >> 1) & 0177; + ccnt = 5; } + for (i = 0; i < ccnt; i++) { /* loop through */ + if ((c[i] <= 037) && ctrl[c[i]]) { /* control char? */ + fwrite (lpt_buf, 1, bptr, lpt_unit.fileref); + fputs (ctrl[c[i]], lpt_unit.fileref); + if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + bptr = 0; + lpt_updsta (STA_DON | STA_ALM); + return SCPE_IOERR; } + lpt_unit.pos = ftell (lpt_unit.fileref); + bptr = more = 0; } + else { if (bptr < LPT_BSIZE) lpt_buf[bptr++] = c[i]; + else lpt_sta = lpt_sta | STA_OVF; } } } + +lcnt = lcnt - 1; /* decr line count */ +if (lcnt) sim_activate (&lpt_unit, lpt_unit.wait); /* more to do? */ +else lpt_updsta (STA_DON); /* no, set done */ +return SCPE_OK; +} + +/* Update status */ + +int32 lpt_updsta (int32 new) +{ +lpt_sta = (lpt_sta | new) & ~(STA_CLR | STA_ERR | STA_BUSY); +if (lpt_sta & STA_EFLGS) lpt_sta = lpt_sta | STA_ERR; /* update errors */ +if (sim_is_active (&lpt_unit)) lpt_sta = lpt_sta | STA_BUSY; +if (lpt_ie && (lpt_sta & STA_DON)) int_req = int_req | INT_LPT; +else int_req = int_req & ~INT_LPT; /* update int */ +return lpt_sta; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +mode = lcnt = bptr = 0; /* clear controls */ +sim_cancel (&lpt_unit); /* deactivate unit */ +if (lpt_unit.flags & UNIT_ATT) lpt_sta = 0; +else lpt_sta = STA_ALM; +lpt_ie = 1; /* enable interrupts */ +lpt_updsta (0); /* update status */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lpt_iors (void) +{ +return ((lpt_sta & STA_DON)? IOS_LPT: 0); +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if (lpt_unit.flags & UNIT_ATT) lpt_sta = lpt_sta & ~STA_ALM; +lpt_updsta (0); +return reason; +} + +/* Detach routine */ + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_updsta (STA_ALM); +return detach_unit (uptr); +} +#endif diff --git a/pdp18b_mt.c b/pdp18b_mt.c new file mode 100644 index 00000000..50a02ebf --- /dev/null +++ b/pdp18b_mt.c @@ -0,0 +1,475 @@ +/* 18b PDP magnetic tape simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt TC59 magnetic tape for PDP-9 + TC59D magnetic tape for PDP-15 + + 04-Oct-98 RMS V2.4 magtape format + 22-Jan-97 RMS V2.3 magtape format + 29-Jun-96 RMS Added unit disable support + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32 byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "pdp18b_defs.h" + +#define MT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK 1 << UNIT_V_WLK +#define UNIT_W_UF 2 /* saved flag width */ +#define USTAT u3 /* unit status */ +#define UNUM u4 /* unit number */ +#define DBSIZE (1 << 12) /* max data record */ +#define DBMASK (DBSIZE - 1) +#define MT_WC 032 /* in core reg */ +#define MT_MA 033 + +/* Command/unit - mt_cu */ + +#define CU_V_UNIT 15 /* unit */ +#define CU_M_UNIT 07 +#define CU_PARITY 0040000 /* parity select */ +#define CU_DUMP 0020000 /* dump mode */ +#define CU_ERASE 0010000 /* ext rec gap */ +#define CU_V_CMD 9 /* command */ +#define CU_M_CMD 07 +#define FN_NOP 00 +#define FN_REWIND 01 +#define FN_READ 02 +#define FN_CMPARE 03 +#define FN_WRITE 04 +#define FN_WREOF 05 +#define FN_SPACEF 06 +#define FN_SPACER 07 +#define CU_IE 0000400 /* interrupt enable */ +#define CU_V_TYPE 6 /* drive type */ +#define CU_M_TYPE 03 +#define TY_9TK 3 +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) +#define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD) +#define GET_TYPE(x) (((x) >> CU_V_TYPE) & CU_M_TYPE) +#define PACKED(x) (((x) & CU_DUMP) || (GET_TYPE (x) != TY_9TK)) + +/* Status - stored in mt_sta or (*) uptr -> USTAT */ + +#define STA_ERR 0400000 /* error */ +#define STA_REW 0200000 /* *rewinding */ +#define STA_BOT 0100000 /* *start of tape */ +#define STA_ILL 0040000 /* illegal cmd */ +#define STA_PAR 0020000 /* parity error */ +#define STA_EOF 0010000 /* *end of file */ +#define STA_EOT 0004000 /* *end of tape */ +#define STA_CPE 0002000 /* compare error */ +#define STA_RLE 0001000 /* rec lnt error */ +#define STA_DLT 0000400 /* data late */ +#define STA_BAD 0000200 /* bad tape */ +#define STA_DON 0000100 /* done */ + +#define STA_CLR 0000077 /* always clear */ +#define STA_DYN (STA_REW | STA_BOT | STA_EOF | STA_EOT) + /* kept in USTAT */ +#define STA_EFLGS (STA_BOT | STA_ILL | STA_PAR | STA_EOF | \ + STA_EOT | STA_CPE | STA_RLE | STA_DLT | STA_BAD) + /* error flags */ + +extern int32 M[]; +extern int32 int_req; +extern UNIT cpu_unit; +int32 mt_cu = 0; /* command/unit */ +int32 mt_sta = 0; /* status register */ +int32 mt_time = 10; /* record latency */ +int32 mt_stopioe = 1; /* stop on error */ +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +int32 mt_updcsta (UNIT *uptr, int32 val); +UNIT *mt_busy (void); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } }; + +REG mt_reg[] = { + { ORDATA (STA, mt_sta, 18) }, + { ORDATA (CMD, mt_cu, 18) }, + { ORDATA (MA, M[MT_MA], 18) }, + { ORDATA (WC, M[MT_WC], 18) }, + { FLDATA (INT, int_req, INT_V_MTA) }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { DRDATA (TIME, mt_time, 24), PV_LEFT }, + { ORDATA (UST0, mt_unit[0].USTAT, 18) }, + { ORDATA (UST1, mt_unit[1].USTAT, 18) }, + { ORDATA (UST2, mt_unit[2].USTAT, 18) }, + { ORDATA (UST3, mt_unit[3].USTAT, 18) }, + { ORDATA (UST4, mt_unit[4].USTAT, 18) }, + { ORDATA (UST5, mt_unit[5].USTAT, 18) }, + { ORDATA (UST6, mt_unit[6].USTAT, 18) }, + { ORDATA (UST7, mt_unit[7].USTAT, 18) }, + { DRDATA (POS0, mt_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, mt_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, mt_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, mt_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, mt_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, mt_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, mt_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, mt_unit[7].pos, 31), PV_LEFT + REG_RO }, + { GRDATA (FLG0, mt_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, mt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, mt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, mt_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, mt_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, mt_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, mt_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, mt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { NULL } }; + +MTAB mt_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &mt_detach }; + +/* IOT routine */ + +int32 mt (int32 pulse, int32 AC) +{ +int32 f; +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +mt_updcsta (uptr, 0); /* update status */ +if (pulse == 001) /* MTTR */ + return (!sim_is_active (uptr))? IOT_SKP + AC: AC; +if (pulse == 021) /* MTCR */ + return (!mt_busy ())? IOT_SKP + AC: AC; +if (pulse == 041) /* MTSF */ + return (mt_sta & (STA_ERR | STA_DON))? IOT_SKP + AC: AC; +if (pulse == 002) return (mt_cu & 0777700); /* MTRC */ +if (pulse == 042) return mt_sta; /* MTRS */ +if ((pulse & 062) == 022) { /* MTAF, MTLC */ + if (!mt_busy ()) mt_cu = mt_sta = 0; /* if not busy, clr */ + mt_sta = mt_sta & ~(STA_ERR | STA_DON); } /* clear flags */ +if ((pulse & 064) == 024) /* MTCM, MTLC */ + mt_cu = (mt_cu & 0770700) | (AC & 0777700); /* load status */ +if (pulse == 004) { /* MTGO */ + f = GET_CMD (mt_cu); /* get function */ + if (mt_busy () || (sim_is_active (uptr)) || + (((f == FN_SPACER) || (f == FN_REWIND)) & (uptr -> pos == 0)) || + (((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WLK)) + || ((uptr -> flags & UNIT_ATT) == 0) || (f == FN_NOP)) + mt_sta = mt_sta | STA_ILL; /* illegal op flag */ + else { if (f == FN_REWIND) uptr -> USTAT = STA_REW; /* rewind? */ + else mt_sta = uptr -> USTAT = 0; /* no, clear status */ + sim_activate (uptr, mt_time); } } /* start io */ +mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ +return AC; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat mt_svc (UNIT *uptr) +{ +int32 c, c1, c2, c3, f, i, p, u, err; +int32 wc, xma; +t_stat rval; +t_mtrlnt tbc, cbc; +unsigned int8 dbuf[(3 * DBSIZE)]; +static t_mtrlnt bceof = { 0 }; + +u = uptr -> UNUM; /* get unit number */ +if (uptr -> USTAT & STA_REW) { /* rewind? */ + uptr -> pos = 0; /* update position */ + if (uptr -> flags & UNIT_ATT) uptr -> USTAT = STA_BOT; + else uptr -> USTAT = 0; + if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr, STA_DON); + return SCPE_OK; } + +f = GET_CMD (mt_cu); /* get command */ +if ((uptr -> flags & UNIT_ATT) == 0) { /* if not attached */ + mt_updcsta (uptr, STA_ILL); /* illegal operation */ + return IORETURN (mt_stopioe, SCPE_UNATT); } + +if ((f == FN_WRITE) || (f == FN_WREOF)) { /* write? */ + if (uptr -> flags & UNIT_WLK) { /* write locked? */ + mt_updcsta (uptr, STA_ILL); /* illegal operation */ + return SCPE_OK; } + mt_cu = mt_cu & ~CU_ERASE; } /* clear erase flag */ + +err = 0; +rval = SCPE_OK; +switch (f) { /* case on function */ + +/* Unit service, continued */ + +case FN_READ: /* read */ +case FN_CMPARE: /* read/compare */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + (feof (uptr -> fileref))) { + uptr -> USTAT = STA_EOT; + mt_updcsta (uptr, STA_RLE); + break; } + if (tbc == 0) { /* tape mark? */ + uptr -> USTAT = STA_EOF; + mt_updcsta (uptr, STA_RLE); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + tbc = MTRL (tbc); /* ignore error flag */ + wc = DBSIZE - (M[MT_WC] & DBMASK); /* get word count */ + cbc = PACKED (mt_cu)? wc * 3: wc * 2; /* expected bc */ + if (tbc != cbc) mt_sta = mt_sta | STA_RLE; /* wrong size? */ + if (tbc < cbc) { /* record small? */ + cbc = tbc; /* use smaller */ + wc = PACKED (mt_cu)? ((tbc + 2) / 3): ((tbc + 1) / 2); } + i = fxread (dbuf, sizeof (int8), cbc, uptr -> fileref); + for ( ; i < cbc; i++) dbuf[i] = 0; /* fill with 0's */ + err = ferror (uptr -> fileref); + for (i = p = 0; i < wc; i++) { /* copy buffer */ + M[MT_MA] = (M[MT_MA] + 1) & 0777777; + xma = M[MT_MA] & ADDRMASK; + if (PACKED (mt_cu)) { /* packed? */ + c1 = dbuf[p++] & 077; + c2 = dbuf[p++] & 077; + c3 = dbuf[p++] & 077; + c = (c1 << 12) | (c2 << 6) | c3; } + else { c1 = dbuf[p++]; + c2 = dbuf[p++]; + c = (c1 << 8) | c2; } + if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c; + else if ((f == FN_CMPARE) && (c != (M[xma] & + (PACKED (mt_cu)? 0777777: 0177777)))) { + mt_updcsta (uptr, STA_CPE); + break; } + M[MT_WC] = (M[MT_WC] + 1) & 0777777; } + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + break; +case FN_WRITE: /* write */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + wc = DBSIZE - (M[MT_WC] & DBMASK); /* get word count */ + tbc = PACKED (mt_cu)? wc * 3: wc * 2; + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + for (i = p = 0; i < wc; i++) { /* copy buf to tape */ + M[MT_MA] = (M[MT_MA] + 1) & 0777777; + xma = M[MT_MA] & ADDRMASK; + if (PACKED (mt_cu)) { /* packed? */ + dbuf[p++] = (M[xma] >> 12) & 077; + dbuf[p++] = (M[xma] >> 6) & 077; + dbuf[p++] = M[xma] & 077; } + else { dbuf[p++] = (M[xma] >> 8) & 0377; + dbuf[p++] = M[xma] & 0377; } + M[MT_WC] = (M[MT_WC] + 1) & 0777777; } + fxwrite (dbuf, sizeof (char), (tbc + 1) & ~1, uptr -> fileref); + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + break; + +/* Unit service, continued */ + +case FN_WREOF: + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + uptr -> USTAT = STA_EOF; + break; +case FN_SPACEF: /* space forward */ + wc = 01000000 - M[MT_WC]; /* get word count */ + do { fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); /* read bc */ + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + feof (uptr -> fileref)) { + uptr -> USTAT = STA_EOT; + break; } + if (tbc == 0) { /* zero bc? */ + uptr -> USTAT = STA_EOF; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); } + while ((M[MT_WC] = (M[MT_WC] + 1) & 0777777) != 0); + break; +case FN_SPACER: /* space reverse */ + wc = 01000000 - M[MT_WC]; /* get word count */ + if (uptr -> pos == 0) { /* at BOT? */ + uptr -> USTAT = STA_BOT; + break; } + do { fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), + SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + tbc = MTRL (tbc); /* ignore error flag */ + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + feof (uptr -> fileref)) { + uptr -> USTAT = STA_BOT; + uptr -> pos = 0; + break; } + if (tbc == 0) { /* end of file? */ + uptr -> USTAT = STA_EOF; + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos - ((tbc + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); + if (uptr -> pos == 0) { /* at BOT? */ + uptr -> USTAT = STA_BOT; + break; } } + while ((M[MT_WC] = (M[MT_WC] + 1) & 0777777) != 0); + break; } /* end case */ + +/* Unit service, continued */ + +if (err != 0) { /* I/O error */ + mt_updcsta (uptr, STA_PAR); /* flag error */ + perror ("MT I/O error"); + rval = SCPE_IOERR; + clearerr (uptr -> fileref); } +mt_updcsta (uptr, STA_DON); /* set done */ +return IORETURN (mt_stopioe, rval); +} + +/* Update controller status */ + +int32 mt_updcsta (UNIT *uptr, int32 new) +{ +mt_sta = (mt_sta & ~(STA_DYN | STA_ERR | STA_CLR)) | + (uptr -> USTAT & STA_DYN) | new; +if (mt_sta & STA_EFLGS) mt_sta = mt_sta | STA_ERR; /* error flag */ +if ((mt_sta & (STA_ERR | STA_DON)) && ((mt_cu & CU_IE) == 0)) + int_req = int_req | INT_MTA; +else int_req = int_req & ~INT_MTA; /* int request */ +return mt_sta; +} + +/* Test if controller busy */ + +UNIT *mt_busy (void) +{ +int32 u; +UNIT *uptr; + +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + if (sim_is_active (uptr) && ((uptr -> USTAT & STA_REW) == 0)) + return uptr; } +return NULL; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +mt_cu = mt_sta = 0; +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + uptr -> UNUM = u; /* init drive number */ + sim_cancel (uptr); /* cancel activity */ + if (uptr -> flags & UNIT_ATT) uptr -> USTAT = STA_BOT; + else uptr -> USTAT = 0; } +mt_updcsta (&mt_unit[0], 0); /* update status */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 mt_iors (void) +{ +return (mt_sta & (STA_ERR | STA_DON))? IOS_MTA: 0; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +uptr -> USTAT = STA_BOT; +mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +if (!sim_is_active (uptr)) uptr -> USTAT = 0; +mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ +return detach_unit (uptr); +} diff --git a/pdp18b_rf.c b/pdp18b_rf.c new file mode 100644 index 00000000..5362aaca --- /dev/null +++ b/pdp18b_rf.c @@ -0,0 +1,267 @@ +/* pdp18b_rf.c: fixed head disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rf RF09/RF09 for PDP-9 + RF15/RS09 for PDP-15 + + 14-Apr-99 RMS Changed t_addr to unsigned + + The RFxx is a head-per-track disk. It uses the multicycle data break + facility. To minimize overhead, the entire RFxx is buffered in memory. + + Two timing parameters are provided: + + rf_time Interword timing. If 0, treated as 1. + rf_burst Burst mode. If 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst. +*/ + +#include "pdp18b_defs.h" +#include + +/* Constants */ + +#define RF_NUMWD 2048 /* words/track */ +#define RF_NUMTR 128 /* tracks/disk */ +#define RF_NUMDK 8 /* disks/controller */ +#define RF_SIZE (RF_NUMDK * RF_NUMTR * RF_NUMWD) /* words/drive */ +#define RF_WMASK (RF_NUMWD - 1) /* word mask */ +#define RF_WC 036 /* word count */ +#define RF_MA 037 /* mem address */ + +/* Function/status register */ + +#define RFS_ERR 0400000 /* error */ +#define RFS_HDW 0200000 /* hardware error */ +#define RFS_APE 0100000 /* addr parity error */ +#define RFS_MXF 0040000 /* missed transfer */ +#define RFS_WCE 0020000 /* write check error */ +#define RFS_DPE 0010000 /* data parity error */ +#define RFS_WLO 0004000 /* write lock error */ +#define RFS_NED 0002000 /* non-existent disk */ +#define RFS_DCH 0001000 /* data chan timing */ +#define RFS_PGE 0000400 /* programming error */ +#define RFS_DON 0000200 /* transfer complete */ +#define RFS_V_FNC 1 /* function */ +#define RFS_M_FNC 03 +#define RFS_FNC (RFS_M_FNC << RFS_V_FNC) +#define FN_NOP 0 +#define FN_READ 1 +#define FN_WRITE 2 +#define FN_WCHK 3 +#define RFS_IE 0000001 /* interrupt enable */ + +#define RFS_CLR 0000170 /* always clear */ +#define RFS_EFLGS (RFS_HDW | RFS_APE | RFS_MXF | RFS_WCE | \ + RFS_DPE | RFS_WLO | RFS_NED ) /* error flags */ +#define MIN_TIME(x) ((x > 0)? (x): 1) +#define GET_FNC(x) (((x) >> RFS_V_FNC) & RFS_M_FNC) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (MIN_TIME(x))), \ + ((double) RF_NUMWD))) +#define RF_BUSY (sim_is_active (&rf_unit)) + +extern int32 M[]; +extern int32 int_req; +extern UNIT cpu_unit; +int32 rf_sta = 0; /* status register */ +int32 rf_da = 0; /* disk address */ +int32 rf_dbuf = 0; /* data buffer */ +int32 rf_wlk[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* write lock */ +int32 rf_time = 10; /* inter-word time */ +int32 rf_burst = 1; /* burst mode flag */ +int32 rf_stopioe = 1; /* stop on error */ +t_stat rf_svc (UNIT *uptr); +t_stat rf_reset (DEVICE *dptr); +int32 rf_updsta (int32 new); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); + +/* RF data structures + + rf_dev RF device descriptor + rf_unit RF unit descriptor + rf_reg RF register list +*/ + +UNIT rf_unit = + { UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + RF_SIZE) }; + +REG rf_reg[] = { + { ORDATA (STA, rf_sta, 18) }, + { ORDATA (DA, rf_da, 21) }, + { ORDATA (MA, M[RF_MA], 18) }, + { ORDATA (WC, M[RF_WC], 18) }, + { ORDATA (BUF, rf_dbuf, 18) }, + { FLDATA (INT, int_req, INT_V_RF) }, + { ORDATA (WLK0, rf_wlk[0], 16) }, + { ORDATA (WLK1, rf_wlk[1], 16) }, + { ORDATA (WLK2, rf_wlk[2], 16) }, + { ORDATA (WLK3, rf_wlk[3], 16) }, + { ORDATA (WLK4, rf_wlk[4], 16) }, + { ORDATA (WLK5, rf_wlk[5], 16) }, + { ORDATA (WLK6, rf_wlk[6], 16) }, + { ORDATA (WLK7, rf_wlk[7], 16) }, + { DRDATA (TIME, rf_time, 24), PV_LEFT }, + { FLDATA (BURST, rf_burst, 0) }, + { FLDATA (STOP_IOE, rf_stopioe, 0) }, + { NULL } }; + +DEVICE rf_dev = { + "RF", &rf_unit, rf_reg, NULL, + 1, 8, 21, 1, 8, 18, + NULL, NULL, &rf_reset, + NULL, NULL, NULL }; + +/* IOT routines */ + +int32 rf70 (int32 pulse, int32 AC) +{ +int32 t; + +if (pulse == 001) /* DSSF */ + return (rf_sta & (RFS_ERR | RFS_DON))? IOT_SKP + AC: AC; +if (pulse == 021) rf_reset (&rf_dev); /* DSCC */ +if ((pulse & 061) == 041) { /* DSCF */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_sta = rf_sta & ~(RFS_FNC | RFS_IE); } /* clear func */ +if (pulse == 002) { /* DRBR */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + return AC | rf_dbuf; } +if (pulse == 022) { /* DRAL */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + return rf_da & 0777777; } +if (pulse == 062) { /* DRAH */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + return (rf_da >> 18) | ((rf_sta & RFS_NED)? 010: 0); } +if ((pulse & 062) == 042) { /* DSFX */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_sta = rf_sta ^ (AC & (RFS_FNC | RFS_IE)); } /* xor func */ +if (pulse == 004) { /* DLBR */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_dbuf = AC; } +if (pulse == 024) { /* DLAL */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_da = (rf_da & ~0777777) | AC; } +if (pulse == 064) { /* DLAH */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_da = (rf_da & 0777777) | ((AC & 07) << 18); } +if ((pulse & 064) == 044) { /* DSCN */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else if (GET_FNC (rf_sta) != FN_NOP) { + t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new */ + if (t < 0) t = t + RF_NUMWD; /* wrap around? */ + sim_activate (&rf_unit, t * rf_time); } } /* schedule op */ +rf_updsta (0); /* update status */ +return AC; +} + +int32 rf72 (int32 pulse, int32 AC) +{ +if (pulse == 002) return AC | GET_POS (rf_time) | /* DLOK */ + (sim_is_active (&rf_unit)? 0400000: 0); +if (pulse == 042) { /* DSCD */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_sta = 0; + rf_updsta (0); } +if (pulse == 0062) { /* DSRS */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + return rf_updsta (0); } +return AC; +} + +/* Unit service + + This code assumes the entire disk is buffered. +*/ + +t_stat rf_svc (UNIT *uptr) +{ +int32 f, pa, d, t; + +if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ + rf_updsta (RFS_NED | RFS_DON); /* set nxd, done */ + return IORETURN (rf_stopioe, SCPE_UNATT); } + +f = GET_FNC (rf_sta); /* get function */ +do { pa = M[RF_MA] = (M[RF_MA] + 1) & ADDRMASK; /* incr mem addr */ + if ((f == FN_READ) && MEM_ADDR_OK (pa)) /* read? */ + M[pa] = *(((int32 *) uptr -> filebuf) + rf_da); + if ((f == FN_WCHK) && /* write check? */ + (M[pa] != *(((int32 *) uptr -> filebuf) + rf_da))) { + rf_updsta (RFS_WCE); /* flag error */ + break; } + if (f == FN_WRITE) { /* write? */ + d = (rf_da >> 18) & 07; /* disk */ + t = (rf_da >> 14) & 017; /* track groups */ + if ((rf_wlk[d] >> t) & 1) { /* write locked? */ + rf_updsta (RFS_WLO); + break; } + else { *(((int32 *) uptr -> filebuf) + rf_da) = M[pa]; + if (((t_addr) rf_da) >= uptr -> hwmark) + uptr -> hwmark = rf_da + 1; } } + rf_da = rf_da + 1; /* incr disk addr */ + if (rf_da > RF_SIZE) { /* disk overflow? */ + rf_da = 0; + rf_updsta (RFS_NED); /* nx disk error */ + break; } + M[RF_WC] = (M[RF_WC] + 1) & 0777777; } /* incr word count */ +while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ + +if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */ + sim_activate (&rf_unit, MIN_TIME (rf_time)); /* sched next */ +else rf_updsta (RFS_DON); +return SCPE_OK; +} + +/* Update status */ + +int32 rf_updsta (int32 new) +{ +rf_sta = (rf_sta | new) & ~(RFS_ERR | RFS_CLR); +if (rf_sta & RFS_EFLGS) rf_sta = rf_sta | RFS_ERR; +if ((rf_sta & (RFS_ERR | RFS_DON)) && (rf_sta & RFS_IE)) + int_req = int_req | INT_RF; +else int_req = int_req & ~INT_RF; +return rf_sta; +} + +/* Reset routine */ + +t_stat rf_reset (DEVICE *dptr) +{ +rf_sta = rf_da = rf_dbuf = 0; +rf_updsta (0); +sim_cancel (&rf_unit); +return SCPE_OK; +} + +/* IORS routine */ + +int32 rf_iors (void) +{ +return ((rf_sta & (RFS_ERR | RFS_DON))? IOS_RF: 0); +} diff --git a/pdp18b_rp.c b/pdp18b_rp.c new file mode 100644 index 00000000..56a67656 --- /dev/null +++ b/pdp18b_rp.c @@ -0,0 +1,444 @@ +/* RP15/RP02 disk pack simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rp RP15/RP02 disk pack + + 14-Apr-99 RMS Changed t_addr to unsigned + 29-Jun-96 RMS Added unit disable support +*/ + +#include "pdp18b_defs.h" + +/* Constants */ + +#define RP_NUMWD 256 /* words/sector */ +#define RP_NUMSC 10 /* sectors/surface */ +#define RP_NUMSF 20 /* surfaces/cylinder */ +#define RP_NUMCY 203 /* cylinders/drive */ +#define RP_NUMDR 8 /* drives/controller */ +#define RP_SIZE (RP_NUMCY * RP_NUMSF * RP_NUMSC * RP_NUMWD) /* words/drive */ + +/* Unit specific flags */ + +#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_W_UF 2 /* user flags width */ +#define UNIT_HWLK (1u << UNIT_V_HWLK) + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* Status register A */ + +#define STA_V_UNIT 15 /* unit select */ +#define STA_M_UNIT 07 +#define STA_V_FUNC 12 /* function */ +#define STA_M_FUNC 07 +#define FN_IDLE 0 +#define FN_READ 1 +#define FN_WRITE 2 +#define FN_RECAL 3 +#define FN_SEEK 4 +#define FN_RDALL 5 +#define FN_WRALL 6 +#define FN_WRCHK 7 +#define FN_2ND 010 /* second state flag */ +#define STA_IED 0004000 /* int enable done */ +#define STA_IEA 0002000 /* int enable attn */ +#define STA_GO 0001000 /* go */ +#define STA_WPE 0000400 /* write lock error */ +#define STA_NXC 0000200 /* nx cyl error */ +#define STA_NXF 0000100 /* nx surface error */ +#define STA_NXS 0000040 /* nx sector error */ +#define STA_HNF 0000020 /* hdr not found */ +#define STA_SUWP 0000010 /* sel unit wrt lock */ +#define STA_SUSI 0000004 /* sel unit seek inc */ +#define STA_DON 0000002 /* done */ +#define STA_ERR 0000001 /* error */ + +#define STA_RW 0777000 /* read/write */ +#define STA_EFLGS (STA_WPE | STA_NXC | STA_NXF | STA_NXS | \ + STA_HNF | STA_SUSI) /* error flags */ +#define STA_DYN (STA_SUWP | STA_SUSI) /* per unit status */ +#define GET_UNIT(x) (((x) >> STA_V_UNIT) & STA_M_UNIT) +#define GET_FUNC(x) (((x) >> STA_V_FUNC) & STA_M_FUNC) + +/* Status register B */ + +#define STB_V_ATT0 17 /* unit 0 attention */ +#define STB_ATTN 0776000 /* attention flags */ +#define STB_SUFU 0001000 /* sel unit unsafe */ +#define STB_PGE 0000400 /* programming error */ +#define STB_EOP 0000200 /* end of pack */ +#define STB_TME 0000100 /* timing error */ +#define STB_FME 0000040 /* format error */ +#define STB_WCE 0000020 /* write check error */ +#define STB_WPE 0000010 /* word parity error */ +#define STB_LON 0000004 /* long parity error */ +#define STB_SUSU 0000002 /* sel unit seeking */ +#define STB_SUNR 0000001 /* sel unit not rdy */ + +#define STB_EFLGS (STB_SUFU | STB_PGE | STB_EOP | STB_TME | STB_FME | \ + STB_WCE | STB_WPE | STB_LON ) /* error flags */ +#define STB_DYN (STB_SUFU | STB_SUSU | STB_SUNR) /* per unit */ + +/* Disk address */ + +#define DA_V_SECT 0 /* sector */ +#define DA_M_SECT 017 +#define DA_V_SURF 5 +#define DA_M_SURF 037 +#define DA_V_CYL 10 /* cylinder */ +#define DA_M_CYL 0377 +#define GET_SECT(x) (((x) >> DA_V_SECT) & DA_M_SECT) +#define GET_SURF(x) (((x) >> DA_V_SURF) & DA_M_SURF) +#define GET_CYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) +#define GET_DA(x) ((((GET_CYL (x) * RP_NUMSF) + GET_SURF (x)) * \ + RP_NUMSC) + GET_SECT (x)) + +#define RP_MIN 2 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern int32 M[]; +extern int32 int_req, nexm; +extern UNIT cpu_unit; +int32 rp_sta = 0; /* status A */ +int32 rp_stb = 0; /* status B */ +int32 rp_ma = 0; /* memory address */ +int32 rp_da = 0; /* disk address */ +int32 rp_wc = 0; /* word count */ +int32 rp_busy = 0; /* busy */ +int32 rp_stopioe = 1; /* stop on error */ +int32 rp_swait = 10; /* seek time */ +int32 rp_rwait = 10; /* rotate time */ +t_stat rp_svc (UNIT *uptr); +void rp_updsta (int32 newa, int32 newb); +t_stat rp_reset (DEVICE *dptr); +t_stat rp_attach (UNIT *uptr, char *cptr); +t_stat rp_detach (UNIT *uptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* RP15 data structures + + rp_dev RP device descriptor + rp_unit RP unit list + rp_reg RP register list + rp_mod RP modifier list +*/ + +UNIT rp_unit[] = { + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) } }; + +REG rp_reg[] = { + { ORDATA (STA, rp_sta, 18) }, + { ORDATA (STB, rp_stb, 18) }, + { ORDATA (DA, rp_da, 18) }, + { ORDATA (MA, rp_ma, 18) }, + { ORDATA (WC, rp_wc, 18) }, + { FLDATA (INT, int_req, INT_V_RP) }, + { FLDATA (BUSY, rp_busy, 0) }, + { FLDATA (STOP_IOE, rp_stopioe, 0) }, + { DRDATA (STIME, rp_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rp_rwait, 24), PV_LEFT }, + { GRDATA (FLG0, rp_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, rp_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, rp_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, rp_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, rp_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, rp_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, rp_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, rp_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { NULL } }; + +MTAB rp_mod[] = { + { UNIT_HWLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE rp_dev = { + "RP", rp_unit, rp_reg, rp_mod, + RP_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &rp_reset, + NULL, &rp_attach, &rp_detach }; + +/* IOT routines */ + +int32 rp63 (int32 pulse, int32 AC) +{ +rp_updsta (0, 0); +if (pulse == 001) /* DPSF */ + return ((rp_sta & (STA_DON | STA_ERR)) || (rp_stb & STB_ATTN))? + IOT_SKP + AC: AC; +if (pulse == 021) /* DPSA */ + return (rp_stb & STB_ATTN)? IOT_SKP + AC: AC; +if (pulse == 041) /* DPSJ */ + return (rp_sta & STA_DON)? IOT_SKP + AC: AC; +if (pulse == 061) /* DPSE */ + return (rp_sta & STA_ERR)? IOT_SKP + AC: AC; +if (pulse == 002) return rp_sta; /* DPOSA */ +if (pulse == 022) return rp_stb; /* DPOSB */ +if (((pulse & 007) == 004) && rp_busy) { /* busy? */ + rp_updsta (0, STB_PGE); + return AC; } +if (pulse == 004) { /* DPLA */ + rp_da = AC; + if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0); + if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0); + if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0); } +if (pulse == 024) { /* DPCS */ + rp_sta = rp_sta & ~(STA_HNF | STA_DON); + rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE | + STB_TME | STB_PGE | STB_EOP); + rp_updsta (0, 0); } +if (pulse == 044) rp_ma = AC; /* DPCA */ +if (pulse == 064) rp_wc = AC; /* DPWC */ +return AC; +} + +/* IOT 64 */ + +int32 rp64 (int32 pulse, int32 AC) +{ +int32 u, f, c; +UNIT *uptr; + +if (pulse == 021) return IOT_SKP + AC; /* DPSN */ +if (pulse == 002) return rp_unit[GET_UNIT (rp_sta)].CYL; /* DPOU */ +if (pulse == 022) return rp_da; /* DPOA */ +if (pulse == 042) return rp_ma; /* DPOC */ +if (pulse == 062) return rp_wc; /* DPOW */ +if ((pulse & 007) != 004) return AC; +if (rp_busy) { /* busy? */ + rp_updsta (0, STB_PGE); + return AC; } +if (pulse == 004) rp_sta = rp_sta & ~STA_RW; /* DPCF */ +if (pulse == 024) rp_sta = rp_sta & (AC | ~STA_RW); /* DPLZ */ +if (pulse == 044) rp_sta = rp_sta | (AC & STA_RW); /* DPLO */ +if (pulse == 064) rp_sta = (rp_sta & ~STA_RW) | (AC & STA_RW); /* DPLF */ +if (rp_sta & STA_GO) { + u = GET_UNIT (rp_sta); /* get unit num */ + uptr = rp_dev.units + u; /* select unit */ + if (sim_is_active (uptr)) return AC; /* can't if busy */ + f = uptr -> FUNC = GET_FUNC (rp_sta); /* get function */ + rp_busy = 1; /* set ctrl busy */ + rp_sta = rp_sta & ~(STA_HNF | STA_DON); /* clear flags */ + rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE | + STB_TME | STB_PGE | STB_EOP | (1 << (STB_V_ATT0 - u))); + if (((uptr -> flags & UNIT_ATT) == 0) || (f == FN_IDLE) || + (f == FN_SEEK) || (f == FN_RECAL)) + sim_activate (uptr, RP_MIN); /* short delay */ + else { c = GET_CYL (rp_da); + c = abs (c - uptr -> CYL) * rp_swait; /* seek time */ + sim_activate (uptr, MAX (RP_MIN, c + rp_rwait)); } } +rp_updsta (0, 0); +return AC; +} + +/* Unit service + + If function = idle, clear busy + If seek or recal initial state, clear attention line, compute seek time, + put on cylinder, set second state + If unit not attached, give error + If seek or recal second state, set attention line, compute errors + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +static int32 fill[RP_NUMWD] = { 0 }; +t_stat rp_svc (UNIT *uptr) +{ +int32 f, u, comp, cyl, sect, surf; +int32 err, pa, da, wc, awc, i; + +u = uptr - rp_dev.units; /* get drv number */ +f = uptr -> FUNC; /* get function */ +if (f == FN_IDLE) { /* idle? */ + rp_busy = 0; /* clear busy */ + return SCPE_OK; } + +if ((f == FN_SEEK) || (f == FN_RECAL)) { /* seek or recal? */ + rp_busy = 0; /* not busy */ + cyl = (f == FN_SEEK)? GET_CYL (rp_da): 0; /* get cylinder */ + sim_activate (uptr, MAX (RP_MIN, abs (cyl - uptr -> CYL) * rp_swait)); + uptr -> CYL = cyl; /* on cylinder */ + uptr -> FUNC = FN_SEEK | FN_2ND; /* set second state */ + rp_updsta (0, 0); /* update status */ + return SCPE_OK; } + +if (f == (FN_SEEK | FN_2ND)) { /* seek done? */ + rp_updsta (0, rp_stb | (1 << (STB_V_ATT0 - u))); /* set attention */ + return SCPE_OK; } + +if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */ + rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */ + return IORETURN (rp_stopioe, SCPE_UNATT); } + +if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0); +if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0); +if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0); +if (rp_sta & (STA_NXS | STA_NXF | STA_NXC)) { /* or bad disk addr? */ + rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */ + return SCPE_OK; } + +pa = rp_ma & ADDRMASK; /* get mem addr */ +da = GET_DA (rp_da) * RP_NUMWD; /* get disk addr */ +wc = 01000000 - rp_wc; /* get true wc */ +if (((t_addr) (pa + wc)) > MEMSIZE) { /* memory overrun? */ + nexm = 1; /* set nexm flag */ + wc = MEMSIZE - pa; } /* limit xfer */ +if ((da + wc) > RP_SIZE) { /* disk overrun? */ + rp_updsta (0, STB_EOP); /* error */ + wc = RP_SIZE - da; } /* limit xfer */ + +err = fseek (uptr -> fileref, da * sizeof (int), SEEK_SET); + +if ((f == FN_READ) && (err == 0)) { /* read? */ + awc = fxread (&M[pa], sizeof (int32), wc, uptr -> fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; + err = ferror (uptr -> fileref); } + +if ((f == FN_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int32), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + if ((err == 0) && (i = (wc & (RP_NUMWD - 1)))) { + fxwrite (fill, sizeof (int), i, uptr -> fileref); + err = ferror (uptr -> fileref); } } + +if ((f == FN_WRCHK) && (err == 0)) { /* write check? */ + for (i = 0; (err == 0) && (i < wc); i++) { + awc = fxread (&comp, sizeof (int32), 1, uptr -> fileref); + if (awc == 0) comp = 0; + if (comp != M[pa + i]) rp_updsta (0, STB_WCE); } + err = ferror (uptr -> fileref); } + +rp_wc = (rp_wc + wc) & 0777777; /* final word count */ +rp_ma = (rp_ma + wc) & 0777777; /* final mem addr */ +da = (da + wc + (RP_NUMWD - 1)) / RP_NUMWD; /* final sector num */ +cyl = da / (RP_NUMSC * RP_NUMSF); /* get cyl */ +if (cyl >= RP_NUMCY) cyl = RP_NUMCY - 1; +surf = (da % (RP_NUMSC * RP_NUMSF)) / RP_NUMSC; /* get surface */ +sect = (da % (RP_NUMSC * RP_NUMSF)) % RP_NUMSC; /* get sector */ +rp_da = (cyl << DA_V_CYL) | (surf << DA_V_SURF) | (sect << DA_V_SECT); +rp_busy = 0; /* clear busy */ +rp_updsta (STA_DON, 0); /* set done */ + +if (err != 0) { /* error? */ + perror ("RP I/O error"); + clearerr (uptr -> fileref); + return IORETURN (rp_stopioe, SCPE_IOERR); } +return SCPE_OK; +} + +/* Update status */ + +void rp_updsta (int32 newa, int32 newb) +{ +int32 f; +UNIT *uptr; + +uptr = rp_dev.units + GET_UNIT (rp_sta); +rp_sta = (rp_sta & ~(STA_DYN | STA_ERR)) | newa; +rp_stb = (rp_stb & ~STB_DYN) | newb; +if (uptr -> flags & UNIT_HWLK) rp_sta = rp_sta | STA_SUWP; +if ((uptr -> flags & UNIT_ATT) == 0) rp_stb = rp_stb | STB_SUFU | STB_SUNR; +else if (sim_is_active (uptr)) { + f = (uptr -> FUNC) & STA_M_FUNC; + if ((f == FN_SEEK) || (f == FN_RECAL)) + rp_stb = rp_stb | STB_SUSU | STB_SUNR; } +else if (uptr -> CYL >= RP_NUMCY) rp_sta = rp_sta | STA_SUSI; +if ((rp_sta & STA_EFLGS) || (rp_stb & STB_EFLGS)) rp_sta = rp_sta | STA_ERR; +if (((rp_sta & (STA_ERR | STA_DON)) && (rp_sta & STA_IED)) || + ((rp_stb & STB_ATTN) && (rp_sta & STA_IEA))) int_req = int_req | INT_RP; +else int_req = int_req & ~INT_RP; +return; +} + +/* Reset routine */ + +t_stat rp_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rp_sta = rp_stb = rp_da = rp_wc = rp_ma = rp_busy = 0; +int_req = int_req & ~INT_RP; +for (i = 0; i < RP_NUMDR; i++) { + uptr = rp_dev.units + i; + sim_cancel (uptr); + uptr -> CYL = uptr -> FUNC = 0; } +return SCPE_OK; +} + +/* IORS routine */ + +int32 rp_iors (void) +{ +return ((rp_sta & (STA_ERR | STA_DON)) || (rp_stb & STB_ATTN))? IOS_RP: 0; +} + +/* Attach unit */ + +t_stat rp_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +rp_updsta (0, 0); +return reason; +} + +/* Detach unit */ + +t_stat rp_detach (UNIT *uptr) +{ +t_stat reason; + +reason = detach_unit (uptr); +rp_updsta (0, 0); +return reason; +} diff --git a/pdp18b_stddev.c b/pdp18b_stddev.c new file mode 100644 index 00000000..e47a7804 --- /dev/null +++ b/pdp18b_stddev.c @@ -0,0 +1,584 @@ +/* pdp18b_stddev.c: 18b PDP's standard devices + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Standardized register naming + 06-Jan-97 RMS Fixed PDP-4 console input + 16-Dec-96 RMS Fixed bug in binary ptr service + + ptr paper tape reader + ptp paper tape punch + tti keyboard + tto teleprinter + clk clock +*/ + +#include "pdp18b_defs.h" +#include + +extern int32 int_req, saved_PC; +extern int32 M[]; +int32 clk_state = 0; +int32 ptr_err = 0, ptr_stopioe = 0, ptr_state = 0; +int32 ptp_err = 0, ptp_stopioe = 0; +int32 tti_state = 0; +int32 tto_state = 0; +t_stat clk_svc (UNIT *uptr); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat ptr_attach (UNIT *uptr, char *cptr); +t_stat ptp_attach (UNIT *uptr, char *cptr); +t_stat ptr_detach (UNIT *uptr); +t_stat ptp_detach (UNIT *uptr); +t_stat ptr_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit + clk_reg CLK register list +*/ + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 5000 }; + +REG clk_reg[] = { + { FLDATA (INT, int_req, INT_V_CLK) }, + { FLDATA (DONE, int_req, INT_V_CLK) }, + { FLDATA (ENABLE, clk_state, 0) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL }; + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 18) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { FLDATA (DONE, int_req, INT_V_PTR) }, +#if defined (IOS_PTRERR) + { FLDATA (ERR, ptr_err, 0) }, +#endif + { ORDATA (STATE, ptr_state, 5), REG_HRO }, + { DRDATA (POS, ptr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, &ptr_attach, &ptr_detach }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { FLDATA (DONE, int_req, INT_V_PTP) }, +#if defined (IOS_PTPERR) + { FLDATA (ERR, ptp_err, 0) }, +#endif + { DRDATA (POS, ptp_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, &ptp_attach, &ptp_detach }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit + tti_reg TTI register list + tti_trans ASCII to Baudot table +*/ + +#if defined (KSR28) +#define TTI_WIDTH 5 +#define TTI_FIGURES (1 << TTI_WIDTH) +#define TTI_2ND (1 << (TTI_WIDTH + 1)) +#define TTI_BOTH (1 << (TTI_WIDTH + 2)) +#define BAUDOT_LETTERS 033 +#define BAUDOT_FIGURES 037 + +static const int32 tti_trans[128] = { + 000,000,000,000,000,000,000,064, /* bell */ + 000,000,0210,000,000,0202,000,000, /* lf, cr */ + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 0204,066,061,045,062,000,053,072, /* space - ' */ + 076,051,000,000,046,070,047,067, /* ( - / */ + 055,075,071,060,052,041,065,074, /* 0 - 7 */ + 054,043,056,057,000,000,000,063, /* 8 - ? */ + 000,030,023,016,022,020,026,013, /* @ - G */ + 005,014,032,036,011,007,006,003, /* H - O */ + 015,035,012,024,001,034,017,031, /* P - W */ + 027,025,021,000,000,000,000,000, /* X - _ */ + 000,030,023,016,022,020,026,013, /* ` - g */ + 005,014,032,036,011,007,006,003, /* h - o */ + 015,035,012,024,001,034,017,031, /* p - w */ + 027,025,021,000,000,000,000,000 }; /* x - DEL */ +#else + +#define TTI_WIDTH 8 +#endif + +#define TTI_MASK ((1 << TTI_WIDTH) - 1) +#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ +#define UNIT_UC (1 << UNIT_V_UC) + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_UC, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, TTI_WIDTH) }, + { FLDATA (INT, int_req, INT_V_TTI) }, + { FLDATA (DONE, int_req, INT_V_TTI) }, +#if defined (KSR28) + { ORDATA (TTI_STATE, tti_state, (TTI_WIDTH + 3)), REG_HRO }, +#else + { FLDATA (UC, tti_unit.flags, UNIT_V_UC), REG_HRO }, +#endif + { DRDATA (POS, tti_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +MTAB tti_mod[] = { +#if !defined (KSR28) + { UNIT_UC, 0, "lower case", "LC", NULL }, + { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, +#endif + { 0 } }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit + tto_reg TTO register list + tto_trans Baudot to ASCII table +*/ + +#if defined (KSR28) +#define TTO_WIDTH 5 +#define TTO_FIGURES (1 << TTO_WIDTH) + +static const char tto_trans[64] = { + 0 ,'T',015,'O',' ','H','N','M', + 012,'L','R','G','I','P','C','V', + 'E','Z','D','B','S','Y','F','X', + 'A','W','J', 0 ,'U','Q','K', 0, + 0 ,'5','\r','9',' ','#',',','.', + 012,')','4','&','8','0',':',';', + '3','"','$','?','\a','6','!','/', + '-','2','\'',0 ,'7','1','(', 0 }; +#else + +#define TTO_WIDTH 8 +#endif + +#define TTO_MASK ((1 << TTO_WIDTH) - 1) + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, TTO_WIDTH) }, + { FLDATA (INT, int_req, INT_V_TTO) }, + { FLDATA (DONE, int_req, INT_V_TTO) }, +#if defined (KSR28) + { FLDATA (TTO_STATE, tto_state, 0), REG_HRO }, +#endif + { DRDATA (POS, tto_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL }; + +/* Clock: IOT routine */ + +int32 clk (int32 pulse, int32 AC) +{ +if (pulse == 001) return (int_req & INT_CLK)? IOT_SKP + AC: AC; /* CLSF */ +if (pulse == 004) clk_reset (&clk_dev); /* CLOF */ +else if (pulse == 044) { /* CLON */ + int_req = int_req & ~INT_CLK; /* clear flag */ + clk_state = 1; /* clock on */ + sim_activate (&clk_unit, clk_unit.wait); } /* start clock */ +return AC; +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +if (clk_state) { /* clock on? */ + M[7] = (M[7] + 1) & 0777777; /* incr counter */ + if (M[7] == 0) int_req = int_req | INT_CLK; /* ovrflo? set flag */ + sim_activate (&clk_unit, clk_unit.wait); } /* reactivate unit */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +int_req = int_req & ~INT_CLK; /* clear flag */ +clk_state = 0; /* clock off */ +sim_cancel (&clk_unit); /* stop clock */ +return SCPE_OK; +} + +/* IORS service for all standard devices */ + +int32 std_iors (void) +{ +return ((int_req & INT_CLK)? IOS_CLK: 0) | + ((int_req & INT_PTR)? IOS_PTR: 0) | + ((int_req & INT_PTP)? IOS_PTP: 0) | + ((int_req & INT_TTI)? IOS_TTI: 0) | + ((int_req & INT_TTO)? IOS_TTO: 0) | +#if defined (IOS_PTRERR) + (ptr_err? IOS_PTRERR: 0) | +#endif +#if defined (IOS_PTPERR) + (ptp_err? IOS_PTPERR: 0) | +#endif + (clk_state? IOS_CLKON: 0); +} + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 pulse, int32 AC) +{ +if (pulse == 001) return (int_req & INT_PTR)? IOT_SKP + AC: AC; /* RSF */ +if (pulse & 002) { /* RRB, RCF */ + int_req = int_req & ~INT_PTR; /* clear done */ + AC = ptr_unit.buf; } /* return buffer */ +if (pulse & 004) { /* RSA, RSB */ + ptr_state = (pulse & 040)? 18: 0; /* set mode */ + int_req = int_req & ~INT_PTR; /* clear done */ + ptr_unit.buf = 0; /* clear buffer */ + sim_activate (&ptr_unit, ptr_unit.wait); } +return AC; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ +#if defined (IOS_PTRERR) + int_req = int_req | INT_PTR; /* if err, set int */ + ptr_err = 1; +#endif + return IORETURN (ptr_stopioe, SCPE_UNATT); } +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ +#if defined (IOS_PTRERR) + int_req = int_req | INT_PTR; /* if err, set done */ + ptr_err = 1; +#endif + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; } +if (ptr_state == 0) ptr_unit.buf = temp & 0377; /* alpha */ +else if (temp & 0200) { /* binary */ + ptr_state = ptr_state - 6; + ptr_unit.buf = ptr_unit.buf | ((temp & 077) << ptr_state); } +if (ptr_state == 0) int_req = int_req | INT_PTR; /* if done, set flag */ +else sim_activate (&ptr_unit, ptr_unit.wait); /* else restart */ +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_state = 0; /* clear state */ +ptr_unit.buf = 0; +int_req = int_req & ~INT_PTR; /* clear flag */ +ptr_err = (ptr_unit.flags & UNIT_ATT)? 0: 1; +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ptr_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +ptr_err = (ptr_unit.flags & UNIT_ATT)? 0: 1; +return reason; +} + +/* Detach routine */ + +t_stat ptr_detach (UNIT *uptr) +{ +ptr_err = 1; +return detach_unit (uptr); +} + +/* Bootstrap routine */ + +#define BOOT_START 017762 +#define BOOT_PC 017770 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 0000000, /* r, 0 */ + 0700101, /* rsf */ + 0617763, /* jmp .-1 */ + 0700112, /* rrb */ + 0700144, /* rsb */ + 0637762, /* jmp i r */ + 0700144, /* go, rsb */ + 0117762, /* g, jms r */ + 0057775, /* dac out */ + 0417775, /* xct out */ + 0117762, /* jms r */ + 0000000, /* out, 0 */ + 0617771 /* jmp g */ +}; + +t_stat ptr_boot (int32 unitno) +{ +int32 i; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_PC; +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 pulse, int32 AC) +{ +if (pulse == 001) return (int_req & INT_PTP)? IOT_SKP + AC: AC; /* PSF */ +if (pulse & 002) int_req = int_req & ~INT_PTP; /* PCF */ +if (pulse & 004) { /* PSA, PSB, PLS */ + int_req = int_req & ~INT_PTP; /* clear flag */ + ptp_unit.buf = (pulse & 040)? /* load punch buf */ + (AC & 077) | 0200: AC & 0377; /* bin or alpha */ + sim_activate (&ptp_unit, ptp_unit.wait); } /* activate unit */ +return AC; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +int_req = int_req | INT_PTP; /* set done flag */ +if ((ptp_unit.flags & UNIT_ATT) == 0) { /* not attached? */ + ptp_err = 1; /* set error */ + return IORETURN (ptp_stopioe, SCPE_UNATT); } +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* I/O error? */ + ptp_err = 1; /* set error */ + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +int_req = int_req & ~INT_PTP; /* clear flag */ +ptp_err = (ptp_unit.flags & UNIT_ATT)? 0: 1; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ptp_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +ptp_err = (ptp_unit.flags & UNIT_ATT)? 0: 1; +return reason; +} + +/* Detach routine */ + +t_stat ptp_detach (UNIT *uptr) +{ +ptp_err = 1; +return detach_unit (uptr); +} + +/* Terminal input: IOT routine */ + +int32 tti (int32 pulse, int32 AC) +{ +if (pulse == 001) return (int_req & INT_TTI)? IOT_SKP + AC: AC; /* KSF */ +if (pulse == 002) { /* KRB */ + int_req = int_req & ~INT_TTI; /* clear flag */ + return tti_unit.buf & TTI_MASK; } /* return buffer */ +return AC; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ + +#if defined (KSR28) /* Baudot... */ +if (tti_state & TTI_2ND) { /* char waiting? */ + tti_unit.buf = tti_state & TTI_MASK; /* return char */ + tti_state = tti_state & ~TTI_2ND; } /* not waiting */ +else { if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; + temp = tti_trans[temp & 0177]; /* translate char */ + if (temp == 0) return SCPE_OK; /* untranslatable? */ + if (((temp & TTI_FIGURES) == (tti_state & TTI_FIGURES)) || + (temp & TTI_BOTH)) tti_unit.buf = temp & TTI_MASK; + else { tti_unit.buf = (temp & TTI_FIGURES)? + BAUDOT_FIGURES: BAUDOT_LETTERS; + tti_state = temp | TTI_2ND; } } /* set 2nd waiting */ +#else /* ASCII... */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +temp = temp & 0177; +if ((tti_unit.flags & UNIT_UC) && islower (temp)) temp = toupper (temp); +tti_unit.buf = temp | 0200; /* got char */ +#endif +int_req = int_req | INT_TTI; /* set flag */ +tti_unit.pos = tti_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; /* clear buffer */ +tti_state = 0; /* clear state */ +int_req = int_req & ~INT_TTI; /* clear flag */ +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 pulse, int32 AC) +{ +if (pulse == 001) return (int_req & INT_TTO)? IOT_SKP + AC: AC; /* TSF */ +if (pulse & 002) int_req = int_req & ~INT_TTO; /* clear flag */ +if (pulse & 004) { /* load buffer */ + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + tto_unit.buf = AC & TTO_MASK; } /* load buffer */ +return AC; +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 out, temp; + +int_req = int_req | INT_TTO; /* set flag */ +#if defined (KSR28) /* Baudot... */ +if (tto_unit.buf == BAUDOT_FIGURES) { /* set figures? */ + tto_state = TTO_FIGURES; + return SCPE_OK; } +if (tto_unit.buf == BAUDOT_LETTERS) { /* set letters? */ + tto_state = 0; + return SCPE_OK; } +out = tto_trans[tto_unit.buf + tto_state]; /* translate */ +#else +out = tto_unit.buf & 0177; /* ASCII... */ +#endif +if ((temp = sim_putchar (out)) != SCPE_OK) return temp; +tto_unit.pos = tto_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; /* clear buffer */ +tto_state = 0; /* clear state */ +int_req = int_req & ~INT_TTO; /* clear flag */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/pdp18b_sys.c b/pdp18b_sys.c new file mode 100644 index 00000000..35aa2de6 --- /dev/null +++ b/pdp18b_sys.c @@ -0,0 +1,708 @@ +/* pdp18b_sys.c: 18b PDP's simulator interface + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface + 20-Oct-97 RMS Fixed endian dependence in RIM loader + (found by Michael Somos) +*/ + +#include "pdp18b_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE clk_dev; +extern DEVICE lpt_dev; +#if defined (DRM) +extern DEVICE drm_dev; +#endif +#if defined (RF) +extern DEVICE rf_dev; +#endif +#if defined (RP) +extern DEVICE rp_dev; +#endif +#if defined (MTA) +extern DEVICE mt_dev; +#endif +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 M[]; +extern int32 memm; +extern int32 saved_PC; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +#if defined (PDP4) +char sim_name[] = "PDP-4"; +#elif defined (PDP7) +char sim_name[] = "PDP-7"; +#elif defined (PDP9) +char sim_name[] = "PDP-9"; +#elif defined (PDP15) +char sim_name[] = "PDP-15"; +#endif + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 3; + +DEVICE *sim_devices[] = { &cpu_dev, + &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, + &clk_dev, &lpt_dev, +#if defined (DRM) + &drm_dev, +#endif +#if defined (RF) + &rf_dev, +#endif +#if defined (RP) + &rp_dev, +#endif +#if defined (MTA) + &mt_dev, +#endif + NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Undefined instruction", + "HALT instruction", + "Breakpoint", + "Nested XCT's" }; + +/* Binary loader + + Until someone finds the binary loader documentation, + this implements RIM loader format +*/ + +int32 getword (FILE *fileref) +{ +int32 i, tmp, word; + +word = 0; +for (i = 0; i < 3;) { + if ((tmp = getc (fileref)) == EOF) return -1; + if (tmp & 0200) { + word = (word << 6) | (tmp & 077); + i++; } } +return word; +} + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 origin, val; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +for (;;) { + if ((val = getword (fileref)) < 0) return SCPE_FMT; + if ((val & 0760000) == 0040000) { /* DAC? */ + origin = val & 017777; + if ((val = getword (fileref)) < 0) return SCPE_FMT; + if (MEM_ADDR_OK (origin)) M[origin++] = val; } + else if ((val & 0760000) == 0600000) { + saved_PC = val & 017777; + return SCPE_OK; } } +return SCPE_FMT; /* error */ +} + +/* Symbol tables */ + +#define I_V_FL 18 /* inst class */ +#define I_M_FL 017 /* class mask */ +#define I_V_DC 22 /* default count */ +#define I_V_NPN 0 /* no operand */ +#define I_V_NPI 1 /* no operand IOT */ +#define I_V_IOT 2 /* IOT */ +#define I_V_MRF 3 /* memory reference */ +#define I_V_OPR 4 /* OPR */ +#define I_V_LAW 5 /* LAW */ +#define I_V_XR 6 /* index */ +#define I_V_XR9 7 /* index literal */ +#define I_V_EST 8 /* EAE setup */ +#define I_V_ESH 9 /* EAE shift */ +#define I_V_EMD 10 /* EAE mul-div */ +#define I_NPN (I_V_NPN << I_V_FL) /* no operand */ +#define I_NPI (I_V_NPI << I_V_FL) /* no operand IOT */ +#define I_IOT (I_V_IOT << I_V_FL) /* IOT */ +#define I_MRF (I_V_MRF << I_V_FL) /* memory reference */ +#define I_OPR (I_V_OPR << I_V_FL) /* OPR */ +#define I_LAW (I_V_LAW << I_V_FL) /* LAW */ +#define I_XR (I_V_XR << I_V_FL) /* index */ +#define I_XR9 (I_V_XR9 << I_V_FL) /* index literal */ +#define I_EST (I_V_EST << I_V_FL) /* EAE setup */ +#define I_ESH (I_V_ESH << I_V_FL) /* EAE shift */ +#define I_EMD (I_V_EMD << I_V_FL) /* EAE mul-div */ +#define MD(x) ((I_EMD) + ((x) << I_V_DC)) + +static const int32 masks[] = { + 0777777, 0777767, 0740000, 0740000, + 0763730, 0760000, 0777000, 0777000, + 0740700, 0760700, 0777700 }; + +static const char *opcode[] = { + "CAL", "DAC", "JMS", "DZM", /* mem refs */ + "LAC", "XOR", "ADD", "TAD", + "XCT", "ISZ", "AND", "SAD", + "JMP", "LAW", + + "LACQ", "LACS", "ABS", "GSM", "LMQ", /* EAE */ + "MUL", "MULS", "DIV", "DIVS", + "IDIV", "IDIVS", "FRDIV", "FRDIVS", + "NORM", "NORMS", + "MUY", "LLK MUY", "DVI", "LLK DVI", + "NMI", "NMIS", "LRS", "LRSS", + "LLS", "LLSS", "ALS", "ALSS", + "EAE-setup", "EAE", /* setup, general */ + + "CLSF", "IOF", "ION", "CLOF", "CLON", /* standard IO devs */ + "RSF", "RRB", "RCF", "RSA", "RSB", + "PSF", "PCF", "PSA", "PSB", "PLS", + "KSF", "KRB", "KCF", "IORS", "IOOS", + "TSF", "TCF", "TPC", "TLS", +#if defined (TYPE62) /* PDP-4 LPT */ + "LPSF", "LPCF", "LPLD", "LPSE", + "LSSF", "LSCF", "LSPR", +#elif defined (TYPE647) /* PDP-7, PDP-9 LPT */ + "LPSF", "LPCB", "LPCD", "LPCD", "LPCD", + "LPL2", "LPLD", "LPL1", + "LPEF", "LPCF", "LPCF", "LPCF", "LPCF", + "LPPB", "LPLS", "LPPS", +#elif defined (LP15) + "LPSF", "LPPM", "LPP1", "LPDI", + "LPRS", "LPOS", "LPEI", "LPCD", "LPCF", +#endif +#if defined (DRM) /* drum */ + "DRLR", "DRLW", "DRSS", "DRCS", + "DRSF", "DRSN", "DRCF", + "DRLCRD", "DRLCWR", "DRLBLK", "DRCONT", + "DRSF", "DRSOK", "DRCF", +#endif +#if defined (RF) /* RF09 */ + "DSSF", "DSCC", "DSCF", + "DRBR", "DRAL", "DSFX", "DRAH", + "DLBR", "DLAL", "DSCN", "DLAH", + "DLOK", "DSCD", "DSRS", + "DGHS", "DGSS", +#endif +#if defined (RP) + "DPSF", "DPSA", "DPSJ", "DPSE", + "DPRSA", "DPOSA", "DPRSB", "DPOSB", + "DPRM", "DPOM", + "DPLA", "DPCS", "DPCA", "DPWC", + "DPLM", "DPEM", "DPSN", + "DPRU", "DPOU", "DPRA", "DPOA", + "DPRC", "DPOC", "DPRW", "DPOW", + "DPCF", "DPLZ", "DPCN", "DPLO", "DPLF", +#endif +#if defined (MTA) /* TC59 */ + "MTTR", "MTCR", "MTSF", "MTRC", "MTAF", + "MTRS", "MTGO", "MTCM", "MTLC", +#endif +#if defined (PDP7) + "ITON", "TTS", "SKP7", "CAF", + "SEM", "EEM", "EMIR", "LEM", +#elif defined (PDP9) + "PFSF", "TTS", "SKP7", "CAF", + "DBR", "SEM", "EEM", "LEM", + "MPSK", "MPSNE", "MPCV", "MPEU", + "MPLD", "MPCNE", + "LPDI", "LPEI", +#elif defined (PDP15) + "PFSF", "TTS", "SPCO", "CAF", + "DBR", "SKP15", "SBA", "DBA", "EBA", + "PAX", "PAL", "AAC", "PXA", + "AXS", "PXL", "PLA", "PLX", + "CLX", "CLL", "AXR", +#endif + "IOT", /* general */ + + "NOP", "STL", "RCL", "RCR", + "CLC", "LAS", "GLK", + "OPR", "SMA", "SZA", "SZA SMA", + "SNL", "SNL SMA", "SNL SZA", "SNL SZA SMA", + "SKP", "SPA", "SNA", "SNA SPA", + "SZL", "SZL SPA", "SZL SNA", "SZL SZA SPA", + "RAL", "SMA RAL", "SZA RAL", "SZA SMA RAL", + "SNL RAL", "SNL SMA RAL", "SNL SZA RAL", "SNL SZA SMA RAL", + "SKP RAL", "SPA RAL", "SNA RAL", "SNA SPA RAL", + "SZL RAL", "SZL SPA RAL", "SZL SNA RAL", "SZL SZA SPA RAL", + "RAR", "SMA RAR", "SZA RAR", "SZA SMA RAR", + "SNL RAR", "SNL SMA RAR", "SNL SZA RAR", "SNL SZA SMA RAR", + "SKP RAR", "SPA RAR", "SNA RAR", "SNA SPA RAR", + "SZL RAR", "SZL SPA RAR", "SZL SNA RAR", "SZL SZA SPA RAR", +#if defined (PDP15) + "IAC", "SMA IAC", "SZA IAC", "SZA SMA IAC", + "SNL IAC", "SNL SMA IAC", "SNL SZA IAC", "SNL SZA SMA IAC", + "SKP IAC", "SPA IAC", "SNA IAC", "SNA SPA IAC", + "SZL IAC", "SZL SPA IAC", "SZL SNA IAC", "SZL SZA SPA IAC", +#else + "RAL RAR", "SMA RAL RAR", "SZA RAL RAR", "SZA SMA RAL RAR", + "SNL RAL RAR", "SNL SMA RAL RAR", "SNL SZA RAL RAR", "SNL SZA SMA RAL RAR", + "SKP RAL RAR", "SPA RAL RAR", "SNA RAL RAR", "SNA SPA RAL RAR", + "SZL RAL RAR", "SZL SPA RAL RAR", "SZL SNA RAL RAR", "SZL SZA SPA RAL RAR", +#endif + "RTWO", "SMA RTWO", "SZA RTWO", "SZA SMA RTWO", + "SNL RTWO", "SNL SMA RTWO", "SNL SZA RTWO", "SNL SZA SMA RTWO", + "SKP RTWO", "SPA RTWO", "SNA RTWO", "SNA SPA RTWO", + "SZL RTWO", "SZL SPA RTWO", "SZL SNA RTWO", "SZL SZA SPA RTWO", + "RTL", "SMA RTL", "SZA RTL", "SZA SMA RTL", + "SNL RTL", "SNL SMA RTL", "SNL SZA RTL", "SNL SZA SMA RTL", + "SKP RTL", "SPA RTL", "SNA RTL", "SNA SPA RTL", + "SZL RTL", "SZL SPA RTL", "SZL SNA RTL", "SZL SZA SPA RTL", + "RTR", "SMA RTR", "SZA RTR", "SZA SMA RTR", + "SNL RTR", "SNL SMA RTR", "SNL SZA RTR", "SNL SZA SMA RTR", + "SKP RTR", "SPA RTR", "SNA RTR", "SNA SPA RTR", + "SZL RTR", "SZL SPA RTR", "SZL SNA RTR", "SZL SZA SPA RTR", +#if defined (PDP15) + "BSW", "SMA BSW", "SZA BSW", "SZA SMA BSW", + "SNL BSW", "SNL SMA BSW", "SNL SZA BSW", "SNL SZA SMA BSW", + "SKP BSW", "SPA BSW", "SNA BSW", "SNA SPA BSW", + "SZL BSW", "SZL SPA BSW", "SZL SNA BSW", "SZL SZA SPA BSW", +#else + "RTL RTR", "SMA RTL RTR", "SZA RTL RTR", "SZA SMA RTL RTR", + "SNL RTL RTR", "SNL SMA RTL RTR", "SNL SZA RTL RTR", "SNL SZA SMA RTL RTR", + "SKP RTL RTR", "SPA RTL RTR", "SNA RTL RTR", "SNA SPA RTL RTR", + "SZL RTL RTR", "SZL SPA RTL RTR", "SZL SNA RTL RTR", "SZL SZA SPA RTL RTR", +#endif + + "LLK", "CLQ", "LSN", "OACQ", "ECLA", /* encode only masks */ + "CMQ", "OMQ", "OSC", + "CLA", "CLL", "CML", "CMA", + "OAS", "HLT", + NULL }; + +static const int32 opc_val[] = { + 0000000+I_MRF, 0040000+I_MRF, 0100000+I_MRF, 0140000+I_MRF, + 0200000+I_MRF, 0240000+I_MRF, 0300000+I_MRF, 0340000+I_MRF, + 0400000+I_MRF, 0440000+I_MRF, 0500000+I_MRF, 0540000+I_MRF, + 0600000+I_MRF, 0760000+I_LAW, + + 0641002+I_NPN, 0641001+I_NPN, 0644000+I_NPN, 0664000+I_NPN, 0652000+I_NPN, + 0653100+MD(022), 0657100+MD(022), 0640300+MD(023), 0644300+MD(023), + 0653300+MD(023), 0657300+MD(023), 0650300+MD(023), 0654300+MD(023), + 0640400+MD(044), 0660400+MD(044), + 0640100+I_ESH, 0660100+I_ESH, 0640300+I_ESH, 0660300+I_ESH, + 0640400+I_ESH, 0660400+I_ESH, 0640500+I_ESH, 0660500+I_ESH, + 0640600+I_ESH, 0660600+I_ESH, 0640700+I_ESH, 0660700+I_ESH, + 0640000+I_EST, 0640000+I_IOT, + + 0700001+I_NPI, 0700002+I_NPI, 0700042+I_NPI, 0700004+I_NPI, 0700044+I_NPI, + 0700101+I_NPI, 0700112+I_NPN, 0700102+I_NPI, 0700104+I_NPI, 0700144+I_NPI, + 0700201+I_NPI, 0700202+I_NPI, 0700204+I_NPI, 0700244+I_NPI, 0700206+I_NPI, + 0700301+I_NPI, 0700312+I_NPN, 0700302+I_NPI, 0700314+I_NPN, 0700304+I_NPI, + 0700401+I_NPI, 0700402+I_NPI, 0700404+I_NPI, 0700406+I_NPI, +#if defined (TYPE62) + 0706501+I_NPI, 0706502+I_NPI, 0706542+I_NPI, 0706506+I_NPI, + 0706601+I_NPI, 0706602+I_NPI, 0706606+I_NPI, +#elif defined (TYPE647) + 0706501+I_NPI, 0706502+I_NPI, 0706522+I_NPI, 0706542+I_NPI, 0706562+I_NPI, + 0706526+I_NPI, 0706546+I_NPI, 0706566+I_NPI, + 0706601+I_NPI, 0706602+I_NPI, 0706622+I_NPI, 0706642+I_NPI, 0706662+I_NPI, + 0706606+I_NPI, 0706626+I_NPI, 0706646+I_NPI, +#elif defined (LP15) + 0706501+I_NPI, 0706521+I_NPI, 0706541+I_NPI, 0706561+I_NPI, + 0706552+I_NPN, 0706542+I_NPI, 0706544+I_NPI, 0706621+I_NPI, 0706641+I_NPI, +#endif +#if defined (DRM) + 0706006+I_NPI, 0706046+I_NPI, 0706106+I_NPI, 0706204+I_NPI, + 0706101+I_NPI, 0706201+I_NPI, 0706102+I_NPI, + 0706006+I_NPI, 0706046+I_NPI, 0706106+I_NPI, 0706204+I_NPI, + 0706101+I_NPI, 0706201+I_NPI, 0706102+I_NPI, +#endif +#if defined (RF) + 0707001+I_NPI, 0707021+I_NPI, 0707041+I_NPI, + 0707002+I_NPI, 0707022+I_NPI, 0707042+I_NPI, 0707062+I_NPI, + 0707004+I_NPI, 0707024+I_NPI, 0707044+I_NPI, 0707064+I_NPI, + 0707202+I_NPI, 0707242+I_NPI, 0707262+I_NPI, + 0707204+I_NPI, 0707224+I_NPI, +#endif +#if defined (RP) + 0706301+I_NPI, 0706321+I_NPI, 0706341+I_NPI, 0706361+I_NPI, + 0706312+I_NPN, 0706302+I_NPI, 0706332+I_NPN, 0706322+I_NPI, + 0706342+I_NPN, 0706352+I_NPI, + 0706304+I_NPI, 0706324+I_NPI, 0706344+I_NPI, 0706364+I_NPI, + 0706411+I_NPN, 0706401+I_NPI, 0706421+I_NPI, + 0706412+I_NPN, 0706402+I_NPI, 0706432+I_NPN, 0706422+I_NPI, + 0706452+I_NPN, 0706442+I_NPI, 0706472+I_NPN, 0706462+I_NPI, + 0706404+I_NPI, 0706424+I_NPI, 0706454+I_NPN, 0706444+I_NPI, 0706464+I_NPI, +#endif +#if defined (MTA) + 0707301+I_NPI, 0707321+I_NPI, 0707341+I_NPI, 0707312+I_NPN, 0707322+I_NPI, + 0707352+I_NPN, 0707304+I_NPI, 0707324+I_NPI, 0707326+I_NPI, +#endif +#if defined (PDP7) + 0703201+I_NPI, 0703301+I_NPI, 0703341+I_NPI, 0703302+I_NPI, + 0707701+I_NPI, 0707702+I_NPI, 0707742+I_NPI, 0707704+I_NPI, +#elif defined (PDP9) + 0700062+I_NPI, 0703301+I_NPI, 0703341+I_NPI, 0703302+I_NPI, + 0703344+I_NPI, 0707701+I_NPI, 0707702+I_NPI, 0707704+I_NPI, + 0701701+I_NPI, 0701741+I_NPI, 0701702+I_NPI, 0701742+I_NPI, + 0701704+I_NPI, 0701644+I_NPI, + 0706504+I_NPI, 0706604+I_NPI, +#elif defined (PDP15) + 0700062+I_NPI, 0703301+I_NPI, 0703341+I_NPI, 0703302+I_NPI, + 0703344+I_NPI, 0707741+I_NPI, 0707761+I_NPI, 0707762+I_NPI, 0707764+I_NPI, + 0721000+I_XR, 0722000+I_XR, 0723000+I_XR9, 0724000+I_XR, + 0725000+I_XR9, 0726000+I_XR, 0730000+I_XR, 0731000+I_XR, + 0735000+I_XR, 0736000+I_XR, 0737000+I_XR9, +#endif + 0700000+I_IOT, + + 0740000+I_NPN, 0744002+I_NPN, 0744010+I_NPN, 0744020+I_NPN, + 0750001+I_NPN, 0750004+I_NPN, 0750010+I_NPN, + 0740000+I_OPR, 0740100+I_OPR, 0740200+I_OPR, 0740300+I_OPR, + 0740400+I_OPR, 0740500+I_OPR, 0740600+I_OPR, 0740700+I_OPR, + 0741000+I_OPR, 0741100+I_OPR, 0741200+I_OPR, 0741300+I_OPR, + 0741400+I_OPR, 0741500+I_OPR, 0741600+I_OPR, 0741700+I_OPR, + 0740010+I_OPR, 0740110+I_OPR, 0740210+I_OPR, 0740310+I_OPR, + 0740410+I_OPR, 0740510+I_OPR, 0740610+I_OPR, 0740710+I_OPR, + 0741010+I_OPR, 0741110+I_OPR, 0741210+I_OPR, 0741310+I_OPR, + 0741410+I_OPR, 0741510+I_OPR, 0741610+I_OPR, 0741710+I_OPR, + 0740020+I_OPR, 0740120+I_OPR, 0740220+I_OPR, 0740320+I_OPR, + 0740420+I_OPR, 0740520+I_OPR, 0740620+I_OPR, 0740720+I_OPR, + 0741020+I_OPR, 0741120+I_OPR, 0741220+I_OPR, 0741320+I_OPR, + 0741420+I_OPR, 0741520+I_OPR, 0741620+I_OPR, 0741720+I_OPR, + 0740030+I_OPR, 0740130+I_OPR, 0740230+I_OPR, 0740330+I_OPR, + 0740430+I_OPR, 0740530+I_OPR, 0740630+I_OPR, 0740730+I_OPR, + 0741030+I_OPR, 0741130+I_OPR, 0741230+I_OPR, 0741330+I_OPR, + 0741430+I_OPR, 0741530+I_OPR, 0741630+I_OPR, 0741730+I_OPR, + 0742000+I_OPR, 0742100+I_OPR, 0742200+I_OPR, 0742300+I_OPR, + 0742400+I_OPR, 0742500+I_OPR, 0742600+I_OPR, 0742700+I_OPR, + 0743000+I_OPR, 0743100+I_OPR, 0743200+I_OPR, 0743300+I_OPR, + 0743400+I_OPR, 0743500+I_OPR, 0743600+I_OPR, 0743700+I_OPR, + 0742010+I_OPR, 0742110+I_OPR, 0742210+I_OPR, 0742310+I_OPR, + 0742410+I_OPR, 0742510+I_OPR, 0742610+I_OPR, 0742710+I_OPR, + 0743010+I_OPR, 0743110+I_OPR, 0743210+I_OPR, 0743310+I_OPR, + 0743410+I_OPR, 0743510+I_OPR, 0743610+I_OPR, 0743710+I_OPR, + 0742020+I_OPR, 0742120+I_OPR, 0742220+I_OPR, 0742320+I_OPR, + 0742420+I_OPR, 0742520+I_OPR, 0742620+I_OPR, 0742720+I_OPR, + 0743020+I_OPR, 0743120+I_OPR, 0743220+I_OPR, 0743320+I_OPR, + 0743420+I_OPR, 0743520+I_OPR, 0743620+I_OPR, 0743720+I_OPR, + 0742030+I_OPR, 0742130+I_OPR, 0742230+I_OPR, 0742330+I_OPR, + 0742430+I_OPR, 0742530+I_OPR, 0742630+I_OPR, 0742730+I_OPR, + 0743030+I_OPR, 0743130+I_OPR, 0743230+I_OPR, 0743330+I_OPR, + 0743430+I_OPR, 0743530+I_OPR, 0743630+I_OPR, 0743730+I_OPR, + + 0660000+I_EST, 0650000+I_EST, 0644000+I_EST, 0642000+I_EST, 0641000+I_EST, + 0640004+I_EST, 0640002+I_EST, 0640001+I_EST, + 0750000+I_OPR, 0744000+I_OPR, 0740002+I_OPR, 0740001+I_OPR, + 0740004+I_OPR, 0740040+I_OPR, + -1 }; + +/* Operate or EAE decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed? +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; } } +return sp; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, k, sp, inst, disp, ma; + +inst = val[0]; +i = val[1]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, "%c", SIXTOASC ((inst >> 12) & 077)); + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; } +#if defined (PDP15) +if (sw & SWMASK ('P')) { /* packed ASCII? */ + fprintf (of, "%c", FMTASC ((inst >> 11) & 0177)); + fprintf (of, "%c", FMTASC ((inst >> 4) & 0177)); + fprintf (of, "%c", FMTASC (((inst << 3) | (i >> 15)) & 0177)); + fprintf (of, "%c", FMTASC ((i >> 8) & 0177)); + fprintf (of, "%c", FMTASC ((i >> 1) & 0177)); + return -1; } +#endif +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0777777) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + case I_V_NPN: /* no operands */ + case I_V_XR: /* index no opers */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + case I_V_NPI: /* IOT no operand */ + fprintf (of, "%s", opcode[i]); /* opcode */ + if (inst & 010) fprintf (of, " +10"); + break; + case I_V_IOT: /* IOT or EAE */ + fprintf (of, "%s %-o", opcode[i], inst & 037777); + break; + case I_V_MRF: /* mem ref */ +#if defined (PDP15) + if (memm) { + disp = inst & 017777; + ma = (addr & 0760000) | disp; } + else { disp = inst & 007777; + ma = (addr & 0770000) | disp; } +#else + disp = inst & 017777; + ma = (addr & 0760000) | disp; +#endif +#if defined (PDP9) || (PDP15) + if ((disp & ~07) == 00010) ma = ma & 00017; +#endif +#if defined (PDP15) + fprintf (of, "%s%s%-o", opcode[i], + ((inst & 0020000)? " @": " "), + (cflag? ma & ADDRMASK: disp)); + if (!memm && (inst & 0010000)) fprintf (of, ",X"); +#else + fprintf (of, "%s%s%-o", opcode[i], + ((inst & 0020000)? " I ": " "), + (cflag? ma & ADDRMASK: disp)); +#endif + break; + case I_V_OPR: /* operate */ + if (sp = (inst & 03730)) fprintf (of, "%s", opcode[i]); + fprint_opr (of, inst & 014047, I_V_OPR, sp); + break; + case I_V_LAW: /* LAW */ + fprintf (of, "%s %-o", opcode[i], inst & 017777); + break; + case I_V_XR9: /* index with lit */ + disp = inst & 0777; + if (disp & 0400) fprintf (of, "%s -%-o", opcode[i], 01000 - disp); + else fprintf (of, "%s %-o", opcode[i], disp); + break; + case I_V_EST: /* EAE setup */ + fprint_opr (of, inst & 037007, I_V_EST, 0); + break; + case I_V_ESH: /* EAE shift */ + sp = fprint_opr (of, inst & 017000, I_V_EST, 0); + fprintf (of, (sp? " %s %-o": "%s %-o"), opcode[i], inst & 077); + break; + case I_V_EMD: /* EAE mul-div */ + disp = inst & 077; /* get actual val */ + k = (opc_val[i] >> I_V_DC) & 077; /* get default val */ + if (disp == k) fprintf (of, "%s", opcode[i]); + else if (disp < k) fprintf (of, "%s -%-o", opcode[i], k - disp); + else fprintf (of, "%s +%-o", opcode[i], disp - k); + break; } /* end case */ + return SCPE_OK; } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get 18b signed number + + Inputs: + *cptr = pointer to input string + *sign = pointer to sign + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_sint (char *cptr, int32 *sign, t_stat *status) +{ +*sign = 0; +if (*cptr == '+') { + *sign = 1; + cptr++; } +else if (*cptr == '-') { + *sign = -1; + cptr++; } +return get_uint (cptr, 8, 0777777, status); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k, sign, dmask, epcmask; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 1; (i < 5) && (cptr[i] != 0); i++) + if (cptr[i] == 0) for (j = i + 1; j <= 5; j++) cptr[j] = 0; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] | 0200; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 077) << 12) | + (((t_value) cptr[1] & 077) << 6) | + ((t_value) cptr[2] & 077); + return SCPE_OK; } +#if defined (PDP15) +if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 0177) << 11) | + (((t_value) cptr[1] & 0177) << 4) | + (((t_value) cptr[2] & 0170) >> 3); + val[1] = (((t_value) cptr[2] & 0007) << 15) | + (((t_value) cptr[3] & 0177) << 8) | + (((t_value) cptr[4] & 0177) << 1); + return -1; } +#endif + +/* Symbolic input, continued */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0777777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ +case I_V_XR: /* index */ + break; +case I_V_XR9: /* index literal */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + d = get_sint (gbuf, &sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (((sign >= 0) && (d > 0377)) || ((sign < 0) && (d > 0400))) + return SCPE_ARG; + val[0] = val[0] | ((sign >= 0)? d: (01000 - d)); + break; +case I_V_LAW: /* law */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + d = get_uint (gbuf, 8, 017777, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + break; +case I_V_MRF: /* mem ref */ +#if !defined (PDP15) + dmask = 017777; + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if (strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | 020000; + cptr = get_glyph (cptr, gbuf, 0); } +#else + if (memm) dmask = 017777; + else dmask = 07777; + if (*cptr == '@') { /* indirect? */ + val[0] = val[0] | 020000; + cptr++; } + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ +#endif + epcmask = ADDRMASK & ~dmask; + d = get_uint (gbuf, 8, ADDRMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (d <= dmask) val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & epcmask) == 0)) + val[0] = val[0] | (d & dmask); + else return SCPE_ARG; +#if defined (PDP15) + if (!memm) { + cptr = get_glyph (cptr, gbuf, 0); + if (gbuf[0] != 0) { + if (strcmp (gbuf, "X") != 0) return SCPE_ARG; + val[0] = val[0] + 010000; } } +#endif + break; +case I_V_EMD: /* or'able */ + val[0] = val[0] | ((opc_val[i] >> I_V_DC) & 077); /* default shift */ +case I_V_EST: case I_V_ESH: +case I_V_NPN: case I_V_NPI: case I_V_IOT: case I_V_OPR: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if (opcode[i] != NULL) { + k = opc_val[i] & 0777777; + if (((k ^ val[0]) & 0740000) != 0) return SCPE_ARG; + val[0] = val[0] | k; } + else { d = get_sint (gbuf, & sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (sign > 0) val[0] = val[0] + d; + else if (sign < 0) val[0] = val[0] - d; + else val[0] = val[0] | d; } } + break; } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/pdp1_cpu.c b/pdp1_cpu.c new file mode 100644 index 00000000..a0bd9801 --- /dev/null +++ b/pdp1_cpu.c @@ -0,0 +1,782 @@ +/* pdp1_cpu.c: PDP-1 CPU simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + + The PDP-1 was Digital's first computer. Although Digital built four + other 18b computers, the later systems (the PDP-4, PDP-7, PDP-9, and + PDP-15) were similar to each other and quite different from the PDP-1. + Accordingly, the PDP-1 requires a distinct simulator. + + The register state for the PDP-1 is: + + AC<0:17> accumulator + IO<0:17> IO register + OV overflow flag + PC<0:15> program counter + IOSTA I/O status register + SBS<0:2> sequence break flip flops + IOH I/O halt flip flop + IOC I/O completion flip flop + EXTM extend mode + PF<1:6> program flags + SS<1:6> sense switches + TW<0:17> test word (switch register) + + Unresolved questions: + + 1. cks: is there a status flag for sequence break enabled? + 2. cks: which bits are line printer print done and space done? + 3. sbs: do sequence breaks accumulate when the break system is off? +*/ + +/* The PDP-1 has six instruction formats: memory reference, skips, + shifts, load immediate, I/O transfer, and operate. The memory + reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in| address | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> <5> mnemonic action + + 00 + 02 AND AC = AC & M[MA] + 04 IOR AC = AC | M[MA] + 06 XOR AC = AC ^ M[MA] + 10 XCT M[MA] is executed as an instruction + 12 + 14 + 16 0 CAL M[100] = AC, AC = PC, PC = 101 + 16 1 JDA M[MA] = AC, AC = PC, PC = MA + 1 + 20 LAC AC = M[MA] + 22 LIO IO = M[MA] + 24 DAC M[MA] = AC + 26 DAP M[MA]<6:17> = AC<6:17> + 30 DIP M[MA]<0:5> = AC<0:5> + 32 DIO M[MA] = IO + 34 DZM M[MA] = 0 + 36 + 40 ADD AC = AC + M[MA] + 42 SUB AC = AC - M[MA] + 44 IDX AC = M[MA] = M[MA] + 1 + 46 ISP AC = M[MA] = M[MA] + 1, skip if AC >= 0 + 50 SAD skip if AC != M[MA] + 52 SAS skip if AC == M[MA] + 54 MUL AC'IO = AC * M[MA] + 56 DIV AC, IO = AC'IO / M[MA] + 60 JMP PC = MA + 62 JSP AC = PC, PC = MA + + Memory reference instructions can access an address space of 64K words. + The address space is divided into sixteen 4K word fields. An + instruction can directly address, via its 12b address, the entire + current field. If extend mode is off, indirect addresses access + the current field, and indirect addressing is multi-level; if off, + they can access all 64K, and indirect addressing is single level. +*/ + +/* The skip format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 1 0| | | | | | | | | | | | | | skip + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | \______/ \______/ + | | | | | | | | + | | | | | | | +---- program flags + | | | | | | +------------- sense switches + | | | | | +------------------- AC == 0 + | | | | +---------------------- AC >= 0 + | | | +------------------------- AC < 0 + | | +---------------------------- OV == 0 + | +------------------------------- IO >= 0 + +------------------------------------- invert skip + + The shift format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 1 1| subopcode | encoded count | shift + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The load immediate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0| S| immediate | LAW + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 70 LAW if S = 0, AC = IR<6:17> + else AC = ~IR<6:17> +*/ + +/* The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 1| W| C| subopcode | device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified subopcode to + specified I/O device. The I/O device may take data from the IO or + return data to the IO, initiate or cancel operations, etc. The + W bit specifies whether the CPU waits for completion, the C bit + whether a completion pulse will be returned from the device. + + The operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 1| | | | | | | | | | | | | | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | \______/ + | | | | | | | | + | | | | | | | +---- PF select + | | | | | | +---------- clear/set PF + | | | | | +------------------- or PC + | | | | +---------------------- clear AC + | | | +------------------------- halt + | | +---------------------------- CMA + | +------------------------------- or TW + +---------------------------------- clear IO + + The operate instruction can be microprogrammed. +*/ + +/* This routine is the instruction decode routine for the PDP-1. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and STOP_INST flag set + XCT loop + indirect address loop + infinite wait state + I/O error in I/O simulator + + 2. Interrupts. With a single channel sequence break system, the + PDP-1 had a single break request flop, b2, here sbs. + If sequence breaks are enabled, and one is not already in + progress, a sequence break occurs. + + 3. Arithmetic. The PDP-1 is a 1's complement system. In 1's + complement arithmetic, a negative number is represented by the + complement (XOR 0777777) of its absolute value. Addition of 1's + complement numbers requires propagating the carry out of the high + order bit back to the low order bit. + + 4. Adding I/O devices. Three modules must be modified: + + pdp1_defs.h add interrupt request definition + pdp1_cpu.c add IOT dispatches + pdp1_sys.c add pointer to data structures to sim_devices +*/ + +#include "pdp1_defs.h" + +#define ILL_ADR_FLAG (1 << ADDRSIZE) +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_MDV (UNIT_V_UF) /* mul/div */ +#define UNIT_MDV (1 << UNIT_V_MDV) +#define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +int32 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC = 0; /* AC */ +int32 IO = 0; /* IO */ +int32 PC = 0; /* PC */ +int32 OV = 0; /* overflow */ +int32 SS = 0; /* sense switches */ +int32 PF = 0; /* program flags */ +int32 TW = 0; /* test word */ +int32 iosta = 0; /* status reg */ +int32 sbs = 0; /* sequence break */ +int32 sbs_init = 0; /* seq break startup */ +int32 ioh = 0; /* I/O halt */ +int32 ioc = 0; /* I/O completion */ +int32 extm = 0; /* ext mem mode */ +int32 extm_init = 0; /* ext mem startup */ +int32 stop_inst = 0; /* stop on rsrv inst */ +int32 xct_max = 16; /* nested XCT limit */ +int32 ind_max = 16; /* nested ind limit */ +int32 old_PC = 0; /* old PC */ +int32 ibkpt_addr = ILL_ADR_FLAG | ADDRMASK; /* breakpoint addr */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +extern int32 sim_int_char; +extern UNIT *sim_clock_queue; +extern int32 ptr (int32 inst, int32 dev, int32 IO); +extern int32 ptp (int32 inst, int32 dev, int32 IO); +extern int32 tti (int32 inst, int32 dev, int32 IO); +extern int32 tto (int32 inst, int32 dev, int32 IO); +extern int32 lpt (int32 inst, int32 dev, int32 IO); +extern t_stat sim_activate (UNIT *uptr, int32 delay); + +int32 sc_map[512] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, /* 00000xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00001xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00010xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00011xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00100xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00101xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00110xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 00111xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 01000xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01001xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01010xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01011xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01100xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01101xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01110xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 01111xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 10000xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10001xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10010xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10011xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10100xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10101xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10110xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11011xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 11000xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11001xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11010xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11011xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11100xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11101xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11110xxxx */ + 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9 /* 11111xxxx */ +}; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, PC, ADDRSIZE) }, + { ORDATA (AC, AC, 18) }, + { ORDATA (IO, IO, 18) }, + { FLDATA (OV, OV, 0) }, + { ORDATA (PF, PF, 6) }, + { ORDATA (SS, SS, 6) }, + { ORDATA (TW, TW, 18) }, + { FLDATA (EXTM, extm, 0) }, + { ORDATA (IOSTA, iosta, 18), REG_RO }, + { FLDATA (SBON, sbs, SB_V_ON) }, + { FLDATA (SBRQ, sbs, SB_V_RQ) }, + { FLDATA (SBIP, sbs, SB_V_IP) }, + { FLDATA (IOH, ioh, 0) }, + { FLDATA (IOC, ioc, 0) }, + { ORDATA (OLDPC, old_PC, ADDRSIZE), REG_RO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (SBS_INIT, sbs_init, SB_V_ON) }, + { FLDATA (EXTM_INIT, extm_init, 0) }, + { FLDATA (MDV, cpu_unit.flags, UNIT_V_MDV), REG_HRO }, + { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, + { DRDATA (IND_MAX, ind_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (BREAK, ibkpt_addr, ADDRSIZE + 1) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_MDV, UNIT_MDV, "multiply/divide", "MDV", NULL }, + { UNIT_MDV, 0, "no multiply/divide", "NOMDV", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, ADDRSIZE, 1, 8, 18, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +int32 IR, MA, op, i, t, xct_count; +int32 sign, signd, v; +int32 dev, io_data, sc, skip; +t_stat reason; +static int32 fs_test[8] = { + 0, 040, 020, 010, 04, 02, 01, 077 }; + +#define EPC_WORD ((OV << 17) | (extm << 16) | PC) +#define INCR_ADDR(x) (((x) & EPCMASK) | (((x) + 1) & DAMASK)) +#define DECR_ADDR(x) (((x) & EPCMASK) | (((x) - 1) & DAMASK)) +#define ABS(x) ((x) ^ (((x) & 0400000)? 0777777: 0)) + +/* Main instruction fetch/decode loop: check events and interrupts */ + +reason = 0; +while (reason == 0) { /* loop until halted */ + +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; } + +if (sbs == (SB_ON | SB_RQ)) { /* interrupt? */ + sbs = SB_ON | SB_IP; /* set in prog flag */ + old_PC = PC; /* save old PC */ + M[0] = AC; /* save state */ + M[1] = EPC_WORD; + M[2] = IO; + PC = 3; /* fetch next from 3 */ + extm = 0; /* extend off */ + OV = 0; } /* clear overflow */ + +if (PC == ibkpt_addr) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save ibkpt */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + +/* Fetch, decode instruction */ + +IR = M[PC]; /* fetch instruction */ +PC = INCR_ADDR (PC); /* increment PC */ +xct_count = 0; /* track nested XCT's */ +sim_interval = sim_interval - 1; + +xct_instr: /* label for XCT */ +if ((IR == 0610001) && ((PC & EPCMASK) == 0) && (sbs & SB_ON)) { + sbs = sbs & ~SB_IP; /* seq debreak */ + old_PC = PC; /* save old PC */ + OV = (M[1] >> 17) & 1; /* restore OV */ + extm = (M[1] >> 16) & 1; /* restore ext mode */ + PC = M[1] & ADDRMASK; /* JMP I 1 */ + continue; } + +op = ((IR >> 13) & 037); /* get opcode */ +if ((op < 032) && (op != 007)) { /* mem ref instr */ + MA = (PC & EPCMASK) | (IR & DAMASK); /* effective address */ + if (IR & IA) { /* indirect addr? */ + if (extm) MA = M[MA] & ADDRMASK; /* if ext, one level */ + else { for (i = 0; i < ind_max; i++) { /* count indirects */ + t = M[MA]; /* get indirect word */ + MA = (PC & EPCMASK) | (t & DAMASK); + if ((t & IA) == 0) break; } + if (i >= ind_max) { /* indirect loop? */ + reason = STOP_IND; + break; } } } } + +switch (op) { /* decode IR<0:4> */ + +/* Logical, load, store instructions */ + +case 001: /* AND */ + AC = AC & M[MA]; + break; +case 002: /* IOR */ + AC = AC | M[MA]; + break; +case 003: /* XOR */ + AC = AC ^ M[MA]; + break; +case 004: /* XCT */ + if (xct_count >= xct_max) { /* too many XCT's? */ + reason = STOP_XCT; + break; } + xct_count = xct_count + 1; /* count XCT's */ + IR = M[MA]; /* get instruction */ + goto xct_instr; /* go execute */ +case 007: /* CAL, JDA */ + MA = (PC & EPCMASK) | ((IR & IA)? (IR & DAMASK): 0100); + old_PC = PC; + M[MA] = AC; + AC = EPC_WORD; + PC = INCR_ADDR (MA); + break; +case 010: /* LAC */ + AC = M[MA]; + break; +case 011: /* LIO */ + IO = M[MA]; + break; +case 012: /* DAC */ + if (MEM_ADDR_OK (MA)) M[MA] = AC; + break; +case 013: /* DAP */ + if (MEM_ADDR_OK (MA)) M[MA] = (AC & DAMASK) | (M[MA] & ~DAMASK); + break; +case 014: /* DIP */ + if (MEM_ADDR_OK (MA)) M[MA] = (AC & ~DAMASK) | (M[MA] & DAMASK); + break; +case 015: /* DIO */ + if (MEM_ADDR_OK (MA)) M[MA] = IO; + break; +case 016: /* DZM */ + if (MEM_ADDR_OK (MA)) M[MA] = 0; + break; + +/* Add, subtract, control + + Add is performed in sequential steps, as follows: + 1. add + 2. end around carry propagate + 3. overflow check + 4. -0 cleanup + + Subtract is performed in sequential steps, as follows: + 1. complement AC + 2. add + 3. end around carry propagate + 4. overflow check + 5. complement AC + Because no -0 check is done, (-0) - (+0) yields a result of -0 +*/ + +case 020: /* ADD */ + t = AC; + AC = AC + M[MA]; + if (AC > 0777777) AC = (AC + 1) & 0777777; /* end around carry */ + if (((~t ^ M[MA]) & (t ^ AC)) & 0400000) OV = 1; + if (AC == 0777777) AC = 0; /* minus 0 cleanup */ + break; +case 021: /* SUB */ + t = AC ^ 0777777; /* complement AC */ + AC = t + M[MA]; /* -AC + MB */ + if (AC > 0777777) AC = (AC + 1) & 0777777; /* end around carry */ + if (((~t ^ M[MA]) & (t ^ AC)) & 0400000) OV = 1; + AC = AC ^ 0777777; /* recomplement AC */ + break; +case 022: /* IDX */ + AC = M[MA] + 1; + if (AC >= 0777777) AC = (AC + 1) & 0777777; + if (MEM_ADDR_OK (MA)) M[MA] = AC; + break; +case 023: /* ISP */ + AC = M[MA] + 1; + if (AC >= 0777777) AC = (AC + 1) & 0777777; + if (MEM_ADDR_OK (MA)) M[MA] = AC; + if (AC < 0400000) PC = INCR_ADDR (PC); + break; +case 024: /* SAD */ + if (AC != M[MA]) PC = INCR_ADDR (PC); + break; +case 025: /* SAS */ + if (AC == M[MA]) PC = INCR_ADDR (PC); + break; +case 030: /* JMP */ + old_PC = PC; + PC = MA; + break; +case 031: /* JSP */ + AC = EPC_WORD; + old_PC = PC; + PC = MA; + break; +case 034: /* LAW */ + AC = (IR & 07777) ^ ((IR & IA)? 0777777: 0); + break; + +/* Multiply and divide + + Multiply and divide step and hardware multiply are exact implementations. + Hardware divide is a 2's complement analog to the actual hardware. +*/ + +case 026: /* MUL */ + if (cpu_unit.flags & UNIT_MDV) { /* hardware? */ + sign = AC ^ M[MA]; /* result sign */ + IO = ABS (AC); /* IO = |AC| */ + v = ABS (M[MA]); /* v = |mpy| */ + for (i = AC = 0; i < 17; i++) { + if (IO & 1) AC = AC + v; + IO = (IO >> 1) | ((AC & 1) << 17); + AC = AC >> 1; } + if ((sign & 0400000) && (AC | IO)) { /* negative, > 0? */ + AC = AC ^ 0777777; + IO = IO ^ 0777777; } } + else { if (IO & 1) AC = AC + M[MA]; /* multiply step */ + if (AC > 0777777) AC = (AC + 1) & 0777777; + if (AC == 0777777) AC = 0; + IO = (IO >> 1) | ((AC & 1) << 17); + AC = AC >> 1; } + break; + +case 027: /* DIV */ + if (cpu_unit.flags & UNIT_MDV) { /* hardware */ + sign = AC ^ M[MA]; /* result sign */ + signd = AC; /* remainder sign */ + if (AC & 0400000) { + AC = AC ^ 0777777; /* AC'IO = |AC'IO| */ + IO = IO ^ 0777777; } + v = ABS (M[MA]); /* v = |divr| */ + if (AC >= v) break; /* overflow? */ + for (i = t = 0; i < 18; i++) { + if (t) AC = (AC + v) & 0777777; + else AC = (AC - v) & 0777777; + t = AC >> 17; + if (i != 17) AC = ((AC << 1) | (IO >> 17)) & 0777777; + IO = ((IO << 1) | (t ^ 1)) & 0777777; } + if (t) AC = (AC + v) & 0777777; /* correct remainder */ + t = ((signd & 0400000) && AC)? AC ^ 0777777: AC; + AC = ((sign & 0400000) && IO)? IO ^ 0777777: IO; + IO = t; + PC = INCR_ADDR (PC); } /* skip */ + else { t = AC >> 17; /* divide step */ + AC = ((AC << 1) | (IO >> 17)) & 0777777; + IO = ((IO << 1) | (t ^ 1)) & 0777777; + if (IO & 1) AC = AC + (M[MA] ^ 0777777); + else AC = AC + M[MA] + 1; + if (AC > 0777777) AC = (AC + 1) & 0777777; + if (AC == 0777777) AC = 0; } + break; + +/* Skip and operate + + Operates execute in the order shown; there are no timing conflicts +*/ + +case 032: /* skip */ + v = (IR >> 3) & 07; /* sense switches */ + t = IR & 07; /* program flags */ + skip = (((IR & 02000) && (IO < 0400000)) || /* SPI */ + ((IR & 01000) && (OV == 0)) || /* SZO */ + ((IR & 00400) && (AC >= 0400000)) || /* SMA */ + ((IR & 00200) && (AC < 0400000)) || /* SPA */ + ((IR & 00100) && (AC == 0)) || /* SZA */ + (v && ((SS & fs_test[v]) == 0)) || /* SZSn */ + (t && ((PF & fs_test[t]) == 0))); /* SZFn */ + if (IR & IA) skip = skip ^ 1; /* invert skip? */ + if (skip) PC = INCR_ADDR (PC); + if (IR & 01000) OV = 0; /* SOV clears OV */ + break; + +case 037: /* operate */ + if (IR & 04000) IO = 0; /* CLI */ + if (IR & 00200) AC = 0; /* CLA */ + if (IR & 02000) AC = AC | TW; /* LAT */ + if (IR & 00100) AC = AC | EPC_WORD; /* LAP */ + if (IR & 01000) AC = AC ^ 0777777; /* CMA */ + if (IR & 00400) reason = STOP_HALT; /* HALT */ + t = IR & 07; /* flag select */ + if (IR & 010) PF = PF | fs_test[t]; /* STFn */ + else PF = PF & ~fs_test[t]; /* CLFn */ + break; + +/* Shifts */ + +case 033: + sc = sc_map[IR & 0777]; /* map shift count */ + switch ((IR >> 9) & 017) { /* case on IR<5:8> */ + case 001: /* RAL */ + AC = ((AC << sc) | (AC >> (18 - sc))) & 0777777; + break; + case 002: /* RIL */ + IO = ((IO << sc) | (IO >> (18 - sc))) & 0777777; + break; + case 003: /* RCL */ + t = AC; + AC = ((AC << sc) | (IO >> (18 - sc))) & 0777777; + IO = ((IO << sc) | (t >> (18 - sc))) & 0777777; + break; + case 005: /* SAL */ + t = (AC & 0400000)? 0777777: 0; + AC = (AC & 0400000) | ((AC << sc) & 0377777) | + (t >> (18 - sc)); + break; + case 006: /* SIL */ + t = (IO & 0400000)? 0777777: 0; + IO = (IO & 0400000) | ((IO << sc) & 0377777) | + (t >> (18 - sc)); + break; + case 007: /* SCL */ + t = (AC & 0400000)? 0777777: 0; + AC = (AC & 0400000) | ((AC << sc) & 0377777) | + (IO >> (18 - sc)); + IO = ((IO << sc) | (t >> (18 - sc))) & 0777777; + break; + case 011: /* RAR */ + AC = ((AC >> sc) | (AC << (18 - sc))) & 0777777; + break; + case 012: /* RIR */ + IO = ((IO >> sc) | (IO << (18 - sc))) & 0777777; + break; + case 013: /* RCR */ + t = IO; + IO = ((IO >> sc) | (AC << (18 - sc))) & 0777777; + AC = ((AC >> sc) | (t << (18 - sc))) & 0777777; + break; + case 015: /* SAR */ + t = (AC & 0400000)? 0777777: 0; + AC = ((AC >> sc) | (t << (18 - sc))) & 0777777; + break; + case 016: /* SIR */ + t = (IO & 0400000)? 0777777: 0; + IO = ((IO >> sc) | (t << (18 - sc))) & 0777777; + break; + case 017: /* SCR */ + t = (AC & 0400000)? 0777777: 0; + IO = ((IO >> sc) | (AC << (18 - sc))) & 0777777; + AC = ((AC >> sc) | (t << (18 - sc))) & 0777777; + break; + default: /* undefined */ + reason = stop_inst; + break; } /* end switch shifts */ + break; + +/* IOT */ + +case 035: + if (IR & IO_WAIT) { /* wait? */ + if (ioh) { /* I/O halt? */ + if (ioc) ioh = 0; /* comp pulse? done */ + else { sim_interval = 0; /* force event */ + PC = DECR_ADDR (PC); } /* re-execute */ + break; } /* skip iot */ + ioh = 1; /* turn on halt */ + PC = DECR_ADDR (PC); } /* re-execute */ + dev = IR & 077; /* get dev addr */ + io_data = IO; /* default data */ + switch (dev) { /* case on dev */ + case 000: /* I/O wait */ + break; + case 001: case 002: case 030: /* paper tape rdr */ + io_data = ptr (IR, dev, IO); + break; + case 003: /* typewriter */ + io_data = tto (IR, dev, IO); + break; + case 004: /* keyboard */ + io_data = tti (IR, dev, IO); + break; + case 005: case 006: /* paper tape punch */ + io_data = ptp (IR, dev, IO); + break; + case 033: /* check status */ + io_data = iosta | ((sbs & SB_ON)? IOS_SQB: 0); + break; + case 045: /* line printer */ + io_data = lpt (IR, dev, IO); + break; + case 054: /* seq brk off */ + sbs = sbs & ~SB_ON; + break; + case 055: /* seq brk on */ + sbs = sbs | SB_ON; + break; + case 056: /* clear seq brk */ + sbs = sbs & ~SB_IP; + break; + case 074: /* extend mode */ + extm = (IR >> 11) & 1; /* set from IR<6> */ + break; + default: /* undefined */ + reason = stop_inst; + break; } /* end switch dev */ + IO = io_data & 0777777; + if (io_data >= IOT_REASON) reason = io_data >> IOT_V_REASON; + break; +default: /* undefined */ + reason = STOP_RSRV; /* halt */ + break; } /* end switch opcode */ +} /* end while */ +return reason; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +sbs = sbs_init; +extm = extm_init; +ioh = ioc = 0; +OV = 0; +PF = 0; +return cpu_svc (&cpu_unit); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 0777777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & 0777777; +return SCPE_OK; +} + +/* Service breakpoint */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} diff --git a/pdp1_defs.h b/pdp1_defs.h new file mode 100644 index 00000000..89032d04 --- /dev/null +++ b/pdp1_defs.h @@ -0,0 +1,100 @@ +/* pdp1_defs.h: 18b PDP simulator definitions + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + + The PDP-1 was Digital's first computer. The system design evolved during + its life, and as a result, specifications are sketchy or contradictory. + This simulator is based on the 1963 maintenance manual. + + This simulator implements the following options: + + Automatic multiply/divide Type 10 + Memory extension control Type 15 + Line printer control Type 62 +*/ + +#include "sim_defs.h" + +/* Memory and peripheral configuration */ + +#define ADDRSIZE 16 /* address bits */ +#define MAXMEMSIZE (1u << ADDRSIZE) /* max mem size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ +#define DAMASK 007777 /* direct addr */ +#define EPCMASK (ADDRMASK & ~DAMASK) /* extended addr */ +#define IA 010000 /* indirect flag */ +#define IO_WAIT 010000 /* I/O sync wait */ +#define IO_CPLS 004000 /* cmopletion pulse */ +#define GEN_CPLS(x) (((x) ^ ((x) << 1)) & IO_WAIT) /* completion pulse? */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE) + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_XCT 4 /* nested XCT's */ +#define STOP_IND 5 /* nested indirects */ +#define STOP_WAIT 6 /* wait state */ + +/* IOT subroutine return codes */ + +#define IOT_V_REASON 18 /* reason */ +#define IOT_REASON (1 << IOT_V_REASON) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* I/O status flags */ + +#define IOS_V_LPN 17 /* light pen */ +#define IOS_V_PTR 16 /* paper tape reader */ +#define IOS_V_TTO 15 /* typewriter out */ +#define IOS_V_TTI 14 /* typewriter in */ +#define IOS_V_PTP 13 /* paper tape punch */ +#define IOS_V_DRM 12 /* drum */ +#define IOS_V_SQB 11 /* sequence break */ +#define IOS_V_PNT 2 /* print done */ +#define IOS_V_SPC 1 /* space done */ + +#define IOS_LPN (1 << IOS_V_LPN) +#define IOS_PTR (1 << IOS_V_PTR) +#define IOS_TTO (1 << IOS_V_TTO) +#define IOS_TTI (1 << IOS_V_TTI) +#define IOS_PTP (1 << IOS_V_PTP) +#define IOS_DRM (1 << IOS_V_DRM) +#define IOS_SQB (1 << IOS_V_SQB) +#define IOS_PNT (1 << IOS_V_PNT) +#define IOS_SPC (1 << IOS_V_SPC) + +/* Sequence break flags */ + +#define SB_V_IP 0 /* in progress */ +#define SB_V_RQ 1 /* request */ +#define SB_V_ON 2 /* enabled */ + +#define SB_IP (1 << SB_V_IP) +#define SB_RQ (1 << SB_V_RQ) +#define SB_ON (1 << SB_V_ON) diff --git a/pdp1_lp.c b/pdp1_lp.c new file mode 100644 index 00000000..2d84288d --- /dev/null +++ b/pdp1_lp.c @@ -0,0 +1,165 @@ +/* pdp1_lp.c: PDP-1 line printer simulator + + Copyright (c) 1993-1999, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt Type 62 line printer for the PDP-1 +*/ + +#include "pdp1_defs.h" +#define BPTR_MAX 40 /* pointer max */ +#define LPT_BSIZE (BPTR_MAX * 3) /* line size */ +#define BPTR_MASK 077 /* buf ptr mask */ +extern int32 ioc, sbs, iosta; +int32 lpt_rpls = 0, lpt_iot = 0, lpt_stopioe = 0, bptr = 0; +unsigned char lpt_buf[LPT_BSIZE + 1] = { 0 }; + +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (PNT, iosta, IOS_V_PNT) }, + { FLDATA (SPC, iosta, IOS_V_SPC) }, + { FLDATA (RPLS, lpt_rpls, 0) }, + { DRDATA (BPTR, bptr, 6) }, + { ORDATA (LPT_STATE, lpt_iot, 6), REG_HRO }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { BRDATA (**BUF, lpt_buf, 8, 8, LPT_BSIZE), REG_HRO }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL }; + +/* Line printer IOT routine */ + +int32 lpt (int32 inst, int32 dev, int32 data) +{ +int32 i; + +static const unsigned char lpt_trans[64] = { + ' ','1','2','3','4','5','6','7','8','9','\'','~','#','V','^','<', + '0','/','S','T','U','V','W','X','Y','Z','"',',','>','^','-','?', + '@','J','K','L','M','N','O','P','Q','R','$','=','-',')','-','(', + '_','A','B','C','D','E','F','G','H','I','*','.','+',']','|','[' }; + +if ((inst & 0700) == 0100) { /* fill buf */ + if (bptr < BPTR_MAX) { /* limit test ptr */ + i = bptr * 3; /* cvt to chr ptr */ + lpt_buf[i++] = lpt_trans[(data >> 12) & 077]; + lpt_buf[i++] = lpt_trans[(data >> 6) & 077]; + lpt_buf[i++] = lpt_trans[data & 077]; } + bptr = (bptr + 1) & BPTR_MASK; + return data; } +lpt_rpls = 0; +if ((inst & 0700) == 0200) { /* space */ + iosta = iosta & ~IOS_SPC; /* space, clear flag */ + lpt_iot = (inst >> 6) & 077; } /* state = space n */ +else { iosta = iosta & ~IOS_PNT; /* clear flag */ + lpt_iot = 0; } /* state = print */ +if (GEN_CPLS (inst)) { /* comp pulse? */ + ioc = 0; /* clear flop */ + lpt_rpls = 1; } /* request completion */ +sim_activate (&lpt_unit, lpt_unit.wait); /* activate */ +return data; +} + +/* Unit service, printer is in one of three states + + lpt_iot = 000 write buffer to file, set state to + lpt_iot = 010 write cr, then write buffer to file + lpt_iot = 02x space command x, then set state to 0 +*/ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i; +static const char *lpt_cc[] = { + "\n", + "\n\n", + "\n\n\n", + "\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f" }; + +sbs = sbs | SB_RQ; /* req seq break */ +ioc = ioc | lpt_rpls; /* restart */ +if (lpt_iot & 020) { /* space? */ + iosta = iosta | IOS_SPC; /* set flag */ + if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); + fputs (lpt_cc[lpt_iot & 07], lpt_unit.fileref); /* print cctl */ + if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } + lpt_iot = 0; } /* clear state */ +else { iosta = iosta | IOS_PNT; /* print */ + if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); + if (lpt_iot & 010) fputc ('\r', lpt_unit.fileref); + fputs (lpt_buf, lpt_unit.fileref); /* print buffer */ + if (ferror (lpt_unit.fileref)) { /* test error */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } + bptr = 0; + for (i = 0; i <= LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ + lpt_iot = 010; } /* set state */ +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +int32 i; + +iosta = iosta & ~(IOS_PNT | IOS_SPC); /* clear flags */ +bptr = 0; /* clear buffer ptr */ +for (i = 0; i <= LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ +lpt_iot = 0; /* clear state */ +lpt_rpls = 0; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/pdp1_stddev.c b/pdp1_stddev.c new file mode 100644 index 00000000..a8cc55bd --- /dev/null +++ b/pdp1_stddev.c @@ -0,0 +1,418 @@ +/* pdp1_stddev.c: PDP-1 standard devices + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + tti keyboard + tto teleprinter + + 30-Oct-00 RMS Standardized device naming +*/ + +#include "pdp1_defs.h" + +#define FIODEC_UC 074 +#define FIODEC_LC 072 +#define UC 0100 /* upper case */ +#define BOTH 0200 /* both cases */ +#define CW 0400 /* char waiting */ +#define TT_WIDTH 077 +extern int32 sbs, ioc, iosta, PF, IO, PC; +extern int32 M[]; +int32 ptr_rpls = 0, ptr_stopioe = 0, ptr_state = 0; +int32 ptp_rpls = 0, ptp_stopioe = 0; +int32 tti_state = 0; +int32 tto_rpls = 0, tto_state = 0; +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* Character translation tables */ + +int32 fiodec_to_ascii[128] = { + ' ', '1', '2', '3', '4', '5', '6', '7', /* lower case */ + '8', '9', 0, 0, 0, 0, 0, 0, + '0', '/', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', 0, ',', 0, 0, '\t', 0, + '@', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 0, 0, '-', ')', '\\', '(', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', '{', '.', '}', '\b', 0, '\r', + ' ', '"', '\'', '~', '#', '!', '&', '<', /* upper case */ + '>', '^', 0, 0, 0, 0, 0, 0, + '`', '?', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 0, '=', 0, 0, '\t', 0, + '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 0, 0, '+', ']', '|', '[', + 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '{', '*', '}', '\b', 0, '\r' }; + +int32 ascii_to_fiodec[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, + BOTH+075, BOTH+036, 0, 0, 0, BOTH+077, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + BOTH+0, UC+005, UC+001, UC+004, 0, 0, UC+006, UC+002, + 057, 055, UC+073, UC+054, 033, 054, 073, 021, + 020, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 0, 0, UC+007, UC+033, UC+010, UC+021, + 040, UC+061, UC+062, UC+063, UC+064, UC+065, UC+066, UC+067, + UC+070, UC+071, UC+041, UC+042, UC+043, UC+044, UC+045, UC+046, + UC+047, UC+050, UC+051, UC+022, UC+023, UC+024, UC+025, UC+026, + UC+027, UC+030, UC+031, UC+057, 056, UC+055, UC+011, UC+040, + UC+020, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 0, UC+056, 0, UC+003, BOTH+075 }; + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 18) }, + { FLDATA (DONE, iosta, IOS_V_PTR) }, + { FLDATA (RPLS, ptr_rpls, 0) }, + { ORDATA (STATE, ptr_state, 5), REG_HRO }, + { DRDATA (POS, ptr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (DONE, iosta, IOS_V_PTP) }, + { FLDATA (RPLS, ptp_rpls, 0) }, + { DRDATA (POS, ptp_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit + tti_reg TTI register list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 6) }, + { FLDATA (DONE, iosta, IOS_V_TTI) }, + { ORDATA (STATE, tti_state, 10), REG_HRO }, + { DRDATA (POS, tti_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 6) }, + { FLDATA (DONE, iosta, IOS_V_TTO) }, + { FLDATA (RPLS, tto_rpls, 0) }, + { ORDATA (STATE, tto_state, 10), REG_HRO }, + { DRDATA (POS, tto_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL }; + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 inst, int32 dev, int32 data) +{ +iosta = iosta & ~IOS_PTR; /* clear flag */ +if (dev == 0030) return ptr_unit.buf; /* RRB */ +ptr_state = (dev == 0002)? 18: 0; /* mode = bin/alp */ +ptr_rpls = 0; +ptr_unit.buf = 0; /* clear buffer */ +sim_activate (&ptr_unit, ptr_unit.wait); +if (GEN_CPLS (inst)) { /* comp pulse? */ + ioc = 0; + ptr_rpls = 1; } +return data; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; } +ptr_unit.pos = ptr_unit.pos + 1; +if (ptr_state == 0) ptr_unit.buf = temp & 0377; /* alpha */ +else if (temp & 0200) { /* binary */ + ptr_state = ptr_state - 6; + ptr_unit.buf = ptr_unit.buf | ((temp & 077) << ptr_state); } +if (ptr_state == 0) { /* done? */ + if (ptr_rpls) IO = ptr_unit.buf; /* restart? fill IO */ + iosta = iosta | IOS_PTR; /* set flag */ + sbs = sbs | SB_RQ; /* req seq break */ + ioc = ioc | ptr_rpls; } /* restart */ +else sim_activate (&ptr_unit, ptr_unit.wait); /* get next char */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_state = 0; /* clear state */ +ptr_unit.buf = 0; +ptr_rpls = 0; +iosta = iosta & ~IOS_PTR; /* clear flag */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 07772 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 0730002, /* r, rpb + wait */ + 0327776, /* dio x */ + 0107776, /* xct x */ + 0730002, /* rpb + wait */ + 0760400, /* x, halt / + 0607772 /* jmp r */ +}; + +t_stat ptr_boot (int32 unitno) +{ +int32 i; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +PC = BOOT_START; +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 inst, int32 dev, int32 data) +{ +iosta = iosta & ~IOS_PTP; /* clear flag */ +ptp_rpls = 0; +ptp_unit.buf = (dev == 0006)? ((data >> 12) | 0200): (data & 0377); +sim_activate (&ptp_unit, ptp_unit.wait); /* start unit */ +if (GEN_CPLS (inst)) { /* comp pulse? */ + ioc = 0; + ptp_rpls = 1; } +return data; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +iosta = iosta | IOS_PTP; /* set flag */ +sbs = sbs | SB_RQ; /* req seq break */ +ioc = ioc | ptp_rpls; /* process restart */ +if ((ptp_unit.flags & UNIT_ATT) == 0) /* not attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* I/O error? */ + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; /* clear state */ +ptp_rpls = 0; +iosta = iosta & ~IOS_PTP; /* clear flag */ +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Terminal input: IOT routine */ + +int32 tti (int32 inst, int32 dev, int32 data) +{ +iosta = iosta & ~IOS_TTI; /* clear flag */ +if (inst & (IO_WAIT | IO_CPLS)) /* wait or sync? */ + return (STOP_RSRV << IOT_V_REASON) | (tti_unit.buf & 077); +return tti_unit.buf & 077; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 in, temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if (tti_state & CW) { /* char waiting? */ + tti_unit.buf = tti_state & TT_WIDTH; /* return char */ + tti_state = tti_state & ~CW; } /* not waiting */ +else { if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; + temp = temp & 0177; + if (temp == 0177) temp = '\b'; /* rubout? bs */ + sim_putchar (temp); /* echo */ + if (temp == '\r') sim_putchar ('\n'); /* cr? add nl */ + in = ascii_to_fiodec[temp]; /* translate char */ + if (in == 0) return SCPE_OK; /* no xlation? */ + if ((in & BOTH) || ((in & UC) == (tti_state & UC))) + tti_unit.buf = in & TT_WIDTH; + else { tti_unit.buf = (in & UC)? FIODEC_UC: FIODEC_LC; + tti_state = in | CW; } } /* set 2nd waiting */ +iosta = iosta | IOS_TTI; /* set flag */ +sbs = sbs | SB_RQ; /* req seq break */ +PF = PF | 040; /* set prog flag 1 */ +tti_unit.pos = tti_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; /* clear buffer */ +tti_state = 0; /* clear state */ +iosta = iosta & ~IOS_TTI; /* clear flag */ +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 inst, int32 dev, int32 data) +{ +iosta = iosta & ~IOS_TTO; /* clear flag */ +tto_rpls = 0; +tto_unit.buf = data & TT_WIDTH; /* load buffer */ +sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ +if (GEN_CPLS (inst)) { /* comp pulse? */ + ioc = 0; + tto_rpls = 1; } +return data; +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 out; + +iosta = iosta | IOS_TTO; /* set flag */ +sbs = sbs | SB_RQ; /* req seq break */ +ioc = ioc | tto_rpls; /* process restart */ +if (tto_unit.buf == FIODEC_UC) { /* upper case? */ + tto_state = UC; + return SCPE_OK; } +if (tto_unit.buf == FIODEC_LC) { /* lower case? */ + tto_state = 0; + return SCPE_OK; } +out = fiodec_to_ascii[tto_unit.buf | tto_state]; /* translate */ +if (out == 0) return SCPE_OK; /* no translation? */ +sim_putchar (out); +tto_unit.pos = tto_unit.pos + 1; +if (out == '\r') { /* cr? add lf */ + sim_putchar ('\n'); + tto_unit.pos = tto_unit.pos + 1; } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; /* clear buffer */ +tto_state = 0; /* clear state */ +tto_rpls = 0; +iosta = iosta & ~IOS_TTO; /* clear flag */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/pdp1_sys.c b/pdp1_sys.c new file mode 100644 index 00000000..73ecb769 --- /dev/null +++ b/pdp1_sys.c @@ -0,0 +1,443 @@ +/* pdp1_sys.c: PDP-1 simulator interface + + Copyright (c) 1993-2000, Robert M. Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface + 20-Oct-97 RMS Fixed endian-dependence in RIM loader + (found by Michael Somos) +*/ + +#include "pdp1_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE lpt_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 M[]; +extern int32 PC; +extern int32 ascii_to_fiodec[], fiodec_to_ascii[]; +extern int32 sc_map[]; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-1"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { &cpu_dev, + &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, + &lpt_dev, NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Undefined instruction", + "HALT instruction", + "Breakpoint", + "Nested XCT's", + "Nested indirect addresses", + "Infinite wait state" }; + +/* Binary loader + + At the moment, implements RIM loader format +*/ + +int32 getword (FILE *fileref) +{ +int32 i, tmp, word; + +word = 0; +for (i = 0; i < 3;) { + if ((tmp = getc (fileref)) == EOF) return -1; + if (tmp & 0200) { + word = (word << 6) | (tmp & 077); + i++; } } +return word; +} + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 origin, val; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +for (;;) { + if ((val = getword (fileref)) < 0) return SCPE_FMT; + if ((val & 0770000) == 0240000) { /* DAC? */ + origin = val & 07777; + if ((val = getword (fileref)) < 0) return SCPE_FMT; + if (MEM_ADDR_OK (origin)) M[origin++] = val; } + else if ((val & 0770000) == 0600000) { + PC = val & 007777; + return SCPE_OK; } } +return SCPE_FMT; /* error */ +} + +/* Symbol tables */ + +#define I_V_FL 18 /* inst class */ +#define I_M_FL 07 /* class mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_IOT 1 /* IOT */ +#define I_V_LAW 2 /* LAW */ +#define I_V_MRF 3 /* memory reference */ +#define I_V_MRI 4 /* mem ref no ind */ +#define I_V_OPR 5 /* OPR */ +#define I_V_SKP 6 /* skip */ +#define I_V_SHF 7 /* shift */ +#define I_NPN (I_V_NPN << I_V_FL) /* no operand */ +#define I_IOT (I_V_IOT << I_V_FL) /* IOT */ +#define I_LAW (I_V_LAW << I_V_FL) /* LAW */ +#define I_MRF (I_V_MRF << I_V_FL) /* memory reference */ +#define I_MRI (I_V_MRI << I_V_FL) /* mem ref no ind */ +#define I_OPR (I_V_OPR << I_V_FL) /* OPR */ +#define I_SKP (I_V_SKP << I_V_FL) /* skip */ +#define I_SHF (I_V_SHF << I_V_FL) /* shift */ + +static const int32 masks[] = { + 0777777, 0763777, 0760000, 0760000, + 0770000, 0760017, 0760077, 0777000 }; + +static const char *opcode[] = { + "AND", "IOR", "XOR", "XCT", /* mem refs */ + "LAC", "LIO", "DAC", "DAP", + "DIP", "DIO", "DZM", "ADD", + "SUB", "IDX", "ISP", "SAD", + "SAS", "MUL", "DIV", "JMP", "JSP", + + "CAL", "JDA", /* mem ref no ind */ + + "IOH", "RPA", "RPB", "RRB", /* I/O instructions */ + "PPA", "PPB", "TYO", "TYI", + "LSM", "ESM", "CBS", + "LEM", "EEM", "CKS", + + "SKP", "SKP I", "CLO", + "SFT", "LAW", "OPR", + + "RAL", "RIL", "RCL", /* shifts */ + "SAL", "SIL", "SCL", + "RAR", "RIR", "RCR", + "SAR", "SIR", "SCR", + + "SZF1", "SZF2", "SZF3", /* skips */ + "SZF4", "SZF5", "SZF6", "SZF7", + "SZS1", "SZS1 SZF1", "SZS1 SZF2", "SZS1 SZ3", + "SZS1 SZF4", "SZS1 SZF5", "SZS1 SZF6", "SZS1 SZF7", + "SZS2", "SZS2 SZF1", "SZS2 SZF2", "SZS2 SZ3", + "SZS2 SZF4", "SZS2 SZF5", "SZS2 SZF6", "SZS2 SZF7", + "SZS3", "SZS3 SZF1", "SZS3 SZF2", "SZS3 SZ3", + "SZS3 SZF4", "SZS3 SZF5", "SZS3 SZF6", "SZS3 SZF7", + "SZS4", "SZS4 SZF1", "SZS4 SZF2", "SZS4 SZ3", + "SZS4 SZF4", "SZS4 SZF5", "SZS4 SZF6", "SZS4 SZF7", + "SZS5", "SZS5 SZF1", "SZS5 SZF2", "SZS5 SZ3", + "SZS5 SZF4", "SZS5 SZF5", "SZS5 SZF6", "SZS5 SZF7", + "SZS6", "SZS6 SZF1", "SZS6 SZF2", "SZS6 SZ3", + "SZS6 SZF4", "SZS6 SZF5", "SZS6 SZF6", "SZS6 SZF7", + "SZS7", "SZS7 SZF1", "SZS7 SZF2", "SZS7 SZ3", + "SZS7 SZF4", "SZS7 SZF5", "SZS7 SZF6", "SZS7 SZF7", + + "CLF1", "CLF2", "CLF3", /* operates */ + "CLF4", "CLF5", "CLF6", "CLF7", + "STF1", "STF2", "STF3", + "STF4", "STF5", "STF6", "STF7", + + "SZA", "SPA", "SMA", /* encode only */ + "SZO", "SPI", "I", + "LAP", "CLA", "HLT", + "CMA", "LAT", "CLI", + NULL, NULL, /* decode only */ + NULL }; + +static const int32 opc_val[] = { + 0020000+I_MRF, 0040000+I_MRF, 0060000+I_MRF, 0100000+I_MRF, + 0200000+I_MRF, 0220000+I_MRF, 0240000+I_MRF, 0260000+I_MRF, + 0300000+I_MRF, 0320000+I_MRF, 0340000+I_MRF, 0400000+I_MRF, + 0420000+I_MRF, 0440000+I_MRF, 0460000+I_MRF, 0500000+I_MRF, + 0520000+I_MRF, 0540000+I_MRF, 0560000+I_MRF, 0600000+I_MRF, 0620000+I_MRF, + + 0160000+I_MRI, 0170000+I_MRI, + + 0730000+I_NPN, 0720001+I_IOT, 0720002+I_IOT, 0720030+I_IOT, + 0720005+I_IOT, 0720006+I_IOT, 0720003+I_IOT, 0720004+I_IOT, + 0720054+I_NPN, 0720055+I_NPN, 0720056+I_NPN, + 0720074+I_NPN, 0724074+I_NPN, 0720033+I_NPN, + + 0640000+I_NPN, 0650000+I_NPN, 0651600+I_NPN, + 0660000+I_NPN, 0700000+I_LAW, 0760000+I_NPN, + + 0661000+I_SHF, 0662000+I_SHF, 0663000+I_SHF, + 0665000+I_SHF, 0666000+I_SHF, 0667000+I_SHF, + 0671000+I_SHF, 0672000+I_SHF, 0673000+I_SHF, + 0675000+I_SHF, 0676000+I_SHF, 0677000+I_SHF, + + 0640001+I_SKP, 0640002+I_SKP, 0640003+I_SKP, + 0640004+I_SKP, 0640005+I_SKP, 0640006+I_SKP, 0640007+I_SKP, + 0640010+I_SKP, 0640011+I_SKP, 0640012+I_SKP, 0640013+I_SKP, + 0640014+I_SKP, 0640015+I_SKP, 0640016+I_SKP, 0640017+I_SKP, + 0640020+I_SKP, 0640021+I_SKP, 0640022+I_SKP, 0640023+I_SKP, + 0640024+I_SKP, 0640025+I_SKP, 0640026+I_SKP, 0640027+I_SKP, + 0640030+I_SKP, 0640031+I_SKP, 0640032+I_SKP, 0640033+I_SKP, + 0640034+I_SKP, 0640035+I_SKP, 0640036+I_SKP, 0640037+I_SKP, + 0640040+I_SKP, 0640041+I_SKP, 0640042+I_SKP, 0640043+I_SKP, + 0640044+I_SKP, 0640045+I_SKP, 0640046+I_SKP, 0640047+I_SKP, + 0640050+I_SKP, 0640051+I_SKP, 0640052+I_SKP, 0640053+I_SKP, + 0640054+I_SKP, 0640055+I_SKP, 0640056+I_SKP, 0640057+I_SKP, + 0640060+I_SKP, 0640061+I_SKP, 0640062+I_SKP, 0640063+I_SKP, + 0640064+I_SKP, 0640065+I_SKP, 0640066+I_SKP, 0640067+I_SKP, + 0640070+I_SKP, 0640071+I_SKP, 0640072+I_SKP, 0640073+I_SKP, + 0640074+I_SKP, 0640075+I_SKP, 0640076+I_SKP, 0640077+I_SKP, + + 0760001+I_OPR, 0760002+I_OPR, 0760003+I_OPR, + 0760004+I_OPR, 0760005+I_OPR, 0760006+I_OPR, 0760007+I_OPR, + 0760011+I_OPR, 0760012+I_OPR, 0760013+I_OPR, + 0760014+I_OPR, 0760015+I_OPR, 0760016+I_OPR, 0760017+I_OPR, + + 0640100+I_SKP, 0640200+I_SKP, 0640400+I_SKP, /* encode only */ + 0641000+I_SKP, 0642000+I_SKP, 0010000+I_SKP, + 0760100+I_OPR, 0760200+I_OPR, 0760400+I_OPR, + 0761000+I_OPR, 0762000+I_OPR, 0764000+I_OPR, + + 0640000+I_SKP, 0760000+I_OPR, /* decode only */ + -1 }; + +/* Operate or skip decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed? +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; } } +return sp; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) fiodec_to_ascii[x] +#define ASCTOSIX(x) (ascii_to_fiodec[x] & 077) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, sp, inst, disp, ma; + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, "%c", SIXTOASC ((inst >> 12) & 077)); + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +disp = inst & 007777; +ma = (addr & EPCMASK) | disp; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0777777) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + case I_V_IOT: /* IOT */ + disp = (inst - opc_val[i]) & 017777; + if (disp == IA) fprintf (of, "%s I", opcode[i]); + else if (disp) fprintf (of, "%s %-o", opcode[i], disp); + else fprintf (of, "%s", opcode[i]); + break; + case I_V_LAW: /* LAW */ + cflag = 0; /* fall thru to MRF */ + case I_V_MRF: /* mem ref */ + fprintf (of, "%s%s%-o", opcode[i], + ((inst & IA)? " I ": " "), (cflag? ma: disp)); + break; + case I_V_MRI: /* mem ref no ind */ + fprintf (of, "%s %-o", opcode[i], (cflag? ma: disp)); + break; + case I_V_OPR: /* operates */ + sp = fprint_opr (of, inst & 007700, j, 0); + if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + case I_V_SKP: /* skips */ + sp = fprint_opr (of, inst & 007700, j, 0); + if (opcode[i]) sp = fprintf (of, (sp? " %s": "%s"), opcode[i]); + if (inst & IA) fprintf (of, sp? " I": "I"); + break; + case I_V_SHF: /* shifts */ + fprintf (of, "%s %-d", opcode[i], sc_map[inst & 0777]); + break; } /* end case */ + return SCPE_OK; } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get 18b signed number + + Inputs: + *cptr = pointer to input string + *sign = pointer to sign + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_sint (char *cptr, int32 *sign, t_stat *status) +{ +*sign = 1; +if (*cptr == '+') { + *sign = 0; + cptr++; } +else if (*cptr == '-') { + *sign = -1; + cptr++; } +return get_uint (cptr, 8, 0777777, status); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k, sign; +t_stat r; +static int32 sc_enc[10] = { 0, 01, 03, 07, 017, 037, 077, 0177, 0377, 0777 }; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 1; (i < 3) && (cptr[i] != 0); i++) + if (cptr[i] == 0) for (j = i + 1; j <= 3; j++) cptr[j] = 0; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (ASCTOSIX (cptr[0] & 077) << 12) | + (ASCTOSIX (cptr[1] & 077) << 6) | + ASCTOSIX (cptr[2] & 077); + return SCPE_OK; } + +/* Symbolic input, continued */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0777777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ +case I_V_LAW: /* LAW */ + cflag = 0; /* fall through */ +case I_V_MRF: case I_V_MRI: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((j != I_V_MRI) && strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | IA; + cptr = get_glyph (cptr, gbuf, 0); } + d = get_uint (gbuf, 8, ADDRMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (d <= DAMASK) val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & EPCMASK) == 0)) + val[0] = val[0] | (d & DAMASK); + else return SCPE_ARG; + break; +case I_V_SHF: /* shift */ + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 10, 9, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | sc_enc[d]; + break; +case I_V_NPN: case I_V_IOT: case I_V_OPR: case I_V_SKP: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if (opcode[i] != NULL) { + k = opc_val[i] & 0777777; + if ((k != IA) && (((k ^ val[0]) & 0760000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; } + else { d = get_sint (gbuf, &sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (sign == 0) val[0] = val[0] + d; + else if (sign < 0) val[0] = val[0] - d; + else val[0] = val[0] | d; } } + break; } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/pdp4.c b/pdp4.c new file mode 100644 index 00000000..05342b04 --- /dev/null +++ b/pdp4.c @@ -0,0 +1 @@ +#define PDP4 0 diff --git a/pdp7.c b/pdp7.c new file mode 100644 index 00000000..39fb8f88 --- /dev/null +++ b/pdp7.c @@ -0,0 +1 @@ +#define PDP7 0 diff --git a/pdp8_clk.c b/pdp8_clk.c new file mode 100644 index 00000000..6e97b1e3 --- /dev/null +++ b/pdp8_clk.c @@ -0,0 +1,118 @@ +/* pdp8_clk.c: PDP-8 real-time clock simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + clk real-time clock + + Note: includes the IOT's for both the PDP-8/E and PDP-8/A clocks +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, dev_enable, dev_done, stop_inst; +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 5000 }; + +REG clk_reg[] = { + { FLDATA (DONE, dev_done, INT_V_CLK) }, + { FLDATA (ENABLE, dev_enable, INT_V_CLK) }, + { FLDATA (INT, int_req, INT_V_CLK) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL }; + +/* IOT routine + + IOT's 6131-6133 are the PDP-8/E clock + IOT's 6135-6137 the PDP-8/A clock +*/ + +int32 clk (int32 pulse, int32 AC) +{ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* CLEI */ + dev_enable = dev_enable | INT_CLK; /* enable clk ints */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 2: /* CLDI */ + dev_enable = dev_enable & ~INT_CLK; /* disable clk ints */ + int_req = int_req & ~INT_CLK; /* update interrupts */ + return AC; +case 3: /* CLSC */ + if (dev_done & INT_CLK) { /* flag set? */ + dev_done = dev_done & ~INT_CLK; /* clear flag */ + int_req = int_req & ~INT_CLK; /* clear int req */ + return IOT_SKP + AC; } + return AC; +case 5: /* CLLE */ + if (AC & 1) dev_enable = dev_enable | INT_CLK; /* test AC<11> */ + else dev_enable = dev_enable & ~INT_CLK; + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 6: /* CLCL */ + dev_done = dev_done & ~INT_CLK; /* clear flag */ + int_req = int_req & ~INT_CLK; /* clear int req */ + return AC; +case 7: /* CLSK */ + return (dev_done & INT_CLK)? IOT_SKP + AC: AC; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_CLK; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +sim_activate (&clk_unit, clk_unit.wait); /* reactivate unit */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +dev_done = dev_done & ~INT_CLK; /* clear done, int */ +int_req = int_req & ~INT_CLK; +dev_enable = dev_enable & ~INT_CLK; /* clear enable */ +sim_activate (&clk_unit, clk_unit.wait); /* activate unit */ +return SCPE_OK; +} diff --git a/pdp8_cpu.c b/pdp8_cpu.c new file mode 100644 index 00000000..16766c1a --- /dev/null +++ b/pdp8_cpu.c @@ -0,0 +1,1093 @@ +/* pdp8_cpu.c: PDP-8 CPU simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + + The register state for the PDP-8 is: + + AC<0:11> accumulator + MQ<0:11> multiplier-quotient + L link flag + PC<0:11> program counter + IF<0:2> instruction field + IB<0:2> instruction buffer + DF<0:2> data field + UF user flag + UB user buffer + SF<0:6> interrupt save field + + The PDP-8 has three instruction formats: memory reference, I/O transfer, + and operate. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in|zr| page offset | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:2> mnemonic action + + 000 AND AC = AC & M[MA] + 001 TAD L'AC = AC + M[MA] + 010 DCA M[MA] = AC, AC = 0 + 011 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 100 JMS M[MA] = PC, PC = MA + 1 + 101 JMP PC = MA + + <3:4> mode action + 00 page zero MA = IF'0'IR<5:11> + 01 current page MA = IF'PC<0:4>'IR<5:11> + 10 indirect page zero MA = xF'M[IF'0'IR<5:11>] + 11 indirect current page MA = xF'M[IF'PC<0:4>'IR<5:11>] + + where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP. + + Memory reference instructions can access an address space of 32K words. + The address space is divided into eight 4K word fields; each field is + divided into thirty-two 128 word pages. An instruction can directly + address, via its 7b offset, locations 0-127 on page zero or on the current + page. All 32k words can be accessed via indirect addressing and the + instruction and data field registers. If an indirect address is in + locations 0010-0017 of any field, the indirect address is incremented + and rewritten to memory before use. +*/ + +/* The I/O transfer format is as follows: + + 0 1 2 3 4 5 6 7 8 9 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | op | device | pulse | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified pulse to the + specified I/O device. The I/O device may take data from the AC, + return data to the AC, initiate or cancel operations, or skip on + status. + + The operate format is as follows: + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 0| | | | | | | | | operate group 1 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | + | | | | | | | +--- increment AC 3 + | | | | | | +--- rotate 1 or 2 4 + | | | | | +--- rotate left 4 + | | | | +--- rotate right 4 + | | | +--- complement L 2 + | | +--- complement AC 2 + | +--- clear L 1 + +-- clear AC 1 + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 1| | | | | | | | 0| operate group 2 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | + | | | | | | +--- halt 3 + | | | | | +--- or switch register 3 + | | | | +--- reverse skip sense 1 + | | | +--- skip on L != 0 1 + | | +--- skip on AC == 0 1 + | +--- skip on AC < 0 1 + +-- clear AC 2 + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 1| | | | | | | | 1| operate group 3 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | \______/ + | | | | | + | | +--|-----+--- EAE command 3 + | | +--- AC -> MQ, 0 -> AC 2 + | +--- MQ v AC --> AC 2 + +-- clear AC 1 + + The operate instruction can be microprogrammed to perform operations + on the AC, MQ, and link. +*/ + +/* This routine is the instruction decode routine for the PDP-8. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and stop_inst flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by three parallel variables: + + dev_done device done flags + dev_enable device interrupt enable flags + int_req interrupt requests + + In addition, int_req contains the interrupt enable flag, the + CIF not pending flag, and the ION not pending flag. If all + three of these flags are set, and at least one interrupt request + is set, then an interrupt occurs. + + 3. Non-existent memory. On the PDP-8, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes outside the current field (indirect writes) need + be checked against actual memory size. + + 3. Adding I/O devices. Three modules must be modified: + + pdp8_defs.h add interrupt request definition + pdp8_cpu.c add IOT dispatch + pdp8_sys.c add pointer to data structures to sim_devices +*/ + +#include "pdp8_defs.h" + +#define ILL_ADR_FLAG 0100000 /* invalid addr flag */ +#define save_ibkpt (cpu_unit.u3) /* saved bkpt addr */ +#define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ +#define UNIT_NOEAE (1 << UNIT_V_NOEAE) +#define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +unsigned int16 M[MAXMEMSIZE] = { 0 }; /* main memory */ +int32 saved_LAC = 0; /* saved L'AC */ +int32 saved_MQ = 0; /* saved MQ */ +int32 saved_PC = 0; /* saved IF'PC */ +int32 saved_DF = 0; /* saved Data Field */ +int32 IB = 0; /* Instruction Buffer */ +int32 SF = 0; /* Save Field */ +int32 emode = 0; /* EAE mode */ +int32 gtf = 0; /* EAE gtf flag */ +int32 SC = 0; /* EAE shift count */ +int32 UB = 0; /* User mode Buffer */ +int32 UF = 0; /* User mode Flag */ +int32 OSR = 0; /* Switch Register */ +int32 old_PC = 0; /* old PC */ +int32 dev_enable = INT_INIT_ENABLE; /* dev intr enables */ +int32 dev_done = 0; /* dev done flags */ +int32 int_req = 0; /* intr requests */ +int32 ibkpt_addr = ILL_ADR_FLAG | ADDRMASK; /* breakpoint addr */ +int32 stop_inst = 0; /* trap on ill inst */ +extern int32 sim_int_char; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value); +extern t_stat sim_activate (UNIT *uptr, int32 delay); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 15) }, + { ORDATA (AC, saved_LAC, 12) }, + { FLDATA (L, saved_LAC, 12) }, + { ORDATA (MQ, saved_MQ, 12) }, + { ORDATA (SR, OSR, 12) }, + { GRDATA (IF, saved_PC, 8, 3, 12) }, + { GRDATA (DF, saved_DF, 8, 3, 12) }, + { GRDATA (IB, IB, 8, 3, 12) }, + { ORDATA (SF, SF, 7) }, + { FLDATA (UB, UB, 0) }, + { FLDATA (UF, UF, 0) }, + { ORDATA (SC, SC, 5) }, + { FLDATA (GTF, gtf, 0) }, + { FLDATA (EMODE, emode, 0) }, + { FLDATA (ION, int_req, INT_V_ION) }, + { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, + { FLDATA (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING) }, + { FLDATA (PWR_INT, int_req, INT_V_PWR) }, + { FLDATA (UF_INT, int_req, INT_V_UF) }, + { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, + { ORDATA (DONE, dev_done, INT_V_DIRECT), REG_RO }, + { ORDATA (ENABLE, dev_enable, INT_V_DIRECT), REG_RO }, + { FLDATA (NOEAE, cpu_unit.flags, UNIT_V_NOEAE), REG_HRO }, + { ORDATA (OLDPC, old_PC, 15), REG_RO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { ORDATA (BREAK, ibkpt_addr, 16) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, + { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 12, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 IR, MB, IF, DF, LAC, MQ; +register t_addr PC, MA; +int32 device, pulse, temp, iot_data; +t_stat reason; +extern int32 tti (int32 pulse, int32 AC); +extern int32 tto (int32 pulse, int32 AC); +extern int32 ptr (int32 pulse, int32 AC); +extern int32 ptp (int32 pulse, int32 AC); +extern int32 clk (int32 pulse, int32 AC); +extern int32 lpt (int32 pulse, int32 AC); +extern int32 rk (int32 pulse, int32 AC); +extern int32 rx (int32 pulse, int32 AC); +extern int32 rf60 (int32 pulse, int32 AC); +extern int32 rf61 (int32 pulse, int32 AC); +extern int32 rf62 (int32 pulse, int32 AC); +extern int32 rf64 (int32 pulse, int32 AC); +extern int32 mt70 (int32 pulse, int32 AC); +extern int32 mt71 (int32 pulse, int32 AC); +extern int32 mt72 (int32 pulse, int32 AC); + +/* Restore register state */ + +PC = saved_PC & 007777; /* load local copies */ +IF = saved_PC & 070000; +DF = saved_DF & 070000; +LAC = saved_LAC & 017777; +MQ = saved_MQ & 07777; +int_req = INT_UPDATE; +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; } + +if (int_req > INT_PENDING) { /* interrupt? */ + int_req = int_req & ~INT_ION; /* interrupts off */ + SF = (UF << 6) | (IF >> 9) | (DF >> 12); /* form save field */ + IF = IB = DF = UF = UB = 0; /* clear mem ext */ + old_PC = M[0] = PC; /* save PC in 0 */ + PC = 1; } /* fetch next from 1 */ + +MA = IF | PC; /* form PC */ +if (MA == ((t_addr) ibkpt_addr)) { /* breakpoint? */ + save_ibkpt = ibkpt_addr; /* save ibkpt */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate (&cpu_unit, 1); /* sched re-enable */ + reason = STOP_IBKPT; /* stop simulation */ + break; } + +IR = M[MA]; /* fetch instruction */ +PC = (PC + 1) & 07777; /* increment PC */ +int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ +sim_interval = sim_interval - 1; + +/* Instruction decoding. + + The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>) + are decoded together. This produces 32 decode points, four per + major opcode. For IOT, the extra decode points are not useful; + for OPR, only the group flag (IR<3>) is used. + + The following macros define the address calculations for data and + jump calculations. Data calculations return a full 15b extended + address, jump calculations a 12b field-relative address. + + Note that MA contains IF'PC. +*/ + +#define ZERO_PAGE MA = IF | (IR & 0177) +#define CURR_PAGE MA = (MA & 077600) | (IR & 0177) +#define INDIRECT if ((MA & 07770) != 00010) MA = DF | M[MA]; \ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777) + +#define ZERO_PAGE_J MA = IR & 0177 +#define CURR_PAGE_J MA = (MA & 007600) | (IR & 0177) +#define INDIRECT_J if ((MA & 07770) != 00010) MA = M[MA]; \ + else MA = (M[MA] = (M[MA] + 1) & 07777) +#define CHANGE_FIELD IF = IB; UF = UB; \ + int_req = int_req | INT_NO_CIF_PENDING + +switch ((IR >> 7) & 037) { /* decode IR<0:4> */ + +/* Opcode 0, AND */ + +case 000: /* AND, dir, zero */ + ZERO_PAGE; + LAC = LAC & (M[MA] | 010000); + break; +case 001: /* AND, dir, curr */ + CURR_PAGE; + LAC = LAC & (M[MA] | 010000); + break; +case 002: /* AND, indir, zero */ + ZERO_PAGE; + INDIRECT; + LAC = LAC & (M[MA] | 010000); + break; +case 003: /* AND, indir, curr */ + CURR_PAGE; + INDIRECT; + LAC = LAC & (M[MA] | 010000); + break; + +/* Opcode 1, TAD */ + +case 004: /* TAD, dir, zero */ + ZERO_PAGE; + LAC = (LAC + M[MA]) & 017777; + break; +case 005: /* TAD, dir, curr */ + CURR_PAGE; + LAC = (LAC + M[MA]) & 017777; + break; +case 006: /* TAD, indir, zero */ + ZERO_PAGE; + INDIRECT; + LAC = (LAC + M[MA]) & 017777; + break; +case 007: /* TAD, indir, curr */ + CURR_PAGE; + INDIRECT; + LAC = (LAC + M[MA]) & 017777; + break; + +/* Opcode 2, ISZ */ + +case 010: /* ISZ, dir, zero */ + ZERO_PAGE; + M[MA] = MB = (M[MA] + 1) & 07777; + if (MB == 0) PC = (PC + 1) & 07777; + break; +case 011: /* ISZ, dir, curr */ + CURR_PAGE; + M[MA] = MB = (M[MA] + 1) & 07777; + if (MB == 0) PC = (PC + 1) & 07777; + break; +case 012: /* ISZ, indir, zero */ + ZERO_PAGE; + INDIRECT; + MB = (M[MA] + 1) & 07777; + if (MEM_ADDR_OK (MA)) M[MA] = MB; + if (MB == 0) PC = (PC + 1) & 07777; + break; +case 013: /* ISZ, indir, curr */ + CURR_PAGE; + INDIRECT; + MB = (M[MA] + 1) & 07777; + if (MEM_ADDR_OK (MA)) M[MA] = MB; + if (MB == 0) PC = (PC + 1) & 07777; + break; + +/* Opcode 3, DCA */ + +case 014: /* DCA, dir, zero */ + ZERO_PAGE; + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; +case 015: /* DCA, dir, curr */ + CURR_PAGE; + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; +case 016: /* DCA, indir, zero */ + ZERO_PAGE; + INDIRECT; + if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; +case 017: /* DCA, indir, curr */ + CURR_PAGE; + INDIRECT; + if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + +/* Opcode 4, JMS */ + +case 020: /* JMS, dir, zero */ + ZERO_PAGE_J; + CHANGE_FIELD; + M[IF | MA] = old_PC = PC; + PC = (MA + 1) & 07777; + break; +case 021: /* JMS, dir, curr */ + CURR_PAGE_J; + CHANGE_FIELD; + M[IF | MA] = old_PC = PC; + PC = (MA + 1) & 07777; + break; +case 022: /* JMS, indir, zero */ + ZERO_PAGE; + INDIRECT_J; + CHANGE_FIELD; + MA = IF | MA; + old_PC = PC; + if (MEM_ADDR_OK (MA)) M[MA] = PC; + PC = (MA + 1) & 07777; + break; +case 023: /* JMS, indir, curr */ + CURR_PAGE; + INDIRECT_J; + CHANGE_FIELD; + MA = IF | MA; + old_PC = PC; + if (MEM_ADDR_OK (MA)) M[MA] = PC; + PC = (MA + 1) & 07777; + break; + +/* Opcode 5, JMP */ + +case 024: /* JMP, dir, zero */ + ZERO_PAGE_J; + CHANGE_FIELD; + old_PC = PC; + PC = MA; + break; +case 025: /* JMP, dir, curr */ + CURR_PAGE_J; + CHANGE_FIELD; + old_PC = PC; + PC = MA; + break; +case 026: /* JMP, indir, zero */ + ZERO_PAGE; + INDIRECT_J; + CHANGE_FIELD; + old_PC = PC; + PC = MA; + break; +case 027: /* JMP, indir, curr */ + CURR_PAGE; + INDIRECT_J; + CHANGE_FIELD; + old_PC = PC; + PC = MA; + break; + +/* Opcode 7, OPR group 1 */ + +case 034:case 035: /* OPR, group 1 */ + switch ((IR >> 4) & 017) { /* decode IR<4:7> */ + case 0: /* nop */ + break; + case 1: /* CML */ + LAC = LAC ^ 010000; + break; + case 2: /* CMA */ + LAC = LAC ^ 07777; + break; + case 3: /* CMA CML */ + LAC = LAC ^ 017777; + break; + case 4: /* CLL */ + LAC = LAC & 07777; + break; + case 5: /* CLL CML = STL */ + LAC = LAC | 010000; + break; + case 6: /* CLL CMA */ + LAC = (LAC ^ 07777) & 07777; + break; + case 7: /* CLL CMA CML */ + LAC = (LAC ^ 07777) | 010000; + break; + case 010: /* CLA */ + LAC = LAC & 010000; + break; + case 011: /* CLA CML */ + LAC = (LAC & 010000) ^ 010000; + break; + case 012: /* CLA CMA = STA */ + LAC = LAC | 07777; + break; + case 013: /* CLA CMA CML */ + LAC = (LAC | 07777) ^ 010000; + break; + case 014: /* CLA CLL */ + LAC = 0; + break; + case 015: /* CLA CLL CML */ + LAC = 010000; + break; + case 016: /* CLA CLL CMA */ + LAC = 07777; + break; + case 017: /* CLA CLL CMA CML */ + LAC = 017777; + break; } /* end switch opers */ + +/* OPR group 1, continued */ + + if (IR & 01) LAC = (LAC + 1) & 017777; /* IAC */ + switch ((IR >> 1) & 07) { /* decode IR<8:10> */ + case 0: /* nop */ + break; + case 1: /* BSW */ + LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6); + break; + case 2: /* RAL */ + LAC = ((LAC << 1) | (LAC >> 12)) & 017777; + break; + case 3: /* RTL */ + LAC = ((LAC << 2) | (LAC >> 11)) & 017777; + break; + case 4: /* RAR */ + LAC = ((LAC >> 1) | (LAC << 12)) & 017777; + break; + case 5: /* RTR */ + LAC = ((LAC >> 2) | (LAC << 11)) & 017777; + break; + case 6: /* RAL RAR - undef */ + LAC = LAC & (IR | 010000); /* uses AND path */ + break; + case 7: /* RTL RTR - undef */ + LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); + break; } /* uses address path */ + break; /* end group 1 */ + +/* OPR group 2 */ + +case 036:case 037: /* OPR, groups 2, 3 */ + if ((IR & 01) == 0) { /* group 2 */ + switch ((IR >> 3) & 017) { /* decode IR<6:8> */ + case 0: /* nop */ + break; + case 1: /* SKP */ + PC = (PC + 1) & 07777; + break; + case 2: /* SNL */ + if (LAC >= 010000) PC = (PC + 1) & 07777; + break; + case 3: /* SZL */ + if (LAC < 010000) PC = (PC + 1) & 07777; + break; + case 4: /* SZA */ + if ((LAC & 07777) == 0) PC = (PC + 1) & 07777; + break; + case 5: /* SNA */ + if ((LAC & 07777) != 0) PC = (PC + 1) & 07777; + break; + case 6: /* SZA | SNL */ + if ((LAC == 0) || (LAC >= 010000)) + PC = (PC + 1) & 07777; + break; + case 7: /* SNA & SZL */ + if ((LAC != 0) && (LAC < 010000)) PC = (PC + 1) & 07777; + break; + case 010: /* SMA */ + if ((LAC & 04000) != 0) PC = (PC + 1) & 07777; + break; + case 011: /* SPA */ + if ((LAC & 04000) == 0) PC = (PC + 1) & 07777; + break; + case 012: /* SMA | SNL */ + if (LAC >= 04000) PC = (PC + 1) & 07777; + break; + case 013: /* SPA & SZL */ + if (LAC < 04000) PC = (PC + 1) & 07777; + break; + case 014: /* SMA | SZA */ + if (((LAC & 04000) != 0) || ((LAC & 07777) == 0)) + PC = (PC + 1) & 07777; + break; + case 015: /* SPA & SNA */ + if (((LAC & 04000) == 0) && ((LAC & 07777) != 0)) + PC = (PC + 1) & 07777; + break; + case 016: /* SMA | SZA | SNL */ + if ((LAC >= 04000) || (LAC == 0)) PC = (PC + 1) & 07777; + break; + case 017: /* SPA & SNA & SZL */ + if ((LAC < 04000) && (LAC != 0)) PC = (PC + 1) & 07777; + break; } /* end switch skips */ + if (IR & 0200) LAC = LAC & 010000; /* CLA */ + if ((IR & 06) && UF) int_req = int_req | INT_UF; + else { if (IR & 04) LAC = LAC | OSR; /* OSR */ + if (IR & 02) reason = STOP_HALT; } /* HLT */ + break; } /* end group 2 */ + +/* OPR group 3 standard + + MQA!MQL exchanges AC and MQ, as follows: + + temp = MQ; + MQ = LAC & 07777; + LAC = LAC & 010000 | temp; +*/ + + temp = MQ; /* group 3 */ + if (IR & 0200) LAC = LAC & 010000; /* CLA */ + if (IR & 0020) { /* MQL */ + MQ = LAC & 07777; + LAC = LAC & 010000; } + if (IR & 0100) LAC = LAC | temp; /* MQA */ + if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) { + reason = stop_inst; /* EAE not present */ + break; } + +/* OPR group 3 EAE + + The EAE operates in two modes: + + Mode A, PDP-8/I compatible + Mode B, extended capability + + Mode B provides eight additional subfunctions; in addition, some + of the Mode A functions operate differently in Mode B. + + The mode switch instructions are decoded explicitly and cannot be + microprogrammed with other EAE functions (SWAB performs an MQL as + part of standard group 3 decoding). If mode switching is decoded, + all other EAE timing is suppressed. +*/ + + if (IR == 07431) { /* SWAB */ + emode = 1; /* set mode flag */ + break; } + if (IR == 07447) { /* SWBA */ + emode = gtf = 0; /* clear mode, gtf */ + break; } + +/* If not switching modes, the EAE operation is determined by the mode + and IR<6,8:10>: + + <6:10> mode A mode B comments + + 0x000 NOP NOP + 0x001 SCL ACS + 0x010 MUY MUY if mode B, next = address + 0x011 DVI DVI if mode B, next = address + 0x100 NMI NMI if mode B, clear AC if + result = 4000'0000 + 0x101 SHL SHL if mode A, extra shift + 0x110 ASR ASR if mode A, extra shift + 0x111 LSR LSR if mode A, extra shift + 1x000 SCA SCA + 1x001 SCA + SCL DAD + 1x010 SCA + MUY DST + 1x011 SCA + DVI SWBA NOP if not detected earlier + 1x100 SCA + NMI DPSZ + 1x101 SCA + SHL DPIC must be combined with MQA!MQL + 1x110 SCA + ASR DCM must be combined with MQA!MQL + 1x111 SCA + LSR SAM + + EAE instructions which fetch memory operands use the CPU's DEFER + state to read the first word; if the address operand is in locations + x0010 - x0017, it is autoincremented. +*/ + +/* EAE continued */ + + switch ((IR >> 1) & 027) { /* decode IR<6,8:10> */ + case 020: /* mode A, B: SCA */ + LAC = LAC | SC; + break; + case 0: /* mode A, B: NOP */ + break; + case 021: /* mode B: DAD */ + if (emode) { + MA = IF | PC; + INDIRECT; /* defer state */ + MQ = MQ + M[MA]; + MA = DF | ((MA + 1) & 07777); + LAC = (LAC & 07777) + M[MA] + (MQ >> 12); + MQ = MQ & 07777; + PC = (PC + 1) & 07777; + break; } + LAC = LAC | SC; /* mode A: SCA then */ + case 1: /* mode B: ACS */ + if (emode) { + SC = LAC & 037; + LAC = LAC & 010000; + break; } + SC = (~M[IF | PC]) & 037; /* mode A: SCL */ + PC = (PC + 1) & 07777; + break; + case 022: /* mode B: DST */ + if (emode) { + MA = IF | PC; + INDIRECT; /* defer state */ + if (MEM_ADDR_OK (MA)) M[MA] = MQ & 07777; + MA = DF | ((MA + 1) & 07777); + if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; + PC = (PC + 1) & 07777; + break; } + LAC = LAC | SC; /* mode A: SCA then */ + case 2: /* MUY */ + MA = IF | PC; + if (emode) { INDIRECT; } /* mode B: defer */ + temp = (MQ * M[MA]) + (LAC & 07777); + LAC = (temp >> 12) & 07777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = 014; /* 12 shifts */ + break; + +/* EAE continued */ + + case 023: /* mode B: SWBA */ + if (emode) break; + LAC = LAC | SC; /* mode A: SCA then */ + case 3: /* DVI */ + MA = IF | PC; + if (emode) { INDIRECT; } /* mode B: defer */ + if ((LAC & 07777) >= M[MA]) { /* overflow? */ + LAC = LAC | 010000; /* set link */ + MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */ + SC = 01; } /* 1 shift */ + else { temp = ((LAC & 07777) << 12) | MQ; + MQ = temp / M[MA]; + LAC = temp % M[MA]; + SC = 015; } /* 13 shifts */ + PC = (PC + 1) & 07777; + break; + case 024: /* mode B: DPSZ */ + if (emode) { + if (((LAC | MQ) & 07777) == 0) PC = (PC + 1) & 07777; + break; } + LAC = LAC | SC; /* mode A: SCA then */ + case 4: /* NMI */ + temp = (LAC << 12) | MQ; /* preserve link */ + for (SC = 0; ((temp & 017777777) != 0) && + (temp & 040000000) == ((temp << 1) & 040000000); SC++) + temp = temp << 1; + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + if (emode && ((LAC & 07777) == 04000) && (MQ == 0)) + LAC = LAC & 010000; /* clr if 4000'0000 */ + break; + case 025: /* mode B: DPIC */ + if (emode) { + temp = (LAC + 1) & 07777; /* SWP already done! */ + LAC = MQ + (temp == 0); + MQ = temp; + break; } + LAC = LAC | SC; /* mode A: SCA then */ + case 5: /* SHL */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC << 12) | MQ) << SC; /* preserve link */ + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + +/* EAE continued */ + + case 026: /* mode B: DCM */ + if (emode) { + temp = (-LAC) & 07777; /* SWP already done! */ + LAC = (MQ ^ 07777) + (temp == 0); + MQ = temp; + break; } + LAC = LAC | SC; /* mode A: SCA then */ + case 6: /* ASR */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC & 07777) << 12) | MQ; /* sext from AC0 */ + if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; + temp = temp >> SC; + if (LAC & 04000) temp = temp | ((SC > 24)? -1: + (-1 << (24 - SC))); + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + case 027: /* mode B: SAM */ + if (emode) { + temp = LAC & 07777; + LAC = MQ + (temp ^ 07777) + 1; /* L'AC = MQ - AC */ + gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11); + break; } + LAC = LAC | SC; /* mode A: SCA then */ + case 7: /* LSR */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC & 07777) << 12) | MQ; /* clear link */ + if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; + temp = temp >> SC; + LAC = (temp >> 12) & 07777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; } /* end switch */ + break; /* end case 7 */ + +/* Opcode 6, IOT */ + +case 030:case 031:case 032:case 033: /* IOT */ + if (UF) { /* privileged? */ + int_req = int_req | INT_UF; + break; } + device = (IR >> 3) & 077; /* device = IR<3:8> */ + pulse = IR & 07; /* pulse = IR<9:11> */ + iot_data = LAC & 07777; /* AC unchanged */ + switch (device) { /* decode IR<3:8> */ + case 0: /* CPU control */ + switch (pulse) { /* decode IR<9:11> */ + case 0: /* SKON */ + if (int_req & INT_ION) PC = (PC + 1) & 07777; + int_req = int_req & ~INT_ION; + break; + case 1: /* ION */ + int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; + break; + case 2: /* IOF */ + int_req = int_req & ~INT_ION; + break; + case 3: /* SRQ */ + if (int_req & INT_ALL) PC = (PC + 1) & 07777; + break; + case 4: /* GTF */ + LAC = (LAC & 010000) | + ((LAC & 010000) >> 1) | (gtf << 10) | + (((int_req & INT_ALL) != 0) << 9) | + (((int_req & INT_ION) != 0) << 7) | SF; + break; + case 5: /* RTF */ + gtf = ((LAC & 02000) >> 10); + UB = (LAC & 0100) >> 6; + IB = (LAC & 0070) << 9; + DF = (LAC & 0007) << 12; + LAC = ((LAC & 04000) << 1) | iot_data; + int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING; + break; + case 6: /* SGT */ + if (gtf) PC = (PC + 1) & 07777; + break; + case 7: /* CAF */ + gtf = 0; + emode = 0; + int_req = int_req & INT_NO_CIF_PENDING; + dev_done = 0; + dev_enable = INT_INIT_ENABLE; + LAC = 0; + break; } /* end switch pulse */ + continue; /* skip rest of IOT */ + +/* IOT, continued: memory extension */ + + case 020:case 021:case 022:case 023: + case 024:case 025:case 026:case 027: /* memory extension */ + switch (pulse) { /* decode IR<9:11> */ + case 1: /* CDF */ + DF = (IR & 0070) << 9; + break; + case 2: /* CIF */ + IB = (IR & 0070) << 9; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + case 3: /* CDF CIF */ + DF = IB = (IR & 0070) << 9; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + case 4: + switch (device & 07) { /* decode IR<6:8> */ + case 0: /* CINT */ + int_req = int_req & ~INT_UF; + break; + case 1: /* RDF */ + LAC = LAC | (DF >> 9); + break; + case 2: /* RIF */ + LAC = LAC | (IF >> 9); + break; + case 3: /* RIB */ + LAC = LAC | SF; + break; + case 4: /* RMF */ + UB = (SF & 0100) >> 6; + IB = (SF & 0070) << 9; + DF = (SF & 0007) << 12; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + case 5: /* SINT */ + if (int_req & INT_UF) PC = (PC + 1) & 07777; + break; + case 6: /* CUF */ + UB = 0; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + case 7: /* SUF */ + UB = 1; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; } /* end switch device */ + break; + default: + reason = stop_inst; + break; } /* end switch pulse */ + continue; /* skip rest of IOT */ + +/* IOT, continued: other special cases */ + + case 010: /* power fail */ + switch (pulse) { /* decode IR<9:11> */ + case 1: /* SBE */ + break; + case 2: /* SPL */ + if (int_req & INT_PWR) PC = (PC + 1) & 07777; + break; + case 3: /* CAL */ + int_req = int_req & ~INT_PWR; + break; + default: + reason = stop_inst; + break; } /* end switch pulse */ + continue; /* skip rest of IOT */ + default: /* unknown device */ + reason = stop_inst; /* stop on flag */ + continue; /* skip rest of IOT */ + +/* IOT, continued: I/O devices */ + + case 1: /* PTR */ + iot_data = ptr (pulse, iot_data); + break; + case 2: /* PTP */ + iot_data = ptp (pulse, iot_data); + break; + case 3: /* TTI */ + iot_data = tti (pulse, iot_data); + break; + case 4: /* TTO */ + iot_data = tto (pulse, iot_data); + break; + case 013: /* CLK */ + iot_data = clk (pulse, iot_data); + break; + case 060: /* RF08 */ + iot_data = rf60 (pulse, iot_data); + break; + case 061: + iot_data = rf61 (pulse, iot_data); + break; + case 062: + iot_data = rf62 (pulse, iot_data); + break; + case 064: + iot_data = rf64 (pulse, iot_data); + break; + case 066: /* LPT */ + iot_data = lpt (pulse, iot_data); + break; + case 070: /* TM8E */ + iot_data = mt70 (pulse, iot_data); + break; + case 071: + iot_data = mt71 (pulse, iot_data); + break; + case 072: + iot_data = mt72 (pulse, iot_data); + break; + case 074: /* RK8E */ + iot_data = rk (pulse, iot_data); + break; + case 075: /* RX8E */ + iot_data = rx (pulse, iot_data); + break; } /* end switch device */ + LAC = (LAC & 010000) | (iot_data & 07777); + if (iot_data & IOT_SKP) PC = (PC + 1) & 07777; + if (iot_data >= IOT_REASON) reason = iot_data >> IOT_V_REASON; + break; } /* end switch opcode */ +} /* end while */ + +/* Simulation halted */ + +saved_PC = IF | (PC & 07777); /* save copies */ +saved_DF = DF & 070000; +saved_LAC = LAC & 017777; +saved_MQ = MQ & 07777; +return reason; +} /* end sim_instr */ + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING; +saved_DF = IB = (saved_PC >> 12) & 03; +UF = UB = gtf = emode = 0; +return cpu_svc (&cpu_unit); +} + +/* Breakpoint service */ + +t_stat cpu_svc (UNIT *uptr) +{ +if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) ibkpt_addr = save_ibkpt; +save_ibkpt = -1; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 07777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & 07777; +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 value) +{ +int32 mc = 0; +t_addr i; + +if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 07777) != 0)) + return SCPE_ARG; +for (i = value; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = value; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} diff --git a/pdp8_defs.h b/pdp8_defs.h new file mode 100644 index 00000000..27ef7928 --- /dev/null +++ b/pdp8_defs.h @@ -0,0 +1,114 @@ +/* pdp8_defs.h: PDP-8 simulator definitions + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + 19-Mar-95 RMS Added dynamic memory size. + 02-May-94 RMS Added non-existent memory handling. + + The author gratefully acknowledges the help of Max Burnet, Richie Lary, + and Bill Haygood in resolving questions about the PDP-8 +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE) + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ + +/* IOT subroutine return codes */ + +#define IOT_V_SKP 12 /* skip */ +#define IOT_V_REASON 13 /* reason */ +#define IOT_SKP (1 << IOT_V_SKP) +#define IOT_REASON (1 << IOT_V_REASON) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Interrupt flags + + The interrupt flags consist of three groups: + + 1. Devices with individual interrupt enables. These record + their interrupt requests in device_done and their enables + in device_enable, and must occupy the low bit positions. + + 2. Devices without interrupt enables. These record their + interrupt requests directly in int_req, and must occupy + the middle bit positions. + + 3. Overhead. These exist only in int_req and must occupy the + high bit positions. + + Because the PDP-8 does not have priority interrupts, the order + of devices within groups does not matter. +*/ + +#define INT_V_START 0 /* enable start */ +#define INT_V_LPT (INT_V_START+0) /* line printer */ +#define INT_V_PTP (INT_V_START+1) /* tape punch */ +#define INT_V_PTR (INT_V_START+2) /* tape reader */ +#define INT_V_TTO (INT_V_START+3) /* terminal */ +#define INT_V_TTI (INT_V_START+4) /* keyboard */ +#define INT_V_CLK (INT_V_START+5) /* clock */ +#define INT_V_DIRECT (INT_V_START+6) /* direct start */ +#define INT_V_RX (INT_V_DIRECT+0) /* RX8E */ +#define INT_V_RK (INT_V_DIRECT+1) /* RK8E */ +#define INT_V_RF (INT_V_DIRECT+2) /* RF08 */ +#define INT_V_MT (INT_V_DIRECT+3) /* TM8E */ +#define INT_V_PWR (INT_V_DIRECT+4) /* power int */ +#define INT_V_UF (INT_V_DIRECT+5) /* user int */ +#define INT_V_OVHD (INT_V_DIRECT+6) /* overhead start */ +#define INT_V_NO_ION_PENDING (INT_V_OVHD+0) /* ion pending */ +#define INT_V_NO_CIF_PENDING (INT_V_OVHD+1) /* cif pending */ +#define INT_V_ION (INT_V_OVHD+2) /* interrupts on */ + +#define INT_LPT (1 << INT_V_LPT) +#define INT_PTP (1 << INT_V_PTP) +#define INT_PTR (1 << INT_V_PTR) +#define INT_TTO (1 << INT_V_TTO) +#define INT_TTI (1 << INT_V_TTI) +#define INT_CLK (1 << INT_V_CLK) +#define INT_RX (1 << INT_V_RX) +#define INT_RK (1 << INT_V_RK) +#define INT_RF (1 << INT_V_RF) +#define INT_MT (1 << INT_V_MT) +#define INT_PWR (1 << INT_V_PWR) +#define INT_UF (1 << INT_V_UF) +#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) +#define INT_NO_CIF_PENDING (1 << INT_V_NO_CIF_PENDING) +#define INT_ION (1 << INT_V_ION) +#define INT_DEV_ENABLE ((1 << INT_V_DIRECT) - 1) /* devices w/enables */ +#define INT_ALL ((1 << INT_V_OVHD) - 1) /* all interrupts */ +#define INT_INIT_ENABLE (INT_TTI+INT_TTO+INT_PTR+INT_PTP+INT_LPT) +#define INT_PENDING (INT_ION+INT_NO_CIF_PENDING+INT_NO_ION_PENDING) +#define INT_UPDATE ((int_req & ~INT_DEV_ENABLE) | (dev_done & dev_enable)) diff --git a/pdp8_lp.c b/pdp8_lp.c new file mode 100644 index 00000000..f322a578 --- /dev/null +++ b/pdp8_lp.c @@ -0,0 +1,150 @@ +/* pdp8_lp.c: PDP-8 line printer simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt line printer +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, dev_done, dev_enable, stop_inst; +int32 lpt_err = 0; /* error flag */ +int32 lpt_stopioe = 0; /* stop on error */ +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat lpt_detach (UNIT *uptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (ERR, lpt_err, 0) }, + { FLDATA (DONE, dev_done, INT_V_LPT) }, + { FLDATA (ENABLE, dev_enable, INT_V_LPT) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { DRDATA (POS, lpt_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { NULL } }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach }; + +/* IOT routine */ + +int32 lpt (int32 pulse, int32 AC) +{ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* PSKF */ + return (dev_done & INT_LPT)? IOT_SKP + AC: AC; +case 2: /* PCLF */ + dev_done = dev_done & ~INT_LPT; /* clear flag */ + int_req = int_req & ~INT_LPT; /* clear int req */ + return AC; +case 3: /* PSKE */ + return (lpt_err)? IOT_SKP + AC: AC; +case 6: /* PCLF!PSTB */ + dev_done = dev_done & ~INT_LPT; /* clear flag */ + int_req = int_req & ~INT_LPT; /* clear int req */ +case 4: /* PSTB */ + lpt_unit.buf = AC & 0177; /* load buffer */ + if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || + (lpt_unit.buf == 012)) { + sim_activate (&lpt_unit, lpt_unit.wait); + return AC; } + return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC; +case 5: /* PSIE */ + dev_enable = dev_enable | INT_LPT; /* set enable */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 7: /* PCIE */ + dev_enable = dev_enable & ~INT_LPT; /* clear enable */ + int_req = int_req & ~INT_LPT; /* clear int req */ + return AC; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_LPT; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((lpt_unit.flags & UNIT_ATT) == 0) { + lpt_err = 1; + return IORETURN (lpt_stopioe, SCPE_UNATT); } +if (putc (lpt_unit.buf, lpt_unit.fileref) == EOF) { + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; } +lpt_unit.pos = ftell (lpt_unit.fileref); +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; +dev_done = dev_done & ~INT_LPT; /* clear done, int */ +int_req = int_req & ~INT_LPT; +dev_enable = dev_enable | INT_LPT; /* set enable */ +lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; +return reason; +} + +/* Detach routine */ + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_err = 1; +return detach_unit (uptr); +} diff --git a/pdp8_mt.c b/pdp8_mt.c new file mode 100644 index 00000000..9656b28a --- /dev/null +++ b/pdp8_mt.c @@ -0,0 +1,600 @@ +/* PDP-8 magnetic tape simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Oct-98 RMS V2.4 magtape format + 22-Jan-97 RMS V2.3 magtape format + 01-Jan-96 RMS Rewritten from TM8-E Maintenance Manual + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "pdp8_defs.h" + +#define MT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_W_UF 2 /* saved user flags */ +#define USTAT u3 /* unit status */ +#define UNUM u4 /* unit number */ +#define DBSIZE (1 << 12) /* max data cmd */ +#define DBMASK (SBSIZE - 1) + +/* Command/unit - mt_cu */ + +#define CU_V_UNIT 9 /* unit */ +#define CU_M_UNIT 07 +#define CU_PARITY 00400 /* parity select */ +#define CU_IEE 00200 /* error int enable */ +#define CU_IED 00100 /* done int enable */ +#define CU_V_EMA 3 /* ext mem address */ +#define CU_M_EMA 07 +#define CU_EMA (CU_M_EMA << CU_V_EMA) +#define CU_DTY 00002 /* drive type */ +#define CU_UNPAK 00001 /* 6b vs 8b mode */ +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) +#define GET_EMA(x) (((x) & CU_EMA) << (12 - CU_V_EMA)) + +/* Function - mt_fn */ + +#define FN_V_FNC 9 /* function */ +#define FN_M_FNC 07 +#define FN_UNLOAD 00 +#define FN_REWIND 01 +#define FN_READ 02 +#define FN_CMPARE 03 +#define FN_WRITE 04 +#define FN_WREOF 05 +#define FN_SPACEF 06 +#define FN_SPACER 07 +#define FN_ERASE 00400 /* erase */ +#define FN_CRC 00200 /* read CRC */ +#define FN_GO 00100 /* go */ +#define FN_INC 00040 /* incr mode */ +#define FN_RMASK 07740 /* readable bits */ +#define GET_FNC(x) (((x) >> FN_V_FNC) & FN_M_FNC) + +/* Status - stored in mt_sta or (*) uptr -> USTAT */ + +#define STA_ERR (04000 << 12) /* error */ +#define STA_REW (02000 << 12) /* *rewinding */ +#define STA_BOT (01000 << 12) /* *start of tape */ +#define STA_REM (00400 << 12) /* *offline */ +#define STA_PAR (00200 << 12) /* parity error */ +#define STA_EOF (00100 << 12) /* *end of file */ +#define STA_RLE (00040 << 12) /* rec lnt error */ +#define STA_DLT (00020 << 12) /* data late */ +#define STA_EOT (00010 << 12) /* *end of tape */ +#define STA_WLK (00004 << 12) /* *write locked */ +#define STA_CPE (00002 << 12) /* compare error */ +#define STA_ILL (00001 << 12) /* illegal */ +#define STA_INC 00010 /* increment error */ +#define STA_LAT 00004 /* lateral par error */ +#define STA_CRC 00002 /* CRC error */ +#define STA_LON 00001 /* long par error */ + +#define STA_CLR (FN_RMASK | 00020) /* always clear */ +#define STA_DYN (STA_REW | STA_BOT | STA_REM | STA_EOF | \ + STA_EOT | STA_WLK) /* kept in USTAT */ +#define STA_EFLGS (STA_BOT | STA_PAR | STA_RLE | STA_DLT | \ + STA_EOT | STA_CPE | STA_ILL | STA_EOF | STA_INC) + /* set error */ +#define TUR(u) (!sim_is_active (u)) /* tape unit ready */ + +extern unsigned int16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; +int32 mt_cu = 0; /* command/unit */ +int32 mt_fn = 0; /* function */ +int32 mt_ca = 0; /* current address */ +int32 mt_wc = 0; /* word count */ +int32 mt_sta = 0; /* status register */ +int32 mt_db = 0; /* data buffer */ +int32 mt_done = 0; /* mag tape flag */ +int32 mt_time = 10; /* record latency */ +int32 mt_stopioe = 1; /* stop on error */ +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +int32 mt_updcsta (UNIT *uptr); +int32 mt_ixma (int32 xma); +t_stat mt_vlock (UNIT *uptr, int32 val); +UNIT *mt_busy (void); +void mt_set_done (void); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } }; + +REG mt_reg[] = { + { ORDATA (CMD, mt_cu, 12) }, + { ORDATA (FNC, mt_fn, 12) }, + { ORDATA (CA, mt_ca, 12) }, + { ORDATA (WC, mt_wc, 12) }, + { ORDATA (DB, mt_db, 12) }, + { GRDATA (STA, mt_sta, 8, 12, 12) }, + { ORDATA (STA2, mt_sta, 6) }, + { FLDATA (DONE, mt_done, 0) }, + { FLDATA (INT, int_req, INT_V_MT) }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { DRDATA (TIME, mt_time, 24), PV_LEFT }, + { ORDATA (UST0, mt_unit[0].USTAT, 24) }, + { ORDATA (UST1, mt_unit[1].USTAT, 24) }, + { ORDATA (UST2, mt_unit[2].USTAT, 24) }, + { ORDATA (UST3, mt_unit[3].USTAT, 24) }, + { ORDATA (UST4, mt_unit[4].USTAT, 24) }, + { ORDATA (UST5, mt_unit[5].USTAT, 24) }, + { ORDATA (UST6, mt_unit[6].USTAT, 24) }, + { ORDATA (UST7, mt_unit[7].USTAT, 24) }, + { DRDATA (POS0, mt_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, mt_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, mt_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, mt_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, mt_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, mt_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, mt_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, mt_unit[7].pos, 31), PV_LEFT + REG_RO }, + { GRDATA (FLG0, mt_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, mt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, mt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, mt_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, mt_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, mt_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, mt_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, mt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { NULL } }; + +MTAB mt_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", &mt_vlock }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &mt_vlock }, + { 0 } }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &mt_detach }; + +/* IOT routines */ + +int32 mt70 (int32 pulse, int32 AC) +{ +int32 f; +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* LWCR */ + mt_wc = AC; /* load word count */ + return 0; +case 2: /* CWCR */ + mt_wc = 0; /* clear word count */ + return AC; +case 3: /* LCAR */ + mt_ca = AC; /* load mem address */ + return 0; +case 4: /* CCAR */ + mt_ca = 0; /* clear mem address */ + return AC; +case 5: /* LCMR */ + if (mt_busy ()) mt_sta = mt_sta | STA_ILL; /* busy? illegal op */ + mt_cu = AC; /* load command reg */ + mt_updcsta (mt_dev.units + GET_UNIT (mt_cu)); + return 0; + +/* MT70, continued */ + +case 6: /* LFGR */ + if (mt_busy ()) mt_sta = mt_sta | STA_ILL; /* busy? illegal op */ + mt_fn = AC; /* load function */ + if ((mt_fn & FN_GO) == 0) { /* go set? */ + mt_updcsta (uptr); /* update status */ + return 0; } + f = GET_FNC (mt_fn); /* get function */ + if (((uptr -> flags & UNIT_ATT) == 0) || !TUR (uptr) || + (((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WLK)) + || (((f == FN_SPACER) || (f == FN_REWIND)) && (uptr -> pos == 0))) { + mt_sta = mt_sta | STA_ILL; /* illegal op error */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return 0; } + uptr -> USTAT = uptr -> USTAT & STA_WLK; /* clear status */ + if (f == FN_UNLOAD) { /* unload? */ + detach_unit (uptr); /* set offline */ + uptr -> USTAT = STA_REW | STA_REM; /* rewinding, off */ + mt_set_done (); } /* set done */ + else if (f == FN_REWIND) { /* rewind */ + uptr -> USTAT = uptr -> USTAT | STA_REW; /* rewinding */ + mt_set_done (); } /* set done */ + else mt_done = 0; /* clear done */ + mt_updcsta (uptr); /* update status */ + sim_activate (uptr, mt_time); /* start io */ + return 0; +case 7: /* LDBR */ + if (mt_busy ()) mt_sta = mt_sta | STA_ILL; /* busy? illegal op */ + mt_db = AC; /* load buffer */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return 0; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +return AC; +} + +/* IOTs, continued */ + +int32 mt71 (int32 pulse, int32 AC) +{ +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); +switch (pulse) { /* decode IR<9:11> */ +case 1: /* RWCR */ + return mt_wc; /* read word count */ +case 2: /* CLT */ + mt_reset (&mt_dev); /* reset everything */ + return AC; +case 3: /* RCAR */ + return mt_ca; /* read mem address */ +case 4: /* RMSR */ + return ((mt_updcsta (uptr) >> 12) & 07777); /* read status */ +case 5: /* RCMR */ + return mt_cu; /* read command */ +case 6: /* RFSR */ + return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK)) + & 07777); /* read function */ +case 7: /* RDBR */ + return mt_db; /* read data buffer */ +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +return AC; +} + +int32 mt72 (int32 pulse, int32 AC) +{ +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* SKEF */ + return (mt_sta & STA_ERR)? IOT_SKP + AC: AC; +case 2: /* SKCB */ + return (!mt_busy ())? IOT_SKP + AC: AC; +case 3: /* SKJD */ + return mt_done? IOT_SKP + AC: AC; +case 4: /* SKTR */ + return (TUR (uptr))? IOT_SKP + AC: AC; +case 5: /* CLF */ + if (TUR (uptr)) mt_reset (&mt_dev); /* if TUR, zap */ + else { mt_sta = 0; /* clear status */ + mt_done = 0; /* clear done */ + mt_updcsta (uptr); } /* update status */ + return AC; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +return AC; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat mt_svc (UNIT *uptr) +{ +int32 f, i, p, u, err, wc, xma; +t_stat rval; +t_mtrlnt tbc, cbc; +unsigned int16 c, c1, c2; +unsigned int8 dbuf[(2 * DBSIZE)]; +static t_mtrlnt bceof = { 0 }; + +u = uptr -> UNUM; /* get unit number */ +if (uptr -> USTAT & STA_REW) { /* rewind? */ + uptr -> pos = 0; /* update position */ + if (uptr -> flags & UNIT_ATT) /* still on line? */ + uptr -> USTAT = (uptr -> USTAT & STA_WLK) | STA_BOT; + else uptr -> USTAT = STA_REM; + if (u == GET_UNIT (mt_cu)) { /* selected? */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); } /* update status */ + return SCPE_OK; } + +if ((uptr -> flags & UNIT_ATT) == 0) { /* if not attached */ + uptr -> USTAT = STA_REM; /* unit off line */ + mt_sta = mt_sta | STA_ILL; /* illegal operation */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return IORETURN (mt_stopioe, SCPE_UNATT); } + +f = GET_FNC (mt_fn); /* get command */ +if (((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WLK)) { + mt_sta = mt_sta | STA_ILL; /* illegal operation */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return SCPE_OK; } + +err = 0; +rval = SCPE_OK; +xma = GET_EMA (mt_cu) + mt_ca; /* get mem addr */ +wc = 010000 - mt_wc; /* get wc */ +switch (f) { /* case on function */ + +/* Unit service, continued */ + +case FN_READ: /* read */ +case FN_CMPARE: /* read/compare */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + (feof (uptr -> fileref))) { + uptr -> USTAT = uptr -> USTAT | STA_EOT | STA_RLE; + break; } + if (tbc == 0) { /* tape mark? */ + uptr -> USTAT = uptr -> USTAT | STA_EOF | STA_RLE; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + cbc = (mt_cu & CU_UNPAK)? wc: wc * 2; /* expected bc */ + tbc = MTRL (tbc); /* ignore error flag */ + if (tbc != cbc) mt_sta = mt_sta | STA_RLE; /* wrong size? */ + if (tbc < cbc) { /* record small? */ + cbc = tbc; /* use smaller */ + wc = (mt_cu & CU_UNPAK)? cbc: (cbc + 1) / 2; } + i = fxread (dbuf, sizeof (int8), cbc, uptr -> fileref); + for ( ; i < cbc; i++) dbuf[i] = 0; /* fill with 0's */ + err = ferror (uptr -> fileref); + for (i = p = 0; i < wc; i++) { /* copy buffer */ + xma = mt_ixma (xma); /* increment xma */ + if (mt_cu & CU_UNPAK) c = dbuf[p++]; + else { c1 = dbuf[p++] & 077; + c2 = dbuf[p++] & 077; + c = (c1 << 6) | c2; } + if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c; + else if ((f == FN_CMPARE) && (M[xma] != c)) { + mt_sta = mt_sta | STA_CPE; + break; } } + mt_wc = (mt_wc + wc) & 07777; /* update wc */ + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + /* update tape pos */ + (2 * sizeof (t_mtrlnt)); + break; +case FN_WRITE: /* write */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + tbc = (mt_cu & CU_UNPAK)? wc: wc * 2; + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + for (i = p = 0; i < wc; i++) { /* copy buf to tape */ + xma = mt_ixma (xma); /* incr mem addr */ + if (mt_cu & CU_UNPAK) dbuf[p++] = M[xma] & 0377; + else { dbuf[p++] = (M[xma] >> 6) & 077; + dbuf[p++] = M[xma] & 077; } } + fxwrite (dbuf, sizeof (int8), (tbc + 1) & ~1, uptr -> fileref); + fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + mt_wc = 0; + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + /* update tape pos */ + (2 * sizeof (t_mtrlnt)); + break; +case FN_WREOF: + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); /* write eof */ + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update tape pos */ + break; + +/* Unit service, continued */ + +case FN_SPACEF: /* space forward */ + do { mt_wc = (mt_wc + 1) & 07777; /* incr wc */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + feof (uptr -> fileref)) { + uptr -> USTAT = uptr -> USTAT | STA_EOT; + break; } + if (tbc == 0) { /* zero bc? */ + uptr -> USTAT = uptr -> USTAT | STA_EOF; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); } + while (mt_wc != 0); + break; +case FN_SPACER: /* space reverse */ + if (uptr -> pos == 0) { /* at BOT? */ + uptr -> USTAT = uptr -> USTAT | STA_BOT; + break; } + do { mt_wc = (mt_wc + 1) & 07777; /* incr wc */ + fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), + SEEK_SET); + fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); + if ((err = ferror (uptr -> fileref)) || /* error or eof? */ + feof (uptr -> fileref)) { + uptr -> USTAT = uptr -> USTAT | STA_BOT; + uptr -> pos = 0; + break; } + if (tbc == 0) { /* start of prv file? */ + uptr -> USTAT = uptr -> USTAT | STA_EOF; + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); + if (uptr -> pos == 0) { /* at BOT? */ + uptr -> USTAT = uptr -> USTAT | STA_BOT; + break; } } + while (mt_wc != 0); + break; } /* end case */ + +/* Unit service, continued */ + +if (err != 0) { /* I/O error */ + mt_sta = mt_sta | STA_PAR | STA_CRC; /* flag error */ + perror ("MT I/O error"); + rval = SCPE_IOERR; + clearerr (uptr -> fileref); } +mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA); +mt_ca = xma & 07777; /* update mem addr */ +mt_set_done (); /* set done */ +mt_updcsta (uptr); /* update status */ +return IORETURN (mt_stopioe, rval); +} + +/* Update controller status */ + +int32 mt_updcsta (UNIT *uptr) +{ +mt_sta = (mt_sta & ~(STA_DYN | STA_ERR | STA_CLR)) | (uptr -> USTAT & STA_DYN); +if (mt_sta & STA_EFLGS) mt_sta = mt_sta | STA_ERR; +if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) || + (mt_done && (mt_cu & CU_IED))) int_req = int_req | INT_MT; +else int_req = int_req & ~INT_MT; +return mt_sta; +} + +/* Test if controller busy */ + +UNIT *mt_busy (void) +{ +int32 u; +UNIT *uptr; + +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + if (sim_is_active (uptr) && ((uptr -> USTAT & STA_REW) == 0)) + return uptr; } +return NULL; +} + +/* Increment extended memory address */ + +int32 mt_ixma (int32 xma) /* incr extended ma */ +{ +int32 v; + +v = ((xma + 1) & 07777) | (xma & 070000); /* wrapped incr */ +if (mt_fn & FN_INC) { /* increment mode? */ + if (xma == 077777) mt_sta = mt_sta | STA_INC; /* at limit? error */ + else v = xma + 1; } /* else 15b incr */ +return v; +} + +/* Set done */ + +void mt_set_done (void) +{ +mt_done = 1; /* set done */ +mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC); /* clear func<4:6> */ +return; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0; +int_req = int_req & ~INT_MT; /* clear interrupt */ +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + uptr -> UNUM = u; /* init drive number */ + sim_cancel (uptr); /* cancel activity */ + if (uptr -> flags & UNIT_ATT) uptr -> USTAT = + ((uptr -> pos)? 0: STA_BOT) | + ((uptr -> flags & UNIT_WLK)? STA_WLK: 0); + else uptr -> USTAT = STA_REM; } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +uptr -> USTAT = STA_BOT | ((uptr -> flags & UNIT_WLK)? STA_WLK: 0); +if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr); +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +if (!sim_is_active (uptr)) uptr -> USTAT = STA_REM; +if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr); +return detach_unit (uptr); +} + +/* Write lock/enable routine */ + +t_stat mt_vlock (UNIT *uptr, int32 val) +{ +if ((uptr -> flags & UNIT_ATT) && val) uptr -> USTAT = uptr -> USTAT | STA_WLK; +else uptr -> USTAT = uptr -> USTAT & ~STA_WLK; +if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr); +return SCPE_OK; +} diff --git a/pdp8_pt.c b/pdp8_pt.c new file mode 100644 index 00000000..6575be96 --- /dev/null +++ b/pdp8_pt.c @@ -0,0 +1,245 @@ +/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Mar-98 RMS Added RIM loader as PTR bootstrap. + + ptr paper tape reader + ptp paper tape punch +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, dev_done, dev_enable, stop_inst; +extern unsigned int16 M[]; +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_PTR) }, + { FLDATA (ENABLE, dev_enable, INT_V_PTR) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { DRDATA (POS, ptr_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_PTP) }, + { FLDATA (ENABLE, dev_enable, INT_V_PTP) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { DRDATA (POS, ptp_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL }; + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 pulse, int32 AC) +{ +switch (pulse) { /* decode IR<9:11> */ +case 0: /* RPE */ + dev_enable = dev_enable | (INT_PTR+INT_PTP); /* set enable */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 1: /* RSF */ + return (dev_done & INT_PTR)? IOT_SKP + AC: AC; +case 6: /* RFC!RRB */ + sim_activate (&ptr_unit, ptr_unit.wait); +case 2: /* RRB */ + dev_done = dev_done & ~INT_PTR; /* clear flag */ + int_req = int_req & ~INT_PTR; /* clear int req */ + return (AC | ptr_unit.buf); /* or data to AC */ +case 4: /* RFC */ + sim_activate (&ptr_unit, ptr_unit.wait); + dev_done = dev_done & ~INT_PTR; /* clear flag */ + int_req = int_req & ~INT_PTR; /* clear int req */ + return AC; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; } +dev_done = dev_done | INT_PTR; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +ptr_unit.buf = temp & 0377; +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; +dev_done = dev_done & ~INT_PTR; /* clear done, int */ +int_req = int_req & ~INT_PTR; +dev_enable = dev_enable | INT_PTR; /* set enable */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 pulse, int32 AC) +{ +switch (pulse) { /* decode IR<9:11> */ +case 0: /* PCE */ + dev_enable = dev_enable & ~(INT_PTR+INT_PTP); /* clear enables */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 1: /* PSF */ + return (dev_done & INT_PTP)? IOT_SKP + AC: AC; +case 2: /* PCF */ + dev_done = dev_done & ~INT_PTP; /* clear flag */ + int_req = int_req & ~INT_PTP; /* clear int req */ + return AC; +case 6: /* PLS */ + dev_done = dev_done & ~INT_PTP; /* clear flag */ + int_req = int_req & ~INT_PTP; /* clear int req */ +case 4: /* PPC */ + ptp_unit.buf = AC & 0377; /* load punch buf */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ + return AC; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_PTP; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +dev_done = dev_done & ~INT_PTP; /* clear done, int */ +int_req = int_req & ~INT_PTP; +dev_enable = dev_enable | INT_PTP; /* set enable */ +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 07756 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 06014, /* 7756, RFC */ + 06011, /* 7757, LOOP, RSF */ + 05357, /* JMP .-1 */ + 06016, /* RFC RRB */ + 07106, /* CLL RTL*/ + 07006, /* RTL */ + 07510, /* SPA*/ + 05374, /* JMP 7774 */ + 07006, /* RTL */ + 06011, /* RSF */ + 05367, /* JMP .-1 */ + 06016, /* RFC RRB */ + 07420, /* SNL */ + 03776, /* DCA I 7776 */ + 03376, /* 7774, DCA 7776 */ + 05357, /* JMP 7757 */ + 00000, /* 7776, 0 */ + 05301 /* 7777, JMP 7701 */ +}; + +t_stat ptr_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp8_rf.c b/pdp8_rf.c new file mode 100644 index 00000000..e25624c0 --- /dev/null +++ b/pdp8_rf.c @@ -0,0 +1,301 @@ +/* pdp8_rf.c: RF08 fixed head disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Apr-99 RMS Changed t_addr to unsigned + 30-Mar-98 RMS Fixed bug in RF bootstrap. + + The RF08 is a head-per-track disk. It uses the three cycle data break + facility. To minimize overhead, the entire RF08 is buffered in memory. + + Two timing parameters are provided: + + rf_time Interword timing, must be non-zero + rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst +*/ + +#include "pdp8_defs.h" +#include + +/* Constants */ + +#define RF_NUMWD 2048 /* words/track */ +#define RF_NUMTR 128 /* tracks/disk */ +#define RF_NUMDK 4 /* disks/controller */ +#define RF_SIZE (RF_NUMDK * RF_NUMTR * RF_NUMWD) /* words/drive */ +#define RF_WC 07750 /* word count */ +#define RF_MA 07751 /* mem address */ +#define RF_WMASK (RF_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define RF_READ 3 /* read */ +#define RF_WRITE 5 /* write */ + +/* Status register */ + +#define RFS_PCA 04000 /* photocell status */ +#define RFS_DRE 02000 /* data req enable */ +#define RFS_WLS 01000 /* write lock status */ +#define RFS_EIE 00400 /* error int enable */ +#define RFS_PIE 00200 /* photocell int enb */ +#define RFS_CIE 00100 /* done int enable */ +#define RFS_MEX 00070 /* memory extension */ +#define RFS_DRL 00004 /* data late error */ +#define RFS_NXD 00002 /* non-existent disk */ +#define RFS_PER 00001 /* parity error */ +#define RFS_ERR (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER) +#define RFS_V_MEX 3 + +#define GET_MEX(x) (((x) & RFS_MEX) << (12 - RFS_V_MEX)) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) RF_NUMWD))) +#define UPDATE_PCELL if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \ + else rf_sta = rf_sta & ~RFS_PCA +#define RF_INT_UPDATE if ((rf_done && (rf_sta & RFS_CIE)) || \ + ((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \ + ((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \ + int_req = int_req | INT_RF; \ + else int_req = int_req & ~INT_RF + +extern int32 int_req, stop_inst; +extern unsigned int16 M[]; +extern UNIT cpu_unit; +int32 rf_sta = 0; /* status register */ +int32 rf_da = 0; /* disk address */ +int32 rf_done = 0; /* done flag */ +int32 rf_wlk = 0; /* write lock */ +int32 rf_time = 10; /* inter-word time */ +int32 rf_burst = 1; /* burst mode flag */ +int32 rf_stopioe = 1; /* stop on error */ +t_stat rf_svc (UNIT *uptr); +t_stat pcell_svc (UNIT *uptr); +t_stat rf_reset (DEVICE *dptr); +t_stat rf_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* RF08 data structures + + rf_dev RF device descriptor + rf_unit RF unit descriptor + pcell_unit photocell timing unit (orphan) + rf_reg RF register list +*/ + +UNIT rf_unit = + { UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + RF_SIZE) }; + +UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) }; + +REG rf_reg[] = { + { ORDATA (STA, rf_sta, 12) }, + { ORDATA (DA, rf_da, 20) }, + { ORDATA (MA, M[RF_MA], 12) }, + { ORDATA (WC, M[RF_WC], 12) }, + { FLDATA (DONE, rf_done, 0) }, + { FLDATA (INT, int_req, INT_V_RF) }, + { ORDATA (WLK, rf_wlk, 32) }, + { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (BURST, rf_burst, 0) }, + { FLDATA (STOP_IOE, rf_stopioe, 0) }, + { NULL } }; + +DEVICE rf_dev = { + "RF", &rf_unit, rf_reg, NULL, + 1, 8, 20, 1, 8, 12, + NULL, NULL, &rf_reset, + &rf_boot, NULL, NULL }; + +/* IOT routines */ + +int32 rf60 (int32 pulse, int32 AC) +{ +int32 t; + +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* DCMA */ + rf_da = rf_da & ~07777; /* clear DAR<8:19> */ + return AC; +case 3:case 5: /* DMAR, DMAW */ + rf_da = (rf_da & ~07777) | AC; /* DAR<8:19> <- AC */ + rf_done = 0; /* clear done */ + rf_sta = rf_sta & ~RFS_ERR; /* clear errors */ + RF_INT_UPDATE; /* update int req */ + rf_unit.FUNC = pulse; /* save function */ + t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */ + if (t < 0) t = t + RF_NUMWD; /* wrap around? */ + sim_activate (&rf_unit, t * rf_time); /* schedule op */ + return 0; /* clear AC */ +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +int32 rf61 (int32 pulse, int32 AC) +{ +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* DCIM */ + rf_sta = rf_sta & 07007; /* clear STA<3:8> */ + int_req = int_req & ~INT_RF; /* clear int req */ + sim_cancel (&pcell_unit); /* cancel photocell */ + return AC; +case 2: /* DSAC */ + return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP + AC: AC; +case 5: /* DIML */ + rf_sta = (rf_sta & 07007) | (AC & 0770); /* STA<3:8> <- AC */ + if (rf_sta & RFS_PIE) /* photocell int? */ + sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) * + rf_time); + else sim_cancel (&pcell_unit); + RF_INT_UPDATE; /* update int req */ + return 0; /* clear AC */ +case 6: /* DIMA */ + return rf_sta; /* AC <- STA<0:11> */ +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* IOT's, continued */ + +int32 rf62 (int32 pulse, int32 AC) +{ +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* DFSE */ + return (rf_sta & RFS_ERR)? IOT_SKP + AC: AC; +case 2: /* DFSC */ + return (rf_done)? IOT_SKP + AC: AC; +case 3: /* DISK */ + return (rf_done || (rf_sta & RFS_ERR))? IOT_SKP + AC: AC; +case 6: /* DMAC */ + return rf_da & 07777; /* AC <- DAR<0:11> */ +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +int32 rf64 (int32 pulse, int32 AC) +{ +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ +case 1: /* DCXA */ + rf_da = rf_da & 07777; /* clear DAR<0:7> */ + return AC; +case 3: /* DXAL */ + rf_da = (rf_da & 07777) | ((AC & 0377) << 12); /* DAR<0:7> <- AC */ + return 0; /* clear AC */ +case 5: /* DXAC */ + return ((rf_da >> 12) & 0377); /* AC <- DAR<0:7> */ +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +t_stat rf_svc (UNIT *uptr) +{ +int32 pa, t, mex; + +UPDATE_PCELL; /* update photocell */ +if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ + rf_sta = rf_sta | RFS_NXD; + rf_done = 1; + RF_INT_UPDATE; /* update int req */ + return IORETURN (rf_stopioe, SCPE_UNATT); } + +mex = GET_MEX (rf_sta); +do { M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */ + pa = mex | M[RF_MA]; /* add extension */ + if (uptr -> FUNC == RF_READ) { + if (MEM_ADDR_OK (pa)) /* read, check nxm */ + M[pa] = *(((int16 *) uptr -> filebuf) + rf_da); } + else { t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07); + if ((rf_wlk >> t) & 1) rf_sta = rf_sta | RFS_WLS; + else { *(((int16 *) uptr -> filebuf) + rf_da) = M[pa]; + if (((t_addr) rf_da) >= uptr -> hwmark) + uptr -> hwmark = rf_da + 1; } } + rf_da = (rf_da + 1) & 03777777; /* incr disk addr */ + M[RF_WC] = (M[RF_WC] + 1) & 07777; } /* incr word count */ +while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ + +if (M[RF_WC] != 0) /* more to do? */ + sim_activate (&rf_unit, rf_time); /* sched next */ +else { rf_done = 1; /* done */ + RF_INT_UPDATE; } /* update int req */ +return SCPE_OK; +} + +/* Photocell unit service */ + +t_stat pcell_svc (UNIT *uptr) +{ +rf_sta = rf_sta | RFS_PCA; /* set photocell */ +if (rf_sta & RFS_PIE) { /* int enable? */ + sim_activate (&pcell_unit, RF_NUMWD * rf_time); + int_req = int_req | INT_RF; } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat rf_reset (DEVICE *dptr) +{ +rf_sta = rf_da = 0; +rf_done = 1; +int_req = int_req & ~INT_RF; /* clear interrupt */ +sim_cancel (&rf_unit); +sim_cancel (&pcell_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 07750 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 07600, /* 7750, CLA CLL ; also word count */ + 06603, /* 7751, DMAR ; also address */ + 06622, /* 7752, DFSC ; done? */ + 05352, /* 7753, JMP .-1 ; no */ + 05752 /* 7754, JMP @.-2 ; enter boot */ +}; + +t_stat rf_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp8_rk.c b/pdp8_rk.c new file mode 100644 index 00000000..fed86b9a --- /dev/null +++ b/pdp8_rk.c @@ -0,0 +1,395 @@ +/* pdp8_rk.c: RK8E cartridge disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 29-Jun-96 RMS Added unit disable feature +*/ + +#include "pdp8_defs.h" + +/* Constants */ + +#define RK_NUMSC 16 /* sectors/surface */ +#define RK_NUMSF 2 /* surfaces/cylinder */ +#define RK_NUMCY 203 /* cylinders/drive */ +#define RK_NUMWD 256 /* words/sector */ +#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */ +#define RK_NUMDR 4 /* drives/controller */ +#define RK_M_NUMDR 03 + +/* Flags in the unit flags word */ + +#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ +#define UNIT_W_UF 3 /* user flags width */ +#define UNIT_HWLK (1 << UNIT_V_HWLK) +#define UNIT_SWLK (1 << UNIT_V_SWLK) + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* Status register */ + +#define RKS_DONE 04000 /* transfer done */ +#define RKS_HMOV 02000 /* heads moving */ +#define RKS_SKFL 00400 /* drive seek fail */ +#define RKS_NRDY 00200 /* drive not ready */ +#define RKS_BUSY 00100 /* control busy error */ +#define RKS_TMO 00040 /* timeout error */ +#define RKS_WLK 00020 /* write lock error */ +#define RKS_CRC 00010 /* CRC error */ +#define RKS_DLT 00004 /* data late error */ +#define RKS_STAT 00002 /* drive status error */ +#define RKS_CYL 00001 /* cyl address error */ +#define RKS_ERR (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL) + +/* Command register */ + +#define RKC_M_FUNC 07 /* function */ +#define RKC_READ 0 +#define RKC_RALL 1 +#define RKC_WLK 2 +#define RKC_SEEK 3 +#define RKC_WRITE 4 +#define RKC_WALL 5 +#define RKC_V_FUNC 9 +#define RKC_IE 00400 /* interrupt enable */ +#define RKC_SKDN 00200 /* int on seek done */ +#define RKC_HALF 00100 /* 128W sector */ +#define RKC_MEX 00070 /* memory extension */ +#define RKC_V_MEX 3 +#define RKC_M_DRV 03 /* drive select */ +#define RKC_V_DRV 1 +#define RKC_CYHI 00001 /* high cylinder addr */ + +#define GET_FUNC(x) (((x) >> RKC_V_FUNC) & RKC_M_FUNC) +#define GET_DRIVE(x) (((x) >> RKC_V_DRV) & RKC_M_DRV) +#define GET_MEX(x) (((x) & RKC_MEX) << (12 - RKC_V_MEX)) + +/* Disk address */ + +#define RKD_V_SECT 0 /* sector */ +#define RKD_M_SECT 017 +#define RKD_V_SUR 4 /* surface */ +#define RKD_M_SUR 01 +#define RKD_V_CYL 5 /* cylinder */ +#define RKD_M_CYL 0177 +#define GET_CYL(x,y) ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \ + (((y) >> RKD_V_CYL) & RKD_M_CYL)) +#define GET_DA(x,y) ((((x) & RKC_CYHI) << 12) | y) + +/* Reset commands */ + +#define RKX_CLS 0 /* clear status */ +#define RKX_CLC 1 /* clear control */ +#define RKX_CLD 2 /* clear drive */ +#define RKX_CLSA 3 /* clear status alt */ + +#define RK_INT_UPDATE \ + if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \ + ((rk_cmd & RKC_IE) != 0)) int_req = int_req | INT_RK; \ + else int_req = int_req & ~INT_RK +#define RK_MIN 10 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern int32 int_req, stop_inst; +extern unsigned int16 M[]; +extern UNIT cpu_unit; +int32 rk_busy = 0; /* controller busy */ +int32 rk_sta = 0; /* status register */ +int32 rk_cmd = 0; /* command register */ +int32 rk_da = 0; /* disk address */ +int32 rk_ma = 0; /* memory address */ +int32 rk_swait = 10, rk_rwait = 10; /* seek, rotate wait */ +int32 rk_stopioe = 1; /* stop on error */ +t_stat rk_svc (UNIT *uptr); +t_stat rk_reset (DEVICE *dptr); +t_stat rk_boot (int32 unitno); +void rk_go (int32 function, int32 cylinder); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern int32 sim_is_active (UNIT *uptr); +extern size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +extern size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); + +/* RK-8E data structures + + rk_dev RK device descriptor + rk_unit RK unit list + rk_reg RK register list + rk_mod RK modifiers list +*/ + +UNIT rk_unit[] = { + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RK_SIZE) } }; + +REG rk_reg[] = { + { ORDATA (STA, rk_sta, 12) }, + { ORDATA (CMD, rk_cmd, 12) }, + { ORDATA (DA, rk_da, 12) }, + { ORDATA (MA, rk_ma, 12) }, + { FLDATA (BUSY, rk_busy, 0) }, + { FLDATA (INT, int_req, INT_V_RK) }, + { DRDATA (STIME, rk_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rk_rwait, 24), PV_LEFT }, + { GRDATA (FLG0, rk_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, rk_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, rk_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, rk_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { FLDATA (STOP_IOE, rk_stopioe, 0) }, + { NULL } }; + +MTAB rk_mod[] = { + { (UNIT_HWLK+UNIT_SWLK), 0, "write enabled", "ENABLED", NULL }, + { (UNIT_HWLK+UNIT_SWLK), UNIT_HWLK, "write locked", "LOCKED", NULL }, + { (UNIT_HWLK+UNIT_SWLK), UNIT_SWLK, "write locked", NULL, NULL }, + { (UNIT_HWLK+UNIT_SWLK), (UNIT_HWLK+UNIT_SWLK), "write locked", + NULL, NULL }, + { 0 } }; + +DEVICE rk_dev = { + "RK", rk_unit, rk_reg, rk_mod, + RK_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &rk_reset, + &rk_boot, NULL, NULL }; + +/* IOT routine */ + +int32 rk (int32 pulse, int32 AC) +{ +int32 i; +UNIT *uptr; + +switch (pulse) { /* decode IR<9:11> */ +case 0: /* unused */ + return (stop_inst << IOT_V_REASON) + AC; +case 1: /* DSKP */ + return (rk_sta & (RKS_DONE + RKS_ERR))? /* skip on done, err */ + IOT_SKP + AC: AC; +case 2: /* DCLR */ + rk_sta = 0; /* clear status */ + switch (AC & 03) { /* decode AC<10:11> */ + case RKX_CLS: /* clear status */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + case RKX_CLSA: /* clear status alt */ + break; + case RKX_CLC: /* clear control */ + rk_cmd = rk_busy = 0; /* clear registers */ + rk_ma = rk_da = 0; + for (i = 0; i < RK_NUMDR; i++) sim_cancel (&rk_unit[i]); + break; + case RKX_CLD: /* reset drive */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else rk_go (RKC_SEEK, 0); /* seek to 0 */ + break; } /* end switch AC */ + break; +case 3: /* DLAG */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else { rk_da = AC; /* load disk addr */ + rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da)); } + break; +case 4: /* DLCA */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else rk_ma = AC; /* load curr addr */ + break; +case 5: /* DRST */ + uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ + rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY); /* clear dynamic */ + if ((uptr -> flags & UNIT_ATT) == 0) rk_sta = rk_sta | RKS_NRDY; + if (sim_is_active (uptr)) rk_sta = rk_sta | RKS_HMOV; + return rk_sta; +case 6: /* DLDC */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else { rk_cmd = AC; /* load command */ + rk_sta = 0; } /* clear status */ + break; +case 7: /* DMAN */ + break; } /* end case pulse */ +RK_INT_UPDATE; /* update int req */ +return 0; /* clear AC */ +} + +/* Initiate new function + + Called with function, cylinder, to allow recalibrate as well as + load and go to be processed by this routine. + + Assumes that the controller is idle, and that updating of interrupt + request will be done by the caller. +*/ + +void rk_go (int32 func, int32 cyl) +{ +int32 t; +UNIT *uptr; + +if (func == RKC_RALL) func = RKC_READ; /* all? use standard */ +if (func == RKC_WALL) func = RKC_WRITE; +uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ +if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */ + rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; + return; } +if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) { /* busy or bad cyl? */ + rk_sta = rk_sta | RKS_DONE | RKS_STAT; + return; } +if ((func == RKC_WRITE) && (uptr -> flags & (UNIT_HWLK + UNIT_SWLK))) { + rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ + return; } +if (func == RKC_WLK) { /* write lock? */ + uptr -> flags = uptr -> flags | UNIT_SWLK; + rk_sta = rk_sta | RKS_DONE; + return; } +t = abs (cyl - uptr -> CYL) * rk_swait; /* seek time */ +if (func == RKC_SEEK) { /* seek? */ + sim_activate (uptr, MAX (RK_MIN, t)); /* schedule */ + rk_sta = rk_sta | RKS_DONE; } /* set done */ +else { sim_activate (uptr, t + rk_rwait); /* schedule */ + rk_busy = 1; } /* set busy */ +uptr -> FUNC = func; /* save func */ +uptr -> CYL = cyl; /* put on cylinder */ +return; +} + +/* Unit service + + If seek, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder address for + the current command. + + Note that memory addresses wrap around in the current field. +*/ + +static unsigned int16 fill[RK_NUMWD/2] = { 0 }; +t_stat rk_svc (UNIT *uptr) +{ +int32 err, wc, wc1, awc, swc, pa, da; +UNIT *seluptr; + +if (uptr -> FUNC == RKC_SEEK) { /* seek? */ + seluptr = rk_dev.units + GET_DRIVE (rk_cmd); /* see if selected */ + if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) { + rk_sta = rk_sta | RKS_DONE; + RK_INT_UPDATE; } + return SCPE_OK; } + +if ((uptr -> flags & UNIT_ATT) == 0) { /* not att? abort */ + rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; + rk_busy = 0; + RK_INT_UPDATE; + return IORETURN (rk_stopioe, SCPE_UNATT); } + +pa = GET_MEX (rk_cmd) | rk_ma; /* phys address */ +da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */ +swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */ +if ((wc1 = ((rk_ma + wc) - 010000)) > 0) wc = wc - wc1; /* if wrap, limit */ +err = fseek (uptr -> fileref, da, SEEK_SET); /* locate sector */ + +if ((uptr -> FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */ + awc = fxread (&M[pa], sizeof (int16), wc, uptr -> fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; /* fill if eof */ + err = ferror (uptr -> fileref); + if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ + pa = pa & 070000; /* wrap phys addr */ + awc = fxread (&M[pa], sizeof (int16), wc1, uptr -> fileref); + for ( ; awc < wc1; awc++) M[pa + awc] = 0; /* fill if eof */ + err = ferror (uptr -> fileref); } } + +if ((uptr -> FUNC == RKC_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int16), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ + pa = pa & 070000; /* wrap phys addr */ + fxwrite (&M[pa], sizeof (int16), wc1, uptr -> fileref); + err = ferror (uptr -> fileref); } + if ((rk_cmd & RKC_HALF) && (err == 0)) { /* fill half sector */ + fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr -> fileref); + err = ferror (uptr -> fileref); } } + +rk_ma = (rk_ma + swc) & 07777; /* incr mem addr reg */ +rk_sta = rk_sta | RKS_DONE; /* set done */ +rk_busy = 0; +RK_INT_UPDATE; + +if (err != 0) { + perror ("RK I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat rk_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0; +int_req = int_req & ~INT_RK; /* clear interrupt */ +for (i = 0; i < RK_NUMDR; i++) { /* stop all units */ + uptr = rk_dev.units + i; + sim_cancel (uptr); + uptr -> flags = uptr -> flags & ~UNIT_SWLK; + uptr -> CYL = uptr -> FUNC = 0; } +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 023 +#define BOOT_UNIT 032 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 06007, /* 23, CAF */ + 06744, /* 24, DLCA ; addr = 0 */ + 01032, /* 25, TAD UNIT ; unit no */ + 06746, /* 26, DLDC ; command, unit */ + 06743, /* 27, DLAG ; disk addr, go */ + 01032, /* 30, TAD UNIT ; unit no, for OS */ + 05031, /* 31, JMP . */ + 00000 /* UNIT, 0 ; in bits <9:10> */ +}; + +t_stat rk_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp8_rx.c b/pdp8_rx.c new file mode 100644 index 00000000..76697d8b --- /dev/null +++ b/pdp8_rx.c @@ -0,0 +1,436 @@ +/* pdp8_rx.c: RX8E/RX01 floppy disk simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rx RX8E disk controller + + An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. + Tracks are numbered 0-76, sectors 1-26. The RX8E can store data in + 8b mode or 12b mode. In 8b mode, the controller reads or writes + 128 bytes per sector. In 12b mode, the reads or writes 64 12b words + per sector. The 12b words are bit packed into the first 96 bytes + of the sector; the last 32 bytes are zeroed on writes. + + 14-Apr-99 RMS Changed t_addr to unsigned + 15-Aug-96 RMS Fixed bug in LCD +*/ + +#include "pdp8_defs.h" + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 /* cf Jones!! */ +#define RX_NUMBY 128 /* bytes/sector */ +#define RX_NUMWD (RX_NUMBY / 2) /* words/sector */ +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_UF) + +#define IDLE 0 /* idle state */ +#define RWDS 1 /* rw, sect next */ +#define RWDT 2 /* rw, track next */ +#define FILL 3 /* fill buffer */ +#define EMPTY 4 /* empty buffer */ +#define CMD_COMPLETE 5 /* set done next */ +#define INIT_COMPLETE 6 /* init compl next */ + +#define RXCS_V_FUNC 1 /* function */ +#define RXCS_M_FUNC 7 +#define RXCS_FILL 0 /* fill buffer */ +#define RXCS_EMPTY 1 /* empty buffer */ +#define RXCS_WRITE 2 /* write sector */ +#define RXCS_READ 3 /* read sector */ +#define RXCS_RXES 5 /* read status */ +#define RXCS_WRDEL 6 /* write del data */ +#define RXCS_ECODE 7 /* read error code */ +#define RXCS_DRV 0020 /* drive */ +#define RXCS_MODE 0100 /* mode */ +#define RXCS_MAINT 0200 /* maintenance */ + +#define RXES_CRC 0001 /* CRC error */ +#define RXES_PAR 0002 /* parity error */ +#define RXES_ID 0004 /* init done */ +#define RXES_WLK 0010 /* write protect */ +#define RXES_DD 0100 /* deleted data */ +#define RXES_DRDY 0200 /* drive ready */ + +#define TRACK u3 /* current track */ +#define READ_RXDBR ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr) +#define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY + +extern int32 int_req, dev_done, dev_enable; +int32 rx_tr = 0; /* xfer ready flag */ +int32 rx_err = 0; /* error flag */ +int32 rx_csr = 0; /* control/status */ +int32 rx_dbr = 0; /* data buffer */ +int32 rx_esr = 0; /* error status */ +int32 rx_ecode = 0; /* error code */ +int32 rx_track = 0; /* desired track */ +int32 rx_sector = 0; /* desired sector */ +int32 rx_state = IDLE; /* controller state */ +int32 rx_cwait = 100; /* command time */ +int32 rx_swait = 10; /* seek, per track */ +int32 rx_xwait = 1; /* tr set time */ +int32 rx_stopioe = 1; /* stop on error */ +unsigned int8 buf[RX_NUMBY] = { 0 }; /* sector buffer */ +int32 bufptr = 0; /* buffer pointer */ +t_stat rx_svc (UNIT *uptr); +t_stat rx_reset (DEVICE *dptr); +t_stat rx_boot (int32 unitno); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); + +/* RX8E data structures + + rx_dev RX device descriptor + rx_unit RX unit list + rx_reg RX register list + rx_mod RX modifier list +*/ + +UNIT rx_unit[] = { + { UDATA (&rx_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) }, + { UDATA (&rx_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) } }; + +REG rx_reg[] = { + { ORDATA (RXCS, rx_csr, 12) }, + { ORDATA (RXDB, rx_dbr, 12) }, + { ORDATA (RXES, rx_esr, 8) }, + { ORDATA (RXERR, rx_ecode, 8) }, + { ORDATA (RXTA, rx_track, 8) }, + { ORDATA (RXSA, rx_sector, 8) }, + { ORDATA (STAPTR, rx_state, 3), REG_RO }, + { ORDATA (BUFPTR, bufptr, 7) }, + { FLDATA (TR, rx_tr, 0) }, + { FLDATA (ERR, rx_err, 0) }, + { FLDATA (DONE, dev_done, INT_V_RX) }, + { FLDATA (ENABLE, dev_enable, INT_V_RX) }, + { FLDATA (INT, int_req, INT_V_RX) }, + { DRDATA (CTIME, rx_cwait, 24), PV_LEFT }, + { DRDATA (STIME, rx_swait, 24), PV_LEFT }, + { DRDATA (XTIME, rx_xwait, 24), PV_LEFT }, + { FLDATA (FLG0, rx_unit[0].flags, UNIT_V_WLK), REG_HRO }, + { FLDATA (FLG1, rx_unit[1].flags, UNIT_V_WLK), REG_HRO }, + { FLDATA (STOP_IOE, rx_stopioe, 0) }, + { BRDATA (*BUF, buf, 8, 8, RX_NUMBY), REG_HRO }, + { NULL } }; + +MTAB rx_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE rx_dev = { + "RX", rx_unit, rx_reg, rx_mod, + RX_NUMDR, 8, 20, 1, 8, 8, + NULL, NULL, &rx_reset, + &rx_boot, NULL, NULL }; + +/* IOT routine */ + +int32 rx (int32 pulse, int32 AC) +{ +int32 drv; + +switch (pulse) { /* decode IR<9:11> */ +case 0: /* unused */ + return AC; +case 1: /* LCD */ + if (rx_state != IDLE) return AC; /* ignore if busy */ + rx_dbr = rx_csr = AC; /* save new command */ + dev_done = dev_done & ~INT_RX; /* clear done, int */ + int_req = int_req & ~INT_RX; + rx_tr = rx_err = 0; /* clear flags */ + bufptr = 0; /* clear buf pointer */ + switch ((AC >> RXCS_V_FUNC) & RXCS_M_FUNC) { /* decode command */ + case RXCS_FILL: + rx_state = FILL; /* state = fill */ + rx_tr = 1; /* xfer is ready */ + break; + case RXCS_EMPTY: + rx_state = EMPTY; /* state = empty */ + sim_activate (&rx_unit[0], rx_xwait); /* sched xfer */ + break; + case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: + rx_state = RWDS; /* state = get sector */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + default: + rx_state = CMD_COMPLETE; /* state = cmd compl */ + drv = (rx_csr & RXCS_DRV) > 0; /* get drive number */ + sim_activate (&rx_unit[drv], rx_cwait); /* sched done */ + break; } /* end switch func */ + return 0; /* clear AC */ +case 2: /* XDR */ + switch (rx_state & 07) { /* case on state */ + default: /* default */ + return READ_RXDBR; /* return data reg */ + case EMPTY: /* emptying buffer */ + sim_activate (&rx_unit[0], rx_xwait); /* sched xfer */ + return READ_RXDBR; /* return data reg */ + case RWDS: /* sector */ + rx_sector = AC & RX_M_SECTOR; /* save sector */ + case FILL: /* fill */ + rx_dbr = AC; /* save data */ + sim_activate (&rx_unit[0], rx_xwait); /* sched xfer */ + break; + case RWDT: /* track */ + rx_track = AC & RX_M_TRACK; /* save track */ + rx_dbr = AC; /* save data */ + drv = (rx_csr & RXCS_DRV) > 0; /* get drive number */ + sim_activate (&rx_unit[drv], /* sched done */ + rx_swait * abs (rx_track - rx_unit[drv].TRACK)); + break; } /* end switch state */ + return AC; +case 3: /* STR */ + if (rx_tr != 0) { + rx_tr = 0; + return IOT_SKP + AC; } + return AC; +case 4: /* SER */ + if (rx_err != 0) { + rx_err = 0; + return IOT_SKP + AC; } + return AC; +case 5: /* SDN */ + if ((dev_done & INT_RX) != 0) { + dev_done = dev_done & ~INT_RX; + int_req = int_req & ~INT_RX; + return IOT_SKP + AC; } + return AC; +case 6: /* INTR */ + if (AC & 1) dev_enable = dev_enable | INT_RX; + else dev_enable = dev_enable & ~INT_RX; + int_req = INT_UPDATE; + return AC; +case 7: /* INIT */ + rx_reset (&rx_dev); /* reset device */ + return AC; } /* end case pulse */ +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here, treat as unknown command + RWDS Just transferred sector, wait for track, set tr + RWDT Just transferred track, do read or write, finish command + FILL copy dbr to buf[bufptr], advance ptr + if bufptr > max, finish command, else set tr + EMPTY if bufptr > max, finish command, else + copy buf[bufptr] to dbr, advance ptr, set tr + CMD_COMPLETE copy requested data to dbr, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command + + For RWDT and CMD_COMPLETE, the input argument is the selected drive; + otherwise, it is drive 0. +*/ + +t_stat rx_svc (UNIT *uptr) +{ +int32 i, func, byptr; +t_addr da; +t_stat rval; +void rx_done (int32 new_dbr, int32 new_ecode); +#define PTR12(x) (((x) + (x) + (x)) >> 1) + +rval = SCPE_OK; /* assume ok */ +func = (rx_csr >> RXCS_V_FUNC) & RXCS_M_FUNC; /* get function */ +switch (rx_state) { /* case on state */ +case IDLE: /* idle */ + rx_done (rx_esr, 0); /* done */ + break; +case EMPTY: /* empty buffer */ + if (rx_csr & RXCS_MODE) { /* 8b xfer? */ + if (bufptr >= RX_NUMBY) { /* done? */ + rx_done (rx_esr, 0); /* set done */ + break; } /* and exit */ + rx_dbr = buf[bufptr]; } /* else get data */ + else { byptr = PTR12 (bufptr); /* 12b xfer */ + if (bufptr >= RX_NUMWD) { /* done? */ + rx_done (rx_esr, 0); /* set done */ + break; } /* and exit */ + rx_dbr = (bufptr & 1)? /* get data */ + ((buf[byptr] & 017) << 8) | buf[byptr + 1]: + (buf[byptr] << 4) | ((buf[byptr + 1] >> 4) & 017); } + bufptr = bufptr + 1; + rx_tr = 1; + break; +case FILL: /* fill buffer */ + if (rx_csr & RXCS_MODE) { /* 8b xfer? */ + buf[bufptr] = rx_dbr; /* fill buffer */ + bufptr = bufptr + 1; + if (bufptr < RX_NUMBY) rx_tr = 1; /* if more, set xfer */ + else rx_done (rx_esr, 0); } /* else done */ + else { byptr = PTR12 (bufptr); /* 12b xfer */ + if (bufptr & 1) { /* odd or even? */ + buf[byptr] = (buf[byptr] & 0360) | ((rx_dbr >> 8) & 017); + buf[byptr + 1] = rx_dbr & 0377; } + else { + buf[byptr] = (rx_dbr >> 4) & 0377; + buf[byptr + 1] = (rx_dbr & 017) << 4; } + bufptr = bufptr + 1; + if (bufptr < RX_NUMWD) rx_tr = 1; /* if more, set xfer */ + else { for (i = PTR12 (RX_NUMWD); i < RX_NUMBY; i++) + buf[i] = 0; /* else fill sector */ + rx_done (rx_esr, 0); } } /* set done */ + break; +case RWDS: /* wait for sector */ + rx_tr = 1; /* set xfer ready */ + rx_state = RWDT; /* advance state */ + break; +case RWDT: /* wait for track */ + if (rx_track >= RX_NUMTR) { /* bad track? */ + rx_done (rx_esr, 0040); /* done, error */ + break; } + uptr -> TRACK = rx_track; /* now on track */ + if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ + rx_done (rx_esr, 0070); /* done, error */ + break; } + if ((uptr -> flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (rx_esr, 0110); /* done, error */ + rval = SCPE_UNATT; /* return error */ + break; } + da = CALC_DA (rx_track, rx_sector); /* get disk address */ + if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */ + if (func == RXCS_READ) { /* read? */ + for (i = 0; i < RX_NUMBY; i++) + buf[i] = *(((int8 *) uptr -> filebuf) + da + i); } + else { if (uptr -> flags & UNIT_WLK) { /* write and locked? */ + rx_esr = rx_esr | RXES_WLK; /* flag error */ + rx_done (rx_esr, 0100); /* done, error */ + break; } + for (i = 0; i < RX_NUMBY; i++) /* write */ + *(((int8 *) uptr -> filebuf) + da + i) = buf[i]; + da = da + RX_NUMBY; + if (da > uptr -> hwmark) uptr -> hwmark = da; } + rx_done (rx_esr, 0); /* done */ + break; +case CMD_COMPLETE: /* command complete */ + if (func == RXCS_ECODE) rx_done (rx_ecode, 0); + else if (uptr -> flags & UNIT_ATT) rx_done (rx_esr | RXES_DRDY, 0); + else rx_done (rx_esr, 0); + break; +case INIT_COMPLETE: /* init complete */ + rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ + rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ + if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (rx_esr | RXES_ID, 0010); /* init done, error */ + break; } + da = CALC_DA (1, 1); /* track 1, sector 1 */ + for (i = 0; i < RX_NUMBY; i++) /* read sector */ + buf[i] = *(((int8 *) uptr -> filebuf) + da + i); + rx_done (rx_esr | RXES_ID | RXES_DRDY, 0); /* set done */ + if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020; + break; } /* end case state */ +return IORETURN (rx_stopioe, rval); +} + +/* Command complete. Set done and put final value in interface register, + return to IDLE state. +*/ + +void rx_done (int32 new_dbr, int32 new_ecode) +{ +dev_done = dev_done | INT_RX; /* set done */ +int_req = INT_UPDATE; /* update ints */ +rx_dbr = new_dbr; /* update buffer */ +if (new_ecode != 0) { /* test for error */ + rx_ecode = new_ecode; + rx_err = 1; } +rx_state = IDLE; /* now idle */ +return; +} + +/* Reset routine. The RX is one of the few devices that schedules + an I/O transfer as part of its initialization. +*/ + +t_stat rx_reset (DEVICE *dptr) +{ +rx_esr = rx_ecode = 0; /* clear error */ +rx_tr = rx_err = 0; /* clear flags */ +dev_done = dev_done & ~INT_RX; /* clear done, int */ +int_req = int_req & ~INT_RX; +rx_dbr = rx_csr = 0; /* 12b mode, drive 0 */ +rx_state = INIT_COMPLETE; /* set state */ +sim_cancel (&rx_unit[1]); /* cancel drive 1 */ +sim_activate (&rx_unit[0], /* start drive 0 */ + rx_swait * abs (1 - rx_unit[0].TRACK)); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 022 +#define BOOT_INST 060 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 06755, /* 22, SDN */ + 05022, /* 23, JMP .-1 */ + 07126, /* 24, CLL CML RTL ; read command + */ + 01060, /* 25, TAD UNIT ; unit no */ + 06751, /* 26, LCD ; load read+unit */ + 07201, /* 27, CLL IAC ; AC = 1 */ + 04053, /* 30, JMS 053 ; load sector */ + 04053, /* 31, JMS 053 ; load track */ + 07104, /* 32, CLL RAL ; AC = 2 */ + 06755, /* 33, SDN */ + 05054, /* 34, JMP 54 */ + 06754, /* 35, SER */ + 07450, /* 36, SNA ; more to do? */ + 07610, /* 37, CLA SKP ; error */ + 05046, /* 40, JMP 46 ; go empty */ + 07402, 07402, /* 41-45, HALT ; error */ + 07402, 07402, 07402, + 06751, /* 46, LCD ; load empty */ + 04053, /* 47, JMS 53 ; get data */ + 03002, /* 50, DCA 2 ; store */ + 02050, /* 51, ISZ 50 ; incr store */ + 05047, /* 52, JMP 47 ; loop until done */ + 00000, /* 53, 0 */ + 06753, /* 54, STR */ + 05033, /* 55, JMP 33 */ + 06752, /* 56, XDR */ + 05453, /* 57, JMP I 53 */ + 07024, /* UNIT, CML RAL ; for unit 1 */ + 06030 /* 61, KCC */ +}; + +t_stat rx_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; +extern unsigned int16 M[]; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +M[BOOT_INST] = unitno? 07024: 07004; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp8_sys.c b/pdp8_sys.c new file mode 100644 index 00000000..e11c6d2f --- /dev/null +++ b/pdp8_sys.c @@ -0,0 +1,471 @@ +/* pdp8_sys.c: PDP-8 simulator interface + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface + 10-Apr-98 RMS Added RIM loader support + 17-Feb-97 RMS Fixed bug in handling of bin loader fields +*/ + +#include "pdp8_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE clk_dev, lpt_dev; +extern DEVICE rk_dev, rx_dev; +extern DEVICE rf_dev; +extern DEVICE mt_dev; +extern REG cpu_reg[]; +extern unsigned int16 M[]; +extern int32 sim_switches; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-8"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { &cpu_dev, + &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, + &clk_dev, &lpt_dev, + &rk_dev, &rx_dev, &rf_dev, + &mt_dev, + NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "HALT instruction", + "Breakpoint" }; + +/* Binary loader + + Two loader formats are supported: RIM loader (-r) and BIN loader. + + RIM loader format cosists of alternating pairs of addresses and 12-bit + words. It can only operate in field 0 and is not checksummed. + + BIN loader format consists of a string of 12-bit words (made up from + 7-bit characters) between leader and trailer (200). The last word on + tape is the checksum. A word with the "link" bit set is a new origin; + a character > 0200 indicates a change of field. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, int flag) +{ +int32 rubout, word, low, high, csum, newf, state, i; +t_addr origin, field; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +rubout = state = field = newf = origin = csum = 0; +if (sim_switches & SWMASK ('R')) { /* RIM format? */ + while ((i = getc (fileref)) != EOF) { + switch (state) { + case 0: /* leader */ + if ((i != 0) && (i < 0200)) state = 1; + high = i; + break; + case 1: /* low byte */ + word = (high << 6) | i; /* form word */ + if (word > 07777) origin = word & 07777; + else M[origin] = word; + state = 2; + break; + case 2: /* high byte */ + if (i >= 0200) return SCPE_OK; /* end of tape? */ + high = i; /* save high */ + state = 1; + break; } /* end switch */ + } /* end while */ + } /* end if */ +else { while ((i = getc (fileref)) != EOF) { /* BIN format */ + if (rubout) { + rubout = 0; + continue; } + if (i == 0377) { + rubout = 1; + continue; } + if (i > 0200) { + newf = (i & 070) << 9; + continue; } + switch (state) { + case 0: /* leader */ + if ((i != 0) && (i != 0200)) state = 1; + high = i; /* save as high */ + break; + case 1: /* low byte */ + low = i; + state = 2; + break; + case 2: /* high with test */ + word = (high << 6) | low; + if (i == 0200) { /* end of tape? */ + if ((csum - word) & 07777) return SCPE_CSUM; + return SCPE_OK; } + csum = csum + low + high; + if (word >= 010000) origin = word & 07777; + else { if ((field | origin) >= MEMSIZE) + return SCPE_NXM; + M[field | origin] = word & 07777; + origin = (origin + 1) & 07777; } + field = newf; + high = i; + state = 1; + break; } /* end switch */ + } /* end while */ + } /* end else */ +return SCPE_FMT; /* eof? error */ +} + +/* Symbol tables */ + +#define I_V_FL 18 /* flag start */ +#define I_M_FL 07 /* flag mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_FLD 1 /* field change */ +#define I_V_MRF 2 /* mem ref */ +#define I_V_IOT 3 /* general IOT */ +#define I_V_OP1 4 /* operate 1 */ +#define I_V_OP2 5 /* operate 2 */ +#define I_V_OP3 6 /* operate 3 */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_FLD (I_V_FLD << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_IOT (I_V_IOT << I_V_FL) +#define I_OP1 (I_V_OP1 << I_V_FL) +#define I_OP2 (I_V_OP2 << I_V_FL) +#define I_OP3 (I_V_OP3 << I_V_FL) + +static const int32 masks[] = { + 07777, 07707, 07000, 07000, + 07416, 07571, 017457 }; + +static const char *opcode[] = { + "SKON", "ION", "IOF", "SRQ", + "GTF", "RTF", "SGT", "CAF", + "RPE", "RSF", "RRB", "RFC", "RFC RRB", + "PCE", "PSF", "PCF", "PPC", "PLS", + "KCF", "KSF", "KCC", "KRS", "KIE", "KRB", + "TLF", "TSF", "TCF", "TPC", "SPI", "TLS", + "SBE", "SPL", "CAL", + "CLEI", "CLDI", "CLSC", "CLLE", "CLCL", "CLSK", + "CINT", "RDF", "RIF", "RIB", + "RMF", "SINT", "CUF", "SUF", + "ADCL", "ADLM", "ADST", "ADRB", + "ADSK", "ADSE", "ADLE", "ADRS", + "DCMA", "DMAR", "DMAW", + "DCIM", "DSAC", "DIML", "DIMA", + "DFSE", "DFSC", "DISK", "DMAC", + "DCXA", "DXAL", "DXAC", + "PSKF", "PCLF", "PSKE", + "PSTB", "PSIE", "PCLF PSTB", "PCIE", + "LWCR", "CWCR", "LCAR", + "CCAR", "LCMR", "LFGR", "LDBR", + "RWCR", "CLT", "RCAR", + "RMSR", "RCMR", "RFSR", "RDBR", + "SKEF", "SKCB", "SKJD", "SKTR", "CLF", + "DSKP", "DCLR", "DLAG", + "DLCA", "DRST", "DLDC", "DMAN", + "LCD", "XDR", "STR", + "SER", "SDN", "INTR", "INIT", + "DTRA", "DTCA", "DTXA", "DTLA", + "DTSF", "DTRB", "DTLB", + "CDF", "CIF", "CIF CDF", + "AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT", + "NOP", "NOP2", "NOP3", "SWAB", "SWBA", + "STL", "GLK", "STA", "LAS", "CIA", + "BSW", "RAL", "RTL", "RAR", "RTR", "RAL RAR", "RTL RTR", + "SKP", "SNL", "SZL", + "SZA", "SNA", "SZA SNL", "SNA SZL", + "SMA", "SPA", "SMA SNL", "SPA SZL", + "SMA SZA", "SPA SNA", "SMA SZA SNL", "SPA SNA SZL", + "SCL", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", + "SCA", "SCA SCL", "SCA MUY", "SCA DVI", + "SCA NMI", "SCA SHL", "SCA ASR", "SCA LSR", + "ACS", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", + "SCA", "DAD", "DST", "SWBA", + "DPSZ", "DPIC", "DCIM", "SAM", + "CLA", "CLL", "CMA", "CML", "IAC", /* encode only */ + "CLA", "OAS", "HLT", + "CLA", "MQA", "MQL", + NULL, NULL, NULL, NULL, /* decode only */ + NULL }; + +static const int32 opc_val[] = { + 06000+I_NPN, 06001+I_NPN, 06002+I_NPN, 06003+I_NPN, + 06004+I_NPN, 06005+I_NPN, 06006+I_NPN, 06007+I_NPN, + 06010+I_NPN, 06011+I_NPN, 06012+I_NPN, 06014+I_NPN, 06016+I_NPN, + 06020+I_NPN, 06021+I_NPN, 06022+I_NPN, 06024+I_NPN, 06026+I_NPN, + 06030+I_NPN, 06031+I_NPN, 06032+I_NPN, 06034+I_NPN, 06035+I_NPN, 06036+I_NPN, + 06040+I_NPN, 06041+I_NPN, 06042+I_NPN, 06044+I_NPN, 06045+I_NPN, 06046+I_NPN, + 06101+I_NPN, 06102+I_NPN, 06103+I_NPN, + 06131+I_NPN, 06132+I_NPN, 06133+I_NPN, 06135+I_NPN, 06136+I_NPN, 06137+I_NPN, + 06204+I_NPN, 06214+I_NPN, 06224+I_NPN, 06234+I_NPN, + 06244+I_NPN, 06254+I_NPN, 06264+I_NPN, 06274+I_NPN, + 06530+I_NPN, 06531+I_NPN, 06532+I_NPN, 06533+I_NPN, + 06534+I_NPN, 06535+I_NPN, 06536+I_NPN, 06537+I_NPN, + 06601+I_NPN, 06603+I_NPN, 06605+I_NPN, + 06611+I_NPN, 06612+I_NPN, 06615+I_NPN, 06616+I_NPN, + 06621+I_NPN, 06622+I_NPN, 06623+I_NPN, 06626+I_NPN, + 06641+I_NPN, 06643+I_NPN, 06645+I_NPN, + 06661+I_NPN, 06662+I_NPN, 06663+I_NPN, + 06664+I_NPN, 06665+I_NPN, 06666+I_NPN, 06667+I_NPN, + 06701+I_NPN, 06702+I_NPN, 06703+I_NPN, + 06704+I_NPN, 06705+I_NPN, 06706+I_NPN, 06707+I_NPN, + 06711+I_NPN, 06712+I_NPN, 06713+I_NPN, + 06714+I_NPN, 06715+I_NPN, 06716+I_NPN, 06717+I_NPN, + 06721+I_NPN, 06722+I_NPN, 06723+I_NPN, 06724+I_NPN, 06725+I_NPN, + 06741+I_NPN, 06742+I_NPN, 06743+I_NPN, + 06744+I_NPN, 06745+I_NPN, 06746+I_NPN, 06747+I_NPN, + 06751+I_NPN, 06752+I_NPN, 06753+I_NPN, + 06754+I_NPN, 06755+I_NPN, 06756+I_NPN, 06757+I_NPN, + 06761+I_NPN, 06762+I_NPN, 06764+I_NPN, 06766+I_NPN, + 06771+I_NPN, 06772+I_NPN, 06774+I_NPN, + 06201+I_FLD, 06202+I_FLD, 06203+I_FLD, + 00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF, + 04000+I_MRF, 05000+I_MRF, 06000+I_IOT, + 07000+I_NPN, 07400+I_NPN, 07401+I_NPN, 07431+I_NPN, 07447+I_NPN, + 07120+I_NPN, 07204+I_NPN, 07240+I_NPN, 07604+I_NPN, 07041+I_NPN, + 07002+I_OP1, 07004+I_OP1, 07006+I_OP1, + 07010+I_OP1, 07012+I_OP1, 07014+I_OP1, 07016+I_OP1, + 07410+I_OP2, 07420+I_OP2, 07430+I_OP2, + 07440+I_OP2, 07450+I_OP2, 07460+I_OP2, 07470+I_OP2, + 07500+I_OP2, 07510+I_OP2, 07520+I_OP2, 07530+I_OP2, + 07540+I_OP2, 07550+I_OP2, 07560+I_OP2, 07570+I_OP2, + 07403+I_OP3, 07405+I_OP3, 07407+I_OP3, + 07411+I_OP3, 07413+I_OP3, 07415+I_OP3, 07417+I_OP3, + 07441+I_OP3, 07443+I_OP3, 07445+I_OP3, 07447+I_OP3, + 07451+I_OP3, 07453+I_OP3, 07455+I_OP3, 07457+I_OP3, + 017403+I_OP3, 017405+I_OP3, 0174017+I_OP3, + 017411+I_OP3, 017413+I_OP3, 017415+I_OP3, 017417+I_OP3, + 017441+I_OP3, 017443+I_OP3, 017445+I_OP3, 017447+I_OP3, + 017451+I_OP3, 017453+I_OP3, 017455+I_OP3, 017457+I_OP3, + 07200+I_OP1, 07100+I_OP1, 07040+I_OP1, 07020+I_OP1, 07001+I_OP1, + 07600+I_OP2, 07404+I_OP2, 07402+I_OP2, + 07601+I_OP3, 07501+I_OP3, 07421+I_OP3, + 07000+I_OP1, 07400+I_OP2, 07401+I_OP3, 017401+I_OP3, + -1 }; + +/* Operate decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; } } +return sp; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, sp, inst, disp; +extern int32 emode; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +inst = val[0] | ((emode & 1) << 12); /* include EAE mode */ +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 017777) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + case I_V_FLD: /* field change */ + fprintf (of, "%s %-o", opcode[i], (inst >> 3) & 07); + break; + case I_V_MRF: /* mem ref */ + disp = inst & 0177; /* displacement */ + fprintf (of, "%s%s", opcode[i], ((inst & 00400)? " I ": " ")); + if (inst & 0200) { /* current page? */ + if (cflag) fprintf (of, "%-o", (addr & 07600) | disp); + else fprintf (of, "C %-o", disp); } + else fprintf (of, "%-o", disp); /* page zero */ + break; + case I_V_IOT: /* IOT */ + fprintf (of, "%s %-o", opcode[i], inst & 0777); + break; + case I_V_OP1: /* operate group 1 */ + sp = fprint_opr (of, inst & 0361, j, 0); + if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + case I_V_OP2: /* operate group 2 */ + if (opcode[i]) fprintf (of, "%s", opcode[i]); /* skips */ + fprint_opr (of, inst & 0206, j, opcode[i] != NULL); + break; + case I_V_OP3: /* operate group 3 */ + sp = fprint_opr (of, inst & 0320, j, 0); + if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; } /* end case */ + return SCPE_OK; } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] | 0200; + return SCPE_OK; } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 077) << 6) | + ((t_value) cptr[1] & 077); + return SCPE_OK; } + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 07777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ +case I_V_IOT: /* IOT */ + cptr = get_glyph (cptr, gbuf, 0); /* get dev+pulse */ + d = get_uint (gbuf, 8, 0777, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + break; +case I_V_FLD: /* field */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if (opcode[i] != NULL) { + k = (opc_val[i] >> I_V_FL) & I_M_FL; + if (k != j) return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 07777); } + else { d = get_uint (gbuf, 8, 07, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << 3); + break; } } + break; +case I_V_MRF: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if (strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | 0400; + cptr = get_glyph (cptr, gbuf, 0); } + if ((k = (strcmp (gbuf, "C") == 0)) || (strcmp (gbuf, "Z") == 0)) { + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 8, 0177, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d | (k? 0200: 0); } + else { d = get_uint (gbuf, 8, 07777, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (d <= 0177) val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & 07600) == 0)) + val[0] = val[0] | (d & 0177) | 0200; + else return SCPE_ARG; } + break; +case I_V_NPN: case I_V_OP1: case I_V_OP2: case I_V_OP3: /* operates */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + k = opc_val[i] & 07777; + if ((opcode[i] == NULL) || (((k ^ val[0]) & 07000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; } + break; } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/pdp8_tt.c b/pdp8_tt.c new file mode 100644 index 00000000..6a0bddca --- /dev/null +++ b/pdp8_tt.c @@ -0,0 +1,211 @@ +/* pdp8_tt.c: PDP-8 console terminal simulator + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti terminal input + tto terminal output +*/ + +#include "pdp8_defs.h" +#include + +#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ +#define UNIT_UC (1 << UNIT_V_UC) +extern int32 int_req, dev_done, dev_enable, stop_inst; +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat sim_cancel (UNIT *uptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list + tti_mod TTI modifiers list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_UC, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_TTI) }, + { FLDATA (ENABLE, dev_enable, INT_V_TTI) }, + { FLDATA (INT, int_req, INT_V_TTI) }, + { DRDATA (POS, tti_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (UC, tti_unit.flags, UNIT_V_UC), REG_HRO }, + { NULL } }; + +MTAB tti_mod[] = { + { UNIT_UC, 0, "lower case", "LC", NULL }, + { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, + { 0 } }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_TTO) }, + { FLDATA (ENABLE, dev_enable, INT_V_TTO) }, + { FLDATA (INT, int_req, INT_V_TTO) }, + { DRDATA (POS, tto_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL }; + +/* Terminal input: IOT routine */ + +int32 tti (int32 pulse, int32 AC) +{ +switch (pulse) { /* decode IR<9:11> */ +case 0: /* KCF */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return AC; +case 1: /* KSF */ + return (dev_done & INT_TTI)? IOT_SKP + AC: AC; +case 2: /* KCC */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return 0; /* clear AC */ +case 4: /* KRS */ + return (AC | tti_unit.buf); /* return buffer */ +case 5: /* KIE */ + if (AC & 1) dev_enable = dev_enable | (INT_TTI+INT_TTO); + else dev_enable = dev_enable & ~(INT_TTI+INT_TTO); + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 6: /* KRB */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return (tti_unit.buf); /* return buffer */ +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +temp = temp & 0177; +if ((tti_unit.flags & UNIT_UC) && islower (temp)) + temp = toupper (temp); +tti_unit.buf = temp | 0200; /* got char */ +dev_done = dev_done | INT_TTI; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +tti_unit.pos = tti_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +dev_done = dev_done & ~INT_TTI; /* clear done, int */ +int_req = int_req & ~INT_TTI; +dev_enable = dev_enable | INT_TTI; /* set enable */ +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 pulse, int32 AC) +{ +switch (pulse) { /* decode IR<9:11> */ +case 0: /* TLF */ + dev_done = dev_done | INT_TTO; /* set flag */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; +case 1: /* TSF */ + return (dev_done & INT_TTO)? IOT_SKP + AC: AC; +case 2: /* TCF */ + dev_done = dev_done & ~INT_TTO; /* clear flag */ + int_req = int_req & ~INT_TTO; /* clear int req */ + return AC; +case 5: /* SPI */ + return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC; +case 6: /* TLS */ + dev_done = dev_done & ~INT_TTO; /* clear flag */ + int_req = int_req & ~INT_TTO; /* clear int req */ +case 4: /* TPC */ + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + tto_unit.buf = AC; /* load buffer */ + return AC; +default: + return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 temp; + +dev_done = dev_done | INT_TTO; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((temp = sim_putchar (tto_unit.buf & 0177)) != SCPE_OK) return temp; +tto_unit.pos = tto_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +dev_done = dev_done & ~INT_TTO; /* clear done, int */ +int_req = int_req & ~INT_TTO; +dev_enable = dev_enable | INT_TTO; /* set enable */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/pdp9.c b/pdp9.c new file mode 100644 index 00000000..99c2be08 --- /dev/null +++ b/pdp9.c @@ -0,0 +1 @@ +#define PDP9 0 diff --git a/scp.c b/scp.c new file mode 100644 index 00000000..5cec04da --- /dev/null +++ b/scp.c @@ -0,0 +1,2119 @@ +/* scp.c: simulator control program + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-00 RMS Added output file option to examine + 11-Jul-99 RMS V2.5 changes + 13-Apr-99 RMS Fixed handling of 32b addresses + 04-Oct-98 RMS V2.4 changes + 20-Aug-98 RMS Added radix commands + 05-Jun-98 RMS Fixed bug in ^D handling for UNIX + 10-Apr-98 RMS Added switches to all commands + 26-Oct-97 RMS Added search capability + 25-Jan-97 RMS Revised data types + 23-Jan-97 RMS Added bi-endian I/O + 06-Sep-96 RMS Fixed bug in variable length IEXAMINE + 16-Jun-96 RMS Changed interface to parse/print_sym + 06-Apr-96 RMS Added error checking in reset all + 07-Jan-96 RMS Added register buffers in save/restore + 11-Dec-95 RMS Fixed ordering bug in save/restore + 22-May-95 RMS Added symbolic input + 13-Apr-95 RMS Added symbolic printouts +*/ + +#define SCP 1 /* defining module */ +#include "sim_defs.h" +#include +#include +#include +#define EX_D 0 /* deposit */ +#define EX_E 1 /* examine */ +#define EX_I 2 /* interactive */ +#define SCH_OR 0 /* search logicals */ +#define SCH_AND 1 +#define SCH_XOR 2 +#define SCH_E 0 /* search booleans */ +#define SCH_N 1 +#define SCH_G 2 +#define SCH_L 3 +#define SCH_EE 4 +#define SCH_NE 5 +#define SCH_GE 6 +#define SCH_LE 7 +#define SWHIDE (1u << 26) /* enable hiding */ +#define SRBUFSIZ 1024 /* save/restore buffer */ +#define RU_RUN 0 /* run */ +#define RU_GO 1 /* go */ +#define RU_STEP 2 /* step */ +#define RU_CONT 3 /* continue */ +#define RU_BOOT 4 /* boot */ +#define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \ + x = sim_interval + +extern char sim_name[]; +extern DEVICE *sim_devices[]; +extern REG *sim_PC; +extern char *sim_stop_messages[]; +extern t_stat sim_instr (void); +extern t_stat sim_load (FILE *ptr, char *cptr, int flag); +extern int32 sim_emax; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); +extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, + int32 sw); +extern t_stat ttinit (void); +extern t_stat ttrunstate (void); +extern t_stat ttcmdstate (void); +extern t_stat ttclose (void); +UNIT *sim_clock_queue = NULL; +int32 sim_interval = 0; +int32 sim_switches = 0; +static double sim_time; +static int32 noqueue_time; +volatile int32 stop_cpu = 0; +t_value *sim_eval = NULL; +int32 sim_end = 1; /* 1 = little, 0 = big */ +unsigned char sim_flip[FLIP_SIZE]; + +#define print_val(a,b,c,d) fprint_val (stdout, (a), (b), (c), (d)) +#define SZ_D(dp) (size_map[((dp) -> dwidth + CHAR_BIT - 1) / CHAR_BIT]) +#define SZ_R(rp) \ + (size_map[((rp) -> width + (rp) -> offset + CHAR_BIT - 1) / CHAR_BIT]) +#if defined (int64) +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + j); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + j); \ + else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + j); \ + else v = *(((uint64 *) mb) + j); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + j) = (uint16) v; \ + else if (sz == sizeof (uint32)) *(((uint32 *) mb) + j) = (uint32) v; \ + else *(((uint64 *) mb) + j) = v; +#else +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + j); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + j); \ + else v = *(((uint32 *) mb) + j); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + j) = (uint16) v; \ + else *(((uint32 *) mb) + j) = v; +#endif +#define GET_SWITCHES(cp,gb) \ + for (sim_switches = 0; *cp == '-'; ) { \ + int32 lsw; \ + cp = get_glyph (cp, gb, 0); \ + if ((lsw = get_switches (gb)) <= 0) return SCPE_ARG; \ + sim_switches = sim_switches | lsw; } +#define GET_RADIX(val,dft) \ + if (sim_switches & SWMASK ('O')) val = 8; \ + else if (sim_switches & SWMASK ('D')) val = 10; \ + else if (sim_switches & SWMASK ('H')) val = 16; \ + else val = dft; + +int32 get_switches (char *cptr); +t_value get_rval (REG *rptr, int idx); +void put_rval (REG *rptr, int idx, t_value val, t_value mask); +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); +t_value strtotv (char *inptr, char **endptr, int radix); +t_stat fprint_val (FILE *stream, t_value val, int rdx, int wid, int fmt); +char *read_line (char *ptr, int size, FILE *stream); +DEVICE *find_device (char *ptr, int32 *iptr); +DEVICE *find_dev_from_unit (UNIT *uptr); +REG *find_reg (char *ptr, char **optr, DEVICE *dptr); +t_stat detach_all (int start_device); +t_stat ex_reg (FILE *ofile, t_value val, int flag, REG *rptr); +t_stat dep_reg (int flag, char *cptr, REG *rptr); +t_stat ex_addr (FILE *ofile, int flag, t_addr addr, DEVICE *dptr, UNIT *uptr); +t_stat dep_addr (int flag, char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int dfltinc); +SCHTAB *get_search (char *cptr, DEVICE *dptr, SCHTAB *schptr); +int test_search (t_value val, SCHTAB *schptr); +t_stat step_svc (UNIT *ptr); + +UNIT step_unit = { UDATA (&step_svc, 0, 0) }; +const char save_ver[] = "V2.5"; +const char *scp_error_messages[] = { + "Address space exceeded", + "Unit not attached", + "I/O error", + "Checksum error", + "Format error", + "Unit not attachable", + "File open error", + "Memory exhausted", + "Invalid argument", + "Step expired", + "Unknown command", + "Read only argument", + "Command not completed", + "Simulation stopped", + "Goodbye", + "Console input I/O error", + "Console output I/O error", + "End of file", + "Relocation error", + "No settable parameters", + "Unit already attached" }; + +const size_t size_map[] = { sizeof (int8), + sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32) +#if defined (int64) + , sizeof (int64), sizeof (int64), sizeof (int64), sizeof (int64) +#endif +}; + +const t_value width_mask[] = { 0, + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF +#if defined (int64) + , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF, + 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF, + 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF, + 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF, + 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF +#endif +}; + +int main (int argc, char *argv[]) +{ +char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr; +int32 i, stat; +FILE *fpin; +union {int32 i; char c[sizeof (int32)]; } end_test; +t_stat reset_cmd (int flag, char *ptr); +t_stat exdep_cmd (int flag, char *ptr); +t_stat load_cmd (int flag, char *ptr); +t_stat run_cmd (int flag, char *ptr); +t_stat attach_cmd (int flag, char *ptr); +t_stat detach_cmd (int flag, char *ptr); +t_stat save_cmd (int flag, char *ptr); +t_stat restore_cmd (int flag, char *ptr); +t_stat exit_cmd (int flag, char *ptr); +t_stat set_cmd (int flag, char *ptr); +t_stat show_cmd (int flag, char *ptr); +t_stat add_cmd (int flag, char *ptr); +t_stat remove_cmd (int flag, char *ptr); +t_stat help_cmd (int flag, char *ptr); + +static CTAB cmd_table[] = { + { "RESET", &reset_cmd, 0 }, + { "EXAMINE", &exdep_cmd, EX_E }, + { "IEXAMINE", &exdep_cmd, EX_E+EX_I }, + { "DEPOSIT", &exdep_cmd, EX_D }, + { "IDEPOSIT", &exdep_cmd, EX_D+EX_I }, + { "RUN", &run_cmd, RU_RUN }, + { "GO", &run_cmd, RU_GO }, + { "STEP", &run_cmd, RU_STEP }, + { "CONT", &run_cmd, RU_CONT }, + { "BOOT", &run_cmd, RU_BOOT }, + { "ATTACH", &attach_cmd, 0 }, + { "DETACH", &detach_cmd, 0 }, + { "SAVE", &save_cmd, 0 }, + { "RESTORE", &restore_cmd, 0 }, + { "GET", &restore_cmd, 0 }, + { "LOAD", &load_cmd, 0 }, + { "DUMP", &load_cmd, 1 }, + { "EXIT", &exit_cmd, 0 }, + { "QUIT", &exit_cmd, 0 }, + { "BYE", &exit_cmd, 0 }, + { "SET", &set_cmd, 0 }, + { "SHOW", &show_cmd, 0 }, + { "ADD", &add_cmd, 0 }, + { "REMOVE", &remove_cmd, 0 }, + { "HELP", &help_cmd, 0 }, + { NULL, NULL, 0 } }; + +/* Main command loop */ + +printf ("\n%s simulator V2.5\n", sim_name); +end_test.i = 1; /* test endian-ness */ +sim_end = end_test.c[0]; +if (sim_emax <= 0) sim_emax = 1; +if ((sim_eval = calloc (sim_emax, sizeof (t_value))) == NULL) { + printf ("Unable to allocate examine buffer\n"); + return 0; }; +if ((stat = ttinit ()) != SCPE_OK) { + printf ("Fatal terminal initialization error\n%s\n", + scp_error_messages[stat - SCPE_BASE]); + return 0; } +stop_cpu = 0; +sim_interval = 0; +sim_time = 0; +noqueue_time = 0; +sim_clock_queue = NULL; +if ((stat = reset_all (0)) != SCPE_OK) { + printf ("Fatal simulator initialization error\n%s\n", + scp_error_messages[stat - SCPE_BASE]); + return 0; } + +if ((argc > 1) && (argv[1] != NULL) && + ((fpin = fopen (argv[1], "r")) != NULL)) { /* command file? */ + do { cptr = read_line (cbuf, CBUFSIZE, fpin); + if (cptr == NULL) break; /* exit on eof */ + if (*cptr == 0) continue; /* ignore blank */ + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + for (i = 0; cmd_table[i].name != NULL; i++) { + if (MATCH_CMD (gbuf, cmd_table[i].name) == 0) { + stat = cmd_table[i].action (cmd_table[i].arg, cptr); + break; } } + if (stat >= SCPE_BASE) + printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); + } while (stat != SCPE_EXIT); } /* end if cmd file */ + +do { printf ("sim> "); /* prompt */ + cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */ + stat = SCPE_UNK; + if (cptr == NULL) continue; /* ignore EOF */ + if (*cptr == 0) continue; /* ignore blank */ + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + for (i = 0; cmd_table[i].name != NULL; i++) { + if (MATCH_CMD (gbuf, cmd_table[i].name) == 0) { + stat = cmd_table[i].action (cmd_table[i].arg, cptr); + break; } } + if (stat >= SCPE_BASE) + printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); +} while (stat != SCPE_EXIT); + +detach_all (0); +ttclose (); +return 0; +} + +/* Exit command */ + +t_stat exit_cmd (int flag, char *cptr) +{ +return SCPE_EXIT; +} + +/* Help command */ + +t_stat help_cmd (int flag, char *cptr) +{ +printf ("r{eset} {ALL|} reset simulator\n"); +printf ("e{xamine} examine memory or registers\n"); +printf ("ie{xamine} interactive examine memory or registers\n"); +printf ("d{eposit} deposit in memory or registers\n"); +printf ("id{eposit} interactive deposit in memory or registers\n"); +printf ("l{oad} {} load binary file\n"); +printf ("du(mp) {} dump binary file\n"); +printf ("ru{n} {new PC} reset and start simulation\n"); +printf ("go {new PC} start simulation\n"); +printf ("c{ont} continue simulation\n"); +printf ("s{tep} {n} simulate n instructions\n"); +printf ("b{oot} | bootstrap device\n"); +printf ("at{tach} attach file to simulated unit\n"); +printf ("det{ach} detach file from simulated unit\n"); +printf ("sa{ve} save simulator to file\n"); +printf ("rest{ore}|ge{t} restore simulator from file\n"); +printf ("exi{t}|q{uit}|by{e} exit from simulation\n"); +printf ("set set unit parameter\n"); +printf ("show show device parameters\n"); +printf ("sh{ow} c{onfiguration} show configuration\n"); +printf ("sh{ow} m{odifiers} show modifiers\n"); +printf ("sh{ow} q{ueue} show event queue\n"); +printf ("sh{ow} t{ime} show simulated time\n"); +printf ("ad{d} add unit to configuration\n"); +printf ("rem{ove} remove unit from configuration\n"); +printf ("h{elp} type this message\n"); +return SCPE_OK; +} + +/* Set command */ + +t_stat set_cmd (int flag, char *cptr) +{ +int32 i, unitno; +t_stat r; +char gbuf[CBUFSIZE]; +DEVICE *dptr; +UNIT *uptr; +MTAB *mptr; +t_stat set_radix (DEVICE *dptr, int flag); +static CTAB set_table[] = { + { "OCTAL", &set_radix, 8 }, + { "DECIMAL", &set_radix, 10 }, + { "HEX", &set_radix, 16 }, + { NULL, NULL, 0 } }; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +dptr = find_device (gbuf, &unitno); /* find device */ +if ((dptr == NULL) || (dptr -> units == NULL) || + (*cptr == 0)) return SCPE_ARG; /* argument? */ +cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ +if (*cptr != 0) return SCPE_ARG; /* now eol? */ +uptr = dptr -> units + unitno; +if (uptr -> flags & UNIT_DIS) return SCPE_ARG; /* disabled? */ +for (i = 0; set_table[i].name != NULL; i++) { /* check globals */ + if (MATCH_CMD (gbuf, set_table[i].name) == 0) + return set_table[i].action (dptr, set_table[i].arg); } +if (dptr -> modifiers == NULL) return SCPE_NOPARAM; /* any modifiers? */ +for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) { + if ((mptr -> mstring != NULL) && + (MATCH_CMD (gbuf, mptr -> mstring) == 0)) { + if ((mptr -> valid != NULL) && + ((r = mptr -> valid (uptr, mptr -> match)) != SCPE_OK)) + return r; /* invalid? */ + uptr -> flags = (uptr -> flags & ~(mptr -> mask)) | + (mptr -> match & mptr -> mask); /* set new value */ + return SCPE_OK; } } +return SCPE_ARG; /* no match */ +} + +/* Set radix routine */ + +t_stat set_radix (DEVICE *dptr, int flag) +{ +dptr -> dradix = flag & 017; +return SCPE_OK; +} + +/* Show command */ + +t_stat show_cmd (int flag, char *cptr) +{ +int32 i; +char gbuf[CBUFSIZE]; +DEVICE *dptr; +t_stat show_config (int flag); +t_stat show_queue (int flag); +t_stat show_time (int flag); +t_stat show_modifiers (int flag); +t_stat show_device (DEVICE *dptr); +static CTAB show_table[] = { + { "CONFIGURATION", &show_config, 0 }, + { "QUEUE", &show_queue, 0 }, + { "TIME", &show_time, 0 }, + { "MODIFIERS", &show_modifiers, 0 }, + { NULL, NULL, 0 } }; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_ARG; /* now eol? */ +for (i = 0; show_table[i].name != NULL; i++) { /* find command */ + if (MATCH_CMD (gbuf, show_table[i].name) == 0) + return show_table[i].action (show_table[i].arg); } +dptr = find_device (gbuf, NULL); /* find device */ +if (dptr == NULL) return SCPE_ARG; +return show_device (dptr); +} + +/* Show processors */ + +t_stat show_device (DEVICE *dptr) +{ +int32 j, ucnt; +t_addr kval; +UNIT *uptr; +MTAB *mptr; + +printf ("%s", dptr -> name); /* print dev name */ +for (j = ucnt = 0; j < dptr -> numunits; j++) { + uptr = (dptr -> units) + j; + if (!(uptr -> flags & UNIT_DIS)) ucnt++; } +if (dptr -> numunits == 0) printf ("\n"); +else { if (ucnt == 0) printf (", all units disabled\n"); + else if (ucnt > 1) printf (", %d units\n", ucnt); } +for (j = 0; j < dptr -> numunits; j++) { + uptr = (dptr -> units) + j; + kval = (uptr -> flags & UNIT_BINK)? 1024: 1000; + if (uptr -> flags & UNIT_DIS) continue; + if (ucnt > 1) printf (" unit %d", j); + if (uptr -> flags & UNIT_FIX) { + if (uptr -> capac < kval) + printf (", %d%s", uptr -> capac, + ((dptr -> dwidth / dptr -> aincr) > 8)? "W": "B"); + else printf (", %dK%s", uptr -> capac / kval, + ((dptr -> dwidth / dptr -> aincr) > 8)? "W": "B"); } + if (uptr -> flags & UNIT_ATT) + printf (", attached to %s", uptr -> filename); + else if (uptr -> flags & UNIT_ATTABLE) printf (", not attached"); + if (dptr -> modifiers != NULL) { + for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) { + if ((mptr -> pstring != NULL) && + ((uptr -> flags & mptr -> mask) == mptr -> match)) + printf (", %s", mptr -> pstring); } } + printf ("\n"); } +return SCPE_OK; +} + +t_stat show_config (int flag) +{ +int32 i; +DEVICE *dptr; + +printf ("%s simulator configuration\n\n", sim_name); +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) show_device (dptr); +return SCPE_OK; +} + +t_stat show_queue (int flag) +{ +DEVICE *dptr; +UNIT *uptr; +int32 accum; + +if (sim_clock_queue == NULL) { + printf ("%s event queue empty, time = %-16.0f\n", sim_name, sim_time); + return SCPE_OK; } +printf ("%s event queue status, time = %-16.0f\n", sim_name, sim_time); +accum = 0; +for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr -> next) { + if (uptr == &step_unit) printf (" Step timer"); + else if ((dptr = find_dev_from_unit (uptr)) != NULL) { + printf (" %s", dptr -> name); + if (dptr -> numunits > 1) printf (" unit %d", + uptr - dptr -> units); } + else printf (" Unknown"); + printf (" at %d\n", accum + uptr -> time); + accum = accum + uptr -> time; } +return SCPE_OK; +} + +t_stat show_time (int flag) +{ +printf ("Time: %-16.0f\n", sim_time); +return SCPE_OK; +} + +t_stat show_modifiers (int flag) +{ +int i, any; +DEVICE *dptr; +MTAB *mptr; + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + any = 0; + for (mptr = dptr -> modifiers; mptr && (mptr -> mask != 0); mptr++) { + if (mptr -> mstring) { + if (any++) printf (", %s", mptr -> mstring); + else printf ("%s %s", dptr -> name, mptr -> mstring); } } + if (any) printf ("\n"); } +return SCPE_OK; +} + +/* Add and remove commands and routines + + ad[d] add unit to configuration + rem[ove] remove unit from configuration +*/ + +t_stat add_cmd (int flag, char *cptr) +{ +int32 unitno; +char gbuf[CBUFSIZE]; +DEVICE *dptr; +UNIT *uptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +dptr = find_device (gbuf, &unitno); /* locate device */ +if ((dptr == NULL) || (dptr -> units == NULL) || + (*cptr != 0)) return SCPE_ARG; /* found it? */ +uptr = dptr -> units + unitno; /* locate unit */ +if ((uptr -> flags & UNIT_DISABLE) && (uptr -> flags & UNIT_DIS)) { + uptr -> flags = uptr -> flags & ~UNIT_DIS; /* enable it */ + return SCPE_OK; } +return SCPE_ARG; /* not valid */ +} + +t_stat remove_cmd (int flag, char *cptr) +{ +int32 unitno; +char gbuf[CBUFSIZE]; +DEVICE *dptr; +UNIT *uptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +dptr = find_device (gbuf, &unitno); /* locate device */ +if ((dptr == NULL) || (dptr -> units == NULL) || + (*cptr != 0)) return SCPE_ARG; /* found it? */ +uptr = dptr -> units + unitno; /* locate unit */ +if ((uptr -> flags & UNIT_DISABLE) && !(uptr -> flags & UNIT_DIS) && + !(uptr -> flags & UNIT_ATT) && !sim_is_active (uptr)) { + uptr -> flags = uptr -> flags | UNIT_DIS; /* disable it */ + return SCPE_OK; } +return SCPE_ARG; /* not valid */ +} + +/* Reset command and routines + + re[set] reset all devices + re[set] all reset all devices + re[set] device reset specific device +*/ + +t_stat reset_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +if (*cptr == 0) return (reset_all (0)); /* reset(cr) */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_ARG; /* now (cr)? */ +if (strcmp (gbuf, "ALL") == 0) return (reset_all (0)); +if ((dptr = find_device (gbuf, NULL)) == NULL) return SCPE_ARG; +if (dptr -> reset != NULL) return dptr -> reset (dptr); +else return SCPE_OK; +} + +/* Reset devices start..end + + Inputs: + start = number of starting device + Outputs: + status = error status +*/ + +t_stat reset_all (int start) +{ +DEVICE *dptr; +int32 i; +t_stat reason; + +if (start < 0) return SCPE_ARG; +for (i = 0; i < start; i++) { + if (sim_devices[i] == NULL) return SCPE_ARG; } +for (i = start; (dptr = sim_devices[i]) != NULL; i++) { + if (dptr -> reset != NULL) { + reason = dptr -> reset (dptr); + if (reason != SCPE_OK) return reason; } } +return SCPE_OK; +} + +/* Load and dump commands + + lo[ad] filename {arg} load specified file + du[mp] filename {arg} dump to specified file +*/ + +t_stat load_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +FILE *loadfile; +t_stat reason; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +if (*cptr == 0) return SCPE_ARG; /* must be more */ +cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ +loadfile = fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */ +if (loadfile == NULL) return SCPE_OPENERR; +reason = sim_load (loadfile, cptr, flag); /* load or dump */ +fclose (loadfile); +return reason; +} + +/* Attach command + + at[tach] unit file attach specified unit to file +*/ + +t_stat attach_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +int32 unitno; +DEVICE *dptr; +UNIT *uptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +if (*cptr == 0) return SCPE_ARG; +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr == 0) return SCPE_ARG; +dptr = find_device (gbuf, &unitno); +if ((dptr == NULL) || (dptr -> units == NULL)) return SCPE_ARG; +uptr = (dptr -> units) + unitno; +if (dptr -> attach != NULL) return dptr -> attach (uptr, cptr); +return attach_unit (uptr, cptr); +} + +t_stat attach_unit (UNIT *uptr, char *cptr) +{ +DEVICE *dptr; +t_stat reason; + +if (uptr -> flags & UNIT_DIS) return SCPE_ARG; /* disabled? */ +if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT; /* not attachable? */ +if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; +if (uptr -> flags & UNIT_ATT) { /* already attached? */ + reason = detach_unit (uptr); + if (reason != SCPE_OK) return reason; } +uptr -> filename = calloc (CBUFSIZE, sizeof (char)); +if (uptr -> filename == NULL) return SCPE_MEM; +strncpy (uptr -> filename, cptr, CBUFSIZE); +uptr -> fileref = fopen (cptr, "rb+"); +if (uptr -> fileref == NULL) { + uptr -> fileref = fopen (cptr, "wb+"); + if (uptr -> fileref == NULL) return SCPE_OPENERR; + printf ("%s: creating new file\n", dptr -> name); } +if (uptr -> flags & UNIT_BUFABLE) { + if ((uptr -> filebuf = calloc (uptr -> capac, SZ_D (dptr))) != NULL) { + printf ("%s: buffering file in memory\n", dptr -> name); + uptr -> hwmark = fxread (uptr -> filebuf, SZ_D (dptr), + uptr -> capac, uptr -> fileref); + uptr -> flags = uptr -> flags | UNIT_BUF; } + else if (uptr -> flags & UNIT_MUSTBUF) return SCPE_MEM; } +uptr -> flags = uptr -> flags | UNIT_ATT; +uptr -> pos = 0; +return SCPE_OK; +} + +/* Detach command + + det[ach] all detach all units + det[ach] unit detach specified unit +*/ + +t_stat detach_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +int32 unitno; +DEVICE *dptr; +UNIT *uptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +if (*cptr == 0) return SCPE_ARG; +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_ARG; +if (strcmp (gbuf, "ALL") == 0) return (detach_all (0)); +dptr = find_device (gbuf, &unitno); +if ((dptr == NULL) || (dptr -> units == NULL)) return SCPE_ARG; +uptr = (dptr -> units) + unitno; +if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT; +if (dptr -> detach != NULL) return dptr -> detach (uptr); +return detach_unit (uptr); +} + +/* Detach devices start..end + + Inputs: + start = number of starting device + Outputs: + status = error status +*/ + +t_stat detach_all (int start) +{ +int32 i, j; +t_stat reason; +DEVICE *dptr; +UNIT *uptr; + +if ((start < 0) || (start > 1)) return SCPE_ARG; +for (i = start; (dptr = sim_devices[i]) != NULL; i++) { + for (j = 0; j < dptr -> numunits; j++) { + uptr = (dptr -> units) + j; + if (dptr -> detach != NULL) reason = dptr -> detach (uptr); + else reason = detach_unit (uptr); + if (reason != SCPE_OK) return reason; } } +return SCPE_OK; +} + +t_stat detach_unit (UNIT *uptr) +{ +DEVICE *dptr; + +if (uptr == NULL) return SCPE_ARG; +if (!(uptr -> flags & UNIT_ATT)) return SCPE_OK; +if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK; +uptr -> flags = uptr -> flags & ~UNIT_ATT; +if (uptr -> flags & UNIT_BUF) { + printf ("%s: writing buffer to file\n", dptr -> name); + uptr -> flags = uptr -> flags & ~UNIT_BUF; + rewind (uptr -> fileref); + fxwrite (uptr -> filebuf, SZ_D (dptr), uptr -> hwmark, uptr -> fileref); + if (ferror (uptr -> fileref)) perror ("I/O error"); + free (uptr -> filebuf); + uptr -> filebuf = NULL; } +free (uptr -> filename); +uptr -> filename = NULL; +return (fclose (uptr -> fileref) == EOF)? SCPE_IOERR: SCPE_OK; +} + +/* Save command + + sa[ve] filename save state to specified file +*/ + +t_stat save_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +void *mbuf; +FILE *sfile; +int32 i, j, l, t; +t_addr k, high; +t_value val; +t_stat r; +t_bool zeroflg; +size_t sz; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +#define WRITE_I(xx) fxwrite (&(xx), sizeof (xx), 1, sfile) + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +if (*cptr == 0) return SCPE_ARG; +if ((sfile = fopen (cptr, "wb")) == NULL) return SCPE_OPENERR; +fputs (save_ver, sfile); /* save format version */ +fputc ('\n', sfile); +fputs (sim_name, sfile); /* sim name */ +fputc ('\n', sfile); +WRITE_I (sim_time); /* sim time */ + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ + fputs (dptr -> name, sfile); /* device name */ + fputc ('\n', sfile); + for (j = 0; j < dptr -> numunits; j++) { + uptr = (dptr -> units) + j; + t = sim_is_active (uptr); + WRITE_I (j); /* unit number */ + WRITE_I (t); /* activation time */ + WRITE_I (uptr -> u3); /* unit specific */ + WRITE_I (uptr -> u4); + if (uptr -> flags & UNIT_ATT) fputs (uptr -> filename, sfile); + fputc ('\n', sfile); + if (((uptr -> flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) && + (dptr -> examine != NULL) && + ((high = uptr -> capac) != 0)) { /* memory-like unit? */ + WRITE_I (high); /* write size */ + sz = SZ_D (dptr); + if ((mbuf = calloc (SRBUFSIZ, sz)) == NULL) { + fclose (sfile); + return SCPE_MEM; } + for (k = 0; k < high; ) { + zeroflg = TRUE; + for (l = 0; (l < SRBUFSIZ) && (k < high); + l++, k = k + (dptr -> aincr)) { + r = dptr -> examine (&val, k, uptr, 0); + if (r != SCPE_OK) return r; + if (val) zeroflg = FALSE; + SZ_STORE (sz, val, mbuf, l); + } /* end for l */ + if (zeroflg) { /* all zero's? */ + l = -l; /* invert block count */ + WRITE_I (l); } /* write only count */ + else { + WRITE_I (l); /* block count */ + fxwrite (mbuf, l, sz, sfile); } + } /* end for k */ + free (mbuf); /* dealloc buffer */ + } /* end if mem */ + else { high = 0; + WRITE_I (high); } /* no memory */ + } /* end unit loop */ + j = -1; /* write marker */ + WRITE_I (j); + for (rptr = dptr -> registers; /* loop thru regs */ + (rptr != NULL) && (rptr -> name != NULL); rptr++) { + fputs (rptr -> name, sfile); /* name */ + fputc ('\n', sfile); + for (j = 0; j < rptr -> depth; j++) { /* loop thru values */ + val = get_rval (rptr, j); /* get value */ + WRITE_I (val); } } /* store */ + fputc ('\n', sfile); } /* end registers */ +fputc ('\n', sfile); /* end devices */ +r = (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */ +fclose (sfile); +return r; +} + +/* Restore command + + re[store] filename restore state from specified file +*/ + +t_stat restore_cmd (int flag, char *cptr) +{ +char buf[CBUFSIZE]; +void *mbuf; +FILE *rfile; +int32 i, j, blkcnt, limit, unitno, time; +t_addr k, high; +t_value val, mask, vzro = 0; +t_stat r; +size_t sz; +t_bool v25 = FALSE; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +#define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \ + { fclose (rfile); return SCPE_IOERR; } +#define READ_I(xx) if (fxread (&xx, sizeof (xx), 1, rfile) <= 0) \ + { fclose (rfile); return SCPE_IOERR; } + +GET_SWITCHES (cptr, buf); /* test for switches */ +if (*cptr == 0) return SCPE_ARG; +if ((rfile = fopen (cptr, "rb")) == NULL) return SCPE_OPENERR; +READ_S (buf); /* save ver or sim name */ +if (strcmp (buf, save_ver) == 0) { /* version? */ + v25 = TRUE; /* set flag */ + READ_S (buf); } /* read name */ +if (strcmp (buf, sim_name)) { /* name match? */ + printf ("Wrong system type: %s\n", buf); + fclose (rfile); + return SCPE_OK; } +READ_I (sim_time); /* sim time */ + +for ( ;; ) { /* device loop */ + READ_S (buf); /* read device name */ + if (buf[0] == 0) break; /* last? */ + if ((dptr = find_device (buf, NULL)) == NULL) { + printf ("Invalid device name: %s\n", buf); + fclose (rfile); + return SCPE_INCOMP; } + for ( ;; ) { /* unit loop */ + READ_I (unitno); /* unit number */ + if (unitno < 0) break; + if (unitno >= dptr -> numunits) { + printf ("Invalid unit number %s%d\n", dptr -> name, + unitno); + fclose (rfile); + return SCPE_INCOMP; } + READ_I (time); /* event time */ + uptr = (dptr -> units) + unitno; + sim_cancel (uptr); + if (time > 0) sim_activate (uptr, time - 1); + READ_I (uptr -> u3); /* device specific */ + READ_I (uptr -> u4); + READ_S (buf); /* attached file */ + if (buf[0] != 0) { + uptr -> flags = uptr -> flags & ~UNIT_DIS; + r = attach_unit (uptr, buf); + if (r != SCPE_OK) return r; } + READ_I (high); /* memory capacity */ + if (high > 0) { /* any memory? */ + if (((uptr -> flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) || + (high > uptr -> capac) || (dptr -> deposit == NULL)) { + printf ("Invalid memory bound: %u\n", high); + fclose (rfile); + return SCPE_INCOMP; } + if (v25) { + sz = SZ_D (dptr); + if ((mbuf = calloc (SRBUFSIZ, sz)) == NULL) { + fclose (rfile); + return SCPE_MEM; } + for (k = 0; k < high; ) { + READ_I (blkcnt); + if (blkcnt < 0) limit = -blkcnt; + else limit = fxread (mbuf, sz, blkcnt, rfile); + if (limit <= 0) { + fclose (rfile); + return SCPE_IOERR; } + for (j = 0; j < limit; j++, k = k + (dptr -> aincr)) { + if (blkcnt < 0) val = 0; + else SZ_LOAD (sz, val, mbuf, j); + r = dptr -> deposit (val, k, uptr, 0); + if (r != SCPE_OK) return r; + } /* end for j */ + } /* end for k */ + free (mbuf); /* dealloc buffer */ + } /* end if v25 */ + else { for (k = 0; k < high; k = k + (dptr -> aincr)) { + READ_I (val); + if (((t_svalue) val) < 0) { + for (j = (int32) val + 1; j < 0; j++) { + r = dptr -> deposit (vzro, k, uptr, 0); + if (r != SCPE_OK) return r; + k = k + (dptr -> aincr); } + val = 0; } + r = dptr -> deposit (val, k, uptr, 0); + if (r != SCPE_OK) return r; } + } /* end else v25 */ + } /* end if high */ + } /* end unit loop */ + for ( ;; ) { /* register loop */ + READ_S (buf); /* read reg name */ + if (buf[0] == 0) break; /* last? */ + if ((rptr = find_reg (buf, NULL, dptr)) == NULL) { + printf ("Invalid register name: %s\n", buf); + fclose (rfile); + return SCPE_INCOMP; } + mask = width_mask[rptr -> width]; + for (i = 0; i < rptr -> depth; i++) { /* loop thru values */ + READ_I (val); /* read value */ + if (val > mask) + printf ("Invalid register value: %s\n", buf); + else put_rval (rptr, i, val, mask); } } + } /* end device loop */ +fclose (rfile); +return SCPE_OK; +} + +/* Run, go, cont, step commands + + ru[n] [new PC] reset and start simulation + go [new PC] start simulation + co[nt] start simulation + s[tep] [step limit] start simulation for 'limit' instructions + b[oot] device bootstrap from device and start simulation +*/ + +t_stat run_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +int32 i, j, step, unitno; +t_stat r; +t_addr k; +t_value pcval; +DEVICE *dptr; +UNIT *uptr; +void int_handler (int signal); + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +step = 0; +if (((flag == RU_RUN) || (flag == RU_GO)) && (*cptr != 0)) { /* run or go */ + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if ((r = dep_reg (0, gbuf, sim_PC)) != SCPE_OK) return r; } + +if (flag == RU_STEP) { /* step */ + if (*cptr == 0) step = 1; + else { cptr = get_glyph (cptr, gbuf, 0); + step = (int32) get_uint (gbuf, 10, INT_MAX, &r); + if ((r != SCPE_OK) || (step == 0)) return SCPE_ARG; } } + +if (flag == RU_BOOT) { /* boot */ + if (*cptr == 0) return SCPE_ARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + dptr = find_device (gbuf, &unitno); + if ((dptr == NULL) || (dptr -> units == NULL) || + (dptr -> boot == NULL)) return SCPE_ARG; + uptr = dptr -> units + unitno; + if (uptr -> flags & UNIT_DIS) return SCPE_ARG; /* disabled? */ + if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT; + if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT; + if ((r = dptr -> boot (unitno)) != SCPE_OK) return r; } + +if (*cptr != 0) return SCPE_ARG; + +if ((flag == RU_RUN) || (flag == RU_BOOT)) { /* run or boot */ + sim_interval = 0; /* reset queue */ + sim_time = 0; + noqueue_time = 0; + sim_clock_queue = NULL; + if ((r = reset_all (0)) != SCPE_OK) return r; } +for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { + for (j = 0; j < dptr -> numunits; j++) { + uptr = (dptr -> units) + j; + if ((uptr -> flags & (UNIT_ATT + UNIT_SEQ)) == + (UNIT_ATT + UNIT_SEQ)) + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); } } +stop_cpu = 0; +if ((int) signal (SIGINT, int_handler) == -1) { /* set WRU */ + printf ("Simulator interrupt handler setup failed\n"); + return SCPE_OK; } +if (ttrunstate () != SCPE_OK) { /* set console */ + ttcmdstate (); + printf ("Simulator terminal setup failed\n"); + return SCPE_OK; } +if (step) sim_activate (&step_unit, step); /* set step timer */ +r = sim_instr(); + +ttcmdstate (); /* restore console */ +signal (SIGINT, SIG_DFL); /* cancel WRU */ +sim_cancel (&step_unit); /* cancel step timer */ +if (sim_clock_queue != NULL) { /* update sim time */ + UPDATE_SIM_TIME (sim_clock_queue -> time); } +else { UPDATE_SIM_TIME (noqueue_time); } +#ifdef VMS +printf ("\n"); +#endif +if (r >= SCPE_BASE) printf ("\n%s, %s: ", scp_error_messages[r - SCPE_BASE], + sim_PC -> name); +else printf ("\n%s, %s: ", sim_stop_messages[r], sim_PC -> name); +pcval = get_rval (sim_PC, 0); +print_val (pcval, sim_PC -> radix, sim_PC -> width, + sim_PC -> flags & REG_FMT); +if (((dptr = sim_devices[0]) != NULL) && (dptr -> examine != NULL)) { + for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; + for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr -> aincr) { + if (r = dptr -> examine (&sim_eval[i], k, dptr -> units, + SWMASK ('V')) != SCPE_OK) break; } + if ((r == SCPE_OK) || (i > 0)) { + printf (" ("); + if (fprint_sym (stdout, (t_addr) pcval, sim_eval, NULL, SWMASK('M')) > 0) + fprint_val (stdout, sim_eval[0], dptr -> dradix, + dptr -> dwidth, PV_RZRO); + printf (")"); } } +printf ("\n"); +return SCPE_OK; +} + +/* Run time routines */ + +/* Unit service for step timeout, originally scheduled by STEP n command + + Return step timeout SCP code, will cause simulation to stop +*/ + +t_stat step_svc (UNIT *uptr) +{ +return SCPE_STEP; +} + +/* Signal handler for ^C signal + + Set stop simulation flag +*/ + +void int_handler (int sig) +{ +stop_cpu = 1; +return; +} + +/* Examine/deposit commands + + ex[amine] [modifiers] list examine + de[posit] [modifiers] list val deposit + ie[xamine] [modifiers] list interactive examine + id[eposit] [modifiers] list interactive deposit + + modifiers + @filename output file + -letter(s) switches + devname'n device name and unit number + [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification + + list list of addresses and registers + addr[:addr|-addr] address range + ALL all addresses + register[:register|-register] register range + STATE all registers +*/ + +t_stat exdep_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE], *gptr, *tptr; +int32 unitno, t; +t_bool log; +t_addr low, high; +t_stat reason; +DEVICE *dptr, *tdptr; +UNIT *uptr; +REG *lowr, *highr; +SCHTAB stab, *schptr; +FILE *ofile; +t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int flag, char *ptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr); +t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int flag, char *ptr, + REG *lptr, REG *hptr); + +if (*cptr == 0) return SCPE_ARG; /* err if no args */ +ofile = NULL; /* no output file */ +log = FALSE; +sim_switches = 0; /* no switches */ +schptr = NULL; /* no search */ +stab.logic = SCH_OR; /* default search params */ +stab.bool = SCH_GE; +stab.mask = stab.comp = 0; +dptr = sim_devices[0]; /* default device, unit */ +unitno = 0; +for (;;) { /* loop through modifiers */ + if (*cptr == 0) return SCPE_ARG; /* error if no arguments */ + if (*cptr == '@') { /* output file spec? */ + if (flag != EX_E) return SCPE_ARG; /* examine only */ + if (log) { /* already got one? */ + fclose (ofile); /* one per customer */ + return SCPE_ARG; } + cptr = get_glyph_nc (cptr + 1, gbuf, 0); + ofile = fopen (gbuf, "a"); /* open for append */ + if (ofile == NULL) return SCPE_OPENERR; + log = TRUE; + continue; } /* look for more */ + cptr = get_glyph (cptr, gbuf, 0); + if ((t = get_switches (gbuf)) != 0) { /* try for switches */ + if (t < 0) return SCPE_ARG; /* err if bad switch */ + sim_switches = sim_switches | t; } /* or in new switches */ + else if (get_search (gbuf, dptr, &stab) != NULL) { /* try for search */ + schptr = &stab; } /* set search */ + else if ((tdptr = find_device (gbuf, &t)) != NULL) { /* try for unit */ + dptr = tdptr; /* set as default */ + unitno = t; } + else break; } /* not recognized, break out */ +if ((*cptr == 0) == (flag == 0)) return SCPE_ARG; /* eol if needed? */ +if (dptr -> units == NULL) return SCPE_ARG; /* got a unit? */ + +if (ofile == NULL) ofile = stdout; /* no file? stdout */ +uptr = (dptr -> units) + unitno; +for (gptr = gbuf, reason = SCPE_OK; + (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) { + if (strncmp (gptr, "ALL", strlen ("ALL")) == 0) { + tptr = gptr + strlen ("ALL"); + if ((*tptr == 0) || (*tptr == ',' && tptr++)) { + if ((uptr -> capac == 0) | (flag == EX_E)) return SCPE_ARG; + high = (uptr -> capac) - (dptr -> aincr); + reason = exdep_addr_loop (ofile, schptr, flag, cptr, + 0, high, dptr, uptr); + continue; } } + + if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) { + tptr = gptr + strlen ("STATE"); + if ((*tptr == 0) || (*tptr == ',' && tptr++)) { + if ((lowr = dptr -> registers) == NULL) return SCPE_ARG; + for (highr = lowr; highr -> name != NULL; highr++) ; + sim_switches = sim_switches | SWHIDE; + reason = exdep_reg_loop (ofile, schptr, flag, cptr, + lowr, --highr); + continue; } } + + if (lowr = find_reg (gptr, &tptr, dptr)) { + highr = lowr; + if ((*tptr == '-') || (*tptr == ':')) { + highr = find_reg (tptr + 1, &tptr, dptr); + if (highr == NULL) return SCPE_ARG; } + if ((*tptr == 0) || (*tptr == ',' && tptr++)) { + reason = exdep_reg_loop (ofile, schptr, flag, cptr, + lowr, highr); + continue; } } + + errno = 0; + low = strtoul (gptr, &tptr, dptr -> aradix); + if ((errno == 0) && (gptr != tptr)) { + high = low; + if ((*tptr == '-') || (*tptr == ':')) { + gptr = tptr + 1; + errno = 0; + high = strtoul (gptr, &tptr, dptr -> aradix); + if (errno || (gptr == tptr)) return SCPE_ARG; } + if ((*tptr == 0) || (*tptr == ',' && tptr++)) { + reason = exdep_addr_loop (ofile, schptr, flag, cptr, + low, high, dptr, uptr); + continue; } } + + reason = SCPE_ARG; /* unrecognized */ + } /* end for */ +if (log) fclose (ofile); /* close output file */ +return reason; +} + +/* Loop controllers for examine/deposit + + exdep_reg_loop examine/deposit range of registers + exdep_addr_loop examine/deposit range of addresses +*/ + +t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int flag, char *cptr, + REG *lowr, REG *highr) +{ +t_stat reason; +t_value val; +REG *rptr; + +if ((lowr == NULL) || (highr == NULL)) return SCPE_ARG; +if (lowr > highr) return SCPE_ARG; +for (rptr = lowr; rptr <= highr; rptr++) { + if ((sim_switches & SWHIDE) && (rptr -> flags & REG_HIDDEN)) continue; + val = get_rval (rptr, 0); + if (schptr && !test_search (val, schptr)) continue; + if (flag != EX_D) { + reason = ex_reg (ofile, val, flag, rptr); + if (reason != SCPE_OK) return reason; } + if (flag != EX_E) { + reason = dep_reg (flag, cptr, rptr); + if (reason != SCPE_OK) return reason; } } +return SCPE_OK; +} + +t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int flag, char *cptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr) +{ +t_addr i, mask; +t_stat reason; + +if (uptr -> flags & UNIT_DIS) return SCPE_ARG; /* disabled? */ +reason = 0; +mask = (t_addr) width_mask[dptr -> awidth]; +if ((low > mask) || (high > mask) || (low > high)) return SCPE_ARG; +for (i = low; i <= high; i = i + (dptr -> aincr)) { + reason = get_aval (i, dptr, uptr); /* get data */ + if (reason != SCPE_OK) return reason; /* return if error */ + if (schptr && !test_search (sim_eval[0], schptr)) continue; + if (flag != EX_D) { + reason = ex_addr (ofile, flag, i, dptr, uptr); + if (reason > SCPE_OK) return reason; } + if (flag != EX_E) { + reason = dep_addr (flag, cptr, i, dptr, uptr, reason); + if (reason > SCPE_OK) return reason; } + if (reason < SCPE_OK) i = i - (reason * dptr -> aincr); } +return SCPE_OK; +} + +/* Examine register routine + + Inputs: + ofile = output stream + val = current register value + flag = type of ex/mod command (ex, iex, idep) + rptr = pointer to register descriptor + Outputs: + return = error status +*/ + +t_stat ex_reg (FILE *ofile, t_value val, int flag, REG *rptr) +{ +int32 rdx; + +if (rptr == NULL) return SCPE_ARG; +fprintf (ofile, "%s: ", rptr -> name); +if (!(flag & EX_E)) return SCPE_OK; +GET_RADIX (rdx, rptr -> radix); +fprint_val (ofile, val, rdx, rptr -> width, rptr -> flags & REG_FMT); +if (flag & EX_I) fprintf (ofile, " "); +else fprintf (ofile, "\n"); +return SCPE_OK; +} + +/* Get register value + + Inputs: + rptr = pointer to register descriptor + idx = index (SAVE register buffers only) + Outputs: + return = register value +*/ + +t_value get_rval (REG *rptr, int idx) +{ +size_t sz; +t_value val; + +sz = SZ_R (rptr); +if ((rptr -> depth > 1) && (sz == sizeof (uint8))) + val = *(((uint8 *) rptr -> loc) + idx); +else if ((rptr -> depth > 1) && (sz == sizeof (uint16))) + val = *(((uint16 *) rptr -> loc) + idx); +#if !defined (int64) +else val = *(((uint32 *) rptr -> loc) + idx); +#else +else if (sz <= sizeof (uint32)) + val = *(((uint32 *) rptr -> loc) + idx); +else val = *(((uint64 *) rptr -> loc) + idx); +#endif +val = (val >> rptr -> offset) & width_mask[rptr -> width]; +return val; +} + +/* Deposit register routine + + Inputs: + flag = type of deposit (normal/interactive) + cptr = pointer to input string + rptr = pointer to register descriptor + Outputs: + return = error status +*/ + +t_stat dep_reg (int flag, char *cptr, REG *rptr) +{ +t_stat r; +t_value val, mask; +int32 rdx; +char gbuf[CBUFSIZE]; + +if ((cptr == NULL) || (rptr == NULL)) return SCPE_ARG; +if (rptr -> flags & REG_RO) return SCPE_RO; +if (flag & EX_I) { + cptr = read_line (gbuf, CBUFSIZE, stdin); + if (cptr == NULL) return 1; /* force exit */ + if (*cptr == 0) return SCPE_OK; } /* success */ +errno = 0; +mask = width_mask[rptr -> width]; +GET_RADIX (rdx, rptr -> radix); +val = get_uint (cptr, rdx, mask, &r); +if (r != SCPE_OK) return SCPE_ARG; +if ((rptr -> flags & REG_NZ) && (val == 0)) return SCPE_ARG; +put_rval (rptr, 0, val, mask); +return SCPE_OK; +} + +/* Put register value + + Inputs: + rptr = pointer to register descriptor + idx = index (RESTORE reg buffers only) + val = new value + mask = mask + Outputs: + none +*/ + +void put_rval (REG *rptr, int idx, t_value val, t_value mask) +{ +size_t sz; + +#define PUT_RVAL(sz,rp,id,val,msk) \ + *(((sz *) rp -> loc) + id) = \ + (*(((sz *) rp -> loc) + id) & \ + ~((msk) << (rp) -> offset)) | ((val) << (rp) -> offset) + +sz = SZ_R (rptr); +if ((rptr -> depth > 1) && (sz == sizeof (uint8))) + PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask); +else if ((rptr -> depth > 1) && (sz == sizeof (uint16))) + PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask); +#if !defined (int64) +else PUT_RVAL (uint32, rptr, idx, val, mask); +#else +else if (sz <= sizeof (uint32)) + PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask); +else PUT_RVAL (uint64, rptr, idx, val, mask); +#endif +return; +} + +/* Examine address routine + + Inputs: (sim_eval is an implicit argument) + ofile = output stream + flag = type of ex/mod command (ex, iex, idep) + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + Outputs: + return = if >= 0, error status + if < 0, number of extra words retired +*/ + +t_stat ex_addr (FILE *ofile, int flag, t_addr addr, DEVICE *dptr, UNIT *uptr) +{ +t_stat reason; +int32 rdx; + +fprint_val (ofile, addr, dptr -> aradix, dptr -> awidth, PV_LEFT); +fprintf (ofile, ": "); +if (!(flag & EX_E)) return SCPE_OK; + +GET_RADIX (rdx, dptr -> dradix); +if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) + reason = fprint_val (ofile, sim_eval[0], rdx, dptr -> dwidth, PV_RZRO); +if (flag & EX_I) fprintf (ofile, " "); +else fprintf (ofile, "\n"); +return reason; +} + +/* Get address routine + + Inputs: + flag = type of ex/mod command (ex, iex, idep) + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + Outputs: (sim_eval is an implicit output) + return = error status +*/ + +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr) +{ +int32 i; +t_value mask; +t_addr j, loc; +t_stat reason; +size_t sz; + +if ((dptr == NULL) || (uptr == NULL)) return SCPE_ARG; +mask = width_mask[dptr -> dwidth]; +for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; +for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr -> aincr) { + if (dptr -> examine != NULL) { + reason = dptr -> examine (&sim_eval[i], j, uptr, sim_switches); + if (reason != SCPE_OK) break; } + else { if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT; + if ((uptr -> flags & UNIT_FIX) && (j >= uptr -> capac)) { + reason = SCPE_NXM; + break; } + sz = SZ_D (dptr); + loc = j / dptr -> aincr; + if (uptr -> flags & UNIT_BUF) { + SZ_LOAD (sz, sim_eval[i], uptr -> filebuf, loc); } + else { fseek (uptr -> fileref, sz * loc, SEEK_SET); + fxread (&sim_eval[i], sz, 1, uptr -> fileref); + if ((feof (uptr -> fileref)) && + !(uptr -> flags & UNIT_FIX)) { + reason = SCPE_EOF; + break; } + else if (ferror (uptr -> fileref)) { + clearerr (uptr -> fileref); + reason = SCPE_IOERR; + break; } } } + sim_eval[i] = sim_eval[i] & mask; } +if ((reason != SCPE_OK) && (i == 0)) return reason; +return SCPE_OK; +} + +/* Deposit address routine + + Inputs: + flag = type of deposit (normal/interactive) + cptr = pointer to input string + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + dfltinc = value to return on cr input + Outputs: + return = if >= 0, error status + if < 0, number of extra words retired +*/ + +t_stat dep_addr (int flag, char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int dfltinc) +{ +int32 i, count, rdx; +t_addr j, loc; +t_stat r, reason; +t_value mask; +size_t sz; +char gbuf[CBUFSIZE]; + +if (dptr == NULL) return SCPE_ARG; +if (flag & EX_I) { + cptr = read_line (gbuf, CBUFSIZE, stdin); + if (cptr == NULL) return 1; /* force exit */ + if (*cptr == 0) return dfltinc; } /* success */ +mask = width_mask[dptr -> dwidth]; + +GET_RADIX (rdx, dptr -> dradix); +if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) { + sim_eval[0] = get_uint (cptr, rdx, mask, &reason); + if (reason != SCPE_OK) return reason; } +count = 1 - reason; + +for (i = 0, j = addr; i < count; i++, j = j + dptr -> aincr) { + sim_eval[i] = sim_eval[i] & mask; + if (dptr -> deposit != NULL) { + r = dptr -> deposit (sim_eval[i], j, uptr, sim_switches); + if (r != SCPE_OK) return r; } + else { if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT; + if ((uptr -> flags & UNIT_FIX) && (j >= uptr -> capac)) + return SCPE_NXM; + sz = SZ_D (dptr); + loc = j / dptr -> aincr; + if (uptr -> flags & UNIT_BUF) { + SZ_STORE (sz, sim_eval[i], uptr -> filebuf, loc); + if (loc >= uptr -> hwmark) uptr -> hwmark = loc + 1; } + else { fseek (uptr -> fileref, sz * loc, SEEK_SET); + fxwrite (sim_eval, sz, 1, uptr -> fileref); + if (ferror (uptr -> fileref)) { + clearerr (uptr -> fileref); + return SCPE_IOERR; } } } } +return reason; +} + +/* String processing routines + + read_line read line + + Inputs: + cptr = pointer to buffer + size = maximum size + stream = pointer to input stream + Outputs: + optr = pointer to first non-blank character + NULL if EOF +*/ + +char *read_line (char *cptr, int size, FILE *stream) +{ +char *tptr; + +cptr = fgets (cptr, size, stream); /* get cmd line */ +if (cptr == NULL) { + clearerr (stream); /* clear error */ + return NULL; } /* ignore EOF */ +for (tptr = cptr; tptr < (cptr + size); tptr++) /* remove cr */ + if (*tptr == '\n') *tptr = 0; +while (isspace (*cptr)) cptr++; /* absorb spaces */ +return cptr; +} + +/* get_glyph get next glyph (force upper case) + get_glyph_nc get next glyph (no conversion) + get_glyph_gen get next glyph (general case) + + Inputs: + iptr = pointer to input string + optr = pointer to output string + mchar = optional end of glyph character + flag = TRUE for convert to upper case (_gen only) + Outputs + result = pointer to next character in input string +*/ + +char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc) +{ +while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) { + if (islower (*iptr) && uc) *optr = toupper (*iptr); + else *optr = *iptr; + iptr++; optr++; } +*optr = 0; +if (mchar && (*iptr == mchar)) iptr++; /* skip terminator */ +while (isspace (*iptr)) iptr++; /* absorb spaces */ +return iptr; +} + +char *get_glyph (char *iptr, char *optr, char mchar) +{ +return get_glyph_gen (iptr, optr, mchar, TRUE); +} + +char *get_glyph_nc (char *iptr, char *optr, char mchar) +{ +return get_glyph_gen (iptr, optr, mchar, FALSE); +} + +/* get_yn yes/no question + + Inputs: + cptr = pointer to question + deflt = default answer + Outputs: + result = true if yes, false if no +*/ + +t_stat get_yn (char *ques, t_stat deflt) +{ +char cbuf[CBUFSIZE], *cptr; + +printf ("%s ", ques); +cptr = read_line (cbuf, CBUFSIZE, stdin); +if ((cptr == NULL) || (*cptr == 0)) return deflt; +if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE; +return FALSE; +} + +/* get_uint unsigned number + + Inputs: + cptr = pointer to input string + radix = input radix + max = maximum acceptable value + *status = pointer to error status + Outputs: + val = value +*/ + +t_value get_uint (char *cptr, int radix, t_value max, t_stat *status) +{ +t_value val; +char *tptr; + +val = strtotv (cptr, &tptr, radix); +if ((cptr == tptr) || (val > max) || (*tptr != 0)) *status = SCPE_ARG; +else *status = SCPE_OK; +return val; +} + +/* Find_device find device matching input string + + Inputs: + cptr = pointer to input string + iptr = pointer to unit number (can be null) + Outputs: + result = pointer to device + *iptr = unit number, if valid +*/ + +DEVICE *find_device (char *cptr, int32 *iptr) +{ +int32 i, lenn, unitno; +t_stat r; +DEVICE *dptr; + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + lenn = strlen (dptr -> name); + if (strncmp (cptr, dptr -> name, lenn) != 0) continue; + cptr = cptr + lenn; + if ((*cptr == 0) || (dptr -> numunits == 0)) unitno = 0; + else { unitno = (int32) get_uint (cptr, 10, dptr -> numunits - 1, &r); + if (r != SCPE_OK) return NULL; } + if (iptr != NULL) *iptr = unitno; + return sim_devices[i]; } +return NULL; +} + +/* Find_dev_from_unit find device for unit + + Inputs: + uptr = pointer to unit + Outputs: + result = pointer to device +*/ + +DEVICE *find_dev_from_unit (UNIT *uptr) +{ +DEVICE *dptr; +int32 i, j; + +if (uptr == NULL) return NULL; +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + for (j = 0; j < dptr -> numunits; j++) { + if (uptr == (dptr -> units + j)) return dptr; } } +return NULL; +} + +/* find_reg find register matching input string + + Inputs: + cptr = pointer to input string + optr = pointer to output pointer (can be null) + dptr = pointer to device + Outputs: + result = pointer to register, NULL if error + *optr = pointer to next character in input string +*/ + +REG *find_reg (char *cptr, char **optr, DEVICE *dptr) +{ +char *tptr; +REG *rptr; + +if ((cptr == NULL) || (dptr == NULL) || + (dptr -> registers == NULL)) return NULL; +tptr = cptr; +do { tptr++; } + while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_')); +for (rptr = dptr -> registers; rptr -> name != NULL; rptr++) { + if (strncmp (cptr, rptr -> name, tptr - cptr) == 0) { + if (optr != NULL) *optr = tptr; + return rptr; } } +return NULL; +} + +/* get_switches get switches from input string + + Inputs: + cptr = pointer to input string + Outputs: + sw = switch bit mask + 0 if no switches, -1 if error +*/ + +int32 get_switches (char *cptr) +{ +int32 sw; + +if (*cptr != '-') return 0; +sw = 0; +for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) { + if (isalpha (*cptr) == 0) return -1; + sw = sw | SWMASK (*cptr); } +return sw; +} + +/* Get search specification + + Inputs: + cptr = pointer to input string + dptr = pointer to device + schptr = pointer to search table + Outputs: + return = NULL if error + schptr if valid search specification +*/ + +SCHTAB *get_search (char *cptr, DEVICE *dptr, SCHTAB *schptr) +{ +int c, logop, cmpop; +t_value logval, cmpval; +char *sptr, *tptr; +const char logstr[] = "|&^", cmpstr[] = "=!><"; + +if (*cptr == 0) return NULL; /* check for clause */ +for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */ + if (sptr = strchr (logstr, c)) { /* check for mask */ + logop = sptr - logstr; + logval = strtotv (cptr, &tptr, dptr -> dradix); + if (cptr == tptr) return NULL; + cptr = tptr; } + else if (sptr = strchr (cmpstr, c)) { /* check for bool */ + cmpop = sptr - cmpstr; + if (*cptr == '=') { + cmpop = cmpop + strlen (cmpstr); + cptr++; } + cmpval = strtotv (cptr, &tptr, dptr -> dradix); + if (cptr == tptr) return NULL; + cptr = tptr; } + else return NULL; } /* end while */ +if (logop >= 0) { + schptr -> logic = logop; + schptr -> mask = logval; } +if (cmpop >= 0) { + schptr -> bool = cmpop; + schptr -> comp = cmpval; } +return schptr; +} + +/* Test value against search specification + + Inputs: + val = value to test + schptr = pointer to search table + Outputs: + return = 1 if value passes search criteria, 0 if not +*/ + +int test_search (t_value val, SCHTAB *schptr) +{ +if (schptr == NULL) return 0; +switch (schptr -> logic) { /* case on logical */ +case SCH_OR: + val = val | schptr -> mask; + break; +case SCH_AND: + val = val & schptr -> mask; + break; +case SCH_XOR: + val = val ^ schptr -> mask; + break; } +switch (schptr -> bool) { /* case on comparison */ +case SCH_E: case SCH_EE: + return (val == schptr -> comp); +case SCH_N: case SCH_NE: + return (val != schptr -> comp); +case SCH_G: + return (val > schptr -> comp); +case SCH_GE: + return (val >= schptr -> comp); +case SCH_L: + return (val < schptr -> comp); +case SCH_LE: + return (val <= schptr -> comp); } +return 0; +} + +/* General radix input routine + + Inputs: + inptr = string to convert + endptr = pointer to first unconverted character + radix = radix for input + Outputs: + value = converted value + + On an error, the endptr will equal the inptr. +*/ + +t_value strtotv (char *inptr, char **endptr, int radix) +{ +int nodigit; +t_value val; +int c, digit; + +*endptr = inptr; /* assume fails */ +if ((radix < 2) || (radix > 36)) return 0; +while (isspace (*inptr)) inptr++; /* bypass white space */ +val = 0; +nodigit = 1; +for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */ + if (islower (c)) c = toupper (c); + if (isdigit (c)) digit = c - (int) '0'; /* digit? */ + else digit = c + 10 - (int) 'A'; /* no, letter */ + if (digit >= radix) return 0; /* valid in radix? */ + val = (val * radix) + digit; /* add to value */ + nodigit = 0; } +if (nodigit) return 0; /* no digits? */ +*endptr = inptr; /* result pointer */ +return val; +} + +/* General radix printing routine + + Inputs: + stream = stream designator + val = value to print + radix = radix to print + width = width to print + format = leading zeroes format + Outputs: + status = error status +*/ + +t_stat fprint_val (FILE *stream, t_value val, int radix, + int width, int format) +{ +#define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value))) +t_value owtest, wtest; +int32 d, digit, ndigits; +char dbuf[MAX_WIDTH + 1]; + +for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' '; +dbuf[MAX_WIDTH] = 0; +d = MAX_WIDTH; +do { d = d - 1; + digit = (int32) (val % (unsigned) radix); + val = val / (unsigned) radix; + dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10); + } while ((d > 0) && (val != 0)); + +if (format != PV_LEFT) { + wtest = owtest = radix; + ndigits = 1; + while ((wtest < width_mask[width]) && (wtest >= owtest)) { + owtest = wtest; + wtest = wtest * radix; + ndigits = ndigits + 1; } + if ((MAX_WIDTH - ndigits) < d) d = MAX_WIDTH - ndigits; } +if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR; +return SCPE_OK; +} + +/* Event queue routines + + sim_activate add entry to event queue + sim_cancel remove entry from event queue + sim_process_event process entries on event queue + sim_is_active see if entry is on event queue + sim_atime return absolute time for an entry + sim_gtime return global time + + Asynchronous events are set up by queueing a unit data structure + to the event queue with a timeout (in simulator units, relative + to the current time). Each simulator 'times' these events by + counting down interval counter sim_interval. When this reaches + zero the simulator calls sim_process_event to process the event + and to see if further events need to be processed, or sim_interval + reset to count the next one. + + The event queue is maintained in clock order; entry timeouts are + RELATIVE to the time in the previous entry. + + Sim_process_event - process event + + Inputs: + none + Outputs: + reason reason code returned by any event processor, + or 0 (SCPE_OK) if no exceptions +*/ + +t_stat sim_process_event (void) +{ +UNIT *uptr; +t_stat reason; + +if (stop_cpu) return SCPE_STOP; /* stop CPU? */ +if (sim_clock_queue == NULL) { /* queue empty? */ + UPDATE_SIM_TIME (noqueue_time); /* update sim time */ + sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ + return SCPE_OK; } +UPDATE_SIM_TIME (sim_clock_queue -> time); /* update sim time */ +do { uptr = sim_clock_queue; /* get first */ + sim_clock_queue = uptr -> next; /* remove first */ + uptr -> next = NULL; /* hygiene */ + uptr -> time = 0; + if (sim_clock_queue != NULL) sim_interval = sim_clock_queue -> time; + else sim_interval = noqueue_time = NOQUEUE_WAIT; + if (uptr -> action != NULL) reason = uptr -> action (uptr); + else reason = SCPE_OK; + } while ((reason == SCPE_OK) && (sim_interval == 0)); + +/* Empty queue forces sim_interval != 0 */ + +return reason; +} + +/* Activate (queue) event + + Inputs: + uptr = pointer to unit + event_time = relative timeout + Outputs: + reason = result (SCPE_OK if ok) +*/ + +t_stat sim_activate (UNIT *uptr, int32 event_time) +{ +UNIT *cptr, *prvptr; +int32 accum; + +if (event_time < 0) return SCPE_ARG; +if (sim_is_active (uptr)) return SCPE_OK; /* already active? */ +if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } +else { UPDATE_SIM_TIME (sim_clock_queue -> time); } /* update sim time */ + +prvptr = NULL; +accum = 0; +for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) { + if (event_time < accum + cptr -> time) break; + accum = accum + cptr -> time; + prvptr = cptr; } +if (prvptr == NULL) { /* insert at head */ + cptr = uptr -> next = sim_clock_queue; + sim_clock_queue = uptr; } +else { cptr = uptr -> next = prvptr -> next; /* insert at prvptr */ + prvptr -> next = uptr; } +uptr -> time = event_time - accum; +if (cptr != NULL) cptr -> time = cptr -> time - uptr -> time; +sim_interval = sim_clock_queue -> time; +return SCPE_OK; +} + +/* Cancel (dequeue) event + + Inputs: + uptr = pointer to unit + Outputs: + reason = result (SCPE_OK if ok) + +*/ + +t_stat sim_cancel (UNIT *uptr) +{ +UNIT *cptr, *nptr; + +if (sim_clock_queue == NULL) return SCPE_OK; +UPDATE_SIM_TIME (sim_clock_queue -> time); /* update sim time */ +nptr = NULL; +if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr -> next; +else { for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) { + if (cptr -> next == uptr) { + nptr = cptr -> next = uptr -> next; + break; } } } /* end queue scan */ +if (nptr != NULL) nptr -> time = nptr -> time + uptr -> time; +uptr -> next = NULL; /* hygiene */ +uptr -> time = 0; +if (sim_clock_queue != NULL) sim_interval = sim_clock_queue -> time; +else sim_interval = noqueue_time = NOQUEUE_WAIT; +return SCPE_OK; +} + +/* Test for entry in queue, return activation time + + Inputs: + uptr = pointer to unit + Outputs: + result = absolute activation time + 1, 0 if inactive +*/ + +int32 sim_is_active (UNIT *uptr) +{ +UNIT *cptr; +int32 accum; + +accum = 0; +for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) { + accum = accum + cptr -> time; + if (cptr == uptr) return accum + 1; } +return 0; +} + +/* Return global time + + Inputs: none + Outputs: + time = global time +*/ + +double sim_gtime (void) +{ +if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } +else { UPDATE_SIM_TIME (sim_clock_queue -> time); } +return sim_time; +} + +/* Endian independent binary I/O package + + For consistency, all binary data read and written by the simulator + is stored in little endian data order. That is, in a multi-byte + data item, the bytes are written out right to left, low order byte + to high order byte. On a big endian host, data is read and written + from high byte to low byte. Consequently, data written on a little + endian system must be byte reversed to be usable on a big endian + system, and vice versa. + + These routines are analogs of the standard C runtime routines + fread and fwrite. If the host is little endian, or the data items + are size char, then the calls are passed directly to fread or + fwrite. Otherwise, these routines perform the necessary byte swaps + using an intermediate buffer. +*/ + +size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr) +{ +size_t c, j, nelem, nbuf, lcnt, total; +int32 i, k; +unsigned char *sptr, *dptr; + +if (sim_end || (size == sizeof (char))) + return fread (bptr, size, count, fptr); +if ((size == 0) || (count == 0)) return 0; +nelem = FLIP_SIZE / size; /* elements in buffer */ +nbuf = count / nelem; /* number buffers */ +lcnt = count % nelem; /* count in last buf */ +if (lcnt) nbuf = nbuf + 1; +else lcnt = nelem; +total = 0; +for (i = nbuf; i > 0; i--) { + c = fread (sim_flip, size, (i == 1? lcnt: nelem), fptr); + if (c == 0) return total; + total = total + c; + for (j = 0, sptr = sim_flip, dptr = bptr; j < c; j++) { + for (k = size - 1; k >= 0; k--) *(dptr + k) = *sptr++; + dptr = dptr + size; } } +return total; +} + +size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr) +{ +size_t c, j, nelem, nbuf, lcnt, total; +int32 i, k; +unsigned char *sptr, *dptr; + +if (sim_end || (size == sizeof (char))) + return fwrite (bptr, size, count, fptr); +if ((size == 0) || (count == 0)) return 0; +nelem = FLIP_SIZE / size; /* elements in buffer */ +nbuf = count / nelem; /* number buffers */ +lcnt = count % nelem; /* count in last buf */ +if (lcnt) nbuf = nbuf + 1; +else lcnt = nelem; +total = 0; +for (i = nbuf; i > 0; i--) { + c = (i == 1)? lcnt: nelem; + for (j = 0, sptr = bptr, dptr = sim_flip; j < c; j++) { + for (k = size - 1; k >= 0; k--) + *(dptr + k) = *sptr++; + dptr = dptr + size; } + c = fwrite (sim_flip, size, c, fptr); + if (c == 0) return total; + total = total + c; } +return total; +} + diff --git a/scp_tty.c b/scp_tty.c new file mode 100644 index 00000000..165063a1 --- /dev/null +++ b/scp_tty.c @@ -0,0 +1,357 @@ +/* scp_tty.c: Operating system-dependent terminal I/O routines. + + Copyright (c) 1993-1999, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 18-Aug-98 RMS Added BeOS support + 13-Oct-97 RMS Added NetBSD terminal support + 25-Jan-97 RMS Added POSIX terminal I/O support + 02-Jan-97 RMS Fixed bug in sim_poll_kbd + + ttinit - called once to get initial terminal state + ttrunstate - called to put terminal into run state + ttcmdstate - called to return terminal to command state + ttclose - called once before the simulator exits + sim_poll_kbd - poll for keyboard input + sim_putchar - output character to terminal + + Versions are included for generic (BSD) UNIX and for VMS. + The generic UNIX version works with LINUX. +*/ + +#include "sim_defs.h" +int32 sim_int_char = 005; /* interrupt character */ + +/* VMS routines */ + +#ifdef VMS +#define __TTYROUTINES 0 +#include +#include +#include +#include +#include +#include +#define EFN 0 +unsigned int32 tty_chan = 0; +typedef struct { + unsigned short sense_count; + unsigned char sense_first_char; + unsigned char sense_reserved; + unsigned int32 stat; + unsigned int32 stat2; } SENSE_BUF; +typedef struct { + unsigned int16 status; + unsigned int16 count; + unsigned int32 dev_status; } IOSB; +SENSE_BUF cmd_mode = { 0 }; +SENSE_BUF run_mode = { 0 }; + +t_stat ttinit (void) +{ +unsigned int32 status; +IOSB iosb; +$DESCRIPTOR (terminal_device, "tt"); + +status = sys$assign (&terminal_device, &tty_chan, 0, 0); +if (status != SS$_NORMAL) return SCPE_TTIERR; +status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0, + &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +run_mode = cmd_mode; +run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC); +run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU; +return SCPE_OK; +} + +t_stat ttrunstate (void) +{ +unsigned int status; +IOSB iosb; + +status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, + &run_mode, sizeof (run_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat ttcmdstate (void) +{ +unsigned int status; +IOSB iosb; + +status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, + &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat ttclose (void) +{ +return ttcmdstate (); +} + +t_stat sim_poll_kbd (void) +{ +unsigned int status, term[2]; +unsigned char buf[4]; +IOSB iosb; +SENSE_BUF sense; + +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb, + 0, 0, &sense, 8, 0, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +if (sense.sense_count == 0) return SCPE_OK; +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, + IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, + &iosb, 0, 0, buf, 1, 0, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_OK; +if (buf[0] == sim_int_char) return SCPE_STOP; +return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_putchar (int32 out) +{ +unsigned int status; +char c; +IOSB iosb; + +c = out; +status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT, + &iosb, 0, 0, &c, 1, 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTOERR; +return SCPE_OK; +} +#endif + +/* Win32 routines */ + +#ifdef _WIN32 +#define __TTYROUTINES 0 +#include + +t_stat ttinit (void) +{ +return SCPE_OK; +} + +t_stat ttrunstate (void) +{ +return SCPE_OK; +} + +t_stat ttcmdstate (void) +{ +return SCPE_OK; +} + +t_stat ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_poll_kbd (void) +{ +int c; + +if (!kbhit ()) return SCPE_OK; +c = _getch (); +if ((c & 0177) == '\b') c = 0177; +if ((c & 0177) == sim_int_char) return SCPE_STOP; +return c | SCPE_KFLAG; +} + +t_stat sim_putchar (int32 c) +{ +_putch (c); +return SCPE_OK; +} + +#endif + +/* BSD UNIX routines */ + +#ifdef BSDTTY +#define __TTYROUTINES 0 +#include +#include + +struct sgttyb cmdtty,runtty; /* V6/V7 stty data */ +struct tchars cmdtchars,runtchars; /* V7 editing */ +struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ +int cmdfl,runfl; /* TTY flags */ + +t_stat ttinit (void) +{ +cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */ +runfl = cmdfl | FNDELAY; +if (ioctl (0, TIOCGETP, &cmdtty) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCGETC, &cmdtchars) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCGLTC, &cmdltchars) < 0) return SCPE_TTIERR; +runtty = cmdtty; /* initial run state */ +runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK; +runtchars.t_intrc = sim_int_char; /* interrupt */ +runtchars.t_quitc = 0xFF; /* no quit */ +runtchars.t_startc = 0xFF; /* no host sync */ +runtchars.t_stopc = 0xFF; +runtchars.t_eofc = 0xFF; +runtchars.t_brkc = 0xFF; +runltchars.t_suspc = 0xFF; /* no specials of any kind */ +runltchars.t_dsuspc = 0xFF; +runltchars.t_rprntc = 0xFF; +runltchars.t_flushc = 0xFF; +runltchars.t_werasc = 0xFF; +runltchars.t_lnextc = 0xFF; +return SCPE_OK; /* return success */ +} + +t_stat ttrunstate (void) +{ +runtchars.t_intrc = sim_int_char; /* in case changed */ +fcntl (0, F_SETFL, runfl); /* non-block mode */ +if (ioctl (0, TIOCSETP, &runtty) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSETC, &runtchars) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSLTC, &runltchars) < 0) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat ttcmdstate (void) +{ +fcntl (0, F_SETFL, cmdfl); /* block mode */ +if (ioctl (0, TIOCSETP, &cmdtty) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSETC, &cmdtchars) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSLTC, &cmdltchars) < 0) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat ttclose (void) +{ +return ttcmdstate (); +} + +t_stat sim_poll_kbd (void) +{ +int status; +unsigned char buf[1]; + +status = read (0, buf, 1); +if (status != 1) return SCPE_OK; +else return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_putchar (int32 out) +{ +char c; + +c = out; +write (1, &c, 1); +return SCPE_OK; +} +#endif + +/* POSIX UNIX routines */ + +#ifndef __TTYROUTINES +#include + +struct termios cmdtty, runtty; + +t_stat ttinit (void) +{ +if (tcgetattr (0, &cmdtty) < 0) return SCPE_TTIERR; /* get old flags */ +runtty = cmdtty; +runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON); /* no echo or edit */ +runtty.c_oflag = runtty.c_oflag & ~OPOST; /* no output edit */ +runtty.c_iflag = runtty.c_iflag & ~ICRNL; /* no cr conversion */ +runtty.c_cc[VINTR] = sim_int_char; /* interrupt */ +runtty.c_cc[VQUIT] = 0; /* no quit */ +runtty.c_cc[VERASE] = 0; +runtty.c_cc[VKILL] = 0; +runtty.c_cc[VEOF] = 0; +runtty.c_cc[VEOL] = 0; +runtty.c_cc[VSTART] = 0; /* no host sync */ +runtty.c_cc[VSUSP] = 0; +runtty.c_cc[VSTOP] = 0; +#if defined (VREPRINT) +runtty.c_cc[VREPRINT] = 0; /* no specials */ +#endif +#if defined (VDISCARD) +runtty.c_cc[VDISCARD] = 0; +#endif +#if defined (VWERASE) +runtty.c_cc[VWERASE] = 0; +#endif +#if defined (VLNEXT) +runtty.c_cc[VLNEXT] = 0; +#endif +runtty.c_cc[VMIN] = 0; /* no waiting */ +runtty.c_cc[VTIME] = 0; +#if defined (VDSUSP) +runtty.c_cc[VDSUSP] = 0; +#endif +#if defined (VSTATUS) +runtty.c_cc[VSTATUS] = 0; +#endif +return SCPE_OK; +} + +t_stat ttrunstate (void) +{ +runtty.c_cc[VINTR] = sim_int_char; /* in case changed */ +if (tcsetattr (0, TCSAFLUSH, &runtty) < 0) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat ttcmdstate (void) +{ +if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat ttclose (void) +{ +return ttcmdstate (); +} + +t_stat sim_poll_kbd (void) +{ +int status; +unsigned char buf[1]; + +status = read (0, buf, 1); +if (status != 1) return SCPE_OK; +else return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_putchar (int32 out) +{ +char c; + +c = out; +write (1, &c, 1); +return SCPE_OK; +} +#endif + diff --git a/sim_defs.h b/sim_defs.h new file mode 100644 index 00000000..1c66e9f0 --- /dev/null +++ b/sim_defs.h @@ -0,0 +1,303 @@ +/* sim_defs.h: simulator definitions + + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-Oct-00 RMS Editorial revisions for V2.5 + 11-Jul-99 RMS Added unsigned int data types + 14-Apr-99 RMS Converted t_addr to unsigned + 04-Oct-98 RMS Additional definitions for V2.4 + + The interface between the simulator control package (SCP) and the + simulator consists of the following routines and data structures + + sim_name simulator name string + sim_devices[] array of pointers to simulated devices + sim_PC pointer to saved PC register descriptor + sim_interval simulator interval to next event + sim_stop_messages[] array of pointers to stop messages + sim_instr() instruction execution routine + sim_load() binary loader routine + sim_emax maximum number of words in an instruction + + In addition, the simulator must supply routines to print and parse + architecture specific formats + + print_sym print symbolic output + parse_sym parse symbolic input +*/ + +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* Length specific integer declarations */ + +#define int8 char +#define int16 short +#define int32 int +typedef int t_stat; /* status */ +typedef int t_bool; /* boolean */ +typedef unsigned int8 uint8; +typedef unsigned int16 uint16; +typedef unsigned int32 uint32, t_addr; /* address */ +#if defined (_INT64) && defined (WIN32) +#define int64 __int64 /* for Windows */ +#elif defined (_INT64) +#define int64 long +#endif +#if defined (int64) +typedef unsigned int64 uint64, t_value; /* value */ +typedef int64 t_svalue; /* signed value */ +#else +typedef unsigned int32 t_value; +typedef int32 t_svalue; +#endif + +/* System independent definitions */ + +typedef int32 t_mtrlnt; /* magtape rec lnt */ +#define MTRF(x) ((x) & (1u << 31)) /* record error flg */ +#define MTRL(x) ((x) & ((1u << 31) - 1)) /* record length */ +#define FLIP_SIZE (1 << 16) /* flip buf size */ +#define CBUFSIZE 128 /* string buf size */ + +/* Simulator status codes + + 0 ok + 1 - (SCPE_BASE - 1) simulator specific + SCPE_BASE - n general +*/ + +#define SCPE_OK 0 /* normal return */ +#define SCPE_BASE 32 /* base for messages */ +#define SCPE_NXM (SCPE_BASE + 0) /* nxm */ +#define SCPE_UNATT (SCPE_BASE + 1) /* no file */ +#define SCPE_IOERR (SCPE_BASE + 2) /* I/O error */ +#define SCPE_CSUM (SCPE_BASE + 3) /* loader cksum */ +#define SCPE_FMT (SCPE_BASE + 4) /* loader format */ +#define SCPE_NOATT (SCPE_BASE + 5) /* not attachable */ +#define SCPE_OPENERR (SCPE_BASE + 6) /* open error */ +#define SCPE_MEM (SCPE_BASE + 7) /* alloc error */ +#define SCPE_ARG (SCPE_BASE + 8) /* argument error */ +#define SCPE_STEP (SCPE_BASE + 9) /* step expired */ +#define SCPE_UNK (SCPE_BASE + 10) /* unknown command */ +#define SCPE_RO (SCPE_BASE + 11) /* read only */ +#define SCPE_INCOMP (SCPE_BASE + 12) /* incomplete */ +#define SCPE_STOP (SCPE_BASE + 13) /* sim stopped */ +#define SCPE_EXIT (SCPE_BASE + 14) /* sim exit */ +#define SCPE_TTIERR (SCPE_BASE + 15) /* console tti err */ +#define SCPE_TTOERR (SCPE_BASE + 16) /* console tto err */ +#define SCPE_EOF (SCPE_BASE + 17) /* end of file */ +#define SCPE_REL (SCPE_BASE + 18) /* relocation error */ +#define SCPE_NOPARAM (SCPE_BASE + 19) /* no parameters */ +#define SCPE_ALATT (SCPE_BASE + 20) /* already attached */ +#define SCPE_KFLAG 01000 /* tti data flag */ + +/* Print value format codes */ + +#define PV_RZRO 0 /* right, zero fill */ +#define PV_RSPC 1 /* right, space fill */ +#define PV_LEFT 2 /* left justify */ + +/* Default timing parameters */ + +#define KBD_POLL_WAIT 5000 /* keyboard poll */ +#define SERIAL_IN_WAIT 100 /* serial in time */ +#define SERIAL_OUT_WAIT 10 /* serial output */ +#define NOQUEUE_WAIT 10000 /* min check time */ + +/* Convert switch letter to bit mask */ + +#define SWMASK(x) (1u << (((int) (x)) - ((int) 'A'))) + +/* String match */ + +#define MATCH_CMD(ptr,cmd) strncmp ((ptr), (cmd), strlen (ptr)) + +/* Device data structure */ + +struct device { + char *name; /* name */ + struct unit *units; /* units */ + struct reg *registers; /* registers */ + struct mtab *modifiers; /* modifiers */ + int numunits; /* #units */ + int aradix; /* address radix */ + int awidth; /* address width */ + int aincr; /* addr increment */ + int dradix; /* data radix */ + int dwidth; /* data width */ + t_stat (*examine)(); /* examine routine */ + t_stat (*deposit)(); /* deposit routine */ + t_stat (*reset)(); /* reset routine */ + t_stat (*boot)(); /* boot routine */ + t_stat (*attach)(); /* attach routine */ + t_stat (*detach)(); /* detach routine */ +}; + +/* Unit data structure + + Parts of the unit structure are device specific, that is, they are + not referenced by the simulator control package and can be freely + used by device simulators. Fields starting with 'buf', and flags + starting with 'UF', are device specific. The definitions given here + are for a typical sequential device. +*/ + +struct unit { + struct unit *next; /* next active */ + t_stat (*action)(); /* action routine */ + char *filename; /* open file name */ + FILE *fileref; /* file reference */ + void *filebuf; /* memory buffer */ + t_addr hwmark; /* high water mark */ + int32 time; /* time out */ + int32 flags; /* flags */ + t_addr capac; /* capacity */ + t_addr pos; /* file position */ + int32 buf; /* buffer */ + int32 wait; /* wait */ + int32 u3; /* device specific */ + int32 u4; /* device specific */ +}; + +/* Unit flags */ + +#define UNIT_ATTABLE 000001 /* attachable */ +#define UNIT_RO 000002 /* read only */ +#define UNIT_FIX 000004 /* fixed capacity */ +#define UNIT_SEQ 000010 /* sequential */ +#define UNIT_ATT 000020 /* attached */ +#define UNIT_BINK 000040 /* K = power of 2 */ +#define UNIT_BUFABLE 000100 /* bufferable */ +#define UNIT_MUSTBUF 000200 /* must buffer */ +#define UNIT_BUF 000400 /* buffered */ +#define UNIT_DISABLE 001000 /* disable-able */ +#define UNIT_DIS 002000 /* disabled */ +#define UNIT_V_UF 11 /* device specific */ + +/* Register data structure */ + +struct reg { + char *name; /* name */ + void *loc; /* location */ + int radix; /* radix */ + int width; /* width */ + int offset; /* starting bit */ + int depth; /* save depth */ + int32 flags; /* flags */ +}; + +#define REG_FMT 003 /* see PV_x */ +#define REG_RO 004 /* read only */ +#define REG_HIDDEN 010 /* hidden */ +#define REG_NZ 020 /* must be non-zero */ +#define REG_HRO (REG_RO + REG_HIDDEN) /* hidden, read only */ + +/* Command table */ + +struct ctab { + char *name; /* name */ + t_stat (*action)(); /* action routine */ + int arg; /* argument */ +}; + +/* Modifier table */ + +struct mtab { + int32 mask; /* mask */ + int32 match; /* match */ + char *pstring; /* print string */ + char *mstring; /* match string */ + t_stat (*valid)(); /* validation routine */ +}; + +/* Search table */ + +struct schtab { + int logic; /* logical operator */ + int bool; /* boolean operator */ + t_value mask; /* mask for logical */ + t_value comp; /* comparison for boolean */ +}; + +/* The following macros define structure contents */ + +#define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),(cap),0,0 + +#if defined (__STDC__) || defined (_WIN32) +#define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1 +#define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1 +#define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1 +#define FLDATA(nm,loc,pos) #nm, &(loc), 2, 1, (pos), 1 +#define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1 +#define BRDATA(nm,loc,rdx,wd,dep) #nm, (loc), (rdx), (wd), 0, (dep) +#else +#define ORDATA(nm,loc,wd) "nm", &(loc), 8, (wd), 0, 1 +#define DRDATA(nm,loc,wd) "nm", &(loc), 10, (wd), 0, 1 +#define HRDATA(nm,loc,wd) "nm", &(loc), 16, (wd), 0, 1 +#define FLDATA(nm,loc,pos) "nm", &(loc), 2, 1, (pos), 1 +#define GRDATA(nm,loc,rdx,wd,pos) "nm", &(loc), (rdx), (wd), (pos), 1 +#define BRDATA(nm,loc,rdx,wd,dep) "nm", (loc), (rdx), (wd), 0, (dep) +#endif + +/* Typedefs for principal structures */ + +typedef struct device DEVICE; +typedef struct unit UNIT; +typedef struct reg REG; +typedef struct ctab CTAB; +typedef struct mtab MTAB; +typedef struct schtab SCHTAB; + +/* Function prototypes */ + +#if defined (SCP) /* defining routine? */ +#define EXTERN +#else /* referencing routine */ +#define EXTERN extern +#endif + +EXTERN t_stat sim_process_event (void); +EXTERN t_stat sim_activate (UNIT *uptr, int32 interval); +EXTERN t_stat sim_cancel (UNIT *uptr); +EXTERN int32 sim_is_active (UNIT *uptr); +EXTERN double sim_gtime (void); +EXTERN t_stat attach_unit (UNIT *uptr, char *cptr); +EXTERN t_stat detach_unit (UNIT *uptr); +EXTERN t_stat reset_all (int start_device); +EXTERN size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr); +EXTERN size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr); +EXTERN t_stat get_yn (char *ques, t_stat deflt); +EXTERN char *get_glyph (char *iptr, char *optr, char mchar); +EXTERN char *get_glyph_nc (char *iptr, char *optr, char mchar); +EXTERN t_value get_uint (char *cptr, int radix, t_value max, t_stat *status); +EXTERN t_value strtotv (char *cptr, char **endptr, int radix); diff --git a/simh.doc b/simh.doc new file mode 100644 index 00000000..284935ea --- /dev/null +++ b/simh.doc @@ -0,0 +1,714 @@ +{\rtf1\ansi\ansicpg1252\uc1 \deff0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;} +{\f3\froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f16\froman\fcharset238\fprq2 Times New Roman CE;}{\f17\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f19\froman\fcharset161\fprq2 Times New Roman Greek;} +{\f20\froman\fcharset162\fprq2 Times New Roman Tur;}{\f21\froman\fcharset186\fprq2 Times New Roman Baltic;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0; +\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{ +\widctlpar\adjustright \fs20\cgrid \snext0 Normal;}{\*\cs10 \additive Default Paragraph Font;}}{\*\listtable{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid22442644}{\list\listtemplateid-1866037950{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers +\'01;}\fbias0 \fi-360\li360\jclisttab\tx360 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'03\'00.\'01;}{\levelnumbers\'01\'03;}\fbias0 \fi-360\li360\jclisttab\tx360 }{\listlevel\levelnfc0 +\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'05\'00.\'01.\'02;}{\levelnumbers\'01\'03\'05;}\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0 +\levelindent0{\leveltext\'07\'00.\'01.\'02.\'03;}{\levelnumbers\'01\'03\'05\'07;}\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext +\'09\'00.\'01.\'02.\'03.\'04;}{\levelnumbers\'01\'03\'05\'07\'09;}\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext +\'0b\'00.\'01.\'02.\'03.\'04.\'05;}{\levelnumbers\'01\'03\'05\'07\'09\'0b;}\fbias0 \fi-1080\li1080\jclisttab\tx1080 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext +\'0d\'00.\'01.\'02.\'03.\'04.\'05.\'06;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d;}\fbias0 \fi-1080\li1080\jclisttab\tx1080 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext +\'0f\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f;}\fbias0 \fi-1440\li1440\jclisttab\tx1440 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext +\'11\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.\'08;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f\'11;}\fbias0 \fi-1440\li1440\jclisttab\tx1440 }{\listname ;}\listid155609463}{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid602111040}{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid621424954}{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid717362522}{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid1080104542}{\list\listtemplateid-5054418{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat2\levelspace0\levelindent0{\leveltext +\'01\'00;}{\levelnumbers\'01;}\fbias0 \fi-435\li435\jclisttab\tx435 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03\'00.\'01;}{\levelnumbers\'01\'03;}\fbias0 \fi-435\li435\jclisttab\tx435 }{\listlevel +\levelnfc0\leveljc0\levelfollow0\levelstartat2\levelspace0\levelindent0{\leveltext\'05\'00.\'01.\'02;}{\levelnumbers\'01\'03\'05;}\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'07\'00.\'01.\'02.\'03;}{\levelnumbers\'01\'03\'05\'07;}\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'09\'00.\'01.\'02.\'03.\'04;}{\levelnumbers +\'01\'03\'05\'07\'09;}\fbias0 \fi-1080\li1080\jclisttab\tx1080 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'0b\'00.\'01.\'02.\'03.\'04.\'05;}{\levelnumbers\'01\'03\'05\'07\'09\'0b;}\fbias0 \fi-1080\li1080 +\jclisttab\tx1080 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'0d\'00.\'01.\'02.\'03.\'04.\'05.\'06;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d;}\fbias0 \fi-1440\li1440\jclisttab\tx1440 }{\listlevel +\levelnfc0\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'0f\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f;}\fbias0 \fi-1440\li1440\jclisttab\tx1440 }{\listlevel\levelnfc0\leveljc0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'11\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.\'08;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f\'11;}\fbias0 \fi-1800\li1800\jclisttab\tx1800 }{\listname ;}\listid1262834005} +{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid1272592440} +{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid1339387548} +{\list\listtemplateid1706078190\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid1409111490} +{\list\listtemplateid230209238{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat3\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\fbias0 \fi-360\li360\jclisttab\tx360 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1 +\levellegal\levelspace0\levelindent0{\leveltext\'03\'00.\'01;}{\levelnumbers\'01\'03;}\fbias0 \fi-360\li360\jclisttab\tx360 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext +\'05\'00.\'01.\'02;}{\levelnumbers\'01\'03\'05;}\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'07\'00.\'01.\'02.\'03;}{\levelnumbers\'01\'03\'05\'07;} +\fbias0 \fi-720\li720\jclisttab\tx720 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'09\'00.\'01.\'02.\'03.\'04;}{\levelnumbers\'01\'03\'05\'07\'09;}\fbias0 \fi-720\li720\jclisttab\tx720 } +{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'0b\'00.\'01.\'02.\'03.\'04.\'05;}{\levelnumbers\'01\'03\'05\'07\'09\'0b;}\fbias0 \fi-1080\li1080\jclisttab\tx1080 }{\listlevel\levelnfc0\leveljc0 +\levelfollow0\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'0d\'00.\'01.\'02.\'03.\'04.\'05.\'06;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d;}\fbias0 \fi-1080\li1080\jclisttab\tx1080 }{\listlevel\levelnfc0\leveljc0\levelfollow0 +\levelstartat1\levellegal\levelspace0\levelindent0{\leveltext\'0f\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f;}\fbias0 \fi-1440\li1440\jclisttab\tx1440 }{\listlevel\levelnfc0\leveljc0\levelfollow0\levelstartat1 +\levellegal\levelspace0\levelindent0{\leveltext\'11\'00.\'01.\'02.\'03.\'04.\'05.\'06.\'07.\'08;}{\levelnumbers\'01\'03\'05\'07\'09\'0b\'0d\'0f\'11;}\fbias0 \fi-1440\li1440\jclisttab\tx1440 }{\listname ;}\listid1502426668}{\list\listtemplateid1706078190 +\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid1645162521}{\list\listtemplateid1706078190\listsimple +{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid2006126440}{\list\listtemplateid1706078190\listsimple{\listlevel +\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li720\jclisttab\tx720 }{\listname ;}\listid2076124660}}{\*\listoverridetable{\listoverride\listid155609463 +\listoverridecount0\ls1}{\listoverride\listid1409111490\listoverridecount0\ls2}{\listoverride\listid1272592440\listoverridecount0\ls3}{\listoverride\listid2076124660\listoverridecount0\ls4}{\listoverride\listid717362522\listoverridecount0\ls5} +{\listoverride\listid1339387548\listoverridecount0\ls6}{\listoverride\listid621424954\listoverridecount0\ls7}{\listoverride\listid1080104542\listoverridecount0\ls8}{\listoverride\listid1262834005\listoverridecount0\ls9}{\listoverride\listid2006126440 +\listoverridecount0\ls10}{\listoverride\listid602111040\listoverridecount0\ls11}{\listoverride\listid22442644\listoverridecount0\ls12}{\listoverride\listid1502426668\listoverridecount0\ls13}{\listoverride\listid1645162521\listoverridecount0\ls14}}{\info +{\title Writing a Simulator for the SIMH System}{\author Bob Supnik}{\operator Bob Supnik}{\creatim\yr1997\mo8\dy30\hr19}{\revtim\yr2000\mo11\dy18\hr18\min39}{\version10}{\edmins391}{\nofpages15}{\nofwords5229}{\nofchars29808} +{\*\company Digital Equipment Corporation}{\nofcharsws0}{\vern113}}\widowctrl\ftnbj\aenddoc\hyphcaps0\formshade\viewkind4\viewscale100\pgbrdrhead\pgbrdrfoot \fet0\sectd \linex0\endnhere\sectdefaultcl {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang +{\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang{\pntxtb (} +{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl9 +\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard\plain \widctlpar\adjustright \fs20\cgrid {\f1 Writing a Simulator for the SIMH System +\par Revised 30-Oct-00 for V2.5 +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 1.\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls1\adjustright {\f1 Overview +\par }\pard \widctlpar\adjustright {\f1 +\par SIMH (history simulators) is a set of portable programs, written in C, which simulate various historically interesting computers. This document describes how to design, write, a +nd check out a new simulator for SIMH. It is not an introduction to either the philosophy or external operation of SIMH, and the reader should be familiar with both of those topics before proceeding. Nor is it a guide to the internal design or operation + +of SIMH, except insofar as those areas interact with simulator design. Instead, this manual presents and explains the form, meaning, and operation of the interfaces between simulators and the SIMH simulator control package. It also offers some suggestio +ns for utilizing the services SIMH offers and explains the constraints which all simulators operating within SIMH will experience. +\par +\par Some terminology: Each simulator consists of a standard }{\i\f1 simulator control package}{\f1 (SCP), which provides a control framework and utility routines for a simulator; and a unique }{\i\f1 virtual machine}{\f1 + (VM), which implements the simulated processor and selected peripherals. A VM consists of multiple }{\i\f1 devices}{\f1 , such as the CPU, paper tape reader, disk controller, etc. Each controller consists of a named state space (called }{\i\f1 +registers}{\f1 ) and one or more }{\i\f1 units}{\f1 . Each unit consists of a numbered state space (called a }{\i\f1 data set}{\f1 ). }{\i\f1 }{\f1 The }{\i\f1 host computer}{\f1 is the system on which SIMH runs; the }{\i\f1 target computer}{\f1 + is the system being simulated. +\par +\par SIMH is unabashedly based on the MIMIC simulation system, designed in the late 60\rquote s by Len Fehskens, Mike McCarthy, and Bob Supnik. This document is based on MIMIC\rquote s published interface specification, \ldblquote +How to Write a Virtual Machine for the MIMIC Simulation System\rdblquote , by Len Fehskens and Bob Supnik. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 2.\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls1\adjustright {\f1 Designing a Virtual Machine +\par }\pard \widctlpar\adjustright {\f1 +\par SIMH is written in C. The host system must support (at least) 32-bit data types (64-bit data types for the PDP-10 and other large-word target systems). To cope with the vagaries of C data types, +SIMH defines some unambiguous data types for its interfaces: +\par +\par \tab SIMH data type\tab \tab \tab interpretation in typical C +\par +\par \tab int8\tab \tab \tab \tab char +\par \tab int16\tab \tab \tab \tab short +\par \tab int32\tab \tab \tab \tab long +\par \tab int64\tab \tab \tab \tab long long, _int64 (system specific) +\par \tab t_addr\tab \tab \tab \tab simulated address, int32 +\par \tab t_val\tab \tab \tab \tab simulated value, unsigned int32 +\par \tab t_mtrec\tab \tab \tab \tab mag tape record length, int32 +\par \tab t_stat\tab \tab \tab \tab status code, int +\par \tab t_bool\tab \tab \tab \tab true/false value, int +\par +\par In addition, SIMH defines structures for each of its major data elements +\par +\par \tab }{\b\f1 DEVICE}{\f1 \tab \tab \tab device definition structure +\par \tab }{\b\f1 UNIT}{\f1 \tab \tab \tab \tab unit definition structure +\par \tab }{\b\f1 REG}{\f1 \tab \tab \tab \tab register definition structure +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls1\adjustright {\f1 VM Organization +\par }\pard \widctlpar\adjustright {\f1 +\par A virtual machine (VM) is a collection of devices bound together through their internal logic. Each device is named and corresponds more or less to a hunk of hardware on the real machine; for example: +\par +\par \tab VM device\tab \tab \tab Real machine hardware +\par +\par \tab CPU\tab \tab \tab \tab central processor and main memory +\par \tab PTR\tab \tab \tab \tab paper tape reader controller and paper tape reader +\par \tab TTI\tab \tab \tab \tab console keyboard +\par \tab TTO\tab \tab \tab \tab console output +\par \tab DKP\tab \tab \tab \tab disk pack controller and drives +\par +\par There may +be more than one device per physical hardware entity, as for the console; but for each user-accessible device there must be at least one. One of these devices will have the pre-eminent responsibility for directing simulated operations. Normally, this is + the CPU, but it could be a higher-level entity, such as a bus master. +\par +\par The VM actually runs as a subroutine of the simulator control package (SCP). It provides a master routine for running simulated programs and other routines and data structures to implement SCP\rquote +s command and control functions. The interfaces between a VM and SCP are relatively few: +\par +\par \tab Interface\tab \tab \tab Function +\par +\par \tab char }{\b\f1 sim_name[]}{\f1 \tab \tab simulator name string +\par \tab REG *}{\b\f1 sim_pc}{\f1 \tab \tab \tab pointer to simulated program counter +\par \tab int32 }{\b\f1 sim_emax}{\f1 \tab \tab maximum number of words in an instruction +\par \tab DEVICE *}{\b\f1 sim_devices[]}{\f1 \tab table of pointers to simulated devices, NULL terminated +\par \tab char *}{\b\f1 sim_stop_messages[]}{\f1 \tab table of pointers to error messages +\par \tab t_stat }{\b\f1 sim_load}{\f1 (FILE *fref)\tab binary loader subroutine +\par \tab t_stat }{\b\f1 sim_inst}{\f1 (void)\tab \tab instruction execution subroutine +\par \tab t_stat }{\b\f1 parse_sym}{\f1 (\'85)\tab \tab symbolic instruction parse subroutine (optional) +\par \tab t_stat }{\b\f1 fprint_sym}{\f1 (\'85)\tab \tab symbolic instruction print subroutine (optional) +\par +\par There is no required organization for VM code. The following convention has been used so far. Let name be the }{\i\f1 name}{\f1 of the real system (i1401 for the IBM 1401; pdp1 for the PDP-1; pdp18b for the other 18-bit PDP\rquote +s; pdp8 for the PDP-8; pdp11 for the PDP11; nova for Nova; hp2100 for the HP 21XX; id4 for the Interdata 4): +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls2\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls2\adjustright {\i\f1 name}{\f1 .h contains definition +s for the particular simulator +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls2\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls2\adjustright {\i\f1 name}{\f1 +_sys.c contains all the SCP interfaces except the instruction simulator +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls2\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls2\adjustright {\i\f1 name}{\f1 +_cpu.c contains the instruction simulator and CPU data structures +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls2\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls2\adjustright {\i\f1 name}{\f1 +_stddev.c contains the peripherals which were standard with the real system. +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls2\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls2\adjustright {\i\f1 name}{\f1 +_lp.c contains the line printer. +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls2\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls2\adjustright {\i\f1 name}{\f1 +_mt.c contains the mag tape controller and drives, etc. +\par }\pard \fi1440\widctlpar\adjustright {\f1 +\par }\pard \widctlpar\adjustright {\f1 The SIMH standard definitions are in sim_defs.h, the simulator control package in scp.c, and the operating-system dependent console I/O routines in scp_tty.c. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls1\ilvl1\adjustright {\f1 CPU Organization +\par }\pard \widctlpar\adjustright {\f1 +\par Most CPU\rquote s perform at least the following functions: +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls3\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls3\adjustright {\f1 Time keeping +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls3\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls3\adjustright {\f1 Instruction fetching +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls3\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls3\adjustright {\f1 Address decoding +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls3\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls3\adjustright {\f1 Execution of non-I/O instructions + +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls3\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls3\adjustright {\f1 I/O command processing +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls3\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls3\adjustright {\f1 Interrupt processing +\par }\pard \widctlpar\adjustright {\f1 +\par Instruction execution is actually the least complicated part of the design; memory and I/O organization should be tackled first. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Time Base +\par }\pard \widctlpar\adjustright {\f1 +\par In order to simulate asynchronous events, such as I/O completion, the VM must define and keep a time base. This can be accurate (for example, nanoseconds of executi +on) or arbitrary (for example, number of instructions executed), but it must be consistently used throughout the VM. All existing VM\rquote s count time in instructions. +\par +\par The CPU is responsible for counting down the event counter }{\b\f1 sim_interval}{\f1 and calling the asynchronous event controller }{\b\f1 sim_process_event}{\f1 . The record keeping for timing is done by SCP. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Memory Organization +\par }\pard \widctlpar\adjustright {\f1 +\par The criterion for memory layout is very simple: use the SIMH data type that is as large as (or if necessary, larger than), the word length of + the real machine. Note that the criterion is word length, not addressability: the PDP-11 has byte addressable memory, but it is a 16-bit machine, and its memory is defined as unsigned int16 M[]. It may seem tempting to define memory as a union of int8 +and int16 data types, but this would make the resulting VM endian-dependent. Instead, the VM should be based on the underlying word size of the real machine, and byte manipulation should be done explicitly. Examples: +\par +\par \tab Simulator\tab \tab memory size\tab \tab memory declaration +\par +\par \tab IBM 1401\tab \tab 6-bit\tab \tab \tab int8 +\par \tab PDP-8\tab \tab \tab 12-bit\tab \tab \tab int16 +\par \tab PDP-11, Nova\tab \tab 16-bit\tab \tab \tab int16 +\par \tab PDP-10\tab \tab \tab 36-bit\tab \tab \tab int64 +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.3\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Interrupt Organization +\par }\pard \widctlpar\adjustright {\f1 +\par The design of the VM\rquote s interrupt structure is a complex interaction between efficiency and fidelity to the hardware. If the VM\rquote +s interrupt structure is too abstract, interrupt driven software may not run. On the other hand, if it follows the hardware too literally, it may significantly reduce simulation speed. One rule I can offer is to minimize the fetch-phase cost of i +nterrupts, even if this complicates the (much less frequent) evaluation of the interrupt system following an I/O operation. Another is not to over-generalize; even if the real hardware could support 64 or 256 interrupting devices, the simulators will be +running much smaller configurations. I\rquote ll start with a simple interrupt structure and then offer suggestions for generalization. +\par +\par In the simplest structure, interrupt requests correspond to device flags and are kept in an interrupt request variable, with o +ne flag per bit. The fetch-phase evaluation of interrupts consists of two steps: are interrupts enabled, and is there an interrupt outstanding? If all the interrupt requests are kept as single-bit flags in a variable, the fetch-phase test is very fast: + +\par +\par \tab if (int_enable && int_requests) \{ \'85process interrupt\'85 \} +\par +\par Indeed, if the interrupt enable flag is not visible state, it can be made the highest bit in the interrupt request variable, and the two tests combined: +\par +\par \tab if (int_requests > INT_ENABLE) \{ \'85process interrupt\'85 \} +\par +\par Setting or clearing device flags directly sets or clears the appropriate interrupt request flag: +\par +\par \tab set: \tab int_requests = int_requests | DEVICE_FLAG; +\par \tab clear:\tab int_requests = int_requests & ~DEVICE_FLAG; +\par +\par At a slightly higher complexity, interrupt + requests do not correspond directly to device flags but are based on masking the device flags with an enable (or disable) mask. There are now three parallel variables: interrupt requests, device flags, and interrupt enable mask. The fetch-phase test do +es not change; however, the evaluation of whether an interrupt is pending now requires an extra step: +\par +\par \tab enable:\tab int_requests = device_flags & int_enables; +\par \tab disable:\tab int_requests = device_flags & ~int_disables; +\par +\par If required for interrupt processing, the high +est priority interrupting device can be determined by scanning the interrupt request variable from high priority to low until a set bit is found. The bit position can then be back-mapped through a table to determine the address or interrupt vector of the + interrupting device. +\par +\par At yet higher complexity, the interrupt system may be too complex or too large to evaluate during the fetch-phase. In this case, an interrupt pending flag is created, and it is evaluated by subroutine call whenever a change could occ +ur (start of execution, I/O instruction issued, device time out occurs). This makes fetch-phase evaluation simple and isolates interrupt evaluation to a common subroutine. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.4\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 I/O Dispatching +\par }\pard \widctlpar\adjustright {\f1 +\par I/O dispatching consists of four steps: +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls14\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls14\adjustright {\f1 Identify the I/O command + and analyze for the device address. +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls14\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls14\adjustright {\f1 Locate the selected device. + +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls14\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls14\adjustright {\f1 +Break down the I/O command into standard fields. +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls14\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls14\adjustright {\f1 Call the device processor. +\par }\pard \widctlpar\adjustright {\f1 +\par Analyzing an I/O command is usually easy. Most systems have one or more explicit I/O instructions containing an I +/O command and a device address. Memory mapped I/O is more complicated; the identification of a reference to I/O space becomes part of memory addressing. This usually requires centralizing memory reads and writes into subroutines, rather than as inline +code. +\par +\par Once an I/O command has been analyzed, the CPU must locate the device subroutine. The simplest way is a large switch statement with hardwired subroutine calls. Slightly more modular is to call through a dispatch table, with NULL entries representin +g non-existant devices. Before calling the device routine, the CPU usually breaks down the I/O command into standard fields. This simplifies writing the peripheral simulator. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.5\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Instruction Execution +\par }\pard \widctlpar\adjustright {\f1 +\par Instruction execution is the responsibility of VM subroutine }{\b\f1 sim_instr}{\f1 . It is called from SCP as a result of a RUN, GO, CONT, or BOOT command. It begins executing instructions at the current PC (}{\b\f1 sim_PC}{\f1 + points to its register description block) and continues until halted by an error or an external event. +\par +\par Whe +n called, the CPU needs to account for any state changes that the user made. For example, it may need to re-evaluate whether an interrupt is pending, or restore frequently used state to local register variables for efficiency. The actual instruction fet +ch and execute cycle is usually structured as a loop controlled by an error variable, e.g., +\par +\par \tab reason = 0; +\par \tab do \{ \'85 \} while (reason == 0);\tab or\tab while (reason == 0) \{ \'85 \} +\par +\par Within this loop, the usual order of events is: +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls4\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls4\adjustright {\f1 +If the event timer sim_interval has reached zero, process any timed events. This is done by SCP subroutine sim_process_event (void). Because this is the polling mechanism for user-generated processor halts (^E), errors must be recognized immediately: + +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par }\pard \li1440\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 if (sim_interval <= 0) \{ +\par }\pard \fi720\li1440\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 if (reason = sim_process_event ()) break; \} +\par }\pard \fi2160\widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls4\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls4\adjustright {\f1 +Check for outstanding interrupts and process if required. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls4\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls4\adjustright {\f1 +Check for other processor-unique events, such as wait-state outstanding or traps outstanding. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls4\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls4\adjustright {\f1 +Check for an instruction breakpoint. SCP has no breakpoint facility, but it is customary to implement a single instruction breakpoint to help with processor code. All the existing CPU\rquote s use the same mechanism, see the sources for details. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls4\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls4\adjustright {\f1 +Fetch the next instruction, increment the PC, optionally decode the address, and dispatch (via a switch statement) for execution. +\par }\pard \widctlpar\adjustright {\f1 +\par A few guidelines for implementation: +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls5\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls5\adjustright {\f1 +In general, code should reflect the hardware being simulated. This is usually simplest and easiest to debug. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls5\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls5\adjustright {\f1 +The VM should provide some debugging aids. The existing CPU\rquote s all provide an instruction breakpoint, an OLDPC register, and error stops on invalid instructions or operations. +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls1\ilvl1\adjustright {\f1 Peripheral Device Organization +\par }\pard \widctlpar\adjustright {\f1 +\par The basic elements of a VM are devices, each corresponding roughly to a real chunk of hardware. A device cons +ists of register-based state and one or more units. Thus a multi-drive disk subsystem is a single device (representing the hardware of the real controller) and one or more units (each representing a single disk drive). Sometimes the device and its unit +are the same entity as, for example, in the case of a paper tape reader. However, a single physical hardware device, such as the console, may be broken up for convenience into separate input and output devices. +\par +\par In general, units correspond to individual s +ources of input or output (one tape transport, one A-to-D channel). Units are the basic medium for both device timing and device I/O. Except for the console, all I/O devices are simulated as host-resident files. SCP allows the user to make an explicit +association between a host-resident file and a simulated hardware entity. +\par +\par Both devices and units have state. Devices operate on }{\i\f1 registers}{\f1 , which contain information about the state of the device, and indirectly, about the state of the units. Units operate on }{\i\f1 data sets}{\f1 +, which may be thought of as individual instances of input or output, such as a disk pack or a punched paper tape. In a typical multi-unit device, all units are the same, and the device performs similar operations on all of them, depending o +n which one has been selected by the program being simulated. Schematically: +\par +\par (Note: SIMH, like MIMIC, restricts registers to devices. This requires replicated registers, for example, disk drive current state, to have unique names in the device name space.) +\par +\par For each structural level, SIMH defines, and the VM must supply, a corresponding data structure. }{\b\f1 device}{\f1 structures correspond to devices, }{\b\f1 reg}{\f1 structures to registers, and }{\b\f1 unit}{\f1 + structures to units. These structures are described in detail in section 3. +\par +\par The primary functions of a peripheral are: +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls6\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls6\adjustright {\f1 command decoding and execution + +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls6\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls6\adjustright {\f1 device timing +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls6\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls6\adjustright {\f1 data transmission. +\par }\pard \widctlpar\adjustright {\f1 +\par Command decoding is fairly obvious. At least one section of the peripheral code module will be devoted to processing directives issued by the +CPU. Typically, the command decoder will be responsible for register and flag manipulation, and for issuing or canceling I/O requests. The former is easy, but the later requires a thorough understanding of device timing. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Device Timing +\par }\pard \widctlpar\adjustright {\f1 +\par The principal pro +blem in I/O device simulation is imitating asynchronous operations in a sequential simulation environment. Fortunately, the timing characteristics of most I/O devices do not vary with external circumstances. The distinction between devices whose timing +i +s externally generated (e.g., console keyboard) and those whose timing is externally generated (disk, paper tape reader) is crucial. With an externally timed device, there is no way to know when an in-progress operation will begin or end; with an interna +lly timed device, given the time when an operation starts, the end time can be calculated. +\par +\par For an internally timed device, the elapsed time between the start and conclusion of an operation is called the wait time. Some typical internally timed devices and their wait times include: +\par +\par \tab PTR (300 char/sec)\tab \tab 3.3 msec +\par \tab PTP (50 char/sec)\tab \tab 20 msec +\par \tab CLK (line frequency)\tab \tab 16.6 msec +\par \tab TTO (30 char/sec)\tab \tab 33 msec +\par +\par Mass storage devices, such as disks and tapes, do not have a fixed response time, but a start-to-finish time can be calculated based on current versus desired position, state of motion, etc. +\par +\par For an externally timed device, there is no portable mechanism by which a VM can be notified of an external event. Because the only important externally timed device is the console keyboard, all current VM\rquote +s poll for keyboard input, thus converting the externally timed keyboard to a pseudo-internally timed device. +\par +\par SCP provides the supporting routines for device timing. SCP maintains a list of devices (called }{\i\f1 active devices}{\f1 ) which are in the process of timing out. It also provides routines for querying or manipulating this list (called the }{\i\f1 +active queue}{\f1 ). Lastly, it provides a routine for checking for timed-out units and executing a VM-specified action when a time-out occurs. +\par +\par Device timing is done with the UNIT structure, described in section 3. To set up a timed operation, the peripheral calculates a waiting period for a unit and places that unit on the active queue. The CPU counts down the waiting period. When the wai +ting period has expired, sim_process_event removes the unit from the active queue and calls a device subroutine. A device may also cancel an outstanding timed operation and query the state of the queue. The timing subroutines are: +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls7\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls7\adjustright {\f1 t_stat }{\b\f1 sim_activate}{\f1 + (U +NIT *uptr, int32 wait). This routine places the specified unit on the active queue with the specified waiting period. A waiting period of 0 is legal; negative waits cause an error. If the unit is already active, the active queue is not changed, and no +error occurs. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls7\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls7\adjustright {\f1 t_stat }{\b\f1 sim_cancel}{\f1 + (UNIT *uptr). This routine removes the specified unit from the active queue. If the unit is not on the queue, no error occurs. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls7\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls7\adjustright {\f1 int32 }{\b\f1 sim_is_active}{\f1 + (UNIT *uptr). This routine tests whether a unit is in the active queue. If it is, the routine returns the time (+1) remaining; if it is not, the routine returns 0. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls7\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls7\adjustright {\f1 double }{\b\f1 sim_gtime}{\f1 + (void). This routine returns the time elapsed since the last RUN or BOOT command. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls7\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls7\adjustright {\f1 t_stat }{\b\f1 sim_process_event}{ +\f1 (void). This routine removes all timed out units from the active queue and calls the appropriate device subroutine to service the time-out. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls7\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls7\adjustright {\f1 int32 }{\b\f1 sim_interval}{\f1 +. This variable counts down the first outstanding timed event. If there are no timed events outstanding, SCP counts down a \ldblquote null interval\rdblquote of 10,000 time units. +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Data I/O +\par }\pard \widctlpar\adjustright {\f1 +\par For most devices, timing is half the battle (for clocks it is the entire war); the other half is I/O. Except for the console, all I/O devices are simulated as files on the host file system in little-endian format. + SCP provides facilities for associating files with units (ATTACH command) and for reading and writing data from and to devices in a endian- and size-independent way. +\par +\par For most devices, the VM designer does not have to be concerned about the formatting of simulated device files. I/O occurs in 1, 2, or 4 byte quantities; SCP automatically chooses the correct data size and corrects for byte ordering. Specific issues: + +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls8\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls8\adjustright {\f1 +Line printers should write data as 7-bit ASCII, with newlines replacing carriage-return/line-feed sequences. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls8\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls8\adjustright {\f1 +Disks should be viewed as linear data sets, from sector 0 of surface 0 of cylinder 0 to the last sector on the disk. This allows easy transcription of real disks to files usable by the simulator. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls8\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls8\adjustright {\f1 +Magtapes, by convention, use a record +based format. Each record consists of a leading 32-bit record length, the record data (padded with a byte of 0 if the record length is odd), and a trailing 32-bit record length. File marks are recorded as one record length of 0. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls8\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls8\adjustright {\f1 Cards have 12 bits of da +ta per column, but the data is most conveniently viewed as (ASCII) characters. Existing card reader simulators do not support binary operation. +\par }\pard \widctlpar\adjustright {\f1 +\par Data I/O varies between fixed and variable capacity devices, and between buffered and non-buffered devices. A + fixed capacity device differs from a variable capacity device in that the file attached to the former has a maximum size, while the file attached to the latter may expand indefinitely. A buffered device differs from a non-buffered device in that the for +mer buffers its data set in host memory, while the latter maintains it as a file. Most variable capacity devices (such as the paper tape reader and punch) are sequential; all buffered devices are fixed capacity. +\par +\par 2.3.2.1 Reading and Writing Data +\par +\par The ATTACH command creates an association between a host file and an I/O unit. For non-buffered devices, ATTACH stores the file pointer for the host file in the }{\b\f1 fileref}{\f1 + field of the UNIT structure. For buffered devices, ATTACH reads the entire host file into an allocated buffer pointed to by the }{\b\f1 filebuf }{\f1 field of the UNIT structure. +\par +\par For non-buffered devices, I/O is done with standard C subroutines plus the SCP routines }{\b\f1 fxread}{\f1 and }{\b\f1 fxwrite}{\f1 . }{\b\f1 fxread}{\f1 and }{\b\f1 fxwrite}{\f1 are identical in calling sequence and function to fread +and fwrite, respectively, but will correct for endian dependencies. For buffered devices, I/O is done by copying data to or from the allocated buffer. The device code must maintain the number (+1) of the highest address modified in the }{\b\f1 hwmark}{ +\f1 field of the UNIT structure. For both the non-buffered and buffered cases, the device must perform all address calculations and positioning operations. +\par +\par The DETACH command breaks the association between a host file and an I/O unit. For buffered devices, DETACH writes the allocated buffer back to the host file. +\par +\par 2.3.2.2 Console I/O +\par +\par SCP provides two routines for console I/O. +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls10\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls10\adjustright {\f1 t_stat }{\b\f1 sim_poll_char }{ +\f1 (void). This routine polls for keyboard input. If there is a character, it returns SCPE_KFLAG + the character. If the user typed the interrupt character (^E), it returns SCPE_STOP. If there is no input, it returns SCPE_OK. +\par }\pard \widctlpar{\*\pn \pnlvlcont\ilvl0\ls0\pnrnot0\pndec }\adjustright {\f1 +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li720\widctlpar\jclisttab\tx720{\*\pn \pnlvlblt\ilvl0\ls10\pnrnot0\pnf3\pnstart1\pnindent720\pnhang{\pntxtb \'b7}}\ls10\adjustright {\f1 t_stat }{\b\f1 sim_putchar}{\f1 + (int32 char). This routine types the specified ASCII character on the console. There are no errors. +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\adjustright {\f1 Data Structures +\par }\pard \widctlpar\adjustright {\f1 +\par The devices, units, and registers which make up a VM are formally described through a set of data structures which interface the VM to the control portions of SCP. The devices themselves are pointed to by the device list array }{\b\f1 sim_devices[]}{\f1 +. Within a device, both units and registers are allocated contiguously as arrays of structures. In addition, many devices allow the user to set or clear options via a modifications table. +\par +\par {\listtext\pard\plain\b\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\b\f1 device}{\f1 Structure +\par }\pard \widctlpar\adjustright {\f1 +\par Devices are defined by the }{\b\f1 device}{\f1 structure (typedef }{\b\f1 DEVICE}{\f1 ), which has the following fields: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 struct device \{ +\par \tab char\tab \tab *name;\tab \tab \tab \tab /* name */ +\par \tab struct unit \tab *units;\tab \tab \tab \tab /* units */ +\par \tab struct reg\tab *registers;\tab \tab \tab /* registers */ +\par \tab struct mtab\tab *modifiers;\tab \tab \tab /* modifiers */ +\par \tab int\tab \tab numunits;\tab \tab \tab /* #units */ +\par \tab int\tab \tab aradix;\tab \tab \tab \tab /* address radix */ +\par \tab int\tab \tab awidth;\tab \tab \tab \tab /* address width */ +\par \tab int\tab \tab aincr;\tab \tab \tab \tab /* addr increment */ +\par \tab int\tab \tab dradix;\tab \tab \tab \tab /* data radix */ +\par \tab int\tab \tab dwidth;\tab \tab \tab \tab /* data width */ +\par \tab t_stat\tab \tab (*examine)();\tab \tab \tab /* examine routine */ +\par \tab t_stat\tab \tab (*deposit)();\tab \tab \tab /* deposit routine */ +\par \tab t_stat\tab \tab (*reset)();\tab \tab \tab /* reset routine */ +\par \tab t_stat\tab \tab (*boot)();\tab \tab \tab /* boot routine */ +\par \tab t_stat\tab \tab (*attach)();\tab \tab \tab /* attach routine */ +\par \tab t_stat\tab \tab (*detach)();\tab \tab \tab /* detach routine */ +\par \}; +\par }\pard \widctlpar\adjustright {\f1 +\par The fields are the following: +\par +\par }\pard \li720\widctlpar\adjustright {\b\f1 name}{\f1 \tab \tab device name, string of all capital alphanumeric characters. +\par }{\b\f1 units}{\f1 \tab \tab pointer to array of }{\b\f1 unit}{\f1 structures, or NULL if none. +\par }{\b\f1 registers}{\f1 \tab pointer to array of }{\b\f1 reg}{\f1 structures, or NULL if none. +\par }{\b\f1 modifiers}{\f1 \tab pointer to array of }{\b\f1 mtab}{\f1 structures, or NULL if none. +\par }{\b\f1 numunits}{\f1 \tab number of units in this device. +\par }{\b\f1 aradix}{\f1 \tab \tab radix for input and display of device addresses, 2 to 16 inclusive. +\par }{\b\f1 awidth}{\f1 \tab \tab width in bits of a device address, 1 to 31 inclusive. +\par }\pard \fi-1440\li2160\widctlpar\adjustright {\b\f1 aincr}{\f1 \tab increment between device addresses, normally 1; however, byte addressed devices with 16-bit words specify 2, with 32-bit words 4. +\par }\pard \li720\widctlpar\adjustright {\b\f1 dradix}{\f1 \tab \tab radix for input and display of device data, 2 to 16 inclusive. +\par }{\b\f1 dwidth}{\f1 \tab \tab width in bits of device data, 1 to 32 inclusive. +\par }{\b\f1 examine}{\f1 \tab address of special device data read routine, or NULL if none is required. +\par }{\b\f1 deposit}{\f1 \tab \tab address of special device data write routine, or NULL if none is required. +\par }{\b\f1 reset}{\f1 \tab \tab address of device reset routine, or NULL if none is required. +\par }{\b\f1 boot}{\f1 \tab \tab address of device bootstrap routine, or NULL if none is required. +\par }{\b\f1 attach}{\f1 \tab \tab address of special device attach routine, or NULL if none is required. +\par }{\b\f1 detach}{\f1 \tab \tab address of special device detach routine, or NULL if none is required. +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Examine and Deposit Routines +\par }\pard \widctlpar\adjustright {\f1 +\par For devices which maintain their data sets as host files, SCP implements the examine and deposit data functions. However, devices which maintain their d +ata sets as private state (typically just the CPU) must supply special examine and deposit routines. The calling sequences are: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 t_stat }{\i\f1 examine_routine}{\f1 (t_val *eval_array, t_addr addr, UNIT *uptr, int32 switches) \endash Copy }{\b\f1 sim_emax}{\f1 consecutive addresses for unit }{\i\f1 uptr}{\f1 , starting at }{\i\f1 addr}{ +\f1 , into }{\i\f1 eval_array}{\f1 . The }{\i\f1 switch}{\f1 variable has bit set if the n\rquote th letter was specified as a switch to the examine command. +\par +\par t_stat }{\i\f1 deposit_routine}{\f1 (t_val value, t_addr addr, UNIT *uptr, int32 switches) \endash Store the specified }{\i\f1 value}{\f1 in the specified }{\i\f1 addr}{\f1 for unit }{\i\f1 uptr}{\f1 . The }{\i\f1 switch}{\f1 + variable is the same as for the examine routine. +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Reset Routine +\par }\pard \widctlpar\adjustright {\f1 +\par The reset routine implements the device reset function for the RESET, RUN, and BOOT commands. Its calling sequence is: +\par +\par \tab t_stat }{\i\f1 reset_routine}{\f1 (DEVICE *dptr) \endash Reset the specified device to its initial state. +\par +\par A typical reset routine clears all device flags and cancels any outstanding timing operations. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.3\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Boot Routine +\par }\pard \widctlpar\adjustright {\f1 +\par If a device responds to a BOOT command, the boot routine implements the bootstrapping function. Its calling sequence is: +\par +\par \tab t_stat boot_routine (int32 unit_number) \endash Bootstrap the specified unit. +\par +\par A typical bootstrap routine copies a bootstrap loader into main memory and sets the PC to the starting address of the loader. SCP then starts simulation at the specified address. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.1.4\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Attach and Detach Routines +\par }\pard \widctlpar\adjustright {\f1 +\par Normally, the ATTACH and DETACH commands are handled by SCP. However, devices which need to pre- or post-process these commands must supply special attach and detach routines. The calling sequences are: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 t_stat }{\i\f1 attach_routine }{\f1 (UNIT *uptr, char *file) \endash Attach the specified }{\i\f1 file}{\f1 to the unit }{\i\f1 uptr}{\f1 . +\par +\par t_stat }{\i\f1 detach_routine}{\f1 (UNIT *uptr) \endash Detach unit }{\i\f1 uptr}{\f1 . +\par }\pard \widctlpar\adjustright {\f1 +\par In practice, these routines always invoke the standard SCP routines, }{\b\f1 attach_unit}{\f1 and }{\b\f1 detach_unit}{\f1 , respectively. For example, here are special attach and detach routines to update line printer error state: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 t_stat lpt_attach (UNIT *uptr, char *cptr) \{ +\par }\pard \fi720\li720\widctlpar\adjustright {\f1 t_stat r; +\par if ((r = attach_unit (uptr, cptr)) != SCPE_OK) return r; +\par lpt_error = 0; +\par return SCPE_OK; +\par }\pard \li720\widctlpar\adjustright {\f1 \} +\par +\par t_stat lpt_detach (UNIT *uptr) \{ +\par \tab lpt_error = 1; +\par \tab return detach_unit (uptr); +\par \} +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\b\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\b\f1 unit}{\f1 Structure +\par }\pard \widctlpar\adjustright {\b\f1 +\par }{\f1 Units are allocated as contiguous array. Each unit is defined with a }{\b\f1 unit}{\f1 structure (typedef }{\b\f1 UNIT}{\f1 ), which has the following fields: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 struct unit \{ +\par \tab struct unit\tab *next;\tab \tab \tab \tab /* next active */ +\par \tab t_stat\tab \tab (*action)();\tab \tab \tab /* action routine */ +\par \tab char\tab \tab *filename;\tab \tab \tab /* open file name */ +\par \tab FILE\tab \tab *fileref;\tab \tab \tab \tab /* file reference */ +\par \tab void\tab \tab *filebuf;\tab \tab \tab \tab /* memory buffer */ +\par \tab t_addr\tab \tab hwmark;\tab \tab \tab /* high water mark */ +\par \tab int32\tab \tab time;\tab \tab \tab \tab /* time out */ +\par \tab int32\tab \tab flags;\tab \tab \tab \tab /* flags */ +\par \tab t_addr\tab \tab capac;\tab \tab \tab \tab /* capacity */ +\par \tab t_addr\tab \tab pos;\tab \tab \tab \tab /* file position */ +\par \tab int32\tab \tab buf;\tab \tab \tab \tab /* buffer */ +\par \tab int32\tab \tab wait;\tab \tab \tab \tab /* wait */ +\par \tab int32\tab \tab u3;\tab \tab \tab \tab /* device specific */ +\par \tab int32\tab \tab u4;\tab \tab \tab \tab /* device specific */ +\par \}; +\par }\pard \widctlpar\adjustright {\f1 +\par The fields are the following +\par +\par }\pard \li720\widctlpar\adjustright {\b\f1 next}{\f1 \tab \tab pointer to next unit in active queue, NULL if none. +\par }{\b\f1 action}{\f1 \tab \tab address of unit time-out service routine. +\par }{\b\f1 filename}{\f1 \tab pointer to name of attached file, NULL if none. +\par }{\b\f1 fileref}{\f1 \tab \tab pointer to FILE structure of attached file, NULL if none. +\par }{\b\f1 hwmark}{\f1 \tab buffered devices only; highest modified address, + 1. +\par }{\b\f1 time}{\f1 \tab \tab increment until time-out beyond previous unit in active queue. +\par }{\b\f1 flags}{\f1 \tab \tab unit flags. +\par }{\b\f1 capac}{\f1 \tab \tab unit capacity, 0 if variable. +\par }{\b\f1 pos}{\f1 \tab \tab sequential devices only; next device address to be read or written. +\par }{\b\f1 buf}{\f1 \tab \tab by convention, the unit buffer, but can be used for other purposes. +\par }{\b\f1 wait}{\f1 \tab \tab by convention, the unit wait time, but can be used for other purposes. +\par }{\b\f1 u3}{\f1 \tab \tab user-defined. +\par }{\b\f1 u4}{\f1 \tab \tab user-defined. +\par }\pard \widctlpar\adjustright {\b\f1 +\par buf, wait, u3, u4}{\f1 are all saved and restored by the SAVE and RESTORE commands and thus can be used for unit state which must be preserved. +\par +\par Macro }{\b\f1 UDATA}{\f1 is available to fill in the common fields of a UNIT. It is invoked by +\par +\par \tab UDATA\tab \tab (action_routine, flags, capacity) +\par +\par Fields after }{\b\f1 buf}{\f1 can be filled in manually, e.g, +\par +\par \tab UNIT lpt_unit = \{ UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), 500 \}; +\par +\par defines the line printer as a sequential unit with a wait time of 500. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Unit Flags +\par }\pard \widctlpar\adjustright {\f1 +\par The }{\b\f1 flags }{\f1 field contains indicators of current unit status. SIMH defines 11 flags: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 flag name\tab \tab meaning if set +\par +\par UNIT_DISABLE\tab \tab the unit responds to ENABLE and DISABLE. +\par UNIT_DIS\tab \tab the unit is currently disabled. +\par UNIT_ATTABLE\tab the unit responds to ATTACH and DETACH. +\par UNIT_ATT\tab \tab the unit is currently attached to a file. +\par UNIT_BUFABLE\tab the unit can buffer its data set in memory. +\par UNIT_MUSTBUF\tab the unit must buffer its data set in memory. +\par UNIT_BUF\tab \tab the unit is currently buffering its data set in memory. +\par UNIT_RO\tab \tab the unit is read only. +\par UNIT_SEQ\tab \tab the unit is sequential. +\par UNIX_FIX\tab \tab the unit is fixed capacity. +\par UNIT_BINK\tab \tab the unit measures \ldblquote K\rdblquote as 1024, rather than 1000. +\par }\pard \widctlpar\adjustright {\f1 +\par Starting at bit position UNIT_V_UF, the remaining flags are device-specific. Device-specific flags are set and cleared with the SET and CLEAR commands, which reference the MTAB array (see below). De +vice-specific flags and UNIT_DIS are not automatically saved and restored; the device must supply a register covering these bits. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Service Routine +\par }\pard \widctlpar\adjustright {\f1 +\par This routine is called by }{\b\f1 sim_process_event}{\f1 when a unit times out. Its calling sequence is: +\par +\par }\pard \fi720\widctlpar\adjustright {\f1 t_stat }{\i\f1 service_routine}{\f1 (UNIT *uptr) +\par }\pard \widctlpar\adjustright {\f1 +\par The status returned by the service routine is passed by }{\b\f1 sim_process_event}{\f1 back to the CPU. +\par +\par {\listtext\pard\plain\b\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.3\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\b\f1 reg}{\f1 Structure +\par }\pard \widctlpar\adjustright {\b\f1 +\par }{\f1 Registers are allocated as contiguous array, with a NULL register at the end. Each register is defined with a }{\b\f1 reg}{\f1 structure (typedef }{\b\f1 REG}{\f1 ), which has the following fields: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 struct reg \{ +\par \tab char\tab \tab *name;\tab \tab \tab \tab /* name */ +\par \tab void\tab \tab *loc;\tab \tab \tab \tab /* location */ +\par \tab int\tab \tab radix;\tab \tab \tab \tab /* radix */ +\par \tab int\tab \tab width;\tab \tab \tab \tab /* width */ +\par \tab int\tab \tab offset;\tab \tab \tab \tab /* starting bit */ +\par \tab int\tab \tab depth;\tab \tab \tab \tab /* save depth */ +\par \tab int32\tab \tab flags;\tab \tab \tab \tab /* flags */ +\par \}; +\par }\pard \widctlpar\adjustright {\f1 +\par The fields are the following: +\par +\par }\pard \li720\widctlpar\adjustright {\b\f1 name}{\f1 \tab \tab device name, string of all capital alphanumeric characters. +\par }{\b\f1 loc}{\f1 \tab \tab pointer to location of the register value. +\par }{\b\f1 radix}{\f1 \tab \tab radix for input and display of data, 2 to 16 inclusive. +\par }{\b\f1 width}{\f1 \tab \tab width in bits of data, 1 to 32 inclusive. +\par }{\b\f1 width\tab }{\f1 \tab bit offset (from right end of data). +\par }{\b\f1 depth\tab }{\f1 \tab size of data array (normally 1). +\par }{\b\f1 flags}{\f1 \tab \tab flags and formatting information. +\par }\pard \widctlpar\adjustright {\f1 +\par The }{\b\f1 depth}{\f1 field is only used with special \ldblquote arrayed registers\rdblquote , like the data buffer in the PDP floppy disk +controller. Arrayed registers cannot be examined or deposited, but all the values in the array will be saved and restored by SAVE and RESTORE. +\par +\par Macros }{\b\f1 ORDATA}{\f1 , }{\b\f1 DRDATA}{\f1 , and }{\b\f1 HRDATA}{\f1 define right-justified octal, decimal, and hexidecimal registers, respectively. They are invoked by: +\par +\par \tab xRDATA\tab (name, location, width) +\par +\par Macro }{\b\f1 FLDATA}{\f1 defines a one-bit binary flag at an arbitrary offset in a 32-bit word. It is invoked by: +\par +\par \tab FLDATA\tab (name, location, bit_position) +\par +\par Macro }{\b\f1 GRDATA}{\f1 defines a register with arbitrary location and radix. It is invoked by: +\par +\par \tab GRDATA\tab (name, location, radix, width, bit_position) +\par +\par Finally }{\b\f1 BRDATA}{\f1 defines an arrayed register. It is invoked by: +\par +\par \tab BRDATA\tab (name, location, radix, width, depth) +\par +\par The }{\b\f1 flag}{\f1 field can be filled in manually, e.g., +\par +\par \tab REG lpt_reg = \{ +\par \tab \tab \{ DRDATA\tab (POS, lpt_unit.pos, 31), PV_LFT \}, \'85 \} +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.3.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Register Flags +\par }\pard \widctlpar\adjustright {\f1 +\par The }{\b\f1 flags }{\f1 field contains indicators that control register examination and deposit. +\par +\par }\pard \li720\widctlpar\adjustright {\f1 flag name\tab \tab meaning if specified +\par +\par PV_RZRO\tab \tab print register right justified with leading zeroes. +\par PV_RSPC\tab \tab print register right justified with leading spaces. +\par PV_LEFT\tab \tab print register left justified. +\par REG_RO\tab \tab register is read only. +\par REG_HIDDEN\tab \tab register is hidden (will not appear in EXAMINE STATE). +\par REG_HRO\tab \tab register is read only and hidden. +\par REG_NZ\tab \tab new register values must be non-zero. +\par +\par {\listtext\pard\plain\b\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.4\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\b\f1 mtab}{\f1 Structure +\par }\pard \widctlpar\adjustright {\b\f1 +\par }{\f1 Device-specific SHOW and SET commands are processed using the modifications array, which is allocated as contiguous array, with a NULL at the end. Each possible modification is defined with a }{\b\f1 mtab}{\f1 structure (synonym }{\b\f1 MTAB}{\f1 +), which has the following fields: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 struct mtab \{ +\par \tab int32\tab \tab mask;\tab \tab \tab \tab /* mask */ +\par \tab int32\tab \tab match;\tab \tab \tab \tab /* match */ +\par \tab char\tab \tab *pstring;\tab \tab \tab /* print string */ +\par \tab char\tab \tab *mstring;\tab \tab \tab /* match string */ +\par \tab t_stat\tab \tab (*valid)();\tab \tab \tab /* validation routine */ +\par \}; +\par }\pard \widctlpar\adjustright {\f1 +\par The fields are the following: +\par +\par }\pard \li720\widctlpar\adjustright {\b\f1 mask}{\f1 \tab \tab bit mask for testing the unit.}{\b\f1 flags}{\f1 field +\par }\pard \fi-1440\li2160\widctlpar\adjustright {\b\f1 match}{\f1 \tab (SHOW) if the masked bits equal this value, }{\b\f1 pstring}{\f1 is printed +\par }{\b\f1 \tab }{\f1 (SET) if }{\b\f1 mstring}{\f1 is matched, the masked bits are set to this value +\par }{\b\f1 pstring}{\f1 \tab pointer to character string printed on a match (SHOW) +\par }{\b\f1 mstring}{\f1 \tab pointer to character string to be matched (SET, CLEAR) +\par }{\b\f1 valid}{\f1 \tab address of validation routine, or NULL if none required +\par }\pard \fi-1440\li1440\widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.4.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls13\ilvl2\adjustright {\f1 Validation Routine +\par }\pard \widctlpar\adjustright {\f1 +\par The validation routine is called during SET processing to make sure that the proposed modification is valid. It can make other state changes required by the modification or initiate additional dialogs needed by the modifier. Its calling sequence is: + +\par +\par }\pard \li720\widctlpar\adjustright {\f1 t_stat }{\i\f1 validation_routine}{\f1 (UNIT *uptr, int32 value) \endash test that }{\i\f1 uptr}{\f1 .}{\b\f1 flags}{\f1 can be set to }{\i\f1 value}{\f1 . +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.5\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\f1 Other Data Structures +\par }\pard \widctlpar\adjustright {\f1 +\par char }{\b\f1 sim_name[]}{\f1 is a character array containing the VM name. +\par +\par int32 }{\b\f1 sim_emax}{\f1 contains the maximum number of words needed to hold the largest instruction or data item in the VM. Examine and deposit will process up to }{\b\f1 sim_emax}{\f1 words. +\par +\par DEVICE *}{\b\f1 sim_devices[]}{\f1 is an array of pointers to all the devices in the VM. It is terminated by a NULL. By convention, the CPU is always the first device in the array. +\par +\par REG *}{\b\f1 sim_PC}{\f1 points to the }{\b\f1 reg}{\f1 structure for the program counter. By convention, the PC is always the first register in the CPU\rquote s register array. +\par +\par char *}{\b\f1 sim_stop_messages[]}{\f1 is an array of pointers to character strings, corresponding to error status returns greater than zero. If }{\b\f1 sim_instr}{\f1 returns status code n > 0, then }{\b\f1 sim_stop_message[n]}{\f1 is printed by SCP. + +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 4.\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\adjustright {\f1 VM Provided Routines +\par }\pard \widctlpar\adjustright {\f1 +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 4.1\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\f1 Instruction Execution +\par }\pard \widctlpar\adjustright {\f1 +\par Instruction execution is performed by routine }{\b\f1 sim_instr}{\f1 . Its calling sequence is: +\par +\par t_stat }{\b\f1 sim_instr}{\f1 (void) \endash Execute from current PC until error or halt. +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 4.2\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\f1 Binary Load and Dump +\par }\pard \widctlpar\adjustright {\f1 +\par If the VM responds to the LOAD (or DUMP) command, the loader (dumper) is implemented by routine }{\b\f1 sim_load}{\f1 . Its calling sequence is: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 t_stat }{\b\f1 sim_load}{\f1 (FILE *fptr, char *buf, t_bool flag) - If }{\i\f1 flag}{\f1 = 0, load data from binary file }{\i\f1 fptr}{\f1 . If }{\i\f1 flag}{\f1 = 1, dump data to binary file }{\i\f1 fptr}{\f1 +. For either command, }{\i\f1 buf}{\f1 contains any VM-specific arguments. +\par }\pard \widctlpar\adjustright {\f1 +\par If LOAD or DUMP is not implemented, }{\b\f1 sim_load}{\f1 should simply return SCPE_ARG. The LOAD and DUMP commands open and close the specified file for }{\b\f1 sim_load}{\f1 . +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 4.3\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360\ls13\ilvl1\adjustright {\f1 Symbolic Examination and Deposit +\par }\pard \widctlpar\adjustright {\f1 +\par If the VM provides symbolic examination and deposit of data, it must provide two routines, }{\b\f1 fprint_sym}{\f1 for output and }{\b\f1 parse_sym}{\f1 for input. Their calling sequences are: +\par +\par }\pard \li720\widctlpar\adjustright {\f1 t_stat }{\b\f1 fprint_sym}{\f1 (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 switch) \endash Based on the }{\i\f1 switch}{\f1 variable, symbolically output to stream }{\i\f1 ofile}{\f1 + the data in array }{\i\f1 val}{\f1 at the specified }{\i\f1 addr}{\f1 in unit }{\i\f1 uptr}{\f1 . +\par +\par t_stat }{\b\f1 parse_sym}{\f1 (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 switch) \endash Based on the }{\i\f1 switch}{\f1 variable, parse character string }{\i\f1 cptr}{\f1 for a symbolic value }{\i\f1 val}{\f1 at the specified }{ +\i\f1 addr}{\f1 in unit }{\i\f1 uptr}{\f1 . +\par }\pard \widctlpar\adjustright {\f1 +\par If symbolic processing is not implemented, or the output value or input string cannot be parsed, these routines should return SCPE_ARG. If the processing was successful and con +sumed more than a single word, then these routines should return extra number of words (not bytes) consumed as a }{\b\f1 negative}{\f1 + number. If the processing was successful and consumed a single word, then these routines should return SCPE_OK. For example, PDP-11 }{\b\f1 parse_sym}{\f1 would respond as follows to various inputs: +\par +\par \tab input\tab \tab \tab \tab return value +\par +\par \tab XYZGH\tab \tab \tab \tab SCPE_ARG +\par \tab MOV R0,R1\tab \tab \tab SCPE_OK +\par \tab MOV #4,R5\tab \tab \tab -1 +\par \tab MOV 1234,5670\tab \tab -2 +\par +\par The interpretation of switch values is arbitrary, but the following are used by existing VM\rquote s: +\par +\par \tab switch\tab \tab \tab \tab interpretation +\par +\par \tab -a\tab \tab \tab \tab single character +\par \tab -c\tab \tab \tab \tab character string +\par \tab -m\tab \tab \tab \tab instruction mnemonic +\par +\par In addition, on input, a leading \lquote (apostrophe) is interpreted to mean a single character, and a leading \ldblquote (double quote) is interpreted to mean a character string. +\par }} \ No newline at end of file diff --git a/simh_doc.txt b/simh_doc.txt new file mode 100644 index 00000000..e133f4f2 --- /dev/null +++ b/simh_doc.txt @@ -0,0 +1,4181 @@ +To: Users +From: Bob Supnik +Subj: Simulator Usage, V2.5 +Date: 1-Jan-01 + + COPYRIGHT NOTICE + +The following copyright notice applies to both the SIMH source and binary: + + Original code published in 1993-2000, written by Robert M Supnik + Copyright (c) 1993-2000, Robert M Supnik + + 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 + ROBERT M SUPNIK 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 Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + +This memorandum documents the PDP-8, PDP-11, PDP-1, other 18b PDP, Nova, +IBM 1401, HP 2100, and Interdata 4 simulators. These simulators are freeware; +refer to the license terms above for conditions of use. Support is not available. +The best way to fix problems or add features is to read and modify the sources +yourself. Alternately, you can send Internet mail to mps@tiac.net, but +a response is not guaranteed. + +The simulators use a common command interface. The memorandum first +describes the common features of the command interface and then provides +information on each of the individual simulators. + + +1. Compiling And Running A Simulator + +The simulators have been tested on VAX VMS, Alpha VMS, Alpha UNIX, Intel +FreeBSD, Intel LINUX, and Windows 95/Windows NT (Visual C++ environment). +Porting to other environments will require changes to the operating system +dependent code in scp_tty.c. + +To compile the simulators on VMS, use these commands (note that separate +compilations are required for each of the 18b PDP's): + + $ cc pdp8_*.c,scp.c,scp_tty.c ! PDP-8 + $ link/exec=pdp8 pdp8_*.obj,scp.obj,scp_tty.obj + + $ cc pdp11_*.c,scp.c,scp_tty.c ! PDP-11 + $ link/exec=pdp11 pdp11_*.obj,scp.obj,scp_tty.obj + + $ cc nova_*.c,scp.c,scp_tty.c ! Nova + $ link/exec=nova nova_*.obj,scp.obj,scp_tty.obj + + $ cc pdp1_*.c,scp.c,scp_tty.c ! PDP-1 + $ link/exec=pdp1 pdp1_*.obj,scp.obj,scp_tty.obj + + $ cc/define=PDP{4,7,9,15} pdp18b_*.c,scp.c,scp_tty.c + $ link/exec=pdp{4,7,9,15} pdp18b_*.obj,scp.obj,scp_tty.obj + + $ cc i1401_*.c,scp.c,scp_tty.c ! IBM 1401 + $ link/exec=i1401 i1401_*.obj,scp.obj,scp_tty.obj + + $ cc hp2100_*.c,scp.c,scp_tty.c ! HP 2100 + $ link/exec=hp2100 hp2100_*.obj,scp.obj,scp_tty.obj + + $ cc id4_*.c,scp.c,scp_tty.c ! Interdata 4 + $ link/exec=id4 id4_*.obj,scp.obj,scp_tty.obj + +On version of VMS prior to 6.2, the simulators must then be defined as +foreign commands so that they can be started by name. + +To compile the simulators on Alpha UNIX or any UNIX variant which supports +the POSIX compliant TERMIOS interface, use the following commands (note +that separate compilations are required for each of the 18b PDP's): + + % cc pdp8_*.c scp*.c -lm -o pdp8 + % cc pdp11_*.c scp*.c -lm -o pdp11 + % cc nova_*.c scp*.c -lm -o nova + % cc pdp1_*.c scp*.c -o pdp1 + % cc -DPDP{4,7,9,15} pdp18b_*.c scp*.c -lm -o pdp{4,7,9,15} + % cc i1401_*.c scp*.c -o i1401 + % cc hp2100_*.c scp*.c -o hp2100 + % cc id4_*.c scp*.c -o id4 + +These commands should work with most UNIX variants. If your UNIX only +supports the old BSD terminal interface, add -DBSDTTY to each command. +If your UNIX automatically includes the math library, omit -lm from +the command line. + +To start the simulator, simply type its name. The simulator takes +one optional argument, a startup command file. If specified, this +file should contain a series of non-interactive simulator commands, +one per line. These command can be used to set up standard parameters, +for example, disk sizes. + + % pdp8 (cr) or + % pdp11 (cr) or + % nova (cr) or + % pdp1 (cr) or + % pdp{4,7,9,15} (cr) or + % i1401 (cr) or + % hp2100 (cr) or + % id4 (cr) + +The simulator types out its name and version, executes the commands +in the startup file, if any, and then prompts for input with + + sim> + +2. Simulator Conventions + +A simulator consists of a series of devices, the first of which is always +the CPU. A device consists of named registers and one or more numbered +units. Registers correspond to device state, units to device address +spaces. Thus, the CPU device might have registers like PC, ION, etc, +and a unit corresponding to main memory; a disk device might have +registers like BUSY, DONE, etc, and units corresponding to individual +disk drives. Except for main memory, device address spaces are simulated +as unstructured binary disk files in the host file system. The SHOW CONFIG +command displays the simulator configuration. + +A simulator keeps time in terms of arbitrary units, usually one time unit +per instruction executed. Simulated events (such as completion of I/O) +are scheduled at some number of time units in the future. The simulator +executes synchronously, invoking event processors when simulated events +are scheduled to occur. Even asynchronous events, like keyboard input, +are handled by polling at synchronous intervals. The SHOW QUEUE command +displays the simulator event queue. + +3. Commands + +Simulator commands consist of a command verb, optional switches, and optional +arguments. Switches take the form: + + -{...} + +Multiple switches may be specified separately or together: -abcd or +-a -b -c -d are treated identically. Verbs, switches, and other +input (except for file names) are case insensitive. + +3.1 Loading and Saving Programs + +The LOAD command (abbreviation LO) loads a file in binary paper-tape +loader format: + + sim> load {implementation options}(cr) + +The number of formats supported is implementation specific. Options +(such as load within range) are also implementation specific. + +The DUMP command (abbreviation DU) dumps memory in binary paper- +tape loader format: + + sim> dump {implementation options}(cr) + +The number of formats supported is implementation specific. Options +(such as dump within range) are also implementation specific. + +3.2 Saving and Restoring State + +The SAVE command (abbreviation SA) save the complete state of the +simulator to a file. This includes the contents of main memory and +all registers, and the I/O connections of devices: + + sim> save (cr) + +The RESTORE command (abbreviation REST, alternately GET) restores a +previously saved simulator state: + + sim> restore (cr) + +Note: SAVE file format compresses zeroes to minimize file size. + +3.3 Resetting Devices + +The RESET command (abbreviation RE) resets a device or the entire +simulator to a predefined condition: + + sim> RESET(cr) -- reset all devices + sim> RESET ALL(cr) -- reset all devices + sim> RESET (cr) -- reset specified device + +Typically, RESET stops any in-progress I/O operation, clears any +interrupt request, and returns the device to a quiescent state. It +does not clear main memory or affect I/O connections. + +3.4 Connecting and Disconnecting Devices + +Except for main memory, simulated unit address spaces are simulated as +unstructured binary disk files in the host file system. Before using a +simulated unit the user must specify the file to be accessed by that +unit. The ATTACH (abbreviation AT) command associates a unit and a file: + + sim> ATTACH (cr) + +If the file does not exist, it is created, and an appropriate message +is printed. + +The DETACH (abbreviation DET) command breaks the association between a +unit and a file and closes the file: + + sim> DETACH ALL(cr) -- detach all units + sim> DETACH (cr) -- detach specified unit + +The EXIT command performs an automatic DETACH ALL. + +3.5 Examining and Changing State + +There are four commands to examine and change state: + + EXAMINE (abbreviated E) examines state + DEPOSIT (abbreviated D) changes state + IEXAMINE (interactive examine, abbreviated IE) examines state + and allows the user to interactively change it + IDEPOSIT (interactive deposit, abbreviated ID) allows the user + to interactively change state + +All four commands take the form + + command {modifiers} + +Deposit must also include a deposit value at the end of the command. + +There are four kinds of modifiers: switches, device/unit name, search +specifiers, and for EXAMINE, output file. Switches have been described +previously. A device/unit name identifies the device and unit whose +address space is to be examined or modified. If no device is specified, +the CPU (main memory)is selected; if a device but no unit is specified, +unit 0 of the device is selected. + +Search specifiers provide criteria for testing addresses or registers +to see if they should be processed. A specifier consists of a logical +operator, a relational operator, or both, optionally separated by spaces. + + { } + +where the logical operator is & (and), | (or), or ^ (xor), and the +relational operator is = or == (equal), ! or != (not equal), >= +(greater than or equal), > (greater than), <= (less than or equal), or +< (less than). If a logical operator is specified without a relational +operator, it is ignored. If a relational operator is specified without +a logical operator, no logical operation is performed. All comparisons +are unsigned. + +The output file modifier redirects command output to a file instead of +the console. An output file modifier consists of @ followed by a +valid file name. + +Modifiers may be specified in any order. If multiple modifiers of the +same time are specified, later modifiers override earlier modifiers. +Note that if the device/unit name comes after the search specifier, +the values will interpreted in the radix of the CPU, rather than of the +device/unit. + +The "state list" consists of one or more of the following, separated +by commas: + + register the specified register + register1-register2 all the registers starting at register1 + up to and including register2 + address the specified location + address1-address2 all locations starting at address1 up to + and including address2 + STATE all registers in the device + ALL all locations in the unit + +Switches can be used to control the format of display information: + + -a display as ASCII + -c display as character string + -m display as instruction mnemonics + -o display as octal + -d display as decimal + -h display as hexidecimal + +The simulators typically accept symbolic input (see simulator sections). + +Examples: + + sim> ex 1000-1100 -- examine 1100:1100 + sim> de PC 1040 -- set PC to 1040 + sim> ie 40-50 -- interactively examine 40:50 + sim> ie >1000 40-50 -- interactively examine the subset + of locations 40:50 that are >1000 + sim> ex rx0 50060 -- examine 50060, RX unit 0 + sim> de all 0 -- set main memory to 0 + sim> de &77 >0 0 -- set all addresses whose low order + bits are non-zero to 0 + sim> ex -m @memdump.txt 0-7777 -- dump memory to file + +Note: to terminate an interactive command, simply type a bad value (eg, +XYZ) when input is requested. + +3.6 Running Programs + +The RUN command (abbreviated RU) resets all devices, deposits its argument +(if given) in the PC, and starts execution. If no argument is given, +execution starts at the current PC. + +The GO command does NOT reset devices, deposits its argument (if given) in +the PC, and starts execution. If no argument is given, execution starts at +the current PC. + +The CONT command (abbreviated CO) does NOT reset devices and resumes +execution at the current PC. + +The STEP command (abbreviated S) resumes execution at the current PC for +the number of instructions given by its argument. If no argument is +supplied, one instruction is executed. + +The BOOT command (abbreviated BO) bootstraps the device and unit given +by its argument. If no unit is supplied, unit 0 is bootstrapped. The +specified unit must be attached to a file. + +3.7 Stopping Programs + +Programs run until the simulator detects an error or stop condition, or +until the user forces a stop condition. + +3.7.1 Simulator Detected Stop Conditions + +These simulator-detected conditions stop simulation: + + - HALT instruction. If a HALT instruction is decoded, + simulation stops. + + - Breakpoint. The IBKPT register provides a single virtual + address breakpoint. If the PC matches the contents of the + IBKPT register, simulation stops. The breakpoint is + automatically disabled for the next instruction execution. + + - I/O error. If an I/O error occurs during simulation of an + I/O operation, and the device stop-on-I/O-error flag is set, + simulation usually stops. + + - Processor condition. Certain processor conditions can stop + simulation; these are described with the individual simulators. + +3.7.2 User Specified Stop Conditions + +Typing the interrupt character stops simulation. The interrupt character +is defined by the WRU (where are you) register and is initially set to +005 (^E). + +3.8 Setting Device Parameters + +The SET command (abbreviated SE) changes the status of a device parameter: + + sim> SET + +Most parameters are simulator and device specific. Disk drives, for +example, can usually be set write ENABLED or write LOCKED; if a device +supports multiple drive types, the SET command can be used to specify +the drive type. + +All devices recognize the following parameters: + + OCT sets the data radix = 8 + DEC sets the data radix = 10 + HEX sets the data radix = 16 + +3.9 Displaying Parameters and Status + +The SHOW CONFIGURATION command shows the simulator configuration and the +status of all simulated devices. + +The SHOW MODIFIERS command shows the modifiers available on all simulated +devices. + +The SHOW command shows the status of the named simulated device. + +The SHOW QUEUE command shows the state of the simulator event queue. Times +are in "simulation units", typically one unit per instruction execution, +relative to the current simulation time. + +The SHOW TIME command shows the number of time units elapsed since +the last RUN command. + +3.10 Altering the Simulated Configuration + +In devices with multiple units, the REMOVE command removes the +specified unit from the configuration. Once removed, a unit cannot be +manipulated in any way until it is added back to the configuration. + +ADD adds back a unit that had been removed from the configuration. + +3.11 Exiting The Simulator + +EXIT (synonyms QUIT and BYE) returns control to the operating system. + +4. PDP-8 Features + +The PDP-8 simulator is configured as follows: + +device simulates +name(s) + +CPU PDP-8/E CPU with 32KW of memory +- KE8E extended arithmetic element (EAE) +- KM8E memory management and timeshare control +PTR,PTP PC8E paper tape reader/punch +TTI,TTO KL8E console terminal +LPT LE8E line printer +CLK DK8E line frequency clock (also PDP-8/A compatible) +RK RK8E/RK05 cartridge disk controller with four drives +RF RF08/RS08 fixed head disk controller with four platters +RX RX8E/RX01 floppy disk controller with two drives +MT TM8E/TU10 magnetic tape controller with eight drives + +The PDP-8 simulator implements one unique stop condition: if an undefined +instruction (unimplemented IOT or OPR) is decoded, and register STOP_INST +is set, the simulator halts. + +The PDP-8 loader supports both RIM format and BIN format tapes. BIN is +the default; to load a RIM format tape, use the -r switch with LOAD. The +DUMP command is not implemented. + +4.1 CPU + +The only CPU options are the presence of the EAE and the size of main +memory; the memory extension and time-share control is always included, +even if memory size is 4K. + + SET CPU EAE enable EAE + SET CPU NOEAE disable EAE + SET CPU 4K set memory size = 4K + SET CPU 8K set memory size = 8K + SET CPU 12K set memory size = 12K + SET CPU 16K set memory size = 16K + SET CPU 20K set memory size = 20K + SET CPU 24K set memory size = 24K + SET CPU 28K set memory size = 28K + SET CPU 32K set memory size = 32K + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 32K. + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + PC 15 program counter, including IF as high 3 bits + AC 12 accumulator + MQ 12 multiplier-quotient + L 1 link + SR 12 front panel switches + IF 3 instruction field + DF 3 data field + IB 3 instruction field buffer + SF 7 save field + UF 1 user mode flag + UB 1 user mode buffer + SC 5 EAE shift counter + GTF 1 EAE greater than flag + EMODE 1 EAE mode (0 = A, 1 = B) + ION 1 interrupt enable + ION_DELAY 1 interrupt enable delay for ION + CIF_DELAY 1 interrupt enable delay for CIF + PWR_INT 1 power fail interrupt + UF_INT 1 user mode violation interrupt + INT 15 interrupt pending flags + DONE 15 device done flags + ENABLE 15 device interrupt enable flags + OLDPC 15 PC prior to last JMP, JMS, or interrupt + STOP_INST 1 stop on undefined instruction + BREAK 16 breakpoint address (177777 to disable) + WRU 8 interrupt character + +4.2 Programmed I/O Devices + +4.2.1 PC8E Paper Tape Reader (PTR) + +The paper tape reader (PTR) reads data from a disk file. The POS +register specifies the number of the next data item to be read. Thus, +by changing POS, the user can backspace or advance the reader. + +The paper tape reader supports the BOOT command. BOOT PTR copies the +RIM loader into memory and starts it running. + +The paper tape reader implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + ENABLE 1 interrupt enable flag + INT 1 interrupt pending flag + POS 31 position in the input file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + end of file 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +4.2.2 PC8E Paper Tape Punch (PTP) + +The paper tape punch (PTP) writes data to a disk file. The POS register +specifies the number of the next data item to bewritten. Thus, by +changing POS, the user can backspace or advance the punch. + +The paper tape punch implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + ENABLE 1 interrupt enable flag + INT 1 interrupt pending flag + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +4.2.3 KL8E Terminal Input (TTI) + +The terminal input (TTI) reads from the controling console port. The +input side has one option, UC; when set, it automatically converts lower +case input to upper case. This is required by OS/8 and is on by default. + +The terminal input implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + ENABLE 1 interrupt enable flag + INT 1 interrupt pending flag + POS 31 number of characters input + TIME 24 keyboard polling interval + +4.2.4 KL8E Terminal Output (TTO) + +The terminal output (TTO) writes to the controling console port. It +implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + ENABLE 1 interrupt enable flag + INT 1 interrupt pending flag + POS 31 number of characters output + TIME 24 time from I/O initiation to interrupt + +4.2.5 LE8E Line Printer (LPT) + +The line printer (LPT) writes data to a disk file. The POS register +specifies the number of the next data item to be read or written. Thus, +by changing POS, the user can backspace or advance the printer. + +The line printer implements these registers: + + name size comments + + BUF 8 last data item processed + ERR 1 error status flag + DONE 1 device done flag + ENABLE 1 interrupt enable flag + INT 1 interrupt pending flag + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 set error flag + + OS I/O error x report error and stop + +4.2.6 DK8E Line-Frequency Clock (CLK) + +The real-time clock (CLK) implements these registers: + + name size comments + + DONE 1 device done flag + ENABLE 1 interrupt enable flag + INT 1 interrupt pending flag + TIME 24 clock interval + +4.3 RK8E Cartridge Disk (RK) + +RK8E options include the ability to make units write enabled or write locked: + + SET RKn LOCKED set unit n write locked + SET RKn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The RK8E implements these registers: + + name size comments + + STA 12 status + DA 12 disk address + MA 12 current memory address + CMD 12 disk command + BUSY 1 control busy flag + INT 1 interrupt pending flag + STIME 24 seek time, per cylinder + RTIME 24 rotational delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + + end of file x assume rest of disk is zero + + OS I/O error x report error and stop + +4.4 RX8E/RX01 Floppy Disk (RX) + +RX8E options include the ability to set units write enabled or write locked: + + SET RXn LOCKED set unit n write locked + SET RXn ENABLED set unit n write enabled + +The RX8E implements these registers: + + name size comments + + RXCS 12 status + RXDB 12 data buffer + RXES 8 error status + RXTA 8 current track + RXSA 8 current sector + STAPTR 3 controller state + BUFPTR 3 buffer pointer + INT 1 interrupt pending flag + DONE 1 device done flag + ENABLE 1 interrupt enable flag + TR 1 transfer ready flag + ERR 1 error flag + CTIME 24 command completion time + STIME 24 seek time, per track + XTIME 24 transfer ready delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + +RX01 data files are buffered in memory; therefore, end of file and OS +I/O errors cannot occur. + +4.5 RF08/RS08 Fixed Head Disk (RF) + +The RF08 implements these registers: + + name size comments + + STA 12 status + DA 20 current disk address + MA 12 memory address (in memory) + WC 12 word count (in memory) + WLK 32 write lock switches + INT 1 interrupt pending flag + DONE 1 device done flag + TIME 24 rotational delay, per word + BURST 1 burst flag + STOP_IOE 1 stop on I/O error + +The RF08 is a three-cycle data break device. If BURST = 0, word transfers +are scheduled individually; if BURST = 1, the entire transfer occurs in +a single data break. + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + +RF08 data files are buffered in memory; therefore, end of file and OS +I/O errors cannot occur. + +4.6 TM8E Magnetic Tape (MT) + +Magnetic tape options include the ability to make units write enabled or +or write locked. + + SET MTn LOCKED set unit n write locked + SET MTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The magnetic tape controller implements these registers: + + name size comments + + CMD 12 command + FNC 12 function + CA 12 memory address + WC 12 word count + DB 12 data buffer + STA 12 main status + STA2 6 secondary status + DONE 1 device done flag + INT 1 interrupt pending flag + STOP_IOE 1 stop on I/O error + TIME 24 record delay + UST0..7 24 unit status, units 0..n + POS0..7 31 position, units 0..n + +Error handling is as follows: + + error processed as + + not attached tape not ready + + end of file (read or space) end of physical tape + (write) ignored + + OS I/O error report error and stop + +4.7 Symbolic Display and Input + +The PDP-8 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as (sixbit) character string + -m display instruction mnemonics + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c two character sixbit string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses standard PDP-8 assembler syntax. There are four +instruction classes: memory reference, IOT, field change, and operate. + +Memory reference instructions have the format + + memref {I} {C/Z} address + +where I signifies indirect, C a current page reference, and Z a zero page +reference. The address is an octal number in the range 0 - 07777; if C or +Z is specified, the address is a page offset in the range 0 - 177. Normally, +C is not needed; the simulator figures out from the address what mode to use. +However, when referencing memory outside the CPU (eg, disks), there is no +valid PC, and C must be used to specify current page addressing. + +IOT instructions consist of single mnemonics, eg, KRB, TLS. IOT instructions +may be or'd together + + iot iot iot... + +The simulator does not check the legality of the proposed combination. IOT's +for which there is no opcode may be specified as IOT n, where n is an octal +number in the range 0 - 0777. + +Field change instructions (CIF, CDF) have the format + + fldchg field + +where field is an octal number in the range 0 - 7. Field change instructions +may be or'd together. + +Operate instructions have the format + + opr opr opr... + +The simulator does not check the legality of the proposed combination. EAE +mode A and B mnemonics may be specified regardless of the EAE mode. The +operands for MUY and DVI must be deposited explicitly. + +5. PDP-11 Features + +The PDP-11 simulator is configured as follows: + +device simulates +name(s) + +CPU J-11 CPU with 256KB of memory +- FP11 floating point unit (FPA) +- CIS11 commercial instruction set (CIS, off by default) +PTR,PTP PC11 paper tape reader/punch +TTI,TTO DL11 console terminal +LPT LP11 line printer +CLK line frequency clock +RK RK11/RK05 cartridge disk controller with eight drives +RL RLV12/RL01(2) cartridge disk controller with four drives +RP RM02/03/05/80, RP04/05/06/07 Massbus style controller + with eight drives +RX RX11/RX01 floppy disk controller with two drives +TM TM11/TU10 magnetic tape controller with eight drives + +The PDP-11 simulator implements several unique stop conditions: + + - abort during exception vector fetch, and register STOP_VEC is set + - abort during exception stack push, and register STOP_SPA is set + - trap condition 'n' occurs, and register STOP_TRAP is set + - wait state entered, and no I/O operations outstanding + (ie, no interrupt can ever occur) + +The PDP-11 loader supports standard binary format tapes. The DUMP command +is not implemented. + +5.1 CPU + +The only CPU options are disabling of 22b addressing, the CIS instruction +set, and the size of main memory. + + SET CPU 18B disable 22b addressing + SET CPU 22B enable 22b addressing (default) + SET CPU NOCIS disable CIS instructions (default) + SET CPU CIS enable CIS instructions + SET CPU 16K set memory size = 16KB + SET CPU 32K set memory size = 32KB + SET CPU 48K set memory size = 48KB + SET CPU 64K set memory size = 64KB + SET CPU 96K set memory size = 96KB + SET CPU 128K set memory size = 128KB + SET CPU 192K set memory size = 192KB + SET CPU 256K set memory size = 256KB + SET CPU 384K set memory size = 384KB + SET CPU 512K set memory size = 512KB + SET CPU 768K set memory size = 768KB + SET CPU 1024K (or 1M) set memory size = 1024KB + SET CPU 2048K (or 2M) set memory size = 2048KB + SET CPU 3072K (or 3M) set memory size = 3072KB + SET CPU 4096K (or 4M) set memory size = 4096KB + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 256KB. + +These switches are recognized when examining or depositing in CPU memory: + + -v interpret address as virtual + -d if mem mgt enabled, force data space + -k if mem mgt enabled, force kernel mode + -s if mem mgt enabled, force supervisor mode + -u if mem mgt enabled, force user mode + -p if mem mgt enabled, force previous mode + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + PC 16 program counter + R0..R5 16 R0..R5, first register set + R10..R15 16 R0..R5, second register set + KSP 16 kernel stack pointer + SSP 16 supervisor stack pointer + USP 16 user stack pointer + PSW 16 processor status word + CM 2 current mode, PSW<15:14> + PM 2 previous mode, PSW<13:12> + RS 2 register set, PSW<11> + IPL 3 interrupt priority level, PSW<7:5> + T 1 trace bit, PSW<4> + N 1 negative flag, PSW<3> + Z 1 zero flag, PSW<2> + V 1 overflow flag, PSW<1> + C 1 carry flag, PSW<0> + SR 16 front panel switches + DR 16 front panel display + MEMERR 16 memory error register + CCR 16 cache control register + MAINT 16 maintenance register + HITMISS 16 hit/miss register + CPUERR 16 CPU error register + PIRQ 16 programmed interrupt requests + FAC0H..FAC5H 32 FAC0..FAC5, high 32 bits + FAC0L..FAC5L 32 FAC0..FAC5, low 32 bits + FPS 16 floating point status + FEA 16 floating exception address + FEC 4 floating exception code + MMR0..3 16 memory management registers 0..3 + {K/S/U}{I/D}{PAR/PDR}{0..7} + 16 memory management registers + INT 32 interrupt pending flags + TRAP 18 trap pending flags + WAIT 0 wait state flag + WAIT_ENABLE 0 wait state enable flag + STOP_TRAPS 18 stop on trap flags + STOP_VECA 1 stop on read abort in trap or interrupt + STOP_SPA 1 stop on stack push abort in trap or interrupt + OLDPC 16 PC prior to last JMP, JMS, or interrupt + BREAK 16 breakpoint address (1 to disable) + WRU 8 interrupt character + +5.2 Programmed I/O Devices + +5.2.1 PC11 Paper Tape Reader (PTR) + +The paper tape reader (PTR) reads data from a disk file. The POS +register specifies the number of the next data item to be read. Thus, +by changing POS, the user can backspace or advance the reader. + +The paper tape reader implements these registers: + + name size comments + + BUF 8 last data item processed + CSR 16 control/status register + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + BUSY 1 busy flag (CSR<11>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + POS 31 position in the input file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + end of file 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +5.2.2 PC11 Paper Tape Punch (PTP) + +The paper tape punch (PTP) writes data to a disk file. The POS +register specifies the number of the next data item to be written. +Thus, by by changing POS, the user can backspace or advance the punch. + +The paper tape punch implements these registers: + + name size comments + + BUF 8 last data item processed + CSR 16 control/status register + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +5.2.3 KL11 Terminal Input (TTI) + +The terminal input (TTI) reads from the controling console port. It +implements these registers: + + name size comments + + BUF 8 last data item processed + CSR 16 control/status register + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + POS 31 number of characters input + TIME 24 keyboard polling interval + +5.2.4 KL11 Terminal Output (TTO) + +The terminal output (TTO) writes to the controling console port. It +implements these registers: + + name size comments + + BUF 8 last data item processed + CSR 16 control/status register + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + POS 31 number of characters input + TIME 24 time from I/O initiation to interrupt + +5.2.5 LP11 Line Printer (LPT) + +The line printer (LPT) writes data to a disk file. The POS register +specifies the number of the next data item to be written. Thus, +by changing POS, the user can backspace or advance the printer. + +The line printer implements these registers: + + name size comments + + BUF 8 last data item processed + CSR 16 control/status register + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of paper + + OS I/O error x report error and stop + +5.2.6 Line-Time Clock (CLK) + +The clock (CLK) implements these registers: + + name size comments + + CSR 16 control/status register + INT 1 interrupt pending flag + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + TIME 24 clock frequency + +5.3 RK11/RK05 Cartridge Disk (RK) + +RK11 options include the ability to make units write enabled or write locked: + + SET RKn LOCKED set unit n write locked + SET RKn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The RK11 implements these registers: + + name size comments + + RKCS 16 control/status + RKDA 16 disk address + RKBA 16 memory address + RKWC 16 word count + RKDS 16 drive status + RKER 16 error status + INTQ 9 interrupt queue + DRVN 3 number of last selected drive + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + INT 1 interrupt pending flag + STIME 24 seek time, per cylinder + RTIME 24 rotational delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + + end of file x assume rest of disk is zero + + OS I/O error x report error and stop + +5.4 RX11/RX01 Floppy Disk (RX) + +RX11 options include the ability to make units write enabled or write locked: + + SET RXn LOCKED set unit n write locked + SET RXn ENABLED set unit n write enabled + +The RX11 implements these registers: + + name size comments + + RXCS 12 status + RXDB 8 data buffer + RXES 8 error status + RXERR 8 error code + RXTA 8 current track + RXSA 8 current sector + STAPTR 3 controller state + BUFPTR 3 buffer pointer + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + TR 1 transfer ready flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + DONE 1 device done flag (CSR<5>) + CTIME 24 command completion time + STIME 24 seek time, per track + XTIME 24 transfer ready delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + +RX01 data files are buffered in memory; therefore, end of file and OS +I/O errors cannot occur. + +5.5 RL11(V12)/RL01,RL02 Cartridge Disk (RL) + +RL11 options include the ability to set units write enabled or write locked, +to set the drive size to RL01, RL02, or autosize, and to write a DEC standard +044 compliant bad block table on the last track: + + SET RLn LOCKED set unit n write locked + SET RLn ENABLED set unit n write enabled + SET RLn RL01 set size to RL01 + SET RLn RL02 set size to RL02 + SET RLn AUTOSIZE set size based on file size at attach + SET RLn BADBLOCK write bad block table on last track + +The size options can be used only when a unit is not attached to a file. The +bad block option can be used only when a unit is attached to a file. Units +can also be REMOVEd or ADDed to the configuration. + +The RL11 implements these registers: + + name size comments + + RLCS 16 control/status + RLDA 16 disk address + RLBA 16 memory address + RLBAE 6 memory address extension (RLV12) + RLMP..RLMP2 16 multipurpose register queue + INT 1 interrupt pending flag + ERR 1 error flag (CSR<15>) + DONE 1 device done flag (CSR<7>) + IE 1 interrupt enable flag (CSR<6>) + STIME 24 seek time, per cylinder + RTIME 24 rotational delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + + end of file x assume rest of disk is zero + + OS I/O error x report error and stop + +5.6 RM02/03/05/80, RP04/05/06/07 Disk Pack Drives (RP) + +The RP controller implements a "Massbus style" 22b direct interface for +large disk drives. It is more abstract than other device simulators, with +just enough detail to run operating system drivers. In addition, the RP +controller conflates the details of the RM series controllers with the RP +series controllers, although there were detailed differences. + +RP options include the ability to set units write enabled or write locked, +to set the drive size to one of seven disk types, or autosize, and to write +a DEC standard 044 compliant bad block table on the last track: + + SET RPn LOCKED set unit n write locked + SET RPn ENABLED set unit n write enabled + SET RPn RM03 set size to RM03 + SET RPn RM05 set size to RM05 + SET RPn RM80 set size to RM80 + SET RPn RP04 set size to RP04 + SET RPn RP06 set size to RP06 + SET RPn RP07 set size to RP07 + SET RPn AUTOSIZE set size based on file size at attach + SET RLn BADBLOCK write bad block table on last track + +The size options can be used only when a unit is not attached to a file. The +bad block option can be used only when a unit is attached to a file. Units +can also be REMOVEd or ADDed to the configuration. + +The RP controller implements these registers: + + name size comments + + RPCS1 16 control/status 1 + RPCS2 16 control/status 2 + RPCS3 16 control/status 3 + RPWC 16 word count + RPBA 16 bus address + RPBAE 6 bus address extension + RPDA 16 desired surface, sector + RPDC 8 desired cylinder + RPOF 16 offset + RPDS0..7 16 drive status, drives 0-7 + RPDE0..7 16 drive error, drives 0-7 + RPER2 16 error status 2 + RPER3 16 error status 3 + RPDB 16 data buffer + RPMR 16 maintenance register + INT 1 interrupt pending flag + SC 1 special condition (CSR1<15>) + DONE 1 device done flag (CSR1<7>) + IE 1 interrupt enable flag (CSR1<6>) + STIME 24 seek time, per cylinder + RTIME 24 rotational delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + + end of file x assume rest of disk is zero + + OS I/O error x report error and stop + +5.7 TM11 Magnetic Tape (TM) + +Magnetic tape options include the ability to make units write enabled or +or write locked. + + SET TMn LOCKED set unit n write locked + SET TMn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The magnetic tape bootstrap supports both original and DEC standard boot +formats. Originally, a tape bootstrap read and executed the first record +on tape. To allow for ANSI labels, the DEC standard bootstrap skipped the +first record and read and executed the second. The DEC standard is the +default; to bootstrap an original format tape, use the -o switch with the +BOOT command. + +The magnetic tape controller implements these registers: + + name size comments + + MTS 16 status + MTC 16 command + MTCMA 16 memory address + MTBRC 16 byte/record count + INT 1 interrupt pending flag + ERR 1 error flag + DONE 1 device done flag + IE 1 interrupt enable flag + STOP_IOE 1 stop on I/O error + TIME 24 delay + UST0..7 16 unit status, units 0..n + POS0..7 31 position, units 0..n + +Error handling is as follows: + + error processed as + + not attached tape not ready + + end of file (read or space) end of physical tape + (write) ignored + + OS I/O error report error and stop + +5.8 Symbolic Display and Input + +The PDP-11 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as two character ASCII string + -m display instruction mnemonics + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c two character ASCII string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses standard PDP-11 assembler syntax. There are sixteen +instruction classes: + +class operands examples comments + +no operands none HALT, RESET +3b literal literal, 0 - 7 SPL +6b literal literal, 0 - 077 MARK +8b literal literal, 0 - 0377 EMT, TRAP +register register RTS +sop specifier SWAB, CLR, ASL +reg-sop register, specifier JSR, XOR, MUL +fop flt specifier ABSf, NEGf +ac-fop flt reg, flt specifier LDf, MULf +ac-sop flt reg, specifier LDEXP, STEXP +ac-moded sop flt reg, specifier LDCif, STCfi +dop specifier, specifier MOV, ADD, BIC +cond branch address BR, BCC, BNE +sob register, address SOB +cc clear cc clear instructions CLC, CLV, CLZ, CLN combinable +cc set cc set instructions SEC, SEV, SEZ, SEN combinable + +For floating point opcodes, F and D variants, and I and L variants, may be +specified regardless of the state of FPS. + +The syntax for specifiers is as follows: + +syntax specifier displacement comments + +Rn 0n - +Fn 0n - only in flt reg classes +(Rn) 1n - +@(Rn) 7n 0 equivalent to @0(Rn) +(Rn)+ 2n - +@(Rn)+ 3n - +-(Rn) 4n - +@-(Rn) 5n - +{+/-}d(Rn) 6n {+/-}d +@{+/-}d(Rn) 7n {+/-}d +#n 27 n +@#n 37 n +.+/-n 67 +/-n - 4 +@.+/-n 77 +/-n - 4 +{+/-}n 67 {+/-}n - PC - 4 if on disk, 37 and n +@{+/-}n 77 {+/-}n - PC - 4 if on disk, invalid + +6. Nova Features + +The Nova simulator is configured as follows: + +device simulates +name(s) + +CPU Nova CPU with 32KW of memory +- hardware multiply/divide +PTR,PTP paper tape reader/punch +TTI,TTO console terminal +LPT line printer +CLK real-time clock +DK head-per-track disk controller +DP moving head disk controller with four drives +MT magnetic tape controller with eight drives + +The Nova simulator implements these unique stop conditions: + + - reference to undefined I/O device, and STOP_DEV is set + - more than INDMAX indirect addresses are detected during + an interrupt + - more than INDMAX indirect addresses are detected during + memory reference address decoding + +The Nova loader supports standard binary format tapes. The DUMP command +is not implemented. + +6.1 CPU + +The only CPU options are the presence of the optional instructions +and the size of main memory. + + SET CPU NOVA4 enable Nova4 instructions + SET CPU NOVA3 enable Nova3 instructions + SET CPU MDV enable multiply/divide + SET CPU NONE disable all optional instructions + SET CPU 4K set memory size = 4K + SET CPU 8K set memory size = 8K + SET CPU 12K set memory size = 12K + SET CPU 16K set memory size = 16K + SET CPU 20K set memory size = 20K + SET CPU 24K set memory size = 24K + SET CPU 28K set memory size = 28K + SET CPU 32K set memory size = 32K + +(Nova 4 = optional multiply/divide, stack, byte, trap instructions) +(Nova 3 = optional multiply/divide, stack instructions) +(MDV = multiply/divide instructions) + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 32K. + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + PC 15 program counter + AC0..AC3 16 accumulators 0..3 + C 1 carry + SR 16 front panel switches + PI 16 priority interrupt mask + ION 1 interrupt enable + ION_DELAY 1 interrupt enable delay for ION + PWR 1 power fail interrupt + INT 15 interrupt pending flags + BUSY 15 device busy flags + DONE 15 device done flags + DISABLE 15 device interrupt disable flags + STOP_DEV 1 stop on undefined IOT + INDMAX 15 maximum number of nested indirects + OLDPC 15 PC prior to last JMP, JMS, or interrupt + BREAK 16 breakpoint address (177777 to disable) + WRU 8 interrupt character + +6.2 Programmed I/O Devices + +6.2.1 Paper Tape Reader (PTR) + +The paper tape reader (PTR) reads data from a disk file. The POS +register specifies the number of the next data item to be read. Thus, +by changing POS, the user can backspace or advance the reader. + +The paper tape reader implements these registers: + + name size comments + + BUF 8 last data item processed + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + POS 31 position in the input file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + errpr STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape or paper + + end of file 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +6.2.2 Paper Tape Punch (PTP) + +The paper tape punch (PTP) writes data to a disk file. The POS +register specifies the number of the next data item to be written. +Thus, by changing POS, the user can backspace or advance the punch. + +The paper tape punch implements these registers: + + name size comments + + BUF 8 last data item processed + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +6.2.3 Terminal Input (TTI) + +The terminal input reads from the controling console port. Terminal +options include the ability to set limited Dasher compatibility mode or +normal mode: + + SET TTI ANSI normal mode + SET TTI DASHER Dasher mode + SET TTO ANSI normal mode + SET TTO DASHER Dasher mode + +Setting either TTI or TTO changes both devices. In Dasher mode, carriage +return is changed to newline on input, and ^X is changed to backspace. + +The terminal input implements these registers: + + name size comments + + BUF 8 last data item processed + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + POS 31 number of characters input + TIME 24 keyboard polling interval + +6.2.4 Terminal Output (TTO) + +The terminal output writes to the controling console port. Terminal +options include the ability to set limited Dasher compatibility mode or +normal mode: + + SET TTI ANSI normal mode + SET TTI DASHER Dasher mode + SET TTO ANSI normal mode + SET TTO DASHER Dasher mode + +Setting either TTI or TTO changes both devices. In Dasher mode, carriage +return is changed to newline on input, and ^X is changed to backspace. + +The terminal output implements these registers: + + name size comments + + BUF 8 last data item processed + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + POS 31 number of characters output + TIME 24 time from I/O initiation to interrupt + +6.2.5 Programmed I/O Devices (LPT) + +The line printer (LPT) writes data to a disk file. The POS register +specifies the number of the next data item to be written. Thus, +by changing POS, the user can backspace or advance the printer. + +The line printer implements these registers: + + name size comments + + BUF 8 last data item processed + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of paper + + OS I/O error x report error and stop + +6.2.6 Real-Time Clock (CLK) + +The real-time clock (CLK) implements these registers: + + name size comments + + SELECT 2 selected clock interval + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + TIME0 24 clock frequency, select = 0 + TIME1 24 clock frequency, select = 1 + TIME2 24 clock frequency, select = 2 + TIME3 24 clock frequency, select = 3 + +6.3 Fixed Head Disk (DK) + +The fixed head disk controller implements these registers: + + name size comments + + STAT 16 status + DA 16 disk address + MA 16 memory address + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 device disable flag + INT 1 interrupt pending flag + WLK 8 write lock switches + TIME 24 rotational delay, per sector + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + +Fixed head disk data files are buffered in memory; therefore, end of file +and OS I/O errors cannot occur. + +6.4 Moving Head Disk (DP) + +Moving head disk options include the ability to make units write enabled or +write locked, and to select the type of drive: + + SET DPn LOCKED set unit n write locked + SET DPn ENABLED set unit n write enabled + SET DPn FLOPPY set unit n to floppy disk + SET DPn D31 set unit n to Diablo 31 + SET DPn D44 set unit n to Diablo 44 + SET DPn C111 set unit n to Century 111 + SET DPn C114 set unit n to Century 114 + SET DPn 6225 set unit n to 6225 + SET DPn 6099 set unit n to 6099 + SET DPn 6227 set unit n to 6227 + SET DPn 6070 set unit n to 6070 + SET DPn 6103 set unit n to 6103 + SET DPn 4231 set unit n to 4231 + +Units can also be REMOVEd or ADDed to the configuration. + +All drives have 256 16b words per sector. The other disk parameters are: + + drive cylinders surfaces sectors size (MW) DG models + + floppy 77 1 8 .158 6038 + D31 203 2 12 1.247 4047, 4237, 4238 + D44 408 4 12 5.014 4234, 6045 + C111 203 10 6 3.118 4048 + C114 203 20 12 12.472 4057, 2314 + 6225 20 2 245 2.508 + 6099 32 4 192 6.291 + 6227 20 6 245 7.526 + 6070 24 4 408 10.027 + 6103 32 8 192 12.583 + 4231 23 19 411 45.979 + +The moving head disk controller implements these registers: + + name size comments + + FCCY 16 flags, command, cylinder + USSC 16 unit, surface, sector, count + STAT 16 status + MA 16 memory address + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + STIME 24 seek time, per cylinder + RTIME 24 rotational delay + +Error handling is as follows: + + error processed as + + not attached disk not ready + + end of file assume rest of disk is zero + + OS I/O error report error and stop + +6.5 Magnetic Tape (MT) + +Magnetic tape options include the ability to make units write enabled or +or write locked. + + SET MTn LOCKED set unit n write locked + SET MTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The magnetic tape controller implements these registers: + + name size comments + + CU 16 command, unit + MA 16 memory address + WC 16 word count + STA1 16 status word 1 + STA2 16 status word 2 + EP 1 extended polling mode (not supported) + BUSY 1 device busy flag + DONE 1 device done flag + DISABLE 1 interrupt disable flag + INT 1 interrupt pending flag + STOP_IOE 1 stop on I/O error + CTIME 24 controller delay + RTIME 24 record delay + UST0..7 32 unit status, units 0..n + POS0..7 31 position, units 0..n + +Error handling is as follows: + + error processed as + + not attached tape not ready + + end of file (read or space) end of physical tape + (write) ignored + + OS I/O error report error and stop + +6.6 Symbolic Display and Input + +The Nova simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as two character ASCII string + -m display instruction mnemonics + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c two character ASCII string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses standard Nova assembler syntax. There are three +instruction classes: memory reference, IOT, and operate. + +Memory reference instructions have the format + + memref {ac,}{@}address{,index} + +LDA and STA require an initial register; ISZ, DSZ, JSR, and JMP do not. +The syntax for addresses and indices is as follows: + +syntax mode displacement comments + +0 <= n < 0400 0 n +{+/-}n >= 0400 1 {+/-}n - PC must be in range [-200, 177] + invalid on disk +.+/-n 1 {+/-}n must be in range [-200, 177] +{+/-}n,2 2 {+/-}n must be in range [-200, 177] +{+/-}n,3 3 {+/-}n must be in range [-200, 177] + +IOT instructions have one of four formats + + syntax example + + iot HALT + iot reg INTA + iot device SKPDN + iot reg,device DOAS + +Devices may be specified as mnemonics or as numbers in the range 0 - 077. + +Operate instructions have the format + + opcode{#} reg,reg{,skip} + +In all Nova instructions, blanks may be substituted for commas as field +delimiters. + +7. PDP-1 Features + +The PDP-1 is configured as follows: + +device simulates +name(s) + +CPU PDP-1 CPU with up to 64KW of memory +PTR,PTP integral paper tape reader/punch +TTI,TTO Flexowriter typewriter input/output +LPT Type 62 line printer + +The PDP-1 simulator implements the following unique stop conditions: + + - an unimplemented instruction is decoded, and register + STOP_INST is set + - more than INDMAX indirect addresses are detected during + memory reference address decoding + - more than XCTMAX nested executes are detected during + instruction execution + - wait state entered, and no I/O operations outstanding + (ie, no interrupt can ever occur) + +The PDP-1 loader supports RIM format tapes. The DUMP command is not +implemented. + +7.1 CPU + +The only CPU options are the presence of hardware multiply/divide and the +size of main memory. + + SET CPU MDV enable multiply/divide + SET CPU NOMDV disable multiply/divide + SET CPU 4K set memory size = 4K + SET CPU 8K set memory size = 8K + SET CPU 12K set memory size = 12K + SET CPU 16K set memory size = 16K + SET CPU 20K set memory size = 20K + SET CPU 24K set memory size = 24K + SET CPU 28K set memory size = 28K + SET CPU 32K set memory size = 32K + SET CPU 48K set memory size = 48K + SET CPU 64K set memory size = 64K + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 64K. + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + PC 16 program counter + AC 18 accumulator + IO 18 IO register + OV 1 overflow flag + PF 6 program flags<1:6> + SS 6 sense switches<1:6> + TW 18 test word (front panel switches) + EXTM 1 extend mode + IOSTA 18 IO status register + SBON 1 sequence break enable + SBRQ 1 sequence break request + SBIP 1 sequence break in progress + IOH 1 I/O halt in progress + IOC 1 I/O continue + OLDPC 16 PC prior to last transfer + STOP_INST 1 stop on undefined instruction + SBS_INIT 1 initial state of sequence break enable + EXTM_INIT 1 initial state of extend mode + BREAK 17 breakpoint address (377777 to disable) + WRU 8 interrupt character + +7.2 Programmed I/O Devices + +7.2.1 Paper Tape Reader (PTR) + +The paper tape reader (PTR) reads data from or a disk file. The POS +register specifies the number of the next data item to be read. Thus, +by changing POS, the user can backspace or advance the reader. + +The paper tape reader implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + RPLS 1 return restart pulse flag + POS 31 position in the input file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + end of file 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +7.2.2 Paper Tape Punch (PTP) + +The paper tape punch (PTP) writes data to a disk file. The POS +register specifies the number of the next data item to be written. +Thus, by changing POS, the user can backspace or advance the punch. + +The paper tape punch implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + RPLS 1 return restart pulse flag + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +7.2.3 Terminal Input (TTI) + +The terminal input (TTO) reads from the controling console port. +It implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + POS 31 number of characters input + TIME 24 keyboard polling interval + +7.2.4 Terminal Output (TTO) + +The terminal output (TTO) writes to the controling console port. +It implements these registers: + + name size comments + + BUF 8 last data item processed + DONE 1 device done flag + RPLS 1 return restart pulse flag + POS 31 number of characters output + TIME 24 time from I/O initiation to interrupt + +7.2.5 Type 62 Line Printer (LPT) + +The paper line printer (LPT) writes data to a disk file. The POS +register specifies the number of the next data item to be written. +Thus, by changing POS, the user can backspace or advance the printer. + +The line printer implements these registers: + + name size comments + + BUF 8 last data item processed + PNT 1 printing done flag + SPC 1 spacing done flag + RPLS 1 return restart pulse flag + BPTR 6 print buffer pointer + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +7.3 Symbolic Display and Input + +The PDP-1 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as FIODEC character string + -m display instruction mnemonics + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c three character FIODEC string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses modified PDP-1 assembler syntax. There are six +instruction classes: memory reference, shift, skip, operate, IOT, and +LAW. + +Memory reference instructions have the format + + memref {I} address + +where I signifies indirect reference. The address is an octal number in +the range 0 - 0177777. + +Shift instructions have the format + + shift shift_count + +The shift count is an octal number in the range 0-9. + +Skip instructions consist of single mnemonics, eg, SZA, SZS4. Skip +instructions may be or'd together + + skip skip skip... + +The sense of a skip can be inverted by including the mnemonic I. + +Operate instructions consist of single mnemonics, eg, CLA, CLI. Operate +instructions may be or'd together + + opr opr opr... + +IOT instructions consist of single mnemonics, eg, TYI, TYO. IOT +instructions may include an octal numeric modifier or the modifier I: + + iot modifier + +The simulator does not check the legality of skip, operate, or IOT +combinations. + +Finally, the LAW instruction has the format + + LAW {I} immediate + +where immediate is in the range 0 to 07777. + +7.4 Character Sets + +The PDP-1's console was a Frieden Flexowriter; its character encoding +was known as FIODEC. The PDP-1's line printer used a modified Hollerith +character set. The following table provides equivalences between ASCII +characters and the PDP-1's I/O devices. In the console table, UC stands +for upper case. + + PDP-1 PDP-1 +ASCII console line printer + +000 - 007 none none +bs 075 none +tab 036 none +012 - 014 none none +cr 077 none +016 - 037 none none +space 000 000 +! {OR} UC+005 none +" UC+001 none +# {IMPLIES} UC+004 none +$ none none +% none none +& {AND} UC+006 none +' UC+002 none +( 057 057 +) 055 055 +* {TIMES} UC+073 072 ++ UC+054 074 +, 033 033 +- 054 054 +. 073 073 +/ 021 021 +0 020 020 +1 001 001 +2 002 002 +3 003 003 +4 004 004 +5 005 005 +6 006 006 +7 007 007 +8 010 010 +9 011 011 +: none none +; none none +< UC+007 034 += UC+033 053 +> UC+010 034 +? UC+021 037 +@ {MID DOT} 040 {MID DOT} 040 +A UC+061 061 +B UC+062 062 +C UC+063 063 +D UC+064 064 +E UC+065 065 +F UC+066 066 +G UC+067 067 +H UC+070 070 +I UC+071 071 +J UC+041 041 +K UC+042 042 +L UC+043 043 +M UC+044 044 +N UC+045 045 +O UC+046 046 +P UC+047 047 +Q UC+050 050 +R UC+051 051 +S UC+022 022 +T UC+023 023 +U UC+024 024 +V UC+025 025 +W UC+026 026 +X UC+027 027 +Y UC+030 030 +Z UC+031 031 +[ UC+057 none +\ {OVERLINE} 056 {OVERLINE} 056 +] UC+055 none +^ {UP ARROW} UC+011 {UP ARROW} 035 +_ UC+040 UC+040 +` {RT ARROW} UC+020 036 +a 061 none +b 062 none +c 063 none +d 064 none +e 065 none +f 066 none +g 067 none +h 070 none +i 071 none +j 041 none +k 042 none +l 043 none +m 044 none +n 045 none +o 046 none +p 047 none +q 050 none +r 051 none +s 022 none +t 023 none +u 024 none +v 025 none +w 026 none +x 027 none +y 030 none +z 031 none +{ none none +| UC+056 076 +} none none +~ UC+003 013 +del 075 none + +8. 18b PDP Features + +The other four 18b PDP's (PDP-4, PDP-7, PDP-9, PDP-15) are very similar +and are configured as follows: + +system device simulates + name(s) + +PDP-4 CPU PDP-4 CPU with 8KW of memory + PTR,PTP integral paper tape/Type 75 punch + TTI,TTO KSR28 console terminal (Baudot code) + LPT Type 62 line printer (Hollerith code) + CLK integral real-time clock + +PDP-7 CPU PDP-7 CPU with 32KW of memory + - Type 177 extended arithmetic element (EAE) + - Type 148 memory extension + PTR,PTP Type 444 paper tape reader/Type 75 punch + TTI,TTO KSR 33 console terminal + LPT Type 647 line printer + CLK integral real-time clock + DRM Type 24 serial drum + +PDP-9 CPU PDP-9 CPU with 32KW of memory + - KE09A extended arithmetic element (EAE) + - KG09B memory extension + - KP09A power detection + - KX09A memory protection + PTR,PTP PC09A paper tape reader/punch + TTI,TTO KSR 33 console terminal + LPT Type 647E line printer + CLK integral real-time clock + RF RF09/RS09 fixed-head disk + MT TC59/TU10 magnetic tape + +PDP-15 CPU PDP-15 CPU with 32KW of memory + - KE15 extended arithmetic element (EAE) + - KF15 power detection + - KM15 memory protection + PTR,PTP PC15 paper tape reader/punch + TTI,TTO KSR 35 console terminal + LPT LP15 line printer + CLK integral real-time clock + RP RP15/RP02 disk pack + RF RF15/RS09 fixed-head disk + MT TC59/TU10 magnetic tape + +The 18b PDP simulators implement several unique stop conditions: + + - an unimplemented instruction is decoded, and register + STOP_INST is set + - more than XCTMAX nested executes are detected during + instruction execution + +The 18b PDP loader supports RIM format tapes. The DUMP command is not +implemented. + +8.1 CPU + +The only CPU options are the presence of the EAE and the size of main memory. + + SET CPU EAE enable EAE + SET CPU NOEAE disable EAE + SET CPU 4K set memory size = 4K + SET CPU 8K set memory size = 8K + SET CPU 12K set memory size = 12K + SET CPU 16K set memory size = 16K + SET CPU 20K set memory size = 20K + SET CPU 24K set memory size = 24K + SET CPU 28K set memory size = 28K + SET CPU 32K set memory size = 32K + SET CPU 48K set memory size = 48K + SET CPU 64K set memory size = 64K + SET CPU 80K set memory size = 80K + SET CPU 96K set memory size = 96K + SET CPU 112K set memory size = 112K + SET CPU 128K set memory size = 128K + +Memory sizes greater than 8K are only available on the PDP-7, PDP-9, and +PDP-15; memory sizes greater than 32KW are only available on the PDP-15. +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 8K for the PDP-4, 32K +for the PDP-7 and PDP-9, and 128K for the PDP-15. + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + system name size comments + + all PC addr program counter + 7,9 PC 15 program counter + 15 PC 17 program counter + all AC 18 accumulator + 7,9,15 MQ 18 multiplier-quotient + 7,9,15 SC 6 shift counter + 7,9,15 EAE_AC_SIGN 1 EAE AC sign + all L 1 link + 7,9 EXTM 1 extend mode + 15 BANKM 1 bank mode + 7 TRAPM 1 trap mode + 9,15 USMD 1 user mode + 9,15 USMDBUF 1 user mode buffer + 9,15 BR addr memory protection bounds + 7,9,15 TRAPP 1 trap pending + 9,15 NEXM 1 non-existent memory violation + 9,15 PRVN 1 privilege violation + 7,9 EMIRP 1 EMIR instruction pending + 9,15 RESTP 1 DBR or RES instruction pending + 15 XR 18 index register + 15 LR 18 limit register + all SR 18 front panel switches + all INT 32 interrupt requests + all IORS 18 IORS register + all ION 1 interrupt enable + all ION_DELAY 2 interrupt enable delay + all OLDPC addr PC prior to last transfer + all STOP_INST 1 stop on undefined instruction + all BREAK 18 breakpoint address (777777 to disable) + all WRU 8 interrupt character + +"addr" signifies the address width of the system (13b for the PDP-4, 15b for +the PDP-7 and PDP-9, 17b for the PDP-15). + +8.2 Programmed I/O Devices + +8.2.1 Paper Tape Reader (PTR) + +The paper tape reader (PTR) reads data from a disk file. The POS +register specifies the number of the next data item to be read. Thus, +by changing POS, the user can backspace or advance the reader. + +The paper tape reader implements these registers: + + name size comments + + BUF 8 last data item processed + INT 1 interrupt pending flag + DONE 1 device done flag + ERR 1 error flag (PDP-9, PDP-15 only) + POS 31 position in the input file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + end of file 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +8.2.2 Paper Tape Punch (PTP) + +The ppaper tape punch (PTP) writes data to a disk file. The POS +register specifies the number of the next data item to be written. +Thus, by changing POS, the user can backspace or advance the punch. + +The paper tape punch implements these registers: + + name size comments + + BUF 8 last data item processed + INT 1 interrupt pending flag + DONE 1 device done flag + ERR 1 error flag (PDP-9, PDP-15 only) + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +8.2.3 Terminal Input (TTI) + +The terminal input (TTI) reads from the controling console port. The +input has one option, UC; when set, it automatically converts lower +case input to upper case. + +The terminal input implements these registers: + + name size comments + + BUF 8 last data item processed + INT 1 interrupt pending flag + DONE 1 device done flag + POS 31 number of characters input + TIME 24 keyboard polling interval + +8.2.4 Terminal Output (TTO)n backspace or advance these devices. + +The terminal output (TTO) writes to the controling console port. It +implements these registers: + + name size comments + + BUF 8 last data item processed + INT 1 interrupt pending flag + DONE 1 device done flag + POS 31 number of chararacters output + TIME 24 time from I/O initiation to interrupt + +8.2.5 Line Printer (LPT) + +The line printer (LPT) writes data to a disk file. The POS register +specifies the number of the next data item to be written. Thus, +by changing POS, the user can backspace or advance the printer. + +The PDP-4 used a Type 62 printer controller, with these registers: + + name size comments + + BUF 8 last data item processed + INT 1 interrupt pending flag + DONE 1 device done flag + SPC 1 spacing done flag + BPTR 6 print buffer pointer + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +The PDP-7 and PDP-7 used a Type 647 printer controller, with these +registers: + + name size comments + + BUF 8 last data item processed + INT 1 interrupt pending flag + DONE 1 device done flag + ENABLE 1 interrupt enable (PDP-9 only) + ERR 1 error flag + BPTR 7 print buffer pointer + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +The PDP-15 used an LP15 printer controller, with these registers: + + name size comments + + STA 18 status register + MA 18 DMA memory address + INT 1 interrupt pending flag + ENABLE 1 interrupt enable + LCNT 8 line counter + BPTR 7 print buffer pointer + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + +For all three models, error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +8.2.6 Real-Time Clock (CLK) + +The real-time clock (CLK) implements these registers: + + name size comments + + INT 1 interrupt pending flag + DONE 1 device done flag + ENABLE 1 clock enable + TIME 24 clock frequency + +8.3 RP15/RP02 Disk Pack (RP) + +RP15 options include the ability to make units write enabled or write locked: + + SET RPn LOCKED set unit n write locked + SET RPn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The RP15 implements these registers: + + name size comments + + STA 18 status A + STB 18 status B + DA 18 disk address + MA 18 current memory address + WC 18 word count + INT 1 interrupt pending flag + BUSY 1 control busy flag + STIME 24 seek time, per cylinder + RTIME 24 rotational delay + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + + end of file x assume rest of disk is zero + + OS I/O error x report error and stop + +8.4 Type 24 Serial Drum (DRM) + +The serial drum (DRM) implements these registers: + + name size comments + + DA 9 drum address (sector number) + MA 15 current memory address + INT 1 interrupt pending flag + DONE 1 device done flag + ERR 1 error flag + WLK 32 write lock switches + TIME 24 rotational latency, per word + STOP_IOE 1 stop on I/O error + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + +Drum data files are buffered in memory; therefore, end of file and OS +I/O errors cannot occur. + +8.5 RF09/RF15/RS09 Fixed Head Disk (RF) + +The RF09/RF15 implements these registers: + + name size comments + + STA 18 status + DA 21 current disk address + MA 18 memory address (in memory) + WC 18 word count (in memory) + BUF 18 data buffer (diagnostic only) + INT 1 interrupt pending flag + WLK0..7 16 write lock switches for disks 0..7 + TIME 24 rotational delay, per word + BURST 1 burst flag + STOP_IOE 1 stop on I/O error + +The RF09/RF15 is a three-cycle data break device. If BURST = 0, word +transfers are scheduled individually; if BURST = 1, the entire transfer +occurs in a single data break. + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 disk not ready + +RF15/RF09 data files are buffered in memory; therefore, end of file and OS +I/O errors cannot occur. + +8.6 TC59/TU10 Magnetic Tape (MT) + +Magnetic tape options include the ability to make units write enabled or +or write locked. + + SET MTn LOCKED set unit n write locked + SET MTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The magnetic tape controller implements these registers: + + name size comments + + CMD 18 command + STA 18 main status + MA 18 memory address (in memory) + WC 18 word count (in memory) + INT 1 interrupt pending flag + STOP_IOE 1 stop on I/O error + TIME 24 record delay + UST0..7 24 unit status, units 0..n + POS0..7 31 position, units 0..n + +Error handling is as follows: + + error processed as + + not attached tape not ready + + end of file (read or space) end of physical tape + (write) ignored + + OS I/O error report error and stop + +8.7 Symbolic Display and Input + +The 18b PDP simulators implement symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as (sixbit) character string + -m display instruction mnemonics + +The PDP-15 also recognizes an additional switch: + + -p display as packed ASCII (five 7b ASCII + characters in two 18b words) + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c three character sixbit string + alphabetic instruction mnemonic + numeric octal number + +The PDP-15 also recognizes an additional input mode: + + # or -p five character packed ASCII string in + two 18b words + +Instruction input uses standard 18b PDP assembler syntax. There are six +instruction classes: memory reference, EAE, index (PDP-15 only), IOT, +operate, and LAW. + +Memory reference instructions have the format + + memref {I/@} address{,X} + +where I (PDP-4, PDP-7, PDP-9) /@ (PDP-15) signifies indirect reference, +and X signifies indexing (PDP-15 in page mode only). The address is an +octal number in the range 0 - 017777 (PDP-4, PDP-7, PDP-9, and PDP-15 in +bank mode) or 0 - 07777 (PDP-15 in page mode). + +IOT instructions consist of single mnemonics, eg, KRB, TLS. IOT instructions +may be or'd together + + iot iot iot... + +IOT's may also include the number 10, signifying clear the accumulator + + iot 10 + +The simulator does not check the legality of IOT combinations. IOT's for +which there is no opcode may be specified as IOT n, where n is an octal +number in the range 0 - 07777. + +EAE instructions have the format + + eae {+/- shift count} + +EAE instructions may be or'd together + + eae eae eae... + +The simulator does not check the legality of EAE combinations. EAE's for +which there is no opcode may be specified as EAE n, where n is an octal +number in the range 0 - 037777. + +Index instructions (PDP-15 only) have the format + + index {immediate} + +The immediate, if allowed, must be in the range of -0400 to +0377. + +Operate instructions have the format + + opr opr opr... + +The simulator does not check the legality of the proposed combination. The +operands for MUY and DVI must be deposited explicitly. + +Finally, the LAW instruction has the format + + LAW immediate + +where immediate is in the range of 0 to 017777. + +8.8 Character Sets + +The PDP-4's console was an ASR-28 Teletype; its character encoding was +Baudot. The PDP-4's line printer used a modified Hollerith character +set. The PDP-7's and PDP-9's consoles were KSR-33 Teletypes; their +character sets were basically ASCII. The PDP-7's and PDP-9's line +printers used sixbit encoding (ASCII codes 040 - 0137 masked to six +bits). The PDP-15's I/O devices were all ASCII. The following table +provides equivalences between ASCII characters and the PDP-4's I/O devices. +In the console table, FG stands for figures (upper case). + + PDP-4 PDP-4 +ASCII console line printer + +000 - 006 none none +bell FG+024 none +010 - 011 none none +lf 010 none +013 - 014 none none +cr 002 none +016 - 037 none none +space 004 000 +! FG+026 none +" FG+021 none +# FG+005 none +$ FG+062 none +% none none +& FG+013 none +' FG+032 none +( FG+036 057 +) FG+011 055 +* none 072 ++ none 074 +, FG+006 033 +- FG+030 054 +. FG+007 073 +/ FG+027 021 +0 FG+015 020 +1 FG+035 001 +2 FG+031 002 +3 FG+020 003 +4 FG+012 004 +5 FG+001 005 +6 FG+025 006 +7 FG+034 007 +8 FG+014 010 +9 FG+003 011 +: FG+016 none +; FG+017 none +< none 034 += none 053 +> none 034 +? FG+023 037 +@ none {MID DOT} 040 +A 030 061 +B 023 062 +C 016 063 +D 022 064 +E 020 065 +F 026 066 +G 013 067 +H 005 070 +I 014 071 +J 032 041 +K 036 042 +L 011 043 +M 007 044 +N 006 045 +O 003 046 +P 015 047 +Q 035 050 +R 012 051 +S 024 022 +T 001 023 +U 034 024 +V 017 025 +W 031 026 +X 027 027 +Y 025 030 +Z 021 031 +[ none none +\ none {OVERLINE} 056 +] none none +^ none {UP ARROW} 035 +_ none UC+040 +0140 - 0177 none none + +9. IBM 1401 Features + +The IBM 1401 simulator is configured as follows: + +device simulates +name(s) + +CPU IBM 1401 CPU with 16K of memory +CDR,CDP IBM 1402 card reader/punch +LPT IBM 1403 line printer +INQ IBM 1407 inquiry terminal +MT IBM 729 7-track magnetic tape controller with six drives + +The IBM 1401 simulator implements many unique stop conditions. On almost +any kind of error the simulator stops: + + unimplemented opcode + reference to non-existent memory + reference to non-existent device + no word mark under opcode + invalid A address + invalid B address + invalid instruction length + invalid modifier character + invalid branch address + invalid magtape unit number + invalid magtape record length + write to locked magtape drive + skip to unpunched carriage control tape channel + card reader hopper empty + address register wrap-around + single character A field in MCE + single character B field in MCE + hanging $ in MCE with EPE enabled + I/O check with I/O stop switch set + +The LOAD and DUMP commands are not implemented. + +9.1 CPU + +The CPU options include a number of special features and the size of main +memory. Note that the Modify Address special feature is always included +when memory size is greater than 4K. + + SET CPU XSA enable advanced programming special feature + SET CPU NOXSA disable advanced programming + SET CPU HLE enable high/low/equal special feature + SET CPU NOHLE disable high/low/equal + SET CPU BBE enable branch on bit equal special feature + SET CPU NOBBE disable branch on bit equal + SET CPU MR enable move record special feature + SET CPU NOMR disable move record + SET CPU EPE enable extended print edit special feature + SET CPU NOEPE disable extended print edit + SET CPU 4K set memory size = 4K + SET CPU 8K set memory size = 8K + SET CPU 12K set memory size = 12K + SET CPU 16K set memory size = 16K + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initially, memory size is 16K, and all special +features are enabled. + +Memory is implemented as 7 bit BCD characters, as follows: + + 6 5 4 3 2 1 0 + + word B bit A bit 8 4 2 1 + mark <-- zone --> <-------- digit --------> + +In BCD, the decimal digits 0-9 are (octal) values 012, 001, 002, 003, 004, +005, 006, 007, 010, 011, respectively. Signs are encoded in the zone bits, +with 00, 01, and 11 being positive, and 10 being negative. + +CPU registers include the visible state of the processor. The 1401 has no +interrupt system. + + name size comments + + IS 14 instruction storage address register (PC) + AS 14 A storage address register + BS 14 B storage address register + ASERR 1 AS invalid flag + BSERR 1 BS invalid flag + SSA 1 sense switch A + SSB 1 sense switch B + SSC 1 sense switch C + SSD 1 sense switch D + SSE 1 sense switch E + SSF 1 sense switch F + SSG 1 sense switch G + EQU 1 equal compare indicator + UNEQ 1 unequal compare indicator + HIGH 1 high compare indicator + LOW 1 low compare indicator + OVF 1 overflow indicator + IOCHK 1 I/O check switch + PRCHK 1 process check switch + OLDIS 1 IS prior to last branch + BREAK 17 breakpoint address (1000000 to disable) + WRU 8 interrupt character + +9.2 1402 Card Reader/Punch (CDR, CDP, STKR) + +The IBM 1402 card/reader punch is simulated as three independent devices: +the card reader (CDR), the card punch (CDP), and the reader and punch +stackers (STKR). STRK units 0, 1, 2, and 4 correspond to the reader +normal stacker, reader stacker 1, shared stacker 2/8, and punch stacker +4, respectively. + +The card reader reads data from disk files, while the punch and stackers +write data to disk files. Cards are simulated as ASCII text lines with +terminating newlines; column binary is not supported. For each unit, +the POS register specifies the number of the next data item to be read or +written. Thus, by changing POS, the user can backspace or advance these +devices. + +The reader/punch registers are: + + device name size comments + + CDR LAST 1 last card indicator + ERR 1 error indicator + S1 1 stacker 1 select flag + S2 1 stacker 2 select flag + POS 31 position + TIME 24 delay window for stacker select + + CDP ERR 1 error indicator + S4 1 stacker 4 select flag + S8 1 stacker 8 select flag + + STKR POS0 31 position, normal reader stack + POS1 31 position, reader stacker 1 + POS2 31 position, shared stacker 2/8 + POS4 31 position, punch stacker 4 + +Error handling is as follows: + + device error processed as + + reader end of file if SSA set, set LAST indicator + on next Read, report error and stop + + reader,punch not attached report error and stop + OS I/O error print error message + if IOCHK set, report error and stop + otherwise, set ERR indicator + + stacker not attached ignored + OS I/O error print error message + if IOCHK set, report error and stop + +9.3 1403 Line Printer (LPT) + +The IBM 1403 line printer (LPT) writes its data, converted to ASCII, to +a disk file. The line printer supports three different print character +sets or "chains": + + SET LPT PCF full 64 character chain + SET LPT PCA 48 character business chain + SET LPT PCH 48 character FORTRAN chain + +In addition, the line printer can be programmed with a carriage control +tape. The LOAD command loads a new carriage control tape: + + LOAD load carriage control tape file + +The format of a carriage control tape consists of multiple lines. Each +line contains an optional repeat count, enclosed in parentheses, optionally +followed by a series of column numbers separated by commas. Column numbers +must be between 1 and 12; a column number of zero denotes top of form. The +following are all legal carriage control specifications: + + no punch + (5) 5 lines with no punches + 1,5,7,8 columns 1, 5, 7, 8 punched + (10)2 10 lines with column 2 punched + 1,0 column 1 punched; top of form + +The default form is 66 lines long, with column 1 and the top of form mark +on line 1, and the rest blank. + +The line printer registers are: + + name size comments + + LINES 8 number of newlines after next print + LFLAG 1 carriage control flag (1 = skip, 0 = space) + CCTP 8 carriage control tape pointer + CCTL 8 carriage control tape length (read only) + ERR 1 error indicator + POS 31 position + +Error handling is as follows: + + error processed as + + not attached report error and stop + + OS I/O error print error message + if IOCHK set, report error and stop + otherwise, set ERR indicator + +9.4 1407 Inquiry Terminal (INQ) + +The IBM 1407 inquiry terminal (INQ) is a half-duplex console. It polls +the controling keyboard of the simulator periodically for inquiry requests. +The inquiry terminal registers are: + + name size comments + + INQC 7 inquiry request character (initially ESC) + INR 1 inquiry request indicator + INC 1 inquiry cleared indicator + TIME 24 polling interval + +When the 1401 CPU requests input from the keyboard, the message [Enter] +is printed out, followed by a new line. The CPU hangs waiting for input +until either the return/enter key is pressed, or the inquiry request +character is typed in. The latter cancels the type-in and sets INC. + +The inquiry terminal has no errors. + +9.5 729 Magnetic Tape (MT) + +The magnetic tape controller supports six drives, numbered 1 through 6. +Magnetic tape options include the ability to make units write enabled or +or write locked. + + SET MTn LOCKED set unit n write locked + SET MTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The magnetic tape controller implements these registers: + + name size comments + + END 1 end of file indicator + ERR 1 error indicator + PAR 1 parity error indicator + POS1..6 31 position, drives 1..6 + +Error handling is as follows: + + error processed as + + not attached report error and stop + + end of file (read or space) end of physical tape + (write) ignored + + OS I/O error print error message + if IOCHK set, report error and stop + otherwise, set ERR indicator + +9.6 Symbolic Display and Input + +The IBM 1401 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -c display as single character + (BCD for CPU and MT, ASCII for others) + -s display as wordmark terminated BCD string + (CPU only) + -m display instruction mnemonics + (CPU only) + +In a CPU character display, word marks are denoted by ~. + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or " or -c or -s characters (BCD for CPU and MT, ASCII + for others) + alphabetic instruction mnemonic + numeric octal number + +Instruction input is free format, with spaces separating fields. There +are six instruction formats: 1, 2, 4, 5, 7, and 8 characters: + + 1 character opcode + 2 character opcode 'modifier + 4 character opcode address + 5 character opcode address 'modifier + 7 character opcode address address + 8 character opcode address address 'modifier + +Addresses are always decimal, except for special I/O addresses in the A +field, which may be specified as %xy, where x denotes the device and y +the unit number. + +For the CPU, string input may encompass multiple characters. A word mark +is denoted by ~ and must precede the character to be marked. All other +devices can only accept single character input, without word marks. + +9.7 Character Sets + +The IBM 1401 used a 6b character code called BCD (binary coded decimal). +Some of the characters have no equivalent in ASCII and require different +representations: + +BCD ASCII IBM 1401 print +code representation character chains + +00 space +01 1 +02 2 +03 3 +04 4 +05 5 +06 6 +07 7 +10 8 +11 9 +12 0 +13 # = in H chain +14 @ ' in H chain +15 : blank in A, H chains +16 > blank in A, H chains +17 ( tape mark blank in A, H chains +20 ^ alternate blank blank in A, H chains +21 / +22 S +23 T +24 U +25 V +26 W +27 X +30 Y +31 Z +32 ' record mark +33 , +34 % ( in H chain +35 = word mark blank in A, H chains +36 \ blank in A, H chains +37 + blank in A, H chains +40 - +41 J +42 K +43 L +44 M +45 N +46 O +47 P +50 Q +51 R +52 ! +53 $ +54 * +55 ] blank in A, H chains +56 ; blank in A, H chains +57 _ delta blank in A, H chains +60 & +61 A +62 B +63 C +64 D +65 E +66 F +67 G +70 H +71 I +72 ? +73 . +74 ) lozenge +75 [ blank in A, H chains +76 < blank in A, H chains +77 " group mark blank in A, H chains + +10. HP2100 Features + +The HP2100 simulator is configured as follows: + +device simulates +name(s) + +CPU 2116, 2100, or 21MX CPU with 32KW memory +DMA0, DMA1 dual channel DMA controller +PTR,PTP paper tape reader/punch +TTY console terminal +LPT LE8E line printer +CLK 12639C time base generator +DP 12557A cartridge disk controller with four drives +MT 12559 magnetic tape controller with one drives + +The HP2100 simulator implements several unique stop conditions: + + - decode of an undefined instruction, and STOP_INST is et + - reference to an undefined I/O device, and STOP_DEV is set + - more than INDMAX indirect references are detected during + memory reference address decoding + +The HP2100 loader supports standard absolute binary format. The DUMP +command is not implemented. + +10.1 CPU + +CPU options include choice of instruction set and memory size. + + SET CPU 2116 2116 instructions + SET CPU 2100 2100 instructions + SET CPU 21MX 21MX instructions + SET CPU 4K set memory size = 4K + SET CPU 8K set memory size = 8K + SET CPU 16K set memory size = 16K + SET CPU 24K set memory size = 24K + SET CPU 32K set memory size = 32K + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 32K. + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + P 15 program counter + A 16 A register + B 16 B register + X 16 X index register (21MX) + Y 16 Y index register (21MX) + S 16 switch/display register + E 1 extend flag + O 1 overflow flag + ION 1 interrupt enable flag + ION_DEFER 1 interrupt defer flag + IADDR 6 most recent interrupting device + MPCTL 1 memory protection enable (2100, 21MX) + MPFLG 1 memory protection flag (2100, 21MX) + MPFBF 1 memory protection flag buffer (2100, 21MX) + MFENCE 15 memory protection fence (2100, 21MX) + MADDR 16 memory protection error address (2100, 21MX) + STOP_INST 1 stop on undefined instruction + STOP_DEV 1 stop on undefined device + INDMAX 1 indirect address limit + OLDP 15 PC prior to last JMP, JSB, or interrupt + BREAK 16 breakpoint address (177777 to disable) + WRU 8 interrupt character + +10.2 DMA Controllers + +The HP2100 includes two DMA channel controllers (DMA0 and DMA1). Each +DMA channel has the following visible state: + + name size comments + + CMD 1 channel enabled + CTL 1 interrupt enabled + FLG 1 channel ready + FBF 1 channel ready buffer + CW1 1 command word 1 + CW2 1 command word 2 + CW3 1 command word 3 + +10.3 Variable Device Assignments + +On the HP2100, I/O device take their device numbers from the backplane +slot they are plugged into. Thus, device number assignments vary +considerably from system to system, and software package to software +package. The HP2100 simulator supports dynamic device reassignment +with the command: + + SET DEVNO + +This initiates a dialog that displays the current device number and +allows the user to assign a new one: + + Device number: old (cr) + +The new device number must be in the range 010..077 (octal) and must +not be currently assigned to another device. For devices with two +device numbers, only the lower numbered device number can be changed; +the higher is automatically set to the lower + 1. + +10.4 Programmed I/O Devices + +10.4.1 Paper Tape Reader (PTR) + +The paper tape reader (PTR) reads data from a disk file. The POS +register specifies the number of the next data item to be read. +Thus, by changing POS, the user can backspace or advance the reader. + +The paper tape reader supports the BOOT command. BOOT PTR copies the +absolute binary loader into memory and starts it running. + +The paper tape reader implements these registers: + + name size comments + + BUF 8 last data item processed + CTL 1 device/interrupt enable + FLG 1 device ready + FBF 1 device ready buffer + POS 31 position in the input file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + DEVNO 6 current device number (read only) + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + end of file 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +10.4.2 Paper Tape Punch (PTP) + +The paper tape punch (PTP) writes data to a disk file. The POS +register specifies the number of the next data item to be written. +Thus, by changing POS, the user can backspace or advance the punch. + +The paper tape punch implements these registers: + + name size comments + + BUF 8 last data item processed + CTL 1 device/interrupt enable + FLG 1 device ready + FBF 1 device ready buffer + POS 31 position in the output file + TIME 24 time from I/O initiation to interrupt + STOP_IOE 1 stop on I/O error + DEVNO 6 current device number (read only) + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +10.4.3 Console Terminal (TTY) + +The console terminal has three units: keyboard (unit 0), printer +(unit 1), and punch (unit 2). The keyboard reads from, and the +printer writes to, the controlling console port. The punch writes +to a disk file. The keyboard has one option, UC; when set, it +automatically converts lower case input to upper case. This is on +by default. + +The terminal implements these registers: + + name size comments + + BUF 8 last data item processed + MODE 16 mode + CTL 1 device/interrupt enable + FLG 1 device ready + FBF 1 device ready buffer + KPOS 31 number of characters input + KTIME 24 keyboard polling interval + TPOS 31 number of characters printed + TTIME 24 time from I/O initiation to interrupt + PPOS 31 position in the punch output file + STOP_IOE 1 punch stop on I/O error + DEVNO 6 current device number (read only) + +Error handling for the punch is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape + + OS I/O error x report error and stop + +10.4.4 12653A Line Printer (LPT) + +The line printer (LPT) writes data to a disk file. The POS register +specifies the number of the next data item to be written. Thus, +by changing POS, the user can backspace or advance the printer. + +The line printer implements these registers: + + name size comments + + BUF 8 last data item processed + CTL 1 device/interrupt enable + FLG 1 device ready + FBF 1 device ready buffer + POS 31 position in the output file + CTIME 24 time between characters + PTIME 24 time for a print operation + STOP_IOE 1 stop on I/O error + DEVNO 6 current device number (read only) + +Error handling is as follows: + + error STOP_IOE processed as + + not attached 1 report error and stop + 0 out of tape or paper + + OS I/O error x report error and stop + +10.4.5 12639C Time Base Generator (CLK) + +The time base generator (CLK) implements these registers: + + name size comments + + SEL 3 time base select + CTL 1 device/interrupt enable + FLG 1 device ready + FBF 1 device ready buffer + ERR 1 error flag + TIME0..TIME7 31 clock intervals, select = 0..7 + DEVNO 6 current device number (read only) + +10.5 12559C Cartridge Disk (DP) + +The 12559C cartridge disk has two separate devices, a data channel and +a device controller. The data channel includes a 128-word (one sector) +buffer for reads and writes. The device controller includes the four +disk drives. Disk drives can be REMOVEd or ADDed to the configuration. + +The data channel implements these registers: + + name size comments + + IBUF 16 input buffer + OBUF 16 output buffer + BPTR 7 sector buffer pointer + CMD 1 channel enabled + CTL 1 interrupt enabled + FLG 1 channel ready + FBF 1 channel ready buffer + DEVNO 6 current device number (read only) + +The device controller implements these registers: + + name size comments + + OBUF 16 output buffer + BUSY 3 busy (unit #, + 1, of active unit) + RARC 8 record address register cylinder + RARH 2 record address register head + RARS 4 record address register sector + CNT 5 check record count + CMD 1 controller enabled + CTL 1 interrupt enabled + FLG 1 controller ready + FBF 1 controller ready buffer + EOC 1 end of cylinder pending + CTIME 24 command delay time + STIME 24 seek delay time, per cylinder + XTIME 24 interword transfer time + STA0 16 drive 0 status + STA1 16 drive 1 status + STA2 16 drive 2 status + STA3 16 drive 3 status + DEVNO 6 current device number (read only) + +Error handling is as follows: + + error processed as + + not attached disk not ready + + end of file assume rest of disk is zero + + OS I/O error report error and stop + +10.6 12557 Magnetic Tape (MT) + +Magnetic tape options include the ability to make the unit write enabled +or write locked. + + SET MT LOCKED set unit write locked + SET MT ENABLED set unit write enabled + +The 12557 mag tape drive has two separate devices, a data channel and +a device controller. The data channel includes a maximum record sized +buffer for reads and writes. The device controller includes the tape +unit + +The data channel implements these registers: + + name size comments + + FLG 1 channel ready + BPTR 16 buffer pointer (reads and writes) + BMAX 16 buffer size (writes) + DEVNO 6 current device number (read only) + +The device controller implements these registers: + + name size comments + + FNC 8 current function + STA 9 tape status + BUF 8 buffer + BUSY 3 busy (unit #, + 1, of active unit) + CTL 1 interrupt enabled + FLG 1 controller ready + FBF 1 controller ready buffer + DTF 1 data transfer flop + FSVC 1 first service flop + POS 31 magtape position + CTIME 24 command delay time + XTIME 24 interword transfer delay time + STOP_IOE 1 stop on I/O error + DEVNO 6 current device number (read only) + +Error handling is as follows: + + error processed as + + not attached tape not ready + + end of file (read or space) end of physical tape + (write) ignored + + OS I/O error report error and stop + +10.7 Symbolic Display and Input + +The HP2100 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as two character string + -m display instruction mnemonics + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c two character sixbit string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses standard HP2100 assembler syntax. There are seven +instruction classes: memory reference, I/O, shift, alter skip, extended +shift, extended memory reference, extended two address reference. + +Memory reference instructions have the format + + memref {C/Z} address{,I} + +where I signifies indirect, C a current page reference, and Z a zero page +reference. The address is an octal number in the range 0 - 077777; if C or +Z is specified, the address is a page offset in the range 0 - 01777. Normally, +C is not needed; the simulator figures out from the address what mode to use. +However, when referencing memory outside the CPU (eg, disks), there is no +valid PC, and C must be used to specify current page addressing. + +IOT instructions have the format + + io device{,C} + +where C signifies that the device flag is to be cleared. The device is an +octal number in the range 0 - 77. + +Shift and alter/skip instructions have the format + + sub-op sub-op sub-op... + +The simulator checks that the combination of sub-opcodes is legal. + +Extended shift instructions have the format + + extshift count + +where count is an octal number in the range 1 - 020. + +Extended memory reference instructions have the format + + extmemref address{,I} + +where I signifies indirect addressing. The address is an octal number in +the range 0 - 077777. + +Extended two address instructions have the format + + ext2addr addr1{,I},addr2{,I} + +where I signifies indirect addressing. Both address 1 and address 2 are +octal numbers in the range 0 - 077777. + +11. Interdata 4 Features + +The Interdata 4 simulator is not fully debugged. Lack of documentation +and software makes checkout very difficult. The simulator is included in +the hopes that a fellow enthusiast may be able to provide software or +additional documentation to facilitate further debug. + +The Interdata 4 simulator is configured as follows: + +device simulates +name(s) + +CPU Interdata 4 CPU with 64KB memory +PT paper tape reader/punch +TT console terminal + +The Interdata 4 simulator implements one unique stop condition: + + - decode of an undefined instruction, and STOP_INST is set + +The LOAD and DUMP command are not implemented. + +11.1 CPU + +The only CPU options are memory size: + + SET CPU 8K set memory size = 8KB + SET CPU 16K set memory size = 16KB + SET CPU 24K set memory size = 24KB + SET CPU 32K set memory size = 32KB + SET CPU 48K set memory size = 48KB + SET CPU 64K set memory size = 64KB + +If memory size is being reduced, and the memory being truncated contains +non-zero data, the simulator asks for confirmation. Data in the truncated +portion of memory is lost. Initial memory size is 64KB. + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + PC 16 program counter + R0..RF 16 general registers + F0, F2..FE 32 floating point registers + PSW 16 processor status word + CC 4 condition codes, PSW<12:15> + SR 16 switch register + DR 16 display register low 16 bits + DR1 16 display register high 16 bits + DRMOD 1 display mode + DRPOS 2 display pointer position + SRPOS 1 switch pointer position + IRQ0..IRQ7 32 interrupt requests + IEN0..IEN7 32 interrupt enables + STOP_INST 1 stop on undefined instruction + OLDPC 16 PC prior to last branch or interrupt + BREAK 17 breakpoint address (377777 to disable) + WRU 8 interrupt character + +11.2 Paper Tape Reader/Punch (PT) + +The paper tape reader and punch (PT units 0 and 1) read data from or +write data to disk files. The RPOS and PPOS registers specify the +number of the next data item to be read and written, respectively. +Thus, by changing RPOS or PPOS, the user can backspace or advance +these devices. + +The paper tape reader supports the BOOT command. BOOT PTR copies the +so-called '50 loader' into memory and starts it running. + +The paper tape controller implements these registers: + + name size comments + + RBUF 8 reader buffer + RPOS 31 reader position in the input file + RTIME 24 time from reader start to interrupt + RSTOP_IOE 1 reader stop on I/O error + PBUF 8 punch buffer + PPOS 31 punch position in the output file + PTIME 24 time from punch start to interrupt + PSTOP_IOE 1 punch stop on I/O error + IREQ 1 paper tape interrupt request + IENB 1 paper tape interrupt enable + RUN 1 paper tape running + SLEW 1 paper tape reader slew mode + BUSY 1 paper tape busy + RW 1 paper tape read/write mode + +Error handling is as follows: + + type error STOP_IOE processed as + + in,out not attached 1 report error and stop + 0 out of tape + + in end of file 1 report error and stop + 0 out of tape + + in,out OS I/O error x report error and stop + +11.3 Teletype (TT) + +The teletype reads and writes to the controlling console port. The +keyboard has one option, UC; when set, it automatically converts lower +case input to upper case. This is on by default. + + name size comments + + KBUF 8 keyboard buffer + KPOS 31 number of characters output + KTIME 24 keyboard polling interval + TBUF 8 output buffer + TPOS 31 number of characters output + TTIME 24 time from output start to interrupt + IREQ 1 teletype interrupt request + IENB 1 teletype interrupt enable + HDPX 1 teletype half-duplex + BUSY 1 teletype busy + RW 1 teletype read/write mode + +11.4 Symbolic Display and Input + +The Interdata 4 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as two character string + -m display instruction mnemonics + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c two character sixbit string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses standard Interdata 4 assembler syntax. There are +four instruction classes: register, memory, register-register, register- +memory. + +Register instructions have the format + + rop regnum + +where the register number is a hex digit, optionally preceded by R, +between 0 and F. + +Memory instructions have the format + + mop address{(index)} + +where address is a hex number between 0 and 0xFFFF, and the index register +is a hex digit, optinally preceded by R, between 0 and F. + +Register-register instructions have the format + + rrop regnum,regnum + +where the register numbers are hex digits, optionally preceded by R, +between 1 and F. + +Register-memory instructions have the format + + rmop regnum,address{(index)} + +where the register number is a hex digit, optionally preceded by R, +between 0 and F, the address is a hex number between 0 and 0xFFFF, and +the index register is a hex digit, optionally preceded by R, between +1 and F. + +Appendix 1: File Representations + +All file representations are little endian. On big endian hosts, the +simulator automatically performs any required byte swapping. + +1. Hard Disks + +Hard disks are represented as unstructured binary files of 16b data items for +the 12b and 16b simulators, of 32b data items for the 18b and 32b simulators, +and 64b for the 36b simulators. + +2. Floppy Disks + +PDP-8 and PDP-11 floppy disks are represented as unstructured binary files +of 8b data items. They are nearly identical to the floppy disk images for +Doug Jones' PDP-8 simulator but lack the initial 256 byte header. A utility +for converting between the two formats is easily written. + +3. Magnetic Tapes + +Magnetic tapes are represented as unstructured binary files of 8b data +items. Each record consists of a 32b record header, in little endian +format, followed by n 8b bytes of data, followed by a repeat of the 32b +record header. The high order bit of the record header is used as an +error flag; the remaining 31b are the byte count of the record. If +the byte count is odd, the record is padded to even length; the pad +byte is undefined. + +Magnetic tapes are endian independent and consistent across simulator +families. A magtape produced by the Nova simulator will appear to +have its 16b words byte swapped if read by the PDP-11 simulator. + +4. Line Printers + +Line printer output is represented by an ASCII file of lines separated by +the newline character. Overprinting is represented by a line ending in +return rather than newline. + +Appendix 2: Sample Software + +1. PDP-8 + +1.1 ESI-X + +ESI-X is an interactive program for technical computation. It can +execute both immediate commands and stored programs (like BASIC). ESI-X +is provided as both source and as a binary loader format paper-tape +image. For more information see the documentation included with the +program. My thanks to Dave Waks, who wrote the program, and to Paul +Pierce and Tim Litt, who recovered the source from its archival medium. + +To load and run ESI-X: + + sim> load esix.bin + sim> run 5400 + _TYPE 2+2. + 2+2 = 4 + +1.2 FOCAL69 + +FOCAL69 is an interactive program for technical computations. It can +execute both immediate commands and stored programs (like BASIC). FOCAL69 +is provided as a binary loader format paper-tape image. To load and +run FOCAL69: + + sim> load focal69.bin + sim> run 200 + *TYPE 2+2 + = 4.000* + +1.3 PDP-8 OS/8 + +OS/8 is the PDP-8's mass storage-based operating system. It provides a +program development and execution environment for assembler, BASIC, and +FORTRAN programs. OS/8 is provided under license, as is, without fee, by +Digital Equipment Corporation, for non-commercial use only. Please read +the enclosed license agreement for full terms and conditions. This license +agreement must be reproduced with any copy of the OS/8 disk images. My +thanks to Doug Jones of the University of Iowa, who provided the disk +images, and to Digital Equipment Corporation, which provided the license. + +To boot and run OS/8: + + sim> att rx0 os8sys_rx.dsk + sim> att rx1 os8f4_rx.dsk + sim> boot rx0 + + .DA dd-mmm-yy + . + +Note that OS/8 only recognizes upper case characters. The first disk +(drive 0) is the system disk; it also includes BASIC. The second disk +(drive 1) includes FORTRAN. + +2. PDP-11 + +2.1 UNIX V5, V6, V7 + +UNIX was first developed on the PDP-7; its first widespread usage was on +the PDP-11. UNIX provides a program development and execution environment +for assembler and C programs. UNIX V5, V7, V7 for the PDP-11 is provided +under license, as is, without fee, by Santa Cruz Organization (SCO), for +non-commercial use only. Please read the enclosed license agreement for +full terms and conditions. This license must be reproduced with any copy +of the UNIX V5, V6, V7 disk images. My thanks to PUPS, the PDP-11 UNIX +Preservation Society of Australia, which provided the disk images, and to +SCO, which provided the license. + +2.1.1 UNIX V5 + +UNIX V5 is contained on a single RK05 disk image. To boot UNIX: + + sim> set cpu 18b + sim> att rk0 unix_v5_rk.dsk + sim> boot rk + @unix + login: root + #ls -l + +2.1.2 UNIX V6 + +UNIX V6 is contained on three RK05 disk images. To boot UNIX: + + sim> set cpu 18b + sim> att rk0 unix0_v6_rk.dsk + sim> att rk1 unix1_v6_rk.dsk + sim> att rk3 unix3_v6_rk.dsk + sim> boot rk0 + @unix + login: root + # ls -l + +2.1.3 UNIX V7 + +NOTE: The V7 disk images with V2.3c and later releases are new and +replace the prior versions, which were corrupt. + +UNIX V7 is contained on a single RL02 disk image. To boot UNIX: + + sim> set cpu 18b + sim> set rl0 RL02 + sim> att rl0 unix_v7_rl.dsk + sim> boot rl0 + @boot + New Boot, known devices are hp ht rk rl rp tm vt + : rl(0,0)rl2unix + # + +A smaller image is contained on a single RK05 disk image. To boot UNIX: + + sim> set cpu 18b + sim> att rk0 unix_v7_rk.dsk + sim> boot rk0 + @boot + New Boot, known devices are hp ht rk rl rp tm vt + : rk(0,0)rkunix + # STTY -LCASE + # + +2.2 RT-11 + +RT-11 is the PDP-11's single user operating system. It provides a program +development and execution environment for assembler, BASIC, and FORTRAN +programs. RT-11 is provided under license, as is, without fee, by Mentec +Corporation, for non-commercial use ONLY ON THIS SIMULATOR. Please read +the enclosed license agreement for full terms and conditions. This license +agreement must be reproduced with any copy of the RT-11 disk image. My +thanks to John Wilson, a private collector, who provided the disk image +for RT-11 V4; to Megan Gentry, of Digital Equipment Corporation, who +provided the disk image for RT-11 V5.3; and to Mentec Corporation, which +provided the license. + +2.2.1 RT-11 V4 + +RT-11 is contained in a single RK05 disk image. To boot and run RT-11: + + sim> att rk0 rtv4_rk.dsk + sim> boot rk0 + +For RL, RM, and RP series disks, RT-11 expects to find a manufacturer's bad +block table in the last track of the disk. Therefore, INITialization of a +new (all zero's) disk fails, because there is no valid bad block table. To +create a minimal bad block table, use the SET BADBLOCK command. + +2.2.2 RT-11 V5.3 + +RT-11 is contained in a single RL02 disk image. To boot and run RT-11: + + sim> set rl0 rl02 + sim> att rl0 rtv53_rl.dsk + sim> boot rl0 + +This is a full RT-11 distribution kit. It expects the user to copy the +distribution pack and generate a new system. This requires mounting +blank packs on RL1. When a blank pack is attached to the simulator, +a bad block table must be created with the SET BADBLOCK command. + +3. Nova RDOS + +RDOS is the Nova's real-time mass storage operating system. It provides a +program development and execution environment for assembler, BASIC, and +FORTRAN programs. RDOS is provided under license, as is, without fee, by +Data General Corporation, for non-commercial use only. Please read the +enclosed license agreement for full terms and conditions. This license +agreement must be reproduced with any copy of the RDOS disk image. My +thanks to Carl Friend, a private collector, who provided the disk image, +and to Data General Corporation, which provided the license. + +To boot and run RDOS: + + sim> att dp0 rdos_d31.dsk + sim> set tti dasher + sim> boot dp0 + FILENAME? (cr) + DATE (mm/dd/yy)? xx/yy/zz + TIME (hh:mm:ss)? hh:mm:ss + R + list/e + +4. PDP-1 LISP + +PDP-1 LISP is an interactive interpreter for the Lisp language. It can +execute both interactive commands and stored programs. The startup +instructions for LISP are complicated; see the documentation included +with the program for details. My thanks to Peter Deutsch, who wrote the +program, to Gordon Greene, who typed it in from a printed listing, and +to Paul McJones, who helped with the final debug process. + +5. PDP-7 SIM8 + +PDP-7 SIM8 is a PDP-8 simulator for the PDP-7. It implements an 8K +PDP-8/I with keyboard, teleprinter, reader, punch, and line printer. +It provides an interactive console environment for control and debug +of the simulated PDP-8. For more information see the documentation +included with the program. My thanks to Dave Waks, who wrote the +program, and to Paul Pierce and Tim Litt, who recovered the source +from its archival medium. + +To load and run SIM8: + + sim> load sim8.rim + sim> run + AC/ 0000 + +6. 1401 Single Card "Koans" + +One of the art forms for the IBM 1401 was packing useful programs into a +single punched card. Three samples are included: + +i1401_ctolp.cd prints a card deck on the line printer +i1401_ctopu.cd copies a card deck to the card punch +i1401_hello.cd prints "HELLO WORLD" on the line printer and stops + +To use the reproduction cards, simply insert them at the beginning of a +text file, terminated by newline. Attach the modified file to the card +reader, attach a blank file to the output device, and boot the card reader. + +7. HP2116 16K BASIC + +HP BASIC is a paper-tape centric implementation of BASIC for a 16KW +HP2116. Device numbers correspond to the default simulator settings: + + PTR = 10 + TTY = 11 + PTP = 12 + +The program is a complete but early BASIC and has one unsual requirement: +all programs must include a valid END statement to run correctly. My +thanks to Jeff Moffatt for providing the program. + +To load and run BASIC: + + sim> load basic1.abs + sim> run 100 + READY + 10 PRINT SQR(2) + 20 END + RUN + 1.41421 + +Appendix 3: Debug Status + +The debug status of each simulated CPU and device is as follows: + + system PDP-8 PDP-11 Nova PDP-1 18b PDP +device +CPU y y y y y +FPU - y - - - +CIS - h - - - +console y y y y y +paper tape y y y h y +card reader - - - - - +line printer y y y h y +clock y y y - h +hard disk y y y - h +fixed disk h - h - h +floppy disk y y y - - +mag tape h y y - h + + system 1401 2100 Id4 +device +CPU h y h +FPU - - h +CIS - - - +console h y h +paper tape - h h +card reader h h - +line printer h - - +clock - h - +hard disk - h - +fixed disk - h - +floppy disk - - - +mag tape h h - + +legend: y = runs operating system or sample program + d = runs diagnostics + h = runs hand-generated test cases + n = untested + - = not applicable + +Revision History (since Rev 1.1) + +Rev 2.5, Jan, 01 + Removed Digital and Compaq from copyrights, as + authorized by Compaq Sr VP Bill Strecker + Revised save/restore format for 64b simulators + Added examine to file + Added unsigned integer data types to sim_defs + Added Nova 3 and Nova 4 instructions to Nova CPU + Added HP2100 + Added Interdata 4 + Fixed indirect loop through autoinc/dec in Nova CPU + Fixed MDV enabled test in Nova CPU + +Rev 2.4, Jan, 99 + Placed all sources under X11-like open source license + Added DUMP command, revised sim_load interface + Added SHOW MODIFIERS command + Revised magtape format to include record error flag + Fixed 64b problems in SCP + Fixed big endian problem in PDP-11 bad block routine + Fixed interrupt on error bug in PDP-11 RP/RM disks + Fixed ROL/ROR inversion in PDP-11 symbolic routines + +Rev 2.3d, Sep, 98 + Added BeOS support + Added radix commands and switches + Added PDP-11 CIS support + Added RT11 V5.3 to distribution kits + Fixed "shift 32" bugs in SCP, PDP-11 floating point + Fixed bug in PDP-11 paper tape reader + Fixed bug in ^D handling + +Rev 2.3c, May, 98 + Fixed bug in PDP-11 DIV overflow check + Fixed bugs in PDP-11 magtape bootstrap + Fixed bug in PDP-11 magtape unit select + Replaced UNIX V7 disk images + +Rev 2.3b, May, 98 + Added switch recognition to all simulator commands + Added RIM loader to PDP-8 paper tape reader and loader + Added second block bootstrap to PDP-11 magtape + Fixed bug in PDP-8 RF bootstrap + Fixed bug in PDP-11 symbolic display + Fixed bugs in PDP-11 floating point (LDEXP, STEXP, + MODf, STCfi, overflow handling) + +Rev 2.3a, Nov, 97 + Added search capability + Added bad block table command to PDP-11 disks + Added bootstrap to PDP-11 magtape + Added additional Nova moving head disks + Added RT-11 sample software + Fixed bugs in PDP-11 RM/RP disks + Fixed bugs in Nova moving head disks + Fixed endian dependence in 18b PDP RIM loader + +Rev 2.3, Mar, 97 + Changed UNIX terminal I/O to TERMIOS + Changed magtape format to double ended + Changed PDP-8 current page mnemonic from T to C + Added endian independent I/O routines + Added precise integer data types + Fixed bug in sim_poll_kbd + Fixed bug in PDP-8 binary loader + Fixed bugs in TM11 magtape + Fixed bug in RX11 bootstrap + Fixed bug in 18b PDP ADD + Fixed bug in 18b PDP paper tape reader + Fixed bug in PDP-4 console + Fixed bug in PDP-4,7 line printer + Added PDP-11 RP + Added PDP-1 + +Rev 2.2d, Dec, 96 + Added ADD/REMOVE commands + Added unit enable/disable support to device simulators + Added features for IBM 1401 project + Added switch recognition for symbolic input + Fixed bug in variable length IEXAMINE + Fixed LCD bug in RX8E + Initial changes for Win32 + Added IBM 1401 + +Rev 2.2b, Apr, 96 + Added PDP-11 dynamic memory size support + +Rev 2.2a, Feb, 96 + New endian independent magtape format + +Rev 2.2 Jan, 96 + Added register buffers for save/restore + Added 18b PDP's + Guaranteed TTI, CLK times are non-zero + Fixed breakpoint/RUN interaction bug + Fixed magnetic tape backspace to EOF bug + Fixed ISZ/DCA inversion in PDP-8 symbol table + Fixed sixbit conversion in PDP-8 examine/deposit + Fixed origin increment bug in PDP-11 binary loader + Fixed GCC longjmp optimization bug in PDP-11 CPU + Fixed unit number calculation bug in SCP and in + Nova, PDP-11, 18b PDP moving head disks + +Rev 2.1 Dec, 95 + Fixed PTR bug (setting done on EOF) in PDP-8, Nova + Fixed RX bug (setting error on INIT if drive 1 is + not attached) in PDP-8, PDP-11 + Fixed RF treatment of photocell flag in PDP-8 + Fixed autosize bug (always chose smallest disk if new + file) in PDP-11, Nova + Fixed not attached bug (reported as not attachable) in + most mass storage devices + Fixed Nova boot ROMs + Fixed bug in RESTORE (didn't requeue if delay = 0) + Fixed bug in RESTORE (clobbered device position) + Declared static constant arrays as static const + Added PDP-8, Nova magnetic tape simulators + Added Dasher mode to Nova terminal simulator + Added LINUX support + +Rev 2.0 May, 95 + Added symbolic assembly/disassembly + +Acknowledgements + +SIMH would not have been possible without help from around the world. I +would like to acknowledge the help of the following people, all of whom +donated their time and talent to this "computer archaeology" project: + +Bill Ackerman PDP-1 consulting +Winfried Bergmann Linux port testing +Phil Budne Solaris port testing +Max Burnet PDP information, documentation, and software +James Carpenter LINUX port testing +Chip Charlot PDP-11 RT-11, RSTS/E, RSX-11M legal permissions +Dave Conroy HP 21xx documentation +L Peter Deutsch PDP-1 LISP software +Ethan Dicks PDP-11 2.9 BSD debugging +Carl Friend Nova and Interdata documentation, and RDOS software +Megan Gentry PDP-11 integer debugging, make file +Dick Greeley PDP-8 OS/8 and PDP-10 TOPS-10/20 legal permissions +Gordon Greene PDP-1 LISP machine readable source +Lynne Grettum PDP-11 RT-11, RSTS/E, RSX-11M legal permissions +Franc Grootjen PDP-11 2.11 BSD debugging +Ken Harrenstein PDP-10 simulator +Bill Haygood PDP-8 information, simulator, and software +Jim Jaeger IBM 1401 information +Doug Jones PDP-8 information, simulator, and software +Don Lewine Nova documentation and legal permissions +Scott McGregor PDP-11 UNIX legal permissions +Jeff Moffatt HP 2100 information, documentation, and software +Alec Muffett Solaris port testing +Dutch Owen Nova moving head disk debugging, Altair simulator +Paul Pierce IBM 1401 diagnostics, media recovery +Craig St Clair PDP documentation +Richard Schedler Public repository maintenance +Stephen Schultz PDP-11 2.11 BSD debugging +Olaf Seibert NetBSD port testing +Brian & Barry Silverman PDP-1 simulator and software +Tim Shoppa Nova documentation and RDOS software +Michael Somos PDP-1 debugging +Hans-Michael Stahl OS/2 port testing, TERMIOS implementation +Larry Stewart Initial suggestion for the project +Bill Strecker Permission to revert copyrights +Chris Suddick PDP-11 floating point debugging +Ben Thomas VMS character-by-character I/O routines +Warren Toomey PDP-11 UNIX software +Deb Toivonen PDP documentation +Leendert Van Doorn PDP-11 UNIX V6 debugging, TERMIOS implementation +David Waks PDP-8 ESI-X and PDP-7 SIM8 software +Tom West Nova documentation +John Wilson PDP-11 simulator and software + +In addition, the following companies have graciously licensed their +software at no cost for hobbyist use: + +Data General Corporation +Digital Equipment Corporation +Compaq Computer Corporation +Mentec Corporation +The Santa Cruz Operation