From 4d6dfa4bdbb19d7b5ddf5c3e1bbe2f74a3c007e4 Mon Sep 17 00:00:00 2001 From: Bob Supnik Date: Tue, 6 Nov 2001 20:50:00 -0800 Subject: [PATCH] simh v2.6 --- 0readme26.txt | 35 + altair.txt | 211 +++ altair_cpu.c | 1179 ++++++++++++++++ altair_defs.h | 24 + altair_dsk.c | 345 +++++ altair_sio.c | 238 ++++ altair_sys.c | 288 ++++ eclipse.txt | 24 + eclipse_cpu.c | 3421 +++++++++++++++++++++++++++++++++++++++++++++++ eclipse_tt.c | 382 ++++++ hp2100_cpu.c | 4 +- hp2100_defs.h | 2 +- hp2100_dp.c | 10 +- hp2100_lp.c | 2 +- hp2100_mt.c | 8 +- hp2100_stddev.c | 4 +- hp2100_sys.c | 7 +- i1401_cd.c | 11 +- i1401_cpu.c | 4 +- i1401_defs.h | 2 +- i1401_iq.c | 6 +- i1401_lp.c | 10 +- i1401_mt.c | 8 +- i1401_sys.c | 7 +- id4_cpu.c | 6 +- id4_defs.h | 2 +- id4_fp.c | 21 +- id4_stddev.c | 4 +- id4_sys.c | 7 +- mtcvtv23.c | 81 -- nova_clk.c | 33 +- nova_cpu.c | 17 +- nova_defs.h | 8 +- nova_dkp.c | 24 +- nova_dsk.c | 8 +- nova_lp.c | 2 +- nova_mta.c | 16 +- nova_plt.c | 9 +- nova_pt.c | 2 +- nova_sys.c | 7 +- nova_tt.c | 2 +- nova_tt1.c | 8 +- pdp10_cpu.c | 2063 ++++++++++++++++++++++++++++ pdp10_defs.h | 626 +++++++++ pdp10_fe.c | 162 +++ pdp10_ksio.c | 546 ++++++++ pdp10_lp20.c | 582 ++++++++ pdp10_mdfp.c | 681 ++++++++++ pdp10_pag.c | 792 +++++++++++ pdp10_pt.c | 279 ++++ pdp10_rp.c | 1093 +++++++++++++++ pdp10_sys.c | 784 +++++++++++ pdp10_tim.c | 178 +++ pdp10_tu.c | 1045 +++++++++++++++ pdp10_xtnd.c | 638 +++++++++ pdp11_cis.c | 11 +- pdp11_cpu.c | 92 +- pdp11_defs.h | 36 +- pdp11_fp.c | 4 +- pdp11_lp.c | 6 +- pdp11_rk.c | 29 +- pdp11_rl.c | 39 +- pdp11_rp.c | 169 +-- pdp11_rx.c | 41 +- pdp11_stddev.c | 23 +- pdp11_sys.c | 27 +- pdp11_tc.c | 1138 ++++++++++++++++ pdp11_tm.c | 24 +- pdp11_ts.c | 901 +++++++++++++ pdp15.c | 1 - pdp18b_cpu.c | 51 +- pdp18b_defs.h | 11 +- pdp18b_drm.c | 8 +- pdp18b_dt.c | 1072 +++++++++++++++ pdp18b_lp.c | 39 +- pdp18b_mt.c | 48 +- pdp18b_rf.c | 24 +- pdp18b_rp.c | 15 +- pdp18b_stddev.c | 188 ++- pdp18b_sys.c | 45 +- pdp1_cpu.c | 21 +- pdp1_defs.h | 6 +- pdp1_lp.c | 11 +- pdp1_stddev.c | 4 +- pdp1_sys.c | 5 +- pdp4.c | 1 - pdp7.c | 1 - pdp8_clk.c | 29 +- pdp8_cpu.c | 68 +- pdp8_defs.h | 22 +- pdp8_df.c | 278 ++++ pdp8_dt.c | 1150 ++++++++++++++++ pdp8_lp.c | 16 +- pdp8_mt.c | 39 +- pdp8_pt.c | 25 +- pdp8_rf.c | 97 +- pdp8_rk.c | 19 +- pdp8_rx.c | 65 +- pdp8_sys.c | 34 +- pdp8_tt.c | 21 +- pdp9.c | 1 - scp.c | 521 ++++++-- scp_tty.c | 120 +- sim_defs.h | 23 +- simh.doc | 206 +-- simh_doc.txt | 966 ++++++++++++- simh_swre.txt | 63 +- 107 files changed, 22745 insertions(+), 1067 deletions(-) create mode 100644 0readme26.txt create mode 100644 altair.txt create mode 100644 altair_cpu.c create mode 100644 altair_defs.h create mode 100644 altair_dsk.c create mode 100644 altair_sio.c create mode 100644 altair_sys.c create mode 100644 eclipse.txt create mode 100644 eclipse_cpu.c create mode 100644 eclipse_tt.c delete mode 100644 mtcvtv23.c create mode 100644 pdp10_cpu.c create mode 100644 pdp10_defs.h create mode 100644 pdp10_fe.c create mode 100644 pdp10_ksio.c create mode 100644 pdp10_lp20.c create mode 100644 pdp10_mdfp.c create mode 100644 pdp10_pag.c create mode 100644 pdp10_pt.c create mode 100644 pdp10_rp.c create mode 100644 pdp10_sys.c create mode 100644 pdp10_tim.c create mode 100644 pdp10_tu.c create mode 100644 pdp10_xtnd.c create mode 100644 pdp11_tc.c create mode 100644 pdp11_ts.c delete mode 100644 pdp15.c create mode 100644 pdp18b_dt.c delete mode 100644 pdp4.c delete mode 100644 pdp7.c create mode 100644 pdp8_df.c create mode 100644 pdp8_dt.c delete mode 100644 pdp9.c diff --git a/0readme26.txt b/0readme26.txt new file mode 100644 index 00000000..cd0579e8 --- /dev/null +++ b/0readme26.txt @@ -0,0 +1,35 @@ +Release notes for simh V2.6 + +1. Register arrays + +The simulator has supported register arrays for some time, but their contents +were always hidden, and register arrays had names like *BUF. Register arrays +can now be examined and modified, and the names have changed to normal form. +As a result, SAVE FILES FROM PRIOR RELEASES WILL NOT RESTORE PROPERLY, because +the previous array names won't be found. These errors will occur AFTER main +memory has been restored, so memory contents can be salvaged; but most device +state will be lost. + +2. USE_INT64 instead of _INT64 + +As a #define, _INT64 conflicts with some compiler implementations. Therefore, +the enable switch for 64b has been changed to USE_INT64, e.g., + + % cc -o pdp10 -DUSE_INT64 pdp10_*.c,scp*.c -lm + +3. int64 definition defaults to long long + +If 64b is specified, the default compiler declaration for int64 is 'long long', +with exceptions for Win32 (_int64) and Digital UNIX (long). + +4. Real-time clock calibration + +Many of the simulators now calibrate their real-time clocks to match wall +time. This allows simulated operating systems to track wall time. + +5. Calling sequence change + +The calling sequence for sim_load has been changed to include the file name. +This allows simulator loaders to use different formats depending on the +extension of the load file. + diff --git a/altair.txt b/altair.txt new file mode 100644 index 00000000..13f0c361 --- /dev/null +++ b/altair.txt @@ -0,0 +1,211 @@ +Altair 8800 Simulator +===================== + +1. Background. + + The MITS (Micro Instrumentation and Telemetry Systems) Altair 8800 +was announced on the January 1975 cover of Popular Electronics, which +boasted you could buy and build this powerful computer kit for only $397. +The kit consisted at that time of only the parts to build a case, power +supply, card cage (18 slots), CPU card, and memory card with 256 *bytes* of +memory. Still, thousands were ordered within the first few months after the +announcement, starting the personal computer revolution as we know it today. + + Many laugh at the small size of the that first kit, noting there +were no peripherals and the 256 byte memory size. But the computer was an +open system, and by 1977 MITS and many other small startups had added many +expansion cards to make the Altair quite a respectable little computer. The +"Altair Bus" that made this possible was soon called the S-100 Bus, later +adopted as an industry standard, and eventually became the IEE-696 Bus. + +2. Hardware + + We are simulating a fairly "loaded" Altair 8800 from about 1977, +with the following configuration: + + device simulates + name(s) + + CPU Altair 8800 with Intel 8080 CPU board, 62KB + of RAM, 2K of EPROM with start boot ROM. + 2SIO MITS 88-2SIO Dual Serial Interface Board. Port 1 + is assumed to be connected to a serial "glass + TTY" that is your terminal running the Simulator. + PTR Paper Tape Reader attached to port 2 of the + 2SIO board. + PTP Paper Tape Punch attached to port 2 of the + 2SIO board. This also doubles as a printer + port. + DSK MITS 88-DISK Floppy Disk controller with up + to eight drives. + +2.1 CPU + + We have 2 CPU options that were not present on the original +machine but are useful in the simulator. We also allow you to select +memory sizes, but be aware that some sample software requires the full +64K (i.e. CP/M) and the MITS Disk Basic and Altair DOS require about +a minimum of 24K. + + SET CPU 8080 Simulates the 8080 CPU (normal) + SET CPU Z80 Simulates the later Z80 CPU [At the present time + this is not fully implemented and is not to be + trusted with real Z80 software] + SET CPU ITRAP Causes the simulator to halt if an invalid 8080 + Opcode is detected. + SET CPU NOITRAP Does not stop on an invalid Opcode. This is + how the real 8080 works. + SET CPU 4K + SET CPU 8K + SET CPU 12K + SET CPU 16K + ...... + SET CPU 64K All these set various CPU memory configurations. + The 2K EPROM at the high end of memory is always + present and will always boot. + +The BOOT EPROM card starts at address 177400. Jumping to this address +will always boot drive 0 of the floppy controller. If no valid bootable +software is present there the machine crashes. This is historically +accurate behavior. + +The real 8080, on receiving a HLT (Halt) instruction, freezes the processor +and only an interrupt or CPU hardware reset will restore it. The simulator +is alot nicer, it will halt but send you back to the simulator command line. + +CPU Registers include the following: + + name size comments + + PC 16 The Program Counter + A 8 The accumulator + BC 16 The BC register pair. Register B is the high + 8 bits, C is the lower 8 bits + DE 16 The DE register pair. D is the top 8 bits, E is + the bottom. + HL 16 The HL register pair. H is top, L is bottom. + C 1 Carry flag. + Z 1 Zero Flag. + AC 1 Auxillary Carry flag. + P 1 Parity flag. + S 1 Sign flag. + SR 16 The front panel switches. + BREAK 16 Breakpoint address (377777 to disable). + WRU 8 The interrupt character. This starts as 005 + (ctrl-E) but some Altair software uses this + keystroke so best to change this to something + exotic such as 035 (which is Ctl-]). + + +2.2 The Serial I/O Card (2SIO) + + This simple programmed I/O device provides 2 serial ports to the +outside world, which could be hardware jumpered to support RS-232 plugs or a +TTY current loop interface. The standard I/O addresses assigned by MITS +was 20-21 (octal) for the first port, and 22-23 (octal) for the second. +We follow this standard in the Simulator. + + The simulator directs I/O to/from the first port to the screen. The +second port reads from an attachable "tape reader" file on input, and writes +to an attachable "punch file" on output. These files are considered a +simple stream of 8-bit bytes. + +2.3 The 88-DISK controller. + + The MITS 88-DISK is a simple programmed I/O interface to the MITS +8-inch floppy drive, which was basically a Pertec FD-400 with a power +supply and buffer board builtin. The controller supports neither interrupts +nor DMA, so floppy access required the sustained attention of the CPU. +The standard I/O addresses were 10, 11, and 12 (octal), and we follow the +standard. Details on controlling this hardware are in the altair_dsk.c +source file. + + +3. Sample Software + + Running an Altair in 1977 you would be running either MITS Disk +Extended BASIC, or the brand new and sexy CP/M Operating System from Digital +Research. Or possibly, you ordered Altair DOS back when it was promised in +1975, and are still waiting for it to be delivered in early 1977. + + We have samples of all three for you to check out. We can't go into +the details of how they work, but we'll give you a few hints. + + +3.1 CP/M Version 2.2 + + This version is my own port of the standard CP/M to the Altair. +There were some "official" versions but I don't have them. None were +endorsed or sold by MITS to my knowledge, however. + To boot CP/M: + + sim> attach dsk0 altcpm.dsk + sim> go 177400 + 62K CP/M VERSION 2.2 (ALTAIR 8800) + A>DIR + + CP/M feels like DOS, sort of. DIR will work. I have included all +the standard CP/M utilities, plus a few common public-domain ones. I also +include the sources to the customized BIOS and some other small programs. +TYPE will print an ASCII file. DUMP will dump a binary one. LS is a better +DIR than DIR. ASM will assemble .ASM files to Hex, LOAD will "load" them to +binary format (.COM). ED is a simple editor, #A command will bring the +source file to the buffer, T command will "type" lines, L will move lines, +E exits the editor. 20L20T will move down 20 lines, and type 20. Very +DECish. DDT is the debugger, SUBMIT is a batch-type command processor. +A sample batch file that will assemble and write out the bootable CP/M +image (on drive A) is "SYSGEN.SUB". To run it, type "SUBMIT SYSGEN". + + +3.2 MITS Disk Extended BASIC Version 4.1 + + This was the commonly used software for serious users of the Altair +computer. It is a powerful (but slow) BASIC with some extended commands to +allow it to access and manage the disk. There was no operating system it +ran under. To boot: + + sim> attach dsk0 mbasic.dsk + sim> go 177400 + + MEMORY SIZE? [return] + LINEPRINTER? C [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + NUMBER OF FILES? 3 [return] + NUMBER OF RANDOM FILES? 2 [return] + + 44297 BYTES FREE + ALTAIR BASIC REV. 4.1 + [DISK EXTENDED VERSION] + COPYRIGHT 1977 BY MITS INC. + OK + mount 0 + OK + files + + +3.3 Altair DOS Version 1.0 + + This was long promised but not delivered until it was almost +irrelevant. A short attempted tour will reveal it to be a dog, far inferior +to CP/M. To boot: + + sim> attach dsk0 altdos.dsk + sim> go 177400 + + MEMORY SIZE? 64 [return] + INTERRUPTS? N [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + HOW MANY DISK FILES? 3 [return] + HOW MANY RANDOM FILES? 2 [return] + + 056769 BYTES AVAILABLE + DOS MONITOR VER 1.0 + COPYRIGHT 1977 BY MITS INC + .mnt 0 + + .dir 0 + + + + + diff --git a/altair_cpu.c b/altair_cpu.c new file mode 100644 index 00000000..049dbe14 --- /dev/null +++ b/altair_cpu.c @@ -0,0 +1,1179 @@ +/* altair_cpu.c: MITS Altair Intel 8080 CPU simulator + + Copyright (c) 1997, + Charles E. Owen, Jr. + Commercial use prohibited + + The register state for the 8080 CPU is: + + A<0:7> Accumulator + BC<0:15> BC Register Pair + DE<0:15> DE Register Pair + HL<0:15> HL Register Pair + C carry flag + Z zero flag + S Sign bit + AC Aux carry + P Parity bit + PC<0:15> program counter + SP<0:15> Stack Pointer + + The 8080 is an 8-bit CPU, which uses 16-bit registers to address + up to 64KB of memory. + + The 78 basic instructions come in 1, 2, and 3-byte flavors. +*/ + +/* This routine is the instruction decode routine for the 8080. + 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 + I/O error in I/O simulator + Invalid OP code (If ITRAP is set on CPU) + + 2. Interrupts. + There are 8 possible levels of interrupt, and in effect they + do a hardware CALL instruction to one of 8 possible low + memory addresses. + + 3. Non-existent memory. On the 8080, reads to non-existent memory + return 0377, 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: + + altair_cpu.c add I/O service routines to dev_table + altair_sys.c add pointer to data structures in sim_devices +*/ + + +#include + +#include "altair_defs.h" + +#define ILL_ADR_FLAG 0200000 +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ +#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) +#define UNIT_V_CHIP (UNIT_V_UF+1) /* 8080 or Z80 */ +#define UNIT_CHIP (1 << UNIT_V_CHIP) +#define UNIT_V_MSIZE (UNIT_V_UF+2) /* Memory Size */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +unsigned char M[MAXMEMSIZE]; /* memory */ +int32 A = 0; /* accumulator */ +int32 BC = 0; /* BC register pair */ +int32 DE = 0; /* DE register pair */ +int32 HL = 0; /* HL register pair */ +int32 SP = 0; /* Stack pointer */ +int32 C = 0; /* carry flag */ +int32 Z = 0; /* Zero flag */ +int32 AC = 0; /* Aux carry */ +int32 S = 0; /* sign flag */ +int32 P = 0; /* parity flag */ +int32 saved_PC = 0; /* program counter */ +int32 SR = 0; /* switch register */ +int32 INTE = 0; /* Interrupt Enable */ +int32 int_req = 0; /* Interrupt request */ +int32 ibkpt_addr = ILL_ADR_FLAG | ADDRMASK; /* breakpoint addr */ +int32 chip = 0; /* 0 = 8080 chip, 1 = z80 chip */ + +int32 PCX; /* External view of PC */ + +extern int32 sim_int_char; + +/* function prototypes */ + +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); +void setarith(int32 reg); +void setlogical(int32 reg); +void setinc(int32 reg); +int32 getreg(int32 reg); +void putreg(int32 reg, int32 val); +int32 getpair(int32 reg); +int32 getpush(int32 reg); +void putpush(int32 reg, int32 data); +void putpair(int32 reg, int32 val); +void parity(int32 reg); +int32 cond(int32 con); + +extern int32 sio0s(int32 io, int32 data); +extern int32 sio0d(int32 io, int32 data); +extern int32 sio1s(int32 io, int32 data); +extern int32 sio1d(int32 io, int32 data); +extern int32 dsk10(int32 io, int32 data); +extern int32 dsk11(int32 io, int32 data); +extern int32 dsk12(int32 io, int32 data); +int32 nulldev(int32 io, int32 data); + +/* This is the I/O configuration table. There are 255 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ +struct idev { + int32 (*routine)(); +}; +struct idev dev_table[256] = { +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 000 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 004 */ +{&dsk10}, {&dsk11}, {&dsk12}, {&nulldev}, /* 010 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 014 */ +{&sio0s}, {&sio0d}, {&sio1s}, {&sio1d}, /* 020 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 024 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 030 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 034 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 040 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 044 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 050 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 054 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 060 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 064 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 070 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 074 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 100 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 104 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 110 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 114 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 120 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 124 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 130 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 134 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 140 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 144 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 150 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 154 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 160 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 164 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 170 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 174 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 200 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 204 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 210 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 214 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 220 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 224 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 230 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 234 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 240 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 244 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 250 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 254 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 260 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 264 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 270 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 274 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 300 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 304 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 310 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 314 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 320 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 324 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 330 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 334 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 340 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 344 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 350 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 354 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 360 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 364 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 370 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev} }; /* 374 */ + +/* Altair MITS standard BOOT EPROM, fits in upper 256K of memory */ + +int32 bootrom[256] = { + 0041, 0000, 0114, 0021, 0030, 0377, 0016, 0346, + 0032, 0167, 0023, 0043, 0015, 0302, 0010, 0377, + 0303, 0000, 0114, 0000, 0000, 0000, 0000, 0000, + 0363, 0061, 0142, 0115, 0257, 0323, 0010, 0076, /* 46000 */ + 0004, 0323, 0011, 0303, 0031, 0114, 0333, 0010, /* 46010 */ + 0346, 0002, 0302, 0016, 0114, 0076, 0002, 0323, /* 46020 */ + 0011, 0333, 0010, 0346, 0100, 0302, 0016, 0114, + 0021, 0000, 0000, 0006, 0000, 0333, 0010, 0346, + 0004, 0302, 0045, 0114, 0076, 0020, 0365, 0325, + 0305, 0325, 0021, 0206, 0200, 0041, 0324, 0114, + 0333, 0011, 0037, 0332, 0070, 0114, 0346, 0037, + 0270, 0302, 0070, 0114, 0333, 0010, 0267, 0372, + 0104, 0114, 0333, 0012, 0167, 0043, 0035, 0312, + 0132, 0114, 0035, 0333, 0012, 0167, 0043, 0302, + 0104, 0114, 0341, 0021, 0327, 0114, 0001, 0200, + 0000, 0032, 0167, 0276, 0302, 0301, 0114, 0200, + 0107, 0023, 0043, 0015, 0302, 0141, 0114, 0032, + 0376, 0377, 0302, 0170, 0114, 0023, 0032, 0270, + 0301, 0353, 0302, 0265, 0114, 0361, 0361, 0052, + 0325, 0114, 0325, 0021, 0000, 0377, 0315, 0316, + 0114, 0321, 0332, 0276, 0114, 0315, 0316, 0114, + 0322, 0256, 0114, 0004, 0004, 0170, 0376, 0040, + 0332, 0054, 0114, 0006, 0001, 0312, 0054, 0114, + 0333, 0010, 0346, 0002, 0302, 0240, 0114, 0076, + 0001, 0323, 0011, 0303, 0043, 0114, 0076, 0200, + 0323, 0010, 0303, 0000, 0000, 0321, 0361, 0075, + 0302, 0056, 0114, 0076, 0103, 0001, 0076, 0117, + 0001, 0076, 0115, 0107, 0076, 0200, 0323, 0010, + 0170, 0323, 0001, 0303, 0311, 0114, 0172, 0274, + 0300, 0173, 0275, 0311, 0204, 0000, 0114, 0044, + 0026, 0126, 0026, 0000, 0000, 0000, 0000, 0000 +}; + +/* 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 (PC, saved_PC, 16) }, + { ORDATA (A, A, 8) }, + { ORDATA (BC, BC, 16) }, + { ORDATA (DE, DE, 16) }, + { ORDATA (HL, HL, 16) }, + { ORDATA (SP, SP, 16) }, + { FLDATA (C, C, 16) }, + { FLDATA (Z, Z, 16) }, + { FLDATA (AC, AC, 16) }, + { FLDATA (S, S, 16) }, + { FLDATA (P, P, 16) }, + { FLDATA (INTE, INTE, 16) }, + { FLDATA (Z80, cpu_unit.flags, UNIT_V_CHIP), REG_HRO }, + { FLDATA (OPSTOP, cpu_unit.flags, UNIT_V_OPSTOP), REG_HRO }, + { ORDATA (SR, SR, 16) }, + { ORDATA (BREAK, ibkpt_addr, 17) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_CHIP, UNIT_CHIP, "Z80", "Z80", NULL }, + { UNIT_CHIP, 0, "8080", "8080", NULL }, + { UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL }, + { UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", 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, 65535, NULL, "64K", &cpu_set_size }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 16, 1, 8, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +int32 sim_instr (void) +{ + extern int32 sim_interval; + int32 PC, IR, OP, DAR, reason, hi, lo, carry, i; + + PC = saved_PC & ADDRMASK; /* load local PC */ + C = C & 0200000; + 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 > 0) { /* interrupt? */ + + /* 8080 interrupts not implemented yet. None were used, + on a standard Altair 8800. All I/O is programmed. */ + + } /* 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; + } + + if (PC == 0177400) { /* BOOT PROM address */ + for (i = 0; i < 250; i++) { + M[i + 0177400] = bootrom[i] & 0xFF; + } + } + + PCX = PC; + + IR = OP = M[PC]; /* fetch instruction */ + + PC = (PC + 1) & ADDRMASK; /* increment PC */ + + sim_interval--; + + if (OP == 0166) { /* HLT Instruction*/ + reason = STOP_HALT; + PC--; + continue; + } + + /* Handle below all operations which refer to registers or + register pairs. After that, a large switch statement + takes care of all other opcodes */ + + if ((OP & 0xC0) == 0x40) { /* MOV */ + DAR = getreg(OP & 0x07); + putreg((OP >> 3) & 0x07, DAR); + continue; + } + if ((OP & 0xC7) == 0x06) { /* MVI */ + putreg((OP >> 3) & 0x07, M[PC]); + PC++; + continue; + } + if ((OP & 0xCF) == 0x01) { /* LXI */ + DAR = M[PC] & 0x00ff; + PC++; + DAR = DAR | (M[PC] <<8) & 0xFF00;; + putpair((OP >> 4) & 0x03, DAR); + PC++; + continue; + } + if ((OP & 0xEF) == 0x0A) { /* LDAX */ + DAR = getpair((OP >> 4) & 0x03); + putreg(7, M[DAR]); + continue; + } + if ((OP & 0xEF) == 0x02) { /* STAX */ + DAR = getpair((OP >> 4) & 0x03); + M[DAR] = getreg(7); + continue; + } + + if ((OP & 0xF8) == 0xB8) { /* CMP */ + DAR = A & 0xFF; + DAR -= getreg(OP & 0x07); + setarith(DAR); + continue; + } + if ((OP & 0xC7) == 0xC2) { /* JMP */ + if (cond((OP >> 3) & 0x07) == 1) { + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + PC = (hi << 8) + lo; + } else { + PC += 2; + } + continue; + } + if ((OP & 0xC7) == 0xC4) { /* CALL */ + if (cond((OP >> 3) & 0x07) == 1) { + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + SP--; + M[SP] = (PC >> 8) & 0xff; + SP--; + M[SP] = PC & 0xff; + PC = (hi << 8) + lo; + } else { + PC += 2; + } + continue; + } + if ((OP & 0xC7) == 0xC0) { /* RET */ + if (cond((OP >> 3) & 0x07) == 1) { + PC = M[SP]; + SP++; + PC |= (M[SP] << 8) & 0xff00; + SP++; + } + continue; + } + if ((OP & 0xC7) == 0xC7) { /* RST */ + SP--; + M[SP] = (PC >> 8) & 0xff; + SP--; + M[SP] = PC & 0xff; + PC = OP & 0x38; + continue; + } + + if ((OP & 0xCF) == 0xC5) { /* PUSH */ + DAR = getpush((OP >> 4) & 0x03); + SP--; + M[SP] = (DAR >> 8) & 0xff; + SP--; + M[SP] = DAR & 0xff; + continue; + } + if ((OP & 0xCF) == 0xC1) { /*POP */ + DAR = M[SP]; + SP++; + DAR |= M[SP] << 8; + SP++; + putpush((OP >> 4) & 0x03, DAR); + continue; + } + if ((OP & 0xF8) == 0x80) { /* ADD */ + A += getreg(OP & 0x07); + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xF8) == 0x88) { /* ADC */ + carry = 0; + if (C) carry = 1; + A += getreg(OP & 0x07); + A += carry; + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xF8) == 0x90) { /* SUB */ + A -= getreg(OP & 0x07); + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xF8) == 0x98) { /* SBB */ + carry = 0; + if (C) carry = 1; + A -= (getreg(OP & 0x07)) + carry ; + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xC7) == 0x04) { /* INR */ + DAR = getreg((OP >> 3) & 0x07); + DAR++; + setinc(DAR); + DAR = DAR & 0xFF; + putreg((OP >> 3) & 0x07, DAR); + continue; + } + if ((OP & 0xC7) == 0x05) { /* DCR */ + DAR = getreg((OP >> 3) & 0x07); + DAR--; + setinc(DAR); + DAR = DAR & 0xFF; + putreg((OP >> 3) & 0x07, DAR); + continue; + } + if ((OP & 0xCF) == 0x03) { /* INX */ + DAR = getpair((OP >> 4) & 0x03); + DAR++; + DAR = DAR & 0xFFFF; + putpair((OP >> 4) & 0x03, DAR); + continue; + } + if ((OP & 0xCF) == 0x0B) { /* DCX */ + DAR = getpair((OP >> 4) & 0x03); + DAR--; + DAR = DAR & 0xFFFF; + putpair((OP >> 4) & 0x03, DAR); + continue; + } + if ((OP & 0xCF) == 0x09) { /* DAD */ + HL += getpair((OP >> 4) & 0x03); + C = 0; + if (HL & 0x10000) + C = 0200000; + HL = HL & 0xFFFF; + continue; + } + if ((OP & 0xF8) == 0xA0) { /* ANA */ + A &= getreg(OP & 0x07); + C = 0; + setlogical(A); + A &= 0xFF; + continue; + } + if ((OP & 0xF8) == 0xA8) { /* XRA */ + A ^= getreg(OP & 0x07); + C = 0; + setlogical(A); + A &= 0xFF; + continue; + } + if ((OP & 0xF8) == 0xB0) { /* ORA */ + A |= getreg(OP & 0x07); + C = 0; + setlogical(A); + A &= 0xFF; + continue; + } + + + + /* The Big Instruction Decode Switch */ + + switch (IR) { + + /* Logical instructions */ + + case 0376: { /* CPI */ + DAR = A & 0xFF; + DAR -= M[PC]; + PC++; + setarith(DAR); + break; + } + case 0346: { /* ANI */ + A &= M[PC]; + PC++; + C = AC = 0; + setlogical(A); + A &= 0xFF; + break; + } + case 0356: { /* XRI */ + A ^= M[PC]; + PC++; + C = AC = 0; + setlogical(A); + A &= 0xFF; + break; + } + case 0366: { /* ORI */ + A |= M[PC]; + PC++; + C = AC = 0; + setlogical(A); + A &= 0xFF; + break; + } + + /* Jump instructions */ + + case 0303: { /* JMP */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + PC = (hi << 8) + lo; + break; + } + case 0351: { /* PCHL */ + PC = HL; + break; + } + case 0315: { /* CALL */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + SP--; + M[SP] = (PC >> 8) & 0xff; + SP--; + M[SP] = PC & 0xff; + PC = (hi << 8) + lo; + break; + } + case 0311: { /* RET */ + PC = M[SP]; + SP++; + PC |= (M[SP] << 8) & 0xff00; + SP++; + break; + } + + /* Data Transfer Group */ + + case 062: { /* STA */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + M[DAR] = A; + break; + } + case 072: { /* LDA */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + A = M[DAR]; + break; + } + case 042: { /* SHLD */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + M[DAR] = HL; + DAR++; + M[DAR] = (HL >>8) & 0x00ff; + break; + } + case 052: { /* LHLD */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + HL = M[DAR]; + DAR++; + HL = HL | (M[DAR] <<8); + break; + } + case 0353: { /* XCHG */ + DAR = HL; + HL = DE; + DE = DAR; + break; + } + + /* Arithmetic Group */ + + case 0306: { /* ADI */ + A += M[PC]; + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 0316: { /* ACI */ + carry = 0; + if (C) carry = 1; + A += M[PC]; + A += carry; + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 0326: { /* SUI */ + A -= M[PC]; + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 0336: { /* SBI */ + carry = 0; + if (C) carry = 1; + A -= (M[PC] + carry); + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 047: { /* DAA */ + DAR = A & 0x0F; + if (DAR > 9 || AC > 0) { + DAR += 6; + A &= 0xF0; + A |= DAR & 0x0F; + if (DAR & 0x10) + AC = 0200000; + else + AC = 0; + } + DAR = (A >> 4) & 0x0F; + if (DAR > 9 || AC > 0) { + DAR += 6; + if (AC) DAR++; + A &= 0x0F; + A |= (DAR << 4); + } + if ((DAR << 4) & 0x100) + C = 0200000; + else + C = 0; + if (A & 0x80) { + S = 0200000; + } else { + S = 0; + } + if ((A & 0xff) == 0) + Z = 0200000; + else + Z = 0; + parity(A); + A = A & 0xFF; + break; + } + case 07: { /* RLC */ + C = 0; + C = (A << 9) & 0200000; + A = (A << 1) & 0xFF; + if (C) + A |= 0x01; + break; + } + case 017: { /* RRC */ + C = 0; + if (A & 0x01 == 1) + C |= 0200000; + A = (A >> 1) & 0xFF; + if (C) + A |= 0x80; + break; + } + case 027: { /* RAL */ + DAR = C; + C = 0; + C = (A << 9) & 0200000; + A = (A << 1) & 0xFF; + if (DAR) + A |= 1; + else + A &= 0xFE; + break; + } + case 037: { /* RAR */ + DAR = C; + C = 0; + if (A & 0x01 == 1) + C |= 0200000; + A = (A >> 1) & 0xFF; + if (DAR) + A |= 0x80; + else + A &= 0x7F; + break; + } + case 057: { /* CMA */ + A = ~ A; + A &= 0xFF; + break; + } + case 077: { /* CMC */ + C = ~ C; + C &= 0200000; + break; + } + case 067: { /* STC */ + C = 0200000; + break; + } + + /* Stack, I/O & Machine Control Group */ + + case 0: { /* NOP */ + break; + } + case 0343: { /* XTHL */ + lo = M[SP]; + hi = M[SP + 1]; + M[SP] = HL & 0xFF; + M[SP + 1] = (HL >> 8) & 0xFF; + HL = (hi << 8) + lo; + break; + } + case 0371: { /* SPHL */ + SP = HL; + break; + } + case 0373: { /* EI */ + INTE = 0200000; + break; + } + case 0363: { /* DI */ + INTE = 0; + break; + } + case 0333: { /* IN */ + DAR = M[PC] & 0xFF; + PC++; + if (DAR == 0xFF) { + A = (SR >> 8) & 0xFF; + } else { + A = dev_table[DAR].routine(0, 0); + } + break; + } + case 0323: { /* OUT */ + DAR = M[PC] & 0xFF; + PC++; + dev_table[DAR].routine(1, A); + break; + } + + default: { + if (cpu_unit.flags & UNIT_OPSTOP) { + reason = STOP_OPCODE; + PC--; + } + break; + } + } +} + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* Test an 8080 flag condition and return 1 if true, 0 if false */ +int32 cond(int32 con) +{ + int32 r = 0; + + switch (con) { + case 0: + if (Z == 0) return (1); + break; + case 1: + if (Z != 0) return (1); + break; + case 2: + if (C == 0) return (1); + break; + case 3: + if (C != 0) return (1); + break; + case 4: + if (P == 0) return (1); + break; + case 5: + if (P != 0) return (1); + break; + case 6: + if (S == 0) return (1); + break; + case 7: + if (S != 0) return (1); + break; + default: + break; + } + return (0); +} + +/* Set the arry, ign, ero and

arity flags following + an arithmetic operation on 'reg'. +*/ + +void setarith(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x100) + C = 0200000; + else + C = 0; + if (reg & 0x80) { + bc++; + S = 0200000; + } else { + S = 0; + } + if ((reg & 0xff) == 0) + Z = 0200000; + else + Z = 0; + AC = 0; + if (cpu_unit.flags & UNIT_CHIP) { + P = 0; /* parity is zero for *all* arith ops on Z80 */ + } else { + parity(reg); + } +} + +/* Set the arry, ign, ero amd

arity flags following + a logical (bitwise) operation on 'reg'. +*/ + +void setlogical(int32 reg) +{ + C = 0; + if (reg & 0x80) { + S = 0200000; + } else { + S = 0; + } + if ((reg & 0xff) == 0) + Z = 0200000; + else + Z = 0; + AC = 0; + parity(reg); +} + +/* Set the Parity (P) flag based on parity of 'reg', i.e., number + of bits on even: P=0200000, else P=0 +*/ + +void parity(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x01) bc++; + if (reg & 0x02) bc++; + if (reg & 0x04) bc++; + if (reg & 0x08) bc++; + if (reg & 0x10) bc++; + if (reg & 0x20) bc++; + if (reg & 0x40) bc++; + if (reg & 0x80) bc++; + P = ~(bc << 16); + P &= 0200000; +} + +/* Set the ign, ero amd

arity flags following + an INR/DCR operation on 'reg'. +*/ + +void setinc(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x80) { + bc++; + S = 0200000; + } else { + S = 0; + } + if ((reg & 0xff) == 0) + Z = 0200000; + else + Z = 0; + if (cpu_unit.flags & UNIT_CHIP) { + P = 0; /* parity is zero for *all* arith ops on Z80 */ + } else { + parity(reg); + } +} + +/* Get an 8080 register and return it */ +int32 getreg(int32 reg) +{ + switch (reg) { + case 0: + return ((BC >>8) & 0x00ff); + case 1: + return (BC & 0x00FF); + case 2: + return ((DE >>8) & 0x00ff); + case 3: + return (DE & 0x00ff); + case 4: + return ((HL >>8) & 0x00ff); + case 5: + return (HL & 0x00ff); + case 6: + return (M[HL]); + case 7: + return (A); + default: + break; + } +} + +/* Put a value into an 8080 register from memory */ +void putreg(int32 reg, int32 val) +{ + switch (reg) { + case 0: + BC = BC & 0x00FF; + BC = BC | (val <<8); + break; + case 1: + BC = BC & 0xFF00; + BC = BC | val; + break; + case 2: + DE = DE & 0x00FF; + DE = DE | (val <<8); + break; + case 3: + DE = DE & 0xFF00; + DE = DE | val; + break; + case 4: + HL = HL & 0x00FF; + HL = HL | (val <<8); + break; + case 5: + HL = HL & 0xFF00; + HL = HL | val; + break; + case 6: + M[HL] = val & 0xff; + break; + case 7: + A = val & 0xff; + default: + break; + } +} + +/* Return the value of a selected register pair */ +int32 getpair(int32 reg) +{ + switch (reg) { + case 0: + return (BC); + case 1: + return (DE); + case 2: + return (HL); + case 3: + return (SP); + default: + break; + } +} + +/* Return the value of a selected register pair, in PUSH + format where 3 means A& flags, not SP */ +int32 getpush(int32 reg) +{ + int32 stat; + + switch (reg) { + case 0: + return (BC); + case 1: + return (DE); + case 2: + return (HL); + case 3: + stat = A << 8; + if (S) stat |= 0x80; + if (Z) stat |= 0x40; + if (AC) stat |= 0x10; + if (P) stat |= 0x04; + stat |= 0x02; + if (C) stat |= 0x01; + return (stat); + default: + break; + } +} + + +/* Place data into the indicated register pair, in PUSH + format where 3 means A& flags, not SP */ +void putpush(int32 reg, int32 data) +{ + switch (reg) { + case 0: + BC = data; + break; + case 1: + DE = data; + break; + case 2: + HL = data; + break; + case 3: + A = (data >> 8) & 0xff; + S = Z = AC = P = C = 0; + if (data & 0x80) S = 0200000; + if (data & 0x40) Z = 0200000; + if (data & 0x10) AC = 0200000; + if (data & 0x04) P = 0200000; + if (data & 0x01) C = 0200000; + break; + default: + break; + } +} + + +/* Put a value into an 8080 register pair */ +void putpair(int32 reg, int32 val) +{ + switch (reg) { + case 0: + BC = val; + break; + case 1: + DE = val; + break; + case 2: + HL = val; + break; + case 3: + SP = val; + break; + default: + break; + } +} + + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +C = 0; +Z = 0; +saved_PC = 0; +int_req = 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] & 0377; +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 & 0377; + 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; +} + +int32 nulldev(int32 flag, int32 data) +{ + if (flag == 0) + return (0377); +} + diff --git a/altair_defs.h b/altair_defs.h new file mode 100644 index 00000000..548249f0 --- /dev/null +++ b/altair_defs.h @@ -0,0 +1,24 @@ +/* altair_defs.h: MITS Altair simulator definitions + + Copyright (c) 1997, + Charles E Owen + Commercial use prohibited + +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Memory */ + +#define MAXMEMSIZE 65536 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (x < MEMSIZE) + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_OPCODE 4 + diff --git a/altair_dsk.c b/altair_dsk.c new file mode 100644 index 00000000..dd1acbc1 --- /dev/null +++ b/altair_dsk.c @@ -0,0 +1,345 @@ +/* altair_dsk.c: MITS Altair 88-DISK Simulator + + The 88_DISK is a 8-inch floppy controller which can control up + to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives. + Each diskette has physically 77 tracks of 32 137-byte sectors + each. + + The controller is interfaced to the CPU by use of 3 I/O addreses, + standardly, these are device numbers 10, 11, and 12 (octal). + + Address Mode Function + ------- ---- -------- + + 10 Out Selects and enables Controller and Drive + 10 In Indicates status of Drive and Controller + 11 Out Controls Disk Function + 11 In Indicates current sector position of disk + 12 Out Write data + 12 In Read data + + Drive Select Out (Device 10 OUT): + + +---+---+---+---+---+---+---+---+ + | C | X | X | X | Device | + +---+---+---+---+---+---+---+---+ + + C = If this bit is 1, the disk controller selected by 'device' is + cleared. If the bit is zero, 'device' is selected as the + device being controlled by subsequent I/O operations. + X = not used + Device = value zero thru 15, selects drive to be controlled. + + Drive Status In (Device 10 IN): + + +---+---+---+---+---+---+---+---+ + | R | Z | I | X | X | H | M | W | + +---+---+---+---+---+---+---+---+ + + W - When 0, write circuit ready to write another byte. + M - When 0, head movement is allowed + H - When 0, indicates head is loaded for read/write + X - not used (will be 0) + I - When 0, indicates interrupts enabled (not used this simulator) + Z - When 0, indicates head is on track 0 + R - When 0, indicates that read circuit has new byte to read + + Drive Control (Device 11 OUT): + + +---+---+---+---+---+---+---+---+ + | W | C | D | E | U | H | O | I | + +---+---+---+---+---+---+---+---+ + + I - When 1, steps head IN one track + O - When 1, steps head OUT out track + H - When 1, loads head to drive surface + U - When 1, unloads head + E - Enables interrupts (ignored this simulator) + D - Disables interrupts (ignored this simulator) + C - When 1 lowers head current (ignored this simulator) + W - When 1, starts Write Enable sequence: W bit on device 10 + (see above) will go 1 and data will be read from port 12 + until 137 bytes have been read by the controller from + that port. The W bit will go off then, and the sector data + will be written to disk. Before you do this, you must have + stepped the track to the desired number, and waited until + the right sector number is presented on device 11 IN, then + set this bit. + + Sector Position (Device 11 IN): + + As the sectors pass by the read head, they are counted and the + number of the current one is available in this register. + + +---+---+---+---+---+---+---+---+ + | X | X | Sector Number | T | + +---+---+---+---+---+---+---+---+ + + X = Not used + Sector number = binary of the sector number currently under the + head, 0-31. + T = Sector True, is a 1 when the sector is positioned to read or + write. + +*/ + +#include + +#include "altair_defs.h" + +#define UNIT_V_ENABLE (UNIT_V_UF + 0) /* Write Enable */ +#define UNIT_ENABLE (1 << UNIT_V_ENABLE) + +#define DSK_SECTSIZE 137 +#define DSK_SECT 32 +#define DSK_TRACSIZE 4384 +#define DSK_SURF 1 +#define DSK_CYL 77 +#define DSK_SIZE (DSK_SECT * DSK_SURF * DSK_CYL * DSK_SECTSIZE) + +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_reset (DEVICE *dptr); +void writebuf(); + +extern int32 sim_activate (UNIT *uptr, int32 interval); +extern int32 sim_cancel (UNIT *uptr); +extern int32 PCX; + +/* Global data on status */ + +int32 cur_disk = 8; /* Currently selected drive */ +int32 cur_track[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; +int32 cur_sect[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; +int32 cur_byte[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; +int32 cur_flags[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + +char dskbuf[137]; /* Data Buffer */ +int32 dirty = 0; /* 1 when buffer has unwritten data in it */ +UNIT *dptr; /* fileref to write dirty buffer to */ + +int32 dsk_rwait = 100; /* rotate latency */ + + +/* 88DSK Standard I/O Data Structures */ + +UNIT dsk_unit[] = { + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) } + }; + +REG dsk_reg[] = { + { ORDATA (DISK, cur_disk, 4) }, + { NULL } }; + +DEVICE dsk_dev = { + "DSK", dsk_unit, dsk_reg, NULL, + 8, 10, 31, 1, 8, 8, + NULL, NULL, &dsk_reset, + NULL, NULL, NULL }; + +/* Service routines to handle simlulator functions */ + +/* service routine - actually gets char & places in buffer */ + +t_stat dsk_svc (UNIT *uptr) +{ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat dsk_reset (DEVICE *dptr) +{ +cur_disk = 0; +return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +/* Disk Controller Status/Select */ + +/* IMPORTANT: The status flags read by port 8 IN instruction are + INVERTED, that is, 0 is true and 1 is false. To handle this, the + simulator keeps it's own status flags as 0=false, 1=true; and + returns the COMPLEMENT of the status flags when read. This makes + setting/testing of the flag bits more logical, yet meets the + simulation requirement that they are reversed in hardware. +*/ + +int32 dsk10(int32 io, int32 data) +{ + + if (io == 0) { /* IN: return flags */ + return ((~cur_flags[cur_disk]) & 0xFF); /* Return the COMPLEMENT! */ + } + + /* OUT: Controller set/reset/enable/disable */ + + if (dirty == 1) + writebuf(); + + /*printf("\n[%o] OUT 10: %x", PCX, data);*/ + cur_disk = data & 0x0F; + if (data & 0x80) { + cur_flags[cur_disk] = 0; /* Disable drive */ + cur_sect[cur_disk = 0377]; + cur_byte[cur_disk = 0377]; + return (0); + } + cur_flags[cur_disk] = 0x1A; /* Enable: head move true */ + cur_sect[cur_disk] = 0377; /* reset internal counters */ + cur_byte[cur_disk] = 0377; + if (cur_track[cur_disk] == 0) + cur_flags[cur_disk] |= 0x40; /* track 0 if there */ + return (0); +} + +/* Disk Drive Status/Functions */ + +int32 dsk11(int32 io, int32 data) +{ + int32 stat; + + if (io == 0) { /* Read sector position */ + /*printf("\n[%o] IN 11", PCX);*/ + if (dirty == 1) + writebuf(); + if (cur_flags[cur_disk] & 0x04) { /* head loaded? */ + cur_sect[cur_disk]++; + if (cur_sect[cur_disk] > 31) + cur_sect[cur_disk] = 0; + cur_byte[cur_disk] = 0377; + stat = cur_sect[cur_disk] << 1; + stat &= 0x3E; /* return 'sector true' bit = 0 (true) */ + stat |= 0xC0; /* set on 'unused' bits */ + return (stat); + } else { + return (0); /* head not loaded - return 0 */ + } + } + + /* Drive functions */ + + if (cur_disk > 7) + return (0); /* no drive selected - can do nothin */ + + /*printf("\n[%o] OUT 11: %x", PCX, data);*/ + if (data & 0x01) { /* Step head in */ + cur_track[cur_disk]++; + if (cur_track[cur_disk] > 76 ) + cur_track[cur_disk] = 76; + if (dirty == 1) + writebuf(); + cur_sect[cur_disk] = 0377; + cur_byte[cur_disk] = 0377; + } + + if (data & 0x02) { /* Step head out */ + cur_track[cur_disk]--; + if (cur_track[cur_disk] < 0) { + cur_track[cur_disk] = 0; + cur_flags[cur_disk] |= 0x40; /* track 0 if there */ + } + if (dirty == 1) + writebuf(); + cur_sect[cur_disk] = 0377; + cur_byte[cur_disk] = 0377; + } + + if (dirty == 1) + writebuf(); + + if (data & 0x04) { /* Head load */ + cur_flags[cur_disk] |= 0x04; /* turn on head loaded bit */ + cur_flags[cur_disk] |= 0x80; /* turn on 'read data available */ + } + + if (data & 0x08) { /* Head Unload */ + cur_flags[cur_disk] &= 0xFB; /* off on 'head loaded' */ + cur_flags[cur_disk] &= 0x7F; /* off on 'read data avail */ + cur_sect[cur_disk] = 0377; + cur_byte[cur_disk] = 0377; + } + + /* Interrupts & head current are ignored */ + + if (data & 0x80) { /* write sequence start */ + cur_byte[cur_disk] = 0; + cur_flags[cur_disk] |= 0x01; /* enter new write data on */ + } + +} + +/* Disk Data In/Out*/ + +int32 dsk12(int32 io, int32 data) +{ + static int32 rtn, i; + static long pos; + UNIT *uptr; + + uptr = dsk_dev.units + cur_disk; + if (io == 0) { + if ((i = cur_byte[cur_disk]) < 138) { /* just get from buffer */ + cur_byte[cur_disk]++; + return (dskbuf[i] & 0xFF); + } + /* physically read the sector */ + /*printf("\n[%o] IN 12 (READ) T%d S%d", PCX, cur_track[cur_disk], + cur_sect[cur_disk]);*/ + pos = DSK_TRACSIZE * cur_track[cur_disk]; + pos += DSK_SECTSIZE * cur_sect[cur_disk]; + rtn = fseek(uptr -> fileref, pos, 0); + rtn = fread(dskbuf, 137, 1, uptr -> fileref); + cur_byte[cur_disk] = 1; + return (dskbuf[0] & 0xFF); + } else { + if (cur_byte[cur_disk] > 136) { + i = cur_byte[cur_disk]; + dskbuf[i] = data & 0xFF; + writebuf(); + return (0); + } + i = cur_byte[cur_disk]; + dirty = 1; + dptr = uptr; + dskbuf[i] = data & 0xFF; + cur_byte[cur_disk]++; + return (0); + } +} + +void writebuf() +{ + long pos; + int32 rtn, i; + + i = cur_byte[cur_disk]; /* null-fill rest of sector if any */ + while (i < 138) { + dskbuf[i] = 0; + i++; + } + /*printf("\n[%o] OUT 12 (WRITE) T%d S%d", PCX, cur_track[cur_disk], + cur_sect[cur_disk]); i = getch(); */ + pos = DSK_TRACSIZE * cur_track[cur_disk]; /* calc file pos */ + pos += DSK_SECTSIZE * cur_sect[cur_disk]; + rtn = fseek(dptr -> fileref, pos, 0); + rtn = fwrite(dskbuf, 137, 1, dptr -> fileref); + cur_flags[cur_disk] &= 0xFE; /* ENWD off */ + cur_byte[cur_disk] = 0377; + dirty = 0; + return; +} diff --git a/altair_sio.c b/altair_sio.c new file mode 100644 index 00000000..c31c71c0 --- /dev/null +++ b/altair_sio.c @@ -0,0 +1,238 @@ +/* altair_sio: MITS Altair serial I/O card + + These functions support a simulated MITS 2SIO interface card. + The card had two physical I/O ports which could be connected + to any serial I/O device that would connect to a current loop, + RS232, or TTY interface. Available baud rates were jumper + selectable for each port from 110 to 9600. + + All I/O is via programmed I/O. Each each has a status port + and a data port. A write to the status port can select + some options for the device (0x03 will reset the port). + A read of the status port gets the port status: + + +---+---+---+---+---+---+---+---+ + | X X X X X X O I | + +---+---+---+---+---+---+---+---+ + + I - A 1 in this bit position means a character has been received + on the data port and is ready to be read. + O - A 1 in this bit means the port is ready to receive a character + on the data port and transmit it out over the serial line. + + A read to the data port gets the buffered character, a write + to the data port writes the character to the device. +*/ + +#include + +#include "altair_defs.h" + +#define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode */ +#define UNIT_ANSI (1 << UNIT_V_ANSI) + +t_stat sio_svc (UNIT *uptr); +t_stat sio_reset (DEVICE *dptr); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); + +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ + +extern t_stat sim_activate (UNIT *uptr, int32 interval); +extern t_stat sim_cancel (UNIT *uptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* 2SIO Standard I/O Data Structures */ + +UNIT sio_unit = { UDATA (&sio_svc, 0, 0), + KBD_POLL_WAIT }; + +REG sio_reg[] = { + { ORDATA (DATA, sio_unit.buf, 8) }, + { ORDATA (STAT, sio_unit.u3, 8) }, + { NULL } }; + +MTAB sio_mod[] = { + { UNIT_ANSI, 0, "TTY", "TTY", NULL }, + { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, + { 0 } }; + +DEVICE sio_dev = { + "2SIO", &sio_unit, sio_reg, sio_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &sio_reset, + NULL, NULL, NULL }; + +UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ + UNIT_ATTABLE, 0), + KBD_POLL_WAIT }; + +REG ptr_reg[] = { + { ORDATA (DATA, ptr_unit.buf, 8) }, + { ORDATA (STAT, ptr_unit.u3, 8) }, + { ORDATA (POS, ptr_unit.pos, 31) }, + { NULL } }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + NULL, NULL, NULL }; + +UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ + UNIT_ATTABLE, 0), + KBD_POLL_WAIT }; + +REG ptp_reg[] = { + { ORDATA (DATA, ptp_unit.buf, 8) }, + { ORDATA (STAT, ptp_unit.u3, 8) }, + { ORDATA (POS, ptp_unit.pos, 31) }, + { NULL } }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL }; + +/* Service routines to handle simulator functions */ + +/* service routine - actually gets char & places in buffer */ + +int32 sio_svc (UNIT *uptr) +{ + int32 temp; + + sim_activate (&sio_unit, sio_unit.wait); /* continue poll */ + if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) + return temp; /* no char or error? */ + sio_unit.buf = temp & 0377; /* Save char */ + sio_unit.u3 |= 0x01; /* Set status */ + + /* Do any special character handling here */ + + sio_unit.pos++; + return SCPE_OK; +} + + +int32 ptr_svc (UNIT *uptr) +{ + return SCPE_OK; +} + +int32 ptp_svc (UNIT *uptr) +{ + return SCPE_OK; +} + + +/* Reset routine */ + +int32 sio_reset (DEVICE *dptr) +{ + sio_unit.buf = 0; /* Data */ + sio_unit.u3 = 0x02; /* Status */ + sim_activate (&sio_unit, sio_unit.wait); /* activate unit */ + return SCPE_OK; +} + + +int32 ptr_reset (DEVICE *dptr) +{ + ptr_unit.buf = 0; + ptr_unit.u3 = 0x02; + sim_cancel (&ptr_unit); /* deactivate unit */ + return SCPE_OK; +} + +int32 ptp_reset (DEVICE *dptr) +{ + ptp_unit.buf = 0; + ptp_unit.u3 = 0x02; + sim_cancel (&ptp_unit); /* deactivate unit */ + return SCPE_OK; +} + + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +int32 sio0s(int32 io, int32 data) +{ + if (io == 0) { + return (sio_unit.u3); + } else { + if (data == 0x03) { /* reset port! */ + sio_unit.u3 = 0x02; + sio_unit.buf = 0; + sio_unit.pos = 0; + } + return (0); + } +} + +int32 sio0d(int32 io, int32 data) +{ + if (io == 0) { + sio_unit.u3 = sio_unit.u3 & 0xFE; + return (sio_unit.buf); + } else { + sim_putchar(data); + } +} + +/* Port 2 controls the PTR/PTP devices */ + +int32 sio1s(int32 io, int32 data) +{ + if (io == 0) { + if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return 0x02; + if (ptr_unit.u3 != 0) /* No more data? */ + return 0x02; + return (0x03); /* ready to read/write */ + } else { + if (data == 0x03) { + ptr_unit.u3 = 0; + ptr_unit.buf = 0; + ptr_unit.pos = 0; + ptp_unit.u3 = 0; + ptp_unit.buf = 0; + ptp_unit.pos = 0; + } + return (0); + } +} + +int32 sio1d(int32 io, int32 data) +{ + int32 temp; + UNIT *uptr; + + if (io == 0) { + if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return 0; + if (ptr_unit.u3 != 0) + return 0; + uptr = ptr_dev.units; + if ((temp = getc(uptr -> fileref)) == EOF) { /* end of file? */ + ptr_unit.u3 = 0x01; + return 0; + } + ptr_unit.pos++; + return (temp & 0xFF); + } else { + uptr = ptp_dev.units; + putc(data, uptr -> fileref); + ptp_unit.pos++; + } +} + diff --git a/altair_sys.c b/altair_sys.c new file mode 100644 index 00000000..cfba428b --- /dev/null +++ b/altair_sys.c @@ -0,0 +1,288 @@ +/* altair_sys.c: MITS Altair system interface + + (C) Copyright 1997 by Charles E. Owen + Commercial use prohibited +*/ + + +#include + +#include "altair_defs.h" + + +extern DEVICE cpu_dev; +extern DEVICE dsk_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern DEVICE sio_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE lpt_dev; +extern unsigned char M[]; +extern int32 saved_PC; +extern char *get_glyph (char *cptr, char *gbuf, char term); +extern unsigned int32 get_uint (char *cptr, int32 radix, unsigned int32 max, + int32 *status); + +/* 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[] = "Altair 8800"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { &cpu_dev, &sio_dev, &ptr_dev, + &ptp_dev, &dsk_dev, NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O Instruction", + "HALT instruction", + "Breakpoint", + "Invalid Opcode" }; + +static const char *opcode[] = { +"NOP", "LXI B", "STAX B", "INX B", /* 000-003 */ +"INR B", "DCR B", "MVI B", "RLC", /* 004-007 */ +"???", "DAD B", "LDAX B", "DCX B", /* 010-013 */ +"INR C", "DCR C", "MVI C", "RRC", /* 014-017 */ +"???", "LXI D", "STAX D", "INX D", /* 020-023 */ +"INR D", "DCR D", "MVI D", "RAL", /* 024-027 */ +"???", "DAD D", "LDAX D", "DCX D", /* 030-033 */ +"INR E", "DCR E", "MVI E", "RAR", /* 034-037 */ +"???", "LXI H", "SHLD", "INX H", /* 040-043 */ +"INR H", "DCR H", "MVI H", "DAA", /* 044-047 */ +"???", "DAD H", "LHLD", "DCX H", /* 050-053 */ +"INR L", "DCR L", "MVI L", "CMA", /* 054-057 */ +"???", "LXI SP", "STA", "INX SP", /* 060-063 */ +"INR M", "DCR M", "MVI M", "STC", /* 064-067 */ +"???", "DAD SP", "LDA", "DCX SP", /* 070-073 */ +"INR A", "DCR A", "MVI A", "CMC", /* 074-077 */ +"MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", /* 100-103 */ +"MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", /* 104-107 */ +"MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", /* 110-113 */ +"MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", /* 114-117 */ +"MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", /* 120-123 */ +"MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", /* 124-127 */ +"MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", /* 130-133 */ +"MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", /* 134-137 */ +"MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", /* 140-143 */ +"MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", /* 144-147 */ +"MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", /* 150-153 */ +"MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", /* 154-157 */ +"MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", /* 160-163 */ +"MOV M,H", "MOV M,L", "HLT", "MOV M,A", /* 164-167 */ +"MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", /* 170-173 */ +"MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", /* 174-177 */ +"ADD B", "ADD C", "ADD D", "ADD E", /* 200-203 */ +"ADD H", "ADD L", "ADD M", "ADD A", /* 204-207 */ +"ADC B", "ADC C", "ADC D", "ADC E", /* 210-213 */ +"ADC H", "ADC L", "ADC M", "ADC A", /* 214-217 */ +"SUB B", "SUB C", "SUB D", "SUB E", /* 220-223 */ +"SUB H", "SUB L", "SUB M", "SUB A", /* 224-227 */ +"SBB B", "SBB C", "SBB D", "SBB E", /* 230-233 */ +"SBB H", "SBB L", "SBB M", "SBB A", /* 234-237 */ +"ANA B", "ANA C", "ANA D", "ANA E", /* 240-243 */ +"ANA H", "ANA L", "ANA M", "ANA A", /* 244-247 */ +"XRA B", "XRA C", "XRA D", "XRA E", /* 250-253 */ +"XRA H", "XRA L", "XRA M", "XRA A", /* 254-257 */ +"ORA B", "ORA C", "ORA D", "ORA E", /* 260-263 */ +"ORA H", "ORA L", "ORA M", "ORA A", /* 264-267 */ +"CMP B", "CMP C", "CMP D", "CMP E", /* 270-273 */ +"CMP H", "CMP L", "CMP M", "CMP A", /* 274-277 */ +"RNZ", "POP B", "JNZ", "JMP", /* 300-303 */ +"CNZ", "PUSH B", "ADI", "RST 0", /* 304-307 */ +"RZ", "RET", "JZ", "???", /* 310-313 */ +"CZ", "CALL", "ACI", "RST 1", /* 314-317 */ +"RNC", "POP D", "JNC", "OUT", /* 320-323 */ +"CNC", "PUSH D", "SUI", "RST 2", /* 324-327 */ +"RC", "???", "JC", "IN", /* 330-333 */ +"CC", "???", "SBI", "RST 3", /* 334-337 */ +"RPO", "POP H", "JPO", "XTHL", /* 340-343 */ +"CPO", "PUSH H", "ANI", "RST 4", /* 344-347 */ +"RPE", "PCHL", "JPE", "XCHG", /* 350-353 */ +"CPE", "???", "XRI", "RST 5", /* 354-357 */ +"RP", "POP PSW", "JP", "DI", /* 360-363 */ +"CP", "PUSH PSW", "ORI", "RST 6", /* 364-367 */ +"RM", "SPHL", "JM", "EI", /* 370-373 */ +"CM", "???", "CPI", "RST 7", /* 374-377 */ + }; + +int32 oplen[256] = { +1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, +0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1,1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1, +1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1,1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 }; + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no format special format. The + load starts at the current value of the PC. +*/ + +int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 i, addr = 0, cnt = 0; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +addr = saved_PC; +while ((i = getc (fileref)) != EOF) { + M[addr] = i; + addr++; + cnt++; + } /* end while */ +printf ("%d Bytes loaded.\n", cnt); +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 +*/ + +int32 fprint_sym (FILE *of, int32 addr, unsigned int32 *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, c1, c2, inst, adr; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +c1 = (val[0] >> 8) & 0177; +c2 = val[0] & 0177; +if (sw & SWMASK ('A')) { + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; } +if (sw & SWMASK ('C')) { + 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]; +fprintf (of, "%s", opcode[inst]); +if (oplen[inst] == 2) { + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%o", val[1]); +} +if (oplen[inst] == 3) { + adr = val[1] & 0xFF; + adr |= (val[2] << 8) & 0xff00; + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%o", adr); +} +return -(oplen[inst] - 1); +} + +/* 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 +*/ + +int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, unsigned int32 *val, int32 sw) +{ +int32 cflag, i = 0, j, 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] = (unsigned int) 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] = ((unsigned int) cptr[0] << 8) + (unsigned int) cptr[1]; + return SCPE_OK; } + +/* An instruction: get opcode (all characters until null, comma, + or numeric (including spaces). +*/ + +while (1) { + if (*cptr == ',' || *cptr == '\0' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* Allow for RST which has numeric as part of opcode */ + +if (toupper(gbuf[0]) == 'R' && + toupper(gbuf[1]) == 'S' && + toupper(gbuf[2]) == 'T') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* Allow for 'MOV' which is only opcode that has comma in it. */ + +if (toupper(gbuf[0]) == 'M' && + toupper(gbuf[1]) == 'O' && + toupper(gbuf[2]) == 'V') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* kill trailing spaces if any */ +gbuf[i] = '\0'; +for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; +} + +/* find opcode in table */ +for (j = 0; j < 256; j++) { + if (strcmp(gbuf, opcode[j]) == 0) + break; +} +if (j > 255) /* not found */ + return SCPE_ARG; + +val[0] = j; /* store opcode */ +if (oplen[j] < 2) /* if 1-byter we are done */ + return SCPE_OK; +if (*cptr == ',') cptr++; +cptr = get_glyph(cptr, gbuf, 0); /* get address */ +sscanf(gbuf, "%o", &r); +if (oplen[j] == 2) { + val[1] = r & 0xFF; + return (-1); +} +val[1] = r & 0xFF; +val[2] = (r >> 8) & 0xFF; +return (-2); +} diff --git a/eclipse.txt b/eclipse.txt new file mode 100644 index 00000000..c00d270a --- /dev/null +++ b/eclipse.txt @@ -0,0 +1,24 @@ +Charles Owen's Eclipse Modules + +1. Eclipse CPU simulator + +The Eclipse CPU simulator can be used with the V2.5a Nova definitions and peripheral +modules to produce an Eclipse simulator that will run Eclipse mapped RDOS V7.5. The +compilation procedure is the same as for the Nova simulator, except: + + - the symbol ECLIPSE must be defined + - the module eclipse_cpu.c must be substituted for nova_cpu.c + - the output should be named eclipse rather than nova + +For example, to compile under UNIX, move nova_cpu.c out of the source directory +and then give this command: + + % cc -DECLIPSE eclipse_cpu.c nova_*.c -o eclipse + +2. Alternate terminal emulator + +The module eclipse_tt.c can be used with either an Eclipse or Nova CPU simulator +in place of nova_tt.c. It provides a full emulation of the cursor controls on +the Dasher video terminal but requires that the underlying operating system +interpret VT100 cursor controls. Thus, it works under VMS or UNIX but not under +Windows or OS/2. \ No newline at end of file diff --git a/eclipse_cpu.c b/eclipse_cpu.c new file mode 100644 index 00000000..ae00be7a --- /dev/null +++ b/eclipse_cpu.c @@ -0,0 +1,3421 @@ +/* eclipse_cpu.c: Eclipse CPU simulator + + Modified from the original NOVA simulator by Robert Supnik. + + Copyright (c) 1998-2001, Charles E Owen + Portions Copyright (c) 1993-2001, 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. + + cpu Eclipse central processor + + 26-Apr-01 RMS Added device enable/disable support + + The register state for the Eclipse CPU is basically the same as + the NOVA's: + + AC[0:3]<0:15> general registers + C carry flag + PC<0:14> program counter + + In addition, certain low-memory locations are reserved for special + purposes: + + 0: I/O Return Address (from an interrupt) + 1: I/O (Interrupt) handler address + 2: System Call handler address (used by SYC instruction) + 3: Protection Fault handler address + 4: VECTOR stack pointer (VCT Instruction) + 5: Current Interrupt Priority mask + 6: VECTOR stack limit (VCT instruction) + 7: VECTOR stack fault address (VCT again) + 10: Block Pointer (later models only) + 11: Emulation Trap Handler address (microeclipse only) + 20-27: Auto-increment locations (not on microeclipse) + 30-37: Auto-decrement locations (not on microeclipse) + 40: Stack pointer + 41: Frame Pointer + 42: Stack Limit + 43: Stack fault address + 44: XOP Origin address + 45: Floating point fault address + 46: Commercial fault address (not on microeclipse) + 47: Reserved, do not use. + + Note: While all eclipses share most of the "standard" features, + some models added a few quirks and wrinkles, and other models + dropped some features or modified others. Most DG software + is written for a "standard" Eclipse, and avoids these problem + areas. A general overview: + + [subject to major changes as info becomes available!] + + Early (e.g. S/100, S/200, C/300) [Front Panel machines] + + The first Eclipses had the basic MMPU, but certain parts + were kluged, and these were fixed in later MMPU designs. + This results in incompatibility, however. Also, early + CPUs had a feature called "Commercial Instruction Set" + which contained character manipulation, translation + between commercial-format numeric data and FPU formats, + and an elaborate EDIT instruction. Later models kept + only the character manipulation part of this and called + the feature the "Character Instruction Set", leading to + confusion because the initials of both are CIS. AFAIK, + DG dropped support for this MMPU and no version of RDOS + supported it past version 6, if even that. + + Middle (e.g. S/130, C/150, S/230, C/330) [Front Panel] + + These are close to a "Standard". They have the newer, + fixed MMPU. Support for the PIT (Programmed Interval + Timer. The Commercial (not character) instruction set + and FPU are optional. (CIS standard on C models) + + Late (C/350, M/600: [Panel]; S/140, S/280 [Virtual Console]) + + All features of the Middle period are included, plus: + These late Eclipses added a few MMPU wrinkles all their + own, included support for user maps C and D. Character + instruction set is standard, FPU optional. Also, support + for the BMC device. + + MicroEclipse-based (S/20, S/120, Desktops) [Virtual cons.] + + All features of the Late period, in general, plus: + Microeclipses dropped support for the auto increment + and decrement locations at 20-37. They also added + support for invalid instruction traps thru location 11. + The Desktops have an interface to the "Attached Processor", + an 8086, at device code 4. Also, some new CPU device + features to read states info. The Character Instruction + set and FPU are standard on all models. + + The Eclipse instruction set is an elaboration of the NOVA's. The basic + NOVA set is implemented in it's entireity, plus many new Eclipse + instructions are added. Since in theory every possible 16-bit + combination is a NOVA instruction, the Eclipse commands are carved + out of the NOVA set by using the Operate format with the no-load bit + set to 1 and the skip bits set to 000. Since this combination is + in effect a no-op on the NOVA, it was rarely or never used. The + other bits are used to form Eclipse instructions, which have no + other common format. To see the instructions, refer to the Eclipse + section of the instruction decode logic in sim_instr() below. All + Eclipse instructions are checked first, so in case of conflict in + bit patterns, the Eclipse one is executed over the corresponding + NOVA pattern. + + The following discussion talks about NOVA instructions which are + Eclipse instructions also. + + 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: + + eclipse_defs.h add interrupt request definition + eclipse_cpu.c add IOT mask, PI mask, and routine to dev_table + eclipse_sys.c add pointer to data structures to sim_devices +*/ + +/*--------------------------------------------------------------------------- +** ECLIPSE Debugging Facilities +** +** These options are designed to find hard-to-locate flaky bugs by +** providing special error checking and logging. +** +** All are controlled by depositing a value into the DEBUG register. +** A value of zero means no special debugging facilities are turned on. +** This is the default. Debugging invokes a performance hit! Use only +** when necessary. +** +** Debugging means logging information to a file, or to a buffer in +** memory from whence it can be dumped to a file. +** +** 1XXXXX = Log all instructions executed to file "trace.log". +** **CAUTION**: This means the CPU will run SLOWLY and +** the resulting trace.log file will be HUGE. We're talking +** about a megabyte for each 5 seconds or less of wall clock +** time, depending on the speed of your CPU. Note: In this +** mode, interrupts are logged when they are received also. +** +** Note: when detailed logging is off, the last 4096 or so +** instructions executed are saved in a memory buffer, and +** when the sim stops, the "dump" command can write this +** history information to the file "history.log". This only +** works if the DEBUG register is non-zero however, because +** of the performance hit even this recording makes. +** +** XXXXDD = Log all I/O instructions to or from device number +** DD. Log is written to "trace.log", regardless of the +** setting of the instruction trace flag (1XXXXX). If both +** are on, the device traces will be interpersed with the +** instruction traces -- very useful sometimes. +** +** XXX1DD = Device Break. Does a breakpoint in any I/O to +** device DD. Useful, say when a diagnostic gives an +** error message - a device break on 11 (TTO) will stop +** as soon as the error message appears, making the +** trace log much shorter to track back on. +** +** X4XXXX = When this bit is on, the sim will stop if it sees +** an invalid instruction. When DEBUG is zero, any such +** instruction is no-oped with no warning. When DEBUG is +** non-zero, but this bit is 0, a warning will be displayed +** but execution will continue. +** +** X2XXXX = LEF break. When A LEF instruction is executed in +** mapped user space, the sim does a breakpoint right after +** executing the instruction. +** +** Whenever the DEBUG register is non-zero, special error checking +** is enabled in the sim. This will stop the sim automatically +** when a likely error occurs, such as: +** +** 1. Any execution that reaches, or will reach, location 00000. +** 2. Any I/O to device 00 +** 3. An interrupt from device 00. +** 4. An invalid instruction (stop is optional) +** +** Of course, the standard BREAK register is available for breakpoints +** as in all the sims based on this standard. +--------------------------------------------------------------------------*/ + + +#include "nova_defs.h" + +#define ILL_ADR_FLAG 0100000 +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_MICRO (UNIT_V_UF) /* Microeclipse? */ +#define UNIT_MICRO (1 << UNIT_V_MICRO) +#define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_V_17B (UNIT_V_UF) /* 17 bit MAP */ +#define UNIT_17B (1 << UNIT_V_17B) + +unsigned int16 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC[4] = { 0 }; /* accumulators */ +int32 C = 0; /* carry flag */ +int32 saved_PC = 0; /* program counter */ +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 iot_enb = -1; /* IOT enables */ +int32 int_req = 0; /* interrupt requests */ +int32 pimask = 0; /* priority int mask */ +int32 pwr_low = 0; /* power fail flag */ +int32 ind_max = 15; /* 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 */ +int32 model = 130; /* Model of Eclipse */ +int32 speed = 0; /* Delay for each instruction */ + +int32 XCT_mode = 0; /* 1 if XCT mode */ +int32 XCT_inst = 0; /* XCT instruction */ +int32 PPC = -1; + +/* Instruction history buffer */ + +#define HISTMAX 4096 + +int32 hnext = 0; /* # of current entry */ +int32 hwrap = 0; /* 1 if wrapped */ +int32 hmax = HISTMAX; /* Maximum entries b4 wrap */ +unsigned int16 hpc[HISTMAX]; +unsigned int16 hinst[HISTMAX]; +unsigned int16 hinst2[HISTMAX]; +unsigned int16 hac0[HISTMAX]; +unsigned int16 hac1[HISTMAX]; +unsigned int16 hac2[HISTMAX]; +unsigned int16 hac3[HISTMAX]; +unsigned short hflags[HISTMAX]; + +/* Flags: 0x01 - carry bit + 0x02 - int enabled + 0x04 - user map a + 0x08 - user map b + 0x10 - user map c + 0x20 - user map d + 0x80 - this is an int, not an inst. + hpc is return addr + hinst is int_req + hac0 is device + hac1 is int addr +*/ + + + +/* the Eclipse MAP unit: This unit is standard in all Eclipse processors + except for the "original" Eclipses, the S/100, S/200, and C/300. These + use a different and more elaborate MMPU that is not compatible with + the one simulated here. All subsequent Eclipses, from the S/130 on up + to the last models S/280 and C/380 use the map simulated here, including + the MicroEclipses. There are model-dependent quirks. That's why we + have to MODEL register. + + The programming of the MMPU can be found in the LMP instruction, below, + and in the instructions directed to DEV_MAP. + + There are two user maps, called A and B, and four data channel maps, + A thru D. They can be enabled/disabled separately. Some models have + two extra user maps, C and D. These are supported where apporpriate. + +*/ + +#define PAGEMASK 01777 /* Largest physical page possible */ +#define MAPMASK 0101777 /* Valid page bits in map */ +#define INVALID 0101777 /* Mask indicating an invalid page */ +int32 MapStat = 0; /* Map status register */ +int32 Inhibit = 0; /* !0=inhibit interrupts : */ + /* 1 = single cycle inhibit */ + /* 2 = inhibit until indirection */ + /* 3 = inhibit next instruction only */ +int32 Enable = 0; /* User map to activate 1=A 2=B */ +int32 Usermap = 0; /* Active Map? 0=supvr mode, 1=user A, 2 = user B */ +int32 Map[8][32]; /* The actual MAPs 0=dch A, 1=A, 2=B, 3-5=dchB-D 6-7 User C-D */ +int32 Map31 = 037; /* Map for block 31 in supervisor mode */ +int32 SingleCycle = 0; /* Map one LDA/STA */ +int32 Check = 0; /* Page Check Register */ +int32 Fault = 0; /* Fault register */ +int32 MapInit = 0; /* 1 when map initialized */ +int32 MapIntMode = 0; /* Save of map user mode when int occurs */ + +int32 Debug_Flags = 0; /* Debug register - selects debug features */ + +int32 Tron = 0; /* For trace files */ +FILE *Trace; + +t_stat reason; + +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_boot (int32 unitno); +t_stat cpu_set_size (UNIT *uptr, int32 value); +t_stat Debug_Dump (UNIT *uptr, int32 value); +t_stat map_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat map_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat map_reset (DEVICE *dptr); +t_stat map_svc (UNIT *uptr); +int32 GetMap(int32 addr); +int32 PutMap(int32 addr, int32 data); +int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 AC2, int32 AC3, int32 flags); +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); +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +/* 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, + 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 (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 (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 (MICRO, cpu_unit.flags, UNIT_V_MICRO), REG_HRO }, + { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, + { ORDATA (DEBUG, Debug_Flags, 16) }, + { ORDATA (BREAK, ibkpt_addr, 16) }, + { DRDATA (MODEL, model, 16) }, + { DRDATA (SPEED, speed, 16) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_MICRO, UNIT_MICRO, "MICRO", "MICRO", NULL }, + { UNIT_MICRO, 0, "STD", "STD", 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, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, + { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, + { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size }, + { UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size }, + { UNIT_MSIZE, 0, NULL, "DUMP", &Debug_Dump }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 17, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + &cpu_boot, NULL, NULL }; + +/* MAP data structures + + map_dev MAP device descriptor + map_unit MAP unit descriptor + map_reg MAP register list + map_mod MAP modifiers list +*/ + +UNIT map_unit = { UDATA (&map_svc, UNIT_17B, MAXMEMSIZE) }; + +REG map_reg[] = { + { ORDATA (STATUS, MapStat, 16) }, + { ORDATA (ENABLE, Enable, 16) }, + { ORDATA (IINHIB, Inhibit, 16) }, + { ORDATA (ACTIVE, Usermap, 16) }, + { ORDATA (MAP31, Map31, 16) }, + { ORDATA (CYCLE, SingleCycle, 16) }, + { ORDATA (CHECK, Check, 16) }, + { ORDATA (FAULT, Fault, 16) }, + { NULL } }; + +MTAB map_mod[] = { + { UNIT_17B, UNIT_17B, "17bit", "17B", NULL }, + { UNIT_17B, 0, "19bit", "19B", NULL }, + { 0 } }; + +DEVICE map_dev = { + "MAP", &map_unit, map_reg, map_mod, + 1, 8, 17, 1, 8, 16, + &map_ex, &map_dep, NULL, + NULL, NULL, NULL }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 PC, IR, i, t, MA, j, k; +register unsigned int32 mddata, uAC0, uAC1, uAC2, uAC3; +int16 sAC0, sAC1, sAC2; +int32 sddata, mi1, mi2; +t_value simeval[20]; +void mask_out (int32 mask); +/* char debstr[128]; */ +/* char debadd[64]; */ +char debmap[4], debion[4]; +int debcar, iodev, iodata, debflags; +int32 DisMap, debpc; +/* int32 sp, sl; */ +int cmdptr, cmsptr, cmopt, cmptr; +int16 cmslen, cmdlen; +int tabaddr, tabptr; +int32 effective(int32 PC, int32 index, int32 disp); +int32 indirect(int32 d); +int32 LEFmode(int32 PC, int32 index, int32 disp, int32 indirect); +int32 LoadMap(int32 w); +int32 Bytepointer(int32 PC, int32 index); +int32 unimp(int32 PC); +int32 pushrtn(int32 pc); + +/* Restore register state */ + +PC = saved_PC & AMASK; /* load local PC */ +C = C & 0200000; +mask_out (pimask); /* reset int system */ +reason = 0; +if (MapInit == 0) { + MapInit = 1; + for (mi1 = 0; mi1 < 6; mi1++) { /* Initialize MAPs */ + for (mi2 = 0; mi2 < 32; mi2++) { + Map[mi1][mi2] = mi2; + } + } +} + +/* 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 (speed > 0) for (i = 0; i < speed; i++) { j = 0; } + +if (Fault) { /* Check MAP fault */ + Usermap = 0; /* YES: shutdown map */ + MapStat &= ~01; /* Disable MMPU */ + if (Fault & 0100000) /* If it was validity, */ + MapStat &= ~0170; /* Reset other checkbits */ + MapStat |= Fault & 077777; /* Put in fault code */ + Fault = 0; /* Reset fault code */ + t = (GetMap(040) + 1) & AMASK; /* Push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, (PC & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + int_req = int_req & ~INT_ION; /* Disable interrupts */ + PC = indirect(M[003]); /* JMP to loc 3 */ + continue; +} + +if (int_req > INT_PENDING && !Inhibit) { /* interrupt? */ + int_req = int_req & ~INT_ION; + MapIntMode = MapStat; /* Save Status as it was */ + Usermap = 0; /* Inhibit MAP */ + MapStat &= ~1; /* Disable user map */ + if (XCT_mode) { + M[0] = PC - 1; /* If XCT mode rtn to XCT */ + XCT_mode = 0; /* turn off mode */ + } else { + M[0] = PC; /* Save Return Address */ + } + old_PC = PC; + MA = M[1]; + for (i = 0; i < ind_max * 2; i++) { /* count indirects */ + if ((MA & 0100000) == 0) break; + if ((MA & 077770) == 020) + MA = (M[MA & AMASK] = (M[MA & AMASK] + 1) & 0177777); + else if ((MA & 077770) == 030) + MA = (M[MA & AMASK] = (M[MA & AMASK] - 1) & 0177777); + else MA = M[MA & AMASK]; + } + if (i >= ind_max) { + if ((MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + continue; + } else { + reason = STOP_IND_INT; + break; + } + } + if (Debug_Flags) { + iodev = 0; + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + iodev = i; + break; + } + } + if (iodev == 0) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (Debug_Flags & 0100000) { + fprintf(Trace, "--------- Interrupt %o (%o) to %6o ---------\n", int_req, iodev, MA); + } else { + Debug_Entry(PC, int_req, 0, iodev, MA, 0, 0, 0x80); + } + } + PC = MA; +} /* end interrupt */ + +if (Inhibit != 0) { /* Handle 1-instruction inhibit sequence */ + if (Inhibit == 3) /* Used by SYC instruction */ + Inhibit = 4; + if (Inhibit == 4) + Inhibit = 0; +} + +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; +} + +if ((PC < 1 || PC > 077777) && Debug_Flags) { + if (PPC != -1) { /* Don't break on 1st instruction */ + printf("\n<>\n\r", PC, PPC); + reason = STOP_IBKPT; + break; + } +} + +PPC = PC; + +if (Debug_Flags) { + if (!Tron) { + Tron = 1; + Trace = fopen("trace.log", "w"); + } + strcpy(debmap, " "); + strcpy(debion, " "); + debcar = 0; + if (C) debcar = 1; + if (Usermap == 1) strcpy(debmap, "A"); + if (Usermap == 2) strcpy(debmap, "B"); + if (Usermap == 5) strcpy(debmap, "C"); + if (Usermap == 6) strcpy(debmap, "D"); + if (int_req & INT_ION) strcpy(debion, "I"); + if (XCT_mode == 0) { + debpc = PC; + simeval[0] = GetMap(PC); + simeval[1] = GetMap(PC+1); + } else { + debpc = 0177777; + simeval[0] = XCT_inst; + simeval[1] = 0; + } + if (Debug_Flags & 0100000) { + fprintf(Trace, "%s%s%06o acs: %06o %06o %06o %06o %01o ", + debion, debmap, debpc, AC[0], AC[1], AC[2], AC[3], debcar); + fprint_sym (Trace, debpc, simeval, NULL, SWMASK('M')); + fprintf(Trace, "\n"); + } else { + debflags = 0; + if (C) debflags |= 0x01; + if (int_req & INT_ION) debflags |= 0x02; + if (Usermap == 1) debflags |= 0x04; + if (Usermap == 2) debflags |= 0x08; + if (Usermap == 3) debflags |= 0x10; + if (Usermap == 4) debflags |= 0x20; + Debug_Entry(debpc, simeval[0], simeval[1], AC[0], AC[1], AC[2], AC[3], debflags); + } +} + +if (XCT_mode == 0) { /* XCT mode? */ + IR = GetMap(PC); /* No: fetch instr */ + if (Fault) continue; /* Give up if fault */ + PC = (PC + 1) & AMASK; /* bump PC */ +} else { + IR = XCT_inst; /* Yes: Get inst to XCT */ + XCT_mode = 0; /* Go back to normal mode */ +} +int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ +sim_interval = sim_interval - 1; +t = IR >> 11; /* prepare to decode */ + +/* ---------------- BEGIN Eclipse modification --------------------- */ + +/* Eclipse instruction set. These instructions are checked for + before any of the NOVA ones. Eclipse instructions do not + correspond to any patterns, other than bit 0 being 1 and + the last 4 bits are 1000. Words which are not Eclipse + instructions will be interpreted as Nova instructions. */ + +/* Important Note: The order of the if statements is important. + Frequently executed instructions should come first, to enhance + the speed of the simulation. +*/ + +if ((IR & 0100017) == 0100010) { /* This pattern for all */ + /* Eclipse instructions */ + +/****************************************************************/ +/* This is the standard Eclipse instruction set */ +/****************************************************************/ + + /* Byte operations */ + + if ((IR & 0103777) == 0102710) { /* LDB: Load Byte */ + i = (IR >> 13) & 03; + MA = (AC[i] >> 1) & AMASK; + j = (IR >> 11) & 03; + if (AC[i] & 01) { + AC[j] = GetMap(MA) & 0377; + } else { + AC[j] = (GetMap(MA) >> 8) & 0377; + } + continue; + } + if ((IR & 0103777) == 0103010) { /* STB: Store Byte */ + i = (IR >> 13) & 03; + MA = (AC[i] >> 1); + j = (IR >> 11) & 03; + t = GetMap(MA); + if (AC[i] & 01) { + t &= 0177400; + t |= (AC[j] & 0377); + PutMap(MA, t); + } else { + t &= 0377; + t |= (AC[j] & 0377) << 8; + PutMap(MA, t); + } + continue; + } + + /* Fixed-point arithmetic - loads & saves */ + + if ((IR & 0162377) == 0122070) { /* ELDA: Extended LDA */ + i = (IR >> 11) & 3; + t = GetMap(PC); + if (SingleCycle) Usermap = SingleCycle; + AC[i] = GetMap(effective(PC, (IR >> 8) & 3, t)); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0162377) == 0142070) { /* ESTA: Extended STA */ + i = (IR >> 11) & 3; + t = GetMap(PC); + if (SingleCycle) Usermap = SingleCycle; + PutMap((effective(PC, (IR >> 8) & 3, t)), AC[i]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100010) { /* ADI: Add Immediate */ + t = (IR >> 11) & 3; + AC[t] = (AC[t] + ((IR >> 13) & 3) + 1) & 0xffff; + continue; + } + if ((IR & 0103777) == 0100110) { /* SBI: Subtract Immediate */ + t = (IR >> 11) & 3; + AC[t] = (AC[t] - (((IR >> 13) & 3) + 1)) & 0xffff; + continue; + } + if ((IR & 0163777) == 0163770) { /* ADDI: Extended Add Immed. */ + t = (IR >> 11) & 3; + i = GetMap(PC); + PC = (PC + 1) & AMASK; + AC[t] = (AC[t] + i) & 0xffff; + continue; + } + if ((IR & 0103777) == 0100710) { /* XCH: Exchange Accumulators */ + t = AC[(IR >> 11) & 3]; + AC[(IR >> 11) & 3] = AC[(IR >> 13) & 3]; + AC[(IR >> 13) & 3] = t; + continue; + } + if ((IR & 0162377) == 0162070) { /* ELEF: Load Effective Addr */ + t = GetMap(PC); + AC[(IR >> 11) & 3] = effective(PC, (IR >> 8) & 3, t); + PC = (PC + 1) & AMASK; + continue; + } + + /* Logical operations */ + + if ((IR & 0163777) == 0143770) { /* ANDI: And Immediate */ + AC[(IR >> 11) & 3] &= GetMap(PC); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0103770) { /* IORI: Inclusive Or Immed */ + AC[(IR >> 11) & 3] |= GetMap(PC); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0123770) { /* XORI: Exclusive Or Immed */ + AC[(IR >> 11) & 3] ^= GetMap(PC); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100410) { /* IOR: Inclusive Or */ + AC[(IR >> 11) & 3] |= AC[(IR >> 13) & 3]; + continue; + } + if ((IR & 0103777) == 0100510) { /* XOR: Exclusive Or */ + AC[(IR >> 11) & 3] ^= AC[(IR >> 13) & 3]; + continue; + } + if ((IR & 0103777) == 0100610) { /* ANC: And with complemented src */ + AC[(IR >> 11) & 3] &= ~(AC[(IR >> 13) & 3]); + continue; + } + + /* Shift operations */ + + if ((IR & 0103777) == 0101210) { /* LSH: Logical Shift */ + register int16 sh; + sh = AC[(IR >> 13) & 3] & 0377; + i = (IR >> 11) & 3; + if (sh & 0200) { + sh = ~sh + 1; + AC[i] = AC[i] >> sh; + } else { + AC[i] = AC[i] << sh; + } + if (sh > 15) AC[i] = 0; + AC[i] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0101310) { /* DLSH: Double logical shift */ + register int16 sh; + sh = AC[(IR >> 13) & 3] & 0377; + i = (IR >> 11) & 3; + uAC0 = AC[i] << 16; + j = i + 1; + if (j == 4) j = 0; + uAC0 |= AC[j]; + if (sh & 0200) { + sh = (~sh + 1) & 0377; + if (sh < 32) + uAC0 = uAC0 >> sh; + } else { + if (sh < 32) + uAC0 = uAC0 << sh; + } + if (sh > 31) uAC0 = 0; + AC[i] = (uAC0 >> 16) & 0xffff; + AC[j] = uAC0 & 0xffff; + continue; + } + if ((IR & 0103777) == 0101410) { /* HXL: Hex shift left */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + AC[i] = AC[i] << (t * 4); + AC[i] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0101510) { /* HXR: Hex shift right */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + AC[i] = AC[i] >> (t * 4); + AC[i] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0101610) { /* DHXL: Double Hex shift left */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + j = i + 1; + if (j == 4) j = 0; + uAC0 = AC[i] << 16; + uAC0 |= AC[j]; + uAC0 = uAC0 << ((t * 4) & 0177); + AC[i] = (uAC0 >> 16) & 0xffff; + AC[j] = uAC0 & 0xffff; + continue; + } + if ((IR & 0103777) == 0101710) { /* DHXR: Double Hex shift right */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + j = i + 1; + if (j == 4) j = 0; + uAC0 = AC[i] << 16; + uAC0 |= AC[j]; + uAC0 = uAC0 >> ((t * 4) & 0177); + AC[i] = (uAC0 >> 16) & 0xffff; + AC[j] = uAC0 & 0xffff; + continue; + } + + + /* Bit operations */ + + if ((IR & 0103777) == 0102010) { /* BTO: Set bit to one */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + MA = indirect(AC[j] + k); + } else { + MA = (AC[i] >> 4) & AMASK; + } + t = AC[i] & 017; + t = GetMap(MA) | (0100000 >> t); + PutMap(MA, t); + continue; + } + if ((IR & 0103777) == 0102110) { /* BTZ: Set bit to zero */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + MA = indirect(AC[j] + k); + } else { + MA = (AC[j] >> 4) & AMASK; + } + t = AC[i] & 017; + t = GetMap(MA) & ~(0100000 >> t); + PutMap(MA, t); + continue; + } + if ((IR & 0103777) == 0102210) { /* SZB: Skip on zero bit */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + MA = indirect(AC[j] + k); + } else { + MA = (AC[i] >> 4) & AMASK; + } + t = GetMap(MA) << (AC[i] & 017); + if (!(t & 0100000)) PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102770) { /* SNB: Skip on non-zero bit */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + MA = indirect(AC[j] + k); + } else { + MA = (AC[j] >> 4) & AMASK; + } + t = GetMap(MA) << (AC[i] & 017); + if (t & 0100000) PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102310) { /* SZBO: skip on zero bit & set to 1 */ + register int32 save; + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + MA = indirect(AC[j] + k); + } else { + MA = (AC[j] >> 4) & AMASK; + } + t = AC[i] & 017; + save = GetMap(MA); + t = save | (0100000 >> t); + PutMap(MA, t); + t = save << (AC[i] & 017); + if ((t & 0100000) == 0) + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102410) { /* LOB: Locate lead bit */ + register int32 a, r; + register int16 b, c = 0; + a = AC[(IR >> 13) & 3] & 0xffff; + for (i = 0; i < 16; i++) { + if ((a << i) & 0100000) break; + } + r = (IR >> 11) & 3; + b = AC[r]; + b += i; + AC[r] = b & 0177777; + continue; + } + if ((IR & 0103777) == 0102510) { /* LRB: Locate & reset lead bit */ + register int32 a, r; + register int16 b; + j = (IR >> 13) & 3; + a = AC[j]; + for (i = 0; i < 16; i++) { + if ((a << i) & 0100000) break; + } + r = (IR >> 11) & 3; + b = AC[r]; + b += i; + if (j != r) AC[r] = b & 0177777; + AC[j] &= ~(0100000 >> i); + AC[j] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0102610) { /* COB: Count bits */ + register int32 a; + register int16 b, c = 0; + a = AC[(IR >> 13) & 3]; + for (i = 0; i < 16; i++) { + if ((a >> i) & 1) c++; + } + i = (IR >> 11) & 3; + b = AC[i]; + b += c; + AC[i] = b & 0177777; + continue; + } + + + /* Jump & similar operations */ + + if ((IR & 0176377) == 0102070) { /* EJMP: Extended JMP */ + PC = effective(PC, (IR >> 8) & 3, GetMap(PC)); + continue; + } + if ((IR & 0176377) == 0106070) { /* EJSR: Extended JMP to subr */ + t = effective(PC, (IR >> 8) & 3, GetMap(PC)); + AC[3] = (PC + 1) & AMASK; + PC = t & AMASK; + continue; + } + if ((IR & 0176377) == 0112070) { /* EISZ: Ext Inc & skip if 0 */ + MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); + PutMap(MA, ((GetMap(MA) + 1) & 0xffff)); + if (GetMap(MA) == 0) PC = (PC + 1) & AMASK; + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0176377) == 0116070) { /* EDSZ: Ext Dec & skip if 0 */ + MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); + PutMap(MA, ((GetMap(MA) - 1) & 0xffff)); + if (GetMap(MA) == 0) PC = (PC + 1) & AMASK; + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0101010) { /* SGT: Skip if ACS > ACD */ + register int16 a1, d1; + a1 = AC[(IR >> 13) & 3] & 0xffff; + d1 = AC[(IR >> 11) & 3] & 0xffff; + if (a1 > d1) + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0101110) { /* SGE: Skip if ACS >= ACD */ + register int16 a1, d1; + a1 = AC[(IR >> 13) & 3] & 0xffff; + d1 = AC[(IR >> 11) & 3] & 0xffff; + if (a1 >= d1) + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102370) { /* CLM: Compare to limits */ + register int32 s, d, MA; + int16 H, L, ca; + s = (IR >> 13) & 3; + d = (IR >> 11) & 3; + if (s == d) { + L = GetMap(PC); + PC++; + H = GetMap(PC); + PC++; + } else { + MA = AC[d] & AMASK; + L = GetMap(MA); + H = GetMap(MA + 1); + } + ca = AC[s]; + if (ca >= L && ca <= H) PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0123370) { /* XCT: Execute */ + XCT_mode = 1; /* Set up to execute on next loop */ + XCT_inst = AC[(IR >> 11) & 3]; + continue; + } + + /* Memory block operations */ + + if (IR == 0113710) { /* BAM: Block add & move */ + register int32 w; + t = AC[1]; + if (t < 1 || t > 0100000) + continue; + i = indirect(AC[2]); + j = indirect(AC[3]); + while (t) { + w = GetMap(i); + PutMap(j, ((w + AC[0]) & 0xffff)); + if (Fault) break; + t--; + i++; + j++; + i &= AMASK; + j &= AMASK; + } + AC[1] = t; + AC[2] = i & AMASK; + AC[3] = j & AMASK; + continue; + } + if (IR == 0133710) { /* BLM: Block move */ + t = AC[1]; + if (t < 1 || t > 0100000) + continue; + i = indirect(AC[2]); + j = indirect(AC[3]); + while (t) { + PutMap(j, GetMap(i)); + if (Fault) break; + t--; + i++; + j++; + i &= AMASK; + j &= AMASK; + } + AC[1] = t; + AC[2] = i & AMASK; + AC[3] = j & AMASK; + continue; + } + + + /* Stack operations */ + + if ((IR & 0103777) == 0103110) { /* PSH: Push multiple accums */ + register int32 j; + j = (IR >> 11) & 3; + t = GetMap(040) & AMASK; + i = (IR >> 13) & 3; + if (i == j) { + t++; + PutMap(t, AC[i]); + PutMap(040, (t & AMASK)); + if (t > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + while (i != j) { + t++; + PutMap(t, AC[i]); + i++; + if (i == 4) i = 0; + } + t++; + PutMap(t, AC[i]); + PutMap(040, (t & AMASK)); + if ((GetMap(040) & AMASK) > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if ((IR & 0103777) == 0103210) { /* POP: Pop mult accums */ + j = (IR >> 11) & 3; + t = GetMap(040) & AMASK; + i = (IR >> 13) & 3; + if (i == j) { + AC[i] = GetMap(t); + t--; + PutMap(040, (t & AMASK)); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + PutMap(040, GetMap(042)); + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + while (i != j) { + AC[i] = GetMap(t); + t--; + i--; + if (i == -1) i = 3; + } + AC[i] = GetMap(t); + t--; + PutMap(040, (t & AMASK)); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + PutMap(040, GetMap(042)); + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0103710) { /* PSHR: Push return addr */ + t = (GetMap(040) + 1) & AMASK; + PutMap(t, (PC + 1)); + PutMap(040, t); + if ((GetMap(040) & AMASK) > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0163710) { /* SAVE */ + register int32 savep; + savep = ((GetMap(PC) + GetMap(040)) + 5) & AMASK; + if (savep > GetMap(042)) { + pushrtn(PC-1); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + continue; + } + t = GetMap(040) + 1; + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, GetMap(041)); + t++; + savep = PC; + PC = (PC + 1) & AMASK; + PutMap(t, (AC[3] & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + AC[3] = GetMap(040) & AMASK; + PutMap(041, AC[3]); + PutMap(040, ((GetMap(040) + GetMap(savep)) & AMASK)); + continue; + } + if ((IR & 0163777) == 0103370) { /* MSP: Modify stack pointer */ + t = (GetMap(040) + AC[(IR >> 11) & 3]) & 0177777; + if (t > GetMap(042)) { + pushrtn(PC-1); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & AMASK)); + PutMap(042, (GetMap(042) | 0100000)); + continue; + } + PutMap(040, t); + continue; + } + if ((IR & 0176377) == 0102270) { /* PSHJ: Push JMP */ + PutMap(040, (GetMap(040) + 1)); + PutMap((GetMap(040) & AMASK), ((PC + 1) & AMASK)); + if ((GetMap(040) & AMASK) > (GetMap(042) & AMASK)) { + pushrtn(PC+1); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + continue; + } + PC = effective(PC, (IR >> 8) & 3, GetMap(PC)); + continue; + } + if (IR == 0117710) { /* POPJ: Pop PC and Jump */ + PC = GetMap(GetMap(040)) & AMASK; + PutMap(040, (GetMap(040) - 1)); + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0107710) { /* POPB: Pop block */ + PC = GetMap(GetMap(040)) & AMASK; + if (GetMap(GetMap(040)) & 0100000) + C = 0200000; + else + C = 0; + PutMap(040, (GetMap(040) - 1)); + AC[3] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[2] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[1] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[0] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + continue; + } + if (IR == 0127710) { /* RTN: Return */ + PutMap(040, GetMap(041)); + PC = GetMap(GetMap(040)) & AMASK; + if (GetMap(GetMap(040)) & 0100000) + C = 0200000; + else + C = 0; + PutMap(040, (GetMap(040) - 1)); + AC[3] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[2] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[1] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[0] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + PutMap(041, AC[3]); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + PC = indirect(GetMap(043)); + } + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + continue; + } + if (IR == 0167710) { /* RSTR: Restore */ + int32 SVPC; + + SVPC = PC; + PC = GetMap(GetMap(040)) & AMASK; + if (PC == 0 && Debug_Flags) { + printf("\n<>\n\r", SVPC); + reason = STOP_IBKPT; + } + if (GetMap(GetMap(040)) & 0100000) + C = 0200000; + else + C = 0; + PutMap(040, (GetMap(040) - 1)); + AC[3] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[2] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[1] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[0] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + PutMap(043, GetMap(GetMap(040))); + PutMap(040, (GetMap(040) - 1)); + PutMap(042, GetMap(GetMap(040))); + PutMap(040, (GetMap(040) - 1)); + PutMap(041, GetMap(GetMap(040))); + PutMap(040, (GetMap(040) - 1)); + PutMap(040, GetMap(GetMap(040))); + /*t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PC = indirect(GetMap(043)); + }*/ + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + continue; + } + + /* Multiply / Divide */ + + if (IR == 0143710) { /* MUL: Unsigned Multiply */ + uAC0 = (unsigned int32) AC[0]; + uAC1 = (unsigned int32) AC[1]; + uAC2 = (unsigned int32) AC[2]; + + mddata = (uAC1 * uAC2) + uAC0; + AC[0] = (mddata >> 16) & 0177777; + AC[1] = mddata & 0177777; + continue; + } + if (IR == 0147710) { /* MULS: Signed Multiply */ + sAC0 = AC[0]; + sAC1 = AC[1]; + sAC2 = AC[2]; + + sddata = (sAC1 * sAC2) + sAC0; + AC[0] = (sddata >> 16) & 0177777; + AC[1] = sddata & 0177777; + continue; + } + if (IR == 0153710) { /* DIV: Unsigned Divide */ + uAC0 = (unsigned int32) AC[0]; + uAC1 = (unsigned int32) AC[1]; + uAC2 = (unsigned int32) AC[2]; + + if (uAC0 >= uAC2) C = 0200000; + else { C = 0; + mddata = (uAC0 << 16) | uAC1; + AC[1] = mddata / uAC2; + AC[0] = mddata % uAC2; + } + continue; + } + if (IR == 0157710) { /* DIVS: Signed Divide */ + sAC2 = AC[2]; + + C = 0; + sddata = ((AC[0] & 0xffff) << 16) | (AC[1] & 0xffff); + AC[1] = sddata / sAC2; + AC[0] = sddata % sAC2; + if (AC[0] > 077777 || AC[0] < -077776) C = 0200000; + /*if ((AC[0] & 0xFFFF0000) != 0) C = 0200000;*/ + if (AC[1] > 077777 || AC[1] < -077776) C = 0200000; + /*if ((AC[1] & 0xFFFF0000) != 0) C = 0200000;*/ + AC[0] &= 0177777; + AC[1] &= 0177777; + continue; + } + if (IR == 0137710) { /* DIVX: Sign extend and Divide */ + int32 q; + if (AC[1] & 0100000) { + AC[0] = 0177777; + } else { + AC[0] = 0; + } + sAC0 = AC[0]; + sAC1 = AC[1]; + sAC2 = AC[2]; + + C = 0; + sddata = (sAC0 << 16) | sAC1; + q = sddata / sAC2; + AC[0] = sddata % sAC2; + if (q > 0177777) { + C = 0200000; + } else { + AC[1] = q & 0xffff; + } + continue; + } + if ((IR & 0163777) == 0143370) { /* HLV: Halve */ + t = (IR >> 11) & 3; + if (AC[t] & 0100000) { + AC[t] = (0 - AC[t]) & 0xffff; + AC[t] = AC[t] >> 1; + AC[t] = (0 - AC[t]) & 0xffff; + } else { + AC[t] = (AC[t] >> 1) & 0xffff; + } + continue; + } + + /* Decimal arithmetic */ + + if ((IR & 0103777) == 0100210) { /* DAD: Decimal add */ + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + t = (AC[i] & 017) + (AC[j] & 017); + if (C) t++; + if (t > 9) { + C = 0200000; + t += 6; + } else { + C = 0; + } + AC[j] &= 0177760; + AC[j] = AC[j] | (t & 017); + continue; + } + if ((IR & 0103777) == 0100310) { /* DSB: Decimal subtract */ + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + t = (AC[j] & 017) - (AC[i] & 017); + if (!C) t--; + if (t < 0) { + C = 0; + t = 9 - (~t); + } else { + C = 0200000; + } + AC[j] &= 0177760; + AC[j] = AC[j] | (t & 017); + continue; + } + + /* Exotic, complex instructions */ + + if ((IR & 0162377) == 0142170) { /* DSPA: Dispatch */ + register int32 d; + int16 a, H, L; + MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); + H = GetMap(MA - 1) & 0177777; + L = GetMap(MA - 2) & 0177777; + a = AC[(IR >> 11) & 3] & 0177777; + if (a < L || a > H) { + PC = (PC + 1) & AMASK; + continue; + } + d = GetMap(MA - L + a); + if (d == 0177777) { + PC = (PC + 1) & AMASK; + continue; + } + PC = indirect(d) & AMASK; + continue; + } + + if (((IR & 0100077) == 0100030) || + ((IR & 0102077) == 0100070)) { /* XOP: Extended Operation */ + register int32 op, d, sa, da; + op = (IR >> 6) & 037; + if ((IR & 077) == 070) op += 32; + t = GetMap(040) & AMASK; + for (i = 0; i <= 3; i++) { + t++; + PutMap(t, AC[i]); + if (((IR >> 13) & 3) == i) sa = t; + if (((IR >> 11) & 3) == i) da = t; + } + t++; + PutMap(t, PC & AMASK); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + AC[2] = sa; + AC[3] = da; + d = GetMap(GetMap(044) + op); + PC = indirect(d) & AMASK; + if ((GetMap(040) & AMASK) > GetMap(042) & AMASK) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if ((IR & 0103777) == 0103510) { /* SYC: System call */ + register int32 j; + DisMap = Usermap; + Usermap = 0; + MapStat &= ~1; /* Disable MAP */ + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + if (i != 0 || j != 0) { + t = (GetMap(040) + 1) & AMASK; + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, (PC & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PutMap(041, (GetMap(040) & AMASK)); + } + PC = indirect(GetMap(2)) & AMASK; + if (DisMap > 0) + Inhibit = 3; /* Special 1-instruction interrupt inhibit */ + if ((GetMap(040) & AMASK) > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0113410) { /* LMP: Load Map */ + register int32 w, m; + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o LMP (Map=%o)\n", PC - 1, (MapStat>>7)&07); + t = AC[1]; + i = AC[2]; + while (t) { + if (int_req > INT_PENDING && !Inhibit) { /* interrupt? */ + PC = PC - 1; + break; + } + if (!Usermap || !(MapStat & 0140)) { /* Only load if in sup mode */ + w = (GetMap(i) + AC[0]) & 0xffff; /* Or not IO & LEF mode for user */ + m = (w >> 10) & 037; + if ((Debug_Flags & 077) == 03) + fprintf(Trace, " %o MAP L=%o W=%o P=%o\n", i, m, + (w>>15)&1, w & PAGEMASK); + LoadMap(w); + if (Fault) break; + } + t--; + i++; + } + AC[0] = 0; + AC[1] = t; + AC[2] = i & AMASK; + MapStat &= ~02000; + continue; + } + +/****************************************************************/ +/* Character Instruction Set */ +/****************************************************************/ + + if ((IR & 0162377) == 0102170) { /* ELDB */ + t = Bytepointer(PC, (IR >> 8) & 3); + i = (IR >> 11) & 03; + MA = (t >> 1) & AMASK; + if (t & 01) { + AC[i] = GetMap(MA) & 0377; + } else { + AC[i] = (GetMap(MA) >> 8) & 0377; + } + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0162377) == 0122170) { /* ESTB */ + t = Bytepointer(PC, (IR >> 8) & 3); + i = (IR >> 11) & 03; + MA = (t >> 1) & AMASK; + j = GetMap(MA); + if (t & 01) { + j &= 0177400; + j |= (AC[i] & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (AC[i] & 0377) << 8; + PutMap(MA, j); + } + PC = (PC + 1) & AMASK; + continue; + } + + if ((IR & 077) == 050) { /* All CIS end with 050 except ELDB/ESTB */ + + if (IR == 0153650) { /* CMV Character Move */ + cmdlen = AC[0] & 0177777; /* Set up length & direction */ + cmslen = AC[1] & 0177777; /* For both source & dest */ + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + C = 0; /* Do carry now b4 cmslen changes */ + if (abs(cmslen) > abs(cmdlen)) + C = 0200000; + for (i = 0; i < abs(cmdlen); i++) { /* Move loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB */ + if (cmsptr & 01) { + uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + if (cmslen == 0) { + uAC2 = ' ' & 0377; /* Handle short source */ + } + MA = (cmdptr >> 1) & AMASK; /* do an STB */ + j = GetMap(MA); + if (cmdptr & 01) { + j &= 0177400; + j |= (uAC2 & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (uAC2 & 0377) << 8; + PutMap(MA, j); + } + if (cmslen > 0) { + cmsptr++; + cmslen--; + } + if (cmslen < 0) { + cmsptr--; + cmslen++; + } + if (cmdlen > 0) { + cmdptr++; + } else { + cmdptr--; + } + } + AC[0] = 0; + AC[1] = cmslen & 0177777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + + if (IR == 0157650) { /* CMP Character compare */ + cmdlen = AC[0] & 0177777; /* Set up length & direction */ + cmslen = AC[1] & 0177777; /* For both source & dest */ + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + t = 0; /* Equal unless otherwise */ + while (1) { /* Compare loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB - string 1 */ + if (cmslen != 0) { + if (cmsptr & 01) { + uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + } else { + uAC2 = ' ' & 0377; + } + MA = (cmdptr >> 1) & AMASK; /* do an LDB - string 2 */ + if (cmdlen != 0) { + if (cmdptr & 01) { + uAC3 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC3 = (GetMap(MA) >> 8) & 0377; + } + } else { + uAC3 = ' ' & 0377; + } + if (uAC2 > uAC3) { + t = 1; + break; + } + if (uAC2 < uAC3) { + t = -1; + break; + } + if (cmslen > 0) { + cmsptr++; + cmslen--; + } + if (cmslen < 0) { + cmsptr--; + cmslen++; + } + if (cmdlen > 0) { + cmdptr++; + cmdlen--; + } + if (cmdlen < 0) { + cmdptr--; + cmdlen++; + } + if (cmslen == 0 && cmdlen == 0) + break; + } + AC[1] = t & 0177777; + AC[0] = cmdlen & 0177777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + if (IR == 0163650) { /* CTR Character translate */ + tabaddr = indirect(AC[0]); /* Get address of table */ + tabptr = M[tabaddr] & 0177777; /* Get byte pointer */ + cmslen = AC[1] & 0177777; /* Length: both source & dest */ + cmopt = 0; /* Default: COMPARE option */ + if (cmslen < 0) { + cmopt=1; /* MOVE option */ + cmslen = 0 - cmslen; + } + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + t = 0; /* Equal unless otherwise */ + while (1) { /* Translation loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB - string 1 */ + if (cmsptr & 01) { + j = GetMap(MA) & 0377; + } else { + j = (GetMap(MA) >> 8) & 0377; + } + cmptr = tabptr + j; /* Translate */ + MA = (cmptr >> 1) & AMASK; + if (cmptr & 01) { + uAC2 = GetMap(MA) & 0377; + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + if (cmopt) { /* MOVE... */ + MA = (cmdptr >> 1) & AMASK; /* do an STB */ + j = GetMap(MA); + if (cmdptr & 01) { + j &= 0177400; + j |= (uAC2 & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (uAC2 & 0377) << 8; + PutMap(MA, j); + } + } else { /* COMPARE... */ + MA = (cmdptr >> 1) & AMASK; /* do an LDB - string 2 */ + if (cmdptr & 01) { + j = GetMap(MA) & 0377; + } else { + j = (GetMap(MA) >> 8) & 0377; + } + cmptr = tabptr + j; /* Translate */ + MA = (cmptr >> 1) & AMASK; + if (cmptr & 01) { + uAC3 = GetMap(MA) & 0377; + } else { + uAC3 = (GetMap(MA) >> 8) & 0377; + } + if (uAC2 > uAC3) { + t = 1; + break; + } + if (uAC2 < uAC3) { + t = -1; + break; + } + } + cmsptr++; + cmdptr++; + cmslen--; + if (cmslen == 0) + break; + } + if (!cmopt) AC[1] = t; + else + AC[1] = 0; + AC[0] = tabaddr & 077777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + if (IR == 0167650) { /* CMT Char move till true */ + tabaddr = indirect(AC[0]); /* Set up length & direction */ + cmslen = AC[1] & 0177777; /* For both source & dest */ + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + while (1) { /* Move loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB */ + if (cmsptr & 01) { + uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + t = M[tabaddr + (uAC2 >> 4)]; /* Test bit table */ + if (t << (uAC2 & 0x0F) & 0100000) /* quit if bit == 1 */ + break; + MA = (cmdptr >> 1) & AMASK; /* do an STB */ + j = GetMap(MA); + if (cmdptr & 01) { + j &= 0177400; + j |= (uAC2 & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (uAC2 & 0377) << 8; + PutMap(MA, j); + } + if (cmslen > 0) { + cmsptr++; + cmdptr++; + cmslen--; + } + if (cmslen < 0) { + cmsptr--; + cmdptr--; + cmslen++; + } + if (cmslen == 0) + break; + } + AC[0] = tabaddr & 077777; + AC[1] = cmslen & 0177777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + + /*********************************************************** + ** "Commercial" instructions. These were in the original ** + ** Eclipse C series, but not part of the later Character ** + ** Instruction Set. ** + ***********************************************************/ + + if ((IR & 0163777) == 0103650) { /* LDI Load Integer */ + unimp(PC); + continue; + } + if ((IR & 0163777) == 0123650) { /* STI Store Integer */ + unimp(PC); + continue; + } + if (IR == 0143650) { /* LDIX Load Int Extended */ + unimp(PC); + continue; + } + if (IR == 0143750) { /* STIX Store Int Extended */ + unimp(PC); + continue; + } + if ((IR & 0163777) == 0143150) { /* FINT Integerize */ + unimp(PC); + continue; + } + if (IR == 0177650) { /* LSN Load Sign */ + unimp(PC); + continue; + } + if (IR == 0173650) { /* EDIT */ + unimp(PC); + continue; + } + } + + /* FPU Instructions */ + + if ((IR & 0103777) == 0102050) { /* FLDS Load FP single */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102150) { /* FLDD Load FP double */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102250) { /* FSTS Store FP single */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102350) { /* FSTD Store FP double */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102450) { /* FLAS Float from AC */ + continue; + } + if ((IR & 0103777) == 0102550) { /* FLMD Float from memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102650) { /* FFAS Fix to AC */ + continue; + } + if ((IR & 0103777) == 0102750) { /* FFMD Fix to Memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0103550) { /* FMOV Move FP */ + continue; + } + if ((IR & 0103777) == 0100050) { /* FAS Add single to AC */ + continue; + } + if ((IR & 0103777) == 0101050) { /* FAMS Add single to memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100150) { /* FAD Add double */ + continue; + } + if ((IR & 0103777) == 0101150) { /* FAMD Add double to memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100250) { /* FSS Sub single to AC */ + continue; + } + if ((IR & 0103777) == 0101250) { /* FSMS Sub single from memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100350) { /* FSD Sub double from AC */ + continue; + } + if ((IR & 0103777) == 0101350) { /* FSMD Sub double from memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100450) { /* FMS Mult single by AC */ + continue; + } + if ((IR & 0103777) == 0101450) { /* FMMS Mult double by memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100550) { /* FMD Mult double by AC */ + continue; + } + if ((IR & 0103777) == 0101550) { /* FMMD Mult double by memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100650) { /* FDS Div single by AC */ + continue; + } + if ((IR & 0103777) == 0101650) { /* FDMS Div double by memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100650) { /* FDD Div double by AC */ + continue; + } + if ((IR & 0103777) == 0101650) { /* FDMD Div double by memory */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0163050) { /* FNEG Negate */ + continue; + } + if ((IR & 0163777) == 0103050) { /* FNOM Normalize*/ + continue; + } + if ((IR & 0163777) == 0143050) { /* FAB Absolute Value*/ + continue; + } + if ((IR & 0163777) == 0123050) { /* FRH Read High Word */ + continue; + } + if ((IR & 0163777) == 0103150) { /* FSCAL Scale */ + continue; + } + if ((IR & 0163777) == 0123150) { /* FEXP Load Exponent */ + continue; + } + if ((IR & 0163777) == 0163150) { /* FHLV Halve */ + continue; + } + if ((IR & 0103777) == 0103450) { /* FCMP FP Compare */ + continue; + } + if ((IR & 0163777) == 0123350) { /* FLST Load Status */ + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0103350) { /* FSST Store Status */ + PC = (PC + 1) & AMASK; + continue; + } + if (IR == 0143350) { /* FTE Trap Enable */ + continue; + } + if (IR == 0147350) { /* FTD Trap Disable */ + continue; + } + if (IR == 0153350) { /* FCLE Clear Errors */ + continue; + } + if (IR == 0163350) { /* FPSH Push State */ + continue; + } + if (IR == 0167350) { /* FPOP Pop State */ + continue; + } + if (IR == 0103250) { /* FNS No Skip */ + continue; + } + if (IR == 0107250) { /* FSA Always Skip */ + continue; + } + if (IR == 0137250) { /* FSGT */ + continue; + } + if (IR == 0123250) { /* FSLT */ + continue; + } + if (IR == 0113250) { /* FSEQ */ + continue; + } + if (IR == 0133250) { /* FSLE */ + continue; + } + if (IR == 0127250) { /* FSGE */ + continue; + } + if (IR == 0117250) { /* FSNE */ + continue; + } + if (IR == 0143250) { /* FSNM */ + continue; + } + if (IR == 0153250) { /* FSNU */ + continue; + } + if (IR == 0163250) { /* FSNO */ + continue; + } + if (IR == 0147250) { /* FSND */ + continue; + } + if (IR == 0157250) { /* FSNUD */ + continue; + } + if (IR == 0167250) { /* FSNOD */ + continue; + } + if (IR == 0173250) { /* FSNUO */ + continue; + } + if (IR == 0177250) { /* FSNER */ + continue; + } + + if (Debug_Flags) { + printf("\n<>\n\r", IR, PC-1); + if (Debug_Flags & 040000) reason = STOP_IBKPT; + } +} + +if (IR == 061777) { /* VCT: Vector on Interrupt */ + int32 stkchg, vtable; + int32 ventry, dctadr; + int32 old40, old41, old42, old43; + + /* Ok, folks, this is one helluva instruction */ + + stkchg = GetMap(PC) & 0100000; /* Save stack change bit */ + vtable = GetMap(PC) & AMASK; /* Address of vector table */ + + iodev = 0; + int_req = (int_req & ~INT_DEV) | /* Do an INTA w/o an accum */ + (dev_done & ~dev_disable); + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + iodev = i; + break; + } + } + + ventry = GetMap(vtable + iodev); /* Get Vector Entry */ + + if (!(ventry & 0100000)) { /* Direct bit = 0? */ + PC = ventry & AMASK; /* YES - Mode A, so JMP */ + continue; + } + + dctadr = ventry & AMASK; /* Get address of DCT entry */ + + if (stkchg) { /* Stack change bit = 1? */ + old40 = GetMap(040); /* Save stack info */ + old41 = GetMap(041); + old42 = GetMap(042); + old43 = GetMap(043); + PutMap(040, GetMap(004)); /* Loc 4 to stack ptr */ + PutMap(042, GetMap(006)); /* Loc 6 to stack limit */ + PutMap(043, GetMap(007)); /* Loc 7 into stack limit */ + PutMap(040, (GetMap(040) + 1)); /* Push old contents on new stk */ + PutMap(GetMap(040) & AMASK, old40); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, old41); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, old42); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, old43); + } + + t = GetMap(dctadr & AMASK); /* Get word 0 of DCT */ + + if (t & 0100000) { /* Push bit set ? */ + PutMap(040, (GetMap(040) + 1)); /* Push "Standard rtn block" */ + PutMap(GetMap(040) & AMASK, AC[0]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, AC[1]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, AC[2]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, AC[3]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, GetMap(0)); + if (GetMap(0) == 0 && Debug_Flags) { + printf("\n<>\n\r", PC); + reason = STOP_IBKPT; + } + if (C) PutMap(GetMap(040) & AMASK, (GetMap(GetMap(040) & AMASK) | 0100000)); + } + + /************************************************************************* + ** At this point, the instruction is not an Eclipse one. Therefore ** + ** decode it as a Nova instruction just like the Nova does. ** + *************************************************************************/ + + AC[2] = dctadr & AMASK; /* DCT Addr into AC2 */ + + PutMap(040, (GetMap(040) + 1)); /* Push pri int mask onto stack */ + PutMap(GetMap(040) & AMASK, pimask); + + AC[0] = GetMap(dctadr + 1) | pimask;/* Build new mask from word 1 of dct */ + PutMap(005, AC[0]); + + mask_out(pimask = AC[0]); /* Do a mask out inst */ + + PC = GetMap(dctadr) & AMASK; /* Finally, JMP to int routine */ + + continue; +} + +/* Memory reference instructions */ + +if (t < 014) { /* mem ref? */ + register int32 src, MA; + MA = IR & 0377; + switch ((IR >> 8) & 03) { /* decode IR<6:7> */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + if (MA & 0200) MA = 077400 | MA; + MA = (MA + PC - 1) & AMASK; + break; + case 2: /* AC2 relative */ + if (MA & 0200) MA = 077400 | MA; + MA = (MA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + if (MA & 0200) MA = 077400 | MA; + MA = (MA + AC[3]) & AMASK; + break; + } + if (IR & 002000) { /* indirect? */ + for (i = 0; i < (ind_max * 2); i++) { /* count indirects */ + if ((MA & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) + 1) & 0177777)); + else if ((MA & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) - 1) & 0177777)); + else MA = GetMap(MA & AMASK); + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((MA & 0100000) == 0) break; + if (i >= ind_max && (MapStat & 010) && Usermap) break; + } + if (i >= ind_max) { + if ((MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + continue; + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND; + break; + } + } + } + +/* Memory reference, continued */ + + switch (t) { /* decode IR<1:4> */ + case 001: /* JSR */ + AC[3] = PC; + case 000: /* JMP */ + old_PC = PC; + PC = MA; + break; + case 002: /* ISZ */ + src = (GetMap(MA) + 1) & 0177777; + if (MEM_ADDR_OK (MA)) PutMap(MA, src); + if (src == 0) PC = (PC + 1) & AMASK; + break; + case 003: /* DSZ */ + src = (GetMap(MA) - 1) & 0177777; + if (MEM_ADDR_OK (MA)) PutMap(MA, src); + if (src == 0) PC = (PC + 1) & AMASK; + break; + case 004: /* LDA 0 */ + if (SingleCycle) Usermap = SingleCycle; + AC[0] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 005: /* LDA 1 */ + if (SingleCycle) Usermap = SingleCycle; + AC[1] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 006: /* LDA 2 */ + if (SingleCycle) Usermap = SingleCycle; + AC[2] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 007: /* LDA 3 */ + if (SingleCycle) Usermap = SingleCycle; + AC[3] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 010: /* STA 0 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[0]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 011: /* STA 1 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[1]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 012: /* STA 2 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[2]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 013: /* STA 3 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[3]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; } /* end switch */ + } /* end mem ref */ + +/* Operate instruction */ + +else if (t & 020) { /* operate? */ + register int32 src, srcAC, dstAC; + srcAC = (t >> 2) & 3; /* get reg decodes */ + dstAC = t & 03; + switch ((IR >> 4) & 03) { /* decode IR<10:11> */ + case 0: /* load */ + src = AC[srcAC] | C; + break; + case 1: /* clear */ + src = AC[srcAC]; + break; + case 2: /* set */ + src = AC[srcAC] | 0200000; + break; + case 3: /* complement */ + src = AC[srcAC] | (C ^ 0200000); + break; } /* end switch carry */ + switch ((IR >> 8) & 07) { /* decode IR<5:7> */ + case 0: /* COM */ + src = src ^ 0177777; + break; + case 1: /* NEG */ + src = ((src ^ 0177777) + 1) & 0377777; + break; + case 2: /* MOV */ + break; + case 3: /* INC */ + src = (src + 1) & 0377777; + break; + case 4: /* ADC */ + src = ((src ^ 0177777) + AC[dstAC]) & 0377777; + break; + case 5: /* SUB */ + src = ((src ^ 0177777) + AC[dstAC] + 1) & 0377777; + break; + case 6: /* ADD */ + src = (src + AC[dstAC]) & 0377777; + break; + case 7: /* AND */ + src = src & (AC[dstAC] | 0200000); + break; } /* end switch oper */ + +/* Operate, continued */ + + switch ((IR >> 6) & 03) { /* decode IR<8:9> */ + case 0: /* nop */ + break; + case 1: /* L */ + src = ((src << 1) | (src >> 16)) & 0377777; + break; + case 2: /* R */ + src = ((src >> 1) | (src << 16)) & 0377777; + break; + case 3: /* S */ + src = ((src & 0377) << 8) | ((src >> 8) & 0377) | + (src & 0200000); + break; } /* end switch shift */ + switch (IR & 07) { /* decode IR<13:15> */ + case 0: /* nop */ + break; + case 1: /* SKP */ + PC = (PC + 1) & AMASK; + break; + case 2: /* SZC */ + if (src < 0200000) PC = (PC + 1) & AMASK; + break; + case 3: /* SNC */ + if (src >= 0200000) PC = (PC + 1) & AMASK; + break; + case 4: /* SZR */ + if ((src & 0177777) == 0) PC = (PC + 1) & AMASK; + break; + case 5: /* SNR */ + if ((src & 0177777) != 0) PC = (PC + 1) & AMASK; + break; + case 6: /* SEZ */ + if (src <= 0200000) PC = (PC + 1) & AMASK; + break; + case 7: /* SBN */ + if (src > 0200000) PC = (PC + 1) & AMASK; + break; } /* end switch skip */ + if ((IR & 000010) == 0) { /* load? */ + AC[dstAC] = src & 0177777; + C = src & 0200000; } /* end if load */ + } /* end if operate */ + +/* IOT instruction */ + +else { /* IOT */ + register int32 dstAC, pulse, code, device, iodata; + char pulcode[4]; + + if ((MapStat & 0100) && Usermap) { /* We are in LEF Mode */ + AC[(IR >> 11) & 3] = LEFmode(PC - 1, (IR >> 8) & 3, IR & 0377, IR & 02000); + if (Debug_Flags & 020000) { + printf("\n\r<>\n\r", PC-1); + reason = STOP_IBKPT; + } + continue; + } + + dstAC = t & 03; /* decode fields */ + if ((MapStat & 040) && Usermap) { /* I/O protection fault */ + Fault = 020000; + continue; + } + code = (IR >> 8) & 07; + pulse = (IR >> 6) & 03; + device = IR & 077; + if (Debug_Flags && device == 0) { + printf("\n\r<>\n\r", PC-1); + reason = STOP_IBKPT; + } + if ((Debug_Flags & 0100) && (device == (Debug_Flags & 077))) { + printf("\n\r<>\n\r", device); + reason = STOP_IBKPT; + } + if (code == ioSKP) { /* IO skip? */ + switch (pulse) { /* decode IR<8:9> */ + case 0: /* skip if busy */ + if ((device == 077)? (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 == 077)? (int_req & INT_ION) == 0: + (dev_busy & dev_table[device].mask) == 0) + PC = (PC + 1) & AMASK; + break; + case 2: /* skip if done */ + if ((device == 077)? pwr_low != 0: + (dev_done & dev_table[device].mask) != 0) + PC = (PC + 1) & AMASK; + break; + case 3: /* skip if not done */ + if ((device == 077)? 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_CPU) { /* CPU control */ + switch (code) { /* decode IR<5:7> */ + case ioNIO: /* Get CPU ID */ + switch (model) { + case 280: /* S280 */ + AC[0] = 021102; + break; + case 380: + AC[0] = 013212; /* C380 */ + break; + default: + break; + } + break; /* Otherwise no-op */ + 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 IORST */ + reset_all (0); /* reset devices */ + Usermap = 0; /* reset MAP */ + MapStat &= 04; /* Reset MAP status */ + MapIntMode = 0; + Inhibit = 0; + Map31 = 037; + Check = SingleCycle = 0; + Fault = 0; + 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 if (device == DEV_ECC) { + switch (code) { + case ioDIA: /* Read Fault Address */ + AC[dstAC] = 0; + break; + case ioDIB: /* Read fault code */ + AC[dstAC] = 0; + break; + case ioDOA: /* Enable ERCC */ + break; } + } + + else if (device == DEV_MAP) { /* MAP control */ + switch (code) { /* decode IR<5:7> */ + case ioNIO: /* No I/O -- Single */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o NIO %o (No I/O, clear faults)\n", PC-1, dstAC); + MapStat &= ~036000; /* NIO Clears all faults */ + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o NIO %o (No I/O, clear faults) NO EXEC(User mode)\n", PC-1, dstAC); + } + break; + case ioDIA: /* Read map status */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIA %o=%o (Read Map Status)\n", PC-1, dstAC, MapStat); + AC[dstAC] = MapStat & 0xFFFE; + if (MapIntMode & 1) /* Bit 15 is mode asof last int */ + AC[dstAC] |= 1; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIA %o=%o (Read Map Status) NO EXEC(User mode)\n", PC-1, dstAC, MapStat); + } + break; + case ioDOA: /* Load map status */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOA %o=%o (Load Map Status)\n", PC-1, dstAC, AC[dstAC]); + MapStat = AC[dstAC]; + MapIntMode = 0; + Enable = 1; + if (MapStat & 04) Enable = 2; + Check &= ~01600; + Check |= MapStat & 01600; + if (MapStat & 1) + Inhibit = 2; /* Inhibit interrupts */ + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOA %o=%o (Load Map Status) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + case ioDIB: /* not used */ + break; + case ioDOB: /* map block 31 */ + if (!Usermap || !(MapStat && 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOB %o=%o (Map Blk 31)\n", PC-1, dstAC, AC[dstAC]); + Map31 = AC[dstAC] & PAGEMASK; + MapStat &= ~02000; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOB %o=%o (Map Blk 31) NO EXEC (User Mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + case ioDIC: /* Page Check */ + if (!Usermap || !(MapStat & 0140)) { + switch ((Check>>7) & 07) { + case 0: i=1; break; + case 1: i=6; break; + case 2: i=2; break; + case 3: i=7; break; + case 4: i=0; break; + case 5: i=4; break; + case 6: i=3; break; + case 7: i=5; break; + default: break; + } + j = (Check >> 10) & 037; + AC[dstAC] = Map[i][j] & 0101777; + AC[dstAC] |= ((Check << 5) & 070000); + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIC %o=%o (Page Check)\n", PC-1, dstAC, AC[dstAC]); + MapStat &= ~02000; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIC %o=%o (Page Check) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + case ioDOC: /* Init Page Check */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOC %o=%o (Init Pg Chk)\n", PC-1, dstAC, AC[dstAC]); + Check = AC[dstAC]; + MapStat &= ~01600; + MapStat |= (Check & 01600); + MapStat &= ~02000; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOC %o=%o (Init Pg Chk) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + } /* end switch code */ + switch (pulse) { + case iopP: + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o xxxP (Single Cycle)\n", PC-1); + if (Usermap) { + MapStat &= 0177776; + Usermap = 0; + Inhibit = 0; + } else { + SingleCycle = Enable; + Inhibit = 1; /* Inhibit interrupts */ + } + break; } + } /* end CPU control */ + else if ((dev_table[device].mask == 0) || + (dev_table[device].mask & iot_enb)) { /* normal device */ + iodata = dev_table[device].routine (pulse, code, AC[dstAC]); + reason = iodata >> IOT_V_REASON; + if (code & 1) AC[dstAC] = iodata & 0177777; + if ((Debug_Flags & 077) == device && Debug_Flags != 0) { + strcpy(pulcode, ""); + switch (pulse) { + case iopP: + strcpy(pulcode, "P"); + break; + case iopS: + strcpy(pulcode, "S"); + break; + case iopC: + strcpy(pulcode, "C"); + break; + default: + break; + } + switch(code) { + case ioNIO: + fprintf(Trace, "[%o] %o NIO%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + case ioDIA: + fprintf(Trace, "[%o] %o DIA%s %o\n", device, PC-1, pulcode, iodata); + break; + case ioDIB: + fprintf(Trace, "[%o] %o DIB%s %o\n", device, PC-1, pulcode, iodata); + break; + case ioDIC: + fprintf(Trace, "[%o] %o DIC%s %o\n", device, PC-1, pulcode, iodata); + break; + case ioDOA: + fprintf(Trace, "[%o] %o DOA%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + case ioDOB: + fprintf(Trace, "[%o] %o DOB%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + case ioDOC: + fprintf(Trace, "[%o] %o DOC%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + default: + break; + } /* end switch */ + } /* end if debug */ + } /* end else if */ + else reason = stop_dev; } /* end if IOT */ +} /* end while */ + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* Computes and returns a 16-bit effective address, given a + program counter, index, and a displacement. +*/ + +int32 effective(int32 PC, int32 index, int32 disp) +{ + register int32 i, MA; + MA = disp & 077777; + switch (index) { /* decode IR<6:7> */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + MA = (MA + PC) & AMASK; + break; + case 2: /* AC2 relative */ + MA = (MA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + MA = (MA + AC[3]) & AMASK; + break; + } /* end switch mode */ + + if (disp & 0100000) { /* indirect? */ + for (i = 0; i < ind_max * 2; i++) { /* count indirects */ + MA = GetMap(MA & AMASK); + if (SingleCycle) Usermap = 0; + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((MA & 0100000) == 0) break; + if ((MapStat & 010) && Usermap && i >= ind_max) break; + } + if (i >= ind_max && (MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND_INT; /* Stop machine */ + } + } + return (MA & AMASK); +} + +/* Computes and returns a 16-bit effective address, given a + program counter, index, and a displacement. This is a + version supporting the LEF map mode instruction, as + opposed to the ELEF instruction. +*/ + +int32 LEFmode(int32 PC, int32 index, int32 disp, int32 indirect) +{ + register int32 i, MA; + int16 sMA; + MA = disp & 077777; + switch (index) { /* decode IR<6:7> */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + MA = (MA + PC) & AMASK; + break; + case 2: /* AC2 relative */ + sMA = MA; + if (MA & 0200) sMA |= 0xff00; + MA = (sMA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + sMA = MA; + if (MA & 0200) sMA |= 0xff00; + MA = (sMA + AC[3]) & AMASK; + break; + } /* end switch mode */ + + if (indirect) { /* indirect? */ + for (i = 0; i < (ind_max * 2); i++) { /* count indirects */ + if ((MA & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) + 1) & 0177777)); + else if ((MA & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) - 1) & 0177777)); + else MA = GetMap(MA & AMASK); + if (SingleCycle) Usermap = 0; + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((MA & 0100000) == 0) break; + if ((MapStat & 010) && Usermap && i >= ind_max) break; + } + if (i >= ind_max && (MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND_INT; /* Stop machine */ + } + } + return (MA & AMASK); +} + +/* Computes a "Byte pointer" for the Character Instruction set */ +/* This address in 'PC' must point to the displacement word of the instruction */ + +int32 Bytepointer(int32 PC, int32 index) +{ + register int32 MA; + switch (index) { /* decode IR<6:7> */ + case 0: /* page zero */ + MA = 0; + break; + case 1: /* PC relative */ + MA = PC & AMASK; + break; + case 2: /* AC2 relative */ + MA = AC[2] & AMASK; + break; + case 3: /* AC3 relative */ + MA = AC[3] & AMASK; + break; + } /* end switch mode */ + MA = (MA * 2) & 0177777; + MA = MA + M[PC]; + return (MA & 0177777); +} + +/* Given an address, returns either that address if bit 0 is 0, or + or follows an indirection chain until bit 0 is 0 +*/ + +int32 indirect(int32 d) +{ + int i; + + if (d & 0100000) { /* indirect? */ + for (i = 0; i < ind_max * 2; i++) { /* count indirects */ + if ((d & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) + d = (PutMap(d & AMASK, ((GetMap(d & AMASK) + 1) & 0177777))); + else if ((d & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) + d = (PutMap(d & AMASK, ((GetMap(d & AMASK) - 1) & 0177777))); + else d = GetMap(d & AMASK); + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((d & 0100000) == 0) break; + if ((MapStat & 010) && Usermap && i >= ind_max) break; + } + if (i >= ind_max && (MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND; /* Stop machine */ + } + } + return (d); +} + +/* Push a standard return block onto the stack */ + +int32 pushrtn(int32 pc) +{ + int32 t; + + t = (GetMap(040) + 1) & AMASK; + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, pc); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + return 0; +} + + +/* Eclipse memory get/put - uses MAP if enabled */ + +int32 GetMap(int32 addr) +{ + int32 page; + t_addr paddr; + + switch (Usermap) { + case 0: + if (addr < 076000) + return M[addr]; + paddr = ((Map31 & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 1: + page = (addr >> 10) & 037; + paddr = ((Map[1][page] & 01777) << 10) | (addr & 001777); + if (Map[1][page] == INVALID && !SingleCycle) + Fault = 0100000; + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 2: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (Map[2][page] == INVALID && !SingleCycle) + Fault = 0100000; + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 6: + page = (addr >> 10) & 037; + paddr = ((Map[6][page] & PAGEMASK) << 10) | (addr & 001777); + if (Map[6][page] == INVALID && !SingleCycle) + Fault = 0100000; + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 7: + page = (addr >> 10) & 037; + paddr = ((Map[7][page] & PAGEMASK) << 10) | (addr & 001777); + if (Map[7][page] == INVALID && !SingleCycle) + Fault = 0100000; + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + default: + printf("\n\r<>\n\r"); + return M[addr]; + break; + } +} + +int32 PutMap(int32 addr, int32 data) +{ + int32 page; + t_addr paddr; + + switch (Usermap) { + case 0: + if (addr < 076000) { + M[addr] = data; + return (data); + } + paddr = ((Map31 & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) M[paddr] = data; + break; + case 1: + page = (addr >> 10) & 037; + paddr = ((Map[1][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[1][page] & 0100000) && (MapStat & 020)) || Map[1][page] == INVALID) Fault = 010000; + else if (paddr < MEMSIZE) M[paddr] = data; + break; + case 2: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[2][page] & 0100000) && (MapStat & 020)) || Map[2][page] == INVALID) Fault = 010000; + else if (paddr < MEMSIZE) M[paddr] = data; + break; + case 6: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[6][page] & 0100000) && (MapStat & 020)) || Map[6][page] == INVALID) Fault = 010000; + else if (paddr < MEMSIZE) M[paddr] = data; + break; + case 7: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[7][page] & 0100000) && (MapStat & 020)) || Map[7][page] == INVALID) Fault = 010000; + else if (paddr < MEMSIZE) M[paddr] = data; + break; + default: + M[addr] = data; + break; + } + return (data); +} + +#if 0 +int16 GetDCHMap(int32 map, int32 addr) +{ + t_addr paddr; + if (!(MapStat & 02)) return M[addr]; + paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) + return M[paddr]; + return (0); +} + +int16 PutDCHMap(int32 map, int32 addr, int16 data) +{ + t_addr paddr; + if (!(MapStat & 02)) { + M[addr] = data; + return (data); + } + paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) + M[paddr] = data; + return (data); +} +#endif + +/* Given a map number and a logical, returns the physical address, unless + the map is not active, in which case logical = physical. This is + used primarily by the I/O routines to map data channel read/writes. +*/ + +int32 MapAddr(int32 map, int32 addr) +{ + int32 paddr; + if ((map == 0 || map > 2) && !(MapStat & 02)) return addr; + if (map > 0 && map < 3 && Usermap == 0) return addr; + paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); + return paddr; +} + +/* Loads a word into the Eclipse Maps */ + +int32 LoadMap(int32 w) +{ + int32 m; + + m = (w >> 10) & 037; + switch ((MapStat >> 7) & 07) { + case 0: /* Load user A Map */ + Map[1][m] = w & MAPMASK; + break; + case 1: /* Load user C Map */ + Map[6][m] = w & MAPMASK; + break; + case 2: /* Load user B Map */ + Map[2][m] = w & MAPMASK; + break; + case 3: /* Load user D Map */ + Map[7][m] = w & MAPMASK; + break; + case 4: /* Load DCH A Map */ + Map[0][m] = w & MAPMASK; + break; + case 5: /* Load DCH C Map */ + Map[4][m] = w; + break; + case 6: /* Load DCH B Map */ + Map[3][m] = w; + break; + case 7: /* Load DCH D Map */ + Map[5][m] = w; + break; + default: + break; + } + return 0; +} + +/* Displays an error on a unimplemented (in this sim) instr. */ + +int32 unimp(int32 PC) +{ + if (Debug_Flags) + printf("\n\r\007<<>>\n\r", PC - 1, GetMap(PC - 1)); + return 0; +} + +/* 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) +{ +int_req = int_req & ~INT_ION; +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] & 0177777; +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 & 0177777; +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; +} + +/* MAP device services */ + +t_stat map_svc (UNIT *uptr) +{ +return SCPE_OK; +} + +/* Map examine */ + +t_stat map_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((addr & 077) >= 037 || addr > 737) return SCPE_NXM; +uptr->u4 = -2; /* signal to print_sys in eclipse_sys.c: do not map */ +if (vptr != NULL) *vptr = Map[(addr >> 6) & 3][addr & 037] & 0177777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat map_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((addr & 077) >= 037 || addr > 0737) return SCPE_NXM; +uptr->u4 = -2; /* signal to print_sys in eclipse_sys.c: do not map */ +Map[(addr >> 6) & 3][addr & 037] = val & 0177777; +return SCPE_OK; +} + +/* Bootstrap routine for CPU */ + +#define BOOT_START 00000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 062677, /* IORST ;Reset all I/O */ + 060477, /* READS 0 ;Read SR into AC0 */ + 024026, /* LDA 1,C77 ;Get dev mask */ + 0107400, /* AND 0,1 ;Isolate dev code */ + 0124000, /* COM 1,1 ;- device code - 1 */ + 010014, /* LOOP: ISZ OP1 ;Device code to all */ + 010030, /* ISZ OP2 ;I/O instructions */ + 010032, /* ISZ OP3 */ + 0125404, /* INC 1,1,SZR ;done? */ + 000005, /* JMP LOOP ;No, increment again */ + 030016, /* LDA 2,C377 ;place JMP 377 into */ + 050377, /* STA 2,377 ;location 377 */ + 060077, /* OP1: 060077 ;start device (NIOS 0) */ + 0101102, /* MOVL 0,0,SZC ;Test switch 0, low speed? */ + 000377, /* C377: JMP 377 ;no - jmp 377 & wait */ + 004030, /* LOOP2: JSR GET+1 ;Get a frame */ + 0101065, /* MOVC 0,0,SNR ;is it non-zero? */ + 000017, /* JMP LOOP2 ;no, ignore */ + 004027, /* LOOP4: JSR GET ;yes, get full word */ + 046026, /* STA 1,@C77 ;store starting at 100 */ + /* ;2's complement of word ct */ + 010100, /* ISZ 100 ;done? */ + 000022, /* JMP LOOP4 ;no, get another */ + 000077, /* C77: JMP 77 ;yes location ctr and */ + /* ;jmp to last word */ + 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */ + /* OP2: */ + 063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */ + 000030, /* JMP LOOP3 ;no -- wait */ + 060477, /* OP3: 060477 ;y--read in ac0 (DIAS 0,0) */ + 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */ + 000030, /* JMP LOOP3 ;no go back after it */ + 0125300, /* MOVS 1,1 ;yes swap them */ + 001400, /* JMP 0,3 ;rtn with full word */ + 0 /* 0 ;padding */ +}; + +t_stat cpu_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; +} + +int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 AC2, int32 AC3, int32 flags) +{ + hpc[hnext] = PC & 0xffff; + hinst[hnext] = inst & 0xffff; + hinst2[hnext] = inst2 & 0xffff; + hac0[hnext] = AC0 & 0xffff; + hac1[hnext] = AC1 & 0xffff; + hac2[hnext] = AC2 & 0xffff; + hac3[hnext] = AC3 & 0xffff; + hflags[hnext] = flags & 0xffff; + hnext++; + if (hnext >= hmax) { + hwrap = 1; + hnext = 0; + } + return 0; +} + +int32 Debug_Dump(UNIT *uptr, int32 value) +{ + char debmap[4], debion[4]; + t_value simeval[20]; + int debcar; + FILE *Dumpf; + int start, end, ctr; + int count = 0; + + if (!Debug_Flags || Debug_Flags & 0100000) { + printf("History was not logged. Deposit a non-zero value\n"); + printf("in DEBUG with bit 0 being 1 to build history.\n"); + return SCPE_OK; + } + Dumpf = fopen("history.log", "w"); + if (!hwrap) { + start = 0; + end = hnext; + } else { + start = hnext; + end = hnext - 1; + if (end < 0) end = hmax; + } + ctr = start; + while (1) { + if (ctr == end) + break; + count++; + strcpy(debion, " "); + strcpy(debmap, " "); + debcar = 0; + if (hflags[ctr] & 0x80) { + fprintf(Dumpf, "--------- Interrupt %o (%o) to %6o ---------\n", + hinst[ctr], hac0[ctr], hac1[ctr]); + } else { + if (hflags[ctr] & 0x01) debcar = 1; + if (hflags[ctr] & 0x02) strcpy(debion, "I"); + if (hflags[ctr] & 0x04) strcpy(debmap, "A"); + if (hflags[ctr] & 0x08) strcpy(debmap, "B"); + if (hflags[ctr] & 0x10) strcpy(debmap, "C"); + if (hflags[ctr] & 0x20) strcpy(debmap, "D"); + fprintf(Dumpf, "%s%s%06o acs: %06o %06o %06o %06o %01o ", + debion, debmap, hpc[ctr], hac0[ctr], hac1[ctr], hac2[ctr], + hac3[ctr], debcar); + simeval[0] = hinst[ctr]; + simeval[1] = hinst2[ctr]; + fprint_sym (Dumpf, hpc[ctr], simeval, NULL, SWMASK('M')); + fprintf(Dumpf, "\n"); + } + ctr++; + if (ctr > hmax) + ctr = 0; + } + fclose(Dumpf); + printf("\n%d records dumped to history.log\n"); + return SCPE_OK; +} diff --git a/eclipse_tt.c b/eclipse_tt.c new file mode 100644 index 00000000..4b24279e --- /dev/null +++ b/eclipse_tt.c @@ -0,0 +1,382 @@ +/* eclipse_tt.c: Eclipse console terminal simulator + + Copyright (c) 1993-1997, + Robert M Supnik, Digital Equipment Corporation + Commercial use prohibited + + 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; +/* --- BEGIN MODIFIED CODE --- */ +if (tti_unit.flags & UNIT_DASHER) /* translate input */ + translate_in(); +/* --- END MODIFIED CODE --- */ +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; +} + +/* -------------------- BEGIN INSERTION -----------------------*/ + +int curpos = 0; /* used by translate_out() */ +int row = 0, col = 0; /* ditto - for cursor positioning */ +int spec200 = 0; /* signals next char is 'special' */ + +/* Translation: Vt100 input to D200 keycodes. */ + +int32 translate_in() +{ + char rev = 0; + + if (tti_unit.buf == '\r') + rev = '\n'; + if (tti_unit.buf == '\n') + rev = '\r'; + if (rev) + tti_unit.buf = rev; +} + +/* -------------------- END INSERTION -----------------------*/ + +/* 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; +/* --- BEGIN MODIFIED CODE --- */ +if (tto_unit.flags & UNIT_DASHER) { + if ((temp = translate_out(c)) != SCPE_OK) return temp; +} else { + if ((temp = sim_putchar (c)) != SCPE_OK) return temp; + tto_unit.pos = tto_unit.pos + 1; +} +/* --- END MODIFIED CODE --- */ +return SCPE_OK; +} + +/* -------------------- BEGIN INSERTION -----------------------*/ + +/* Translation routine - D200 screen controls to VT-100 controls. */ + +int32 translate_out(int32 c) +{ + int32 temp; + char outstr[32]; + + if (spec200 == 1) { /* Special terminal control seq */ + spec200 = 0; + switch (c) { + case 'C': /* read model ID */ + return SCPE_OK; + case 'E': /* Reverse video off */ + return SCPE_OK; + case 'D': /* Reverse video on */ + return SCPE_OK; + default: + return SCPE_OK; + } + } + if (curpos == 1) { /* 2nd char of cursor position */ + col = c & 0x7f; + curpos++; + return (SCPE_OK); + } + if (curpos == 2) { /* 3rd char of cursor position */ + row = c & 0x7f; + curpos = 0; + sprintf(outstr, "\033[%d;%dH", row+1, col+1); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + } + switch (c) { /* Single-char command or data */ + case 003: /* Blink enable */ + break; + case 004: /* Blink disable */ + break; + case 005: /* Read cursor address */ + break; + case 010: /* Cursor home */ + sprintf(outstr, "\033[1;1H"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row = col = 0; + return (SCPE_OK); + case 012: /* Newline */ + if ((temp = sim_putchar('\r')) != SCPE_OK) return temp; + tto_unit.pos += 1; + if ((temp = sim_putchar(c)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col = 1; + row++; + if (row > 24) row = 1; + return (SCPE_OK); + case 013: /* Erase EOL */ + sprintf(outstr, "\033[K"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 014: /* Erase screen */ + sprintf(outstr, "\033[1;1H\033[2J"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row = col = 0; + return (SCPE_OK); + case 015: /* CR */ + if ((temp = sim_putchar(c)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col = 1; + return (SCPE_OK); + case 016: /* Blink On */ + sprintf(outstr, "\033[5m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 017: /* Blink off */ + sprintf(outstr, "\033[25m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 020: /* Write cursor address */ + curpos = 1; + return SCPE_OK; + case 024: /* underscore on */ + sprintf(outstr, "\033[4m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 025: /* underscore off */ + sprintf(outstr, "\033[24m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + break; + case 027: /* cursor up */ + sprintf(outstr, "\033[A"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row--; + if (row < 1) row = 24; + return (SCPE_OK); + case 030: /* cursor right */ + sprintf(outstr, "\033[C"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + col++; + if (col > 80) { + col = 1; + row++; + if (row > 24) row = 1; + } + return (SCPE_OK); + case 031: /* Cursor left */ + sprintf(outstr, "\033[D"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col--; + if (col < 1) { + col = 80; + row--; + if (row < 1) row = 24; + } + return (SCPE_OK); + case 032: /* Cursor down */ + sprintf(outstr, "\033[B"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row++; + if (row > 24) row = 1; + return (SCPE_OK); + case 034: /* Dim on */ + sprintf(outstr, "\033[22m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 035: /* Dim off */ + sprintf(outstr, "\033[1m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 036: /* Special sequence */ + spec200 = 1; + return SCPE_OK; + default: /* ..A character of data */ + if ((temp = sim_putchar(c)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col++; + if (col > 80) { + col = 1; + row++; + if (row > 24) row = 24; + } + return (SCPE_OK); + } + return SCPE_OK; +} + +int32 putseq(char *seq) +{ + int i, len, temp; + + len = strlen(seq); + for (i = 0; i < len; i++) { + if ((temp = sim_putchar(seq[i])) != SCPE_OK) + return temp; + tto_unit.pos += 1; + } + return SCPE_OK; +} + +/* -------------------- END INSERTION -----------------------*/ + +/* 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/hp2100_cpu.c b/hp2100_cpu.c index c49cd31a..9659109f 100644 --- a/hp2100_cpu.c +++ b/hp2100_cpu.c @@ -1,6 +1,6 @@ /* hp2100_cpu.c: HP 2100 CPU simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -246,7 +246,7 @@ #define UNIT_V_21MX (UNIT_V_UF + 2) /* 21MX vs 2100 */ #define UNIT_21MX (1 << UNIT_V_21MX) -unsigned int16 M[MAXMEMSIZE] = { 0 }; /* memory */ +uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 saved_AR = 0; /* A register */ int32 saved_BR = 0; /* B register */ int32 PC = 0; /* P register */ diff --git a/hp2100_defs.h b/hp2100_defs.h index 676c668f..e70f13f6 100644 --- a/hp2100_defs.h +++ b/hp2100_defs.h @@ -1,6 +1,6 @@ /* hp2100_defs.h: HP 2100 simulator definitions - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/hp2100_dp.c b/hp2100_dp.c index 7c35294a..28006852 100644 --- a/hp2100_dp.c +++ b/hp2100_dp.c @@ -1,6 +1,6 @@ /* hp2100_dp.c: HP 2100 disk pack simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -100,7 +100,7 @@ STA_HUNT + STA_SKI + STA_SKE + STA_NRDY + STA_EOC + \ STA_FLG + STA_DTE) -extern unsigned int16 M[]; +extern uint16 M[]; extern struct hpdev infotab[]; extern int32 PC; extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; @@ -115,7 +115,7 @@ int32 dpc_rarc = 0, dpc_rarh = 0, dpc_rars = 0; /* record addr */ int32 dpd_obuf = 0, dpd_ibuf = 0; /* dch buffers */ int32 dpc_obuf = 0; /* cch buffers */ int32 dp_ptr = 0; /* buffer ptr */ -unsigned int16 dp_buf[DP_NUMWD]; /* sector buffer */ +uint16 dp_buf[DP_NUMWD]; /* sector buffer */ t_stat dpc_svc (UNIT *uptr); t_stat dpc_reset (DEVICE *dptr); @@ -541,7 +541,7 @@ dpc_rarc = dpc_rarh = dpc_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 = 1; /* set fbf */ -infotab[inDPC].flg = infotab[inDPD].flg = 1; /* set fbf */ +infotab[inDPC].flg = infotab[inDPD].flg = 1; /* set flg */ for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */ sim_cancel (&dpc_unit[i]); /* cancel activity */ dpc_unit[i].FNC = 0; /* clear function */ @@ -602,4 +602,4 @@ t_stat dpd_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) if (addr >= DP_NUMWD) return SCPE_NXM; dp_buf[addr] = val & DMASK; return SCPE_OK; -} \ No newline at end of file +} diff --git a/hp2100_lp.c b/hp2100_lp.c index 8e66a38b..faaed1d6 100644 --- a/hp2100_lp.c +++ b/hp2100_lp.c @@ -1,6 +1,6 @@ /* hp2100_lp.c: HP 2100 line printer simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/hp2100_mt.c b/hp2100_mt.c index 984638a0..ddfd1b65 100644 --- a/hp2100_mt.c +++ b/hp2100_mt.c @@ -1,6 +1,6 @@ /* hp2100_mt.c: HP 2100 magnetic tape simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -78,7 +78,7 @@ #define STA_PAR 0002 /* parity error */ #define STA_BUSY 0001 /* busy */ -extern unsigned int16 M[]; +extern uint16 M[]; extern struct hpdev infotab[]; extern int32 PC; extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; @@ -89,7 +89,7 @@ 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 mt_buf[DBSIZE] = { 0 }; /* data buffer */ +uint8 mt_buf[DBSIZE] = { 0 }; /* data buffer */ t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */ static const int32 mtc_cmd[] = { FNC_WC, FNC_RC, FNC_GAP, FNC_FSR, FNC_BSR, FNC_REW, FNC_RWS, FNC_WFM }; @@ -468,4 +468,4 @@ t_stat mtd_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) if (addr >= DBSIZE) return SCPE_NXM; mt_buf[addr] = val & 0377; return SCPE_OK; -} \ No newline at end of file +} diff --git a/hp2100_stddev.c b/hp2100_stddev.c index f3dedbe9..daf43227 100644 --- a/hp2100_stddev.c +++ b/hp2100_stddev.c @@ -1,6 +1,6 @@ /* hp2100_stddev.c: HP2100 standard devices - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -49,7 +49,7 @@ #define CLK_V_ERROR 4 /* clock overrun */ #define CLK_ERROR (1 << CLK_V_ERROR) -extern unsigned int16 M[]; +extern uint16 M[]; extern int32 PC; extern int32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2]; extern UNIT cpu_unit; diff --git a/hp2100_sys.c b/hp2100_sys.c index 3cb00d39..1111b3e4 100644 --- a/hp2100_sys.c +++ b/hp2100_sys.c @@ -1,6 +1,6 @@ /* hp2100_sys.c: HP 2100 simulator interface - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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-Mar-01 RMS Revised load/dump interface (again) 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 @@ -39,7 +40,7 @@ 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[]; +extern uint16 M[]; /* SCP data structures and interface routines @@ -98,7 +99,7 @@ if ((c2 = fgetc (fileref)) == EOF) return -1; return ((c1 & 0377) << 8) | (c2 & 0377); } -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 origin, csum, zerocnt, count, word, i; diff --git a/i1401_cd.c b/i1401_cd.c index 06c2edf8..3b5f7aaa 100644 --- a/i1401_cd.c +++ b/i1401_cd.c @@ -1,6 +1,6 @@ /* i1401_cd.c: IBM 1402 card reader/punch - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -34,12 +34,14 @@ Cards are represented as ASCII text streams terminated by newlines. This allows cards to be created and edited as normal files. + + 13-Apr-01 RMS Revised for register arrays */ #include "i1401_defs.h" #include -extern unsigned char M[]; +extern uint8 M[]; extern int32 ind[64], ssa, iochk; extern char bcd_to_ascii[64]; extern char ascii_to_bcd[128]; @@ -49,9 +51,6 @@ 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 @@ -70,7 +69,7 @@ REG cdr_reg[] = { { 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 }, + { BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) }, { NULL } }; DEVICE cdr_dev = { diff --git a/i1401_cpu.c b/i1401_cpu.c index b1b5e218..ad7e37fe 100644 --- a/i1401_cpu.c +++ b/i1401_cpu.c @@ -1,6 +1,6 @@ /* i1401_cpu.c: IBM 1401 CPU simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -124,7 +124,7 @@ oldIS = saved_IS; \ IS = AS; -unsigned char M[MAXMEMSIZE] = { 0 }; /* main memory */ +uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */ int32 saved_IS = 0; /* saved IS */ int32 AS = 0; /* AS */ int32 BS = 0; /* BS */ diff --git a/i1401_defs.h b/i1401_defs.h index a2a07baf..d3c68c41 100644 --- a/i1401_defs.h +++ b/i1401_defs.h @@ -1,6 +1,6 @@ /* i1401_defs.h: IBM 1401 simulator definitions - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/i1401_iq.c b/i1401_iq.c index c6370afb..3ef3c121 100644 --- a/i1401_iq.c +++ b/i1401_iq.c @@ -1,6 +1,6 @@ /* i1401_iq.c: IBM 1407 inquiry terminal - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -30,7 +30,7 @@ #include extern volatile int32 stop_cpu; -extern unsigned char M[]; +extern uint8 M[]; extern int32 BS, iochk, ind[64]; extern char ascii_to_bcd[128], bcd_to_ascii[64]; extern UNIT cpu_unit; @@ -38,7 +38,6 @@ 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); @@ -117,7 +116,6 @@ case BCD_W: /* output */ return SCPE_OK; default: return STOP_INVM; } /* invalid mod */ -return SCPE_OK; } /* Unit service - polls for WRU or inquiry request */ diff --git a/i1401_lp.c b/i1401_lp.c index 3fff36c6..b1ea1a06 100644 --- a/i1401_lp.c +++ b/i1401_lp.c @@ -1,6 +1,6 @@ /* i1401_lp.c: IBM 1403 line printer simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -22,11 +22,15 @@ 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 1403 line printer + + 13-Apr-01 RMS Revised for register arrays */ #include "i1401_defs.h" -extern unsigned char M[]; +extern uint8 M[]; extern char bcd_to_ascii[64]; extern int32 iochk, ind[64]; int32 cct[CCT_LNT] = { 03 }; @@ -80,7 +84,7 @@ UNIT lpt_unit = { 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 }, + { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, { DRDATA (LINES, lines, 8), PV_LEFT }, { DRDATA (CCTP, cctptr, 8), PV_LEFT }, { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, diff --git a/i1401_mt.c b/i1401_mt.c index c06db181..586ff436 100644 --- a/i1401_mt.c +++ b/i1401_mt.c @@ -1,6 +1,6 @@ /* IBM 1401 magnetic tape simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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-Apr-01 RMS Changed to rewind tape before boot 07-Dec-00 RMS Widened display width from 6 to 8 bits to see record lnt CEO Added tape bootstrap 14-Apr-99 RMS Changed t_addr to unsigned @@ -50,7 +51,7 @@ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_W_UF 2 /* #save flags */ -extern unsigned char M[]; /* memory */ +extern uint8 M[]; /* memory */ extern int32 ind[64]; extern int32 BS, iochk; extern UNIT cpu_unit; @@ -58,8 +59,6 @@ unsigned int8 dbuf[MAXMEMSIZE * 2]; /* tape buffer */ t_stat mt_reset (DEVICE *dptr); t_stat mt_boot (int32 unitno); 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 @@ -286,6 +285,7 @@ t_stat mt_boot (int32 unitno) int32 i; extern int32 saved_IS; +mt_unit[unitno].pos = 0; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; M[BOOT_START + 3] = unitno & 07; saved_IS = BOOT_START; diff --git a/i1401_sys.c b/i1401_sys.c index 57d91c77..6b28dfc7 100644 --- a/i1401_sys.c +++ b/i1401_sys.c @@ -1,6 +1,6 @@ /* i1401_sys.c: IBM 1401 simulator interface - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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-Mar-01 RMS Revised load/dump interface (again) 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface */ @@ -36,7 +37,7 @@ 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 uint8 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); @@ -97,7 +98,7 @@ const char *sim_stop_messages[] = { number of entries */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; t_stat r; diff --git a/id4_cpu.c b/id4_cpu.c index d1e5f854..ec4d01b8 100644 --- a/id4_cpu.c +++ b/id4_cpu.c @@ -1,6 +1,6 @@ /* id4_cpu.c: Interdata 4 CPU simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -121,9 +121,9 @@ PC = ReadW ((n) + 2); \ CC = PSW & CC_MASK -unsigned int16 M[MAXMEMSIZE >> 1] = { 0 }; /* memory */ +uint16 M[MAXMEMSIZE >> 1] = { 0 }; /* memory */ int32 R[16] = { 0 }; /* general registers */ -unsigned int32 F[8] = { 0 }; /* fp registers */ +uint32 F[8] = { 0 }; /* fp registers */ int32 PSW = 0; /* processor status word */ int32 saved_PC = 0; /* program counter */ int32 SR = 0; /* switch register */ diff --git a/id4_defs.h b/id4_defs.h index 132e24aa..76b3bc39 100644 --- a/id4_defs.h +++ b/id4_defs.h @@ -1,6 +1,6 @@ /* id4_defs.h: Interdata 4 simulator definitions - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/id4_fp.c b/id4_fp.c index b2dac670..20f36bf4 100644 --- a/id4_fp.c +++ b/id4_fp.c @@ -1,6 +1,6 @@ /* id4_fp.c: Interdata 4 floating point instructions - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -69,16 +69,21 @@ struct ufp { /* unpacked fp */ #define FP_ROUND 0x80000000 #define FP_DMASK 0xFFFFFFFF -#define FP_SHFR(v,s) if ((s) < 32) { \ +/* Variable and constant shifts; for constants, 0 < k < 32 */ + +#define FP_SHFR_V(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; } + v.frh = 0; } +#define FP_SHFR_K(v,s) v.frl = ((v.frl >> (s)) | \ + (v.frh << (32 - (s)))) & FP_DMASK; \ + v.frh = (v.frh >> (s)) & FP_DMASK extern int32 R[16]; -extern unsigned int32 F[8]; -extern unsigned int16 M[]; +extern uint32 F[8]; +extern uint16 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); @@ -130,7 +135,7 @@ else if (fop2.frh != 0) { /* if op2 = 0, no add */ fop1 = t; } if (ediff = fop1.exp - fop2.exp) { /* exp differ? */ if (ediff > 14) fop2.frh = 0; /* limit shift */ - else { FP_SHFR (fop2, ediff * 4); } } + else { FP_SHFR_V (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); @@ -138,7 +143,7 @@ else if (fop2.frh != 0) { /* if op2 = 0, no add */ 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 */ + FP_SHFR_K (fop1, 4); /* renormalize */ fop1.exp = fop1.exp + 1; } } /* incr exp */ } /* end if fop2 */ return StoreFP (&fop1, r1); /* store result */ @@ -269,4 +274,4 @@ F[r1 >> 1] = ((fop -> sign & FP_M_SIGN) << FP_V_SIGN) | /* pack result */ 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 index 581150ea..73e053e7 100644 --- a/id4_stddev.c +++ b/id4_stddev.c @@ -1,6 +1,6 @@ /* id4_stddev.c: Interdata 4 standard devices - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -33,7 +33,7 @@ #define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ #define UNIT_UC (1 << UNIT_V_UC) -extern unsigned int16 M[]; +extern uint16 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 */ diff --git a/id4_sys.c b/id4_sys.c index d27233ce..091484bf 100644 --- a/id4_sys.c +++ b/id4_sys.c @@ -1,6 +1,6 @@ /* id4_sys.c: Interdata 4 simulator interface - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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-Mar-01 RMS Revised load/dump interface (again) 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface */ @@ -34,7 +35,7 @@ extern DEVICE cpu_dev; extern DEVICE pt_dev, tt_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; -extern unsigned int16 *M; +extern uint16 *M; extern int32 saved_PC; /* SCP data structures and interface routines @@ -69,7 +70,7 @@ const char *sim_stop_messages[] = { To be specified */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { return SCPE_FMT; /* unexpected eof */ } diff --git a/mtcvtv23.c b/mtcvtv23.c deleted file mode 100644 index c57d9610..00000000 --- a/mtcvtv23.c +++ /dev/null @@ -1,81 +0,0 @@ -/* 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 index ad237004..324d6d0b 100644 --- a/nova_clk.c +++ b/nova_clk.c @@ -1,6 +1,6 @@ /* nova_clk.c: NOVA real-time clock simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,18 +23,22 @@ 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 Charles Owen) - clk real-time clock + + 05-Mar-01 RMS Added clock calibration + 24-Sep-97 RMS Fixed bug in unit service (found by Charles Owen) */ #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 */ +int32 clk_time[4] = { 16000, 100000, 10000, 1000 }; /* freq table */ +int32 clk_tps[4] = { 60, 10, 100, 1000 }; /* ticks per sec */ t_stat clk_svc (UNIT *uptr); t_stat clk_reset (DEVICE *dptr); +extern int32 sim_rtc_init (int32 time); +extern int32 sim_rtc_calb (int32 tps); /* CLK data structures @@ -43,7 +47,7 @@ t_stat clk_reset (DEVICE *dptr); clk_reg CLK register list */ -UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; +UNIT clk_unit = { UDATA (&clk_svc, 0, 0) }; REG clk_reg[] = { { ORDATA (SELECT, clk_sel, 2) }, @@ -51,10 +55,10 @@ REG clk_reg[] = { { 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 }, + { DRDATA (TIME0, clk_time[0], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME1, clk_time[1], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME2, clk_time[2], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME3, clk_time[3], 24), REG_NZ + PV_LEFT }, { NULL } }; DEVICE clk_dev = { @@ -67,13 +71,17 @@ DEVICE clk_dev = { int32 clk (int32 pulse, int32 code, int32 AC) { -if (code == ioDOA) clk_sel = AC & 3; +if (code == ioDOA) { /* DOA */ + clk_sel = AC & 3; /* save select */ + sim_rtc_init (clk_time[clk_sel]); } /* init calibr */ 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 */ + if (!sim_is_active (&clk_unit)) /* not running? */ + sim_activate (&clk_unit, /* activate */ + sim_rtc_init (clk_time[clk_sel])); /* init calibr */ break; case iopC: /* clear */ dev_busy = dev_busy & ~INT_CLK; /* clear busy */ @@ -91,7 +99,8 @@ 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 */ +sim_activate (&clk_unit, /* reactivate unit */ + sim_rtc_calb (clk_tps[clk_sel])); /* calibrate delay */ return SCPE_OK; } diff --git a/nova_cpu.c b/nova_cpu.c index 535edd8c..c5ff5874 100644 --- a/nova_cpu.c +++ b/nova_cpu.c @@ -1,6 +1,6 @@ /* nova_cpu.c: NOVA CPU simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,10 @@ 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. + cpu Nova central processor + + 26-Apr-01 RMS Added device enable/disable support + 05-Mar-01 RMS Added clock calibration 22-Dec-00 RMS Added Bruce Ray's second terminal 15-Dec-00 RMS Added Charles Owen's CPU bootstrap 08-Dec-00 RMS Changes from Bruce Ray @@ -219,7 +223,7 @@ #define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) -unsigned int16 M[MAXMEMSIZE] = { 0 }; /* memory */ +uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ int32 AC[4] = { 0 }; /* accumulators */ int32 C = 0; /* carry flag */ int32 saved_PC = 0; /* program counter */ @@ -229,6 +233,7 @@ 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 iot_enb = -1; /* IOT enables */ int32 int_req = 0; /* interrupt requests */ int32 pimask = 0; /* priority int mask */ int32 pwr_low = 0; /* power fail flag */ @@ -333,6 +338,7 @@ REG cpu_reg[] = { { ORDATA (OLDPC, old_PC, 15), REG_RO }, { ORDATA (BREAK, ibkpt_addr, 16) }, { ORDATA (WRU, sim_int_char, 8) }, + { ORDATA (IOTENB, iot_enb, 32), REG_HRO }, { NULL } }; MTAB cpu_mod[] = { @@ -362,6 +368,8 @@ extern int32 sim_interval; register int32 PC, IR, i; register t_stat reason; void mask_out (int32 mask); +extern int32 clk_sel, clk_time[4]; +extern int32 sim_rtc_init (int32 time); /* Restore register state */ @@ -369,6 +377,7 @@ PC = saved_PC & AMASK; /* load local PC */ C = C & CBIT; mask_out (pimask); /* reset int system */ reason = 0; +sim_rtc_init (clk_time[clk_sel]); /* init calibration */ /* Main instruction fetch/decode loop */ @@ -774,10 +783,12 @@ else { /* IOT */ int_req = int_req & ~INT_ION; break; } /* end switch pulse */ } /* end CPU control */ - else { /* normal device */ + else if ((dev_table[device].mask == 0) || + (dev_table[device].mask & iot_enb)) { /* normal device */ iodata = dev_table[device].routine (pulse, code, AC[dstAC]); reason = iodata >> IOT_V_REASON; if (code & 1) AC[dstAC] = iodata & 0177777; } + else reason = stop_dev; } /* end if IOT */ } /* end while */ diff --git a/nova_defs.h b/nova_defs.h index cd377217..5cedf87a 100644 --- a/nova_defs.h +++ b/nova_defs.h @@ -1,6 +1,6 @@ /* nova_defs.h: NOVA/Eclipse simulator definitions - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -171,9 +171,9 @@ */ struct ndev { - int mask; /* done/busy mask */ - int pi; /* assigned pi bit */ - int (*routine)(); /* dispatch routine */ + int32 mask; /* done/busy mask */ + int32 pi; /* assigned pi bit */ + int32 (*routine)(); /* dispatch routine */ }; /* Device flags (simulator representation) diff --git a/nova_dkp.c b/nova_dkp.c index c6efac72..070bd0a4 100644 --- a/nova_dkp.c +++ b/nova_dkp.c @@ -1,6 +1,6 @@ /* nova_dkp.c: NOVA moving head disk simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -25,13 +25,14 @@ dkp moving head disk + 26-Apr-01 RMS Added device enable/disable support 12-Dec-00 RMS Added Eclipse support from Charles Owen 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 Charles Owen) 10-Sep-97 RMS Fixed bug in error reporting (found by Charles Owen) - 25-Nov-96 RMS Defaults to autosize + 25-Nov-96 RMS Defaulted to autosize 29-Jun-96 RMS Added unit disable support */ @@ -267,16 +268,15 @@ struct drvtyp drv_tab[] = { { SECT_4231, SURF_4231, CYL_4231, SIZE_4231, NFMT_4231 }, { 0 } }; -extern unsigned int16 M[]; +extern uint16 M[]; extern UNIT cpu_unit; -extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb; 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 */ -static unsigned int16 tbuf[DKP_NUMWD]; /* transfer buffer */ t_stat dkp_svc (UNIT *uptr); t_stat dkp_reset (DEVICE *dptr); t_stat dkp_boot (int32 unitno); @@ -330,6 +330,7 @@ REG dkp_reg[] = { { 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 }, + { FLDATA (*DEVENB, iot_enb, INT_V_DKP), REG_HRO }, { NULL } }; MTAB dkp_mod[] = { @@ -556,6 +557,7 @@ int32 sc, sa, xcsa, awc, bda; int32 sx, dx, pa; int32 dtype, u, err, newsect, newsurf; t_stat rval; +static uint16 tbuf[DKP_NUMWD]; /* transfer buffer */ rval = SCPE_OK; dtype = GET_DTYPE (uptr -> flags); /* get drive type */ @@ -594,22 +596,22 @@ else { sc = 16 - GET_COUNT (dkp_ussc); /* get sector count */ if (uptr -> FUNC == FCCY_READ) { /* read? */ for (sx = 0; sx < sc; sx++) { /* loop thru sectors */ - awc = fxread (&tbuf, sizeof(uint16), DKP_NUMWD, uptr -> fileref); + awc = fxread (tbuf, sizeof(uint16), DKP_NUMWD, uptr -> fileref); for ( ; awc < DKP_NUMWD; awc++) tbuf[awc] = 0; - if (err = ferror (uptr -> fileref)) break; - for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop thru buffer */ + if (err = ferror (uptr -> fileref)) break; + for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop thru buffer */ pa = MapAddr (0, dkp_ma); if (MEM_ADDR_OK (pa)) M[pa] = tbuf[dx]; dkp_ma = (dkp_ma + 1) & AMASK; } } } if (uptr -> FUNC == FCCY_WRITE) { /* write? */ for (sx = 0; sx < sc; sx++) { /* loop thru sectors */ - for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop into buffer */ + for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop into buffer */ pa = MapAddr (0, dkp_ma); tbuf[dx] = M[pa]; dkp_ma = (dkp_ma + 1) & AMASK; } - fxwrite (&tbuf, sizeof(int16), DKP_NUMWD, uptr -> fileref); - if (err = ferror (uptr -> fileref)) break; } } + fxwrite (tbuf, sizeof(int16), DKP_NUMWD, uptr -> fileref); + if (err = ferror (uptr -> fileref)) break; } } if (err != 0) { perror ("DKP I/O error"); diff --git a/nova_dsk.c b/nova_dsk.c index 64fa3be4..55681c9a 100644 --- a/nova_dsk.c +++ b/nova_dsk.c @@ -1,6 +1,6 @@ /* nova_dsk.c: 4019 fixed head disk simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -25,6 +25,7 @@ dsk fixed head disk + 26-Apr-01 RMS Added device enable/disable support 10-Dec-00 RMS Added Eclipse support 15-Oct-00 RMS Editorial changes 14-Apr-99 RMS Changed t_addr to unsigned @@ -74,9 +75,9 @@ static const int32 sector_map[] = { #define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) DSK_NUMSC))) -extern unsigned int16 M[]; +extern uint16 M[]; extern UNIT cpu_unit; -extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb; int32 dsk_stat = 0; /* status register */ int32 dsk_da = 0; /* disk address */ int32 dsk_ma = 0; /* memory address */ @@ -114,6 +115,7 @@ REG dsk_reg[] = { { ORDATA (WLK, dsk_wlk, 8) }, { DRDATA (TIME, dsk_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, dsk_stopioe, 0) }, + { FLDATA (*DEVENB, iot_enb, INT_V_DSK), REG_HRO }, { NULL } }; DEVICE dsk_dev = { diff --git a/nova_lp.c b/nova_lp.c index 13506b25..36ee1799 100644 --- a/nova_lp.c +++ b/nova_lp.c @@ -1,6 +1,6 @@ /* nova_lp.c: NOVA line printer simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/nova_mta.c b/nova_mta.c index a6748c2d..934d2f82 100644 --- a/nova_mta.c +++ b/nova_mta.c @@ -1,6 +1,6 @@ /* nova_mta.c: NOVA magnetic tape simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -25,12 +25,14 @@ mta magnetic tape + 26-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot 10-Dec-00 RMS Added Eclipse support from Charles Owen 15-Oct-00 RMS Editorial changes 11-Nov-98 CEO Removed clear of mta_ma on iopC 04-Oct-98 RMS V2.4 magtape format 18-Jan-97 RMS V2.3 magtape format - 29-Jun-96 RMS Added unit disable support + 29-Jun-96 RMS Added unit enable/disable support Magnetic tapes are represented as a series of variable records of the form: @@ -130,9 +132,9 @@ #define STA_MON (STA_REW | STA_BOT | STA_WLK | STA_RDY | \ STA_PEM) /* set status chg */ -extern unsigned int16 M[]; +extern uint16 M[]; extern UNIT cpu_unit; -extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb; int32 mta_ma = 0; /* memory address */ int32 mta_wc = 0; /* word count */ int32 mta_cu = 0; /* command/unit */ @@ -221,6 +223,7 @@ REG mta_reg[] = { REG_HRO }, { GRDATA (FLG7, mta_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, + { FLDATA (*DEVENB, iot_enb, INT_V_MTA), REG_HRO }, { NULL } }; MTAB mta_mod[] = { @@ -318,8 +321,8 @@ t_stat mta_svc (UNIT *uptr) int32 c, i, p, u, pa, err; t_stat rval; t_mtrlnt cbc, tbc, wc; -unsigned int16 c1, c2; -unsigned int8 dbuf[2 * DTSIZE]; +uint16 c1, c2; +static uint8 dbuf[2 * DTSIZE]; static t_mtrlnt bceof = { 0 }; rval = SCPE_OK; @@ -593,6 +596,7 @@ t_stat mta_boot (int32 unitno) int32 i; extern int32 saved_PC; +mta_unit[unitno].pos = 0; 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; diff --git a/nova_plt.c b/nova_plt.c index 4ec0d754..30343faa 100644 --- a/nova_plt.c +++ b/nova_plt.c @@ -1,6 +1,6 @@ /* nova_plt.c: NOVA plotter simulator - Copyright (c) 2000, Robert M. Supnik + Copyright (c) 2000-2001, Robert M. Supnik Written by Bruce Ray and used with his gracious permission. Permission is hereby granted, free of charge, to any person obtaining a @@ -23,11 +23,15 @@ 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. + + plt plotter + + 26-Apr-01 RMS Added device enable/disable support */ #include "nova_defs.h" -extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb; int32 plt_stopioe = 0; /* stop on error */ t_stat plt_svc (UNIT *uptr); t_stat plt_reset (DEVICE *dptr); @@ -51,6 +55,7 @@ REG plt_reg[] = { { DRDATA (POS, plt_unit.pos, 31), PV_LEFT }, { DRDATA (TIME, plt_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, plt_stopioe, 0) }, + { FLDATA (*DEVENB, iot_enb, INT_V_PLT), REG_HRO }, { NULL } }; DEVICE plt_dev = { diff --git a/nova_pt.c b/nova_pt.c index 3cef9dae..e7a6de60 100644 --- a/nova_pt.c +++ b/nova_pt.c @@ -1,6 +1,6 @@ /* nova_pt.c: NOVA paper tape read/punch simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/nova_sys.c b/nova_sys.c index 4eed75c6..35a25d14 100644 --- a/nova_sys.c +++ b/nova_sys.c @@ -1,6 +1,6 @@ /* nova_sys.c: NOVA simulator interface - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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-Mar-01 RMS Revised load/dump interface (again) 22-Dec-00 RMS Added second terminal support 10-Dec-00 RMS Added Eclipse support 08-Dec-00 BKR Added plotter support @@ -49,7 +50,7 @@ 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 uint16 M[]; extern int32 saved_PC; /* SCP data structures @@ -119,7 +120,7 @@ const char *sim_stop_messages[] = { If the word count is >1, the block is an error block. */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 data, csum, count, state, i; t_addr origin; diff --git a/nova_tt.c b/nova_tt.c index 20c56333..c8143ecc 100644 --- a/nova_tt.c +++ b/nova_tt.c @@ -1,6 +1,6 @@ /* nova_tt.c: NOVA console terminal simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), diff --git a/nova_tt1.c b/nova_tt1.c index e5acc0af..3e6f9dda 100644 --- a/nova_tt1.c +++ b/nova_tt1.c @@ -1,6 +1,6 @@ /* nova_tt1.c: NOVA second terminal simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, Robert M. Supnik Written by Bruce Ray and used with his gracious permission. Permission is hereby granted, free of charge, to any person obtaining a @@ -26,6 +26,8 @@ tti1 second terminal input tto1 second terminal output + + 26-Apr-01 RMS Added device enable/disable support */ #include "nova_defs.h" @@ -33,7 +35,7 @@ #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; +extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb; int32 tti1_stopioe = 0, tto1_stopioe = 0; /* stop on error */ t_stat tti1_svc (UNIT *uptr); t_stat tto1_svc (UNIT *uptr); @@ -63,6 +65,7 @@ REG tti1_reg[] = { { DRDATA (TIME, tti1_unit.wait, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, tti1_stopioe, 0) }, { FLDATA (MODE, tti1_unit.flags, UNIT_V_DASHER), REG_HRO }, + { FLDATA (*DEVENB, iot_enb, INT_V_TTI1), REG_HRO }, { NULL } }; MTAB ttx1_mod[] = { @@ -95,6 +98,7 @@ REG tto1_reg[] = { { DRDATA (TIME, tto1_unit.wait, 24), PV_LEFT }, { FLDATA (STOP_IOE, tto1_stopioe, 0) }, { FLDATA (MODE, tto1_unit.flags, UNIT_V_DASHER), REG_HRO }, + { FLDATA (*DEVENB, iot_enb, INT_V_TTI1), REG_HRO }, { NULL } }; DEVICE tto1_dev = { diff --git a/pdp10_cpu.c b/pdp10_cpu.c new file mode 100644 index 00000000..1ed9b602 --- /dev/null +++ b/pdp10_cpu.c @@ -0,0 +1,2063 @@ +/* pdp10_cpu.c: PDP-10 CPU simulator + + Copyright (c) 1993-2001, 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. + + cpu KS10 central processor + + 29-Apr-01 RMS Fixed modifier naming conflict + Fixed XCTR/XCTRI, UMOVE/UMOVEM, BLTUB/BLTBU for ITS + Added CLRCSH for ITS + + The 36b system family had six different implementions: PDP-6, KA10, KI10, + L10, KL10 extended, and KS10. This simulator implements the KS10. + + The register state for the KS10 is: + + AC[8][16] accumulators + PC program counter + flags<0:11> state flags + pi_enb<1:7> enabled PI levels + pi_act<1:7> active PI levels + pi_prq<1:7> program PI requests + apr_enb<0:7> enabled system flags + apr_flg<0:7> system flags + ebr executive base register + ubr user base register + hsb halt status block address + spt SPT base + cst CST base + pur process use register + cstm CST mask + + The PDP-10 had just two instruction formats: memory reference + and I/O. + + 000000000 0111 1 1111 112222222222333333 + 012345678 9012 3 4567 890123456789012345 + +---------+----+-+----+------------------+ + | opcode | ac |i| idx| address | memory reference + +---------+----+-+----+------------------+ + + 000 0000000 111 1 1111 112222222222333333 + 012 3456789 012 3 4567 890123456789012345 + +---+-------+---+-+----+------------------+ + |111|device |iop|i| idx| address | I/O + +---+-------+---+-+----+------------------+ +*/ + +/* This routine is the instruction decode routine for the PDP-10. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until an abort occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + MUUO instruction in executive mode + pager error in interrupt sequence + invalid vector table in interrupt sequence + illegal instruction in interrupt sequence + breakpoint encountered + nested indirects exceeding limit + nested XCT's exceeding limit + I/O error in I/O simulator + + 2. Interrupts. PDP-10's have a seven level priority interrupt + system. Interrupt requests can come from internal sources, + such as APR program requests, or external sources, such as + I/O devices. The requests are stored in pi_prq for program + requests, pi_apr for other internal flags, and pi_ioq for + I/O device flags. Internal and device (but not program) + interrupts must be enabled on a level by level basis. When + an interrupt is granted on a level, interrupts at that level + and below are masked until the interrupt is dismissed. + + The I/O device interrupt system is taken from the PDP-11. + int_req stores the interrupt requests for Unibus I/O devices. + Routines in the Unibus adapter map requests in int_req to + PDP-10 levels. The Unibus adapter also calculates which + device to get a vector from when a PDP-10 interrupt is granted. + + 3. Arithmetic. The PDP-10 is a 2's complement system. + + 4. Adding I/O devices. Three modules must be modified: + + pdp10_defs.h add interrupt request definition + pdp10_ksio.c add I/O page linkages + pdp10_sys.c add pointer to data structures to sim_devices +*/ + +#include "pdp10_defs.h" +#include + +#define ILL_ADR_FLAG (1 << VASIZE) +#define save_ibkpt (cpu_unit.u3) +#define UNIT_V_MSIZE (UNIT_V_ITS + 1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +d10 *M = NULL; /* memory */ +d10 acs[AC_NBLK * AC_NUM] = { 0 }; /* AC blocks */ +d10 *ac_cur, *ac_prv; /* AC cur, prv (dyn) */ +a10 epta, upta; /* proc tbl addr (dyn) */ +d10 last_inst = 0; /* most recent inst */ +a10 saved_PC = 0; /* scp: saved PC */ +a10 pager_PC = 0; /* pager: saved PC */ +d10 ebr = 0; /* exec base reg */ +d10 ubr = 0; /* user base reg */ +d10 hsb = 0; /* halt status block */ +d10 spt = 0; /* TOPS20 paging regs */ +d10 cst = 0; +d10 pur = 0; +d10 cstm = 0; +a10 dbr1 = 0; /* ITS paging regs */ +a10 dbr2 = 0; +a10 dbr3 = 0; +a10 dbr4 = 0; +d10 pcst = 0; /* ITS PC sampling */ +int32 pi_on = 0; /* pi system enable */ +int32 pi_enb = 0; /* pi enabled levels */ +int32 pi_act = 0; /* pi active levels */ +int32 pi_ioq = 0; /* pi io requests */ +int32 pi_apr = 0; /* pi apr requests */ +int32 pi_prq = 0; /* pi prog requests */ +int32 apr_enb = 0; /* apr enables */ +int32 apr_flg = 0; /* apr flags */ +int32 apr_lvl = 0; /* apr level */ +int32 qintr = 0; /* interrupt pending */ +int32 flags = 0; /* flags */ +int32 stop_op0 = 0; /* stop on 0 */ +d10 pager_word = 0; /* pager error word */ +int32 rlog = 0; /* extend fixup log */ +int32 ind_max = 32; /* nested ind limit */ +int32 xct_max = 32; /* nested XCT limit */ +int32 astop_cpu = 0; /* address stop */ +a10 old_PC = 0; /* old PC */ +a10 ibkpt_addr = ILL_ADR_FLAG | AMASK; /* breakpoint addr */ +jmp_buf save_env; + +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); +extern int32 sim_int_char; +extern int32 sim_interval; + +extern d10 Read (a10 ea, int32 prv); /* read, read check */ +extern d10 ReadM (a10 ea, int32 prv); /* read, write check */ +extern d10 ReadE (a10 ea); /* read, exec */ +extern d10 ReadP (a10 ea); /* read, physical */ +extern void Write (a10 ea, d10 val, int32 prv); /* write */ +extern void WriteE (a10 ea, d10 val); /* write, exec */ +extern void WriteP (a10 ea, d10 val); /* write, physical */ +extern t_bool AccViol (a10 ea, int32 prv, int32 mode); /* access check */ +extern void set_dyn_ptrs (void); +extern a10 conmap (a10 ea, int32 mode, int32 sw); +extern void fe_intr (); +int32 pi_eval (void); +int32 test_int (void); +void set_ac_display (d10 *acbase); + +/* 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, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, VASIZE) }, + { ORDATA (FLAGS, flags, 18) }, + { ORDATA (AC0, acs[0], 36) }, /* addr in memory */ + { ORDATA (AC1, acs[1], 36) }, /* modified at exit */ + { ORDATA (AC2, acs[2], 36) }, /* to SCP */ + { ORDATA (AC3, acs[3], 36) }, + { ORDATA (AC4, acs[4], 36) }, + { ORDATA (AC5, acs[5], 36) }, + { ORDATA (AC6, acs[6], 36) }, + { ORDATA (AC7, acs[7], 36) }, + { ORDATA (AC10, acs[10], 36) }, + { ORDATA (AC11, acs[11], 36) }, + { ORDATA (AC12, acs[12], 36) }, + { ORDATA (AC13, acs[13], 36) }, + { ORDATA (AC14, acs[14], 36) }, + { ORDATA (AC15, acs[15], 36) }, + { ORDATA (AC16, acs[16], 36) }, + { ORDATA (AC17, acs[17], 36) }, + { ORDATA (IR, last_inst, 36), REG_RO }, + { ORDATA (PFW, pager_word, 36) }, + { ORDATA (EBR, ebr, EBR_N_EBR) }, + { FLDATA (PGON, ebr, EBR_V_PGON) }, + { FLDATA (T20P, ebr, EBR_V_T20P) }, + { ORDATA (UBR, ubr, 36) }, + { GRDATA (CURAC, ubr, 8, 3, UBR_V_CURAC), REG_RO }, + { GRDATA (PRVAC, ubr, 8, 3, UBR_V_PRVAC) }, + { ORDATA (SPT, spt, 36) }, + { ORDATA (CST, cst, 36) }, + { ORDATA (PUR, pur, 36) }, + { ORDATA (CSTM, cstm, 36) }, + { ORDATA (HSB, hsb, 36) }, + { ORDATA (DBR1, dbr1, PASIZE) }, + { ORDATA (DBR2, dbr2, PASIZE) }, + { ORDATA (DBR3, dbr3, PASIZE) }, + { ORDATA (DBR4, dbr4, PASIZE) }, + { ORDATA (PCST, pcst, 36) }, + { ORDATA (PIENB, pi_enb, 7) }, + { FLDATA (PION, pi_on, 0) }, + { ORDATA (PIACT, pi_act, 7) }, + { ORDATA (PIPRQ, pi_prq, 7) }, + { ORDATA (PIIOQ, pi_ioq, 7), REG_RO }, + { ORDATA (PIAPR, pi_apr, 7), REG_RO }, + { ORDATA (APRENB, apr_enb, 8) }, + { ORDATA (APRFLG, apr_flg, 8) }, + { ORDATA (APRLVL, apr_lvl, 3) }, + { ORDATA (RLOG, rlog, 10) }, + { ORDATA (OLDPC, old_PC, VASIZE), REG_RO }, + { DRDATA (INDMAX, ind_max, 8), PV_LEFT + REG_NZ }, + { DRDATA (XCTMAX, xct_max, 8), PV_LEFT + REG_NZ }, + { FLDATA (ITS, cpu_unit.flags, UNIT_V_ITS), REG_HRO }, + { ORDATA (BREAK, ibkpt_addr, VASIZE + 1) }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (STOP_ILL, stop_op0, 0) }, + { BRDATA (REG, acs, 8, 36, AC_NUM * AC_NBLK) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_ITS, 0, "Standard microcode", "STANDARD", NULL }, + { UNIT_ITS, UNIT_ITS, "ITS microcode", "ITS", NULL }, + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, PASIZE, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL }; + +/* Data arrays */ + +const int32 pi_l2bit[8] = { + 0, 0100, 0040, 0020, 0010, 0004, 0002, 0001 }; + +const int32 pi_m2lvl[128] = { + 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +const d10 bytemask[64] = { 0, + 01, 03, 07, 017, 037, 077, + 0177, 0377, 0777, 01777, 03777, 07777, + 017777, 037777, 077777, + 0177777, 0377777, 0777777, + 01777777, 03777777, 07777777, + 017777777, 037777777, 077777777, + 0177777777, 0377777777, 0777777777, + 01777777777, 03777777777, 07777777777, + 017777777777, 037777777777, 077777777777, + 0177777777777, 0377777777777, 0777777777777, + ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, + ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, + ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES }; + +/* JRST classes */ + +#define JRST_U 1 /* ok anywhere */ +#define JRST_E 2 /* ok exec mode */ +#define JRST_UIO 3 /* ok user I/O mode */ + + +/* Address operations */ + +#define IM ((d10) ea) +#define IMS (((d10) ea) << 18) +#define JUMP(x) old_PC = PC, PC = ((a10) (x)) & AMASK +#define SUBJ(x) CLRF (F_AFI | F_FPD | F_TR); JUMP (x) +#define INCPC PC = INCA (PC) + +/* AC operations */ + +#define AOBAC AC(ac) = AOB (AC(ac)) +#define SOBAC AC(ac) = SOB (AC(ac)) +#define G2AC rs[0] = AC(ac), rs[1] = AC(P1) +#define S1AC AC(ac) = rs[0] +#define S2AC S1AC, AC(P1) = rs[1] +#define LAC if (ac) AC(ac) = mb + +/* Memory operations */ + +#define RD mb = Read (ea, MM_OPND) +#define RDAC AC(ac) = Read (ea, MM_OPND) +#define RM mb = ReadM (ea, MM_OPND) +#define RMAC AC(ac) = ReadM (ea, MM_OPND) +#define RDP mb = Read (((a10) AC(ac)) & AMASK, MM_BSTK) +#define RD2 rs[0] = Read (ea, MM_OPND); \ + rs[1] = Read (INCA (ea), MM_OPND) +#define WR Write (ea, mb, MM_OPND) +#define WRAC Write (ea, AC(ac), MM_OPND) +#define WRP(x) Write (((a10) INCA (AC(ac))), (x), MM_BSTK) +#define WR1 Write (ea, rs[0], MM_OPND) +#define WR2 ReadM (INCA (ea), MM_OPND); \ + Write (ea, rs[0], MM_OPND); \ + Write (INCA (ea), rs[1], MM_OPND) + +/* Tests and compares */ + +#define TL(a) (TSTS (a) != 0) +#define TE(a) ((a) == 0) +#define TLE(a) (TL (a) || TE (a)) +#define TGE(a) (TSTS (a) == 0) +#define TN(a) ((a) != 0) +#define TG(a) (TGE (a) && TN (a)) +#define CL(a) ((TSTS (AC(ac) ^ a))? (a < AC(ac)): (AC(ac) < a)) +#define CE(a) (AC(ac) == a) +#define CLE(a) (CL (a) || CE (a)) +#define CGE(a) (!CL (a)) +#define CN(a) (AC(ac) != a) +#define CG(a) (CGE (a) && CN (a)) + +/* Word assemblies */ + +#define FLPC XWD (flags, PC) +#define UUOWORD (((d10) op) << INST_V_OP) | (((d10) ac) << INST_V_AC) | ea +#define APRHWORD ((apr_flg << APR_V_FLG) | (apr_lvl & APR_M_LVL) | \ + ((apr_flg & apr_enb)? APR_IRQ: 0)) +#define APRWORD ((apr_enb << (APR_V_FLG + 18)) | APRHWORD) +#define PIHWORD ((pi_act << PI_V_ACT) | (pi_on << PI_V_ON) | \ + (pi_enb << PI_V_ENB)) +#define PIWORD ((pi_prq << PI_V_PRQ) | PIHWORD) + +/* Instruction operations */ + +#define CIBP if (!TSTF (F_FPD)) { ibp (ea, pflgs); SETF (F_FPD); } +#define LDB AC(ac) = ldb (ea, pflgs) +#define DPB dpb (AC(ac), ea, pflgs) +#define FAD(s) fad (AC(ac), s, FALSE, 0) +#define FADR(s) fad (AC(ac), s, TRUE, 0) +#define FSB(s) fad (AC(ac), s, FALSE, 1) +#define FSBR(s) fad (AC(ac), s, TRUE, 1) +#define FMP(s) fmp (AC(ac), s, FALSE) +#define FMPR(s) fmp (AC(ac), s, TRUE) +#define FDV(s) fdv (AC(ac), s, rs, FALSE) +#define FDVR(s) fdv (AC(ac), s, rs, TRUE) +#define MOVN(s) NEG (s); MOVNF(s) +#define MOVM(s) ABS (s); MOVMF(s) +#define ADD(s) add (AC(ac), s) +#define SUB(s) sub (AC(ac), s) +#define IMUL(s) imul (AC(ac), s) +#define IDIV(s) idiv (AC(ac), s, rs) +#define MUL(s) mul (AC(ac), s, rs) +#define DIV(s) divi (ac, s, rs) +#define AOJ AC(ac) = INC (AC(ac)); INCF (AC(ac)) +#define AOS RM; mb = INC (mb); WR; INCF (mb); LAC +#define SOJ AC(ac) = DEC (AC(ac)); DECF (AC(ac)) +#define SOS RM; mb = DEC (mb); WR; DECF (mb); LAC +#define SETCA(s) ~AC(ac) & DMASK +#define SETCM(s) ~(s) & DMASK; +#define AND(s) AC(ac) & (s) +#define ANDCA(s) ~AC(ac) & (s) +#define ANDCM(s) AC(ac) & ~(s) +#define ANDCB(s) (~AC(ac) & ~(s)) & DMASK +#define IOR(s) AC(ac) | (s) +#define ORCA(s) (~AC(ac) | (s)) & DMASK +#define ORCM(s) (AC(ac) | ~(s)) & DMASK +#define ORCB(s) (~AC(ac) | ~(s)) & DMASK +#define XOR(s) AC(ac) ^ (s) +#define EQV(s) (~(AC(ac) ^ (s))) & DMASK +#define LL(s,d) ((s) & LMASK) | ((d) & RMASK) +#define RL(s,d) (((s) << 18) & LMASK) | ((d) & RMASK) +#define RR(s,d) ((s) & RMASK) | ((d) & LMASK) +#define LR(s,d) (((s) >> 18) & RMASK) | ((d) & LMASK) +#define LLO(s) ((s) & LMASK) | RMASK +#define RLO(s) (((s) << 18) & LMASK) | RMASK +#define RRO(s) ((s) & RMASK) | LMASK +#define LRO(s) (((s) >> 18) & RMASK) | LMASK +#define LLE(s) ((s) & LMASK) | (((s) & LSIGN)? RMASK: 0) +#define RLE(s) (((s) << 18) & LMASK) | (((s) & RSIGN)? RMASK: 0) +#define RRE(s) ((s) & RMASK) | (((s) & RSIGN)? LMASK: 0) +#define LRE(s) (((s) >> 18) & RMASK) | (((s) & LSIGN)? LMASK: 0) +#define TD_ RD +#define TS_ RD; mb = SWP (mb) +#define TL_ mb = IMS +#define TR_ mb = IM +#define T_Z AC(ac) = AC(ac) & ~mb +#define T_O AC(ac) = AC(ac) | mb +#define T_C AC(ac) = AC(ac) ^ mb +#define T__E if ((AC(ac) & mb) == 0) INCPC +#define T__N if ((AC(ac) & mb) != 0) INCPC +#define T__A INCPC +#define IOC if (TSTF (F_USR) && !TSTF (F_UIO)) goto MUUO; +#define IO7(x,y) IOC; fptr = ((ITS)? x[ac]: y[ac]); \ + if (fptr == NULL) goto MUUO; \ + if (fptr (ea, MM_OPND)) INCPC; break; +#define IOA IOC; if (!ITS) ea = calc_ioea (inst, pflgs) +#define IOAM IOC; ea = ((ITS)? ((a10) Read (ea, MM_OPND)): \ + calc_ioea (inst, pflgs)) + +/* Flag tests */ + +#define MOVNF(x) if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1); \ + else if ((x) == 0) SETF (F_C0 | F_C1) +#define MOVMF(x) if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1) +#define INCF(x) if ((x) == 0) SETF (F_C0 | F_C1); \ + else if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1) +#define DECF(x) if ((x) == MAXPOS) SETF (F_C0 | F_AOV | F_T1); \ + else if ((x) != ONES) SETF (F_C0 | F_C1) +#define PUSHF if (LRZ (AC(ac)) == 0) SETF (F_T2) +#define POPF if (LRZ (AC(ac)) == RMASK) SETF (F_T2) +#define DMOVNF if (rs[1] == 0) { MOVNF (rs[0]); } + +t_stat sim_instr (void) +{ + +/* External and forward declarations */ + +extern void dfad (int32 ac, d10 *rs, int32 inv); +extern void dfmp (int32 ac, d10 *rs); +extern void dfdv (int32 ac, d10 *rs); +extern void dmul (int32 ac, d10 *rs); +extern void ddiv (int32 ac, d10 *rs); +extern void fix (int32 ac, d10 mb, t_bool rnd); +extern d10 fad (d10 val, d10 mb, t_bool rnd, int32 inv); +extern d10 fmp (d10 val, d10 mb, t_bool rnd); +extern t_bool fdv (d10 val, d10 mb, d10 *rs, t_bool rnd); +extern d10 fsc (d10 val, a10 ea); +extern d10 fltr (d10 mb); +extern int xtend (int32 ac, a10 ea, int32 pflgs); +extern void xtcln (int32 rlog); +extern d10 map (a10 ea, int32 prv); +extern d10 imul (d10 val, d10 mb); +extern t_bool idiv (d10 val, d10 mb, d10 *rs); +extern void mul (d10 val, d10 mb, d10 *rs); +extern t_bool divi (int32 ac, d10 mb, d10 *rs); +extern t_bool io710 (int32 ac, a10 ea); +extern t_bool io711 (int32 ac, a10 ea); +extern d10 io712 (a10 ea); +extern void io713 (d10 val, a10 ea); +extern void io714 (d10 val, a10 ea); +extern void io715 (d10 val, a10 ea); +extern t_bool io720 (int32 ac, a10 ea); +extern t_bool io721 (int32 ac, a10 ea); +extern d10 io722 (a10 ea); +extern void io723 (d10 val, a10 ea); +extern void io724 (d10 val, a10 ea); +extern void io725 (d10 val, a10 ea); + +extern t_bool clrcsh (a10 ea, int32 prv); +extern t_bool clrpt (a10 ea, int32 prv); +extern t_bool wrubr (a10 ea, int32 prv); +extern t_bool wrebr (a10 ea, int32 prv); +extern t_bool wrhsb (a10 ea, int32 prv); +extern t_bool wrspb (a10 ea, int32 prv); +extern t_bool wrcsb (a10 ea, int32 prv); +extern t_bool wrpur (a10 ea, int32 prv); +extern t_bool wrcstm (a10 ea, int32 prv); +extern t_bool ldbr1 (a10 ea, int32 prv); +extern t_bool ldbr2 (a10 ea, int32 prv); +extern t_bool ldbr3 (a10 ea, int32 prv); +extern t_bool ldbr4 (a10 ea, int32 prv); +extern t_bool rdubr (a10 ea, int32 prv); +extern t_bool rdebr (a10 ea, int32 prv); +extern t_bool rdhsb (a10 ea, int32 prv); +extern t_bool rdspb (a10 ea, int32 prv); +extern t_bool rdcsb (a10 ea, int32 prv); +extern t_bool rdpur (a10 ea, int32 prv); +extern t_bool rdcstm (a10 ea, int32 prv); +extern t_bool sdbr1 (a10 ea, int32 prv); +extern t_bool sdbr2 (a10 ea, int32 prv); +extern t_bool sdbr3 (a10 ea, int32 prv); +extern t_bool sdbr4 (a10 ea, int32 prv); +extern t_bool rdtim (a10 ea, int32 prv); +extern t_bool rdint (a10 ea, int32 prv); +extern t_bool wrtim (a10 ea, int32 prv); +extern t_bool wrint (a10 ea, int32 prv); +extern t_bool rdpcst (a10 ea, int32 prv); +extern t_bool wrpcst (a10 ea, int32 prv); +extern t_bool spm (a10 ea, int32 prv); +extern t_bool lpmr (a10 ea, int32 prv); + +extern int32 pi_ub_vec (int32 lvl, int32 *uba); +extern UNIT tim_unit; +extern int32 sim_rtc_init (int32 time); + +d10 adjsp (d10 val, a10 ea); +void ibp (a10 ea, int32 pflgs); +d10 ldb (a10 ea, int32 pflgs); +void dpb (d10 val, a10 ea, int32 pflgs); +void adjbp (int32 ac, a10 ea, int32 pflgs); +d10 add (d10 val, d10 mb); +d10 sub (d10 val, d10 mb); +void dadd (int32 ac, d10 *rs); +void dsub (int32 ac, d10 *rs); +int32 jffo (d10 val); +d10 lsh (d10 val, a10 ea); +d10 rot (d10 val, a10 ea); +d10 ash (d10 val, a10 ea); +void lshc (int32 ac, a10 ea); +void rotc (int32 ac, a10 ea); +void ashc (int32 ac, a10 ea); +void circ (int32 ac, a10 ea); +void blt (int32 ac, a10 ea, int32 pflgs); +void bltu (int32 ac, a10 ea, int32 pflgs, int dir); +a10 calc_ea (int64 inst, int32 prv); +a10 calc_ioea (d10 inst, int32 prv); +d10 calc_jrstfea (d10 inst, int32 pflgs); +void pi_dismiss (void); +void set_newflags (d10 fl, t_bool jrst); +extern t_bool aprid (a10 ea, int32 prv); +t_bool wrpi (a10 ea, int32 prv); +t_bool rdpi (a10 ea, int32 prv); +t_bool czpi (a10 ea, int32 prv); +t_bool copi (a10 ea, int32 prv); +t_bool wrapr (a10 ea, int32 prv); +t_bool rdapr (a10 ea, int32 prv); +t_bool czapr (a10 ea, int32 prv); +t_bool coapr (a10 ea, int32 prv); + +static t_bool (*io700d[16])() = { + &aprid, NULL, NULL, NULL, &wrapr, &rdapr, &czapr, &coapr, + NULL, NULL, NULL, NULL, &wrpi, &rdpi, &czpi, &copi }; +static t_bool (*io701d[16])() = { + NULL, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; +static t_bool (*io702d[16])() = { + &rdspb, &rdcsb, &rdpur, &rdcstm, &rdtim, &rdint, &rdhsb, NULL, + &wrspb, &wrcsb, &wrpur, &wrcstm, &wrtim, &wrint, &wrhsb, NULL }; +#define io700i io700d +static t_bool (*io701i[16])() = { + &clrcsh, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, + NULL, &rdpcst, NULL, &wrpcst, NULL, NULL, NULL, NULL }; +static t_bool (*io702i[16])() = { + &sdbr1, &sdbr2, &sdbr3, &sdbr4, &rdtim, &rdint, &rdhsb, &spm, + &ldbr1, &ldbr2, &ldbr3, &ldbr4, &wrtim, &wrint, &wrhsb, &lpmr }; +static t_stat jrst_tab[16] = { + JRST_U, JRST_U, JRST_U, 0, JRST_E, JRST_U, JRST_E, JRST_E, + JRST_UIO, 0, JRST_UIO, 0, JRST_E, JRST_U, 0, 0 }; + +register d10 inst; +register a10 PC; +register int32 pflgs; +int32 pager_flags = 0; /* pager: trap flags */ +t_bool pager_pi = FALSE; /* pager: in pi seq */ +int abortval = 0; /* abort value */ +t_bool (*fptr)(); + +/* Restore register state */ + +pager_PC = PC = saved_PC & AMASK; /* load local PC */ +set_dyn_ptrs (); /* set up local ptrs */ +pflgs = 0; /* not trap or PXCT */ +pager_pi = FALSE; /* not in pi sequence */ +rlog = 0; /* not in extend */ +pi_eval (); /* eval pi system */ +astop_cpu = 0; /* no addr stop */ +sim_rtc_init (tim_unit.wait); /* init calibration */ + +/* Abort handling + + Aborts may come from within the simulator to stop simulation (values > 0), + for page fails (values < 0), or for an interrupt check (value = 0). +*/ + +abortval = setjmp (save_env); /* set abort hdlr */ +if ((abortval > 0) || pager_pi) { /* stop or pi err? */ + if (pager_pi && (abortval == PAGE_FAIL)) + abortval = STOP_PAGINT; /* stop for pi err */ + saved_PC = pager_PC & AMASK; /* failing instr PC */ + last_inst = inst; /* last instr decoded */ + set_ac_display (ac_cur); /* set up AC display */ + return abortval; } /* return to SCP */ + +/* Page fail - checked against KS10 ucode */ + +else if (abortval == PAGE_FAIL) { /* page fail */ + register d10 mb; + if (rlog) xtcln (rlog); /* clean up extend */ + rlog = 0; /* clear log */ + if (pflgs & TRAP_CYCLE) flags = pager_flags; /* trap? get flags */ + if (!T20 || ITS) { /* TOPS-10 or ITS */ + register a10 ea; + if (ITS) ea = epta + EPT_ITS_PAG + (pi_m2lvl[pi_act] * 3); + else ea = upta + UPT_T10_PAG; + WriteP (ea, pager_word); /* write page fail wd */ + WriteP (ADDA (ea, 1), XWD (flags, pager_PC)); + mb = ReadP (ADDA (ea, 2)); } + else { WriteP (upta + UPT_T20_PFL, pager_word);/* write page fail wd */ + WriteP (upta + UPT_T20_OFL, XWD (flags, 0)); + WriteP (upta + UPT_T20_OPC, pager_PC); + mb = ReadP (upta + UPT_T20_NPC); } + JUMP (mb); /* set new PC */ + set_newflags (mb, FALSE); /* set new flags */ + pi_eval (); } /* eval pi system */ +else PC = pager_PC; /* intr, restore PC */ + +/* Main instruction fetch/decode loop: check clock queue, intr, trap, bkpt */ + +for ( ;; ) { /* loop until ABORT */ +register int32 op, ac, i, st, xr, xct_cnt; +register a10 ea; +register d10 mb, indrct; +d10 rs[2]; + +pager_PC = PC; /* update pager PC */ +pflgs = 0; /* not in PXCT or trap */ +xct_cnt = 0; /* count XCT's */ +if (astop_cpu) ABORT (STOP_ASTOP); /* address stop? */ +if (sim_interval <= 0) { /* check clock queue */ + if (i = sim_process_event ()) ABORT (i); /* error? stop sim */ + pi_eval (); } /* eval pi system */ + +/* PI interrupt (Unibus or system flags). + On the KS10, only JSR and XPCW are allowed as interrupt instructions. + Because of exec mode addressing, and unconditional processing of flags, + they are explicitly emulated here. +*/ + +if (qintr) { + int32 vec, uba; + pager_pi = TRUE; /* flag in pi seq */ + if (vec = pi_ub_vec (qintr, &uba)) { /* Unibus interrupt? */ + mb = ReadP (epta + EPT_UBIT + uba); /* get dispatch table */ + if (mb == 0) ABORT (STOP_ZERINT); /* invalid? stop */ + inst = ReadE ((((a10) mb) + (vec / 4)) & AMASK); + if (inst == 0) + ABORT (STOP_ZERINT); } + else inst = ReadP (epta + EPT_PIIT + (2 * qintr)); + op = GET_OP (inst); /* get opcode */ + ac = GET_AC (inst); /* get ac */ + if (op == OP_JSR) { /* JSR? */ + ea = calc_ea (inst, MM_CUR); /* calc ea, cur mode */ + WriteE (ea, FLPC); /* save flags+PC, exec */ + JUMP (INCA (ea)); /* PC = ea + 1 */ + set_newflags (0, FALSE); } /* set new flags */ + else if ((op == OP_JRST) && (ac == AC_XPCW)) { /* XPCW? */ + ea = calc_ea (inst, MM_CUR); /* calc ea, cur mode */ + WriteE (ea, XWD (flags, 0)); /* write flags, exec */ + WriteE (ADDA (ea, 1), PC); /* write PC, exec */ + rs[0] = ReadE (ADDA (ea, 2)); /* read new flags */ + rs[1] = ReadE (ADDA (ea, 3)); /* read new PC */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], FALSE); } /* set new flags */ + else ABORT (STOP_ILLINT); /* invalid instr */ + pi_act = pi_act | pi_l2bit[qintr]; /* set level active */ + pi_eval (); /* eval pi system */ + pager_pi = FALSE; /* end of sequence */ + if (sim_interval) sim_interval--; /* charge for instr */ + continue; } /* end if interrupt */ + +/* Traps fetch and execute an instruction from the current mode process table. + On the KS10, the fetch of the next instruction has started, and a page fail + trap on the instruction fetch takes precedence over the trap. During a trap, + flags are cleared before the execute, but if the execute aborts, they must + be restored. Also, the MUUO processor needs to know whether we are in a + trap sequence. Hence, trap in progress is recorded in pflgs, and the + traps for pager restoration are recorded in pager_flags. +*/ + +if (TSTF (F_T1 | F_T2) && PAGING) { + Read (pager_PC = PC, MM_CUR); /* test fetch */ + pflgs = TRAP_CYCLE; /* in a trap sequence */ + pager_flags = flags; /* save flags */ + ea = (TSTF (F_USR)? upta + UPT_TRBASE: epta + EPT_TRBASE) + + GET_TRAPS (flags); + inst = ReadP (ea); /* get trap instr */ + CLRF (F_T1 | F_T2); } /* clear flags */ + +/* Test for instruction breakpoint */ + +else { 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 */ + ABORT (STOP_IBKPT); } /* stop simulation */ + +/* Ready (at last) to get an instruction */ + + inst = Read (pager_PC = PC, MM_CUR); /* get instruction */ + INCPC; + sim_interval = sim_interval - 1; } + +/* Execute instruction. XCT and PXCT also return here. */ + +XCT: +op = GET_OP (inst); /* get opcode */ +ac = GET_AC (inst); /* get AC */ +for (indrct = inst, i = 0; i < ind_max; i++) { /* calc eff addr */ + ea = GET_ADDR (indrct); + xr = GET_XR (indrct); + if (xr) ea = (ea + ((a10) XR (xr, MM_EA))) & AMASK; + if (TST_IND (indrct)) indrct = Read (ea, MM_EA); + else break; } +if (i >= ind_max) + ABORT (STOP_IND); /* too many ind? stop */ +switch (op) { /* case on opcode */ + +/* UUO's (0000 - 0077) - checked against KS10 ucode */ + +case 0000: if (stop_op0) { ABORT (STOP_ILLEG); } + goto MUUO; +case 0001: /* local UUO's */ +case 0002: +case 0003: +case 0004: +case 0005: +case 0006: +case 0007: +case 0010: +case 0011: +case 0012: +case 0013: +case 0014: +case 0015: +case 0016: +case 0017: +case 0020: +case 0021: +case 0022: +case 0023: +case 0024: +case 0025: +case 0026: +case 0027: +case 0030: +case 0031: +case 0032: +case 0033: +case 0034: +case 0035: +case 0036: +case 0037: + Write (040, UUOWORD, MM_CUR); /* store op, ac, ea */ + inst = Read (041, MM_CUR); /* get new instr */ + goto XCT; +/* case 0040 - 0077: MUUO's, handled by default at end of case */ + +/* Floating point, bytes, multiple precision (0100 - 0177) */ + +/* case 0100: MUUO /* UJEN */ +/* case 0101: MUUO /* unassigned */ +case 0102: if (ITS && !TSTF (F_USR)) { /* GFAD (KL), XCTRI (ITS) */ + inst = Read (ea, MM_OPND); + pflgs = pflgs | ac; goto XCT; } + goto MUUO; +case 0103: if (ITS && !TSTF (F_USR)) { /* GFSB (KL), XCTR (ITS) */ + inst = Read (ea, MM_OPND); + pflgs = pflgs | ac; goto XCT; } + goto MUUO; +/* case 0104: MUUO /* JSYS (T20) */ +case 0105: AC(ac) = adjsp (AC(ac), ea); break; /* ADJSP */ +/* case 0106: MUUO /* GFMP (KL)*/ +/* case 0107: MUUO /* GFDV (KL) */ +case 0110: RD2; dfad (ac, rs, 0); break; /* DFAD */ +case 0111: RD2; dfad (ac, rs, 1); break; /* DFSB */ +case 0112: RD2; dfmp (ac, rs); break; /* DFMP */ +case 0113: RD2; dfdv (ac, rs); break; /* DFDV */ +case 0114: RD2; dadd (ac, rs); break; /* DADD */ +case 0115: RD2; dsub (ac, rs); break; /* DSUB */ +case 0116: RD2; dmul (ac, rs); break; /* DMUL */ +case 0117: RD2; ddiv (ac, rs); break; /* DDIV */ +case 0120: RD2; S2AC; break; /* DMOVE */ +case 0121: RD2; DMOVN (rs); S2AC; DMOVNF; break; /* DMOVN */ +case 0122: RD; fix(ac, mb, 0); break; /* FIX */ +case 0123: st = xtend (ac, ea, pflgs); /* EXTEND */ + rlog = 0; switch (st) { /* clear log */ + case XT_SKIP: INCPC; + case XT_NOSK: break; + default: goto MUUO; } + break; +case 0124: G2AC; WR2; break; /* DMOVEM */ +case 0125: G2AC; DMOVN (rs); WR2; DMOVNF; break; /* DMOVNM */ +case 0126: RD; fix (ac, mb, 1); break; /* FIXR */ +case 0127: RD; AC(ac) = fltr (mb); break; /* FLTR */ +/* case 0130: MUUO /* UFA */ +/* case 0131: MUUO /* DFN */ +case 0132: AC(ac) = fsc (AC(ac), ea); break; /* FSC */ +case 0133: if (!ac) ibp (ea, pflgs); /* IBP */ + else adjbp (ac, ea, pflgs); break; +case 0134: CIBP; LDB; CLRF (F_FPD); break; /* ILBP */ +case 0135: LDB; break; /* LDB */ +case 0136: CIBP; DPB; CLRF (F_FPD); break; /* IDBP */ +case 0137: DPB; break; /* DPB */ +case 0140: RD; AC(ac) = FAD (mb); break; /* FAD */ +/* case 0141: MUUO /* FADL */ +case 0142: RM; mb = FAD (mb); WR; break; /* FADM */ +case 0143: RM; AC(ac) = FAD (mb); WRAC; break; /* FADB */ +case 0144: RD; AC(ac) = FADR (mb); break; /* FADR */ +case 0145: AC(ac) = FADR (IMS); break; /* FADRI */ +case 0146: RM; mb = FADR (mb); WR; break; /* FADRM */ +case 0147: RM; AC(ac) = FADR (mb); WRAC; break; /* FADRB */ +case 0150: RD; AC(ac) = FSB (mb); break; /* FSB */ +/* case 0151: MUUO /* FSBL */ +case 0152: RM; mb = FSB (mb); WR; break; /* FSBM */ +case 0153: RM; AC(ac) = FSB (mb); WRAC; break; /* FSBB */ +case 0154: RD; AC(ac) = FSBR (mb); break; /* FSBR */ +case 0155: AC(ac) = FSBR (IMS); break; /* FSBRI */ +case 0156: RM; mb = FSBR (mb); WR; break; /* FSBRM */ +case 0157: RM; AC(ac) = FSBR (mb); WRAC; break; /* FSBRB */ +case 0160: RD; AC(ac) = FMP (mb); break; /* FMP */ +/* case 0161: MUUO /* FMPL */ +case 0162: RM; mb = FMP (mb); WR; break; /* FMPM */ +case 0163: RM; AC(ac) = FMP (mb); WRAC; break; /* FMPB */ +case 0164: RD; AC(ac) = FMPR (mb); break; /* FMPR */ +case 0165: AC(ac) = FMPR (IMS); break; /* FMPRI */ +case 0166: RM; mb = FMPR (mb); WR; break; /* FMPRM */ +case 0167: RM; AC(ac) = FMPR (mb); WRAC; break; /* FMPRB */ +case 0170: RD; if (FDV (mb)) S1AC; break; /* FDV */ +/* case 0171: MUUO /* FDVL */ +case 0172: RM; if (FDV (mb)) WR1; break; /* FDVM */ +case 0173: RM; if (FDV (mb)) { S1AC; WRAC; } break; /* FDVB */ +case 0174: RD; if (FDVR (mb)) S1AC; break; /* FDVR */ +case 0175: if (FDVR (IMS)) S1AC; break; /* FDVRI */ +case 0176: RM; if (FDVR (mb)) WR1; break; /* FDVRM */ +case 0177: RM; if (FDVR (mb)) { S1AC; WRAC; } break; /* FDVRB */ + +/* Move, arithmetic, shift, and jump (0200 - 0277) + + Note that instructions which modify the flags and store a + result in memory must prove the writeability of the result + location before modifying the flags. Also, 0247 and 0257, + if not implemented, are nops, not MUUO's. +*/ + +case 0200: RDAC; break; /* MOVE */ +case 0201: AC(ac) = ea; break; /* MOVEI */ +case 0202: WRAC; break; /* MOVEM */ +case 0203: RM; LAC; break; /* MOVES */ +case 0204: RD; AC(ac) = SWP (mb); break; /* MOVS */ +case 0205: AC(ac) = IMS; break; /* MOVSI */ +case 0206: mb = SWP (AC(ac)); WR; break; /* MOVSM */ +case 0207: RM; mb = SWP (mb); WR; LAC; break; /* MOVSS */ +case 0210: RD; AC(ac) = MOVN (mb); break; /* MOVN */ +case 0211: AC(ac) = MOVN (IM); break; /* MOVNI */ +case 0212: RM; mb = MOVN (AC(ac)); WR; break; /* MOVNM */ +case 0213: RM; mb = MOVN (mb); WR; LAC; break; /* MOVNS */ +case 0214: RD; AC(ac) = MOVM (mb); break; /* MOVM */ +case 0215: AC(ac) = ea; break; /* MOVMI */ +case 0216: RM; mb = MOVM (AC(ac)); WR; break; /* MOVMM */ +case 0217: RM; mb = MOVM (mb); WR; LAC; break; /* MOVMS */ +case 0220: RD; AC(ac) = IMUL (mb); break; /* IMUL */ +case 0221: AC(ac) = IMUL (IM); break; /* IMULI */ +case 0222: RM; mb = IMUL (mb); WR; break; /* IMULM */ +case 0223: RM; AC(ac) = IMUL (mb); WRAC; break; /* IMULB */ +case 0224: RD; MUL (mb); S2AC; break; /* MUL */ +case 0225: MUL (IM); S2AC; break; /* MULI */ +case 0226: RM; MUL (mb); WR1; break; /* MULM */ +case 0227: RM; MUL (mb); WR1; S2AC; break; /* MULB */ +case 0230: RD; if (IDIV (mb)) S2AC; break; /* IDIV */ +case 0231: if (IDIV (IM)) S2AC; break; /* IDIVI */ +case 0232: RM; if (IDIV (mb)) WR1; break; /* IDIVM */ +case 0233: RM; if (IDIV (mb)) { WR1; S2AC; } break; /* IDIVB */ +case 0234: RD; if (DIV (mb)) S2AC; break; /* DIV */ +case 0235: if (DIV (IM)) S2AC; break; /* DIVI */ +case 0236: RM; if (DIV (mb)) WR1; break; /* DIVM */ +case 0237: RM; if (DIV (mb)) { WR1; S2AC; } break; /* DIVB */ +case 0240: AC(ac) = ash (AC(ac), ea); break; /* ASH */ +case 0241: AC(ac) = rot (AC(ac), ea); break; /* ROT */ +case 0242: AC(ac) = lsh (AC(ac), ea); break; /* LSH */ +case 0243: AC(P1) = jffo (AC(ac)); /* JFFO */ + if (AC(ac)) JUMP (ea); break; +case 0244: ashc (ac, ea); break; /* ASHC */ +case 0245: rotc (ac, ea); break; /* ROTC */ +case 0246: lshc (ac, ea); break; /* LSHC */ +case 0247: if (ITS) circ (ac, ea); break; /* (ITS) CIRC */ +case 0250: RM; WRAC; AC(ac) = mb; break; /* EXCH */ +case 0251: blt (ac, ea, pflgs); break; /* BLT */ +case 0252: AOBAC; if (TGE (AC(ac))) JUMP (ea); break; /* AOBJP */ +case 0253: AOBAC; if (TL (AC(ac))) JUMP (ea); break; /* AOBJN */ +/* case 0254: /* shown later /* JRST */ +case 0255: if (flags & (ac << 14)) { /* JFCL */ + JUMP (ea); CLRF (ac << 14); } break; +case 0256: if (xct_cnt++ >= xct_max) /* XCT */ + ABORT (STOP_XCT); + inst = Read (ea, MM_OPND); + if (ac && !TSTF (F_USR) && !ITS) pflgs = pflgs | ac; + goto XCT; +case 0257: if (ITS) goto MUUO; /* MAP */ + AC(ac) = map (ea, MM_OPND); break; +case 0260: WRP (FLPC); AOBAC; /* PUSHJ */ + SUBJ (ea); PUSHF; break; +case 0261: RD; WRP (mb); AOBAC; PUSHF; break; /* PUSH */ +case 0262: RDP; WR; SOBAC; POPF; break; /* POP */ +case 0263: RDP; JUMP (mb); SOBAC; POPF; break; /* POPJ */ +case 0264: Write (ea, FLPC, MM_OPND); /* JSR */ + SUBJ (INCR (ea)); break; +case 0265: AC(ac) = FLPC; SUBJ (ea); break; /* JSP */ +case 0266: WRAC; AC(ac) = XWD (ea, PC); /* JSA */ + JUMP (INCR (ea)); break; +case 0267: AC(ac) = Read ((a10) LRZ (AC(ac)), MM_OPND); /* JRA */ + JUMP (ea); break; +case 0270: RD; AC(ac) = ADD (mb); break; /* ADD */ +case 0271: AC(ac) = ADD (IM); break; /* ADDI */ +case 0272: RM; mb = ADD (mb); WR; break; /* ADDM */ +case 0273: RM; AC(ac) = ADD (mb); WRAC; break; /* ADDB */ +case 0274: RD; AC(ac) = SUB (mb); break; /* SUB */ +case 0275: AC(ac) = SUB (IM); break; /* SUBI */ +case 0276: RM; mb = SUB (mb); WR; break; /* SUBM */ +case 0277: RM; AC(ac) = SUB (mb); WRAC; break; /* SUBB */ + +/* Compare, jump, skip instructions (0300 - 0377) - checked against KS10 ucode */ + +case 0300: break; /* CAI */ +case 0301: if (CL (IM)) INCPC; break; /* CAIL */ +case 0302: if (CE (IM)) INCPC; break; /* CAIE */ +case 0303: if (CLE (IM)) INCPC; break; /* CAILE */ +case 0304: INCPC; break; /* CAIA */ +case 0305: if (CGE (IM)) INCPC; break; /* CAIGE */ +case 0306: if (CN (IM)) INCPC; break; /* CAIN */ +case 0307: if (CG (IM)) INCPC; break; /* CAIG */ +case 0310: RD; break; /* CAM */ +case 0311: RD; if (CL (mb)) INCPC; break; /* CAML */ +case 0312: RD; if (CE (mb)) INCPC; break; /* CAME */ +case 0313: RD; if (CLE (mb)) INCPC; break; /* CAMLE */ +case 0314: RD; INCPC; break; /* CAMA */ +case 0315: RD; if (CGE (mb)) INCPC; break; /* CAMGE */ +case 0316: RD; if (CN (mb)) INCPC; break; /* CAMN */ +case 0317: RD; if (CG (mb)) INCPC; break; /* CAMG */ +case 0320: break; /* JUMP */ +case 0321: if (TL (AC(ac))) JUMP (ea); break; /* JUMPL */ +case 0322: if (TE (AC(ac))) JUMP (ea); break; /* JUMPE */ +case 0323: if (TLE( AC(ac))) JUMP (ea); break; /* JUMPLE */ +case 0324: JUMP (ea); break; /* JUMPA */ +case 0325: if (TGE (AC(ac))) JUMP (ea); break; /* JUMPGE */ +case 0326: if (TN (AC(ac))) JUMP (ea); break; /* JUMPN */ +case 0327: if (TG (AC(ac))) JUMP (ea); break; /* JUMPG */ +case 0330: RD; LAC; break; /* SKIP */ +case 0331: RD; LAC; if (TL (mb)) INCPC; break; /* SKIPL */ +case 0332: RD; LAC; if (TE (mb)) INCPC; break; /* SKIPE */ +case 0333: RD; LAC; if (TLE (mb)) INCPC; break; /* SKIPLE */ +case 0334: RD; LAC; INCPC; break; /* SKIPA */ +case 0335: RD; LAC; if (TGE (mb)) INCPC; break; /* SKIPGE */ +case 0336: RD; LAC; if (TN (mb)) INCPC; break; /* SKIPN */ +case 0337: RD; LAC; if (TG (mb)) INCPC; break; /* SKIPG */ +case 0340: AOJ; break; /* AOJ */ +case 0341: AOJ; if (TL (AC(ac))) JUMP (ea); break; /* AOJL */ +case 0342: AOJ; if (TE (AC(ac))) JUMP (ea); break; /* AOJE */ +case 0343: AOJ; if (TLE (AC(ac))) JUMP (ea); break;/* AOJLE */ +case 0344: AOJ; JUMP(ea); break; /* AOJA */ +case 0345: AOJ; if (TGE (AC(ac))) JUMP (ea); break;/* AOJGE */ +case 0346: AOJ; if (TN (AC(ac))) JUMP (ea); break; /* AOJN */ +case 0347: AOJ; if (TG (AC(ac))) JUMP (ea); break; /* AOJG */ +case 0350: AOS; break; /* AOS */ +case 0351: AOS; if (TL (mb)) INCPC; break; /* AOSL */ +case 0352: AOS; if (TE (mb)) INCPC; break; /* AOSE */ +case 0353: AOS; if (TLE (mb)) INCPC; break; /* AOSLE */ +case 0354: AOS; INCPC; break; /* AOSA */ +case 0355: AOS; if (TGE (mb)) INCPC; break; /* AOSGE */ +case 0356: AOS; if (TN (mb)) INCPC; break; /* AOSN */ +case 0357: AOS; if (TG (mb)) INCPC; break; /* AOSG */ +case 0360: SOJ; break; /* SOJ */ +case 0361: SOJ; if (TL (AC(ac))) JUMP (ea); break; /* SOJL */ +case 0362: SOJ; if (TE (AC(ac))) JUMP (ea); break; /* SOJE */ +case 0363: SOJ; if (TLE (AC(ac))) JUMP (ea); break;/* SOJLE */ +case 0364: SOJ; JUMP(ea); break; /* SOJA */ +case 0365: SOJ; if (TGE (AC(ac))) JUMP (ea); break;/* SOJGE */ +case 0366: SOJ; if (TN (AC(ac))) JUMP (ea); break; /* SOJN */ +case 0367: SOJ; if (TG (AC(ac))) JUMP (ea); break; /* SOJG */ +case 0370: SOS; break; /* SOS */ +case 0371: SOS; if (TL (mb)) INCPC; break; /* SOSL */ +case 0372: SOS; if (TE (mb)) INCPC; break; /* SOSE */ +case 0373: SOS; if (TLE (mb)) INCPC; break; /* SOSLE */ +case 0374: SOS; INCPC; break; /* SOSA */ +case 0375: SOS; if (TGE (mb)) INCPC; break; /* SOSGE */ +case 0376: SOS; if (TN (mb)) INCPC; break; /* SOSN */ +case 0377: SOS; if (TG (mb)) INCPC; break; /* SOSG */ + +/* Boolean instructions (0400 - 0477) - checked against KS10 ucode + + Note that for boolean B, the initial read checks writeability of + the memory operand; hence, it is safe to modify the AC. +*/ + +case 0400: AC(ac) = 0; break; /* SETZ */ +case 0401: AC(ac) = 0; break; /* SETZI */ +case 0402: mb = 0; WR; break; /* SETZM */ +case 0403: mb = 0; WR; AC(ac) = 0; break; /* SETZB */ +case 0404: RD; AC(ac) = AND (mb); break; /* AND */ +case 0405: AC(ac) = AND (IM); break; /* ANDI */ +case 0406: RM; mb = AND (mb); WR; break; /* ANDM */ +case 0407: RM; AC(ac) = AND (mb); WRAC; break; /* ANDB */ +case 0410: RD; AC(ac) = ANDCA (mb); break; /* ANDCA */ +case 0411: AC(ac) = ANDCA (IM); break; /* ANDCAI */ +case 0412: RM; mb = ANDCA (mb); WR; break; /* ANDCAM */ +case 0413: RM; AC(ac) = ANDCA (mb); WRAC; break; /* ANDCAB */ +case 0414: RDAC; break; /* SETM */ +case 0415: AC(ac) = ea; break; /* SETMI */ +case 0416: RM; WR; break; /* SETMM */ +case 0417: RMAC; WRAC; break; /* SETMB */ +case 0420: RD; AC(ac) = ANDCM (mb); break; /* ANDCM */ +case 0421: AC(ac) = ANDCM (IM); break; /* ANDCMI */ +case 0422: RM; mb = ANDCM (mb); WR; break; /* ANDCMM */ +case 0423: RM; AC(ac) = ANDCM (mb); WRAC; break; /* ANDCMB */ +case 0424: break; /* SETA */ +case 0425: break; /* SETAI */ +case 0426: WRAC; break; /* SETAM */ +case 0427: WRAC; break; /* SETAB */ +case 0430: RD; AC(ac) = XOR (mb); break; /* XOR */ +case 0431: AC(ac) = XOR (IM); break; /* XORI */ +case 0432: RM; mb = XOR (mb); WR; break; /* XORM */ +case 0433: RM; AC(ac) = XOR (mb); WRAC; break; /* XORB */ +case 0434: RD; AC(ac) = IOR (mb); break; /* IOR */ +case 0435: AC(ac) = IOR (IM); break; /* IORI */ +case 0436: RM; mb = IOR (mb); WR; break; /* IORM */ +case 0437: RM; AC(ac) = IOR (mb); WRAC; break; /* IORB */ +case 0440: RD; AC(ac) = ANDCB (mb); break; /* ANDCB */ +case 0441: AC(ac) = ANDCB (IM); break; /* ANDCBI */ +case 0442: RM; mb = ANDCB (mb); WR; break; /* ANDCBM */ +case 0443: RM; AC(ac) = ANDCB (mb); WRAC; break; /* ANDCBB */ +case 0444: RD; AC(ac) = EQV (mb); break; /* EQV */ +case 0445: AC(ac) = EQV (IM); break; /* EQVI */ +case 0446: RM; mb = EQV (mb); WR; break; /* EQVM */ +case 0447: RM; AC(ac) = EQV (mb); WRAC; break; /* EQVB */ +case 0450: RD; AC(ac) = SETCA (mb); break; /* SETCA */ +case 0451: AC(ac) = SETCA (IM); break; /* SETCAI */ +case 0452: RM; mb = SETCA (mb); WR; break; /* SETCAM */ +case 0453: RM; AC(ac) = SETCA (mb); WRAC; break; /* SETCAB */ +case 0454: RD; AC(ac) = ORCA (mb); break; /* ORCA */ +case 0455: AC(ac) = ORCA (IM); break; /* ORCAI */ +case 0456: RM; mb = ORCA (mb); WR; break; /* ORCAM */ +case 0457: RM; AC(ac) = ORCA (mb); WRAC; break; /* ORCAB */ +case 0460: RD; AC(ac) = SETCM (mb); break; /* SETCM */ +case 0461: AC(ac) = SETCM (IM); break; /* SETCMI */ +case 0462: RM; mb = SETCM (mb); WR; break; /* SETCMM */ +case 0463: RM; AC(ac) = SETCM (mb); WRAC; break; /* SETCMB */ +case 0464: RD; AC(ac) = ORCM (mb); break; /* ORCM */ +case 0465: AC(ac) = ORCM (IM); break; /* ORCMI */ +case 0466: RM; mb = ORCM (mb); WR; break; /* ORCMM */ +case 0467: RM; AC(ac) = ORCM (mb); WRAC; break; /* ORCMB */ +case 0470: RD; AC(ac) = ORCB (mb); break; /* ORCB */ +case 0471: AC(ac) = ORCB (IM); break; /* ORCBI */ +case 0472: RM; mb = ORCB (mb); WR; break; /* ORCBM */ +case 0473: RM; AC(ac) = ORCB (mb); WRAC; break; /* ORCBB */ +case 0474: AC(ac) = ONES; break; /* SETO */ +case 0475: AC(ac) = ONES; break; /* SETOI */ +case 0476: mb = ONES; WR; break; /* SETOM */ +case 0477: mb = ONES; WR; AC(ac) = ONES; break; /* SETOB */ + +/* Halfword instructions (0500 - 0577) - checked against KS10 ucode */ + +case 0500: RD; AC(ac) = LL (mb, AC(ac)); break; /* HLL */ +case 0501: AC(ac) = LL (IM, AC(ac)); break; /* HLLI */ +case 0502: RM; mb = LL (AC(ac), mb); WR; break; /* HLLM */ +case 0503: RM; mb = LL (mb, mb); WR; LAC; break; /* HLLS */ +case 0504: RD; AC(ac) = RL (mb, AC(ac)); break; /* HRL */ +case 0505: AC(ac) = RL (IM, AC(ac)); break; /* HRLI */ +case 0506: RM; mb = RL (AC(ac), mb); WR; break; /* HRLM */ +case 0507: RM; mb = RL (mb, mb); WR; LAC; break; /* HRLS */ +case 0510: RD; AC(ac) = LLZ (mb); break; /* HLLZ */ +case 0511: AC(ac) = LLZ (IM); break; /* HLLZI */ +case 0512: mb = LLZ (AC(ac)); WR; break; /* HLLZM */ +case 0513: RM; mb = LLZ (mb); WR; LAC; break; /* HLLZS */ +case 0514: RD; AC(ac) = RLZ (mb); break; /* HRLZ */ +case 0515: AC(ac) = RLZ (IM); break; /* HRLZI */ +case 0516: mb = RLZ (AC(ac)); WR; break; /* HRLZM */ +case 0517: RM; mb = RLZ (mb); WR; LAC; break; /* HRLZS */ +case 0520: RD; AC(ac) = LLO (mb); break; /* HLLO */ +case 0521: AC(ac) = LLO (IM); break; /* HLLOI */ +case 0522: mb = LLO (AC(ac)); WR; break; /* HLLOM */ +case 0523: RM; mb = LLO (mb); WR; LAC; break; /* HLLOS */ +case 0524: RD; AC(ac) = RLO (mb); break; /* HRLO */ +case 0525: AC(ac) = RLO (IM); break; /* HRLOI */ +case 0526: mb = RLO (AC(ac)); WR; break; /* HRLOM */ +case 0527: RM; mb = RLO (mb); WR; LAC; break; /* HRLOS */ +case 0530: RD; AC(ac) = LLE (mb); break; /* HLLE */ +case 0531: AC(ac) = LLE (IM); break; /* HLLEI */ +case 0532: mb = LLE (AC(ac)); WR; break; /* HLLEM */ +case 0533: RM; mb = LLE (mb); WR; LAC; break; /* HLLES */ +case 0534: RD; AC(ac) = RLE (mb); break; /* HRLE */ +case 0535: AC(ac) = RLE (IM); break; /* HRLEI */ +case 0536: mb = RLE (AC(ac)); WR; break; /* HRLEM */ +case 0537: RM; mb = RLE (mb); WR; LAC; break; /* HRLES */ +case 0540: RD; AC(ac) = RR (mb, AC(ac)); break; /* HRR */ +case 0541: AC(ac) = RR (IM, AC(ac)); break; /* HRRI */ +case 0542: RM; mb = RR (AC(ac), mb); WR; break; /* HRRM */ +case 0543: RM; mb = RR (mb, mb); WR; LAC; break; /* HRRS */ +case 0544: RD; AC(ac) = LR (mb, AC(ac)); break; /* HLR */ +case 0545: AC(ac) = LR (IM, AC(ac)); break; /* HLRI */ +case 0546: RM; mb = LR (AC(ac), mb); WR; break; /* HLRM */ +case 0547: RM; mb = LR (mb, mb); WR; LAC; break; /* HLRS */ +case 0550: RD; AC(ac) = RRZ (mb); break; /* HRRZ */ +case 0551: AC(ac) = RRZ (IM); break; /* HRRZI */ +case 0552: mb = RRZ (AC(ac)); WR; break; /* HRRZM */ +case 0553: RM; mb = RRZ(mb); WR; LAC; break; /* HRRZS */ +case 0554: RD; AC(ac) = LRZ (mb); break; /* HLRZ */ +case 0555: AC(ac) = LRZ (IM); break; /* HLRZI */ +case 0556: mb = LRZ (AC(ac)); WR; break; /* HLRZM */ +case 0557: RM; mb = LRZ (mb); WR; LAC; break; /* HLRZS */ +case 0560: RD; AC(ac) = RRO (mb); break; /* HRRO */ +case 0561: AC(ac) = RRO (IM); break; /* HRROI */ +case 0562: mb = RRO (AC(ac)); WR; break; /* HRROM */ +case 0563: RM; mb = RRO (mb); WR; LAC; break; /* HRROS */ +case 0564: RD; AC(ac) = LRO (mb); break; /* HLRO */ +case 0565: AC(ac) = LRO (IM); break; /* HLROI */ +case 0566: mb = LRO (AC(ac)); WR; break; /* HLROM */ +case 0567: RM; mb = LRO (mb); WR; LAC; break; /* HLROS */ +case 0570: RD; AC(ac) = RRE (mb); break; /* HRRE */ +case 0571: AC(ac) = RRE (IM); break; /* HRREI */ +case 0572: mb = RRE (AC(ac)); WR; break; /* HRREM */ +case 0573: RM; mb = RRE (mb); WR; LAC; break; /* HRRES */ +case 0574: RD; AC(ac) = LRE (mb); break; /* HLRE */ +case 0575: AC(ac) = LRE (IM); break; /* HLREI */ +case 0576: mb = LRE (AC(ac)); WR; break; /* HLREM */ +case 0577: RM; mb = LRE (mb); WR; LAC; break; /* HLRES */ + +/* Test instructions (0600 - 0677) - checked against KS10 ucode + In the KS10 ucode, TDN and TSN do not fetch an operand; the Processor + Reference Manual describes them as NOPs that reference memory. +*/ + +case 0600: break; /* TRN */ +case 0601: break; /* TLN */ +case 0602: TR_; T__E; break; /* TRNE */ +case 0603: TL_; T__E; break; /* TLNE */ +case 0604: T__A; break; /* TRNA */ +case 0605: T__A; break; /* TLNA */ +case 0606: TR_; T__N; break; /* TRNN */ +case 0607: TL_; T__N; break; /* TLNN */ +case 0610: TD_; break; /* TDN */ +case 0611: TS_; break; /* TSN */ +case 0612: TD_; T__E; break; /* TDNE */ +case 0613: TS_; T__E; break; /* TSNE */ +case 0614: TD_; T__A; break; /* TDNA */ +case 0615: TS_; T__A; break; /* TSNA */ +case 0616: TD_; T__N; break; /* TDNN */ +case 0617: TS_; T__N; break; /* TSNN */ +case 0620: TR_; T_Z; break; /* TRZ */ +case 0621: TL_; T_Z; break; /* TLZ */ +case 0622: TR_; T__E; T_Z; break; /* TRZE */ +case 0623: TL_; T__E; T_Z; break; /* TLZE */ +case 0624: TR_; T__A; T_Z; break; /* TRZA */ +case 0625: TL_; T__A; T_Z; break; /* TLZA */ +case 0626: TR_; T__N; T_Z; break; /* TRZN */ +case 0627: TL_; T__N; T_Z; break; /* TLZN */ +case 0630: TD_; T_Z; break; /* TDZ */ +case 0631: TS_; T_Z; break; /* TSZ */ +case 0632: TD_; T__E; T_Z; break; /* TDZE */ +case 0633: TS_; T__E; T_Z; break; /* TSZE */ +case 0634: TD_; T__A; T_Z; break; /* TDZA */ +case 0635: TS_; T__A; T_Z; break; /* TSZA */ +case 0636: TD_; T__N; T_Z; break; /* TDZN */ +case 0637: TS_; T__N; T_Z; break; /* TSZN */ +case 0640: TR_; T_C; break; /* TRC */ +case 0641: TL_; T_C; break; /* TLC */ +case 0642: TR_; T__E; T_C; break; /* TRCE */ +case 0643: TL_; T__E; T_C; break; /* TLCE */ +case 0644: TR_; T__A; T_C; break; /* TRCA */ +case 0645: TL_; T__A; T_C; break; /* TLCA */ +case 0646: TR_; T__N; T_C; break; /* TRCN */ +case 0647: TL_; T__N; T_C; break; /* TLCN */ +case 0650: TD_; T_C; break; /* TDC */ +case 0651: TS_; T_C; break; /* TSC */ +case 0652: TD_; T__E; T_C; break; /* TDCE */ +case 0653: TS_; T__E; T_C; break; /* TSCE */ +case 0654: TD_; T__A; T_C; break; /* TDCA */ +case 0655: TS_; T__A; T_C; break; /* TSCA */ +case 0656: TD_; T__N; T_C; break; /* TDCN */ +case 0657: TS_; T__N; T_C; break; /* TSCN */ +case 0660: TR_; T_O; break; /* TRO */ +case 0661: TL_; T_O; break; /* TLO */ +case 0662: TR_; T__E; T_O; break; /* TROE */ +case 0663: TL_; T__E; T_O; break; /* TLOE */ +case 0664: TR_; T__A; T_O; break; /* TROA */ +case 0665: TL_; T__A; T_O; break; /* TLOA */ +case 0666: TR_; T__N; T_O; break; /* TRON */ +case 0667: TL_; T__N; T_O; break; /* TLON */ +case 0670: TD_; T_O; break; /* TDO */ +case 0671: TS_; T_O; break; /* TSO */ +case 0672: TD_; T__E; T_O; break; /* TDOE */ +case 0673: TS_; T__E; T_O; break; /* TSOE */ +case 0674: TD_; T__A; T_O; break; /* TDOA */ +case 0675: TS_; T__A; T_O; break; /* TSOA */ +case 0676: TD_; T__N; T_O; break; /* TDON */ +case 0677: TS_; T__N; T_O; break; /* TSON */ + +/* I/O instructions (0700 - 0777) + + Only the defined I/O instructions have explicit case labels; + the rest default to unimplemented (monitor UUO). Note that + 710-715 and 720-725 have different definitions under ITS and + use normal effective addresses instead of the special address + calculation required by TOPS-10 and TOPS-20. +*/ + +case 0700: IO7 (io700i, io700d); break; /* I/O 0 */ +case 0701: IO7 (io701i, io701d); break; /* I/O 1 */ +case 0702: IO7 (io702i, io702d); break; /* I/O 2 */ +case 0704: IOC; AC(ac) = Read (ea, OPND_PXCT); break; /* UMOVE */ +case 0705: IOC; Write (ea, AC(ac), OPND_PXCT); break; /* UMOVEM */ +case 0710: IOA; if (io710 (ac, ea)) INCPC; break; /* TIOE, IORDI */ +case 0711: IOA; if (io711 (ac, ea)) INCPC; break; /* TION, IORDQ */ +case 0712: IOAM; AC(ac) = io712 (ea); break; /* RDIO, IORD */ +case 0713: IOAM; io713 (AC(ac), ea); break; /* WRIO, IOWR */ +case 0714: IOA; io714 (AC(ac), ea); break; /* BSIO, IOWRI */ +case 0715: IOA; io715 (AC(ac), ea); break; /* BCIO, IOWRQ */ +case 0716: IOC; bltu (ac, ea, pflgs, 0); break; /* BLTBU */ +case 0717: IOC; bltu (ac, ea, pflgs, 1); break; /* BLTUB */ +case 0720: IOA; if (io720 (ac, ea)) INCPC; break; /* TIOEB, IORDBI */ +case 0721: IOA; if (io721 (ac, ea)) INCPC; break; /* TIONB, IORDBQ */ +case 0722: IOAM; AC(ac) = io722 (ea); break; /* RDIOB, IORDB */ +case 0723: IOAM; io723 (AC(ac), ea); break; /* WRIOB, IOWRB */ +case 0724: IOA; io724 (AC(ac), ea); break; /* BSIOB, IOWRBI */ +case 0725: IOA; io725 (AC(ac), ea); break; /* BCIOB, IOWRBQ */ + +/* If undefined, monitor UUO - checked against KS10 ucode + The KS10 implements a much more limited version of MUUO flag handling. + In the KS10, the trap ucode checks for opcodes 000-077. If the opcode + is in that range, the trap flags are not cleared. Instead, the MUUO + microcode stores the flags with traps cleared, and uses the trap flags + to determine how to vector. Thus, MUUO's >= 100 will vector incorrectly. +*/ + +default: +MUUO: if (T20) { /* TOPS20? */ + int32 tf = (op << (INST_V_OP - 18)) | (ac << (INST_V_AC - 18)); + WriteP (upta + UPT_MUUO, XWD ( /* store flags,,op+ac */ + flags & ~(F_T2 | F_T1), tf)); /* traps clear */ + WriteP (upta + UPT_MUPC, PC); /* store PC */ + WriteP (upta + UPT_T20_UEA, ea); /* store eff addr */ + WriteP (upta + UPT_T20_CTX, UBRWORD); } /* store context */ + else { WriteP (upta + UPT_MUUO, UUOWORD); /* store instr word */ + WriteP (upta + UPT_MUPC, XWD ( /* store flags,,PC */ + flags & ~(F_T2 | F_T1), PC)); /* traps clear */ + WriteP (upta + UPT_T10_CTX, UBRWORD); } /* store context */ + ea = upta + (TSTF (F_USR)? UPT_UNPC: UPT_ENPC) + + (pflgs & TRAP_CYCLE? UPT_NPCT: 0); /* calculate vector */ + mb = ReadP (ea); /* new flags, PC */ + JUMP (mb); /* set new PC */ + if (TSTF (F_USR)) mb = mb | XWD (F_UIO, 0); /* set PCU */ + set_newflags (mb, FALSE); /* set new flags */ + break; + +/* JRST - checked against KS10 ucode + Differences from the KS10: the KS10 + - (JRSTF, JEN) refetches the base instruction from PC - 1 + - (XJEN) dismisses interrupt before reading the new flags and PC + - (XPCW) writes the old flags and PC before reading the new + ITS microcode includes extended JRST's, although they are not used +*/ + +case 0254: /* JRST */ + i = jrst_tab[ac]; /* get subop flags */ + if ((i == 0) || ((i == JRST_E) && TSTF (F_USR)) || + ((i == JRST_UIO) && TSTF (F_USR) && !TSTF (F_UIO))) + goto MUUO; /* not legal */ + switch (ac) { /* case on subopcode */ + case 000: /* JRST 0 = jump */ + case 001: /* JRST 1 = portal */ + JUMP (ea); + break; + case 002: /* JRST 2 = JRSTF */ + mb = calc_jrstfea (inst, pflgs); /* recalc addr w flgs */ + JUMP (ea); /* set new PC */ + set_newflags (mb, TRUE); /* set new flags */ + break; + case 004: /* JRST 4 = halt */ + JUMP (ea); /* old_PC = halt + 1 */ + pager_PC = PC; /* force right PC */ + ABORT (STOP_HALT); /* known to be exec */ + break; + case 005: /* JRST 5 = XJRSTF */ + RD2; /* read doubleword */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], TRUE); /* set new flags */ + break; + case 006: /* JRST 6 = XJEN */ + RD2; /* read doubleword */ + pi_dismiss (); /* page ok, dismiss */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], FALSE); /* known to be exec */ + break; + case 007: /* JRST 7 = XPCW */ + ea = ADDA (i = ea, 2); /* new flags, PC */ + RD2; /* read, test page fail */ + ReadM (INCA (i), MM_OPND); /* test PC write */ + Write (i, XWD (flags, 0), MM_OPND); /* write flags */ + Write (INCA (i), PC, MM_OPND); /* write PC */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], FALSE); /* known to be exec */ + break; + case 010: /* JRST 10 = dismiss */ + pi_dismiss (); /* dismiss int */ + JUMP (ea); /* set new PC */ + break; + case 012: /* JRST 12 = JEN */ + mb = calc_jrstfea (inst, pflgs); /* recalc addr w flgs */ + JUMP (ea); /* set new PC */ + set_newflags (mb, TRUE); /* set new flags */ + pi_dismiss (); /* dismiss int */ + break; + case 014: /* JRST 14 = SFM */ + Write (ea, XWD (flags, 0), MM_OPND); + break; + case 015: /* JRST 15 = XJRST */ + if (!T20) goto MUUO; /* only in TOPS20 */ + JUMP (Read (ea, MM_OPND)); /* jump to M[ea] */ + break; } /* end case subop */ + break; } /* end case op */ +} /* end for */ + +/* Should never get here */ + +ABORT (STOP_UNKNOWN); +} + +/* Single word integer routines */ + +/* Integer add + + Truth table for integer add + + case a b r flags + 1 + + + none + 2 + + - AOV + C1 + 3 + - + C0 + C1 + 4 + - - - + 5 - + + C0 + C1 + 6 - + - - + 7 - - + AOV + C0 + 8 - - - C0 + C1 +*/ + +d10 add (d10 a, d10 b) +{ +d10 r; + +r = (a + b) & DMASK; +if (TSTS (a & b)) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ + return r; } +if (!TSTS (a | b)) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); /* case 2 */ + return r; } /* case 1 */ +if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +return r; +} + +/* Integer subtract - actually ac + ~op + 1 */ + +d10 sub (d10 a, d10 b) +{ +d10 r; + +r = (a - b) & DMASK; +if (TSTS (a & ~b)) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ + return r; } +if (!TSTS (a | ~b)) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); /* case 2 */ + return r; } /* case 1 */ +if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +return r; +} + + +/* Logical shift */ + +d10 lsh (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea); + +if (sc > 35) return 0; +if (ea & RSIGN) return (val >> sc); +return ((val << sc) & DMASK); +} + +/* Rotate */ + +d10 rot (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea) % 36; + +if (sc == 0) return val; +if (ea & RSIGN) sc = 36 - sc; +return (((val << sc) | (val >> (36 - sc))) & DMASK); +} + +/* Double word integer instructions */ + +/* Double add - see case table for single add */ + +void dadd (int32 ac, d10 *rs) +{ +d10 r; +int32 p1 = ADDAC (ac, 1); + +AC(p1) = CLRS (AC(p1)) + CLRS (rs[1]); /* add lo */ +r = (AC(ac) + rs[0] + (TSTS (AC(p1))? 1: 0)) & DMASK; /* add hi+cry */ +if (TSTS (AC(ac) & rs[0])) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); } /* case 7 */ +else if (!TSTS (AC(ac) | rs[0])) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); } /* case 2 */ +else if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +AC(ac) = r; +AC(p1) = TSTS (r)? SETS (AC(p1)): CLRS (AC(p1)); +return; +} + +/* Double subtract - see comments for single subtract */ + +void dsub (int32 ac, d10 *rs) +{ +d10 r; +int32 p1 = ADDAC (ac, 1); + +AC(p1) = CLRS (AC(p1)) - CLRS (rs[1]); /* sub lo */ +r = (AC(ac) - rs[0] - (TSTS (AC(p1))? 1: 0)) & DMASK; /* sub hi,borrow */ +if (TSTS (AC(ac) & ~rs[0])) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); } /* case 7 */ +else if (!TSTS (AC(ac) | ~rs[0])) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); } /* case 2 */ +else if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +AC(ac) = r; +AC(p1) = (TSTS (r)? SETS (AC(p1)): CLRS (AC(p1))) & DMASK; +return; +} + + +/* Logical shift combined */ + +void lshc (int32 ac, a10 ea) +{ +int32 p1 = ADDAC (ac, 1); +int32 sc = LIT8 (ea); + +if (sc > 71) AC(ac) = AC(p1) = 0; +else if (ea & RSIGN) { + if (sc >= 36) { + AC(p1) = AC(ac) >> (sc - 36); + AC(ac) = 0; } + else { AC(p1) = ((AC(p1) >> sc) | (AC(ac) << (36 - sc))) & DMASK; + AC(ac) = AC(ac) >> sc; } } +else { if (sc >= 36) { + AC(ac) = (AC(p1) << (sc - 36)) & DMASK; + AC(p1) = 0; } + else { AC(ac) = ((AC(ac) << sc) | (AC(p1) >> (36 - sc))) & DMASK; + AC(p1) = (AC(p1) << sc) & DMASK; } } +return; +} + +/* Rotate combined */ + +void rotc (int32 ac, a10 ea) +{ +int32 p1 = ADDAC (ac, 1); +int32 sc = LIT8 (ea) % 72; +d10 t = AC(ac); + +if (sc == 0) return; +if (ea & RSIGN) sc = 72 - sc; +if (sc >= 36) { + AC(ac) = ((AC(p1) << (sc - 36)) | (t >> (72 - sc))) & DMASK; + AC(p1) = ((t << (sc - 36)) | (AC(p1) >> (72 - sc))) & DMASK; } +else { AC(ac) = ((t << sc) | (AC(p1) >> (36 - sc))) & DMASK; + AC(p1) = ((AC(p1) << sc) | (t >> (36 - sc))) & DMASK; } +return; +} + +/* Arithmetic shifts */ + +d10 ash (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea); +d10 sign = TSTS (val); +d10 fill = sign? ONES: 0; +d10 so; + +if (sc == 0) return val; +if (sc > 35) sc = 35; /* cap sc at 35 */ +if (ea & RSIGN) + return (((val >> sc) | (fill << (36 - sc))) & DMASK); +so = val >> (35 - sc); /* bits lost left + sign */ +if (so != (sign? bytemask[sc + 1]: 0)) SETF (F_AOV | F_T1); +return (sign | ((val << sc) & MMASK)); +} + +void ashc (int32 ac, a10 ea) +{ +int32 sc = LIT8 (ea); +int32 p1 = ADDAC (ac, 1); +d10 sign = TSTS (AC(ac)); +d10 fill = sign? ONES: 0; +d10 so; + +if (sc == 0) return; +if (sc > 70) sc = 70; /* cap sc at 70 */ +AC(ac) = CLRS (AC(ac)); /* clear signs */ +AC(p1) = CLRS (AC(p1)); +if (ea & RSIGN) { + if (sc >= 35) { /* right 36..70 */ + AC(p1) = ((AC(ac) >> (sc - 35)) | (fill << (70 - sc))) & DMASK; + AC(ac) = fill; } + else { AC(p1) = sign | /* right 1..35 */ + (((AC(p1) >> sc) | (AC(ac) << (35 - sc))) & MMASK); + AC(ac) = ((AC(ac) >> sc) | (fill << (35 - sc))) & DMASK; } } +else { if (sc >= 35) { /* left 36..70 */ + so = AC(p1) >> (70 - sc); /* bits lost left */ + if ((AC(ac) != (sign? MMASK: 0)) || + (so != (sign? bytemask[sc - 35]: 0))) SETF (F_AOV | F_T1); + AC(ac) = sign | ((AC(p1) << (sc - 35)) & MMASK); + AC(p1) = sign; } + else { so = AC(ac) >> (35 - sc); /* bits lost left */ + if (so != (sign? bytemask[sc]: 0)) SETF (F_AOV | F_T1); + AC(ac) = sign | + (((AC(ac) << sc) | (AC(p1) >> (35 - sc))) & MMASK); + AC(p1) = sign | ((AC(p1) << sc) & MMASK); } } +return; +} + +/* Effective address routines */ + +/* Calculate effective address - used by byte instructions, extended + instructions, and interrupts to get a different mapping context from + the main loop. prv is either EABP_PXCT or MM_CUR. +*/ + +a10 calc_ea (d10 inst, int32 prv) +{ +register int32 i, ea, xr; +register d10 indrct; + +for (indrct = inst, i = 0; i < ind_max; i++) { + ea = GET_ADDR (indrct); + xr = GET_XR (indrct); + if (xr) ea = (ea + ((a10) XR (xr, prv))) & AMASK; + if (TST_IND (indrct)) indrct = Read (ea, prv); + else break; } +if (i >= ind_max) ABORT (STOP_IND); +return ea; +} + +/* Calculate I/O effective address. Cases: + - No index or indirect, return addr from instruction + - Index only, index >= 0, return 36b sum of addr + index + - Index only, index <= 0, return 18b sum of addr + index + - Indirect, calculate 18b sum of addr + index, return + entire word fetch (single level) +*/ + +a10 calc_ioea (d10 inst, int32 pflgs) +{ +register int32 xr; +register a10 ea; + +xr = GET_XR (inst); +ea = GET_ADDR (inst); +if (TST_IND (inst)) { /* indirect? */ + if (xr) ea = (ea + ((a10) XR (xr, MM_EA))) & AMASK; + ea = (a10) Read (ea, MM_EA); } +else if (xr) { /* direct + idx? */ + ea = ea + ((a10) XR (xr, MM_EA)); + if (TSTS (XR (xr, MM_EA))) ea = ea & AMASK; } +return ea; +} + +/* Calculate JRSTF effective address. This routine preserves + the left half of the effective address, to be the new flags. +*/ + +d10 calc_jrstfea (d10 inst, int32 pflgs) +{ +register int32 i, xr; +register d10 mb; + +for (i = 0; i < ind_max; i++) { + mb = inst; + xr = GET_XR (inst); + if (xr) mb = (mb & AMASK) + XR (xr, MM_EA); + if (TST_IND (inst)) inst = Read (((a10) mb) & AMASK, MM_EA); + else break; } +if (i >= ind_max) ABORT (STOP_IND); +return (mb & DMASK); +} + +/* Byte pointer routines */ + +/* Increment byte pointer - checked against KS10 ucode */ + +void ibp (a10 ea, int32 pflgs) +{ +register int32 p, s; +register d10 bp; + +bp = ReadM (ea, MM_OPND); /* get byte ptr */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +p = p - s; /* adv P */ +if (p < 0) { /* end of word? */ + bp = (bp & LMASK) | (INCR (bp)); /* incr addr */ + p = (36 - s) & 077; } /* reset P */ +bp = PUT_P (bp, p); /* store new P */ +Write (ea, bp, MM_OPND); /* store byte ptr */ +return; +} + +/* Load byte */ + +d10 ldb (a10 ea, int32 pflgs) +{ +register a10 ba; +register int32 p, s; +register d10 bp, wd; + +bp = Read (ea, MM_OPND); /* get byte ptr */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EABP); /* get addr of byte */ +wd = Read (ba, MM_BSTK); /* read word */ +wd = (wd >> p); /* align byte */ +wd = wd & bytemask[s]; /* mask to size */ +return wd; +} + +/* Deposit byte - must use read and write to get page fail correct */ + +void dpb (d10 val, a10 ea, int32 pflgs) +{ +register a10 ba; +register int32 p, s; +register d10 bp, wd, mask; + +bp = Read (ea, MM_OPND); /* get byte ptr */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EABP); /* get addr of byte */ +wd = Read (ba, MM_BSTK); /* read word */ +mask = bytemask[s] << p; /* shift mask, val */ +val = val << p; +wd = (wd & ~mask) | (val & mask); /* insert byte */ +Write (ba, wd & DMASK, MM_BSTK); +return; +} + +/* Adjust byte pointer - checked against KS10 ucode + The KS10 divide checks if the bytes per word = 0, which is a simpler + formulation of the processor reference manual check. +*/ + +void adjbp (int32 ac, a10 ea, int32 pflgs) +{ +int32 p, s; +d10 bp, newby, left, byadj, bywrd, val, wdadj; + +val = AC(ac); /* get adjustment */ +bp = Read (ea, MM_OPND); /* get byte pointer */ +p = GET_P (bp); /* get p */ +s = GET_S (bp); /* get s */ +if (s) { + left = (36 - p) / s; /* bytes to left of p */ + bywrd = left + (p / s); /* bytes per word */ + if (bywrd == 0) { /* zero bytes? */ + SETF (F_AOV | F_T1 | F_DCK); /* set flags */ + return; } /* abort operation */ + newby = left + SXT (val); /* adjusted byte # */ + wdadj = newby / bywrd; /* word adjustment */ + byadj = (newby >= 0)? newby % bywrd: -((-newby) % bywrd); + if (byadj <= 0) { + byadj = byadj + bywrd; /* make adj positive */ + wdadj = wdadj - 1; } + p = (36 - ((int32) byadj) * s) - ((36 - p) % s); /* new p */ + bp = (PUT_P (bp, p) & LMASK) | ((bp + wdadj) & RMASK); } +AC(ac) = bp; +return; +} + +/* Block transfer - checked against KS10 ucode + The KS10 uses instruction specific recovery code in page fail + to set the AC properly for restart. Lacking this mechanism, + the simulator must test references in advance. + The clocking test guarantees forward progress under single step. +*/ + +void blt (int32 ac, a10 ea, int32 pflgs) +{ +a10 srca = (a10) LRZ (AC(ac)); +a10 dsta = (a10) RRZ (AC(ac)); +a10 lnt = ea - dsta + 1; +d10 srcv; +int32 flg, t; + +AC(ac) = XWD (srca + lnt, dsta + lnt); +for (flg = 0; dsta <= ea; flg++) { /* loop */ + if (flg && (t = test_int ())) { /* timer event? */ + AC(ac) = XWD (srca, dsta); /* AC for intr */ + ABORT (t); } + if (AccViol (srca & AMASK, MM_BSTK, PTF_RD)) { /* src access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + Read (srca & AMASK, MM_BSTK); } /* force trap */ + if (AccViol (dsta & AMASK, MM_OPND, PTF_WR)) { /* dst access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + ReadM (dsta & AMASK, MM_OPND); } /* force trap */ + srcv = Read (srca & AMASK, MM_BSTK); /* read */ + Write (dsta & AMASK, srcv, MM_OPND); /* write */ + srca = srca + 1; /* incr addr */ + dsta = dsta + 1; } +return; +} + +/* I/O block transfers - byte to Unibus (0) and Unibus to byte (1) */ + +#define BYTE1 0776000000000 +#define BYTE2 0001774000000 +#define BYTE3 0000003770000 +#define BYTE4 0000000007760 +/* unused 0000000000017 */ + +void bltu (int32 ac, a10 ea, int32 pflgs, int dir) +{ +a10 srca = (a10) LRZ (AC(ac)); +a10 dsta = (a10) RRZ (AC(ac)); +a10 lnt = ea - dsta + 1; +d10 srcv, dstv; +int32 flg, t; + +AC(ac) = XWD (srca + lnt, dsta + lnt); +for (flg = 0; dsta <= ea; flg++) { /* loop */ + if (flg && (t = test_int ())) { /* timer event? */ + AC(ac) = XWD (srca, dsta); /* AC for intr */ + ABORT (t); } + if (AccViol (srca & AMASK, MM_BSTK, PTF_RD)) { /* src access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + Read (srca & AMASK, MM_BSTK); } /* force trap */ + if (AccViol (dsta & AMASK, MM_OPND, PTF_WR)) { /* dst access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + ReadM (dsta & AMASK, MM_OPND); } /* force trap */ + srcv = Read (srca & AMASK, MM_BSTK); /* read */ + if (dir) dstv = ((srcv << 10) & BYTE1) | ((srcv >> 6) & BYTE2) | + ((srcv << 12) & BYTE3) | ((srcv >> 4) & BYTE4); + else dstv = ((srcv & BYTE1) >> 10) | ((srcv & BYTE2) << 6) | + ((srcv & BYTE3) >> 12) | ((srcv & BYTE4) << 4); + Write (dsta & AMASK, dstv, MM_OPND); /* write */ + srca = srca + 1; /* incr addr */ + dsta = dsta + 1; } +return; +} + +/* Utility routine to test for I/O event and interrupt */ + +int32 test_int (void) +{ +int32 t; + +if (sim_interval <= 0) { /* check queue */ + if (t = sim_process_event ()) return t; /* IO event? */ + if (pi_eval ()) return (INTERRUPT); } /* interrupt? */ +else sim_interval--; /* count clock */ +return 0; +} + +/* Miscellaneous routines */ + +/* Adjust stack pointer + + The reference manual says to trap on: + 1) E < 0, left changes from + to - + 2) E >= 0, left changes from - to + + This is the same as trap on: + 1) E and left result have same signs + 2) initial value and left result have different signs + */ + +d10 adjsp (d10 val, a10 ea) +{ +d10 imm = ea; +d10 left, right; + +left = ADDL (val, imm); +right = ADDR (val, imm); +if (TSTS ((val ^ left) & (~left ^ RLZ (imm)))) SETF (F_T2); +return (left | right); +} + +/* Jump if find first ones + Takes advantage of 7 bit find first table for priority interrupts. +*/ + +int32 jffo (d10 val) +{ +int32 i, by; + +if ((val & DMASK) == 0) return 0; +for (i = 0; i <= 28; i = i + 7) { /* scan five bytes */ + by = (int32) ((val >> (29 - i)) & 0177); + if (by) return (pi_m2lvl[by] + i - 1); } +return 35; /* must be bit 35 */ +} + +/* Circulate - ITS only instruction + + Bits rotated out of AC are rotated into the opposite end of AC+1 - why? + No attempt is made to optimize this instruction. +*/ + +void circ (int32 ac, int32 ea) +{ +int32 sc = LIT8 (ea) % 72; +int32 p1 = ADDAC (ac,1); +int32 i; +d10 val; + +if (sc == 0) return; /* any shift? */ +if (ea & RSIGN) sc = 72 - sc; /* if right, make left */ +for (i = 0; i < sc; i++) { /* one bit at a time */ + val = TSTS (AC(ac)); /* shift out */ + AC(ac) = ((AC(ac) << 1) | (AC(p1) & 1)) & DMASK; + AC(p1) = (AC(p1) >> 1) | val; } /* shift in */ +return; +} + +/* Arithmetic processor (APR) + + The APR subsystem includes miscellaneous interrupts that are individually + maskable but which interrupt on a single, selectable level + + Instructions for the arithmetic processor: + APRID read system id + WRAPR (CONO APR) write system flags + RDAPR (CONI APR) read system flags + (CONSO APR) test system flags + (CONSZ APR) test system flags +*/ + +t_bool aprid (a10 ea, int32 prv) +{ +Write (ea, (ITS)? UC_AIDITS: UC_AIDDEC, prv); +return FALSE; +} + +/* Checked against KS10 ucode */ + +t_bool wrapr (a10 ea, int32 prv) +{ +int32 bits = APR_GETF (ea); + +apr_lvl = ea & APR_M_LVL; +if (ea & APR_SENB) apr_enb = apr_enb | bits; /* set enables? */ +if (ea & APR_CENB) apr_enb = apr_enb & ~bits; /* clear enables? */ +if (ea & APR_CFLG) apr_flg = apr_flg & ~bits; /* clear flags? */ +if (ea & APR_SFLG) apr_flg = apr_flg | bits; /* set flags? */ +if (apr_flg & APRF_ITC) { /* interrupt console? */ + fe_intr (); /* explicit callout */ + apr_flg = apr_flg & ~APRF_ITC; } /* interrupt clears */ +pi_eval (); /* eval pi system */ +return FALSE; +} + +t_bool rdapr (a10 ea, int32 prv) +{ +Write (ea, (d10) APRWORD, prv); +return FALSE; +} + +t_bool czapr (a10 ea, int32 prv) +{ +return ((APRHWORD & ea)? FALSE: TRUE); +} + +t_bool coapr (a10 ea, int32 prv) +{ +return ((APRHWORD & ea)? TRUE: FALSE); +} + +/* Routine to change the processor flags, called from JRST, MUUO, interrupt. + If jrst is TRUE, must munge flags for executive security. + Because the KS10 lacks the public flag, these checks are simplified. +*/ + +void set_newflags (d10 newf, t_bool jrst) +{ +int32 fl = (int32) LRZ (newf); + +if (jrst && TSTF (F_USR)) { /* if in user now */ + fl = fl | F_USR; /* can't clear user */ + if (!TSTF (F_UIO)) fl = fl & ~F_UIO; } /* if !UIO, can't set */ +flags = fl & F_MASK; /* set new flags */ +set_dyn_ptrs (); /* set new ptrs */ +return; +} + +/* Priority interrupt system (PI) + + The priority interrupt system has three sources of requests + (pi_apr) system flags - synthesized on the fly + (pi_ioq) I/O interrupts - synthesized on the fly + pi_prq program requests + APR and I/O requests are masked with the PI enable mask; the program + requests are not. If priority interrupts are enabled, and there is + a request at a level exceeding the currently active level, then an + interrupt occurs. + + Instructions for the priority interrupt system: + WRPI (CONO PI) write pi system + RDPI (CONI PI) read pi system + (CONSO PI) test pi system + (CONSZ PI) test pi system + + Routines for the priority interrupt system: + pi_eval return level number of highest interrupt + pi_dismiss dismiss highest outstanding interrupt + + Checked against KS10 ucode - KS10 UUO's if <18:21> are non-zero +*/ + +t_bool wrpi (a10 ea, int32 prv) +{ +int32 lvl = ea & PI_M_LVL; + +if (ea & PI_INIT) pi_on = pi_enb = pi_act = pi_prq = 0; +if (ea & PI_CPRQ) pi_prq = pi_prq & ~lvl; /* clear prog reqs? */ +if (ea & PI_SPRQ) pi_prq = pi_prq | lvl; /* set prog reqs? */ +if (ea & PI_SENB) pi_enb = pi_enb | lvl; /* enable levels? */ +if (ea & PI_CENB) pi_enb = pi_enb & ~lvl; /* disable levels? */ +if (ea & PI_SON) pi_on = 1; /* enable pi? */ +if (ea & PI_CON) pi_on = 0; /* disable pi? */ +pi_eval (); /* eval pi system */ +return FALSE; +} + +t_bool rdpi (a10 ea, int32 prv) +{ +Write (ea, (d10) PIWORD, prv); +return FALSE; +} + +t_bool czpi (a10 ea, int32 prv) +{ +return ((PIHWORD & ea)? FALSE: TRUE); +} + +t_bool copi (a10 ea, int32 prv) +{ +return ((PIHWORD & ea)? TRUE: FALSE); +} + +/* Priority interrupt evaluation + + The Processor Reference Manuals says that program interrupt + requests occur whether the corresponding level is enabled or + not. However, the KS10, starting with microcode edit 47, + masked program requests under the enable mask, just like APR + and I/O requests. This is not formally documented but appears + to be necessary for the TOPS20 console port to run correclty. +*/ + +int32 pi_eval (void) +{ +int32 reqlvl, actlvl; +extern int32 pi_ub_eval (); + +qintr = 0; +if (pi_on) { + pi_apr = (apr_flg & apr_enb)? pi_l2bit[apr_lvl]: 0; + pi_ioq = pi_ub_eval (); + reqlvl = pi_m2lvl[((pi_apr | pi_ioq | pi_prq) & pi_enb)]; + actlvl = pi_m2lvl[pi_act]; + if ((actlvl == 0) || (reqlvl < actlvl)) qintr = reqlvl; } +return qintr; +} + +void pi_dismiss (void) +{ +pi_act = pi_act & ~pi_l2bit[pi_m2lvl[pi_act]]; /* clr left most bit */ +pi_eval (); /* eval pi system */ +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +flags = 0; /* clear flags */ +ebr = ubr = 0; /* clear paging */ +pi_enb = pi_act = pi_prq = 0; /* clear PI */ +apr_enb = apr_flg = apr_lvl = 0; /* clear APR */ +pcst = 0; /* clear PC samp */ +rlog = 0; /* clear reg log */ +hsb = (ITS)? UC_HSBITS: UC_HSBDEC; /* set HSB */ +set_dyn_ptrs (); +set_ac_display (ac_cur); +pi_eval (); +if (M == NULL) M = calloc (MAXMEMSIZE, sizeof (d10)); +if (M == NULL) return SCPE_MEM; +return cpu_svc (&cpu_unit); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw) +{ +if (vptr == NULL) return SCPE_ARG; +if (ea < AC_NUM) *vptr = AC(ea) & DMASK; +else { if (sw & SWMASK ('V')) { + ea = conmap (ea, PTF_CON, sw); + if (ea >= MAXMEMSIZE) return SCPE_REL; } + if (ea >= MEMSIZE) return SCPE_NXM; + *vptr = M[ea] & DMASK; } +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) +{ +if (ea < AC_NUM) AC(ea) = val & DMASK; +else { if (sw & SWMASK ('V')) { + ea = conmap (ea, PTF_CON | PTF_WR, sw); + if (ea >= MAXMEMSIZE) return SCPE_REL; } + if (ea >= MEMSIZE) return SCPE_NXM; + M[ea] = val & DMASK; } +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; +} + +/* Set current AC pointers for SCP */ + +void set_ac_display (d10 *acbase) +{ +extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); +REG *rptr; +int i; + +rptr = find_reg ("AC0", NULL, &cpu_dev); +if (rptr == NULL) return; +for (i = 0; i < AC_NUM; i++, rptr++) rptr -> loc = (void *) (acbase + i); +return; +} diff --git a/pdp10_defs.h b/pdp10_defs.h new file mode 100644 index 00000000..17ebaba1 --- /dev/null +++ b/pdp10_defs.h @@ -0,0 +1,626 @@ +/* pdp10_defs.h: PDP-10 simulator definitions + + Copyright (c) 1993-2001, 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 "sim_defs.h" /* simulator defns */ + +/* Digital Equipment Corporation's 36b family had six implementations: + + name mips comments + + PDP-6 0.25 Original 36b implementation, 1964 + KA10 0.38 First PDP-10, flip chips, 1967 + KI10 0.72 First paging system, flip chip + MSI, 1969 + KL10 1.8 First ECL system, ECL 10K, 1972 + KL10X 1.8 Expanded addressing, ECL 10K, 1975 + KS10 0.3 Last 36b system, 2901 based, 1979 + + In addition, it ran four major (incompatible) operating systems: + + name company comments + + TOPS-10 DEC Original timesharing system + ITS MIT "Incompatible Timesharing System" + TENEX BBN ARPA-sponsored, became + TOPS-20 DEC Commercial version of TENEX + + All of the implementations differ from one another, in instruction set, + I/O structure, and memory management. Further, each of the operating + systems customized the microcode of the paging systems (KI10, KL10, KS10) + for additional instructions and specialized memory management. As a + result, there is no "reference implementation" for the 36b family that + will run all programs and all operating systems. The conditionalization + and generality needed to support the full matrix of models and operating + systems, and to support 36b hardware on 32b data types, is beyond the + scope of this project. + + Instead, this simulator emulates one model -- the KS10. It has the best + documentation and allows reuse of some of the Unibus peripheral emulators + written for the PDP-11 simulator. Further, the simulator requires that + the underlying compiler support 64b integer data types, allowing 36b data + to be maintained in a single data item. Lastly, the simulator implements + the maximum memory size, so that NXM's never happen. +*/ + +/* Data types */ + +typedef int32 a10; /* PDP-10 addr (30b) */ +typedef int64 d10; /* PDP-10 data (36b) */ + +/* Abort codes, used to sort out longjmp's back to the main loop + Codes > 0 are simulator stop codes + Codes < 0 are internal aborts + Code = 0 stops execution for an interrupt check +*/ + +#define STOP_HALT 1 /* halted */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_ILLEG 3 /* illegal instr */ +#define STOP_ILLINT 4 /* illegal intr inst */ +#define STOP_PAGINT 5 /* page fail in intr */ +#define STOP_ZERINT 6 /* zero vec in intr */ +#define STOP_NXMPHY 7 /* nxm on phys ref */ +#define STOP_IND 8 /* indirection loop */ +#define STOP_XCT 9 /* XCT loop */ +#define STOP_ILLIOC 10 /* invalid UBA num */ +#define STOP_MTRLNT 11 /* invalid mt rec lnt */ +#define STOP_ASTOP 12 /* address stop */ +#define STOP_UNKNOWN 13 /* unknown stop */ +#define PAGE_FAIL -1 /* page fail */ +#define INTERRUPT -2 /* interrupt */ +#define ABORT(x) longjmp (save_env, (x)) /* abort */ +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +/* Return codes from eXTEND */ + +#define XT_MUUO 0 /* invalid operation */ +#define XT_SKIP 1 /* skip return */ +#define XT_NOSK 2 /* no skip return */ + +/* Operating system flags, kept in cpu_unit.flags */ + +#define UNIT_V_ITS (UNIT_V_UF) /* ITS */ +#define UNIT_ITS (1 << UNIT_V_ITS) +#define ITS (cpu_unit.flags & UNIT_ITS) + +/* Architectural constants */ + +#define PASIZE 20 /* phys addr width */ +#define MAXMEMSIZE (1 << PASIZE) /* maximum memory */ +#define PAMASK ((1 << PASIZE) - 1) +#define MEMSIZE MAXMEMSIZE /* fixed, KISS */ +#define MEM_ADDR_NXM(x) ((x) >= MEMSIZE) +#define VASIZE 18 /* virtual addr width */ +#define AMASK ((1 << VASIZE) - 1) /* virtual addr mask */ +#define LMASK 0777777000000 /* left mask */ +#define LSIGN 0400000000000 /* left sign */ +#define RMASK 0000000777777 /* right mask */ +#define RSIGN 0000000400000 /* right sign */ +#define DMASK 0777777777777 /* data mask */ +#define SIGN 0400000000000 /* sign */ +#define MMASK 0377777777777 /* magnitude mask */ +#define ONES 0777777777777 +#define MAXPOS 0377777777777 +#define MAXNEG 0400000000000 + +/* Instruction format */ + +#define INST_V_OP 27 /* opcode */ +#define INST_M_OP 0777 +#define INST_V_DEV 26 +#define INST_M_DEV 0177 /* device */ +#define INST_V_AC 23 /* AC */ +#define INST_M_AC 017 +#define INST_V_IND 22 /* indirect */ +#define INST_IND (1 << INST_V_IND) +#define INST_V_XR 18 /* index */ +#define INST_M_XR 017 +#define OP_JRST 0254 /* JRST */ +#define AC_XPCW 07 /* XPCW */ +#define OP_JSR 0264 /* JSR */ +#define GET_OP(x) ((int32) (((x) >> INST_V_OP) & INST_M_OP)) +#define GET_DEV(x) ((int32) (((x) >> INST_V_DEV) & INST_M_DEV)) +#define GET_AC(x) ((int32) (((x) >> INST_V_AC) & INST_M_AC)) +#define TST_IND(x) ((x) & INST_IND) +#define GET_XR(x) ((int32) (((x) >> INST_V_XR) & INST_M_XR)) +#define GET_ADDR(x) ((a10) ((x) & AMASK)) + +/* Byte pointer format */ + +#define BP_V_P 30 /* position */ +#define BP_M_P 077 +#define BP_P 0770000000000 +#define BP_V_S 24 /* size */ +#define BP_M_S 077 +#define BP_S 0007700000000 +#define GET_P(x) ((int32) (((x) >> BP_V_P) & BP_M_P)) +#define GET_S(x) ((int32) (((x) >> BP_V_S) & BP_M_S)) +#define PUT_P(b,x) (((b) & ~BP_P) | ((((int64) (x)) & BP_M_P) << BP_V_P)) + +/* Flags (stored in their own halfword) */ + +#define F_V_AOV 17 /* arithmetic ovflo */ +#define F_V_C0 16 /* carry 0 */ +#define F_V_C1 15 /* carry 1 */ +#define F_V_FOV 14 /* floating ovflo */ +#define F_V_FPD 13 /* first part done */ +#define F_V_USR 12 /* user mode */ +#define F_V_UIO 11 /* user I/O mode */ +#define F_V_PUB 10 /* public mode */ +#define F_V_AFI 9 /* addr fail inhibit */ +#define F_V_T2 8 /* trap 2 */ +#define F_V_T1 7 /* trap 1 */ +#define F_V_FXU 6 /* floating exp unflo */ +#define F_V_DCK 5 /* divide check */ +#define F_AOV (1 << F_V_AOV) +#define F_C0 (1 << F_V_C0) +#define F_C1 (1 << F_V_C1) +#define F_FOV (1 << F_V_FOV) +#define F_FPD (1 << F_V_FPD) +#define F_USR (1 << F_V_USR) +#define F_UIO (1 << F_V_UIO) +#define F_PUB (1 << F_V_PUB) +#define F_AFI (1 << F_V_AFI) +#define F_T2 (1 << F_V_T2) +#define F_T1 (1 << F_V_T1) +#define F_TR (F_T1 | F_T2) +#define F_FXU (1 << F_V_FXU) +#define F_DCK (1 << F_V_DCK) +#define F_1PR (F_AFI) /* ITS: 1-proceed */ +#define F_MASK 0777740 /* all flags */ +#define SETF(x) flags = flags | (x) +#define CLRF(x) flags = flags & ~(x) +#define TSTF(x) (flags & (x)) +#define GET_TRAPS(x) (((x) & (F_T2 | F_T1)) >> F_V_T1) + +/* Priority interrupt system */ + +#define PI_CPRQ 020000 /* drop prog req */ +#define PI_INIT 010000 /* clear pi system */ +#define PI_SPRQ 004000 /* set prog req */ +#define PI_SENB 002000 /* set enables */ +#define PI_CENB 001000 /* clear enables */ +#define PI_CON 000400 /* turn off pi system */ +#define PI_SON 000200 /* turn on pi system */ +#define PI_M_LVL 000177 /* level mask */ +#define PI_V_PRQ 18 /* in CONI */ +#define PI_V_ACT 8 +#define PI_V_ON 7 +#define PI_V_ENB 0 + +/* Arithmetic processor flags */ + +#define APR_SENB 0100000 /* set enable */ +#define APR_CENB 0040000 /* clear enable */ +#define APR_CFLG 0020000 /* clear flag */ +#define APR_SFLG 0010000 /* set flag */ +#define APR_IRQ 0000010 /* int request */ +#define APR_M_LVL 0000007 /* pi level */ +#define APR_V_FLG 4 /* system flags */ +#define APR_M_FLG 0377 +#define APRF_ITC (002000 >> APR_V_FLG) /* int console flag */ +#define APRF_NXM (000400 >> APR_V_FLG) /* nxm flag */ +#define APRF_TIM (000040 >> APR_V_FLG) /* timer request */ +#define APRF_CON (000020 >> APR_V_FLG) /* console int */ +#define APR_GETF(x) (((x) >> APR_V_FLG) & APR_M_FLG) + +/* Virtual address, DEC paging */ + +#define PAG_V_OFF 0 /* offset - must be 0 */ +#define PAG_N_OFF 9 /* page offset width */ +#define PAG_SIZE 01000 /* page offset size */ +#define PAG_M_OFF 0777 /* mask for offset */ +#define PAG_V_PN PAG_N_OFF /* page number */ +#define PAG_N_PPN (PASIZE - PAG_N_OFF) /* phys pageno width */ +#define PAG_M_PPN 03777 /* phys pageno mask */ +#define PAG_PPN 03777000 +#define PAG_N_VPN (VASIZE - PAG_N_OFF) /* virt pageno width */ +#define PAG_M_VPN 0777 /* virt pageno mask */ +#define PAG_VPN 0777000 +#define PAG_GETOFF(x) ((x) & PAG_M_OFF) +#define PAG_GETVPN(x) (((x) >> PAG_V_PN) & PAG_M_VPN) +#define PAG_XPTEPA(p,x) (((p) + PAG_GETOFF (x)) & PAMASK) +#define PAG_PTEPA(p,x) (((((int32) (p)) & PTE_PPMASK) << PAG_V_PN) + PAG_GETOFF (x)) + +/* Page table entry, TOPS-10 paging */ + +#define PTE_T10_A 0400000 /* T10: access */ +#define PTE_T10_P 0200000 /* T10: public */ +#define PTE_T10_W 0100000 /* T10: writeable */ +#define PTE_T10_S 0040000 /* T10: software */ +#define PTE_T10_C 0020000 /* T10: cacheable */ +#define PTE_PPMASK PAG_M_PPN + +/* Page table entry, TOPS-20 paging */ + +#define PTE_T20_V_TYP 33 /* T20: pointer type */ +#define PTE_T20_M_TYP 07 +#define T20_NOA 0 /* no access */ +#define T20_IMM 1 /* immediate */ +#define T20_SHR 2 /* shared */ +#define T20_IND 3 /* indirect */ +#define PTE_T20_W 0020000000000 /* T20: writeable */ +#define PTE_T20_C 0004000000000 /* T20: cacheable */ +#define PTE_T20_STM 0000077000000 /* T20: storage medium */ +#define PTE_T20_V_PMI 18 /* page map index */ +#define PTE_T20_M_PMI 0777 +#define T20_GETTYP(x) ((int32) (((x) >> PTE_T20_V_TYP) & PTE_T20_M_TYP)) +#define T20_GETPMI(x) ((int32) (((x) >> PTE_T20_V_PMI) & PTE_T20_M_PMI)) + +/* CST entry, TOPS-20 paging */ + +#define CST_AGE 0770000000000 /* age field */ +#define CST_M 0000000000001 /* modified */ + +/* Page fail word, DEC paging */ + +#define PF_USER 0400000000000 /* user mode */ +#define PF_HARD 0200000000000 /* nx I/O reg */ +#define PF_NXM 0370000000000 /* nx memory */ +#define PF_T10_A 0100000000000 /* T10: pte A bit */ +#define PF_T10_W 0040000000000 /* T10: pte W bit */ +#define PF_T10_S 0020000000000 /* T10: pte S bit */ +#define PF_T20_DN 0100000000000 /* T20: eval done */ +#define PF_T20_M 0040000000000 /* T20: modified */ +#define PF_T20_W 0020000000000 /* T20: writeable */ +#define PF_WRITE 0010000000000 /* write reference */ +#define PF_PUB 0004000000000 /* pte public bit */ +#define PF_C 0002000000000 /* pte C bit */ +#define PF_VIRT 0001000000000 /* pfl: virt ref */ +#define PF_NXMP 0001000000000 /* nxm: phys ref */ +#define PF_IO 0000200000000 /* I/O reference */ +#define PF_BYTE 0000020000000 /* I/O byte ref */ + +/* Virtual address, ITS paging */ + +#define ITS_V_OFF 0 /* offset - must be 0 */ +#define ITS_N_OFF 10 /* page offset width */ +#define ITS_SIZE 02000 /* page offset size */ +#define ITS_M_OFF 01777 /* mask for offset */ +#define ITS_V_PN ITS_N_OFF /* page number */ +#define ITS_N_PPN (PASIZE- ITS_N_OFF) /* phys pageno width */ +#define ITS_M_PPN 01777 /* phys pageno mask */ +#define ITS_PPN 03776000 +#define ITS_N_VPN (VASIZE - ITS_N_OFF) /* virt pageno width */ +#define ITS_M_VPN 0377 /* virt pageno mask */ +#define ITS_VPN 0776000 +#define ITS_GETVPN(x) (((x) >> ITS_V_PN) & ITS_M_VPN) + +/* Page table entry, ITS paging */ + +#define PTE_ITS_V_ACC 16 /* access field */ +#define PTE_ITS_M_ACC 03 +#define ITS_ACC_NO 0 /* no access */ +#define ITS_ACC_RO 1 /* read only */ +#define ITS_ACC_RWF 2 /* read-write first */ +#define ITS_ACC_RW 3 /* read write */ +#define PTE_ITS_AGE 0020000 /* age */ +#define PTE_ITS_C 0010000 /* cacheable */ +#define PTE_ITS_PPMASK ITS_M_PPN +#define ITS_GETACC(x) (((x) >> PTE_ITS_V_ACC) & PTE_ITS_M_ACC) + +/* Page fail word, ITS paging */ + +#define PF_ITS_WRITE 0010000000000 /* write reference */ +#define PF_ITS_V_ACC 28 /* access from PTE */ + +/* Page table fill operations */ + +#define PTF_RD 0 /* read check */ +#define PTF_WR 1 /* write check */ +#define PTF_MAP 2 /* map instruction */ +#define PTF_CON 4 /* console access */ + +/* User base register */ + +#define UBR_SETACB 0400000000000 /* set AC blocks */ +#define UBR_SETUBR 0100000000000 /* set UBR */ +#define UBR_V_CURAC 27 /* current AC block */ +#define UBR_V_PRVAC 24 /* previous AC block */ +#define UBR_M_AC 07 +#define UBR_ACBMASK 0007700000000 +#define UBR_V_UBR 0 /* user base register */ +#define UBR_N_UBR 11 +#define UBR_M_UBR 03777 +#define UBR_UBRMASK 0000000003777 +#define UBR_GETCURAC(x) ((int32) (((x) >> UBR_V_CURAC) & UBR_M_AC)) +#define UBR_GETPRVAC(x) ((int32) (((x) >> UBR_V_PRVAC) & UBR_M_AC)) +#define UBR_GETUBR(x) ((int32) (((x) >> UBR_V_UBR) & PAG_M_PPN)) +#define UBRWORD (ubr | UBR_SETACB | UBR_SETUBR) + +/* Executive base register */ + +#define EBR_V_T20P 14 /* TOPS20 paging */ +#define EBR_T20P (1u << EBR_V_T20P) +#define EBR_V_PGON 13 /* enable paging */ +#define EBR_PGON (1u << EBR_V_PGON) +#define EBR_V_EBR 0 /* exec base register */ +#define EBR_N_EBR 11 +#define EBR_M_EBR 03777 +#define EBR_MASK (EBR_T20P | EBR_PGON | (EBR_M_EBR << EBR_V_EBR)) +#define EBR_GETEBR(x) ((int32) (((x) >> EBR_V_EBR) & PAG_M_PPN)) +#define PAGING (ebr & EBR_PGON) +#define T20 (ebr & EBR_T20P) + +/* AC and mapping contexts + + There are only two real contexts for selecting the AC block and + the memory map: current and previous. However, PXCT allows the + choice of current versus previous to be made selectively for + various parts of an instruction. The PXCT flags (and the trap + in progress flag) are kept in a dynamic CPU variable. +*/ + +#define TRAP_CYCLE 040 /* trap in progress */ +#define EA_PXCT 010 /* eff addr calc */ +#define OPND_PXCT 004 /* operand, bdst */ +#define EABP_PXCT 002 /* bp eff addr calc */ +#define BSTK_PXCT 001 /* stk, bp op, bsrc */ +#define XSRC_PXCT 002 /* extend source */ +#define XDST_PXCT 001 /* extend destination */ +#define MM_CUR 000 /* current context */ +#define MM_EA (pflgs & EA_PXCT) +#define MM_OPND (pflgs & OPND_PXCT) +#define MM_EABP (pflgs & EABP_PXCT) +#define MM_BSTK (pflgs & BSTK_PXCT) + +/* Accumulator access. The AC blocks are kept in array acs[AC_NBLK * AC_NUM]. + Two pointers are provided to the bases of the current and previous blocks. + Macro AC selects the current AC block; macro XR selects current or previous, + depending on whether the selected bit in the "pxct in progress" flag is set. +*/ + +#define AC_NUM 16 /* # AC's/block */ +#define AC_NBLK 8 /* # AC blocks */ +#define AC(r) (ac_cur[r]) /* AC select current */ +#define XR(r,prv) ((prv)? ac_prv[r]: ac_cur[r]) /* AC select context */ +#define ADDAC(x,i) (((x) + (i)) & INST_M_AC) +#define P1 ADDAC (ac, 1) + +/* User process table entries */ + +#define UPT_T10_UMAP 0000 /* T10: user map */ +#define UPT_T10_X340 0400 /* T10: exec 340-377 */ +#define UPT_TRBASE 0420 /* trap base */ +#define UPT_MUUO 0424 /* MUUO block */ +#define UPT_MUPC 0425 /* caller's PC */ +#define UPT_T10_CTX 0426 /* T10: context */ +#define UPT_T20_UEA 0426 /* T20: address */ +#define UPT_T20_CTX 0427 /* T20: context */ +#define UPT_ENPC 0430 /* MUUO new PC, exec */ +#define UPT_UNPC 0434 /* MUUO new PC, user */ +#define UPT_NPCT 1 /* PC offset if trap */ +#define UPT_T10_PAG 0500 /* T10: page fail blk */ +#define UPT_T20_PFL 0500 /* T20: page fail wd */ +#define UPT_T20_OFL 0501 /* T20: flags */ +#define UPT_T20_OPC 0502 /* T20: old PC */ +#define UPT_T20_NPC 0503 /* T20: new PC */ +#define UPT_T20_SCTN 0540 /* T20: section 0 ptr */ + +/* Exec process table entries */ + +#define EPT_PIIT 0040 /* PI interrupt table */ +#define EPT_UBIT 0100 /* Unibus intr table */ +#define EPT_T10_X400 0200 /* T10: exec 400-777 */ +#define EPT_TRBASE 0420 /* trap base */ +#define EPT_ITS_PAG 0440 /* ITS: page fail blk */ +#define EPT_T20_SCTN 0540 /* T20: section 0 ptr */ +#define EPT_T10_X000 0600 /* T10: exec 0 - 337 */ + +/* Microcode constants */ + +#define UC_INHCST 0400000000000 /* inhibit CST update */ +#define UC_UBABLT 0040000000000 /* BLTBU and BLTUB */ +#define UC_KIPAGE 0020000000000 /* "KI" paging */ +#define UC_KLPAGE 0010000000000 /* "KL" paging */ +#define UC_VERDEC (0130 << 18) /* ucode version */ +#define UC_VERITS (262u << 18) +#define UC_SERDEC 4097 /* serial number */ +#define UC_SERITS 1729 +#define UC_AIDDEC (UC_INHCST | UC_UBABLT | UC_KIPAGE | UC_KLPAGE | \ + UC_VERDEC | UC_SERDEC) +#define UC_AIDITS (UC_KIPAGE | UC_VERITS | UC_SERITS) +#define UC_HSBDEC 0376000 /* DEC initial HSB */ +#define UC_HSBITS 0000500 /* ITS initial HSB */ + +/* Front end communications region */ + +#define FE_SWITCH 030 /* halt switch */ +#define FE_KEEPA 031 /* keep alive */ +#define FE_CTYIN 032 /* console in */ +#define FE_CTYOUT 033 /* console out */ +#define FE_KLININ 034 /* KLINIK in */ +#define FE_KLINOUT 035 /* KLINIK out */ +#define FE_RHBASE 036 /* boot: RH11 addr */ +#define FE_UNIT 037 /* boot: unit num */ +#define FE_MTFMT 040 /* boot: magtape params */ +#define FE_CVALID 0400 /* char valid flag */ + +/* Halfword operations */ + +#define ADDL(x,y) (((x) + ((y) << 18)) & LMASK) +#define ADDR(x,y) (((x) + (y)) & RMASK) +#define INCL(x) ADDL (x, 1) +#define INCR(x) ADDR (x, 1) +#define AOB(x) (INCL (x) | INCR(x)) +#define SUBL(x,y) (((x) - ((y) << 18)) & LMASK) +#define SUBR(x,y) (((x) - (y)) & RMASK) +#define DECL(x) SUBL (x, 1) +#define DECR(x) SUBR (x, 1) +#define SOB(x) (DECL (x) | DECR(x)) +#define LLZ(x) ((x) & LMASK) +#define RLZ(x) (((x) << 18) & LMASK) +#define RRZ(x) ((x) & RMASK) +#define LRZ(x) (((x) >> 18) & RMASK) +#define LIT8(x) (((x) & RSIGN)? \ + (((x) & 0377)? (-(x) & 0377): 0400): ((x) & 0377)) + +/* Fullword operations */ + +#define INC(x) (((x) + 1) & DMASK) +#define DEC(x) (((x) - 1) & DMASK) +#define SWP(x) ((((x) << 18) & LMASK) | (((x) >> 18) & RMASK)) +#define XWD(x,y) (((((d10) (x)) << 18) & LMASK) | (((d10) (y)) & RMASK)) +#define SETS(x) ((x) | SIGN) +#define CLRS(x) ((x) & ~SIGN) +#define TSTS(x) ((x) & SIGN) +#define NEG(x) (-(x) & DMASK) +#define ABS(x) (TSTS (x)? NEG(x): (x)) +#define SXT(x) (TSTS (x)? (x) | ~DMASK: (x)) + +/* Doubleword operations (on 2-word arrays) */ + +#define DMOVN(rs) rs[1] = (-rs[1]) & MMASK; \ + rs[0] = (~rs[0] + (rs[1] == 0)) & DMASK +#define MKDNEG(rs) rs[1] = SETS (-rs[1]) & DMASK; \ + rs[0] = (~rs[0] + (rs[1] == MAXNEG)) & DMASK +#define DCMPGE(a,b) ((a[0] > b[0]) || ((a[0] == b[0]) && (a[1] >= b[1]))) + +/* Address operations */ + +#define ADDA(x,i) (((x) + (i)) & AMASK) +#define INCA(x) ADDA (x, 1) + +/* Unibus adapter control/status register */ + +#define UBCS_TMO 0400000 /* timeout */ +#define UBCS_BMD 0200000 /* bad mem data NI */ +#define UBCS_PAR 0100000 /* parity error NI */ +#define UBCS_NXD 0040000 /* nx device */ +#define UBCS_HI 0004000 /* irq on BR7 or BR6 */ +#define UBCS_LO 0002000 /* irq on BR5 or BR4 */ +#define UBCS_PWR 0001000 /* power low NI */ +#define UBCS_DXF 0000200 /* disable xfer NI*/ +#define UBCS_INI 0000100 /* Unibus init */ +#define UBCS_RDZ 0030500 /* read as zero */ +#define UBCS_RDW 0000277 /* read/write bits */ +#define UBCS_V_LHI 3 /* hi pri irq level */ +#define UBCS_V_LLO 0 /* lo pri irq level */ +#define UBCS_M_PRI 07 +#define UBCS_GET_HI(x) (((x) >> UBCS_V_LHI) & UBCS_M_PRI) +#define UBCS_GET_LO(x) (((x) >> UBCS_V_LLO) & UBCS_M_PRI) + +/* Unibus adapter page map */ + +#define UBANUM 2 /* # of Unibus adapters */ +#define UMAP_ASIZE 6 /* address size */ +#define UMAP_MEMSIZE (1 << UMAP_ASIZE) /* length */ +#define UMAP_AMASK (UMAP_MEMSIZE - 1) +#define UMAP_V_RRV 30 /* read reverse */ +#define UMAP_V_DSB 29 /* 16b on NPR read */ +#define UMAP_V_FST 28 /* fast transfer */ +#define UMAP_V_VLD 27 /* valid flag */ +#define UMAP_RRV (1 << UMAP_V_RRV) +#define UMAP_DSB (1 << UMAP_V_DSB) +#define UMAP_FST (1 << UMAP_V_FST) +#define UMAP_VLD (1 << UMAP_V_VLD) +#define UMAP_V_FLWR 14 /* flags as written */ +#define UMAP_V_FLRD 27 /* flags as stored */ +#define UMAP_M_FL 017 +#define UMAP_V_PNWR 0 /* page num, write */ +#define UMAP_V_PNRD 9 /* page num, read */ +#define UMAP_M_PN 03777 +#define UMAP_MASK ((UMAP_M_FL << UMAP_V_FLRD) | (UMAP_M_PN << UMAP_V_PNRD)) +#define UMAP_POSFL(x) (((x) & (UMAP_M_FL << UMAP_V_FLWR)) \ + << (UMAP_V_FLRD - UMAP_V_FLWR)) +#define UMAP_POSPN(x) (((x) & (UMAP_M_PN << UMAP_V_PNWR)) \ + << (UMAP_V_PNRD - UMAP_V_PNWR)) + +/* Unibus I/O constants */ + +#define READ 0 /* PDP11 compatible */ +/* #define READC 1 /* console read */ +#define WRITE 2 +/* #define WRITEC 3 /* console write */ +#define WRITEB 4 +#define IO_V_UBA 18 /* UBA in I/O addr */ +#define IO_N_UBA 16 /* max num of UBA's */ +#define IO_M_UBA (IO_N_UBA - 1) +#define IO_UBA1 (1 << IO_V_UBA) +#define IO_UBA3 (3 << IO_V_UBA) +#define GET_IOUBA(x) (((x) >> IO_V_UBA) & IO_M_UBA) + +/* I/O page layout */ + +#define IO_DZBASE 0760010 /* DZ11 base */ +#define IO_TCUBASE 0760770 /* TCU150 base */ +#define IO_UBMAP 0763000 /* Unibus map base */ +#define IO_UBCS 0763100 /* Unibus c/s reg */ +#define IO_UBMNT 0763101 /* Unibus maint reg */ +#define IO_TMBASE 0772440 /* RH11/tape base */ +#define IO_RHBASE 0776700 /* RH11/disk base */ +#define IO_LPBASE 0775400 /* LP20 base */ +#define IO_PTBASE 0777550 /* PC11 base */ + +/* Common Unibus CSR flags */ + +#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) + +/* I/O system definitions, lifted from the PDP-11 simulator + Interrupt assignments, priority is right to left + + <7:0> = BR7 + <15:8> = BR6 + <23:16> = BR5 + <30:24> = BR4 +*/ + +#define INT_V_RP 8 /* RH11/RP,RM drives */ +#define INT_V_TU 9 /* RH11/TM03/TU45 */ +#define INT_V_DZ 16 /* DZ11 */ +#define INT_V_PTR 24 /* PC11 */ +#define INT_V_PTP 25 +#define INT_V_LP20 26 /* LPT20 */ + +#define INT_RP (1u << INT_V_RP) +#define INT_TU (1u << INT_V_TU) +#define INT_DZ (1u << INT_V_DZ) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_LP20 (1u << INT_V_LP20) + +#define INT_UB1 INT_RP /* on Unibus 1 */ +#define INT_UB3 (0xFFFFFFFFu & ~INT_UB1) /* on Unibus 3 */ + +#define INT_IPL7 0x000000FF /* int level masks */ +#define INT_IPL6 0x0000FF00 +#define INT_IPL5 0x00FF0000 +#define INT_IPL4 0x3F000000 + +#define VEC_PTR 0070 /* interrupt vectors */ +#define VEC_PTP 0074 +#define VEC_TU 0224 +#define VEC_RP 0254 +#define VEC_DZ 0340 +#define VEC_LP20 0754 diff --git a/pdp10_fe.c b/pdp10_fe.c new file mode 100644 index 00000000..71ec41ee --- /dev/null +++ b/pdp10_fe.c @@ -0,0 +1,162 @@ +/* pdp10_fe.c: PDP-10 front end (console terminal) simulator + + Copyright (c) 1993-2001, 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. + + fe KS10 console front end +*/ + +#include "pdp10_defs.h" +#define UNIT_DUMMY (1 << UNIT_V_UF) + +extern d10 *M; +extern int32 apr_flg; +t_stat fei_svc (UNIT *uptr); +t_stat feo_svc (UNIT *uptr); +t_stat fe_reset (DEVICE *dptr); +t_stat fe_stop_os (UNIT *uptr, int32 val); +t_stat fe_ctrl_c (UNIT *uptr, int32 val); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); + +/* FE data structures + + fe_dev FE device descriptor + fe_unit FE unit descriptor + fe_reg FE register list +*/ + +#define fei_unit fe_unit[0] +#define feo_unit fe_unit[1] + +UNIT fe_unit[] = { + { UDATA (&fei_svc, 0, 0), KBD_POLL_WAIT }, + { UDATA (&feo_svc, 0, 0), SERIAL_OUT_WAIT } }; + +REG fe_reg[] = { + { ORDATA (IBUF, fei_unit.buf, 8) }, + { DRDATA (ICOUNT, fei_unit.pos, 31), REG_RO + PV_LEFT }, + { DRDATA (ITIME, fei_unit.wait, 24), REG_NZ + PV_LEFT }, + { ORDATA (OBUF, feo_unit.buf, 8) }, + { DRDATA (OCOUNT, feo_unit.pos, 31), REG_RO + PV_LEFT }, + { DRDATA (OTIME, feo_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +MTAB fe_mod[] = { + { UNIT_DUMMY, 0, NULL, "STOP", &fe_stop_os }, + { UNIT_DUMMY, 0, NULL, "CTRL-C", &fe_ctrl_c }, + { 0 } }; + +DEVICE fe_dev = { + "FE", fe_unit, fe_reg, fe_mod, + 2, 10, 31, 1, 8, 8, + NULL, NULL, &fe_reset, + NULL, NULL, NULL }; + +/* Front end processor (console terminal) + + Communications between the KS10 and its front end is based on an in-memory + status block and two interrupt lines: interrupt-to-control (APR_ITC) and + interrupt-from-console (APR_CON). When the KS10 wants to print a character + on the terminal, + + 1. It places a character, plus the valid flag, in FE_CTYOUT. + 2. It interrupts the front end processor. + 3. The front end processor types the character and then zeroes FE_CTYOUT. + 4. The front end procesor interrupts the KS10. + + When the front end wants to send an input character to the KS10, + + 1. It places a character, plus the valid flag, in FE_CTYIN. + 2. It interrupts the KS10. + 3. It waits for the KS10 to take the character and clear the valid flag. + 4. It can then send more input (the KS10 may signal this by interrupting + the front end). + + Note that the protocol has both ambiguity (interrupt to the KS10 may mean + character printed, or input character available, or both) and lack of + symmetry (the KS10 does not inform the front end that it has taken an + input character). +*/ + +void fe_intr (void) +{ +if (M[FE_CTYOUT] & FE_CVALID) { /* char to print? */ + feo_unit.buf = (int32) M[FE_CTYOUT] & 0177; /* pick it up */ + sim_putchar (feo_unit.buf); /* type it */ + feo_unit.pos = feo_unit.pos + 1; + sim_activate (&feo_unit, feo_unit.time); } /* sched completion */ +else if ((M[FE_CTYIN] & FE_CVALID) == 0) { /* input char taken? */ + sim_cancel (&fei_unit); /* sched immediate */ + sim_activate (&fei_unit, 0); }; /* keyboard poll */ +return; +} + +t_stat feo_svc (UNIT *uptr) +{ +M[FE_CTYOUT] = 0; /* clear char */ +apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ +return SCPE_OK; +} + +t_stat fei_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&fei_unit, fei_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +fei_unit.buf = temp & 0177; +fei_unit.pos = fei_unit.pos + 1; +M[FE_CTYIN] = fei_unit.buf | FE_CVALID; /* put char in mem */ +apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ +return SCPE_OK; +} + +/* Reset */ + +t_stat fe_reset (DEVICE *dptr) +{ +fei_unit.buf = feo_unit.buf = 0; +M[FE_CTYIN] = M[FE_CTYOUT] = 0; +apr_flg = apr_flg & ~(APRF_ITC | APRF_CON); +sim_activate (&fei_unit, fei_unit.wait); /* start input poll */ +return SCPE_OK; +} + +/* Stop operating system */ + +t_stat fe_stop_os (UNIT *uptr, int32 val) +{ +M[FE_SWITCH] = IO_RHBASE; /* tell OS to stop */ +return SCPE_OK; +} + +/* Enter control-C for Windoze */ + +t_stat fe_ctrl_c (UNIT *uptr, int32 val) +{ +fei_unit.buf = 003; /* control-C */ +M[FE_CTYIN] = fei_unit.buf | FE_CVALID; /* put char in mem */ +apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ +return SCPE_OK; +} \ No newline at end of file diff --git a/pdp10_ksio.c b/pdp10_ksio.c new file mode 100644 index 00000000..19c23a79 --- /dev/null +++ b/pdp10_ksio.c @@ -0,0 +1,546 @@ +/* pdp10_ksio.c: PDP-10 KS10 I/O subsystem simulator + + Copyright (c) 1993-2001, 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. + + uba Unibus adapters + + 12-May-01 RMS Fixed typo + + The KS10 uses the PDP-11 Unibus for its I/O, via adapters. While + nominally four adapters are supported, in practice only 1 and 3 + are implemented. The disks are placed on adapter 1, the rest of + the I/O devices on adapter 3. + + In theory, we should maintain completely separate Unibuses, with + distinct PI systems. In practice, this simulator has so few devices + that we can get away with a single PI system, masking for which + devices are on adapter 1, and which on adapter 3. The Unibus + implementation is modeled on the Qbus in the PDP-11 simulator and + is described there. + + The I/O subsystem is programmed by I/O instructions which create + Unibus operations (read, read pause, write, write byte). DMA is + the responsibility of the I/O device simulators, which also implement + Unibus to physical memory mapping. + + The priority interrupt subsystem (and other privileged functions) + is programmed by I/O instructions with internal devices codes + (opcodes 700-702). These are dispatched here, although many are + handled in the memory management unit or elsewhere. + + The ITS instructions are significantly different from the TOPS-10/20 + instructions. They do not use the extended address calculation but + instead provide instruction variants (Q for Unibus adapter 1, I for + Unibus adapter 3) which insert the Unibus adapter number into the + effective address. +*/ + +#include "pdp10_defs.h" +#include + +#define eaRB (ea & ~1) +#define GETBYTE(ea,x) ((((ea) & 1)? (x) >> 8: (x)) & 0377) +#define UBNXM_FAIL(pa,op) \ + n = iocmap[GET_IOUBA (pa)]; \ + if (n >= 0) ubcs[n] = ubcs[n] | UBCS_TMO | UBCS_NXD; \ + pager_word = PF_HARD | PF_VIRT | PF_IO | \ + ((op == WRITEB)? PF_BYTE: 0) | \ + (TSTF (F_USR)? PF_USER: 0) | (pa); \ + ABORT (PAGE_FAIL) + +/* Unibus adapter data */ + +int32 ubcs[UBANUM] = { 0 }; /* status registers */ +int32 ubmap[UBANUM][UMAP_MEMSIZE] = { 0 }; /* Unibus maps */ +int32 int_req = 0; /* interrupt requests */ +int32 dev_enb = -1 & ~(INT_PTR | INT_PTP); /* device enables */ + +/* Map IO controller numbers to Unibus adapters: -1 = non-existent */ + +static int iocmap[IO_N_UBA] = { /* map I/O ext to UBA # */ + -1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static const int32 ubabr76[UBANUM] = { + INT_UB1 & (INT_IPL7 | INT_IPL6), INT_UB3 & (INT_IPL7 | INT_IPL6) }; +static const int32 ubabr54[UBANUM] = { + INT_UB1 & (INT_IPL5 | INT_IPL4), INT_UB3 & (INT_IPL5 | INT_IPL4) }; + +extern d10 *ac_cur; +extern d10 pager_word; +extern int32 flags, pi_l2bit[8]; +extern UNIT cpu_unit; +extern jmp_buf save_env; + +extern d10 Read (a10 ea); +extern void pi_eval (); +extern t_stat dz_rd (int32 *data, int32 addr, int32 access); +extern t_stat dz_wr (int32 data, int32 addr, int32 access); +extern t_stat pt_rd (int32 *data, int32 addr, int32 access); +extern t_stat pt_wr (int32 data, int32 addr, int32 access); +extern t_stat lp20_rd (int32 *data, int32 addr, int32 access); +extern t_stat lp20_wr (int32 data, int32 addr, int32 access); +extern int32 lp20_inta (void); +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 tu_rd (int32 *data, int32 addr, int32 access); +extern t_stat tu_wr (int32 data, int32 addr, int32 access); +extern int32 tu_inta (void); +extern t_stat tcu_rd (int32 *data, int32 addr, int32 access); +t_stat ubmap_rd (int32 *data, int32 addr, int32 access); +t_stat ubmap_wr (int32 data, int32 addr, int32 access); +t_stat ubs_rd (int32 *data, int32 addr, int32 access); +t_stat ubs_wr (int32 data, int32 addr, int32 access); +t_stat rd_zro (int32 *data, int32 addr, int32 access); +t_stat wr_nop (int32 data, int32 addr, int32 access); +t_stat uba_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat uba_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat uba_reset (DEVICE *dptr); +d10 ReadIO (a10 ea); +void WriteIO (a10 ea, d10 val, int32 mode); + +/* Unibus adapter data structures + + uba_dev UBA device descriptor + uba_unit UBA units + uba_reg UBA register list +*/ + +UNIT uba_unit[] = { + { UDATA (NULL, UNIT_FIX, UMAP_MEMSIZE) }, + { UDATA (NULL, UNIT_FIX, UMAP_MEMSIZE) } }; + +REG uba_reg[] = { + { ORDATA (INTREQ, int_req, 32), REG_RO }, + { ORDATA (UB1CS, ubcs[0], 18) }, + { ORDATA (UB3CS, ubcs[1], 18) }, + { ORDATA (DEVENB, dev_enb, 32), REG_HRO }, + { NULL } }; + +DEVICE uba_dev = { + "UBA", uba_unit, uba_reg, NULL, + UBANUM, 8, UMAP_ASIZE, 1, 8, 32, + &uba_ex, &uba_dep, &uba_reset, + NULL, NULL, NULL }; + +/* PDP-11 I/O structures */ + +struct iolink { /* I/O page linkage */ + int32 low; /* low I/O addr */ + int32 high; /* high I/O addr */ + int32 enb; /* enable mask */ + t_stat (*read)(); /* read routine */ + t_stat (*write)(); }; /* write routine */ + +/* Table of I/O devices and corresponding read/write routines + The expected Unibus adapter number is included as the high 2 bits */ + +struct iolink iotable[] = { + { IO_UBA1+IO_RHBASE, IO_UBA1+IO_RHBASE+047, 0, + &rp_rd, &rp_wr }, /* disk */ + { IO_UBA3+IO_TMBASE, IO_UBA3+IO_TMBASE+033, 0, + &tu_rd, &tu_wr }, /* mag tape */ +/* { IO_UBA3+IO_DZBASE, IO_UBA3+IO_DZBASE+07, INT_DZ, + &dz_rd, &dz_wr }, /* terminal mux */ + { IO_UBA3+IO_LPBASE, IO_UBA3+IO_LPBASE+017, 0, + &lp20_rd, &lp20_wr }, /* line printer */ + { IO_UBA3+IO_PTBASE, IO_UBA3+IO_PTBASE+07, INT_PTR, + &pt_rd, &pt_wr }, /* paper tape */ + { IO_UBA1+IO_UBMAP, IO_UBA1+IO_UBMAP+077, 0, + &ubmap_rd, &ubmap_wr }, /* Unibus 1 map */ + { IO_UBA3+IO_UBMAP, IO_UBA3+IO_UBMAP+077, 0, + &ubmap_rd, &ubmap_wr }, /* Unibus 3 map */ + { IO_UBA1+IO_UBCS, IO_UBA1+IO_UBCS, 0, + &ubs_rd, &ubs_wr }, /* Unibus 1 c/s */ + { IO_UBA3+IO_UBCS, IO_UBA3+IO_UBCS, 0, + &ubs_rd, &ubs_wr }, /* Unibus 3 c/s */ + { IO_UBA1+IO_UBMNT, IO_UBA1+IO_UBMNT, 0, + &rd_zro, &wr_nop }, /* Unibus 1 maint */ + { IO_UBA3+IO_UBMNT, IO_UBA3+IO_UBMNT, 0, + &rd_zro, &wr_nop }, /* Unibus 3 maint */ + { IO_UBA3+IO_TCUBASE, IO_UBA3+IO_TCUBASE+05, 0, + &tcu_rd, &wr_nop }, /* TCU150 */ + { 00100000, 00100000, 0, &rd_zro, &wr_nop }, /* Mem sys stat */ + { 0, 0, 0, NULL, NULL } }; + +/* Interrupt request to interrupt action map */ + +int32 (*int_ack[32])() = { /* int ack routines */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &rp_inta, &tu_inta, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &lp20_inta, NULL, NULL, NULL, NULL, NULL }; + +/* Interrupt request to vector map */ + +int32 int_vec[32] = { /* int req to vector */ + 0, 0, 0, 0, 0, 0, 0, 0, + VEC_RP, VEC_TU, 0, 0, 0, 0, 0, 0, + VEC_DZ, 0, 0, 0, 0, 0, 0, 0, + VEC_PTR, VEC_PTP, VEC_LP20, 0, 0, 0, 0, 0 }; + +/* IO 710 (DEC) TIOE - test I/O word, skip if zero + (ITS) IORDI - read word from Unibus 3 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io710 (int32 ac, a10 ea) +{ +d10 val; + +if (ITS) AC(ac) = ReadIO (IO_UBA3 | ea); /* IORDI */ +else { /* TIOE */ + val = ReadIO (ea); /* read word */ + if ((AC(ac) & val) == 0) return TRUE; } +return FALSE; +} + +/* IO 711 (DEC) TION - test I/O word, skip if non-zero + (ITS) IORDQ - read word from Unibus 1 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io711 (int32 ac, a10 ea) +{ +d10 val; + +if (ITS) AC(ac) = ReadIO (IO_UBA1 | ea); /* IORDQ */ +else { /* TION */ + val = ReadIO (ea); /* read word */ + if ((AC(ac) & val) != 0) return TRUE; } +return FALSE; +} + +/* IO 712 (DEC) RDIO - read I/O word, addr in ea + (ITS) IORD - read I/O word, addr in M[ea] +*/ + +d10 io712 (a10 ea) +{ +return ReadIO (ea); /* RDIO, IORD */ +} + +/* IO 713 (DEC) WRIO - write I/O word, addr in ea + (ITS) IOWR - write I/O word, addr in M[ea] +*/ + +void io713 (d10 val, a10 ea) +{ +WriteIO (ea, val & 0177777, WRITE); /* WRIO, IOWR */ +return; +} + +/* IO 714 (DEC) BSIO - set bit in I/O address + (ITS) IOWRI - write word to Unibus 3 +*/ + +void io714 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0177777; +if (ITS) WriteIO (IO_UBA3 | ea, val, WRITE); /* IOWRI */ +else { + temp = ReadIO (ea); /* BSIO */ + temp = temp | val; + WriteIO (ea, temp, WRITE); } +return; +} + +/* IO 715 (DEC) BCIO - clear bit in I/O address + (ITS) IOWRQ - write word to Unibus 1 +*/ + +void io715 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0177777; +if (ITS) WriteIO (IO_UBA1 | ea, val, WRITE); /* IOWRQ */ +else { + temp = ReadIO (ea); /* BCIO */ + temp = temp & ~val; + WriteIO (ea, temp, WRITE); } +return; +} + +/* IO 720 (DEC) TIOEB - test I/O byte, skip if zero + (ITS) IORDBI - read byte from Unibus 3 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io720 (int32 ac, a10 ea) +{ +d10 val; + +if (ITS) { /* IORDBI */ + val = ReadIO (IO_UBA3 | eaRB); + AC(ac) = GETBYTE (ea, val); } +else { /* TIOEB */ + val = ReadIO (eaRB); + val = GETBYTE (ea, val); + if ((AC(ac) & val) == 0) return TRUE; } +return FALSE; +} + +/* IO 721 (DEC) TIONB - test I/O word, skip if non-zero + (ITS) IORDBQ - read word from Unibus 1 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io721 (int32 ac, a10 ea) +{ +d10 val; + +if (ITS) { /* IORDBQ */ + val = ReadIO (IO_UBA1 | eaRB); + AC(ac) = GETBYTE (ea, val); } +else { /* TIONB */ + val = ReadIO (eaRB); + val = GETBYTE (ea, val); + if ((AC(ac) & val) != 0) return TRUE; } +return FALSE; +} + +/* IO 722 (DEC) RDIOB - read I/O byte, addr in ea + (ITS) IORDB - read I/O byte, addr in M[ea] +*/ + +d10 io722 (a10 ea) +{ +d10 val; + +val = ReadIO (eaRB); /* RDIOB, IORDB */ +return GETBYTE (ea, val); +} + +/* IO 723 (DEC) WRIOB - write I/O byte, addr in ea + (ITS) IOWRB - write I/O byte, addr in M[ea] +*/ + +void io723 (d10 val, a10 ea) +{ +WriteIO (ea, val & 0377, WRITEB); /* WRIOB, IOWRB */ +return; +} + +/* IO 724 (DEC) BSIOB - set bit in I/O byte address + (ITS) IOWRBI - write byte to Unibus 3 +*/ + +void io724 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0377; +if (ITS) WriteIO (IO_UBA3 | ea, val, WRITEB); /* IOWRBI */ +else { + temp = ReadIO (eaRB); /* BSIOB */ + temp = GETBYTE (ea, temp); + temp = temp | val; + WriteIO (ea, temp, WRITEB); } +return; +} + +/* IO 725 (DEC) BCIOB - clear bit in I/O byte address + (ITS) IOWRBQ - write byte to Unibus 1 +*/ + +void io725 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0377; +if (ITS) WriteIO (IO_UBA1 | ea, val, WRITEB); /* IOWRBQ */ +else { + temp = ReadIO (eaRB); /* BCIOB */ + temp = GETBYTE (ea, temp); + temp = temp & ~val; + WriteIO (ea, temp, WRITEB); } +return; +} + +/* Read and write I/O devices. + These routines are the linkage between the 64b world of the main + simulator and the 32b world of the device simulators. +*/ + +d10 ReadIO (a10 ea) +{ +int32 n, pa, val; +struct iolink *p; + +pa = (int32) ea; /* cvt addr to 32b */ +for (p = &iotable[0]; p -> low != 0; p++ ) { + if ((pa >= p -> low) && (pa <= p -> high) && + ((p -> enb == 0) || (dev_enb & p -> enb))) { + p -> read (&val, pa, READ); + pi_eval (); + return ((d10) val); } } +UBNXM_FAIL (pa, READ); +} + +void WriteIO (a10 ea, d10 val, int32 mode) +{ +int32 n, pa; +struct iolink *p; + +pa = (int32) ea; /* cvt addr to 32b */ +for (p = &iotable[0]; p -> low != 0; p++ ) { + if ((pa >= p -> low) && (pa <= p -> high) && + ((p -> enb == 0) || (dev_enb & p -> enb))) { + p -> write ((int32) val, pa, mode); + pi_eval (); + return; } } +UBNXM_FAIL (pa, mode); +} + +/* Evaluate Unibus priority interrupts */ + +int32 pi_ub_eval () +{ +int32 i, lvl; + +for (i = lvl = 0; i < UBANUM; i++) { + if (int_req & ubabr76[i]) + lvl = lvl | pi_l2bit[UBCS_GET_HI (ubcs[i])]; + if (int_req & ubabr54[i]) + lvl = lvl | pi_l2bit[UBCS_GET_LO (ubcs[i])]; } +return lvl; +} + +/* Return Unibus device vector + + Takes as input the request level calculated by pi_eval + If there is an interrupting Unibus device at that level, return its vector, + otherwise, returns 0 +*/ + +int32 pi_ub_vec (int32 rlvl, int32 *uba) +{ +int32 i, masked_irq; + +for (i = masked_irq = 0; i < UBANUM; i++) { + if ((rlvl == UBCS_GET_HI (ubcs[i])) && /* req on hi level? */ + (masked_irq = int_req & ubabr76[i])) break; + if ((rlvl == UBCS_GET_LO (ubcs[i])) && /* req on lo level? */ + (masked_irq = int_req & ubabr54[i])) break; } +*uba = (i << 1) + 1; /* store uba # */ +for (i = 0; (i < 32) && masked_irq; i++) { /* find hi pri req */ + if ((masked_irq >> i) & 1) { + int_req = int_req & ~(1u << i); /* clear req */ + if (int_ack[i]) return int_ack[i](); + return int_vec[i]; } } /* return vector */ +return 0; +} + +/* Unibus adapter map routines */ + +t_stat ubmap_rd (int32 *val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +*val = ubmap[n][pa & UMAP_AMASK]; +return SCPE_OK; +} + +t_stat ubmap_wr (int32 val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +ubmap[n][pa & UMAP_AMASK] = UMAP_POSFL (val) | UMAP_POSPN (val); +return SCPE_OK; +} + +/* Unibus adapter control/status routines */ + +t_stat ubs_rd (int32 *val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +if (int_req & ubabr76[n]) ubcs[n] = ubcs[n] | UBCS_HI; +if (int_req & ubabr54[n]) ubcs[n] = ubcs[n] | UBCS_LO; +*val = ubcs[n] = ubcs[n] & ~UBCS_RDZ; +return SCPE_OK; +} + +t_stat ubs_wr (int32 val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +if (val & UBCS_INI) { + reset_all (5); /* start after UBA */ + ubcs[n] = val & UBCS_DXF; } +else ubcs[n] = val & UBCS_RDW; +if (int_req & ubabr76[n]) ubcs[n] = ubcs[n] | UBCS_HI; +if (int_req & ubabr54[n]) ubcs[n] = ubcs[n] | UBCS_LO; +return SCPE_OK; +} + +/* Unibus adapter read zero/write ignore routines */ + +t_stat rd_zro (int32 *val, int32 pa, int32 mode) +{ +*val = 0; +return SCPE_OK; +} + +t_stat wr_nop (int32 val, int32 pa, int32 mode) +{ +return SCPE_OK; +} + +t_stat uba_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 uba = uptr - uba_unit; + +if (addr >= UMAP_MEMSIZE) return SCPE_NXM; +*vptr = ubmap[uba][addr]; +return SCPE_OK; +} + +t_stat uba_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 uba = uptr - uba_unit; + +if (addr >= UMAP_MEMSIZE) return SCPE_NXM; +ubmap[uba][addr] = (int32) val & UMAP_MASK; +return SCPE_OK; +} + +t_stat uba_reset (DEVICE *dptr) +{ +int32 i, uba; + +int_req = 0; +for (uba = 0; uba < UBANUM; uba++) { + ubcs[uba] = 0; + for (i = 0; i < UMAP_MEMSIZE; i++) ubmap[uba][i] = 0; } +pi_eval (); +return SCPE_OK; +} diff --git a/pdp10_lp20.c b/pdp10_lp20.c new file mode 100644 index 00000000..41be1783 --- /dev/null +++ b/pdp10_lp20.c @@ -0,0 +1,582 @@ +/* pdp10_lp20.c: PDP-10 LP20 line printer simulator + + Copyright (c) 1993-2001, 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. + + lp20 line printer +*/ + +#include "pdp10_defs.h" + +#define UNIT_DUMMY (1 << UNIT_V_UF) +#define LP_WIDTH 132 /* printer width */ + +/* DAVFU RAM */ + +#define DV_SIZE 143 /* DAVFU size */ +#define DV_DMASK 077 /* data mask per byte */ +#define DV_TOF 0 /* top of form channel */ +#define DV_MAX 11 /* max channel number */ + +/* Translation RAM */ + +#define TX_SIZE 256 /* translation RAM */ +#define TX_AMASK (TX_SIZE - 1) +#define TX_DMASK 07777 +#define TX_V_FL 8 /* flags */ +#define TX_M_FL 017 +/* define TX_INTR 04000 /* interrupt */ +#define TX_DELH 02000 /* delimiter */ +/* define TX_XLAT 01000 /* translate */ +/* define TX_DVFU 00400 /* DAVFU */ +#define TX_SLEW 00020 /* chan vs slew */ +#define TX_VMASK 00017 /* spacing mask */ +#define TX_CHR 0 /* states: pr char */ +#define TX_RAM 1 /* pr translation */ +#define TX_DVU 2 /* DAVFU action */ +#define TX_INT 3 /* interrupt */ +#define TX_GETFL(x) (((x) >> TX_V_FL) & TX_M_FL) + +/* LPCSRA (765400) */ + +#define CSA_GO 0000001 /* go */ +#define CSA_PAR 0000002 /* parity enable NI */ +#define CSA_V_FNC 2 /* function */ +#define CSA_M_FNC 03 +#define FNC_PR 0 /* print */ +#define FNC_TST 1 /* test */ +#define FNC_DVU 2 /* load DAVFU */ +#define FNC_RAM 3 /* load translation RAM */ +#define FNC_INTERNAL 1 /* internal function */ +#define CSA_FNC (CSA_M_FNC << CSA_V_FNC) +#define CSA_V_UAE 4 /* Unibus addr extension */ +#define CSA_UAE (03 << CSA_V_UAE) +#define CSA_IE 0000100 /* interrupt enable */ +#define CSA_DONE 0000200 /* done */ +#define CSA_INIT 0000400 /* init */ +#define CSA_ECLR 0001000 /* clear errors */ +#define CSA_DELH 0002000 /* delimiter hold */ +#define CSA_ONL 0004000 /* online */ +#define CSA_DVON 0010000 /* DAVFU online */ +#define CSA_UNDF 0020000 /* undefined char */ +#define CSA_PZRO 0040000 /* page counter zero */ +#define CSA_ERR 0100000 /* error */ +#define CSA_RW (CSA_DELH | CSA_IE | CSA_UAE | CSA_FNC | CSA_PAR | CSA_GO) +#define CSA_MBZ (CSA_ECLR | CSA_INIT) +#define CSA_GETUAE(x) (((x) & CSA_UAE) << (16 - CSA_V_UAE)) +#define CSA_GETFNC(x) (((x) >> CSA_V_FNC) & CSA_M_FNC) + +/* LPCSRB (765402) */ + +#define CSB_GOE 0000001 /* go error */ +#define CSB_DTE 0000002 /* DEM timing error NI */ +#define CSB_MTE 0000004 /* MSYN error (Ubus timeout) */ +#define CSB_RPE 0000010 /* RAM parity error NI */ +#define CSB_MPE 0000020 /* MEM parity error NI */ +#define CSB_LPE 0000040 /* LPT parity error NI */ +#define CSB_DVOF 0000100 /* DAVFU not ready */ +#define CSB_OFFL 0000200 /* offline */ +#define CSB_TEST 0003400 /* test mode */ +#define CSB_OVFU 0004000 /* optical VFU NI */ +#define CSB_PBIT 0010000 /* data parity bit NI */ +#define CSB_NRDY 0020000 /* printer error NI */ +#define CSB_LA180 0040000 /* LA180 printer NI */ +#define CSB_VLD 0100000 /* valid data NI */ +#define CSB_ECLR (CSB_GOE | CSB_DTE | CSB_MTE | CSB_RPE | CSB_MPE | CSB_LPE) +#define CSB_ERR (CSB_ECLR | CSB_DVOF | CSB_OFFL) +#define CSB_RW CSB_TEST +#define CSB_MBZ (CSB_DTE | CSB_RPE | CSB_MPE | CSB_LPE | CSB_OVFU |\ + CSB_PBIT | CSB_NRDY | CSB_LA180 | CSB_VLD) + +/* LPBA (765404) */ + +#define XBA_MBZ 0400000 /* addr<17> must be 0 */ + +/* LPBC (765506) */ + +#define BC_MASK 0007777 /* <15:12> MBZ */ + +/* LPPAGC (765510) */ + +#define PAGC_MASK 0007777 /* <15:12> MBZ */ + +/* LPRDAT (765512) */ + +#define RDAT_MASK 0007777 /* <15:12> MBZ */ + +/* LPCOLC/LPCBUF (765514) */ + +/* LPCSUM/LPPDAT (765516) */ + +extern d10 *M; /* main memory */ +extern int32 int_req; +extern int32 ubcs[UBANUM]; /* UBA csr */ +extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* UBA map */ +int32 lpcsa = 0; /* control/status A */ +int32 lpcsb = 0; /* control/status B */ +int32 lpba = 0; /* bus address */ +int32 lpbc = 0; /* byte count */ +int32 lppagc = 0; /* page count */ +int32 lprdat = 0; /* RAM data */ +int32 lpcbuf = 0; /* character buffer */ +int32 lpcolc = 0; /* column count */ +int32 lppdat = 0; /* printer data */ +int32 lpcsum = 0; /* checksum */ +int32 dvptr = 0; /* davfu pointer */ +int32 dvlnt = 0; /* davfu length */ +int32 lp20_irq = 0; /* int request */ +int32 lp20_stopioe = 0; /* stop on error */ +int16 txram[TX_SIZE] = { 0 }; /* translation RAM */ +int16 davfu[DV_SIZE] = { 0 }; /* DAVFU */ +t_stat lp20_svc (UNIT *uptr); +t_stat lp20_reset (DEVICE *dptr); +t_stat lp20_attach (UNIT *uptr, char *ptr); +t_stat lp20_detach (UNIT *uptr); +t_stat lp20_clear_vfu (UNIT *uptr, int32 arg); +t_bool lp20_print (int32 c); +t_bool lp20_adv (int32 c, t_bool advdvu); +t_bool lp20_davfu (int32 c); +void update_lpcs (int32 flg); + +/* LP data structures + + lp20_dev LPT device descriptor + lp20_unit LPT unit descriptor + lp20_reg LPT register list +*/ + +UNIT lp20_unit = { + UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG lp20_reg[] = { + { ORDATA (LPCSA, lpcsa, 16) }, + { ORDATA (LPCSB, lpcsb, 16) }, + { ORDATA (LPBA, lpba, 16) }, + { ORDATA (LPBC, lpbc, 12) }, + { ORDATA (LPPAGC, lppagc, 12) }, + { ORDATA (LPRDAT, lprdat, 12) }, + { ORDATA (LPCBUF, lpcbuf, 8) }, + { ORDATA (LPCOLC, lpcolc, 8) }, + { ORDATA (LPPDAT, lppdat, 8) }, + { ORDATA (LPCSUM, lpcsum, 8) }, + { ORDATA (DVPTR, dvptr, 7) }, + { ORDATA (DVLNT, dvlnt, 7), REG_RO + REG_NZ }, + { FLDATA (INT, int_req, INT_V_LP20) }, + { FLDATA (IRQ, lp20_irq, 0) }, + { FLDATA (ERR, lpcsa, CSR_V_ERR) }, + { FLDATA (DONE, lpcsa, CSR_V_DONE) }, + { FLDATA (IE, lpcsa, CSR_V_IE) }, + { DRDATA (POS, lp20_unit.pos, 31), PV_LEFT }, + { DRDATA (TIME, lp20_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lp20_stopioe, 0) }, + { BRDATA (TXRAM, txram, 8, 12, TX_SIZE) }, + { BRDATA (DAVFU, davfu, 8, 12, DV_SIZE) }, + { NULL } }; + +MTAB lp20_mod[] = { + { UNIT_DUMMY, 0, NULL, "VFUCLEAR", &lp20_clear_vfu }, + { 0 } }; + +DEVICE lp20_dev = { + "LP20", &lp20_unit, lp20_reg, lp20_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp20_reset, + NULL, &lp20_attach, &lp20_detach }; + +/* Line printer routines + + lp20_rd I/O page read + lp20_wr I/O page write + lp20_svc process event (printer ready) + lp20_reset process reset + lp20_attach process attach + lp20_detach process detach +*/ + +t_stat lp20_rd (int32 *data, int32 pa, int32 access) +{ +update_lpcs (0); /* update csr's */ +switch ((pa >> 1) & 07) { /* case on PA<3:1> */ +case 00: /* LPCSA */ + *data = lpcsa = lpcsa & ~CSA_MBZ; + break; +case 01: /* LPCSB */ + *data = lpcsb = lpcsb & ~CSB_MBZ; + break; +case 02: /* LPBA */ + *data = lpba; + break; +case 03: /* LPBC */ + *data = lpbc = lpbc & BC_MASK; + break; +case 04: /* LPPAGC */ + *data = lppagc = lppagc & PAGC_MASK; + break; +case 05: /* LPRDAT */ + *data = lprdat = lprdat & RDAT_MASK; + break; +case 06: /* LPCOLC/LPCBUF */ + *data = (lpcolc << 8) | lpcbuf; + break; +case 07: /* LPCSUM/LPPDAT */ + *data = (lpcsum << 8) | lppdat; + break; } /* end case PA */ +return SCPE_OK; +} + +t_stat lp20_wr (int32 data, int32 pa, int32 access) +{ +update_lpcs (0); /* update csr's */ +switch ((pa >> 1) & 07) { /* case on PA<3:1> */ +case 00: /* LPCSA */ + if (access == WRITEB) data = (pa & 1)? + (lpcsa & 0377) | (data << 8): (lpcsa & ~0377) | data; + if (data & CSA_ECLR) { /* error clear? */ + lpcsa = (lpcsa | CSA_DONE) & ~CSA_GO; /* set done, clr go */ + lpcsb = lpcsb & ~CSB_ECLR; /* clear err */ + sim_cancel (&lp20_unit); } /* cancel I/O */ + if (data & CSA_INIT) lp20_reset (&lp20_dev); /* init? */ + if (data & CSA_GO) { /* go set? */ + if ((lpcsa & CSA_GO) == 0) { /* not set before? */ + if (lpcsb & CSB_ERR) lpcsb = lpcsb | CSB_GOE; + lpcsum = 0; /* clear checksum */ + sim_activate (&lp20_unit, lp20_unit.time); } } + else sim_cancel (&lp20_unit); /* go clr, stop DMA */ + lpcsa = (lpcsa & ~CSA_RW) | (data & CSA_RW); + break; +case 01: /* LPCSB */ + break; /* ignore writes to TEST */ +case 02: /* LPBA */ + if (access == WRITEB) data = (pa & 1)? + (lpba & 0377) | (data << 8): (lpba & ~0377) | data; + lpba = data; + break; +case 03: /* LPBC */ + if (access == WRITEB) data = (pa & 1)? + (lpbc & 0377) | (data << 8): (lpbc & ~0377) | data; + lpbc = data & BC_MASK; + lpcsa = lpcsa & ~CSA_DONE; + break; +case 04: /* LPPAGC */ + if (access == WRITEB) data = (pa & 1)? + (lppagc & 0377) | (data << 8): (lppagc & ~0377) | data; + lppagc = data & PAGC_MASK; + break; +case 05: /* LPRDAT */ + if (access == WRITEB) data = (pa & 1)? + (lprdat & 0377) | (data << 8): (lprdat & ~0377) | data; + lprdat = data & RDAT_MASK; + txram[lpcbuf & TX_AMASK] = lprdat; /* load RAM */ + break; +case 06: /* LPCOLC/LPCBUF */ + if ((access == WRITEB) && (pa & 1)) /* odd byte */ + lpcolc = data & 0377; + else { lpcbuf = data & 0377; /* even byte, word */ + if (access == WRITE) lpcolc = (data >> 8) & 0377; } + break; +case 07: /* LPCSUM/LPPDAT */ + break; } /* read only */ +update_lpcs (0); +return SCPE_OK; +} + +/* Line printer service + + The translation RAM case table is derived from the LP20 spec and + verified against the LP20 RAM simulator in TOPS10 7.04 LPTSPL. + The equations are: + + flags := inter, delim, xlate, paper, delim_hold (from CSRA) + actions : = print_input, print_xlate, davfu_action, interrupt + + if (inter) { + if (!xlate || delim || delim_hold) interrupt; + else if (paper) davfu_action; + else print_xlate; } + else if (paper) { + if (xlate || delim || delim_hold) davfu_action; + else print_input; } + else { + if (xlate || delim || delim_hold) print_xlate; + else print_input; } +*/ + +t_stat lp20_svc (UNIT *uptr) +{ +int32 fnc, i, tbc, vpn, temp, txst, wd10; +int32 dvld = -2; /* must be even */ +int32 err = 0; +t_bool cont; +a10 ba, pa10; + +static const uint32 txcase[32] = { + TX_CHR, TX_RAM, TX_CHR, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, + TX_RAM, TX_RAM, TX_DVU, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, + TX_INT, TX_INT, TX_INT, TX_INT, TX_RAM, TX_INT, TX_DVU, TX_INT, + TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT }; + +lpcsa = lpcsa & ~CSA_GO; +ba = CSA_GETUAE (lpcsa) | lpba; +fnc = CSA_GETFNC (lpcsa); +tbc = 010000 - lpbc; +if (((fnc & FNC_INTERNAL) == 0) && ((lp20_unit.flags & UNIT_ATT) == 0)) { + update_lpcs (CSA_ERR); + IORETURN (lp20_stopioe, SCPE_UNATT); } +if ((fnc == FNC_PR) && (dvlnt == 0)) { + update_lpcs (CSA_ERR); + return SCPE_OK; } + +for (i = 0, cont = TRUE; (i < tbc) && cont; ba++, i++) { + vpn = PAG_GETVPN (ba >> 2); /* get PDP-10 page number */ + if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || + ((ubmap[1][vpn] & UMAP_VLD) == 0)) { /* invalid map? */ + lpcsb = lpcsb | CSB_MTE; /* set NXM error */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ + update_lpcs (CSA_ERR); /* set done */ + break; } + pa10 = (ubmap[1][vpn] + PAG_GETOFF (ba >> 2)) & PAMASK; + if (MEM_ADDR_NXM (pa10)) { /* nxm? */ + lpcsb = lpcsb | CSB_MTE; /* set NXM error */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ + update_lpcs (CSA_ERR); /* set done */ + break; } + wd10 = (int32) ((M[pa10] >> ((ba & 2)? 0: 18)) & 0177777); + lpcbuf = (wd10 >> ((ba & 1)? 8: 0)) & 0377; /* get character */ + lpcsum = (lpcsum + lpcbuf) & 0377; /* add into checksum */ + switch (fnc) { /* switch on function */ + +/* Translation RAM load */ + + case FNC_RAM: /* RAM load */ + txram[(i >> 1) & TX_AMASK] = wd10 & TX_DMASK; + break; + +/* DAVFU RAM load. The DAVFU RAM is actually loaded in bytes, delimited by + a start (354 to 356) and stop (357) byte pair. If the number of bytes + loaded is odd, or no bytes are loaded, the DAVFU is invalid. +*/ + + case FNC_DVU: /* DVU load */ + if ((lpcbuf >= 0354) && (lpcbuf <= 0356)) /* start DVU load? */ + dvld = dvlnt = 0; /* reset lnt */ + else if (lpcbuf == 0357) { /* stop DVU load? */ + dvptr = 0; /* reset ptr */ + if (dvld & 1) dvlnt = 0; } /* if odd, invalid */ + else if (dvld == 0) { /* even state? */ + temp = lpcbuf & DV_DMASK; + dvld = 1; } + else if (dvld == 1) { /* odd state? */ + if (dvlnt < DV_SIZE) davfu[dvlnt++] = + temp | ((lpcbuf & DV_DMASK) << 6); + dvld = 0; } + break; + +/* Print characters */ + + case FNC_PR: /* print */ + lprdat = txram[lpcbuf]; /* get RAM char */ + txst = (TX_GETFL (lprdat) << 1) | /* get state */ + ((lpcsa & CSA_DELH)? 1: 0); /* plus delim hold */ + if (lprdat & TX_DELH) lpcsa = lpcsa | CSA_DELH; + else lpcsa = lpcsa & ~CSA_DELH; + lpcsa = lpcsa & ~CSA_UNDF; /* assume char ok */ + switch (txcase[txst]) { /* case on state */ + case TX_CHR: /* take char */ + cont = lp20_print (lpcbuf); + break; + case TX_RAM: /* take translation */ + cont = lp20_print (lprdat); + break; + case TX_DVU: /* DAVFU action */ + if (lprdat & TX_SLEW) + cont = lp20_adv (lprdat & TX_VMASK, TRUE); + else cont = lp20_davfu (lprdat & TX_VMASK); + break; + case TX_INT: /* interrupt */ + lpcsa = lpcsa | CSA_UNDF; /* set flag */ + cont = FALSE; /* force stop */ + break; } /* end case char state */ + break; + case FNC_TST: /* test */ + break; } /* end case function */ + } /* end for */ +lpba = ba & 0177777; +lpcsa = (lpcsa & ~CSA_UAE) | ((ba >> (16 - CSA_V_UAE)) & CSA_UAE); +lpbc = (lpbc + i) & BC_MASK; +if (lpbc) update_lpcs (CSA_MBZ); /* intr, but not done */ +else update_lpcs (CSA_DONE); /* intr and done */ +if ((fnc == FNC_PR) && ferror (lp20_unit.fileref)) { + perror ("LP I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; } +return SCPE_OK; +} + +/* Print routines + + lp20_print print a character + lp20_adv advance n lines + lp20_davfu advance to channel on VFU + + Return TRUE to continue printing, FALSE to stop +*/ + +t_bool lp20_print (int32 c) +{ +t_bool r = TRUE; +int32 i, rpt = 1; + +lppdat = c & 0177; /* mask char to 7b */ +if (lppdat == 000) return TRUE; /* NUL? no op */ +if (lppdat == 012) return lp20_adv (1, TRUE); /* LF? adv carriage */ +if (lppdat == 014) return lp20_davfu (DV_TOF); /* FF? top of form */ +if (lppdat == 015) lpcolc = 0; /* CR? reset col cntr */ +else if (lppdat == 011) { /* TAB? simulate */ + lppdat = ' '; /* with spaces */ + if (lpcolc >= 128) { + r = lp20_adv (1, TRUE); /* eol? adv carriage */ + rpt = 8; } /* adv to col 9 */ + else rpt = 8 - (lpcolc & 07); } /* else adv 1 to 8 */ +else { if (lppdat < 040) lppdat = ' '; /* cvt non-prnt to spc */ + if (lpcolc >= LP_WIDTH) /* line full? */ + r = lp20_adv (1, TRUE); } /* adv carriage */ +for (i = 0; i < rpt; i++) putc (lppdat, lp20_unit.fileref); +lp20_unit.pos = lp20_unit.pos + rpt; +lpcolc = lpcolc + rpt; +return r; +} + +t_bool lp20_adv (int32 cnt, t_bool dvuadv) +{ +int32 i; + +if (cnt == 0) return TRUE; +lpcolc = 0; /* reset col cntr */ +for (i = 0; i < cnt; i++) putc ('\n', lp20_unit.fileref); +lp20_unit.pos = lp20_unit.pos + cnt; /* print 'n' newlines */ +if (dvuadv) dvptr = (dvptr + cnt) % dvlnt; /* update DAVFU ptr */ +if (davfu[dvptr] & (1 << DV_TOF)) { /* at top of form? */ + if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */ + lpcsa = lpcsa & ~CSA_PZRO; /* update status */ + return TRUE; } + else { lpcsa = lpcsa | CSA_PZRO; /* stop if zero */ + return FALSE; } } +return TRUE; +} + +t_bool lp20_davfu (int32 cnt) +{ +int i; + +if (cnt > DV_MAX) cnt = 7; /* inval chan? */ +for (i = 0; i < dvlnt; i++) { /* search DAVFU */ + dvptr = dvptr + 1; /* adv DAVFU ptr */ + if (dvptr >= dvlnt) dvptr = 0; /* wrap at end */ + if (davfu[dvptr] & (1 << cnt)) { /* channel stop set? */ + if (cnt) return lp20_adv (i + 1, FALSE); /* ~TOF, adv */ + if (lpcolc) lp20_adv (1, FALSE); /* TOF, need newline? */ + putc ('\f', lp20_unit.fileref); /* print form feed */ + lp20_unit.pos = lp20_unit.pos + 1; + if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */ + lpcsa = lpcsa & ~CSA_PZRO; /* update status */ + return TRUE; } + else { lpcsa = lpcsa | CSA_PZRO; /* stop if zero */ + return FALSE; } } + } /* end for */ +dvlnt = 0; /* DAVFU error */ +return FALSE; +} + +/* Update LPCSA, optionally request interrupt */ + +void update_lpcs (int32 flg) +{ +if (flg) lp20_irq = 1; /* set int req */ +lpcsa = (lpcsa | flg) & ~(CSA_MBZ | CSA_ERR | CSA_ONL | CSA_DVON); +lpcsb = (lpcsb | CSB_OFFL | CSB_DVOF) & ~CSB_MBZ; +if (lp20_unit.flags & UNIT_ATT) { + lpcsa = lpcsa | CSA_ONL; + lpcsb = lpcsb & ~CSB_OFFL; } +else lpcsa = lpcsa & ~CSA_DONE; +if (dvlnt) { + lpcsa = lpcsa | CSA_DVON; + lpcsb = lpcsb & ~CSB_DVOF; } +if (lpcsb & CSB_ERR) lpcsa = lpcsa | CSA_ERR; +if ((lpcsa & CSA_IE) && lp20_irq) int_req = int_req | INT_LP20; +else int_req = int_req & ~INT_LP20; +return; +} + +/* Acknowledge interrupt (clear internal request) */ + +int32 lp20_inta (void) +{ +lp20_irq = 0; /* clear int req */ +return VEC_LP20; +} + +t_stat lp20_reset (DEVICE *dptr) +{ +lpcsa = CSA_DONE; +lpcsb = 0; +lpba = lpbc = lppagc = lpcolc = 0; /* clear registers */ +lprdat = lppdat = lpcbuf = lpcsum = 0; +lp20_irq = 0; /* clear int req */ +dvptr = 0; /* reset davfu ptr */ +sim_cancel (&lp20_unit); /* deactivate unit */ +update_lpcs (0); /* update status */ +return SCPE_OK; +} + +t_stat lp20_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); /* attach file */ +if (lpcsa & CSA_ONL) return reason; /* just file chg? */ +if (sim_is_active (&lp20_unit)) update_lpcs (0); /* busy? no int */ +else update_lpcs (CSA_MBZ); /* interrupt */ +return reason; +} + +t_stat lp20_detach (UNIT *uptr) +{ +t_stat reason; + +reason = detach_unit (uptr); +sim_cancel (&lp20_unit); +lpcsa = lpcsa & ~CSA_GO; +update_lpcs (CSA_MBZ); +return reason; +} + +t_stat lp20_clear_vfu (UNIT *uptr, int32 arg) +{ +int i; + +if (!get_yn ("Clear DAVFU? [N]", FALSE)) return SCPE_OK; +for (i = 0; i < DV_SIZE; i++) davfu[i] = 0; +dvlnt = dvptr = 0; +update_lpcs (0); +return SCPE_OK; +} diff --git a/pdp10_mdfp.c b/pdp10_mdfp.c new file mode 100644 index 00000000..b7c6865f --- /dev/null +++ b/pdp10_mdfp.c @@ -0,0 +1,681 @@ +/* pdp10_mdfp.c: PDP-10 multiply/divide and floating point simulator + + Copyright (c) 1993-2001, 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. + + Instructions handled in this module: + imul integer multiply + idiv integer divide + mul multiply + div divide + dmul double precision multiply + ddiv double precision divide + fad(r) floating add (and round) + fsb(r) floating subtract (and round) + fmp(r) floating multiply (and round) + fdv(r) floating divide and round + fsc floating scale + fix(r) floating to fixed (and round) + fltr fixed to floating and round + dfad double precision floating add/subtract + dfmp double precision floating multiply + dfdv double precision floating divide + + The PDP-10 stores double (quad) precision integers in sequential + AC's or memory locations. Integers are stored in 2's complement + form. Only the sign of the high order word matters; the signs + in low order words are ignored on input and set to the sign of + the result on output. Quad precision integers exist only in the + AC's as the result of a DMUL or the dividend of a DDIV. + + 0 00000000011111111112222222222333333 + 0 12345678901234567890123456789012345 + +-+-----------------------------------+ + |S| high order integer | AC(n), A + +-+-----------------------------------+ + |S| low order integer | AC(n + 1), A + 1 + +-+-----------------------------------+ + |S| low order integer | AC(n + 2) + +-+-----------------------------------+ + |S| low order integer | AC(n + 3) + +-+-----------------------------------+ + + The PDP-10 supports two floating point formats: single and double + precision. In both, the exponent is 8 bits, stored in excess + 128 notation. The fraction is expected to be normalized. A + single precision floating point number has 27 bits of fraction; + a double precision number has 62 bits of fraction (the sign + bit of the second word is ignored and is set to zero). + + In a negative floating point number, the exponent is stored in + one's complement form, the fraction in two's complement form. + + 0 00000000 011111111112222222222333333 + 0 12345678 901234567890123456789012345 + +-+--------+---------------------------+ + |S|exponent| high order fraction | AC(n), A + +-+--------+---------------------------+ + |0| low order fraction | AC(n + 1), A + 1 + +-+------------------------------------+ + + Note that treatment of the sign is different for double precision + integers and double precision floating point. DMOVN (implemented + as an inline macro) follows floating point conventions. + + The original PDP-10 CPU (KA10) used a different format for double + precision numbers and included certain instructions to make + software support easier. These instructions were phased out in + the KL10 and KS10 and are treated as MUUO's. + + The KL10 added extended precision (11-bit exponent) floating point + format (so-called G floating). These instructions were not + implemented in the KS10 and are treated as MUUO's. +*/ + +#include "pdp10_defs.h" +#include + +struct ufp { /* unpacked fp number */ + int32 sign; /* sign */ + int32 exp; /* exponent */ + uint64 fhi; /* fraction high */ + uint64 flo; }; /* for double prec */ + +typedef struct ufp UFP; + +#define MSK32 0xFFFFFFFF +#define FIT27 (DMASK - 0x07FFFFFF) +#define FIT32 (DMASK - MSK32) +#define SFRC TRUE /* frac 2's comp */ +#define AFRC FALSE /* frac abs value */ + +/* In packed floating point number */ + +#define FP_BIAS 0200 /* exponent bias */ +#define FP_N_FHI 27 /* # of hi frac bits */ +#define FP_V_FHI 0 /* must be zero */ +#define FP_M_FHI 0000777777777 +#define FP_N_EXP 8 /* # of exp bits */ +#define FP_V_EXP (FP_V_FHI + FP_N_FHI) +#define FP_M_EXP 0377 +#define FP_V_SIGN (FP_V_EXP + FP_N_EXP) /* sign */ +#define FP_N_FLO 35 /* # of lo frac bits */ +#define FP_V_FLO 0 /* must be zero */ +#define FP_M_FLO 0377777777777 +#define GET_FPSIGN(x) ((int32) (((x) >> FP_V_SIGN) & 1)) +#define GET_FPEXP(x) ((int32) (((x) >> FP_V_EXP) & FP_M_EXP)) +#define GET_FPHI(x) ((x) & FP_M_FHI) +#define GET_FPLO(x) ((x) & FP_M_FLO) + +/* In unpacked floating point number */ + +#define FP_N_GUARD 1 /* # of guard bits */ +#define FP_V_UFLO FP_N_GUARD /* <35:1> */ +#define FP_V_URNDD (FP_V_UFLO - 1) /* dp round bit */ +#define FP_V_UFHI (FP_V_UFLO + FP_N_FLO) /* <62:36> */ +#define FP_V_URNDS (FP_V_UFHI - 1) /* sp round bit */ +#define FP_V_UCRY (FP_V_UFHI + FP_N_FHI) /* <63> */ +#define FP_V_UNORM (FP_V_UCRY - 1) /* normalized bit */ +#define FP_UFHI 0x7FFFFFF000000000 +#define FP_UFLO 0x0000000FFFFFFFFE +#define FP_UFRAC 0x7FFFFFFFFFFFFFFE +#define FP_URNDD 0x0000000000000001 +#define FP_URNDS 0x0000000800000000 +#define FP_UNORM 0x4000000000000000 +#define FP_UCRY 0x8000000000000000 +#define FP_ONES 0xFFFFFFFFFFFFFFFF + +#define UNEG(x) ((~x) + 1) +#define DUNEG(x) x.flo = UNEG (x.flo); x.fhi = ~x.fhi + (x.flo == 0) + +extern d10 *ac_cur; /* current AC block */ +extern int32 flags; /* flags */ +void mul (d10 a, d10 b, d10 *rs); +void funpack (d10 h, d10 l, UFP *r, t_bool sgn); +void fnorm (UFP *r, int64 rnd); +d10 fpack (UFP *r, d10 *lo, t_bool fdvneg); + +/* Integer multiply - checked against KS-10 ucode */ + +d10 imul (d10 a, d10 b) +{ +d10 rs[2]; + +if ((a == SIGN) && (b == SIGN)) { /* KS10 hack */ + SETF (F_AOV | F_T1); /* -2**35 squared */ + return SIGN; } +mul (a, b, rs); /* mpy, dprec result */ +if (rs[0] && (rs[0] != ONES)) { /* high not all sign? */ + rs[1] = TSTS (a ^ b)? SETS (rs[1]): CLRS (rs[1]); /* set sign */ + SETF (F_AOV | F_T1); } /* overflow */ +return rs[1]; +} + +/* Integer divide, return quotient, remainder - checked against KS10 ucode + The KS10 does not recognize -2^35/-1 as an error. Instead, it produces + 2^35 (that is, -2^35) as the incorrect result. +*/ + +t_bool idiv (d10 a, d10 b, d10 *rs) +{ +d10 dvd = ABS (a); /* make ops positive */ +d10 dvr = ABS (b); + +if (dvr == 0) { /* divide by 0? */ + SETF (F_DCK | F_AOV | F_T1); /* set flags, return */ + return FALSE; } +rs[0] = dvd / dvr; /* get quotient */ +rs[1] = dvd % dvr; /* get remainder */ +if (TSTS (a ^ b)) rs[0] = NEG (rs[0]); /* sign of result */ +if (TSTS (a)) rs[1] = NEG (rs[1]); /* sign of remainder */ +return TRUE; +} + +/* Multiply, return double precision result - checked against KS10 ucode */ + +void mul (d10 s1, d10 s2, d10 *rs) +{ +register uint64 a = ABS (s1); +register uint64 b = ABS (s2); +register uint64 t, u, r; + +if ((a == 0) || (b == 0)) { /* operand = 0? */ + rs[0] = rs[1] = 0; /* result 0 */ + return; } +if ((a & FIT32) || (b & FIT32)) { /* fit in 64b? */ + t = a >> 18; /* no, split in half */ + a = a & RMASK; /* "dp" multiply */ + u = b >> 18; + b = b & RMASK; + r = (a * b) + (((a * u) + (b * t)) << 18); /* low is only 35b */ + rs[0] = ((t * u) << 1) + (r >> 35); /* so lsh hi 1 */ + rs[1] = r & MMASK; } +else { r = a * b; /* fits, native mpy */ + rs[0] = r >> 35; /* split at bit 35 */ + rs[1] = r & MMASK; } + +if (TSTS (s1 ^ s2)) { MKDNEG (rs); } /* result -? */ +else if (TSTS (rs[0])) { /* result +, 2**70? */ + SETF (F_AOV | F_T1); /* overflow */ + rs[1] = SETS (rs[1]); } /* consistent - */ +return; +} + +/* Divide, return quotient and remainder - checked against KS10 ucode + Note that the initial divide check catches the case -2^70/-2^35; + thus, the quotient can have at most 35 bits. +*/ + +t_bool divi (int32 ac, d10 b, d10 *rs) +{ +register int32 p1 = ADDAC (ac, 1); +register d10 dvr = ABS (b); /* make divr positive */ +int64 t; +int32 i; +d10 dvd[2]; + +dvd[0] = AC(ac); /* divd high */ +dvd[1] = CLRS (AC(p1)); /* divd lo, clr sgn */ +if (TSTS (AC(ac))) { DMOVN (dvd); } /* make divd positive */ +if (dvd[0] >= dvr) { /* divide fail? */ + SETF (F_AOV | F_DCK | F_T1); /* set flags, return */ + return FALSE; } +if (dvd[0] & FIT27) { /* fit in 63b? */ + for (i = 0, rs[0] = 0; i < 35; i++) { /* 35 quotient bits */ + dvd[0] = (dvd[0] << 1) | ((dvd[1] >> 34) & 1); + dvd[1] = (dvd[1] << 1) & MMASK; /* shift dividend */ + rs[0] = rs[0] << 1; /* shift quotient */ + if (dvd[0] >= dvr) { /* subtract work? */ + dvd[0] = dvd[0] - dvr; /* quo bit is 1 */ + rs[0] = rs[0] + 1; } } + rs[1] = dvd[0]; } /* store remainder */ +else { t = (dvd[0] << 35) | dvd[1]; /* concatenate */ + rs[0] = t / dvr; /* quotient */ + rs[1] = t % dvr; } /* remainder */ +if (TSTS (AC(ac) ^ b)) rs[0] = NEG (rs[0]); /* sign of result */ +if (TSTS (AC(ac))) rs[1] = NEG (rs[1]); /* sign of remainder */ +return TRUE; +} + +/* Double precision multiply. This is done the old fashioned way. Cross + product multiplies would be a lot faster but would require more code. +*/ + +void dmul (int32 ac, d10 *mpy) +{ +int32 p1 = ADDAC (ac, 1); +int32 p2 = ADDAC (ac, 2); +int32 p3 = ADDAC (ac, 3); +int32 i; +d10 mpc[2], sign; + +mpc[0] = AC(ac); /* mplcnd hi */ +mpc[1] = CLRS (AC(p1)); /* mplcnd lo, clr sgn */ +sign = mpc[0] ^ mpy[0]; /* sign of result */ +if (TSTS (mpc[0])) { DMOVN (mpc); } /* get abs (mpcnd) */ +if (TSTS (mpy[0])) { DMOVN (mpy); } /* get abs (mpyer) */ +else mpy[1] = CLRS (mpy[1]); /* clear mpy lo sign */ +AC(ac) = AC(p1) = AC(p2) = AC(p3) = 0; /* clear AC's */ +if (((mpy[0] | mpy[1]) == 0) || ((mpc[0] | mpc[1]) == 0)) return; +for (i = 0; i < 71; i++) { /* 71 mpyer bits */ + if (i) { /* shift res, mpy */ + AC(p3) = (AC(p3) >> 1) | ((AC(p2) & 1) << 34); + AC(p2) = (AC(p2) >> 1) | ((AC(p1) & 1) << 34); + AC(p1) = (AC(p1) >> 1) | ((AC(ac) & 1) << 34); + AC(ac) = AC(ac) >> 1; + mpy[1] = (mpy[1] >> 1) | ((mpy[0] & 1) << 34); + mpy[0] = mpy[0] >> 1; } + if (mpy[1] & 1) { /* if mpy lo bit = 1 */ + AC(p1) = AC(p1) + mpc[1]; + AC(ac) = AC(ac) + mpc[0] + (TSTS (AC(p1) != 0)); + AC(p1) = CLRS (AC(p1)); } } +if (TSTS (sign)) { /* result minus? */ + AC(p3) = (-AC(p3)) & MMASK; /* quad negate */ + AC(p2) = (~AC(p2) + (AC(p3) == 0)) & MMASK; + AC(p1) = (~AC(p1) + (AC(p2) == 0)) & MMASK; + AC(ac) = (~AC(ac) + (AC(p1) == 0)) & DMASK; } +else if (TSTS (AC(ac))) SETF (F_AOV | F_T1); /* wrong sign */ +if (TSTS (AC(ac))) { /* if result - */ + AC(p1) = SETS (AC(p1)); /* make signs consistent */ + AC(p2) = SETS (AC(p2)); + AC(p3) = SETS (AC(p3)); } +return; +} + +/* Double precision divide - checked against KS10 ucode */ + +void ddiv (int32 ac, d10 *dvr) +{ +int32 i, cryin; +d10 sign, qu[2], dvd[4]; + +dvd[0] = AC(ac); /* save dividend */ +for (i = 1; i < 4; i++) dvd[i] = CLRS (AC(ADDAC (ac, i))); +sign = AC(ac) ^ dvr[0]; /* sign of result */ +if (TSTS (AC(ac))) { /* get abs (dividend) */ + for (i = 3, cryin = 1; i > 0; i--) { /* negate quad */ + dvd[i] = (~dvd[i] + cryin) & MMASK; /* comp + carry in */ + if (dvd[i]) cryin = 0; } /* next carry in */ + dvd[0] = (~dvd[0] + cryin) & DMASK; } +if (TSTS (dvr[0])) { DMOVN (dvr); } /* get abs (divisor) */ +else dvr[1] = CLRS (dvr[1]); +if (DCMPGE (dvd, dvr)) { /* will divide work? */ + SETF (F_AOV | F_DCK | F_T1); /* no, set flags */ + return; } +qu[0] = qu[1] = 0; /* clear quotient */ +for (i = 0; i < 70; i++) { /* 70 quotient bits */ + dvd[0] = ((dvd[0] << 1) | ((dvd[1] >> 34) & 1)) & DMASK;; + dvd[1] = ((dvd[1] << 1) | ((dvd[2] >> 34) & 1)) & MMASK; + dvd[2] = ((dvd[2] << 1) | ((dvd[3] >> 34) & 1)) & MMASK; + dvd[3] = (dvd[3] << 1) & MMASK; /* shift dividend */ + qu[0] = (qu[0] << 1) | ((qu[1] >> 34) & 1); /* shift quotient */ + qu[1] = (qu[1] << 1) & MMASK; + if (DCMPGE (dvd, dvr)) { /* subtract work? */ + dvd[0] = dvd[0] - dvr[0] - (dvd[1] < dvr[1]); + dvd[1] = (dvd[1] - dvr[1]) & MMASK; /* do subtract */ + qu[1] = qu[1] + 1; } } /* set quotient bit */ +if (TSTS (sign) && (qu[0] | qu[1])) { MKDNEG (qu); } +if (TSTS (AC(ac)) && (dvd[0] | dvd[1])) { MKDNEG (dvd); } +AC(ac) = qu[0]; /* quotient */ +AC(ADDAC(ac, 1)) = qu[1]; +AC(ADDAC(ac, 2)) = dvd[0]; /* remainder */ +AC(ADDAC(ac, 3)) = dvd[1]; +return; +} + +/* Single precision floating add - checked against KS10 ucode + The KS10 shifts the smaller operand regardless of the exponent diff. + This code will not shift more than 63 places; shifts beyond that + cannot change the value of the smaller operand. + + If the signs of the operands are the same, the result sign is the + same as the source sign; the sign of the result fraction is actually + part of the data. If the signs of the operands are different, the + result sign is determined by the fraction sign. +*/ + +d10 fad (d10 op1, d10 op2, t_bool rnd, int32 inv) +{ +register int32 ediff; +UFP a, b, t; + +if (inv) op2 = NEG (op2); /* subtract? -b */ +if (op1 == 0) funpack (op2, 0, &a, AFRC); /* a = 0? result is b */ +else if (op2 == 0) funpack (op1, 0, &a, AFRC); /* b = 0? result is a */ +else { funpack (op1, 0, &a, SFRC); /* unpack operands */ + funpack (op2, 0, &b, SFRC); /* fracs are 2's comp */ + ediff = a.exp - b.exp; /* get exp diff */ + if (ediff < 0) { /* a < b? switch */ + t = a; + a = b; + b = t; + ediff = -ediff; } + if (ediff > 63) ediff = 63; /* cap diff at 63 */ + if (ediff) b.fhi = (int64) b.fhi >> ediff; /* shift b (signed) */ + a.fhi = a.fhi + b.fhi; /* add fractions */ + if (a.sign ^ b.sign) { /* add or subtract? */ + if (a.fhi & FP_UCRY) { /* subtract, frac -? */ + a.fhi = UNEG (a.fhi); /* complement result */ + a.sign = 1; } /* result is - */ + else a.sign = 0; } /* result is + */ + else { if (a.sign) a.fhi = UNEG (a.fhi); /* add, src -? comp */ + if (a.fhi & FP_UCRY) { /* check for carry */ + a.fhi = a.fhi >> 1; /* flo won't be used */ + a.exp = a.exp + 1; } } } +fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ +return fpack (&a, NULL, FALSE); +} + +/* Single precision floating multiply. Because the fractions are 27b, + a 64b multiply can be used for the fraction multiply. The 27b + fractions are positioned 0'frac'0000, resulting in 00'hifrac'0..0. + The extra 0 is accounted for by biasing the result exponent. +*/ + +#define FP_V_SPM (FP_V_UFHI - (32 - FP_N_FHI - 1)) +d10 fmp (d10 op1, d10 op2, t_bool rnd) +{ +UFP a, b; + +funpack (op1, 0, &a, AFRC); /* unpack operands */ +funpack (op2, 0, &b, AFRC); /* fracs are abs val */ +if ((a.fhi == 0) || (b.fhi == 0)) return 0; /* either 0? */ +a.sign = a.sign ^ b.sign; /* result sign */ +a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */ +a.fhi = (a.fhi >> FP_V_SPM) * (b.fhi >> FP_V_SPM); /* high 27b of result */ +fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ +return fpack (&a, NULL, FALSE); +} + +/* Single precision floating divide. Because the fractions are 27b, a + 64b divide can be used for the fraction divide. Note that 28b-29b + of fraction are developed; the code will do one special normalize to + make sure that the 28th bit is not lost. Also note the special + treatment of negative quotients with non-zero remainders; this + implements the note on p2-23 of the Processor Reference Manual. +*/ + +t_bool fdv (d10 op1, d10 op2, d10 *rs, t_bool rnd) +{ +UFP a, b; +uint64 savhi; +t_bool rem = FALSE; + +funpack (op1, 0, &a, AFRC); /* unpack operands */ +funpack (op2, 0, &b, AFRC); /* fracs are abs val */ +if (a.fhi >= 2 * b.fhi) { /* will divide work? */ + SETF (F_AOV | F_DCK | F_FOV | F_T1); + return FALSE; } +if (savhi = a.fhi) { /* dvd = 0? quo = 0 */ + a.sign = a.sign ^ b.sign; /* result sign */ + a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */ + a.fhi = a.fhi / (b.fhi >> (FP_N_FHI + 1)); /* do divide */ + if (a.sign && (savhi != (a.fhi * (b.fhi >> (FP_N_FHI + 1))))) + rem = TRUE; /* KL/KS hack */ + a.fhi = a.fhi << (FP_V_UNORM - FP_N_FHI - 1); /* put quo in place */ + if ((a.fhi & FP_UNORM) == 0) { /* normalize 1b */ + a.fhi = a.fhi << 1; /* before masking */ + a.exp = a.exp - 1; } + a.fhi = a.fhi & (FP_UFHI | FP_URNDS); } /* mask quo to 28b */ +fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ +*rs = fpack (&a, NULL, rem); /* pack result */ +return TRUE; +} + +/* Single precision floating scale. */ + +d10 fsc (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea); +UFP a; + +if (val == 0) return 0; +funpack (val, 0, &a, AFRC); /* unpack operand */ +if (ea & RSIGN) a.exp = a.exp - sc; /* adjust exponent */ +else a.exp = a.exp + sc; +fnorm (&a, 0); /* renormalize */ +return fpack (&a, NULL, FALSE); /* pack result */ +} + +/* Float integer operand and round */ + +d10 fltr (d10 mb) +{ +UFP a; +d10 val = ABS (mb); + +a.sign = GET_FPSIGN (mb); /* get sign */ +a.exp = FP_BIAS + 36; /* initial exponent */ +a.fhi = val << (FP_V_UNORM - 35); /* left justify op */ +a.flo = 0; +fnorm (&a, FP_URNDS); /* normalize, round */ +return fpack (&a, NULL, FALSE); /* pack result */ +} + +/* Fix and truncate/round floating operand */ + +void fix (int32 ac, d10 mb, t_bool rnd) +{ +int32 sc; +uint64 so; +UFP a; + +funpack (mb, 0, &a, AFRC); /* unpack operand */ +if (a.exp > (FP_BIAS + FP_N_FHI + FP_N_EXP)) SETF (F_AOV | F_T1); +else if (a.exp < (FP_BIAS - 1)) AC(ac) = 0; +else { sc = FP_V_UNORM - (a.exp - FP_BIAS) + 1; + AC(ac) = a.fhi >> sc; + if (rnd) { + so = a.fhi << (64 - sc); + if (so >= (0x8000000000000000 + a.sign)) AC(ac) = AC(ac) + 1; } + if (a.sign) AC(ac) = NEG (AC(ac)); } +return; +} + +/* Double precision floating add/subtract + Since a.flo is 0, adding b.flo is just a copy - this is incorporated into + the denormalization step. If there's no denormalization, bflo is zero too. +*/ + +void dfad (int32 ac, d10 *rs, int32 inv) +{ +int32 p1 = ADDAC (ac, 1); +register int32 ediff; +UFP a, b, t; + +if (inv) { DMOVN (rs); } /* subtract? -b */ +if ((AC(ac) | AC(p1)) == 0) funpack (rs[0], rs[1], &a, AFRC); + /* a == 0? sum = b */ +else if ((rs[0] | rs[1]) == 0) funpack (AC(ac), AC(p1), &a, AFRC); + /* b == 0? sum = a */ +else { + funpack (AC(ac), AC(p1), &a, SFRC); /* unpack operands */ + funpack (rs[0], rs[1], &b, SFRC); + ediff = a.exp - b.exp; /* get exp diff */ + if (ediff < 0) { /* a < b? switch */ + t = a; + a = b; + b = t; + ediff = -ediff; } + if (ediff > 127) ediff = 127; /* cap diff at 127 */ + if (ediff > 63) { /* diff > 63? */ + a.flo = (int64) b.fhi >> (ediff - 64); /* b hi to a lo */ + b.fhi = b.sign? FP_ONES: 0; } /* hi = all sign */ + else if (ediff) { /* diff <= 63 */ + a.flo = (b.flo >> ediff) | (b.fhi << (64 - ediff)); + b.fhi = (int64) b.fhi >> ediff; } /* shift b (signed) */ + a.fhi = a.fhi + b.fhi; /* do add */ + if (a.sign ^ b.sign) { /* add or subtract? */ + if (a.fhi & FP_UCRY) { /* subtract, frac -? */ + DUNEG (a); /* complement result */ + a.sign = 1; } /* result is - */ + else a.sign = 0; } /* result is + */ + else { if (a.sign) { DUNEG (a); }; /* add, src -? comp */ + if (a.fhi & FP_UCRY) { /* check for carry */ + a.fhi = a.fhi >> 1; /* flo won't be used */ + a.exp = a.exp + 1; } } } +fnorm (&a, FP_URNDD); /* normalize, round */ +AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ +return; +} + +/* Double precision floating multiply + The 62b fractions are multiplied, with cross products, to produce a + 124b fraction with two leading and two trailing 0's. Because the + product has 2 leading 0's, instead of the normal 1, an extra + normalization step is needed. Accordingly, the exponent calculation + increments the result exponent, to compensate for normalization. +*/ + +void dfmp (int32 ac, d10 *rs) +{ +int32 p1 = ADDAC (ac, 1); +uint64 xh, xl, yh, yl, mid; +UFP a, b; + +funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */ +funpack (rs[0], rs[1], &b, AFRC); +if ((a.fhi == 0) || (b.fhi == 0)) { /* either 0? result 0 */ + AC(ac) = AC(p1) = 0; + return; } +a.sign = a.sign ^ b.sign; /* result sign */ +a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */ +xh = a.fhi >> 32; /* split 62b fracs */ +xl = a.fhi & MSK32; /* into 32b halves */ +yh = b.fhi >> 32; +yl = b.fhi & MSK32; +a.fhi = xh * yh; /* hi xproduct */ +a.flo = xl * yl; /* low xproduct */ +mid = (xh * yl) + (yh * xl); /* fits in 64b */ +a.flo = a.flo + (mid << 32); /* add mid lo to lo */ +a.fhi = a.fhi + ((mid >> 32) & MSK32) + (a.flo < (mid << 32)); +fnorm (&a, FP_URNDD); /* normalize, round */ +AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ +return; +} + +/* Double precision floating divide + This algorithm develops a full 62 bits of quotient, plus one rounding + bit, in the low order 63b of a 64b number. To do this, we must assure + that the initial divide step generates a 1. If it would fail, shift + the dividend left and decrement the result exponent accordingly. +*/ + +void dfdv (int32 ac, d10 *rs) +{ +int32 p1 = ADDAC (ac, 1); +int32 i; +uint64 qu = 0; +UFP a, b; + +funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */ +funpack (rs[0], rs[1], &b, AFRC); +if (a.fhi >= 2 * b.fhi) { /* will divide work? */ + SETF (F_AOV | F_DCK | F_FOV | F_T1); + return; } +if (a.fhi) { /* dvd = 0? quo = 0 */ + a.sign = a.sign ^ b.sign; /* result sign */ + a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */ + if (a.fhi < b.fhi) { /* make sure initial */ + a.fhi = a.fhi << 1; /* divide step will work */ + a.exp = a.exp - 1; } + for (i = 0; i < 63; i++) { /* 63b of quotient */ + qu = qu << 1; /* shift quotient */ + if (a.fhi >= b.fhi) { /* will div work? */ + a.fhi = a.fhi - b.fhi; /* sub, quo = 1 */ + qu = qu + 1; } + a.fhi = a.fhi << 1; } /* shift dividend */ + a.fhi = qu; } +fnorm (&a, FP_URNDD); /* normalize, round */ +AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ +return; +} + +/* Unpack floating point operand */ + +void funpack (d10 h, d10 l, UFP *r, t_bool sgn) +{ +d10 fphi, fplo; + +r -> sign = GET_FPSIGN (h); +r -> exp = GET_FPEXP (h); +fphi = GET_FPHI (h); +fplo = GET_FPLO (l); +r -> fhi = (fphi << FP_V_UFHI) | (fplo << FP_V_UFLO); +r -> flo = 0; +if (r -> sign) { + r -> exp = r -> exp ^ FP_M_EXP; + if (sgn) r -> fhi = r -> fhi | FP_UCRY; /* ext sign */ + else { if (r -> fhi) r -> fhi = UNEG (r -> fhi) & FP_UFRAC; + else { r -> exp = r -> exp + 1; + r -> fhi = FP_UNORM; } } } +return; +} + +/* Normalize and optionally round floating point operand */ + +void fnorm (UFP *a, int64 rnd) +{ +int32 i; +static uint64 normmask[6] = { + 0x6000000000000000, 0x7800000000000000, 0x7F80000000000000, + 0x7FFF800000000000, 0x7FFFFFFF80000000, 0x7FFFFFFFFFFFFFFF }; +static int32 normtab[7] = { 1, 2, 4, 8, 16, 32, 63 }; + +if ((a -> fhi | a -> flo) == 0) { /* if fraction = 0 */ + a -> sign = a -> exp = 0; /* result is 0 */ + return; } +while ((a -> fhi & FP_UNORM) == 0) { /* normalized? */ + for (i = 0; i < 6; i++) { + if (a -> fhi & normmask[i]) break; } + a -> fhi = (a -> fhi << normtab[i]) | (a -> flo >> (64 - normtab[i])); + a -> flo = a -> flo << normtab[i]; + a -> exp = a -> exp - normtab[i]; } +if (rnd) { /* rounding? */ + a -> fhi = a -> fhi + rnd; /* add round const */ + if (a -> fhi & FP_UCRY) { /* if carry out, */ + a -> fhi = a -> fhi >> 1; /* renormalize */ + a -> exp = a -> exp + 1; } } +return; +} + +/* Pack floating point result */ + +d10 fpack (UFP *r, d10 *lo, t_bool fdvneg) +{ +d10 val[2]; + +if (r -> exp < 0) SETF (F_AOV | F_FOV | F_FXU | F_T1); +else if (r -> exp > FP_M_EXP) SETF (F_AOV | F_FOV | F_T1); +val[0] = (((((d10) r -> exp) & FP_M_EXP) << FP_V_EXP) | + ((r -> fhi & FP_UFHI) >> FP_V_UFHI)) & DMASK; +if (lo) val[1] = ((r -> fhi & FP_UFLO) >> FP_V_UFLO) & MMASK; +else val[1] = 0; +if (r -> sign) { /* negate? */ + if (fdvneg) { /* fdvr special? */ + val[1] = ~val[1] & MMASK; /* 1's comp */ + val[0] = ~val[0] & DMASK; } + else { DMOVN (val); } } /* 2's comp */ +if (lo) *lo = val[1]; +return val[0]; +} diff --git a/pdp10_pag.c b/pdp10_pag.c new file mode 100644 index 00000000..c85ab929 --- /dev/null +++ b/pdp10_pag.c @@ -0,0 +1,792 @@ +/* pdp10_pag.c: PDP-10 paging subsystem simulator + + Copyright (c) 1993-2001, 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. + + pag KS10 pager + + 03-May-01 RMS Fixed bug in indirect page table pointer processing + 29-Apr-01 RMS Added CLRCSH for ITS, fixed LPMR + + The pager consists of a standard hardware part (the translation + tables) and an operating-system specific page table fill routine. + + There are two translation tables, one for executive mode and one + for user mode. Each table consists of 512 page table entries, + one for each page in the 18b virtual address space. Each pte + contains (in the hardware) a valid bit, a writeable bit, an + address space bit (executive or user), and a cacheable bit, plus + the physical page number corresponding to the virtual page. In + the simulator, the pte is expanded for rapid processing of normal + reads and writes. An expanded pte contains a valid bit, a writeable + bit, and the physical page number shifted left by the page size. + + Expanded pte meaning + 0 invalid + >0 read only + <0 read write + + There is a third, physical table, which is used in place of the + executive and user tables if paging is off. Its entries are always + valid and always writeable. + + To translate a virtual to physical address, the simulator uses + the virtual page number to index into the appropriate page table. + If the page table entry (pte) is not valid, the page fill routine + is called to see if the entry is merely not filled or is truly + inaccessible. If the pte is valid but not writeable, and the + reference is a write reference, the page fill routine is also + called to see if the reference can be resolved. + + The page fill routine is operating system dependent. Three styles + of paging are supported: + + TOPS10 known in the KS10 microcode as KI10 paging, + used by earlier versions of TOPS10 + TOPS20 known in the KS10 microcode as KL10 paging, + used by later versions of TOPS10, and TOPS20 + ITS used only by ITS + + TOPS10 vs TOPS20 is selected by a bit in the EBR; ITS paging is + "hardwired" (it required different microcode). +*/ + +#include "pdp10_defs.h" +#include + +/* Page table (contains expanded pte's) */ + +#define PTBL_ASIZE PAG_N_VPN +#define PTBL_MEMSIZE (1 << PTBL_ASIZE) /* page table size */ +#define PTBL_AMASK (PTBL_MEMSIZE - 1) +#define PTBL_M (1u << 31) /* must be sign bit */ +#define PTBL_V (1u << 30) +#define PTBL_MASK (PAG_PPN | PTBL_M | PTBL_V) + +/* NXM processing */ + +#define REF_V 0 /* ref is virt */ +#define REF_P 1 /* ref is phys */ +#define PF_OK 0 /* pfail ok */ +#define PF_TR 1 /* pfail trap */ + +extern d10 *M; +extern d10 acs[AC_NBLK * AC_NUM]; +extern d10 *ac_cur, *ac_prv, *last_pa; +extern a10 epta, upta; +extern int32 flags; +extern d10 pager_word; +extern int32 apr_flg; +extern d10 ebr, ubr, hsb; +extern d10 spt, cst, cstm, pur; +extern a10 dbr1, dbr2, dbr3, dbr4; +extern d10 pcst, quant; +extern t_bool paging; +extern UNIT cpu_unit; +extern jmp_buf save_env; +extern int32 test_int (void); +extern int32 pi_eval (void); + +int32 eptbl[PTBL_MEMSIZE]; /* exec page table */ +int32 uptbl[PTBL_MEMSIZE]; /* user page table */ +int32 physptbl[PTBL_MEMSIZE]; /* phys page table */ +int32 *ptbl_cur, *ptbl_prv; +int32 save_ea; + +int32 ptbl_fill (a10 ea, int32 *ptbl, int32 mode); +t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat pag_reset (DEVICE *dptr); +void pag_nxm (a10 pa, int32 phys, int32 trap); + +/* Pager data structures + + pag_dev pager device descriptor + pag_unit pager units + pager_reg pager register list +*/ + +UNIT pag_unit[] = { + { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) }, + { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) } }; + +REG pag_reg[] = { + { ORDATA (PANIC_EA, save_ea, PASIZE), REG_HRO }, + { NULL } }; + +DEVICE pag_dev = { + "PAG", pag_unit, pag_reg, NULL, + 2, 8, PTBL_ASIZE, 1, 8, 32, + &pag_ex, &pag_dep, &pag_reset, + NULL, NULL, NULL }; + +/* Memory read and write routines + + Read - read current or previous, read checking + ReadM - read current or previous, write checking + ReadE - read exec + ReadP - read physical + Write - write current or previous + WriteE - write exec + WriteP - write physical + AccChk - test accessibility of virtual address +*/ + +d10 Read (a10 ea, int32 prv) +{ +register int32 pa, vpn, xpte; + +if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ +if (xpte == 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_RD); +pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ +if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ +return M[pa]; /* return data */ +} + +d10 ReadM (a10 ea, int32 prv) +{ +register int32 pa, vpn, xpte; + +if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ +if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR); +pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ +if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ +return M[pa]; /* return data */ +} + +d10 ReadE (a10 ea) +{ +register int32 pa, vpn, xpte; + +if (ea < AC_NUM) return AC(ea); /* AC? use current */ +if (!PAGING) return M[ea]; /* phys? no mapping */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = eptbl[vpn]; /* get exp pte, exec tbl */ +if (xpte == 0) xpte = ptbl_fill (ea, eptbl, PTF_RD); +pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ +if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ +return M[pa]; /* return data */ +} + +d10 ReadP (a10 ea) +{ +if (ea < AC_NUM) return AC(ea); /* AC request */ +if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */ +return M[ea]; /* return data */ +} + +void Write (a10 ea, d10 val, int32 prv) +{ +register int32 pa, vpn, xpte; + +if (ea < AC_NUM) { /* AC request */ + if (prv) ac_prv[ea] = val; /* write AC */ + else ac_cur[ea] = val; } +else { vpn = PAG_GETVPN (ea); /* get page num */ + xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ + if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR); + pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ + if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ + else M[pa] = val; } /* write data */ +return; +} + +void WriteE (a10 ea, d10 val) +{ +register int32 pa, vpn, xpte; + +if (ea < AC_NUM) AC(ea) = val; /* AC? use current */ +else if (!PAGING) M[ea] = val; /* phys? no mapping */ +else { vpn = PAG_GETVPN (ea); /* get page num */ + xpte = eptbl[vpn]; /* get exp pte, exec tbl */ + if (xpte >= 0) xpte = ptbl_fill (ea, eptbl, PTF_WR); + pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ + if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ + else M[pa] = val; } /* write data */ +return; +} + +void WriteP (a10 ea, d10 val) +{ +if (ea < AC_NUM) AC(ea) = val; /* AC request */ +else { if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */ + M[ea] = val; } /* memory */ +return; +} + +t_bool AccViol (a10 ea, int32 prv, int32 mode) +{ +register int32 vpn, xpte; + +if (ea < AC_NUM) return FALSE; /* AC request */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ +if ((xpte == 0) || ((mode & PTF_WR) && (xpte > 0))) /* not accessible? */ + xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, mode | PTF_MAP); +if (xpte) return FALSE; /* accessible */ +return TRUE; /* not accessible */ +} + +void pag_nxm (a10 pa, int32 phys, int32 trap) +{ +apr_flg = apr_flg | APRF_NXM; /* set APR flag */ +pi_eval (); /* eval intr */ +pager_word = PF_NXM | (phys? PF_NXMP: 0) | + (TSTF (F_USR)? PF_USER: 0) | ((d10) pa); +if (PAGING && trap) ABORT (PAGE_FAIL); /* trap? */ +return; +} + +/* Page table fill + + This routine is called if the page table is invalid, or on a write + reference if the page table is read only. If the access is allowed + it stores the pte in the page table entry and returns an expanded + pte for use by the caller. Otherwise, it generates a page fail. + + Notes: + - If called from the console, invalid references return a pte + of 0, and the page table entry is not filled. + - If called from MAP, invalid references return a pte of 0. The + page fail word is properly set up. +*/ + +#define PAGE_FAIL_TRAP if (mode & (PTF_CON | PTF_MAP)) return 0; \ + ABORT (PAGE_FAIL) +#define READPT(x,y) if (MEM_ADDR_NXM (y)) { \ + pag_nxm (y, REF_P, PF_OK); \ + PAGE_FAIL_TRAP; } \ + x = ReadP (y) + +int32 ptbl_fill (a10 ea, int32 *tbl, int32 mode) +{ + +/* ITS paging is based on conventional page tables. ITS divides each address + space into a 128K high and low section, and uses different descriptor base + pointers (dbr) for each. ITS pages are twice the size of DEC standard; + therefore, the fill routine fills two page table entries and returns the pte + that maps the correct ITS half page. This allows the DEC paging macros to + be used in the normal path read-write routines. + + ITS has no MAP instruction, therefore, physical NXM traps are ok. +*/ + +if (ITS) { /* ITS paging */ + int32 acc, decvpn, pte, vpn, ptead, xpte; + d10 ptewd; + + vpn = ITS_GETVPN (ea); /* get ITS pagno */ + if (tbl == uptbl) + ptead = ((ea & RSIGN)? dbr2: dbr1) + ((vpn >> 1) & 077); + else ptead = ((ea & RSIGN)? dbr4: dbr3) + ((vpn >> 1) & 077); + ptewd = ReadP (ptead); /* get PTE pair */ + pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK); + acc = ITS_GETACC (pte); /* get access */ + pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | + ((mode & PTF_WR)? PF_ITS_WRITE: 0) | (acc << PF_ITS_V_ACC); + if ((acc != ITS_ACC_NO) && (!(mode & PTF_WR) || (acc == ITS_ACC_RW))) { + pte = pte & ~PTE_ITS_AGE; /* clear age */ + if (vpn & 1) WriteP (ptead, (ptewd & LMASK) | pte); + else WriteP (ptead, (ptewd & RMASK) | (((d10) pte) << 18)); + xpte = ((pte & PTE_ITS_PPMASK) << ITS_V_PN) | PTBL_V | + ((acc == ITS_ACC_RW)? PTBL_M: 0); + decvpn = PAG_GETVPN (ea); /* get tlb idx */ + if (!(mode & PTF_CON)) { + tbl[decvpn & ~1] = xpte; /* map lo ITS page */ + tbl[decvpn | 1] = xpte + PAG_SIZE; } /* map hi */ + return (xpte + ((decvpn & 1)? PAG_SIZE: 0)); } + PAGE_FAIL_TRAP; + } /* end ITS paging */ + +/* TOPS-10 paging - checked against KS10 microcode + + TOPS-10 paging is also based on conventional page tables. The user page + tables are arranged contiguously at the beginning of the user process table; + however, the executive page tables are scattered through the executive and + user process tables. +*/ + +else if (!T20) { /* TOPS-10 paging */ + int32 pte, vpn, ptead, xpte; + d10 ptewd; + + vpn = PAG_GETVPN (ea); /* get virt page num */ + if (tbl == uptbl) ptead = upta + UPT_T10_UMAP + (vpn >> 1); + else if (vpn < 0340) ptead = epta + EPT_T10_X000 + (vpn >> 1); + else if (vpn < 0400) ptead = upta + UPT_T10_X340 + ((vpn - 0340) >> 1); + else ptead = epta + EPT_T10_X400 + ((vpn - 0400) >> 1); + READPT (ptewd, ptead); /* get PTE pair */ + pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK); + pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | + ((mode & PTF_WR)? PF_WRITE: 0) | + ((pte & PTE_T10_A)? PF_T10_A | + ((pte & PTE_T10_S)? PF_T10_S: 0): 0); + if (mode & PTF_MAP) pager_word = pager_word | /* map? add to pf wd */ + ((pte & PTE_T10_W)? PF_T10_W: 0) | /* W, S, C bits */ + ((pte & PTE_T10_S)? PF_T10_S: 0) | + ((pte & PTE_T10_C)? PF_C: 0); + if ((pte & PTE_T10_A) && (!(mode & PTF_WR) || (pte & PTE_T10_W))) { + xpte = ((pte & PTE_PPMASK) << PAG_V_PN) | /* calc exp pte */ + PTBL_V | ((pte & PTE_T10_W)? PTBL_M: 0); + if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */ + return xpte; } + PAGE_FAIL_TRAP; + } /* end TOPS10 paging */ + +/* TOPS-20 paging - checked against KS10 ucode. + + TOPS-20 paging has three phases: + + 1. Starting at EPT/UPT + 540 + section number, chase section pointers to + get the pointer to the section page table. In the KS10, because there + is only one section, the microcode caches the result of this evaluation. + Also, the evaluation of indirect pointers is simplified, as the section + table index is ignored. + + 2. Starting with the page map pointer, chase page pointers to get the + pointer to the page. The KS10 allows the operating system to inhibit + updating of the CST (base address = 0). + + 3. Use the page pointer to get the CST entry. If a write reference to + a writeable page, set CST_M. If CST_M is set, set M in page table. +*/ + +else { /* TOPS-20 paging */ + int32 pmi, vpn, xpte; + int32 flg, t; + t_bool stop; + a10 pa, csta; + d10 ptr, cste; + d10 acc = PTE_T20_W | PTE_T20_C; /* init access bits */ + + pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | + ((mode & PTF_WR)? PF_WRITE: 0); /* set page fail word */ + +/* First phase - evaluate section pointers - returns a ptr to a page map + As a single section machine, the KS10 short circuits this part of the + process. In particular, the indirect pointer calculation assumes that + the section table index will be 0. It adds the full pointer (not just + the right half) to the SPT base. If the section index is > 0, the + result is a physical memory address > 256KW. Depending on the size of + memory, the SPT fetch may or may not generate a NXM page fail. The + KS10 then ignores the section table index in fetching the next pointer. + + The KS10 KL10 memory management diagnostic (dskec.sav) tests for this + behavior with a section index of 3. However, this would be a legal + physical address in a system with 1MW. Accordingly, the simulator + special cases non-zero section indices (which can't work in any case) + to generate the right behavior for the diagnostic. +*/ + + vpn = PAG_GETVPN (ea); /* get virt page num */ + pa = (tbl == uptbl)? upta + UPT_T20_SCTN: epta + EPT_T20_SCTN; + READPT (ptr, pa & PAMASK); /* get section 0 ptr */ + for (stop = FALSE, flg = 0; !stop; flg++) { /* eval section ptrs */ + acc = acc & ptr; /* cascade acc bits */ + switch (T20_GETTYP (ptr)) { /* case on ptr type */ + case T20_NOA: /* no access */ + default: /* undefined type */ + PAGE_FAIL_TRAP; /* page fail */ + case T20_IMM: /* immediate */ + stop = TRUE; /* exit */ + break; + case T20_SHR: /* shared */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + READPT (ptr, pa & PAMASK); /* get SPT entry */ + stop = TRUE; /* exit */ + break; + case T20_IND: /* indirect */ + if (flg && (t = test_int ())) ABORT (t); + pmi = T20_GETPMI (ptr); /* get sect tbl idx */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + if (pmi) { /* for dskec */ + pag_nxm ((pmi << 18) | pa, REF_P, PF_OK); + PAGE_FAIL_TRAP; } + READPT (ptr, pa & PAMASK); /* get SPT entry */ + if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } + pa = PAG_PTEPA (ptr, pmi); /* index off page */ + READPT (ptr, pa & PAMASK); /* get pointer */ + break; /* continue in loop */ + } /* end case */ + } /* end for */ + +/* Second phase - found page map ptr, evaluate page pointers */ + + pa = PAG_PTEPA (ptr, vpn); /* get ptbl address */ + for (stop = FALSE, flg = 0; !stop; flg++) { /* eval page ptrs */ + if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-res? */ + if (cst) { /* cst really there? */ + csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK); + READPT (cste, csta); /* get CST entry */ + if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; } + cste = (cste & cstm) | pur; /* update entry */ + WriteP (csta, cste); } /* rewrite */ + READPT (ptr, pa & PAMASK); /* get pointer */ + acc = acc & ptr; /* cascade acc bits */ + switch (T20_GETTYP (ptr)) { /* case on ptr type */ + case T20_NOA: /* no access */ + default: /* undefined type */ + PAGE_FAIL_TRAP; /* page fail */ + case T20_IMM: /* immediate */ + stop = TRUE; /* exit */ + break; + case T20_SHR: /* shared */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + READPT (ptr, pa & PAMASK); /* get SPT entry */ + stop = TRUE; /* exit */ + break; + case T20_IND: /* indirect */ + if (flg && (t = test_int ())) ABORT (t); + pmi = T20_GETPMI (ptr); /* get section index */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + READPT (ptr, pa & PAMASK); /* get SPT entry */ + pa = PAG_PTEPA (ptr, pmi); /* index off page */ + break; /* continue in loop */ + } /* end case */ + } /* end for */ + +/* Last phase - have final page pointer, check modifiability */ + + if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-resident? */ + if (cst) { /* CST really there? */ + csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK); + READPT (cste, csta); /* get CST entry */ + if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; } + cste = (cste & cstm) | pur; } /* update entry */ + else cste = 0; /* no, entry = 0 */ + pager_word = pager_word | PF_T20_DN; /* set eval done */ + xpte = ((int32) ((ptr & PTE_PPMASK) << PAG_V_PN)) | PTBL_V; + if (mode & PTF_WR) { /* write? */ + if (acc & PTE_T20_W) { /* writable? */ + xpte = xpte | PTBL_M; /* set PTE M */ + cste = cste | CST_M; } /* set CST M */ + else { PAGE_FAIL_TRAP; } } /* no, trap */ + if (cst) WriteP (csta, cste); /* write CST entry */ + if (mode & PTF_MAP) pager_word = pager_word | /* map? more in pf wd */ + ((xpte & PTBL_M)? PF_T20_M: 0) | /* M, W, C bits */ + ((acc & PTE_T20_W)? PF_T20_W: 0) | + ((acc & PTE_T20_C)? PF_C: 0); + if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */ + return xpte; + } /* end TOPS20 paging */ +} + +/* Set up pointers for AC, memory, and process table access */ + +void set_dyn_ptrs (void) +{ +int32 t; + +if (PAGING) { + ac_cur = &acs[UBR_GETCURAC (ubr) * AC_NUM]; + ac_prv = &acs[UBR_GETPRVAC (ubr) * AC_NUM]; + if (TSTF (F_USR)) ptbl_cur = ptbl_prv = &uptbl[0]; + else { ptbl_cur = &eptbl[0]; + ptbl_prv = TSTF (F_UIO)? &uptbl[0]: &eptbl[0]; } } +else { ac_cur = ac_prv = &acs[0]; + ptbl_cur = ptbl_prv = &physptbl[0]; } +t = EBR_GETEBR (ebr); +epta = t << PAG_V_PN; +if (ITS) upta = (int32) ubr & PAMASK; +else { t = UBR_GETUBR (ubr); + upta = t << PAG_V_PN; } +return; +} + +/* MAP instruction, TOPS-10 and TOPS-20 only + + According to the KS-10 ucode, map with paging disabled sets + "accessible, writeable, software", regardless of whether + TOPS-10 or TOPS-20 paging is implemented +*/ + +d10 map (a10 ea, int32 prv) +{ +int32 xpte; +d10 val = (TSTF (F_USR)? PF_USER: 0); + +if (!PAGING) return (val | PF_T10_A | PF_T10_W | PF_T10_S | ea); +xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_MAP); /* get exp pte */ +if (xpte) val = (pager_word & ~PAMASK) | PAG_XPTEPA (xpte, ea); +else { if (pager_word & PF_HARD) val = pager_word; /* hard error */ + else val = val | PF_VIRT | ea; } /* inaccessible */ +return val; +} + +/* Mapping routine for console */ + +a10 conmap (a10 ea, int32 mode, int32 sw) +{ +int32 xpte, *tbl; + +if (!PAGING) return ea; +set_dyn_ptrs (); /* in case changed */ +if (sw & SWMASK ('E')) tbl = eptbl; +else if (sw & SWMASK ('U')) tbl = uptbl; +else tbl = ptbl_cur; +xpte = ptbl_fill (ea, tbl, mode); +if (xpte) return PAG_XPTEPA (xpte, ea); +else return MAXMEMSIZE; +} + +/* Common pager instructions */ + +t_bool clrpt (a10 ea, int32 prv) +{ +int32 vpn = PAG_GETVPN (ea); /* get page num */ + +if (ITS) { /* ITS? */ + uptbl[vpn & ~1] = 0; /* clear double size */ + uptbl[vpn | 1] = 0; /* entries in */ + eptbl[vpn & ~1] = 0; /* both page tables */ + eptbl[vpn | 1] = 0; } +else { uptbl[vpn] = 0; /* clear entries in */ + eptbl[vpn] = 0; } /* both page tables */ +return FALSE; +} + +t_bool wrebr (a10 ea, int32 prv) +{ +ebr = ea & EBR_MASK; /* store EBR */ +pag_reset (&pag_dev); /* clear page tables */ +set_dyn_ptrs (); /* set dynamic ptrs */ +return FALSE; +} + +t_bool rdebr (a10 ea, int32 prv) +{ +Write (ea, (ebr & EBR_MASK), prv); +return FALSE; +} + +t_bool wrubr (a10 ea, int32 prv) +{ +d10 val = Read (ea, prv); +d10 ubr_mask = (ITS)? PAMASK: UBR_UBRMASK; /* ITS: ubr is wd addr */ + +if (val & UBR_SETACB) ubr = ubr & ~UBR_ACBMASK; /* set AC's? */ +else val = val & ~UBR_ACBMASK; /* no, keep old val */ +if (val & UBR_SETUBR) { /* set UBR? */ + ubr = ubr & ~ubr_mask; + pag_reset (&pag_dev); } /* yes, clr pg tbls */ +else val = val & ~ubr_mask; /* no, keep old val */ +ubr = (ubr | val) & (UBR_ACBMASK | ubr_mask); +set_dyn_ptrs (); +return FALSE; +} + +t_bool rdubr (a10 ea, int32 prv) +{ +ubr = ubr & (UBR_ACBMASK | (ITS? PAMASK: UBR_UBRMASK)); +Write (ea, UBRWORD, prv); +return FALSE; +} + +t_bool wrhsb (a10 ea, int32 prv) +{ +hsb = Read (ea, prv) & PAMASK; +return FALSE; +} + +t_bool rdhsb (a10 ea, int32 prv) +{ +Write (ea, hsb, prv); +return FALSE; +} + +/* TOPS20 pager instructions */ + +t_bool wrspb (a10 ea, int32 prv) +{ +spt = Read (ea, prv); +return FALSE; +} + +t_bool rdspb (a10 ea, int32 prv) +{ +Write (ea, spt, prv); +return FALSE; +} + +t_bool wrcsb (a10 ea, int32 prv) +{ +cst = Read (ea, prv); +return FALSE; +} + +t_bool rdcsb (a10 ea, int32 prv) +{ +Write (ea, cst, prv); +return FALSE; +} + +t_bool wrpur (a10 ea, int32 prv) +{ +pur = Read (ea, prv); +return FALSE; +} + +t_bool rdpur (a10 ea, int32 prv) +{ +Write (ea, pur, prv); +return FALSE; +} + +t_bool wrcstm (a10 ea, int32 prv) +{ +cstm = Read (ea, prv); +return FALSE; +} + +t_bool rdcstm (a10 ea, int32 prv) +{ +Write (ea, cstm, prv); +return FALSE; +} + +/* ITS pager instructions + The KS10 does not implement the JPC option. +*/ + +t_bool clrcsh (a10 ea, int32 prv) +{ +return FALSE; +} + +t_bool ldbr1 (a10 ea, int32 prv) +{ +dbr1 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr1 (a10 ea, int32 prv) +{ +Write (ea, dbr1, prv); +return FALSE; +} + +t_bool ldbr2 (a10 ea, int32 prv) +{ +dbr2 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr2 (a10 ea, int32 prv) +{ +Write (ea, dbr2, prv); +return FALSE; +} + +t_bool ldbr3 (a10 ea, int32 prv) +{ +dbr3 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr3 (a10 ea, int32 prv) +{ +Write (ea, dbr3, prv); +return FALSE; +} + +t_bool ldbr4 (a10 ea, int32 prv) +{ +dbr4 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr4 (a10 ea, int32 prv) +{ +Write (ea, dbr4, prv); +return FALSE; +} + +t_bool wrpcst (a10 ea, int32 prv) +{ +pcst = Read (ea, prv); +return FALSE; +} + +t_bool rdpcst (a10 ea, int32 prv) +{ +Write (ea, pcst, prv); +return FALSE; +} + +t_bool lpmr (a10 ea, int32 prv) +{ +d10 val; + +val = Read (ADDA (ea, 2), prv); +dbr1 = (a10) (Read (ea, prv) & AMASK); +dbr2 = (a10) (Read (ADDA (ea, 1), prv) & AMASK); +quant = val; +return FALSE; +} + +t_bool spm (a10 ea, int32 prv) +{ + +ReadM (ADDA (ea, 2), prv); +Write (ea, dbr1, prv); +Write (ADDA (ea, 1), dbr2, prv); +Write (ADDA (ea, 2), quant, prv); +return FALSE; +} + +t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 tbln = uptr - pag_unit; + +if (addr >= PTBL_MEMSIZE) return SCPE_NXM; +*vptr = tbln? uptbl[addr]: eptbl[addr];; +return SCPE_OK; +} + +t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 tbln = uptr - pag_unit; + +if (addr >= PTBL_MEMSIZE) return SCPE_NXM; +if (tbln) uptbl[addr] = (int32) val & PTBL_MASK; +else eptbl[addr] = (int32) val & PTBL_MASK; +return SCPE_OK; +} + +t_stat pag_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < PTBL_MEMSIZE; i++) { + eptbl[i] = uptbl[i] = 0; + physptbl[i] = (i << PAG_V_PN) + PTBL_M + PTBL_V; } +return SCPE_OK; +} diff --git a/pdp10_pt.c b/pdp10_pt.c new file mode 100644 index 00000000..69c998ba --- /dev/null +++ b/pdp10_pt.c @@ -0,0 +1,279 @@ +/* pdp10_pt.c: PDP-10 Unibus paper tape reader/punch simulator + + Copyright (c) 1993-2001, 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 "pdp10_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) + +extern int32 int_req, dev_enb; +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 */ +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_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); + +/* 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 (CSR, ptr_csr, 16) }, + { ORDATA (BUF, ptr_unit.buf, 8) }, + { 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) }, + { FLDATA (*DEVENB, dev_enb, INT_V_PTR), REG_HRO }, + { 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) }, + { FLDATA (*DEVENB, dev_enb, INT_V_PTR), REG_HRO }, + { 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 }; + +/* Standard I/O dispatch routine, I/O addresses 17777550-17777557 + + 17777550 ptr CSR + 17777552 ptr buffer + 17777554 ptp CSR + 17777556 ptp buffer + + Note: Word access routines filter out odd addresses. Thus, + an odd address implies an (odd) byte access. +*/ + +t_stat pt_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 03) { /* decode PA<2:1> */ +case 0: /* ptr csr */ + *data = ptr_csr & PTRCSR_IMP; + return SCPE_OK; +case 1: /* ptr buf */ + ptr_csr = ptr_csr & ~CSR_DONE; + int_req = int_req & ~INT_PTR; + *data = ptr_unit.buf & 0377; + return SCPE_OK; +case 2: /* ptp csr */ + *data = ptp_csr & PTPCSR_IMP; + return SCPE_OK; +case 3: /* ptp buf */ + *data = ptp_unit.buf; + return SCPE_OK; } +return SCPE_NXM; /* can't get here */ +} + +t_stat pt_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 03) { /* decode PA<2:1> */ +case 0: /* 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 1: /* ptr buf */ + return SCPE_OK; +case 2: /* 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 3: /* 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; } /* 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); +} diff --git a/pdp10_rp.c b/pdp10_rp.c new file mode 100644 index 00000000..b25613e7 --- /dev/null +++ b/pdp10_rp.c @@ -0,0 +1,1093 @@ +/* pdp10_rp.c - RH11/RP04/05/06/07 RM02/03/05/80 "Massbus" disk controller + + Copyright (c) 1993-2001, 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 RH/RP/RM moving head disks + + 14-May-01 RMS Added check for unattached drive + + The "Massbus style" disks consisted of several different large + capacity drives interfaced through a reasonably common (but not + 100% compatible) family of interfaces into the KS10 Unibus via + the RH11 disk controller. + + WARNING: This controller is somewhat abstract. It is intended to run + the operating system drivers for the PDP-10 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 "pdp10_defs.h" +#include + +#define RP_NUMDR 8 /* #drives */ +#define RP_NUMWD 128 /* 36b words/sector */ +#define RP_MAXFR 32768 /* max transfer */ +#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) +#define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) + +/* 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 */ +#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 | CS2_MXF | CS2_PE) +#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 */ + +/* This controller supports many different disk drive types. These drives + are operated in 576 bytes/sector (128 36b words/sector) mode, which gives + them somewhat different geometry from the PDP-11 variants: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + RM02/3 30 5 823 =67MB + RP04/5 20 19 411 =88MB + RM80 30 14 559 =124MB + RP06 20 19 815 =176MB + RM05 30 19 823 =256MB + RP07 43 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 30 +#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 20 +#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 30 +#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 20 +#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 30 +#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 43 +#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 d10 *M; /* memory */ +extern int32 int_req; +extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus maps */ +extern int32 ubcs[UBANUM]; +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 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_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); + +/* 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+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + (RP06_DTYPE << UNIT_V_DTYPE), RP06_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) }, + { 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_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, 36, + 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; +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 cs1f, drv, i, j; + +cs1f = 0; /* no int on cs1 upd */ +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) && (PA & 1)) data = data << 8; + else { if ((data & CS1_IE) == 0) int_req = int_req & ~INT_RP; + else if (data & CS1_DONE) int_req = int_req | INT_RP; } + if (data & CS1_TRE) { /* error clear? */ + rpcs1 = rpcs1 & ~CS1_TRE; /* clr CS1 */ + rpcs2 = rpcs2 & ~CS2_ERR; } /* clr CS2<15:8> */ + if (access == WRITEB) data = (rpcs1 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + rpcs1 = (rpcs1 & ~CS1_RW) | (data & CS1_RW); + 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) && (PA & 1)) data = data << 8; + if (data & CS2_CLR) rp_reset (&rp_dev); /* init? */ + else { if ((data & ~rpcs2) & (CS2_PE | CS2_MXF)) + cs1f = CS1_SC; /* diagn intr */ + if (access == WRITEB) data = (rpcs2 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + 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 ((access == WRITEB) && (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; + 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 (cs1f, 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) { /* not clear? */ + if ((rpds[drv] & DS_ERR) || /* error or */ + ((rpds[drv] & DS_RDY) == 0)) { /* not ready? */ + rpcs2 = rpcs2 | CS2_PGE; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return; } + if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */ + rper1[drv] = rper1[drv] | ER1_UNS; /* unsafe */ + 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, interrupt */ +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 +*/ + +t_stat rp_svc (UNIT *uptr) +{ +int32 i, dtype, drv, err; +int32 ba, da, vpn; +a10 pa10, mpa10; +int32 wc10, twc10, awc10, fc10; +static d10 dbuf[RP_MAXFR]; + +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); + break; +case FNC_RETURN: /* return to centerline */ + rpds[drv] = (rpds[drv] & ~DS_OF) | DS_ATA; /* clear offset, set attn */ + update_rpcs (CS1_SC, drv); + break; +case FNC_UNLOAD: /* unload */ + rp_detach (uptr); /* detach unit */ + break; +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); + break; + +/* Reads and writes must take into account the complicated relationship + between Unibus addresses and PDP-10 memory addresses, and Unibus + byte and word counts, PDP-10 UBA word counts, and simulator PDP-10 + word counts (due to the fact that the simulator must transfer eight + 8b bytes to do a 36b transfer, whereas the UBA did four 9b bytes). +*/ + +#define XWC_MBZ 0000001 /* wc<0> must be 0 */ +#define XBA_MBZ 0000003 /* addr<1:0> must be 0 */ + +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 */ + break; } +case FNC_WCHK: /* write check */ +case FNC_READ: /* read */ + ba = GET_UAE (rpcs1) | rpba; /* get byte addr */ + wc10 = (0200000 - rpwc) >> 1; /* get PDP-10 wc */ + da = GET_DA (rpdc, rpda, dtype) * RP_NUMWD; /* get disk addr */ + if ((da + wc10) > drv_tab[dtype].size) { /* disk overrun? */ + rper1[drv] = rper1[drv] | ER1_AOE; + if (wc10 > (drv_tab[dtype].size - da)) + wc10 = drv_tab[dtype].size - da; } + + err = fseek (uptr -> fileref, da * sizeof (d10), SEEK_SET); + if (uptr -> FUNC == FNC_WRITE) { /* write? */ + for (twc10 = 0; twc10 < wc10; twc10++) { + pa10 = ba >> 2; + vpn = PAG_GETVPN (pa10); /* map addr */ + if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || (rpwc & XWC_MBZ) || + ((ubmap[0][vpn] & (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != UMAP_VLD)) { + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; } + mpa10 = (ubmap[0][vpn] + PAG_GETOFF (pa10)) & PAMASK; + if (MEM_ADDR_NXM (mpa10)) { /* nx memory? */ + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; } + dbuf[twc10] = M[mpa10]; /* write to disk */ + if ((rpcs2 & CS2_UAI) == 0) ba = ba + 4; } + if (fc10 = twc10 & (RP_NUMWD - 1)) { /* fill? */ + fc10 = RP_NUMWD - fc10; + for (i = 0; i < fc10; i++) dbuf[twc10 + i] = 0; } + fxwrite (dbuf, sizeof (d10), twc10 + fc10, uptr -> fileref); + err = ferror (uptr -> fileref); + } /* end if */ + else { /* read, wchk */ + awc10 = fread (dbuf, sizeof (d10), wc10, uptr -> fileref); + err = ferror (uptr -> fileref); + for ( ; awc10 < wc10; awc10++) dbuf[awc10] = 0; + for (twc10 = 0; twc10 < wc10; twc10++) { + pa10 = ba >> 2; + vpn = PAG_GETVPN (pa10); /* map addr */ + if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || (rpwc & XWC_MBZ) || + ((ubmap[0][vpn] & (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != UMAP_VLD)) { + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; } + mpa10 = (ubmap[0][vpn] + PAG_GETOFF (pa10)) & PAMASK; + if (MEM_ADDR_NXM (mpa10)) { /* nx memory? */ + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; } + if (uptr -> FUNC == FNC_READ) M[mpa10] = dbuf[twc10]; + else if (M[mpa10] != dbuf[twc10]) { + rpcs2 = rpcs2 | CS2_WCE; /* set error */ + break; } + if ((rpcs2 & CS2_UAI) == 0) ba = ba + 4; } + } /* end else */ + + rpwc = (rpwc + (twc10 << 1)) & 0177777; /* final word count */ + rpba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */ + rpcs1 = (rpcs1 & ~ CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); + da = da + twc10 + (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 */ + break; } /* end case func */ +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 | DS_ATA; +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 */ +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; +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 (d10))) { + 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; +} + +/* Device bootstrap */ + +#define BOOT_START 0377000 /* start */ +#define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) + +static const d10 boot_rom_dec[] = { + 0515040000001, /* boot:hrlzi 1,1 ; uba # */ + 0201000140001, /* movei 0,140001 ; vld,fst,pg 1 */ + 0713001000000+IO_UBMAP+1, /* wrio 0,763001(1); set ubmap */ + 0435040000000+IO_RHBASE, /* iori 1,776700 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0713001000010, /* wrio 0,10(1) ; ->RPCS2 */ + 0201000000021, /* movei 0,21 ; preset */ + 0713001000000, /* wrio 0,0(1) ; ->RPCS1 */ + 0201100000001, /* movei 2,1 ; blk #1 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0306140505755, /* cain 3,sixbit /HOM/ */ + 0254000377023, /* jrst .+6 ; match */ + 0201100000010, /* movei 2,10 ; blk #10 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0302140505755, /* caie 3,sixbit /HOM/ */ + 0254200377022, /* halt . ; inv home */ + 0336100001103, /* skipn 2,1103 ; pg of ptrs */ + 0254200377024, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0336100001004, /* skipn 2,1004 ; mon boot */ + 0254200377027, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0254000001000, /* jrst 1000 ; start */ + 0201140176000, /* rdbl:movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_UNIT, /* move 5,FE_UNIT ; unit */ + 0200300000002, /* move 6,2 */ + 0242300777750, /* lsh 6,-24. ; cyl */ + 0713141000002, /* wrio 3,2(1) ; ->RPWC */ + 0713201000004, /* wrio 4,4(1) ; ->RPBA */ + 0713101000006, /* wrio 2,6(1) ; ->RPDA */ + 0713241000010, /* wrio 5,10(1) ; ->RPCS2 */ + 0713301000034, /* wrio 6,34(1) ; ->RPDC */ + 0201000000071, /* movei 0,71 ; read+go */ + 0713001000000, /* wrio 0,0(1) ; ->RPCS1 */ + 0712341000000, /* rdio 7,0(1) ; read csr */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377046, /* jrst .-2 ; loop */ + 0602340100000, /* trne 7,100000 ; test err */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ +}; + +static const d10 boot_rom_its[] = { + 0515040000001, /* boot:hrlzi 1,1 ; uba # */ + 0201000140001, /* movei 0,140001 ; vld,fst,pg 1 */ + 0715000000000+IO_UBMAP+1, /* iowrq 0,763001 ; set ubmap */ + 0435040000000+IO_RHBASE, /* iori 1,776700 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0715001000010, /* iowrq 0,10(1) ; ->RPCS2 */ + 0201000000021, /* movei 0,21 ; preset */ + 0715001000000, /* iowrq 0,0(1) ; ->RPCS1 */ + 0201100000001, /* movei 2,1 ; blk #1 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0306140505755, /* cain 3,sixbit /HOM/ */ + 0254000377023, /* jrst .+6 ; match */ + 0201100000010, /* movei 2,10 ; blk #10 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0302140505755, /* caie 3,sixbit /HOM/ */ + 0254200377022, /* halt . ; inv home */ + 0336100001103, /* skipn 2,1103 ; pg of ptrs */ + 0254200377024, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0336100001004, /* skipn 2,1004 ; mon boot */ + 0254200377027, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0254000001000, /* jrst 1000 ; start */ + 0201140176000, /* rdbl:movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_UNIT, /* move 5,FE_UNIT ; unit */ + 0200300000002, /* move 6,2 */ + 0242300777750, /* lsh 6,-24. ; cyl */ + 0715141000002, /* iowrq 3,2(1) ; ->RPWC */ + 0715201000004, /* iowrq 4,4(1) ; ->RPBA */ + 0715101000006, /* iowrq 2,6(1) ; ->RPDA */ + 0715241000010, /* iowrq 5,10(1) ; ->RPCS2 */ + 0715301000034, /* iowrq 6,34(1) ; ->RPDC */ + 0201000000071, /* movei 0,71 ; read+go */ + 0715001000000, /* iowrq 0,0(1) ; ->RPCS1 */ + 0711341000000, /* iordq 7,0(1) ; read csr */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377046, /* jrst .-2 ; loop */ + 0602340100000, /* trne 7,100000 ; test err */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ +}; + +t_stat rp_boot (int32 unitno) +{ +int32 i; +extern a10 saved_PC; + +M[FE_UNIT] = unitno & CS2_M_UNIT; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = ITS? boot_rom_its[i]: boot_rom_dec[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp10_sys.c b/pdp10_sys.c new file mode 100644 index 00000000..c2f0e75f --- /dev/null +++ b/pdp10_sys.c @@ -0,0 +1,784 @@ +/* pdp10_sys.c: PDP-10 simulator interface + + Copyright (c) 1993-2001, 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-Apr-01 RMS Fixed format for RDPCST, WRPCST + Added CLRCSH for ITS + 03-Apr-01 RMS Added support for loading EXE files + 19-Mar-01 RMS Added support for loading SAV files + 30-Oct-00 RMS Added support for examine to file +*/ + +#include "pdp10_defs.h" +#include + +extern DEVICE cpu_dev, pag_dev; +extern DEVICE tim_dev, fe_dev, uba_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE rp_dev, tu_dev; +/* extern DEVICE dz_dev; */ +extern DEVICE lp20_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern d10 *M; +extern a10 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-10"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &pag_dev, &tim_dev, + &fe_dev, &uba_dev, + &ptr_dev, &ptp_dev, + &lp20_dev, /* &dz_dev, */ + &rp_dev, &tu_dev, + NULL }; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Illegal instruction", + "Illegal interrupt instruction", + "Paging error in interrupt", + "Zero vector table", + "NXM on UPT/EPT reference", + "Nested indirect address limit exceeded", + "Nested XCT limit exceeded", + "Invalid I/O controller", + "Invalid magtape record length", + "Address stop", + "Panic stop" }; + +/* Binary loader, supports RIM10, SAV, EXE */ + +#define FMT_R 1 /* RIM10 */ +#define FMT_S 2 /* SAV */ +#define FMT_E 3 /* EXE */ + +#define EXE_DIR 01776 /* EXE directory */ +#define EXE_VEC 01775 /* EXE entry vec */ +#define EXE_PDV 01774 /* EXE ignored */ +#define EXE_END 01777 /* EXE end + +/* RIM10 loader + + RIM10 format is a binary paper tape format (all data frames + are 200 or greater). It consists of blocks containing + + -count,,origin-1 + word + : + word + checksum (includes IOWD) + : + JRST start +*/ + +d10 getrimw (FILE *fileref) +{ +int32 i, tmp; +d10 word; + +word = 0; +for (i = 0; i < 6;) { + if ((tmp = getc (fileref)) == EOF) return -1; + if (tmp & 0200) { + word = (word << 6) | ((d10) tmp & 077); + i++; } } +return word; +} + +t_stat load_rim (FILE *fileref) +{ +d10 count, cksm, data; +a10 pa; +int32 op; + +for ( ;; ) { /* loop until JRST */ + count = cksm = getrimw (fileref); /* get header */ + if (count < 0) return SCPE_FMT; /* read err? */ + if (TSTS (count)) { /* hdr = IOWD? */ + for ( ; TSTS (count); count = AOB (count)) { + data = getrimw (fileref); /* get data wd */ + if (data < 0) return SCPE_FMT; + cksm = cksm + data; /* add to cksm */ + pa = ((a10) count + 1) & AMASK; /* store */ + M[pa] = data; } /* end for */ + data = getrimw (fileref); /* get cksm */ + if (data < 0) return SCPE_FMT; + if ((cksm + data) & DMASK) return SCPE_CSUM; /* test cksm */ + } /* end if count */ + else { op = GET_OP (count); /* not IOWD */ + if (op != OP_JRST) return SCPE_FMT; /* JRST? */ + saved_PC = (a10) count & AMASK; /* set PC */ + return SCPE_OK; } /* end else */ + } /* end for */ +return SCPE_FMT; +} + +/* SAV file loader + + SAV format is a disk file format (36b words). It consists of + blocks containing: + + -count,,origin-1 + word + : + word + : + JRST start +*/ + +t_stat load_sav (FILE *fileref) +{ +d10 count, data; +a10 pa; +int32 wc, op; + +for ( ;; ) { /* loop */ + wc = fxread (&count, sizeof (d10), 1, fileref); /* read IOWD */ + if (wc == 0) return SCPE_OK; /* done? */ + if (TSTS (count)) { /* IOWD? */ + for ( ; TSTS (count); count = AOB (count)) { + wc = fxread (&data, sizeof (d10), 1, fileref); + if (wc == 0) return SCPE_FMT; + pa = ((a10) count + 1) & AMASK; /* store data */ + M[pa] = data; } /* end for */ + } /* end if count*/ + else { op = GET_OP (count); /* not IOWD */ + if (op != OP_JRST) return SCPE_FMT; /* JRST? */ + saved_PC = (a10) count & AMASK; /* set PC */ + return SCPE_OK; } /* end else */ + } /* end for */ +return SCPE_FMT; +} + +/* EXE file loader + + EXE format is a disk file format (36b words). It consists of + blocks containing: + + block type,,total words = n + n - 1 data words + + Block types are + + EXE_DIR (1776) directory + EXE_VEC (1775) entry vector + EXE_PDV (1774) optional blocks + EXE_END (1777) end block + + The directory blocks are the most important and contain doubleword + page loading information: + + word0<0:8> = flags + <9:35> = page in file (0 if 0 page) + word1<0:8> = repeat count - 1 + <9:35> = page in memory +*/ + +#define DIRSIZ (2 * PAG_SIZE) + +t_stat load_exe (FILE *fileref) +{ +d10 data, dirbuf[DIRSIZ], pagbuf[PAG_SIZE], entbuf[2]; +int32 ndir, entvec, i, j, k, cont, bsz, bty, rpt, wc; +int32 fpage, mpage; +a10 ma; + +ndir = entvec = 0; /* no dir, entvec */ +cont = 1; +do { wc = fxread (&data, sizeof (d10), 1, fileref); /* read blk hdr */ + if (wc == 0) return SCPE_FMT; /* error? */ + bsz = (int32) ((data & RMASK) - 1); /* get count */ + if (bsz <= 0) return SCPE_FMT; /* zero? */ + bty = (int32) LRZ (data); /* get type */ + switch (bty) { /* case type */ + case EXE_DIR: /* directory */ + if (ndir) return SCPE_FMT; /* got one */ + ndir = fxread (dirbuf, sizeof (d10), bsz, fileref); + if (ndir < bsz) return SCPE_FMT; /* error */ + break; + case EXE_PDV: /* ??? */ + fseek (fileref, bsz * sizeof (d10), SEEK_CUR); + break; + case EXE_VEC: /* entry vec */ + if (bsz != 2) return SCPE_FMT; /* must be 2 wds */ + entvec = fxread (entbuf, sizeof (d10), bsz, fileref); + if (entvec < 2) return SCPE_FMT; /* error? */ + cont = 0; /* stop */ + break; + case EXE_END: /* end */ + if (bsz != 0) return SCPE_FMT; /* must be hdr */ + cont = 0; /* stop */ + break; + default: + return SCPE_FMT; } /* end switch */ + } /* end do */ +while (cont); + +for (i = 0; i < ndir; i = i + 2) { /* loop thru dir */ + fpage = (int32) (dirbuf[i] & RMASK); /* file page */ + mpage = (int32) (dirbuf[i + 1] & RMASK); /* memory page */ + rpt = (int32) ((dirbuf[i + 1] >> 27) + 1); /* repeat count */ + for (j = 0; j < rpt; j++, mpage++) { /* loop thru rpts */ + if (fpage) { /* file pages? */ + fseek (fileref, (fpage << PAG_V_PN) * sizeof (d10), SEEK_SET); + wc = fxread (pagbuf, sizeof (d10), PAG_SIZE, fileref); + if (wc < PAG_SIZE) return SCPE_FMT; + fpage++; } + ma = mpage << PAG_V_PN; /* mem addr */ + for (k = 0; k < PAG_SIZE; k++, ma++) { /* copy buf to mem */ + if (MEM_ADDR_NXM (ma)) return SCPE_NXM; + M[ma] = fpage? (pagbuf[k] & DMASK): 0; + } /* end copy */ + } /* end rpt */ + } /* end directory */ +if (entvec && entbuf[1]) + saved_PC = (int32) entbuf[1] & RMASK; /* start addr */ +return SCPE_OK; +} + +/* Master loader */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +d10 data; +int32 wc, fmt; +extern int32 sim_switches; +extern t_bool match_ext (char *fnam, char *ext); + +fmt = 0; /* no fmt */ +if (sim_switches & SWMASK ('R')) fmt = FMT_R; /* -r? */ +else if (sim_switches & SWMASK ('S')) fmt = FMT_S; /* -s? */ +else if (sim_switches & SWMASK ('E')) fmt = FMT_E; /* -e? */ +else if (match_ext (fnam, "RIM")) fmt = FMT_R; /* .RIM? */ +else if (match_ext (fnam, "SAV")) fmt = FMT_S; /* .SAV? */ +else if (match_ext (fnam, "EXE")) fmt = FMT_E; /* .EXE? */ +else { wc = fxread (&data, sizeof (d10), 1, fileref); /* read hdr */ + if (wc == 0) return SCPE_FMT; /* error? */ + if (LRZ (data) == EXE_DIR) fmt = FMT_E; /* EXE magic? */ + else if (TSTS (data)) fmt = FMT_S; /* SAV magic? */ + fseek (fileref, 0, SEEK_SET); } /* rewind */ +switch (fmt) { /* case fmt */ +case FMT_R: /* RIM */ + return load_rim (fileref); +case FMT_S: /* SAV */ + return load_sav (fileref); +case FMT_E: /* EXE */ + return load_exe (fileref); } +printf ("Can't determine load file format\n"); +return SCPE_FMT; +} + +/* Symbol tables */ + +#define I_V_FL 39 /* inst class */ +#define I_M_FL 03 /* class mask */ +#define I_ITS 004000000000000 /* ITS flag */ +#define I_AC 000000000000000 /* AC, address */ +#define I_OP 010000000000000 /* address only */ +#define I_IO 020000000000000 /* classic I/O */ +#define I_V_AC 00 +#define I_V_OP 01 +#define I_V_IO 02 + +static const d10 masks[] = { + 0777000000000, 0777740000000, + 0700340000000, 0777777777777 }; + +static const char *opcode[] = { +"XCTR", "XCTI", /* ITS only */ +"IORDI", "IORDQ", "IORD", "IOWR", "IOWRI", "IOWRQ", +"IORDBI", "IORDBQ", "IORDB", "IOWRB", "IOWRBI", "IOWRBQ", +"CLRCSH", "RDPCST", "WRPCST", +"SDBR1", "SDBR2", "SDBR3", "SDBR4", "SPM", +"LDBR1", "LDBR2", "LDBR3", "LDBR4", "LPMR", + +"PORTAL", "JRSTF", "HALT", /* AC defines op */ +"XJRSTF", "XJEN", "XPCW", +"JEN", "SFM", "XJRST", "IBP", +"JFOV", "JCRY1", "JCRY0", "JCRY", "JOV", + +"APRID", "WRAPR", "RDAPR", "WRPI", "RDPI", "RDUBR", "CLRPT", "WRUBR", +"WREBR", "RDEBR", +"RDSPB", "RDCSB", "RDPUR", "RDCSTM", "RDTIM", "RDINT", "RDHSB", +"WRSPB", "WRCSB", "WRPUR", "WRCSTM", "WRTIM", "WRINT", "WRHSB", + + "LUUO01", "LUUO02", "LUUO03", "LUUO04", "LUUO05", "LUUO06", "LUUO07", +"LUUO10", "LUUO11", "LUUO12", "LUUO13", "LUUO14", "LUUO15", "LUUO16", "LUUO17", +"LUUO20", "LUUO21", "LUUO22", "LUUO23", "LUUO24", "LUUO25", "LUUO26", "LUUO27", +"LUUO30", "LUUO31", "LUUO32", "LUUO33", "LUUO34", "LUUO35", "LUUO36", "LUUO37", +"MUUO40", "MUUO41", "MUUO42", "MUUO43", "MUUO44", "MUUO45", "MUUO46", "MUUO47", +"MUUO50", "MUUO51", "MUUO52", "MUUO53", "MUUO54", "MUUO55", "MUUO56", "MUUO57", +"MUUO60", "MUUO61", "MUUO62", "MUUO63", "MUUO64", "MUUO65", "MUUO66", "MUUO67", +"MUUO70", "MUUO71", "MUUO72", "MUUO73", "MUUO74", "MUUO75", "MUUO76", "MUUO77", + +"UJEN", "GFAD", "GFSB", "JSYS", "ADJSP", "GFMP", "GFDV ", +"DFAD", "DFSB", "DFMP", "DFDV", "DADD", "DSUB", "DMUL", "DDIV", +"DMOVE", "DMOVN", "FIX", "EXTEND", "DMOVEM", "DMOVNM", "FIXR", "FLTR", +"UFA", "DFN", "FSC", "ADJBP", "ILDB", "LDB", "IDPB", "DPB", +"FAD", "FADL", "FADM", "FADB", "FADR", "FADRL", "FADRM", "FADRB", +"FSB", "FSBL", "FSBM", "FSBB", "FSBR", "FSBRL", "FSBRM", "FSBRB", +"FMP", "FMPL", "FMPM", "FMPB", "FMPR", "FMPRL", "FMPRM", "FMPRB", +"FDV", "FDVL", "FDVM", "FDVB", "FDVR", "FDVRL", "FDVRM", "FDVRB", + +"MOVE", "MOVEI", "MOVEM", "MOVES", "MOVS", "MOVSI", "MOVSM", "MOVSS", +"MOVN", "MOVNI", "MOVNM", "MOVNS", "MOVM", "MOVMI", "MOVMM", "MOVMS", +"IMUL", "IMULI", "IMULM", "IMULB", "MUL", "MULI", "MULM", "MULB", +"IDIV", "IDIVI", "IDIVM", "IDIVB", "DIV", "DIVI", "DIVM", "DIVB", +"ASH", "ROT", "LSH", "JFFO", "ASHC", "ROTC", "LSHC", "CIRC", +"EXCH", "BLT", "AOBJP", "AOBJN", "JRST", "JFCL", "XCT", "MAP", +"PUSHJ", "PUSH", "POP", "POPJ", "JSR", "JSP", "JSA", "JRA", +"ADD", "ADDI", "ADDM", "ADDB", "SUB", "SUBI", "SUBM", "SUBB", + +"CAI", "CAIL", "CAIE", "CAILE", "CAIA", "CAIGE", "CAIN", "CAIG", +"CAM", "CAML", "CAME", "CAMLE", "CAMA", "CAMGE", "CAMN", "CAMG", +"JUMP", "JUMPL", "JUMPE", "JUMPLE", "JUMPA", "JUMPGE", "JUMPN", "JUMPG", +"SKIP", "SKIPL", "SKIPE", "SKIPLE", "SKIPA", "SKIPGE", "SKIPN", "SKIPG", +"AOJ", "AOJL", "AOJE", "AOJLE", "AOJA", "AOJGE", "AOJN", "AOJG", +"AOS", "AOSL", "AOSE", "AOSLE", "AOSA", "AOSGE", "AOSN", "AOSG", +"SOJ", "SOJL", "SOJE", "SOJLE", "SOJA", "SOJGE", "SOJN", "SOJG", +"SOS", "SOSL", "SOSE", "SOSLE", "SOSA", "SOSGE", "SOSN", "SOSG", + +"SETZ", "SETZI", "SETZM", "SETZB", "AND", "ANDI", "ANDM", "ANDB", +"ANDCA", "ANDCAI", "ANDCAM", "ANDCAB", "SETM", "SETMI", "SETMM", "SETMB", +"ANDCM", "ANDCMI", "ANDCMM", "ANDCMB", "SETA", "SETAI", "SETAM", "SETAB", +"XOR", "XORI", "XORM", "XORB", "IOR", "IORI", "IORM", "IORB", +"ANDCB", "ANDCBI", "ANDCBM", "ANDCBB", "EQV", "EQVI", "EQVM", "EQVB", +"SETCA", "SETCAI", "SETCAM", "SETCAB", "ORCA", "ORCAI", "ORCAM", "ORCAB", +"SETCM", "SETCMI", "SETCMM", "SETCMB", "ORCM", "ORCMI", "ORCMM", "ORCMB", +"ORCB", "ORCBI", "ORCBM", "ORCBB", "SETO", "SETOI", "SETOM", "SETOB", + +"HLL", "HLLI", "HLLM", "HLLS", "HRL", "HRLI", "HRLM", "HRLS", +"HLLZ", "HLLZI", "HLLZM", "HLLZS", "HRLZ", "HRLZI", "HRLZM", "HRLZS", +"HLLO", "HLLOI", "HLLOM", "HLLOS", "HRLO", "HRLOI", "HRLOM", "HRLOS", +"HLLE", "HLLEI", "HLLEM", "HLLES", "HRLE", "HRLEI", "HRLEM", "HRLES", +"HRR", "HRRI", "HRRM", "HRRS", "HLR", "HLRI", "HLRM", "HLRS", +"HRRZ", "HRRZI", "HRRZM", "HRRZS", "HLRZ", "HLRZI", "HLRZM", "HLRZS", +"HRRO", "HRROI", "HRROM", "HRROS", "HLRO", "HLROI", "HLROM", "HLROS", +"HRRE", "HRREI", "HRREM", "HRRES", "HLRE", "HLREI", "HLREM", "HLRES", + +"TRN", "TLN", "TRNE", "TLNE", "TRNA", "TLNA", "TRNN", "TLNN", +"TDN", "TSN", "TDNE", "TSNE", "TDNA", "TSNA", "TDNN", "TSNN", +"TRZ", "TLZ", "TRZE", "TLZE", "TRZA", "TLZA", "TRZN", "TLZN", +"TDZ", "TSZ", "TDZE", "TSZE", "TDZA", "TSZA", "TDZN", "TSZN", +"TRC", "TLC", "TRCE", "TLCE", "TRCA", "TLCA", "TRCN", "TLCN", +"TDC", "TSC", "TDCE", "TSCE", "TDCA", "TSCA", "TDCN", "TSCN", +"TRO", "TLO", "TROE", "TLOE", "TROA", "TLOA", "TRON", "TLON", +"TDO", "TSO", "TDOE", "TSOE", "TDOA", "TSOA", "TDON", "TSON", + +"UMOVE", "UMOVEM", /* KS10 I/O */ +"TIOE", "TION", "RDIO", "WRIO", +"BSIO", "BCIO", "BLTBU", "BLTUB", +"TIOEB", "TIONB", "RDIOB", "WRIOB", +"BSIOB", "BCIOB", + +"BLKI", "DATAI", "BLKO", "DATAO", /* classic I/O */ +"CONO", "CONI", "CONSZ", "CONSO", + +"CLEAR", "CLEARI", "CLEARM", "CLEARB", +"OR", "ORI", "ORM", "ORB", "XMOVEI", "XHLLI", /* alternate ops */ + + "CMPSL", "CMPSE", "CMPSLE", /* extended ops */ +"EDIT", "CMPSGE", "CMPSN", "CMPSG", +"CVTDBO", "CVTDBT", "CVTBDO", "CVTBDT", +"MOVSO", "MOVST", "MOVSLJ", "MOVSRJ", +"XBLT", "GSNGL", "GDBLE", "GDFIX", +"GFIX", "GDFIXR", "GFIXR", "DGFLTR", +"GFLTR", "GFSC", + +NULL }; + +static const d10 opc_val[] = { + 0102000000000+I_AC+I_ITS, 0103000000000+I_AC+I_ITS, + 0710000000000+I_AC+I_ITS, 0711000000000+I_AC+I_ITS, 0712000000000+I_AC+I_ITS, + 0713000000000+I_AC+I_ITS, 0714000000000+I_AC+I_ITS, 0715000000000+I_AC+I_ITS, + 0720000000000+I_AC+I_ITS, 0721000000000+I_AC+I_ITS, 0722000000000+I_AC+I_ITS, + 0723000000000+I_AC+I_ITS, 0724000000000+I_AC+I_ITS, 0725000000000+I_AC+I_ITS, + 0701000000000+I_OP+I_ITS, 0701440000000+I_OP+I_ITS, 0701540000000+I_OP+I_ITS, + 0702000000000+I_OP+I_ITS, 0702040000000+I_OP+I_ITS, + 0702100000000+I_OP+I_ITS, 0702140000000+I_OP+I_ITS, 0702340000000+I_OP+I_ITS, + 0702400000000+I_OP+I_ITS, 0702440000000+I_OP+I_ITS, + 0702500000000+I_OP+I_ITS, 0702540000000+I_OP+I_ITS, 0702740000000+I_OP+I_ITS, + + 0254040000000+I_OP, 0254100000000+I_OP, + 0254200000000+I_OP, 0254240000000+I_OP, 0254300000000+I_OP, 0254340000000+I_OP, + 0254500000000+I_OP, 0254600000000+I_OP, 0254640000000+I_OP, 0133000000000+I_OP, + 0255040000000+I_OP, 0255100000000+I_OP, 0255200000000+I_OP, 0255300000000+I_OP, + 0255400000000+I_OP, + + 0700000000000+I_OP, 0700200000000+I_OP, 0700240000000+I_OP, 0700600000000+I_OP, + 0700640000000+I_OP, 0701040000000+I_OP, 0701100000000+I_OP, 0701140000000+I_OP, + 0701200000000+I_OP, 0701240000000+I_OP, + 0702000000000+I_OP, 0702040000000+I_OP, 0702100000000+I_OP, 0702140000000+I_OP, + 0702200000000+I_OP, 0702240000000+I_OP, 0702300000000+I_OP, + 0702400000000+I_OP, 0702440000000+I_OP, 0702500000000+I_OP, 0702540000000+I_OP, + 0702600000000+I_OP, 0702640000000+I_OP, 0702700000000+I_OP, + + 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, + 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, + 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, + 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, + 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, + 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, + 0030000000000+I_AC, 0031000000000+I_AC, 0032000000000+I_AC, 0033000000000+I_AC, + 0034000000000+I_AC, 0035000000000+I_AC, 0036000000000+I_AC, 0037000000000+I_AC, + 0040000000000+I_AC, 0041000000000+I_AC, 0042000000000+I_AC, 0043000000000+I_AC, + 0044000000000+I_AC, 0045000000000+I_AC, 0046000000000+I_AC, 0047000000000+I_AC, + 0050000000000+I_AC, 0051000000000+I_AC, 0052000000000+I_AC, 0053000000000+I_AC, + 0054000000000+I_AC, 0055000000000+I_AC, 0056000000000+I_AC, 0057000000000+I_AC, + 0060000000000+I_AC, 0061000000000+I_AC, 0062000000000+I_AC, 0063000000000+I_AC, + 0064000000000+I_AC, 0065000000000+I_AC, 0066000000000+I_AC, 0067000000000+I_AC, + 0070000000000+I_AC, 0071000000000+I_AC, 0072000000000+I_AC, 0073000000000+I_AC, + 0074000000000+I_AC, 0075000000000+I_AC, 0076000000000+I_AC, 0077000000000+I_AC, + + 0100000000000+I_AC, 0102000000000+I_AC, 0103000000000+I_AC, + 0104000000000+I_AC, 0105000000000+I_AC, 0106000000000+I_AC, 0107000000000+I_AC, + 0110000000000+I_AC, 0111000000000+I_AC, 0112000000000+I_AC, 0113000000000+I_AC, + 0114000000000+I_AC, 0115000000000+I_AC, 0116000000000+I_AC, 0117000000000+I_AC, + 0120000000000+I_AC, 0121000000000+I_AC, 0122000000000+I_AC, 0123000000000+I_AC, + 0124000000000+I_AC, 0125000000000+I_AC, 0126000000000+I_AC, 0127000000000+I_AC, + 0130000000000+I_AC, 0131000000000+I_AC, 0132000000000+I_AC, 0133000000000+I_AC, + 0134000000000+I_AC, 0135000000000+I_AC, 0136000000000+I_AC, 0137000000000+I_AC, + 0140000000000+I_AC, 0141000000000+I_AC, 0142000000000+I_AC, 0143000000000+I_AC, + 0144000000000+I_AC, 0145000000000+I_AC, 0146000000000+I_AC, 0147000000000+I_AC, + 0150000000000+I_AC, 0151000000000+I_AC, 0152000000000+I_AC, 0153000000000+I_AC, + 0154000000000+I_AC, 0155000000000+I_AC, 0156000000000+I_AC, 0157000000000+I_AC, + 0160000000000+I_AC, 0161000000000+I_AC, 0162000000000+I_AC, 0163000000000+I_AC, + 0164000000000+I_AC, 0165000000000+I_AC, 0166000000000+I_AC, 0167000000000+I_AC, + 0170000000000+I_AC, 0171000000000+I_AC, 0172000000000+I_AC, 0173000000000+I_AC, + 0174000000000+I_AC, 0175000000000+I_AC, 0176000000000+I_AC, 0177000000000+I_AC, + + 0200000000000+I_AC, 0201000000000+I_AC, 0202000000000+I_AC, 0203000000000+I_AC, + 0204000000000+I_AC, 0205000000000+I_AC, 0206000000000+I_AC, 0207000000000+I_AC, + 0210000000000+I_AC, 0211000000000+I_AC, 0212000000000+I_AC, 0213000000000+I_AC, + 0214000000000+I_AC, 0215000000000+I_AC, 0216000000000+I_AC, 0217000000000+I_AC, + 0220000000000+I_AC, 0221000000000+I_AC, 0222000000000+I_AC, 0223000000000+I_AC, + 0224000000000+I_AC, 0225000000000+I_AC, 0226000000000+I_AC, 0227000000000+I_AC, + 0230000000000+I_AC, 0231000000000+I_AC, 0232000000000+I_AC, 0233000000000+I_AC, + 0234000000000+I_AC, 0235000000000+I_AC, 0236000000000+I_AC, 0237000000000+I_AC, + 0240000000000+I_AC, 0241000000000+I_AC, 0242000000000+I_AC, 0243000000000+I_AC, + 0244000000000+I_AC, 0245000000000+I_AC, 0246000000000+I_AC, 0247000000000+I_AC+I_ITS, + 0250000000000+I_AC, 0251000000000+I_AC, 0252000000000+I_AC, 0253000000000+I_AC, + 0254000000000+I_AC, 0255000000000+I_AC, 0256000000000+I_AC, 0257000000000+I_AC, + 0260000000000+I_AC, 0261000000000+I_AC, 0262000000000+I_AC, 0263000000000+I_AC, + 0264000000000+I_AC, 0265000000000+I_AC, 0266000000000+I_AC, 0267000000000+I_AC, + 0270000000000+I_AC, 0271000000000+I_AC, 0272000000000+I_AC, 0273000000000+I_AC, + 0274000000000+I_AC, 0275000000000+I_AC, 0276000000000+I_AC, 0277000000000+I_AC, + + 0300000000000+I_AC, 0301000000000+I_AC, 0302000000000+I_AC, 0303000000000+I_AC, + 0304000000000+I_AC, 0305000000000+I_AC, 0306000000000+I_AC, 0307000000000+I_AC, + 0310000000000+I_AC, 0311000000000+I_AC, 0312000000000+I_AC, 0313000000000+I_AC, + 0314000000000+I_AC, 0315000000000+I_AC, 0316000000000+I_AC, 0317000000000+I_AC, + 0320000000000+I_AC, 0321000000000+I_AC, 0322000000000+I_AC, 0323000000000+I_AC, + 0324000000000+I_AC, 0325000000000+I_AC, 0326000000000+I_AC, 0327000000000+I_AC, + 0330000000000+I_AC, 0331000000000+I_AC, 0332000000000+I_AC, 0333000000000+I_AC, + 0334000000000+I_AC, 0335000000000+I_AC, 0336000000000+I_AC, 0337000000000+I_AC, + 0340000000000+I_AC, 0341000000000+I_AC, 0342000000000+I_AC, 0343000000000+I_AC, + 0344000000000+I_AC, 0345000000000+I_AC, 0346000000000+I_AC, 0347000000000+I_AC, + 0350000000000+I_AC, 0351000000000+I_AC, 0352000000000+I_AC, 0353000000000+I_AC, + 0354000000000+I_AC, 0355000000000+I_AC, 0356000000000+I_AC, 0357000000000+I_AC, + 0360000000000+I_AC, 0361000000000+I_AC, 0362000000000+I_AC, 0363000000000+I_AC, + 0364000000000+I_AC, 0365000000000+I_AC, 0366000000000+I_AC, 0367000000000+I_AC, + 0370000000000+I_AC, 0371000000000+I_AC, 0372000000000+I_AC, 0373000000000+I_AC, + 0374000000000+I_AC, 0375000000000+I_AC, 0376000000000+I_AC, 0377000000000+I_AC, + + 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, + 0404000000000+I_AC, 0405000000000+I_AC, 0406000000000+I_AC, 0407000000000+I_AC, + 0410000000000+I_AC, 0411000000000+I_AC, 0412000000000+I_AC, 0413000000000+I_AC, + 0414000000000+I_AC, 0415000000000+I_AC, 0416000000000+I_AC, 0417000000000+I_AC, + 0420000000000+I_AC, 0421000000000+I_AC, 0422000000000+I_AC, 0423000000000+I_AC, + 0424000000000+I_AC, 0425000000000+I_AC, 0426000000000+I_AC, 0427000000000+I_AC, + 0430000000000+I_AC, 0431000000000+I_AC, 0432000000000+I_AC, 0433000000000+I_AC, + 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, + 0440000000000+I_AC, 0441000000000+I_AC, 0442000000000+I_AC, 0443000000000+I_AC, + 0444000000000+I_AC, 0445000000000+I_AC, 0446000000000+I_AC, 0447000000000+I_AC, + 0450000000000+I_AC, 0451000000000+I_AC, 0452000000000+I_AC, 0453000000000+I_AC, + 0454000000000+I_AC, 0455000000000+I_AC, 0456000000000+I_AC, 0457000000000+I_AC, + 0460000000000+I_AC, 0461000000000+I_AC, 0462000000000+I_AC, 0463000000000+I_AC, + 0464000000000+I_AC, 0465000000000+I_AC, 0466000000000+I_AC, 0467000000000+I_AC, + 0470000000000+I_AC, 0471000000000+I_AC, 0472000000000+I_AC, 0473000000000+I_AC, + 0474000000000+I_AC, 0475000000000+I_AC, 0476000000000+I_AC, 0477000000000+I_AC, + + 0500000000000+I_AC, 0501000000000+I_AC, 0502000000000+I_AC, 0503000000000+I_AC, + 0504000000000+I_AC, 0505000000000+I_AC, 0506000000000+I_AC, 0507000000000+I_AC, + 0510000000000+I_AC, 0511000000000+I_AC, 0512000000000+I_AC, 0513000000000+I_AC, + 0514000000000+I_AC, 0515000000000+I_AC, 0516000000000+I_AC, 0517000000000+I_AC, + 0520000000000+I_AC, 0521000000000+I_AC, 0522000000000+I_AC, 0523000000000+I_AC, + 0524000000000+I_AC, 0525000000000+I_AC, 0526000000000+I_AC, 0527000000000+I_AC, + 0530000000000+I_AC, 0531000000000+I_AC, 0532000000000+I_AC, 0533000000000+I_AC, + 0534000000000+I_AC, 0535000000000+I_AC, 0536000000000+I_AC, 0537000000000+I_AC, + 0540000000000+I_AC, 0541000000000+I_AC, 0542000000000+I_AC, 0543000000000+I_AC, + 0544000000000+I_AC, 0545000000000+I_AC, 0546000000000+I_AC, 0547000000000+I_AC, + 0550000000000+I_AC, 0551000000000+I_AC, 0552000000000+I_AC, 0553000000000+I_AC, + 0554000000000+I_AC, 0555000000000+I_AC, 0556000000000+I_AC, 0557000000000+I_AC, + 0560000000000+I_AC, 0561000000000+I_AC, 0562000000000+I_AC, 0563000000000+I_AC, + 0564000000000+I_AC, 0565000000000+I_AC, 0566000000000+I_AC, 0567000000000+I_AC, + 0570000000000+I_AC, 0571000000000+I_AC, 0572000000000+I_AC, 0573000000000+I_AC, + 0574000000000+I_AC, 0575000000000+I_AC, 0576000000000+I_AC, 0577000000000+I_AC, + + 0600000000000+I_AC, 0601000000000+I_AC, 0602000000000+I_AC, 0603000000000+I_AC, + 0604000000000+I_AC, 0605000000000+I_AC, 0606000000000+I_AC, 0607000000000+I_AC, + 0610000000000+I_AC, 0611000000000+I_AC, 0612000000000+I_AC, 0613000000000+I_AC, + 0614000000000+I_AC, 0615000000000+I_AC, 0616000000000+I_AC, 0617000000000+I_AC, + 0620000000000+I_AC, 0621000000000+I_AC, 0622000000000+I_AC, 0623000000000+I_AC, + 0624000000000+I_AC, 0625000000000+I_AC, 0626000000000+I_AC, 0627000000000+I_AC, + 0630000000000+I_AC, 0631000000000+I_AC, 0632000000000+I_AC, 0633000000000+I_AC, + 0634000000000+I_AC, 0635000000000+I_AC, 0636000000000+I_AC, 0637000000000+I_AC, + 0640000000000+I_AC, 0641000000000+I_AC, 0642000000000+I_AC, 0643000000000+I_AC, + 0644000000000+I_AC, 0645000000000+I_AC, 0646000000000+I_AC, 0647000000000+I_AC, + 0650000000000+I_AC, 0651000000000+I_AC, 0652000000000+I_AC, 0653000000000+I_AC, + 0654000000000+I_AC, 0655000000000+I_AC, 0656000000000+I_AC, 0657000000000+I_AC, + 0660000000000+I_AC, 0661000000000+I_AC, 0662000000000+I_AC, 0663000000000+I_AC, + 0664000000000+I_AC, 0665000000000+I_AC, 0666000000000+I_AC, 0667000000000+I_AC, + 0670000000000+I_AC, 0671000000000+I_AC, 0672000000000+I_AC, 0673000000000+I_AC, + 0674000000000+I_AC, 0675000000000+I_AC, 0676000000000+I_AC, 0677000000000+I_AC, + + 0704000000000+I_AC, 0705000000000+I_AC, + 0710000000000+I_AC, 0711000000000+I_AC, 0712000000000+I_AC, 0713000000000+I_AC, + 0714000000000+I_AC, 0715000000000+I_AC, 0716000000000+I_AC, 0717000000000+I_AC, + 0720000000000+I_AC, 0721000000000+I_AC, 0722000000000+I_AC, 0723000000000+I_AC, + 0724000000000+I_AC, 0725000000000+I_AC, + + 0700000000000+I_IO, 0700040000000+I_IO, 0700100000000+I_IO, 0700140000000+I_IO, + 0700200000000+I_IO, 0700240000000+I_IO, 0700300000000+I_IO, 0700340000000+I_IO, + + 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, + 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, + 0415000000000+I_AC, 0501000000000+I_AC, + + 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, + 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, + 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, + 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, + 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, + 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, + 0030000000000+I_AC, 0031000000000+I_AC, +-1 }; + +#define NUMDEV 6 + +static const char *devnam[NUMDEV] = { + "APR", "PI", "PAG", "CCA", "TIM", "MTR"}; + + +/* 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) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, j, c, cflag, ac, xr, y, dev; +d10 inst; + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC ((int32) (inst & 0177))); + return SCPE_OK; } +if (sw & SWMASK ('C')) { /* character? */ + for (i = 30; i >= 0; i = i - 6) { + c = (int32) ((inst >> i) & 077); + fprintf (of, "%c", SIXTOASC (c)); } + return SCPE_OK; } +if (sw & SWMASK ('P')) { /* packed? */ + for (i = 29; i >= 0; i = i - 7) { + c = (int32) ((inst >> i) & 0177); + fprintf (of, FMTASC (c)); } + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +ac = GET_AC (inst); +xr = GET_XR (inst); +y = GET_ADDR (inst); +dev = GET_DEV (inst); +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ + if (((opc_val[i] & DMASK) == (inst & masks[j])) && /* match? */ + (((opc_val[i] & I_ITS) == 0) || ITS)) { + fprintf (of, "%s ", opcode[i]); /* opcode */ + switch (j) { /* case on class */ + case I_V_AC: /* AC + address */ + fprintf (of, "%-o,", ac); /* print AC, fall thru */ + case I_V_OP: /* address only */ + if (inst & INST_IND) fprintf (of, "@"); + if (xr) fprintf (of, "%-o(%-o)", y, xr); + else fprintf (of, "%-o", y); + break; + case I_V_IO: /* I/O */ + if (dev < NUMDEV) fprintf (of, "%s,", devnam[dev]); + else fprintf (of, "%-o,", dev); + if (inst & INST_IND) fprintf (of, "@"); + if (xr) fprintf (of, "%-o(%-o)", y, xr); + else fprintf (of, "%-o", y); + break; } /* end case */ + return SCPE_OK; } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get operand, including indirect and index + + Inputs: + *cptr = pointer to input string + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_opnd (char *cptr, t_stat *status) +{ +int32 sign = 0; +t_value val, xr = 0, ind = 0; +char *tptr; + +*status = SCPE_ARG; /* assume fail */ +if (*cptr == '@') { + ind = INST_IND; + cptr++; } +if (*cptr == '+') cptr++; +else if (*cptr == '-') { + sign = 1; + cptr++; } +val = strtotv (cptr, &tptr, 8); +if (val > 0777777) return 0; +if (sign) val = (~val + 1) & 0777777; +cptr = tptr; +if (*cptr == '(') { + cptr++; + xr = strtotv (cptr, &tptr, 8); + if ((cptr == tptr) || (*tptr != ')') || + (xr > AC_NUM) || (xr == 0)) return 0; + cptr = ++tptr; } +if (*cptr == 0) *status = SCPE_OK; +return (ind | (xr << 18) | val); +} + +/* 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, i, j; +t_value ac, dev; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 0; i < 6; i++) { + if (cptr[i] == 0) { + for (j = i + 1; j <= 6; j++) cptr[j] = 0; + break; } } +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 */ + for (i = 0; i < 6; i++) { + val[0] = (val[0] << 6); + if (cptr[i]) val[0] = val[0] | + ((t_value) ((cptr[i] + 040) & 077)); } + return SCPE_OK; } +if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + for (i = 0; i < 5; i++) val[0] = (val[0] << 7) | ((t_value) cptr[i]); + val[0] = val[0] << 1; + 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] & DMASK; /* get value */ +j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ +switch (j) { /* case on class */ +case I_V_AC: /* AC + operand */ + if (strchr (cptr, ',')) { /* AC specified? */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if (gbuf[0]) { /* can be omitted */ + ac = get_uint (gbuf, 8, AC_NUM - 1, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (ac << INST_V_AC); } } +case I_V_OP: /* operand */ + cptr = get_glyph (cptr, gbuf, 0); + val[0] = val[0] | get_opnd (gbuf, &r); + if (r != SCPE_OK) return SCPE_ARG; + break; +case I_V_IO: /* I/O */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + for (dev = 0; (dev < NUMDEV) && (strcmp (devnam[dev], gbuf) != 0); dev++); + if (dev >= NUMDEV) { + dev = get_uint (gbuf, 8, INST_M_DEV, &r); + if (r != SCPE_OK) return SCPE_ARG; } + val[0] = val[0] | (dev << INST_V_DEV); + cptr = get_glyph (cptr, gbuf, 0); + val[0] = val[0] | get_opnd (gbuf, &r); + if (r != SCPE_OK) return SCPE_ARG; + break; } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/pdp10_tim.c b/pdp10_tim.c new file mode 100644 index 00000000..25a8f149 --- /dev/null +++ b/pdp10_tim.c @@ -0,0 +1,178 @@ +/* pdp10_tim.c: PDP-10 tim subsystem simulator + + Copyright (c) 1993-2001, 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 "pdp10_defs.h" +#include + +#define TIM_N_HWRE 12 /* hwre bits */ +#define TIM_HWRE 0000000010000 /* hwre incr */ +#define TB_MASK 037777777777777777777; /* 71 - 12 bits */ +#define TPS 1001 /* ticks per sec */ +#define UNIT_V_Y2K (UNIT_V_UF) /* Y2K compliant OS */ +#define UNIT_Y2K (1u << UNIT_V_Y2K) + +extern int32 apr_flg, pi_act; +extern UNIT cpu_unit; +extern d10 pcst; +extern a10 pager_PC; +int64 timebase = 0; /* 71b timebase */ +d10 ttg = 0; /* time to go */ +d10 period = 0; /* period */ +d10 quant = 0; /* ITS quantum */ +int32 diagflg = 0; /* diagnostics? */ + +t_stat tim_svc (UNIT *uptr); +t_stat tim_reset (DEVICE *dptr); +extern d10 Read (a10 ea, int32 prv); +extern d10 ReadM (a10 ea, int32 prv); +extern void Write (a10 ea, d10 val, int32 prv); +extern void WriteP (a10 ea, d10 val); +extern int32 pi_eval (void); +extern sim_rtc_calb (int32 tps); + +/* TIM data structures + + tim_dev TIM device descriptor + tim_unit TIM unit descriptor + tim_reg TIM register list +*/ + +UNIT tim_unit = { UDATA (&tim_svc, 0, 0), 500 }; + +REG tim_reg[] = { + { ORDATA (TIMEBASE, timebase, 71 - TIM_N_HWRE) }, + { ORDATA (TTG, ttg, 36) }, + { ORDATA (PERIOD, period, 36) }, + { ORDATA (QUANT, quant, 36) }, + { DRDATA (TIME, tim_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (DIAG, diagflg, 0) }, + { FLDATA (Y2K, tim_unit.flags, UNIT_V_Y2K), REG_HRO }, + { NULL } }; + +MTAB tim_mod[] = { + { UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL }, + { UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL }, + { 0 } }; + +DEVICE tim_dev = { + "TIM", &tim_unit, tim_reg, tim_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &tim_reset, + NULL, NULL, NULL }; + +/* Timer instructions */ + +t_bool rdtim (a10 ea, int32 prv) +{ +ReadM (INCA (ea), prv); +Write (ea, (timebase >> (35 - TIM_N_HWRE)) & DMASK, prv); +Write (INCA(ea), (timebase << TIM_N_HWRE) & MMASK, prv); +return FALSE; +} + +t_bool wrtim (a10 ea, int32 prv) +{ +timebase = (Read (ea, prv) << (35 - TIM_N_HWRE)) | + (CLRS (Read (INCA (ea), prv)) >> TIM_N_HWRE); +return FALSE; +} + +t_bool rdint (a10 ea, int32 prv) +{ +Write (ea, period, prv); +return FALSE; +} + +t_bool wrint (a10 ea, int32 prv) +{ +period = Read (ea, prv); +ttg = period; +return FALSE; +} + +/* Timer routines + + tim_svc process event (timer tick) + tim_reset process reset +*/ + +t_stat tim_svc (UNIT *uptr) +{ +sim_activate (&tim_unit, /* reactivate unit */ + (diagflg? tim_unit.wait: sim_rtc_calb (TPS))); +timebase = (timebase + 1) & TB_MASK; /* increment timebase */ +ttg = ttg - TIM_HWRE; /* decrement timer */ +if (ttg <= 0) { /* timeout? */ + ttg = period; /* reload */ + apr_flg = apr_flg | APRF_TIM; } /* request interrupt */ +if (ITS) { /* ITS? */ + if (pi_act == 0) quant = (quant + TIM_HWRE) & DMASK; + if (TSTS (pcst)) { /* PC sampling? */ + pcst = AOB (pcst); /* add 1,,1 */ + WriteP ((a10) pcst & AMASK, pager_PC); } + } /* end ITS */ +return SCPE_OK; +} + +t_stat tim_reset (DEVICE *dptr) +{ +period = ttg = 0; /* clear timer */ +apr_flg = apr_flg & ~APRF_TIM; /* clear interrupt */ +sim_activate (&tim_unit, tim_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Time of year clock */ + +t_stat tcu_rd (int32 *data, int32 PA, int32 access) +{ +time_t curtim; +struct tm *tptr; + +curtim = time (NULL); /* get time */ +tptr = localtime (&curtim); /* decompose */ +if (tptr == NULL) return SCPE_NXM; /* Y2K prob? */ +if ((tptr -> tm_year > 99) && !(tim_unit.flags & UNIT_Y2K)) + tptr -> tm_year = 99; + +switch ((PA >> 1) & 03) { /* decode PA<3:1> */ +case 0: /* year/month/day */ + *data = (((tptr -> tm_year) & 0177) << 9) | + (((tptr -> tm_mon + 1) & 017) << 5) | + ((tptr -> tm_mday) & 037); + return SCPE_OK; +case 1: /* hour/minute */ + *data = (((tptr -> tm_hour) & 037) << 8) | + ((tptr -> tm_min) & 077); + return SCPE_OK; +case 2: /* second */ + *data = (tptr -> tm_sec) & 077; + return SCPE_OK; +case 3: /* status */ + *data = CSR_DONE; + return SCPE_OK; } +return SCPE_NXM; /* can't get here */ +} diff --git a/pdp10_tu.c b/pdp10_tu.c new file mode 100644 index 00000000..2ea54a5a --- /dev/null +++ b/pdp10_tu.c @@ -0,0 +1,1045 @@ +/* pdp10_tu.c - PDP-10 RH11/TM03/TU45 magnetic tape simulator + + Copyright (c) 1993-2001, 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. + + tu RH11/TM03/TU45 magtape + + 4-May-01 RMS Fixed bug in odd address test + 3-May-01 RMS Fixed drive reset to clear SSC + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number, sign = error + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number, sign = error + + 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 "pdp10_defs.h" + +#define TU_NUMFM 1 /* #formatters */ +#define TU_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 UDENS u4 /* unit density */ +#define UD_UNK 0 /* unknown */ +#define XBUFLNT (1 << 16) /* max data buf */ + +/* MTCS1 - 172440 - 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_REWIND 003 /* rewind */ +#define FNC_FCLR 004 /* formatter clear */ +#define FNC_RIP 010 /* read in preset */ +#define FNC_ERASE 012 /* erase tape */ +#define FNC_WREOF 013 /* write tape mark */ +#define FNC_SPACEF 014 /* space forward */ +#define FNC_SPACER 015 /* space reverse */ +#define FNC_WCHKF 024 /* write check */ +#define FNC_WCHKR 027 /* write check rev */ +#define FNC_WRITE 030 /* write */ +#define FNC_READF 034 /* read forward */ +#define FNC_READR 037 /* read reverse */ +#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 | CS1_GO) +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) +#define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) + +/* MTWC - 172442 - word count */ + +/* MTBA - 172444 - base address */ + +#define BA_MBZ 0000001 /* must be zero */ + +/* MTFC - 172446 - frame count */ + +/* MTCS2 - 172450 - control/status 2 */ + +#define CS2_V_FMTR 0 /* formatter select */ +#define CS2_M_FMTR 07 +#define CS2_FMTR (CS2_M_FMTR << CS2_V_FMTR) +#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_NEF 0010000 /* nx fmter err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write chk err NI */ +#define CS2_DLT 0100000 /* data late NI */ +#define CS2_MBZ (CS2_CLR | CS2_WCE) +#define CS2_RW (CS2_FMTR | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) +#define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ + CS2_NEF | CS2_PE | CS2_DLT ) +#define GET_FMTR(x) (((x) >> CS2_V_FMTR) & CS2_M_FMTR) + +/* MTFS - 172452 - formatter status + + indicates kept in drive status + ^ indicates calculated on the fly +*/ + +#define FS_SAT 0000001 /* slave attention */ +#define FS_BOT 0000002 /* ^beginning of tape */ +#define FS_TMK 0000004 /* end of file */ +#define FS_ID 0000010 /* ID burst detected */ +#define FS_SLOW 0000020 /* slowing down NI */ +#define FS_PE 0000040 /* ^PE status */ +#define FS_SSC 0000100 /* slave stat change */ +#define FS_RDY 0000200 /* ^formatter ready */ +#define FS_FPR 0000400 /* formatter present */ +#define FS_EOT 0002000 /* +end of tape */ +#define FS_WRL 0004000 /* ^write locked */ +#define FS_MOL 0010000 /* ^medium online */ +#define FS_PIP 0020000 /* +pos in progress */ +#define FS_ERR 0040000 /* ^error */ +#define FS_ATA 0100000 /* attention active */ +#define FS_REW 0200000 /* +rewinding */ + +#define FS_DYN (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \ + FS_RDY | FS_PE | FS_BOT) + +/* MTER - 172454 - error register */ + +#define ER_ILF 0000001 /* illegal func */ +#define ER_ILR 0000002 /* illegal register */ +#define ER_RMR 0000004 /* reg mod refused */ +#define ER_MCP 0000010 /* Mbus cpar err NI */ +#define ER_FER 0000020 /* format sel err */ +#define ER_MDP 0000040 /* Mbus dpar err NI */ +#define ER_VPE 0000100 /* vert parity err NI */ +#define ER_CRC 0000200 /* CRC err NI */ +#define ER_NSG 0000400 /* non std gap err NI */ +#define ER_FCE 0001000 /* frame count err */ +#define ER_ITM 0002000 /* inv tape mark NI */ +#define ER_NXF 0004000 /* wlock or fnc err */ +#define ER_DTE 0010000 /* time err NI */ +#define ER_OPI 0020000 /* op incomplete */ +#define ER_UNS 0040000 /* drive unsafe */ +#define ER_DCK 0100000 /* data check NI */ + +/* MTAS - 172456 - attention summary */ + +#define AS_U0 0000001 /* unit 0 flag */ + +/* MTCC - 172460 - check character, read only */ + +#define CC_MBZ 0177000 /* must be zero */ + +/* MTDB - 172462 - data buffer */ + +/* MTMR - 172464 - maintenance register */ + +#define MR_RW 0177637 /* read/write */ + +/* MTDT - 172466 - drive type */ + +#define DT_TAPE 0040000 /* tape */ +#define DT_PRES 0002000 /* slave present */ +#define DT_TM03 0000040 /* TM03 formatter */ +#define DT_OFF 0000010 /* drive off */ +#define DT_TE16 0000011 /* TE16 */ +#define DT_TU45 0000012 /* TU45 */ +#define DT_TU77 0000014 /* TU77 */ + +/* MTSN - 172470 - serial number */ + +/* MTTC - 172472 - tape control register */ + +#define TC_V_UNIT 0 /* unit select */ +#define TC_M_UNIT 07 +#define TC_V_EVN 0000010 /* even parity */ +#define TC_V_FMT 4 /* format select */ +#define TC_M_FMT 017 +#define TC_10C 00 /* PDP-10 core dump */ +#define TC_IND 03 /* industry standard */ +#define TC_V_DEN 8 /* density select */ +#define TC_M_DEN 07 +#define TC_800 3 /* 800 bpi */ +#define TC_1600 4 /* 1600 bpi */ +#define TC_AER 0010000 /* abort on error */ +#define TC_SAC 0020000 /* slave addr change */ +#define TC_FCS 0040000 /* frame count status */ +#define TC_ACC 0100000 /* accelerating NI */ +#define TC_RW 0013777 +#define TC_MBZ 0004000 +#define GET_DEN(x) (((x) >> TC_V_DEN) & TC_M_DEN) +#define GET_FMT(x) (((x) >> TC_V_FMT) & TC_M_FMT) +#define GET_DRV(x) (((x) >> TC_V_UNIT) & TC_M_UNIT) + +/* Mapping macros */ + +#define XWC_MBZ 0000001 /* wc<0> mbz */ +#define XBA_MBZ 0000001 /* addr<0> mbz */ +#define XBA_ODD 0000002 /* odd address */ +#define TXFR(b,w,od) if (((b) & XBA_MBZ) || ((w) & XWC_MBZ) || \ + (((b) & XBA_ODD) != ((od) << 1))) { \ + tucs2 = tucs2 | CS2_NEM; \ + ubcs[1] = ubcs[1] | UBCS_TMO; \ + update_tucs (CS1_DONE, drv); \ + return SCPE_OK; } +#define NEWPAGE(v,m) (((v) & PAG_M_OFF) == (m)) +#define MAPM(v,p,f) vpn = PAG_GETVPN (v); \ + if ((vpn >= UMAP_MEMSIZE) || ((ubmap[1][vpn] & \ + (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != \ + (UMAP_VLD | f))) { \ + tucs2 = tucs2 | CS2_NEM; \ + ubcs[1] = ubcs[1] | UBCS_TMO; \ + break; } \ + p = (ubmap[1][vpn] + PAG_GETOFF (v)) & PAMASK; \ + if (MEM_ADDR_NXM (p)) { \ + tucs2 = tucs2 | CS2_NEM; \ + ubcs[1] = ubcs[1] | UBCS_TMO; \ + break; } \ + + +extern d10 *M; /* memory */ +extern int32 int_req; +extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus map */ +extern int32 ubcs[UBANUM]; +extern UNIT cpu_unit; +int32 tucs1 = 0; /* control/status 1 */ +int32 tuwc = 0; /* word count */ +int32 tuba = 0; /* bus address */ +int32 tufc = 0; /* frame count */ +int32 tucs2 = 0; /* control/status 2 */ +int32 tufs = 0; /* formatter status */ +int32 tuer = 0; /* error status */ +int32 tucc = 0; /* check character */ +int32 tudb = 0; /* data buffer */ +int32 tumr = 0; /* maint register */ +int32 tutc = 0; /* tape control */ +int32 tu_time = 10; /* record latency */ +int32 tu_stopioe = 1; /* stop on error */ +int32 tu_log = 0; /* debug */ +int reg_in_fmtr[32] = { /* reg in formatter */ + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +int reg_in_fmtr1[32] = { /* rmr if write + go */ + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; +int fmt_test[16] = { /* fmt bytes/10 wd */ + 5, 0, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +int den_test[8] = { /* valid densities */ + 0, 0, 0, 1, 1, 0, 0, 0 }; + +t_stat tu_svc (UNIT *uptr); +t_stat tu_reset (DEVICE *dptr); +t_stat tu_attach (UNIT *uptr, char *cptr); +t_stat tu_detach (UNIT *uptr); +t_stat tu_boot (int32 unitno); +void tu_go (int32 drv); +void update_tucs (int32 flag, int32 drv); +t_stat tu_vlock (UNIT *uptr, int32 val); + +/* TU data structures + + tu_dev TU device descriptor + tu_unit TU unit list + tu_reg TU register list + tu_mod TU modifier list +*/ + +UNIT tu_unit[] = { + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } }; + +REG tu_reg[] = { + { ORDATA (MTCS1, tucs1, 16) }, + { ORDATA (MTWC, tuwc, 16) }, + { ORDATA (MTBA, tuba, 16) }, + { ORDATA (MTFC, tufc, 16) }, + { ORDATA (MTCS2, tucs2, 16) }, + { ORDATA (MTFS, tufs, 16) }, + { ORDATA (MTER, tuer, 16) }, + { ORDATA (MTCC, tucc, 16) }, + { ORDATA (MTDB, tudb, 16) }, + { ORDATA (MTMR, tumr, 16) }, + { ORDATA (MTTC, tutc, 16) }, + { FLDATA (INT, int_req, INT_V_TU) }, + { FLDATA (DONE, tucs1, CSR_V_DONE) }, + { FLDATA (IE, tucs1, CSR_V_IE) }, + { FLDATA (STOP_IOE, tu_stopioe, 0) }, + { DRDATA (TIME, tu_time, 24), PV_LEFT }, + { ORDATA (UST0, tu_unit[0].USTAT, 17) }, + { ORDATA (UST1, tu_unit[1].USTAT, 17) }, + { ORDATA (UST2, tu_unit[2].USTAT, 17) }, + { ORDATA (UST3, tu_unit[3].USTAT, 17) }, + { ORDATA (UST4, tu_unit[4].USTAT, 17) }, + { ORDATA (UST5, tu_unit[5].USTAT, 17) }, + { ORDATA (UST6, tu_unit[6].USTAT, 17) }, + { ORDATA (UST7, tu_unit[7].USTAT, 17) }, + { DRDATA (POS0, tu_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, tu_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, tu_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, tu_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, tu_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, tu_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, tu_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, tu_unit[7].pos, 31), PV_LEFT + REG_RO }, + { GRDATA (FLG0, tu_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, tu_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, tu_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, tu_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, tu_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, tu_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, tu_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, tu_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { ORDATA (LOG, tu_log, 8), REG_HIDDEN }, + { NULL } }; + +MTAB tu_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", &tu_vlock }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &tu_vlock }, + { 0 } }; + +DEVICE tu_dev = { + "TU", tu_unit, tu_reg, tu_mod, + TU_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &tu_reset, + &tu_boot, &tu_attach, &tu_detach }; + +/* I/O dispatch routine, I/O addresses 17772440 - 17772472 */ + +t_stat tu_rd (int32 *data, int32 PA, int32 access) +{ +int32 fmtr, drv, j; + +fmtr = GET_FMTR (tucs2); /* get current fmtr */ +drv = GET_DRV (tutc); /* get current drive */ +j = (PA >> 1) & 017; /* get reg offset */ +if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ + tucs2 = tucs2 | CS2_NEF; /* set error flag */ + update_tucs (CS1_SC, drv); /* request intr */ + *data = 0; + return SCPE_OK; } + +update_tucs (0, drv); /* update status */ +switch (j) { /* decode PA<4:1> */ +case 000: /* MTCS1 */ + *data = tucs1; + break; +case 001: /* MTWC */ + *data = tuwc; + break; +case 002: /* MTBA */ + *data = tuba = tuba & ~BA_MBZ; + break; +case 003: /* MTFC */ + *data = tufc; + break; +case 004: /* MTCS2 */ + *data = tucs2 = (tucs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; + break; +case 005: /* MTFS */ + *data = tufs & 0177777; /* mask off rewind */ + break; +case 006: /* MTER */ + *data = tuer; + break; +case 007: /* MTAS */ + *data = (tufs & FS_ATA)? AS_U0: 0; + break; +case 010: /* MTCC */ + *data = tucc = tucc & ~CC_MBZ; + break; +case 011: /* MTDB */ + *data = tudb; + break; +case 012: /* MTMR */ + *data = tumr; + break; +case 013: /* MTDT */ + *data = DT_TAPE | DT_TM03 | ((tu_unit[drv].flags & UNIT_DIS)? + DT_OFF: (DT_PRES | DT_TU45)); + break; +case 014: /* MTSN */ + *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1); + break; +case 015: /* MTTC */ + *data = tutc = tutc & ~TC_MBZ; + break; +default: /* all others */ + tuer = tuer | ER_ILR; + update_tucs (0, drv); + break; } +return SCPE_OK; +} + +t_stat tu_wr (int32 data, int32 PA, int32 access) +{ +int32 cs1f, fmtr, drv, j; + +cs1f = 0; /* no int on cs1 upd */ +fmtr = GET_FMTR (tucs2); /* get formatter */ +drv = GET_DRV (tutc); /* get current unit */ +j = (PA >> 1) & 017; /* get reg offset */ +if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ + tucs2 = tucs2 | CS2_NEF; /* set error flag */ + update_tucs (CS1_SC, drv); /* request intr */ + return SCPE_OK; } +if (reg_in_fmtr1[j] && ((tucs1 & CS1_DONE) == 0)) { /* formatter busy? */ + tuer = tuer | ER_RMR; /* won't write */ + update_tucs (0, drv); + return SCPE_OK; } + +switch (j) { /* decode PA<4:1> */ +case 000: /* MTCS1 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + else { if ((data & CS1_IE) == 0) int_req = int_req & ~INT_TU; + else if (data & CS1_DONE) int_req = int_req | INT_TU; } + if (data & CS1_TRE) { /* error clear? */ + tucs1 = tucs1 & ~CS1_TRE; /* clr CS1 */ + tucs2 = tucs2 & ~CS2_ERR; } /* clr CS2<15:8> */ + if (access == WRITEB) data = (tucs1 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + tucs1 = (tucs1 & ~CS1_RW) | (data & CS1_RW); + if (data & CS1_GO) { /* new command? */ + if (fmtr != 0) { /* nx formatter? */ + tucs2 = tucs2 | CS2_NEF; /* set error flag */ + update_tucs (CS1_SC, drv); /* request intr */ + return SCPE_OK; } + if (tucs1 & CS1_DONE) tu_go (drv); /* start if not busy */ + else tucs2 = tucs2 | CS2_PGE; } /* else prog error */ + break; +case 001: /* MTWC */ + if (access == WRITEB) data = (PA & 1)? + (tuwc & 0377) | (data << 8): (tuwc & ~0377) | data; + tuwc = data; + break; +case 002: /* MTBA */ + if (access == WRITEB) data = (PA & 1)? + (tuba & 0377) | (data << 8): (tuba & ~0377) | data; + tuba = data & ~BA_MBZ; + break; +case 003: /* MTFC */ + if (access == WRITEB) data = (PA & 1)? + (tufc & 0377) | (data << 8): (tufc & ~0377) | data; + tufc = data; + tutc = tutc | TC_FCS; /* set fc flag */ + break; +case 004: /* MTCS2 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + if (data & CS2_CLR) tu_reset (&tu_dev); /* init? */ + else { if ((data & ~tucs2) & (CS2_PE | CS2_MXF)) + cs1f = CS1_SC; /* diagn intr */ + if (access == WRITEB) data = (tucs2 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + tucs2 = (tucs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; } + break; +case 007: /* MTAS */ + if ((access == WRITEB) && (PA & 1)) break; + if (data & AS_U0) tufs = tufs & ~FS_ATA; + break; +case 011: /* MTDB */ + if (access == WRITEB) data = (PA & 1)? + (tudb & 0377) | (data << 8): (tudb & ~0377) | data; + tudb = data; + break; +case 012: /* MTMR */ + if (access == WRITEB) data = (PA & 1)? + (tumr & 0377) | (data << 8): (tumr & ~0377) | data; + tumr = (tumr & ~MR_RW) | (data & MR_RW); + break; +case 015: /* MTTC */ + if (access == WRITEB) data = (PA & 1)? + (tutc & 0377) | (data << 8): (tutc & ~0377) | data; + tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC; + drv = GET_DRV (tutc); + break; +case 005: /* MTFS */ +case 006: /* MTER */ +case 010: /* MTCC */ +case 013: /* MTDT */ +case 014: /* MTSN */ + break; /* read only */ +default: /* all others */ + tuer = tuer | ER_ILR; + break; } /* end switch */ +update_tucs (cs1f, drv); /* update status */ +return SCPE_OK; +} + +/* New magtape command */ + +void tu_go (int32 drv) +{ +int32 fnc, den, space_test = FS_BOT; +UNIT *uptr; + +fnc = GET_FNC (tucs1); /* get function */ +den = GET_DEN (tutc); /* get density */ +uptr = tu_dev.units + drv; /* get unit */ +if ((fnc != FNC_FCLR) && /* not clear & err */ + ((tufs & FS_ERR) || sim_is_active (uptr))) { /* or in motion? */ + tucs2 = tucs2 | CS2_PGE; /* set error flag */ + update_tucs (CS1_SC, drv); /* request intr */ + return; } +tufs = tufs & ~FS_ATA; /* clear attention */ +tutc = tutc & ~TC_SAC; /* clear addr change */ + +switch (fnc) { /* case on function */ +case FNC_FCLR: /* drive clear */ + tuer = 0; /* clear errors */ + tutc = tutc & ~TC_FCS; /* clear fc status */ + tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_TMK | FS_ERR); + sim_cancel (uptr); /* reset drive */ + uptr -> USTAT = 0; +case FNC_NOP: + tucs1 = tucs1 & ~CS1_GO; /* no operation */ + return; +case FNC_RIP: /* read-in preset */ + tutc = TC_800; /* density = 800 */ + tu_unit[0].pos = 0; /* rewind unit 0 */ + tu_unit[0].USTAT = 0; + tucs1 = tucs1 & ~CS1_GO; + return; + +case FNC_UNLOAD: /* unload */ + if ((uptr -> flags & UNIT_ATT) == 0) { /* unattached? */ + tuer = tuer | ER_UNS; + break; } + detach_unit (uptr); + uptr -> USTAT = FS_REW; + sim_activate (uptr, tu_time); + tucs1 = tucs1 & ~CS1_GO; + return; +case FNC_REWIND: + if ((uptr -> flags & UNIT_ATT) == 0) { /* unattached? */ + tuer = tuer | ER_UNS; + break; } + uptr -> USTAT = FS_PIP | FS_REW; + sim_activate (uptr, tu_time); + tucs1 = tucs1 & ~CS1_GO; + return; + +case FNC_SPACEF: + space_test = FS_EOT; +case FNC_SPACER: + if ((uptr -> flags & UNIT_ATT) == 0) { /* unattached? */ + tuer = tuer | ER_UNS; + break; } + if ((tufs & space_test) || ((tutc & TC_FCS) == 0)) { + tuer = tuer | ER_NXF; + break; } + uptr -> USTAT = FS_PIP; + goto GO_XFER; + +case FNC_WCHKR: /* wchk = read */ +case FNC_READR: /* read rev */ + if (tufs & FS_BOT) { /* beginning of tape? */ + tuer = tuer | ER_NXF; + break; } + goto DATA_XFER; + +case FNC_WRITE: /* write */ + if (((tutc & TC_FCS) == 0) || /* frame cnt = 0? */ + ((den == TC_800) && (tufc > 0777765))) { /* NRZI, fc < 13? */ + tuer = tuer | ER_NXF; + break; } +case FNC_WREOF: /* write tape mark */ +case FNC_ERASE: /* erase */ + if (uptr -> flags & UNIT_WLK) { /* write locked? */ + tuer = tuer | ER_NXF; + break; } +case FNC_WCHKF: /* wchk = read */ +case FNC_READF: /* read */ +DATA_XFER: + if ((uptr -> flags & UNIT_ATT) == 0) { /* unattached? */ + tuer = tuer | ER_UNS; + break; } + if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ + tuer = tuer | ER_FER; + break; } + if (den_test[den] == 0) { /* invalid density? */ + tuer = tuer | ER_NXF; + break; } + if (uptr -> UDENS == UD_UNK) uptr -> UDENS = den; /* set dens */ +/* else if (uptr -> UDENS != den) { /* density mismatch? */ +/* tuer = tuer | ER_NXF; +/* break; } */ + uptr -> USTAT = 0; +GO_XFER: + tucs2 = tucs2 & ~CS2_ERR; /* clear errors */ + tucs1 = tucs1 & ~(CS1_TRE | CS1_MCPE | CS1_DONE); + tufs = tufs & ~(FS_TMK | FS_ID); /* clear eof, id */ + sim_activate (uptr, tu_time); + return; + +default: /* all others */ + tuer = tuer | ER_ILF; /* not supported */ + break; } /* end case function */ +update_tucs (CS1_SC, drv); /* error, set intr */ +return; +} + +/* Unit service + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations + Unit must be writeable - can't write protect an active unit +*/ + +t_stat tu_svc (UNIT *uptr) +{ +int32 f, fmt, i, j, k, err, wc10, ba10; +int32 ba, fc, wc, drv, mpa10, vpn; +d10 val, v[4]; +t_mtrlnt tbc; +static t_mtrlnt bceof = { 0 }; +static uint8 xbuf[XBUFLNT + 4]; + +drv = uptr - tu_dev.units; +if (uptr -> USTAT & FS_REW) { /* rewind or unload? */ + uptr -> pos = 0; /* update position */ + uptr -> USTAT = 0; /* clear status */ + tufs = tufs | FS_ATA | FS_SSC; + update_tucs (CS1_SC, drv); /* update status */ + return SCPE_OK; } + +f = GET_FNC (tucs1); /* get command */ +fmt = GET_FMT (tutc); /* get format */ +ba = GET_UAE (tucs1) | tuba; /* get bus address */ +wc = 0200000 - tuwc; /* get word count */ +fc = 0200000 - tufc; /* get frame count */ +wc10 = wc >> 1; /* 10 word count */ +ba10 = ba >> 2; /* 10 word addr */ +err = 0; +uptr -> USTAT = 0; /* clear status */ +switch (f) { /* case on function */ + +/* Unit service - non-data transfer commands - set ATA when done */ + +case FNC_SPACEF: /* space forward */ + do { tufc = (tufc + 1) & 0177777; /* incr fc */ + 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 = FS_EOT; + break; } + if (tbc == 0) { /* zero bc? */ + tufs = tufs | FS_TMK; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); } + while (tufc != 0); + if (tufc) tuer = tuer | ER_FCE; + else tutc = tutc & ~TC_FCS; + tufs = tufs | FS_ATA; + break; +case FNC_SPACER: /* space reverse */ + do { tufc = (tufc + 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 -> pos = 0; + break; } + if (tbc == 0) { /* start of prv file? */ + tufs = tufs | FS_TMK; + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + break; } + uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); } + while ((tufc != 0) && (uptr -> pos)); + if (tufc) tuer = tuer | ER_FCE; + else tutc = tutc & ~TC_FCS; + tufs = tufs | FS_ATA; + break; +case FNC_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 */ +case FNC_ERASE: + tufs = tufs | FS_ATA; + break; + +/* Unit service - data transfer commands + + These commands must take into account the action of the "bit fiddler", which + converts between PDP-10 format and tape format. Only two tape formats are + supported: + + PDP-10 core dump: write 36b as byte 0/byte 1/byte 2/byte 3/0000'last nibble + industry mode: write hi 32b as byte 0/byte 1/byte 2/byte 3 + + These commands must also take into account the action of the Unibus adapter, + which munges PDP-10 addresses through the Unibus map. +*/ + +case FNC_READF: /* read */ +case FNC_WCHKF: /* wcheck = read */ + tufc = 0; /* clear frame count */ + if ((uptr -> UDENS == TC_1600) && (uptr -> pos == 0)) + tufs = tufs | FS_ID; /* PE BOT? ID burst */ + TXFR (ba, wc, 0); /* validate transfer */ + 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 = FS_EOT; + break; } + if (MTRF (tbc)) { /* bad record? */ + tuer = tuer | ER_CRC; /* set error flag */ + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + + (2 * sizeof (t_mtrlnt)); + break; } + if (tbc == 0) { /* tape mark? */ + tufs = tufs | FS_TMK; + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); + break; } + if (tbc > XBUFLNT) return STOP_MTRLNT; /* bad rec length? */ + i = fxread (xbuf, sizeof (int8), tbc, uptr -> fileref); + for ( ; i < tbc + 4; i++) xbuf[i] = 0; /* fill/pad with 0's */ + err = ferror (uptr -> fileref); + for (i = j = 0; (i < wc10) && (j < tbc); i++) { + if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ + MAPM (ba10 + i, mpa10, 0); } + for (k = 0; k < 4; k++) v[k] = xbuf[j++]; + val = (v[0] << 28) | (v[1] << 20) | (v[2] << 12) | (v[3] << 4); + if (fmt == TC_10C) val = val | ((d10) xbuf[j++] & 017); + if (f == FNC_READF) M[mpa10] = val; + mpa10 = mpa10 + 1; } /* end for */ + uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt)); + tufc = tbc & 0177777; + tuwc = (tuwc + (i << 1)) & 0177777; + ba = ba + (i << 2); + break; + +case FNC_WRITE: /* write */ + TXFR (ba, wc, 0); /* validate transfer */ + fseek (uptr -> fileref, uptr -> pos, SEEK_SET); + for (i = j = 0; (i < wc10) && (j < fc); i++) { + if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ + MAPM (ba10 + i, mpa10, 0); } + val = M[mpa10]; + xbuf[j++] = (uint8) ((val >> 28) & 0377); + xbuf[j++] = (uint8) ((val >> 20) & 0377); + xbuf[j++] = (uint8) ((val >> 12) & 0377); + xbuf[j++] = (uint8) ((val >> 4) & 0377); + if (fmt == TC_10C) xbuf[j++] = (uint8) (val & 017); + mpa10 = mpa10 + 1; } /* end for */ + if (j < fc) fc = j; /* short record? */ + fxwrite (&fc, sizeof (t_mtrlnt), 1, uptr -> fileref); + fxwrite (xbuf, sizeof (int8), (fc + 1) & ~1, uptr -> fileref); + fxwrite (&fc, sizeof (t_mtrlnt), 1, uptr -> fileref); + err = ferror (uptr -> fileref); + uptr -> pos = uptr -> pos + ((fc + 1) & ~1) + (2 * sizeof (t_mtrlnt)); + tufc = (tufc + fc) & 0177777; + if (tufc == 0) tutc = tutc & ~TC_FCS; + tuwc = (tuwc + (i << 1)) & 0177777; + ba = ba + (i << 2); + break; + +case FNC_READR: /* read reverse */ +case FNC_WCHKR: /* wcheck = read */ + tufc = 0; /* clear frame count */ + TXFR (ba, wc, 1); /* validate xfer rev */ + 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 = FS_EOT; + break; } + if (MTRF (tbc)) { /* bad record? */ + tuer = tuer | ER_CRC; /* set error flag */ + uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - + (2 * sizeof (t_mtrlnt)); + break; } + if (tbc == 0) { /* tape mark? */ + tufs = tufs | FS_TMK; + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); + break; } + if (tbc > XBUFLNT) return STOP_MTRLNT; /* bad rec length? */ + fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt) + - ((tbc + 1) & ~1), SEEK_SET); + fxread (xbuf + 4, sizeof (int8), tbc, uptr -> fileref); + for (i = 0; i < 4; i++) xbuf[i] = 0; + for (i = 0, j = tbc + 4; (i < wc10) && (j >= 4); i++) { + if ((i == 0) || NEWPAGE (ba10 - i, PAG_M_OFF)) { /* map page */ + MAPM (ba10 - i, mpa10, UMAP_RRV); } + val = ((fmt == TC_10C)? (((d10) xbuf [--j]) & 017): 0); + for (k = 0; k < 4; i++) v[k] = xbuf[--j]; + val = val | (v[0] << 4) | (v[1] << 12) | (v[2] << 20) | (v[3] << 28); + if (f == FNC_READR) M[mpa10] = val; + mpa10 = mpa10 - 1; } /* end for */ + uptr -> pos = uptr -> pos - ((tbc + 1) & ~1) - (2 * sizeof (t_mtrlnt)); + tufc = tbc & 0177777; + tuwc = (tuwc + (i << 1)) & 0177777; + ba = ba - (i << 2); + break; } /* end case */ + +/* Unit service, continued */ + +tucs1 = (tucs1 & ~CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); +tuba = ba & 0177777; /* update mem addr */ +tucs1 = tucs1 & ~CS1_GO; /* clear go */ +if (err != 0) { /* I/O error */ + tuer = tuer | ER_CRC; /* flag error */ + update_tucs (CS1_DONE | CS1_TRE, drv); /* set done, err */ + perror ("TU I/O error"); + clearerr (uptr -> fileref); + return IORETURN (tu_stopioe, SCPE_IOERR); } +update_tucs (CS1_DONE, drv); +return SCPE_OK; +} + +/* Controller status update + First update formatter status, then update MTCS1 + If optional argument, request interrupt +*/ + +void update_tucs (int32 flag, int32 drv) +{ +int32 act = sim_is_active (&tu_unit[drv]); + +if (GET_FMTR (tucs2) == 0) { /* formatter present? */ + tufs = (tufs & ~FS_DYN) | FS_FPR; + if (tu_unit[drv].flags & UNIT_ATT) { + tufs = tufs | FS_MOL | tu_unit[drv].USTAT; + if (tu_unit[drv].UDENS == TC_1600) tufs = tufs | FS_PE; + if (tu_unit[drv].flags & UNIT_WLK) tufs = tufs | FS_WRL; + if ((tu_unit[drv].pos == 0) && !act) tufs = tufs | FS_BOT; } + if (tuer) tufs = tufs | FS_ERR; } +else tufs = 0; +tucs1 = (tucs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ)) | CS1_DVA | flag; +if (tucs2 & CS2_ERR) tucs1 = tucs1 | CS1_TRE | CS1_SC; +if (tufs & FS_ATA) tucs1 = tucs1 | CS1_SC; +if (((tucs1 & CS1_IE) == 0) || ((tucs1 & CS1_DONE) == 0)) + int_req = int_req & ~INT_TU; +else if (flag) int_req = int_req | INT_TU; +if ((tucs1 & CS1_DONE) && tufs && !act) tufs = tufs | FS_RDY; +return; +} + +/* Interrupt acknowledge */ + +int32 tu_inta (void) +{ +tucs1 = tucs1 & ~CS1_IE; /* clear int enable */ +return VEC_TU; /* acknowledge */ +} + +/* Reset routine */ + +t_stat tu_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +tucs1 = CS1_DVA | CS1_DONE; +tucs2 = CS2_IR | CS2_OR; +tuba = tufc = 0; +tutc = tuer = 0; +tufs = FS_FPR | FS_RDY; +int_req = int_req & ~INT_TU; /* clear interrupt */ +for (u = 0; u < TU_NUMDR; u++) { /* loop thru units */ + uptr = tu_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + uptr -> USTAT = 0; } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat tu_attach (UNIT *uptr, char *cptr) +{ +int32 drv = uptr - tu_dev.units; +t_stat r; + +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +uptr -> USTAT = 0; /* clear unit status */ +uptr -> UDENS = UD_UNK; /* unknown density */ +tufs = tufs | FS_ATA | FS_SSC; /* set attention */ +if ((GET_FMTR (tucs2) == 0) && (GET_DRV (tutc) == drv)) /* selected drive? */ + tufs = tufs | FS_SAT; /* set slave attn */ +update_tucs (CS1_SC, drv); /* update status */ +return r; +} + +/* Detach routine */ + +t_stat tu_detach (UNIT* uptr) +{ +int32 drv = uptr - tu_dev.units; + +if (sim_is_active (uptr)) { /* unit active? */ + sim_cancel (uptr); /* cancel operation */ + tuer = tuer | ER_UNS; /* set formatter error */ + if ((uptr -> USTAT & FS_REW) == 0) /* data transfer? */ + tucs1 = tucs1 | CS1_DONE | CS1_TRE; } /* set done, err */ +uptr -> USTAT = 0; /* clear status flags */ +tufs = tufs | FS_ATA | FS_SSC; /* set attention */ +update_tucs (CS1_SC, drv); /* update status */ +return detach_unit (uptr); +} + +/* Write lock/enable routine */ + +t_stat tu_vlock (UNIT *uptr, int32 val) +{ +if (sim_is_active (uptr)) return SCPE_ARG; +return SCPE_OK; +} + +/* Device bootstrap */ + +#define BOOT_START 0377000 /* start */ +#define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) + +static const d10 boot_rom_dec[] = { + 0515040000003, /* boot:hrlzi 1,3 ; uba # */ + 0201000040001, /* movei 0,40001 ; vld,pg 1 */ + 0713001000000+IO_UBMAP+1, /* wrio 0,763001(1); set ubmap */ + 0435040000000+IO_TMBASE, /* iori 1,772440 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0713001000010, /* wrio 0,10(1) ; ->MTFS */ + 0201100000031, /* movei 2,31 ; space f */ + 0265740377014, /* jsp 17,tpop ; skip ucode */ + 0201100000071, /* movei 2,71 ; read f */ + 0265740377014, /* jsp 17,tpop ; read boot */ + 0254000001000, /* jrst 1000 ; start */ + 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ + 0713001000032, /* wrio 0,32(1) ; ->MTTC */ + 0201000000011, /* movei 0,11 ; clr+go */ + 0713001000000, /* wrio 0,0(1) ; ->MTCS1 */ + 0201140176000, /* movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ + 0201300000000, /* movei 6,0 ; fmtr */ + 0713141000002, /* wrio 3,2(1) ; ->MTWC */ + 0713201000004, /* wrio 4,4(1) ; ->MTBA */ + 0713301000006, /* wrio 6,6(1) ; ->MTFC */ + 0713301000010, /* wrio 6,10(1) ; ->MTFS */ + 0713241000032, /* wrio 5,32(1) ; ->MTTC */ + 0713101000000, /* wrio 2,0(1) ; ->MTCS1 */ + 0712341000012, /* rdio 7,12(1) ; read FS */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377032, /* jrst .-2 ; loop */ + 0606340040000, /* trnn 7,40000 ; test err */ + 0254017000000, /* jrst 0(17) ; return */ + 0712341000014, /* rdio 7,14(1) ; read err */ + 0302340001000, /* caie 7,1000 ; fce? */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ +}; + +static const d10 boot_rom_its[] = { + 0515040000003, /* boot:hrlzi 1,3 ; uba # - not used */ + 0201000040001, /* movei 0,40001 ; vld,pg 1 */ + 0714000000000+IO_UBMAP+1, /* iowri 0,763001 ; set ubmap */ + 0435040000000+IO_TMBASE, /* iori 1,772440 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0714001000010, /* iowri 0,10(1) ; ->MTFS */ + 0201100000031, /* movei 2,31 ; space f */ + 0265740377014, /* jsp 17,tpop ; skip ucode */ + 0201100000071, /* movei 2,71 ; read f */ + 0265740377014, /* jsp 17,tpop ; read boot */ + 0254000001000, /* jrst 1000 ; start */ + 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ + 0714001000032, /* iowri 0,32(1) ; ->MTTC */ + 0201000000011, /* movei 0,11 ; clr+go */ + 0714001000000, /* iowri 0,0(1) ; ->MTCS1 */ + 0201140176000, /* movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ + 0201300000000, /* movei 6,0 ; fmtr */ + 0714141000002, /* iowri 3,2(1) ; ->MTWC */ + 0714201000004, /* iowri 4,4(1) ; ->MTBA */ + 0714301000006, /* iowri 6,6(1) ; ->MTFC */ + 0714301000010, /* iowri 6,10(1) ; ->MTFS */ + 0714241000032, /* iowri 5,32(1) ; ->MTTC */ + 0714101000000, /* iowri 2,0(1) ; ->MTCS1 */ + 0710341000012, /* iordi 7,12(1) ; read FS */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377032, /* jrst .-2 ; loop */ + 0606340040000, /* trnn 7,40000 ; test err */ + 0254017000000, /* jrst 0(17) ; return */ + 0710341000014, /* iordi 7,14(1) ; read err */ + 0302340001000, /* caie 7,1000 ; fce? */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ +}; +t_stat tu_boot (int32 unitno) +{ +int32 i; +extern a10 saved_PC; + +M[FE_UNIT] = 0; +M[FE_MTFMT] = (unitno & TC_M_UNIT) | (TC_1600 << TC_V_DEN) | (TC_10C << TC_V_FMT); +tu_unit[unitno].pos = 0; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = ITS? boot_rom_its[i]: boot_rom_dec[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp10_xtnd.c b/pdp10_xtnd.c new file mode 100644 index 00000000..89bfaf5b --- /dev/null +++ b/pdp10_xtnd.c @@ -0,0 +1,638 @@ +/* pdp10_xtnd.c: PDP-10 extended instruction simulator + + Copyright (c) 1993-2001, 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-May-01 RMS Fixed compiler warning in xlate + + Instructions handled in this module: + MOVSLJ move string left justified + MOVSO move string offset + MOVST move string translated + MOVSRJ move string right justified + CMPSL compare string, skip on less + CMPSE compare string, skip on equal + CMPSLE compare string, skip on less or equal + CMPSGE compare string, skip on greater or equal + CMPSN compare string, skip on unequal + CMPSG compare string, skip on greater + CVTDBO convert decimal to binary offset + CVTDBT convert decimal to binary translated + CVTBDO convert binary to decimal offset + CVTBDT convert binary to decimal translated + EDIT edit + + The PDP-10 extended instructions deal with non-binary data types, + particularly byte strings and decimal strings. (In the KL10, the + extended instructions include G floating support as well.) They + are very complicated microcoded subroutines that can potentially + run for a very long time. Accordingly, the instructions must test + for interrupts as well as page faults, and be prepared to restart + from either. + + In general, the simulator attempts to keep the AC block up to date, + so that page fails and interrupts can be taken directly at any point. + If the AC block is not up to date, memory accessibility must be tested + before the actual read or write is done. + + The extended instruction routine returns a status code as follows: + XT_NOSK no skip completion + XT_SKIP skip completion + XT_MUUO invalid extended instruction +*/ + +#include "pdp10_defs.h" +#include + +#define MM_XSRC (pflgs & XSRC_PXCT) +#define MM_XDST (pflgs & XDST_PXCT) +#define MM_EA_XSRC ((pflgs & EA_PXCT) && MM_XSRC) +#define MM_EA_XDST ((pflgs & EA_PXCT) && MM_XDST) + +#define XT_CMPSL 001 /* opcodes */ +#define XT_CMPSE 002 +#define XT_CMPSLE 003 +#define XT_EDIT 004 +#define XT_CMPSGE 005 +#define XT_CMPSN 006 +#define XT_CMPSG 007 +#define XT_CVTDBO 010 +#define XT_CVTDBT 011 +#define XT_CVTBDO 012 +#define XT_CVTBDT 013 +#define XT_MOVSO 014 +#define XT_MOVST 015 +#define XT_MOVSLJ 016 +#define XT_MOVSRJ 017 + +/* Translation control */ + +#define XT_LFLG 0400000000000 /* L flag */ +#define XT_SFLG 0400000000000 /* S flag */ +#define XT_NFLG 0200000000000 /* N flag */ +#define XT_MFLG 0100000000000 /* M flag */ + +/* Translation table */ + +#define XT_V_CODE 15 /* translation op */ +#define XT_M_CODE 07 +#define XT_BYMASK 07777 /* byte mask */ +#define XT_DGMASK 017 /* digit mask */ +#define XT_GETCODE(x) ((int32) (((x) >> XT_V_CODE) & XT_M_CODE)) + +/* AC masks */ + +#define XLNTMASK 0000777777777 /* length */ +#define XFLGMASK 0700000000000 /* flags */ +#define XT_MBZ 0777000000000 /* must be zero */ +#define XT_MBZE 0047777000000 /* must be zero, edit */ + +/* Register change log */ + +#define XT_N_RLOG 5 /* entry width */ +#define XT_M_RLOG ((1 << XT_N_RLOG) - 1) /* entry mask */ +#define XT_O_RLOG 1 /* entry offset */ +#define XT_INSRLOG(x,v) v = ((v << XT_N_RLOG) | (((x) + XT_O_RLOG) & XT_M_RLOG)) +#define XT_REMRLOG(x,v) x = (v & XT_M_RLOG) - XT_O_RLOG; \ + v = v >> XT_N_RLOG + +/* Edit */ + +#define ED_V_PBYN 30 /* pattern byte # */ +#define ED_M_PBYN 03 +#define ED_PBYNO 0040000000000 /* overflow bit */ +#define ED_GETPBYN(x) ((int32) (((x) >> ED_V_PBYN) & ED_M_PBYN)) +#define ED_V_POPC 6 /* pattern byte opcode */ +#define ED_M_PAT 0777 /* pattern byte mask */ +#define ED_M_NUM 0077 /* number for msg, etc */ +#define ED_PBYTE(x,y) ((int32) (((x) >> (27 - (ED_GETPBYN (y) * 9))) & ED_M_PAT)) +#define ED_STOP 0000 /* stop */ +#define ED_SELECT 0001 /* select source */ +#define ED_SIGST 0002 /* start significance */ +#define ED_FLDSEP 0003 /* field separator */ +#define ED_EXCHMD 0004 /* exchange mark, dst */ +#define ED_MESSAG 0100 /* message */ +#define ED_SKPM 0500 /* skip if M */ +#define ED_SKPN 0600 /* skip if N */ +#define ED_SKPA 0700 /* skip always */ + +extern d10 *ac_cur; /* current AC block */ +extern d10 bytemask[64]; +extern int32 flags; +extern int32 rlog; +extern jmp_buf save_env; + +extern d10 Read (int32 ea, int32 prv); +extern void Write (int32 ea, d10 val, int32 prv); +extern a10 calc_ea (d10 inst, int32 prv); +extern int32 test_int (void); +d10 incbp (d10 bp); +d10 incloadbp (int32 ac, int32 pflgs); +void incstorebp (d10 val, int32 ac, int32 pflgs); +d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 pflgs); +void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs); + +static const d10 pwrs10[23][2] = { + 0, 0, + 0, 1, + 0, 10, + 0, 100, + 0, 1000, + 0, 10000, + 0, 100000, + 0, 1000000, + 0, 10000000, + 0, 100000000, + 0, 1000000000, + 0, 10000000000, + 2, 31280523264, + 29, 3567587328, + 291, 1316134912, + 2910, 13161349120, + 29103, 28534276096, + 291038, 10464854016, + 2910383, 1569325056, + 29103830, 15693250560, + 291038304, 19493552128, + 2910383045, 23136829440, + 29103830456, 25209864192 }; + +int xtend (int32 ac, int32 ea, int32 pflgs) +{ +d10 b1, b2, ppi; +d10 xinst, xoff, digit, f1, f2, rs[2]; +d10 xflgs = 0; +a10 e1, entad; +int32 p1 = ADDAC (ac, 1); +int32 p3 = ADDAC (ac, 3); +int32 p4 = ADDAC (ac, 4); +int32 flg, i, s2, t, pp, pat, xop, xac, ret; + +xinst = Read (ea, MM_OPND); /* get extended instr */ +xop = GET_OP (xinst); /* get opcode */ +xac = GET_AC (xinst); /* get AC */ +if (xac || (xop == 0) || (xop > XT_MOVSRJ)) return XT_MUUO; +rlog = 0; /* clear log */ +switch (xop) { /* case on opcode */ + +/* String compares - checked against KS10 ucode + If both strings are zero length, they are considered equal. + Both source and destination lengths are MBZ checked. + + AC = source1 length + AC + 1 = source1 byte pointer + AC + 3 = source2 length + AC + 4 = source2 byte pointer +*/ + +case XT_CMPSL: /* CMPSL */ +case XT_CMPSE: /* CMPSE */ +case XT_CMPSLE: /* CMPSLE */ +case XT_CMPSGE: /* CMPSGE */ +case XT_CMPSN: /* CMPSN */ +case XT_CMPSG: /* CMPSG */ + if ((AC(ac) | AC(p3)) & XT_MBZ) return XT_MUUO; /* check length MBZ */ + f1 = Read (ADDA (ea, 1), MM_OPND) & bytemask[GET_S (AC(p1))]; + f2 = Read (ADDA (ea, 2), MM_OPND) & bytemask[GET_S (AC(p4))]; + b1 = b2 = 0; + for (flg = 0; (AC(ac) | AC(p3)) && (b1 == b2); flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + if (AC(ac)) b1 = incloadbp (p1, pflgs); /* src1 */ + else b1 = f1; + if (AC(p3)) b2 = incloadbp (p4, pflgs); /* src2 */ + else b2 = f2; + if (AC(ac)) AC(ac) = (AC(ac) - 1) & XLNTMASK; + if (AC(p3)) AC(p3) = (AC(p3) - 1) & XLNTMASK; } + switch (xop) { + case XT_CMPSL: return (b1 < b2)? XT_SKIP: XT_NOSK; + case XT_CMPSE: return (b1 == b2)? XT_SKIP: XT_NOSK; + case XT_CMPSLE: return (b1 <= b2)? XT_SKIP: XT_NOSK; + case XT_CMPSGE: return (b1 >= b2)? XT_SKIP: XT_NOSK; + case XT_CMPSN: return (b1 != b2)? XT_SKIP: XT_NOSK; + case XT_CMPSG: return (b1 > b2)? XT_SKIP: XT_NOSK; } + return XT_MUUO; + +/* Convert binary to decimal instructions - checked against KS10 ucode + There are no MBZ tests. + + AC'AC + 1 = double precision integer source + AC + 3 = flags and destination length + AC + 4 = destination byte pointer +*/ + +case XT_CVTBDO: /* CVTBDO */ +case XT_CVTBDT: /* CVTBDT */ + e1 = calc_ea (xinst, MM_EA); /* get ext inst addr */ + if (xop == XT_CVTBDO) /* offset? */ + xoff = (e1 & RSIGN)? (e1 | LMASK): e1; /* get offset */ + rs[0] = AC(ac); /* get src opnd */ + rs[1] = CLRS (AC(p1)); + if (!TSTF (F_FPD)) { /* set up done yet? */ + if (TSTS (AC(ac))) { DMOVN (rs); } /* get abs value */ + for (i = 22; i > 1; i--) { /* find field width */ + if (DCMPGE (rs, pwrs10[i])) break; } + if (i > (AC(p3) & XLNTMASK)) return XT_NOSK; + if ((i < (AC(p3) & XLNTMASK)) && (AC(p3) & XT_LFLG)) { + f1 = Read (ADDA (ea, 1), MM_OPND); + filldst (f1, p3, (AC(p3) & XLNTMASK) - i, pflgs); } + else AC(p3) = (AC(p3) & XFLGMASK) | i; + if (TSTS (AC(ac))) AC(p3) = AC(p3) | XT_MFLG; + if (AC(ac) | AC(p1)) AC(p3) = AC(p3) | XT_NFLG; + AC(ac) = rs[0]; /* update state */ + AC(p1) = rs[1]; + SETF (F_FPD); } /* mark set up done */ + +/* Now do actual binary to decimal conversion */ + + for (flg = 0; AC(p3) & XLNTMASK; flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + i = (int32) AC(p3) & XLNTMASK; /* get length */ + if (i > 22) i = 22; /* put in range */ + for (digit = 0; (digit < 10) && DCMPGE (rs, pwrs10[i]); digit++) { + rs[0] = rs[0] - pwrs10[i][0] - (rs[1] < pwrs10[i][1]); + rs[1] = (rs[1] - pwrs10[i][1]) & MMASK; } + if (xop == XT_CVTBDO) digit = (digit + xoff) & DMASK; + else { f1 = Read (e1 + (int32) digit, MM_OPND); + if ((i == 1) && (AC(p3) & XT_LFLG)) f1 = f1 >> 18; + digit = f1 & RMASK; } + incstorebp (digit, p4, pflgs); /* store digit */ + AC(ac) = rs[0]; /* mem access ok */ + AC(p1) = rs[1]; /* update state */ + AC(p3) = (AC(p3) & XFLGMASK) | ((AC(p3) - 1) & XLNTMASK); } + CLRF (F_FPD); /* clear FPD */ + return XT_SKIP; + +/* Convert decimal to binary instructions - checked against KS10 ucode + There are no MBZ tests. + + AC = flags and source length + AC + 1 = source byte pointer + AC + 3'AC + 4 = double precision integer result +*/ + +case XT_CVTDBT: /* CVTDBT */ +case XT_CVTDBO: /* CVTDBO */ + e1 = calc_ea (xinst, MM_EA); /* get ext inst addr */ + if ((AC(ac) & XT_SFLG) == 0) AC(p3) = AC(p4) = 0; /* !S? clr res */ + else AC(p4) = CLRS (AC(p4)); /* clear low sign */ + if (xop == XT_CVTDBO) { /* offset? */ + xoff = (e1 & RSIGN)? (e1 | LMASK): e1; /* get offset */ + AC(ac) = AC(ac) | XT_SFLG; } /* set S flag */ + xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ + for (flg = 0; AC(ac) & XLNTMASK; flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + b1 = incloadbp (p1, pflgs); /* get byte */ + if (xop == XT_CVTDBO) b1 = (b1 + xoff) & DMASK; + else { b1 = xlate (b1, e1, &xflgs, MM_OPND); + if (b1 < 0) { /* terminated? */ + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); + return XT_NOSK; } + if (xflgs & XT_SFLG) b1 = b1 & XT_DGMASK; + else b1 = 0; } + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + if ((b1 < 0) || (b1 > 9)) { /* bad digit? done */ + if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); + return XT_NOSK; } + AC(p4) = (AC(p4) * 10) + b1; /* base * 10 + digit */ + AC(p3) = ((AC(p3) * 10) + (AC(p4) >> 35)) & DMASK; + AC(p4) = AC(p4) & MMASK; } + if (AC(ac) & XT_MFLG) { + AC(p4) = -AC(p4) & MMASK; + AC(p3) = (~AC(p3) + (AC(p4) == 0)) & DMASK; } + if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); + return XT_SKIP; + +/* String move instructions - checked against KS10 ucode + Only the destination length is MBZ checked. + + AC = flags (MOVST only) and source length + AC + 1 = source byte pointer + AC + 3 = destination length + AC + 4 = destination byte pointer +*/ + +case XT_MOVSO: /* MOVSO */ +case XT_MOVST: /* MOVST */ +case XT_MOVSRJ: /* MOVSRJ */ +case XT_MOVSLJ: /* MOVSLJ */ + if (AC(p3) & XT_MBZ) return XT_MUUO; /* test dst lnt MBZ */ + f1 = Read (ADDA (ea, 1), MM_OPND); /* get fill */ + switch (xop) { /* case on instr */ + case XT_MOVSO: /* MOVSO */ + AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ + xoff = calc_ea (xinst, MM_EA); /* get offset */ + if (xoff & RSIGN) xoff = xoff | LMASK; /* sign extend 18b */ + s2 = GET_S (AC(p4)); /* get dst byte size */ + break; + case XT_MOVST: /* MOVST */ + e1 = calc_ea (xinst, MM_EA); /* get xlate tbl addr */ + break; + case XT_MOVSRJ: /* MOVSRJ */ + AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ + if (AC(p3) == 0) return (AC(ac)? XT_NOSK: XT_SKIP); + if (AC(ac) > AC(p3)) { /* adv src ptr */ + for (flg = 0; AC(ac) > AC(p3); flg++) { + if (flg && (t = test_int ())) ABORT (t); + AC(p1) = incbp (AC(p1)); + AC(ac) = (AC(ac) - 1) & XLNTMASK; } } + else if (AC(ac) < AC(p3)) + filldst (f1, p3, AC(p3) - AC(ac), pflgs); + break; + case XT_MOVSLJ: /* MOVSLJ */ + AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ + break; } + + xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ + if (AC(p3) == 0) return (AC(ac)? XT_NOSK: XT_SKIP); + for (flg = 0; AC(p3) & XLNTMASK; flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + if (AC(ac) & XLNTMASK) { /* any source? */ + b1 = incloadbp (p1, pflgs); /* src byte */ + if (xop == XT_MOVSO) { /* offset? */ + b1 = (b1 + xoff) & DMASK; /* test fit */ + if (b1 & ~bytemask[s2]) { + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + return XT_NOSK; } } + else if (xop == XT_MOVST) { /* translate? */ + b1 = xlate (b1, e1, &xflgs, MM_OPND); + if (b1 < 0) { /* upd flags in AC */ + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + return XT_NOSK; } + if (xflgs & XT_SFLG) b1 = b1 & XT_BYMASK; + else b1 = -1; } } + else b1 = f1; + if (b1 >= 0) { /* valid byte? */ + incstorebp (b1, p4, pflgs); /* store byte */ + AC(p3) = (AC(p3) - 1) & XLNTMASK; } /* update state */ + if (AC(ac) & XLNTMASK) AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); } + return (AC(ac) & XLNTMASK)? XT_NOSK: XT_SKIP; + +/* Edit - checked against KS10 ucode + Only the flags/pattern pointer word is MBZ checked. + + AC = flags, pattern pointer + AC + 1 = source byte pointer + AC + 3 = mark address + AC + 4 = destination byte pointer +*/ + +case XT_EDIT: /* EDIT */ + if (AC(ac) & XT_MBZE) return XT_MUUO; /* check pattern MBZ */ + xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ + e1 = calc_ea (xinst, MM_EA); /* get xlate tbl addr */ + for (ppi = 1, ret = -1, flg = 0; ret < 0; flg++, ppi = 1) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + pp = (int32) AC(ac) & AMASK; /* get pattern ptr */ + b1 = Read (pp, MM_OPND); /* get pattern word */ + pat = ED_PBYTE (b1, AC(ac)); /* get pattern byte */ + switch ((pat < 0100)? pat: ((pat >> ED_V_POPC) + 0100)) { + case ED_STOP: /* stop */ + ret = XT_SKIP; /* exit loop */ + break; + case ED_SELECT: /* select source */ + b1 = incloadbp (p1, pflgs); /* get src */ + entad = (e1 + ((int32) b1 >> 1)) & AMASK; + f1 = ((Read (entad, MM_OPND) >> ((b1 & 1)? 0: 18)) & RMASK); + i = XT_GETCODE (f1); + if (i & 2) xflgs = + (i & 1)? xflgs | XT_MFLG: xflgs & ~XT_MFLG; + switch (i) { + case 00: case 02: case 03: + if (xflgs & XT_SFLG) f1 = f1 & XT_BYMASK; + else { f1 = Read (INCA (ea), MM_OPND); + if (f1 == 0) break; } + incstorebp (f1, p4, pflgs); + break; + case 01: + ret = XT_NOSK; /* exit loop */ + break; + case 04: case 06: case 07: + xflgs = xflgs | XT_NFLG; + f1 = f1 & XT_BYMASK; + if ((xflgs & XT_SFLG) == 0) { + f2 = Read (ADDA (ea, 2), MM_OPND); + Write ((a10) AC(p3), AC(p4), MM_OPND); + if (f2) incstorebp (f2, p4, pflgs); + xflgs = xflgs | XT_SFLG; } + incstorebp (f1, p4, pflgs); + break; + case 05: + xflgs = xflgs | XT_NFLG; + ret = XT_NOSK; /* exit loop */ + break; } /* end case xlate op */ + break; + case ED_SIGST: /* start significance */ + if ((xflgs & XT_SFLG) == 0) { + f2 = Read (ADDA (ea, 2), MM_OPND); + Write ((a10) AC(p3), AC(p4), MM_OPND); + if (f2) incstorebp (f2, p4, pflgs); + xflgs = xflgs | XT_SFLG; } + break; + case ED_FLDSEP: /* separate fields */ + xflgs = 0; + break; + case ED_EXCHMD: /* exchange */ + f2 = Read ((int32) (AC(p3) & AMASK), MM_OPND); + Write ((int32) (AC(p3) & AMASK), AC(p4), MM_OPND); + AC(p4) = f2; + break; + case (0100 + (ED_MESSAG >> ED_V_POPC)): /* message */ + if (xflgs & XT_SFLG) + f1 = Read (ea + (pat & ED_M_NUM) + 1, MM_OPND); + else { f1 = Read (ea + 1, MM_OPND); + if (f1 == 0) break; } + incstorebp (f1, p4, pflgs); + break; + case (0100 + (ED_SKPM >> ED_V_POPC)): /* skip on M */ + if (xflgs & XT_MFLG) ppi = (pat & ED_M_NUM) + 2; + break; + case (0100 + (ED_SKPN >> ED_V_POPC)): /* skip on N */ + if (xflgs & XT_NFLG) ppi = (pat & ED_M_NUM) + 2; + break; + case (0100 + (ED_SKPA >> ED_V_POPC)): /* skip always */ + ppi = (pat & ED_M_NUM) + 2; + break; + default: /* NOP or undefined */ + break; } /* end case pttrn op */ + AC(ac) = AC(ac) + ((ppi & ED_M_PBYN) << ED_V_PBYN); + AC(ac) = AC(ac) + (ppi >> 2) + ((AC(ac) & ED_PBYNO)? 1: 0); + AC(ac) = xflgs | (AC(ac) & ~(XT_MBZE | XFLGMASK)); } + return ret; } /* end case xop */ +return XT_MUUO; +} + +/* Supporting subroutines */ + +/* Increment byte pointer, register version */ + +d10 incbp (d10 bp) +{ +int32 p, s; + +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +p = p - s; /* adv P */ +if (p < 0) { /* end of word? */ + bp = (bp & LMASK) | (INCR (bp)); /* increment addr */ + p = (36 - s) & 077; } /* reset P */ +bp = PUT_P (bp, p); /* store new P */ +return bp; +} + +/* Increment and load byte, extended version - uses register log */ + +d10 incloadbp (int32 ac, int32 pflgs) +{ +a10 ba; +d10 bp, wd; +int32 p, s; + +bp = AC(ac) = incbp (AC(ac)); /* increment bp */ +XT_INSRLOG (ac, rlog); /* log change */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EA_XSRC); /* calc bp eff addr */ +wd = Read (ba, MM_XSRC); /* read word */ +wd = (wd >> p) & bytemask[s]; /* get byte */ +return wd; +} + +/* Increment and deposit byte, extended version - uses register log */ + +void incstorebp (d10 val, int32 ac, int32 pflgs) +{ +a10 ba; +d10 bp, wd, mask; +int32 p, s; + +bp = AC(ac) = incbp (AC(ac)); /* increment bp */ +XT_INSRLOG (ac, rlog); /* log change */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EA_XDST); /* calc bp eff addr */ +wd = Read (ba, MM_XDST); /* read, write test */ +mask = bytemask[s] << p; /* shift mask, val */ +val = val << p; +wd = (wd & ~mask) | (val & mask); /* insert byte */ +Write (ba, wd & DMASK, MM_XDST); +return; +} + +/* Translate byte + + Arguments + by = byte to translate + tblad = virtual address of translation table + *xflgs = pointer to word containing translation flags + prv = previous mode flag for table lookup + Returns + xby = >= 0, translated byte + < 0, terminate translation +*/ + +d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 prv) +{ +a10 ea; +int32 tcode; +d10 tblent; + +ea = (tblad + ((int32) by >> 1)) & AMASK; +tblent = ((Read (ea, prv) >> ((by & 1)? 0: 18)) & RMASK); +tcode = XT_GETCODE (tblent); /* get xlate code */ +switch (tcode) { +case 00: + return (*xflgs & XT_SFLG)? tblent: by; +case 01: + break; +case 02: + *xflgs = *xflgs & ~XT_MFLG; + return (*xflgs & XT_SFLG)? tblent: by; +case 03: + *xflgs = *xflgs | XT_MFLG; + return (*xflgs & XT_SFLG)? tblent: by; +case 04: + *xflgs = *xflgs | XT_SFLG | XT_NFLG; + return tblent; +case 05: + *xflgs = *xflgs | XT_NFLG; + break; +case 06: + *xflgs = (*xflgs | XT_SFLG | XT_NFLG) & ~XT_MFLG; + return tblent; +case 07: + *xflgs = *xflgs | XT_SFLG | XT_NFLG | XT_MFLG; + return tblent; } /* end case xlate code */ +return -1; +} + +/* Fill out the destination string + + Arguments: + fill = fill + ac = 2 word AC block (length, byte pointer) + cnt = fill count + pflgs = PXCT flags +*/ + +void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs) +{ +int32 i, t; +int32 p1 = ADDA (ac, 1); + +for (i = 0; i < cnt; i++) { + if (i && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + incstorebp (fill, p1, pflgs); + AC(ac) = (AC(ac) & XFLGMASK) | ((AC(ac) - 1) & XLNTMASK); } + rlog = 0; +return; +} + +/* Clean up after page fault + + Arguments: + logv = register change log + + For each register in logv, decrement the register's contents as + though it were a byte pointer. Note that the KS10 does + do a full decrement calculation but merely adds S to P. +*/ + +void xtcln (int32 logv) +{ +int32 p, reg; + +while (logv) { + XT_REMRLOG (reg, logv); /* get next reg */ + if ((reg >= 0) && (reg < AC_NUM)) { + p = GET_P (AC(reg)) + GET_S (AC(reg)); /* get p + s */ + AC(reg) = PUT_P (AC(reg), p); } /* p <- p + s */ + } +return; +} diff --git a/pdp11_cis.c b/pdp11_cis.c index e978eb6a..9545b70c 100644 --- a/pdp11_cis.c +++ b/pdp11_cis.c @@ -1,6 +1,6 @@ /* pdp11_cis.c: PDP-11 CIS optional instruction set simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -313,7 +313,7 @@ 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; +uint32 nc, digit, result; static DSTR accum, src1, src2, dst; static DSTR mptable[10]; static DSTR Dstr1 = { 0, 0x10, 0, 0, 0 }; @@ -981,9 +981,10 @@ return TestDstr (src); /* clean -0 */ void WriteDstr (int32 *dscr, DSTR *dst, int32 flag) { -int32 c, i, mask, limit, end, type, lnt; -static int32 masktab[8] = { - 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, +int32 c, i, limit, end, type, lnt; +uint32 mask; +static uint32 masktab[8] = { + 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 }; static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 }; diff --git a/pdp11_cpu.c b/pdp11_cpu.c index f0ec0122..b11fb40c 100644 --- a/pdp11_cpu.c +++ b/pdp11_cpu.c @@ -1,6 +1,6 @@ /* pdp11_cpu.c: PDP-11 CPU simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,13 @@ 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. + cpu PDP-11 CPU (J-11 microprocessor) + + 23-Apr-01 RMS Added RK611 support + 05-Apr-01 RMS Added TS11/TSV05 support + 05-Mar-01 RMS Added clock calibration support + 11-Feb-01 RMS Added DECtape support + 25-Jan-01 RMS Fixed 4M memory definition (found by Eric Smith) 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 @@ -179,14 +186,14 @@ #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_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 */ +uint16 *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 */ @@ -195,8 +202,8 @@ 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 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 */ @@ -213,23 +220,24 @@ 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 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_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 */ +int32 ibkpt_addr = ILL_ADR_FLAG | VAMASK; /* breakpoint addr */ +int32 old_PC = 0; /* previous PC */ +int32 dev_enb = (-1) & ~INT_TS; /* dev enables */ 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 */ +uint32 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; @@ -270,6 +278,9 @@ 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 hk_rd (int32 *data, int32 addr, int32 access); +extern t_stat hk_wr (int32 data, int32 addr, int32 access); +extern int32 hk_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); @@ -277,38 +288,45 @@ 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 dt_rd (int32 *data, int32 addr, int32 access); +extern t_stat dt_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); +extern t_stat ts_rd (int32 *data, int32 addr, int32 access); +extern t_stat ts_wr (int32 data, int32 addr, int32 access); /* Auxiliary data structures */ struct iolink { /* I/O page linkage */ int32 low; /* low I/O addr */ int32 high; /* high I/O addr */ + int32 enb; /* enable mask */ 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 } }; + { 017777740, 017777777, 0, &CPU_rd, &CPU_wr }, + { 017777546, 017777567, 0, &std_rd, &std_wr }, + { 017777514, 017777517, 0, &lpt_rd, &lpt_wr }, + { 017777400, 017777417, INT_RK, &rk_rd, &rk_wr }, +/* { 017777440, 017777477, INT_HK, &hk_rd, &hk_wr }, */ + { 017774400, 017774411, INT_RL, &rl_rd, &rl_wr }, + { 017776700, 017776753, INT_RP, &rp_rd, &rp_wr }, + { 017777170, 017777173, INT_RX, &rx_rd, &rx_wr }, + { 017777340, 017777351, INT_DTA, &dt_rd, &dt_wr }, + { 017772520, 017772533, INT_TM, &tm_rd, &tm_wr }, + { 017772520, 017772523, INT_TS, &ts_rd, &ts_wr }, + { 017777600, 017777677, 0, &APR_rd, &APR_wr }, + { 017772200, 017772377, 0, &APR_rd, &APR_wr }, + { 017777570, 017777577, 0, &SR_MMR012_rd, &SR_MMR012_wr }, + { 017772516, 017772517, 0, &MMR3_rd, &MMR3_wr }, + { 0, 0, 0, NULL, 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 }; + 0, 0, 0, VEC_PIRQ, VEC_CLK, VEC_DTA, 0, VEC_PIRQ, + VEC_RK, VEC_RL, VEC_RX, VEC_TM, VEC_RP, VEC_TS, VEC_HK, 0, + 0, 0, 0, VEC_PIRQ, VEC_TTI, VEC_TTO, VEC_PTR, VEC_PTP, + VEC_LPT, 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, @@ -322,7 +340,7 @@ int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */ VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC, VEC_YEL, VEC_PWRFL, VEC_FPE }; -int32 trap_clear[TRAP_V_MAX] = { /* trap clears */ +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, @@ -505,6 +523,7 @@ REG cpu_reg[] = { { ORDATA (OLDPC, old_PC, 16), REG_RO }, { ORDATA (BREAK, ibkpt_addr, 17) }, { ORDATA (WRU, sim_int_char, 8) }, + { ORDATA (DEVENB, dev_enb, 32), REG_HRO }, { NULL} }; MTAB cpu_mod[] = { @@ -530,7 +549,7 @@ MTAB cpu_mod[] = { { 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}, + { UNIT_MSIZE, 4186112, NULL, "4M", &cpu_set_size}, { 0 } }; DEVICE cpu_dev = { @@ -543,6 +562,8 @@ t_stat sim_instr (void) { extern int32 sim_interval; extern UNIT *sim_clock_queue; +extern UNIT clk_unit; +extern int32 sim_rtc_init (int32 time); register int32 IR, srcspec, srcreg, dstspec, dstreg; register int32 src, src2, dst; register int32 i, t, sign, oldrs, trapnum; @@ -582,6 +603,7 @@ CPU_wr (PIRQ, 017777772, WRITE); /* rewrite PIRQ */ trap_req = calc_ints (ipl, int_req, trap_req); trapea = 0; reason = 0; +sim_rtc_init (clk_unit.wait); /* init clock */ /* Abort handling @@ -2043,7 +2065,8 @@ t_stat stat; struct iolink *p; for (p = &iotable[0]; p -> low != 0; p++ ) { - if ((pa >= p -> low) && (pa <= p -> high)) { + if ((pa >= p -> low) && (pa <= p -> high) && + ((p -> enb == 0) || (dev_enb & p -> enb))) { stat = p -> read (data, pa, access); trap_req = calc_ints (ipl, int_req, trap_req); return stat; } } @@ -2056,7 +2079,8 @@ t_stat stat; struct iolink *p; for (p = &iotable[0]; p -> low != 0; p++ ) { - if ((pa >= p -> low) && (pa <= p -> high)) { + if ((pa >= p -> low) && (pa <= p -> high) && + ((p -> enb == 0) || (dev_enb & p -> enb))) { stat = p -> write (data, pa, access); trap_req = calc_ints (ipl, int_req, trap_req); return stat; } } diff --git a/pdp11_defs.h b/pdp11_defs.h index f2fba2a0..f15e7ff1 100644 --- a/pdp11_defs.h +++ b/pdp11_defs.h @@ -1,6 +1,6 @@ /* pdp11_defs.h: PDP-11 simulator definitions - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -25,6 +25,10 @@ The author gratefully acknowledges the help of Max Burnet, Megan Gentry, and John Wilson in resolving questions about the PDP-11 + + 23-Apr-01 RMS Added RK611 support + 05-Apr-01 RMS Added TS11/TSV05 support + 10-Feb-01 RMS Added DECtape support */ #include "sim_defs.h" /* simulator defns */ @@ -40,6 +44,7 @@ #define MAXMEMSIZE 020000000 /* 2**22 */ #define MEMSIZE (cpu_unit.capac) #define ADDR_IS_MEM(x) (((t_addr) (x)) < MEMSIZE) +#define DMASK 0177777 /* Protection modes */ @@ -235,8 +240,8 @@ typedef struct fpac fpac_t; <3:0> = BR7, <3> = PIR7 <7:4> = BR6, <7> = PIR6 - <15:8> = BR5, <15> = PIR5 - <28:16> = BR4, <28> = PIR4 + <19:8> = BR5, <15> = PIR5 + <28:20> = BR4, <28> = PIR4 <29> = PIR3 <30> = PIR2 <31> = PIR1 @@ -244,18 +249,21 @@ typedef struct fpac fpac_t; #define INT_V_PIR7 3 #define INT_V_CLK 4 +#define INT_V_DTA 5 #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_TS 13 +#define INT_V_HK 14 +#define INT_V_PIR5 19 +#define INT_V_TTI 20 +#define INT_V_TTO 21 +#define INT_V_PTR 22 +#define INT_V_PTP 23 +#define INT_V_LPT 24 #define INT_V_PIR4 28 #define INT_V_PIR3 29 #define INT_V_PIR2 30 @@ -263,12 +271,15 @@ typedef struct fpac fpac_t; #define INT_PIR7 (1u << INT_V_PIR7) #define INT_CLK (1u << INT_V_CLK) +#define INT_DTA (1u << INT_V_DTA) #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_TS (1u << INT_V_TS) +#define INT_HK (1u << INT_V_HK) #define INT_PIR5 (1u << INT_V_PIR5) #define INT_PTR (1u << INT_V_PTR) #define INT_PTP (1u << INT_V_PTP) @@ -283,7 +294,7 @@ typedef struct fpac fpac_t; #define INT_IPL7 0x00000000 /* int level masks */ #define INT_IPL6 0x0000000F #define INT_IPL5 0x000000FF -#define INT_IPL4 0x0000FFFF +#define INT_IPL4 0x000FFFFF #define INT_IPL3 0x1FFFFFFF #define INT_IPL2 0x3FFFFFFF #define INT_IPL1 0x7FFFFFFF @@ -296,11 +307,14 @@ typedef struct fpac fpac_t; #define VEC_PTP 0074 #define VEC_CLK 0100 #define VEC_LPT 0200 +#define VEC_HK 0210 #define VEC_RK 0220 #define VEC_RL 0160 -#define VEC_RX 0264 +#define VEC_DTA 0214 #define VEC_TM 0224 +#define VEC_TS 0224 #define VEC_RP 0254 +#define VEC_RX 0264 /* CPU and FPU macros */ diff --git a/pdp11_fp.c b/pdp11_fp.c index 536b2072..bd0732dc 100644 --- a/pdp11_fp.c +++ b/pdp11_fp.c @@ -1,6 +1,6 @@ /* pdp11_fp.c: PDP-11 floating point simulator (32b version) - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -206,7 +206,7 @@ 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, +static const uint32 and_mask[33] = { 0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, diff --git a/pdp11_lp.c b/pdp11_lp.c index 5ca11485..2be16bd4 100644 --- a/pdp11_lp.c +++ b/pdp11_lp.c @@ -1,6 +1,6 @@ /* pdp11_lp.c: PDP-11 line printer simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,7 +23,7 @@ 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 + lpt LP11 line printer 30-Oct-00 RMS Standardized register naming */ @@ -40,8 +40,6 @@ 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 diff --git a/pdp11_rk.c b/pdp11_rk.c index c97e3c5a..9be8d898 100644 --- a/pdp11_rk.c +++ b/pdp11_rk.c @@ -1,6 +1,6 @@ -/* RK11 cartridge disk simulator +/* pdp11_rk.c: RK11 cartridge disk simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,11 @@ 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. + rk RK11/RK05 cartridge disk + + 26-Apr-01 RMS Added device enable/disable support + 25-Mar-01 RMS Fixed block fill calculation + 15-Feb-01 RMS Corrected bootstrap string 29-Jun-96 RMS Added unit disable support. The RK11 is an eight drive cartridge disk subsystem. An RK05 drive @@ -155,8 +160,8 @@ #define RK_MIN 10 #define MAX(x,y) (((x) > (y))? (x): (y)) -extern int32 int_req; -extern unsigned int16 *M; /* memory */ +extern uint16 *M; /* memory */ +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 rkcs = 0; /* control/status */ int32 rkds = 0; /* drive status */ @@ -175,11 +180,6 @@ 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 @@ -231,6 +231,7 @@ REG rk_reg[] = { { GRDATA (FLG7, rk_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, { FLDATA (STOP_IOE, rk_stopioe, 0) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RK), REG_HRO }, { NULL } }; MTAB rk_mod[] = { @@ -408,11 +409,11 @@ return; 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; +int32 pa, da, remc, track, sect; +static uint16 fill[RK_NUMWD] = { 0 }; drv = uptr - rk_dev.units; /* get drv number */ if (uptr -> FUNC == RKCS_SEEK) { /* seek */ @@ -451,8 +452,8 @@ if ((uptr -> FUNC == RKCS_READ) && (err == 0)) { /* read? */ 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); + if ((err == 0) && (remc = (wc & (RK_NUMWD - 1)))) { + fxwrite (fill, sizeof (int16), RK_NUMWD - remc, uptr -> fileref); err = ferror (uptr -> fileref); } } if ((uptr -> FUNC == RKCS_WCHK) && (err == 0)) { /* write check? */ @@ -570,7 +571,7 @@ static const int32 boot_rom[] = { 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0005004, /* CLR R4 */ - 0012705, 0062153, /* MOV #"DK, R5 */ + 0012705, 0045504, /* MOV #"DK, R5 */ 0105711, /* TSTB (R1) */ 0100376, /* BPL .-2 */ 0105011, /* CLRB (R1) */ diff --git a/pdp11_rl.c b/pdp11_rl.c index 8a1271d7..b07dd03b 100644 --- a/pdp11_rl.c +++ b/pdp11_rl.c @@ -1,6 +1,6 @@ -/* RL11 (RLV12) cartridge disk simulator +/* pdp11_rl.c: RL11 (RLV12) cartridge disk simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,11 @@ 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. + rl RL11(RLV12)/RL01/RL02 cartridge disk + + 26-Apr-01 RMS Added device enable/disable support + 25-Mar-01 RMS Fixed block fill calculation + 15-Feb-01 RMS Corrected bootstrap string 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. @@ -148,8 +153,8 @@ #define RLBAE_IMP 0000077 /* implemented */ -extern int32 int_req; -extern unsigned int16 *M; /* memory */ +extern uint16 *M; /* memory */ +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 rlcs = 0; /* control/status */ int32 rlba = 0; /* memory address */ @@ -166,11 +171,6 @@ 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 @@ -218,6 +218,7 @@ REG rl_reg[] = { { 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) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RL), REG_HRO }, { NULL } }; MTAB rl_mod[] = { @@ -306,7 +307,7 @@ case 0: /* RLCS */ switch (GET_FUNC (rlcs)) { /* case on RLCS<3:1> */ case RLCS_NOP: /* nop */ rl_set_done (0); - return SCPE_OK; + break; case RLCS_SEEK: /* seek */ curr = GET_CYL (uptr -> TRK); /* current cylinder */ offs = GET_CYL (rlda); /* offset */ @@ -320,10 +321,10 @@ case 0: /* RLCS */ 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; + break; default: /* data transfer */ sim_activate (uptr, rl_swait); /* activate unit */ - return SCPE_OK; } /* end switch func */ + break; } /* end switch func */ return SCPE_OK; /* end case RLCS */ case 1: /* RLBA */ @@ -357,11 +358,11 @@ case 4: /* RLBAE */ 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; +int32 func, pa, da, remc; +static uint16 fill[RL_NUMWD] = { 0 }; func = GET_FUNC (rlcs); /* get function */ if (func == RLCS_GSTA) { /* get status */ @@ -421,13 +422,13 @@ if ((func >= RLCS_READ) && (err == 0)) { /* read (no hdr)? */ 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); + if ((err == 0) && (remc = (wc & (RL_NUMWD - 1)))) { + fxwrite (fill, sizeof (int16), RL_NUMWD - remc, 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++) { + remc = wc; /* xfer length */ + for (wc = 0; (err == 0) && (wc < remc); 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; } @@ -558,7 +559,7 @@ static const int32 boot_rom[] = { 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0005004, /* CLR R4 */ - 0012705, 0062154, /* MOV "DL, R5 */ + 0012705, 0046104, /* MOV "DL, R5 */ 0005007 /* CLR PC */ }; diff --git a/pdp11_rp.c b/pdp11_rp.c index 327bb297..daa84cee 100644 --- a/pdp11_rp.c +++ b/pdp11_rp.c @@ -1,6 +1,6 @@ /* pdp11_rp.c - RP04/05/06/07 RM02/03/05/80 "Massbus style" disk controller - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,14 @@ 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 RH/RP/RM moving head disks + + 14-May-01 RMS Added check for unattached drive + 25-Apr-01 RMS Added device enable/disable support + 21-Apr-01 RMS Implemented UAI function + 02-Apr-01 RMS Fixed CS1.TRE error clear function, CS2.MXF/PE writes + 25-Mar-01 RMS Corrected block fill calculation, IE write processing + 15-Feb-01 RMS Corrected bootstrap string 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 @@ -50,6 +58,7 @@ #define RP_NUMDR 8 /* #drives */ #define RP_NUMWD 256 /* words/sector */ +#define RP_MAXFR 65536 /* max transfer */ #define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \ ((double) drv_tab[d].sect))) @@ -126,7 +135,7 @@ #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_UAI 0000010 /* addr inhibit */ #define CS2_PAT 0000020 /* parity test NI */ #define CS2_CLR 0000040 /* controller clear */ #define CS2_IR 0000100 /* input ready */ @@ -140,7 +149,7 @@ #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_RW (CS2_UNIT | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) #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) @@ -300,8 +309,8 @@ struct drvtyp drv_tab[] = { { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV }, { 0 } }; -extern int32 int_req; -extern unsigned int16 *M; /* memory */ +extern uint16 *M; /* memory */ +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 rpcs1 = 0; /* control/status 1 */ int32 rpwc = 0; /* word count */ @@ -336,11 +345,6 @@ 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 @@ -424,6 +428,7 @@ REG rp_reg[] = { { GRDATA (FLG7, rp_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, { FLDATA (STOP_IOE, rp_stopioe, 0) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RP), REG_HRO }, { NULL } }; MTAB rp_mod[] = { @@ -570,8 +575,9 @@ return SCPE_OK; t_stat rp_wr (int32 data, int32 PA, int32 access) { -int32 drv, i, j; +int32 cs1f, drv, i, j; +cs1f = 0; /* no int on cs1 upd */ 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 */ @@ -585,11 +591,14 @@ if (reg_in_drive[j] && sim_is_active (&rp_unit[drv])) { /* unit busy? */ 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; + if ((access == WRITEB) && (PA & 1)) data = data << 8; + else { if ((data & CS1_IE) == 0) int_req = int_req & ~INT_RP; + else if (data & CS1_DONE) int_req = int_req | INT_RP; } + if (data & CS1_TRE) { /* error clear? */ + rpcs1 = rpcs1 & ~CS1_TRE; /* clr CS1 */ + rpcs2 = rpcs2 & ~CS2_ERR; } /* clr CS2<15:8> */ + if (access == WRITEB) data = (rpcs1 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; 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); @@ -613,10 +622,13 @@ case 003: /* RPDA */ 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; + if ((access == WRITEB) && (PA & 1)) data = data << 8; + if (data & CS2_CLR) rp_reset (&rp_dev); /* init? */ + else { if ((data & ~rpcs2) & (CS2_PE | CS2_MXF)) + cs1f = CS1_SC; /* diagn intr */ + if (access == WRITEB) data = (rpcs2 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + rpcs2 = (rpcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; } drv = GET_UNIT (rpcs2); break; case 006: /* RPER1 */ @@ -624,7 +636,7 @@ case 006: /* RPER1 */ rper1[drv] = rper1[drv] & data; break; case 007: /* RPAS */ - if (PA & 1) break; + if ((access == WRITEB) && (PA & 1)) break; for (i = 0; i < RP_NUMDR; i++) if (data & (AS_U0 << i)) rpds[i] = rpds[i] & ~DS_ATA; break; @@ -649,12 +661,12 @@ case 016: /* RPDC */ rpdc = data & ~DC_MBZ; break; case 024: /* RPBAE */ - if (PA & 1) break; + if ((access == WRITEB) && (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; + if ((access == WRITEB) && (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)) @@ -674,7 +686,7 @@ case 023: /* RPEC2 */ default: /* all others */ rper1[drv] = rper1[drv] | ER1_ILR; break; } /* end switch */ -update_rpcs (0, drv); /* update status */ +update_rpcs (cs1f, drv); /* update status */ return SCPE_OK; } @@ -692,11 +704,16 @@ 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; } +if (fnc != FNC_DCLR) { /* not clear? */ + if ((rpds[drv] & DS_ERR) || /* error or */ + ((rpds[drv] & DS_RDY) == 0)) { /* not ready? */ + rpcs2 = rpcs2 | CS2_PGE; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return; } + if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */ + rper1[drv] = rper1[drv] | ER1_UNS; /* unsafe */ + 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 */ @@ -771,11 +788,11 @@ return; 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; +int32 i, dtype, drv, err; +int32 ba, wc, awc, twc, da, fc; +static uint16 dbuf[RP_MAXFR]; dtype = GET_DTYPE (uptr -> flags); /* get drive type */ drv = uptr - rp_dev.units; /* get drv number */ @@ -785,73 +802,71 @@ 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; + break; 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; + break; case FNC_UNLOAD: /* unload */ rp_detach (uptr); /* detach unit */ - return SCPE_OK; + break; 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; + break; 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; } + break; } case FNC_WCHK: /* write check */ case FNC_READ: /* read */ - pa = ((rpbae << 16) | rpba) >> 1; /* get mem addr */ + ba = (rpbae << 16) | rpba; /* get byte 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? */ + wc = 0200000 - rpwc; /* get true wc */ + + if ((da + wc) > 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_WRITE) { /* write? */ + for (twc = 0; twc < wc; twc++) { + if (!ADDR_IS_MEM (ba)) { /* nx memory? */ + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + break; } + dbuf[twc] = M[ba >> 1]; /* write to disk */ + if ((rpcs2 & CS2_UAI) == 0) ba = ba + 2; } + if (fc = twc & (RP_NUMWD - 1)) { /* fill? */ + fc = RP_NUMWD - fc; + for (i = 0; i < fc; i++) dbuf[twc + i] = 0; } + fxwrite (dbuf, sizeof (uint16), twc + fc, uptr -> fileref); + err = ferror (uptr -> fileref); + } /* end if */ + else { /* read, wchk */ + awc = fread (dbuf, sizeof (uint16), wc, uptr -> fileref); + err = ferror (uptr -> fileref); + for ( ; awc < wc; awc++) dbuf[awc] = 0; + for (twc = 0; twc < wc; twc++) { + if (!ADDR_IS_MEM (ba)) { /* nx memory? */ + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + break; } + if (uptr -> FUNC == FNC_READ) M[ba >> 1] = dbuf[twc]; + else if (M[ba >> 1] != dbuf[twc]) { + rpcs2 = rpcs2 | CS2_WCE; /* set error */ + break; } + if ((rpcs2 & CS2_UAI) == 0) ba = ba + 2; } + } /* end else */ - 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 */ + rpwc = (rpwc + twc) & 0177777; /* final word count */ + rpba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */ + rpbae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ rpcs1 = (rpcs1 & ~ CS1_UAE) | ((rpbae << CS1_V_UAE) & CS1_UAE); - da = da + wc + (RP_NUMWD - 1); + da = da + twc + (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; @@ -866,7 +881,7 @@ case FNC_READ: /* read */ clearerr (uptr -> fileref); return SCPE_IOERR; } update_rpcs (CS1_DONE, drv); /* set done */ - return SCPE_OK; } /* end case function */ + break; } /* end case function */ return SCPE_OK; } @@ -883,7 +898,7 @@ 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; +if (rper1[drv] | rper2 | rper3) rpds[drv] = rpds[drv] | DS_ERR | DS_ATA; else rpds[drv] = rpds[drv] & ~DS_ERR; rpcs1 = (rpcs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ)) | CS1_DVA | flag; @@ -1016,7 +1031,7 @@ static const int32 boot_rom[] = { 0005002, /* clr R2 */ 0005003, /* clr R3 */ 0005004, /* clr R4 */ - 0012705, 0042120, /* mov #"DP, r5 */ + 0012705, 0050104, /* mov #"DP, r5 */ 0105711, /* tstb (R1) */ 0100376, /* bpl .-2 */ 0105011, /* clrb (R1) */ diff --git a/pdp11_rx.c b/pdp11_rx.c index 10a46782..f064a342 100644 --- a/pdp11_rx.c +++ b/pdp11_rx.c @@ -1,6 +1,6 @@ /* pdp11_rx.c: RX11/RX01 floppy disk simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,8 +23,11 @@ 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 + rx RX11/RX01 floppy disk + 26-Apr-01 RMS Added device enable/disable support + 13-Apr-01 RMS Revised for register arrays + 15-Feb-01 RMS Corrected bootstrap string 14-Apr-99 RMS Changed t_addr to unsigned An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. @@ -84,7 +87,7 @@ #define TRACK u3 /* current track */ #define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY -extern int32 int_req; +extern int32 int_req, dev_enb; int32 rx_csr = 0; /* control/status */ int32 rx_dbr = 0; /* data buffer */ int32 rx_esr = 0; /* error status */ @@ -96,13 +99,11 @@ 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 */ +unsigned int8 rx_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 @@ -138,7 +139,8 @@ REG rx_reg[] = { { 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 }, + { BRDATA (SBUF, rx_buf, 8, 8, RX_NUMBY) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RX), REG_HRO }, { NULL } }; MTAB rx_mod[] = { @@ -250,10 +252,10 @@ case 1: /* RXDB */ 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 + FILL copy ir to rx_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 + copy rx_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 @@ -276,12 +278,12 @@ case IDLE: /* idle */ break; case EMPTY: /* empty buffer */ if (bptr >= RX_NUMBY) rx_done (rx_esr, 0); /* done all? */ - else { rx_dbr = buf[bptr]; /* get next */ + else { rx_dbr = rx_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 */ + rx_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 */ @@ -308,13 +310,13 @@ case RWDT: /* wait for track */ 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); } + rx_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]; + *(((int8 *) uptr -> filebuf) + da + i) = rx_buf[i]; da = da + RX_NUMBY; if (da > uptr -> hwmark) uptr -> hwmark = da; } rx_done (rx_esr, 0); /* done */ @@ -332,7 +334,7 @@ case INIT_COMPLETE: /* init complete */ 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_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 */ @@ -363,11 +365,12 @@ 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)); +if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ + rx_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); } +else rx_done (rx_esr | RXES_ID, 0010); /* no, error */ return SCPE_OK; } @@ -408,7 +411,7 @@ static const int32 boot_rom[] = { 0005002, /* CLR R2 */ 0005003, /* CLR R3 */ 0005004, /* CLR R4 */ - 0012705, 0062170, /* MOV #"DX, R5 */ + 0012705, 0054104, /* MOV #"DX, R5 */ 0005007 /* CLR R7 */ }; @@ -416,7 +419,7 @@ t_stat rx_boot (int32 unitno) { int32 i; extern int32 saved_PC; -extern unsigned short *M; +extern uint16 *M; for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; diff --git a/pdp11_stddev.c b/pdp11_stddev.c index 6a5bd7ec..2d96bb33 100644 --- a/pdp11_stddev.c +++ b/pdp11_stddev.c @@ -1,6 +1,6 @@ /* pdp11_stddev.c: PDP-11 standard I/O devices simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,12 +23,11 @@ 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 + ptr,ptp PC11 paper tape reader/punch + tti,tto DL11 terminal input/output + clk KW11L line frequency clock + 05-Mar-01 RMS Added clock calibration support 30-Oct-00 RMS Standardized register order 25-Jun-98 RMS Fixed bugs in paper tape error handling */ @@ -54,6 +53,7 @@ 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 */ +int32 clk_tps = 60; /* ticks/second */ t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr); @@ -68,10 +68,9 @@ 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); +extern int32 sim_rtc_calb (int32 ticksper); /* PTR data structures @@ -197,6 +196,7 @@ REG clk_reg[] = { { FLDATA (DONE, clk_csr, CSR_V_DONE) }, { FLDATA (IE, clk_csr, CSR_V_IE) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, { NULL } }; DEVICE clk_dev = { @@ -255,7 +255,7 @@ case 012: /* tto csr */ case 013: /* tto buf */ *data = tto_unit.buf; return SCPE_OK; } /* end switch PA */ -return SCPE_NXM; /* can't get here */ +return SCPE_NXM; } t_stat std_wr (int32 data, int32 PA, int32 access) @@ -265,7 +265,6 @@ 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; @@ -319,7 +318,7 @@ case 013: /* tto buf */ 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 */ +return SCPE_NXM; } /* Paper tape reader routines @@ -489,7 +488,7 @@ return SCPE_OK; 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 */ +sim_activate (&clk_unit, sim_rtc_calb (clk_tps)); /* reactivate unit */ return SCPE_OK; } diff --git a/pdp11_sys.c b/pdp11_sys.c index 22a0af23..6e329279 100644 --- a/pdp11_sys.c +++ b/pdp11_sys.c @@ -1,6 +1,6 @@ /* pdp11_sys.c: PDP-11 simulator interface - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,9 @@ 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-Apr-01 RMS Added support for TS11/TSV05 + 14-Mar-01 RMS Revised load/dump interface (again) + 11-Feb-01 RMS Added DECtape support 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). @@ -41,10 +44,12 @@ 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 DEVICE dt_dev, tm_dev; +extern DEVICE ts_dev; +/* extern DEVICE hk_dev; */ extern UNIT cpu_unit; extern REG cpu_reg[]; -extern unsigned int16 *M; +extern uint16 *M; extern int32 saved_PC; /* SCP data structures and interface routines @@ -63,10 +68,16 @@ 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 }; +DEVICE *sim_devices[] = { + &cpu_dev, + &ptr_dev, &ptp_dev, + &tti_dev, &tto_dev, + &lpt_dev, &clk_dev, + &rk_dev, /* &hk_dev, */ + &rl_dev, &rp_dev, + &rx_dev, &dt_dev, + &tm_dev, &ts_dev, + NULL }; const char *sim_stop_messages[] = { "Unknown error", @@ -112,7 +123,7 @@ const char *sim_stop_messages[] = { the PC at which to start the program. */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 csum, count, state, i; t_addr origin; diff --git a/pdp11_tc.c b/pdp11_tc.c new file mode 100644 index 00000000..c4bd936c --- /dev/null +++ b/pdp11_tc.c @@ -0,0 +1,1138 @@ +/* pdp11_tc.c: PDP-11 DECtape simulator + + Copyright (c) 1993-2001, 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. + + tc TC11/TU56 DECtape + + 11-May-01 RMS Fixed bug in reset + 26-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 16-Mar-01 RMS Fixed bug in interrupt after stop + 15-Mar-01 RMS Added 129th word to PDP-8 format + + PDP-11 DECtapes are represented by fixed length data blocks of 18b words. Two + tape formats are supported: + + 16b/18b/36b 256 words per block + 12b 86 words per block [129 x 12b] + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape is + + reverse end zone 36000 lines ~ 10 feet + block 0 + : + block n + forward end zone 36000 lines ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 0 + : + trailer word 4 checksum + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. +*/ + +#include "pdp11_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define DT_M_NUMDR (DT_NUMDR - 1) +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK 1 << UNIT_V_WLK +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_W_UF 3 /* saved flag width */ +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ + +/* System independent DECtape constants */ + +#define DT_EZLIN 36000 /* end zone length */ +#define DT_HTLIN 30 /* header/trailer lines */ +#define DT_BLKLN 6 /* blk no line in h/t */ +#define DT_CSMLN 24 /* checksum line in h/t */ +#define DT_HTWRD (DT_HTLIN / DT_WSIZE) /* header/trailer words */ +#define DT_BLKWD (DT_BLKLN / DT_WSIZE) /* blk no word in h/t */ +#define DT_CSMWD (DT_CSMLN / DT_WSIZE) /* checksum word in h/t */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u) -> flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u) -> flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u) -> flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u) -> flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u) -> flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u) -> pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u) -> pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* TCST - 177340 - status register */ + +#define STA_END 0100000 /* end zone */ +#define STA_PAR 0040000 /* parity err */ +#define STA_MRK 0020000 /* mark trk err */ +#define STA_ILO 0010000 /* illegal op */ +#define STA_SEL 0004000 /* select err */ +#define STA_BLKM 0002000 /* block miss err */ +#define STA_DATM 0001000 /* data miss err */ +#define STA_NXM 0000400 /* nx mem err */ +#define STA_UPS 0000200 /* up to speed */ +#define STA_V_XD 0 /* extended data */ +#define STA_M_XD 03 +#define STA_ALLERR (STA_END | STA_PAR | STA_MRK | STA_ILO | \ + STA_SEL | STA_BLKM | STA_DATM | STA_NXM ) +#define STA_RWERR (STA_END | STA_PAR | STA_MRK | \ + STA_BLKM | STA_DATM | STA_NXM ) +#define STA_RW 0000003 +#define STA_GETXD(x) (((x) >> STA_V_XD) & STA_M_XD) + +/* TCCM - 177342 - command register */ + +/* #define CSR_ERR 0100000 */ +#define CSR_MNT 0020000 /* maint (unimpl) */ +#define CSR_INH 0010000 /* delay inhibit */ +#define CSR_DIR 0004000 /* reverse */ +#define CSR_V_UNIT 8 /* unit select */ +#define CSR_M_UNIT 07 +#define CSR_UNIT (CSR_M_UNIT << CSR_V_UNIT) +/* #define CSR_DONE 0000200 */ +/* #define CSR_IE 0000100 */ +#define CSR_V_MEX 4 /* mem extension */ +#define CSR_M_MEX 03 +#define CSR_MEX (CSR_M_MEX << CSR_V_MEX) +#define CSR_V_FNC 1 /* function */ +#define CSR_M_FNC 07 +#define FNC_STOP 00 /* stop all */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_SSEL 04 /* stop selected */ +#define FNC_WMRK 05 /* write */ +#define FNC_WRIT 06 /* write all */ +#define FNC_WALL 07 /* write timing */ +/* define CSR_GO 0000001 */ +#define CSR_RW 0117576 /* read/write */ + +#define CSR_GETUNIT(x) (((x) >> CSR_V_UNIT) & CSR_M_UNIT) +#define CSR_GETMEX(x) (((x) >> CSR_V_MEX) & CSR_M_MEX) +#define CSR_GETFNC(x) (((x) >> CSR_V_FNC) & CSR_M_FNC) +#define CSR_INCMEX(x) (((x) & ~CSR_MEX) | (((x) + (1 << CSR_V_MEX)) & CSR_MEX)) + +/* TCWC - 177344 - word count */ + +/* TCBA - 177346 - bus address */ + +/* TCDT - 177350 - data */ + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR FNC_WMRK /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr -> STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr -> STATE = (uptr -> STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr -> STATE = (uptr -> STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read, write */ +#define LOG_RA 004 /* read all */ +#define LOG_BL 010 /* block # lblk */ + +#define DT_SETDONE tccm = tccm | CSR_DONE; \ + if (tccm & CSR_IE) int_req = int_req | INT_DTA +#define DT_CLRDONE tccm = tccm & ~CSR_DONE; \ + int_req = int_req & ~INT_DTA +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern uint16 *M; /* memory */ +extern int32 int_req, dev_enb; +extern UNIT cpu_unit; +extern int32 sim_switches; +int32 tcst = 0; /* status */ +int32 tccm = 0; /* command */ +int32 tcwc = 0; /* word count */ +int32 tcba = 0; /* bus address */ +int32 tcdt = 0; /* data */ +int32 dt_ctime = 4; /* fast cmd time */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_actime = 54000; /* accel time */ +int32 dt_dctime = 72000; /* decel time */ +int32 dt_substate = 0; +int32 dt_log = 0; +int32 dt_logblk = 0; +t_stat dt_svc (UNIT *uptr); +t_stat dt_svcdone (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +t_stat dt_boot (int32 unitno); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +void dt_stopunit (UNIT *uptr); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); +extern uint32 sim_grtime (void); +extern int32 sim_is_running; + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svcdone, UNIT_DIS, 0) } }; + +#define DT_TIMER (DT_NUMDR) + +REG dt_reg[] = { + { ORDATA (TCST, tcst, 16) }, + { ORDATA (TCCM, tccm, 16) }, + { ORDATA (TCWC, tcwc, 16) }, + { ORDATA (TCBA, tcba, 16) }, + { ORDATA (TCDT, tcdt, 16) }, + { FLDATA (INT, int_req, INT_V_DTA) }, + { FLDATA (ERR, tccm, CSR_V_ERR) }, + { FLDATA (DONE, tccm, CSR_V_DONE) }, + { FLDATA (IE, tccm, CSR_V_DONE) }, + { DRDATA (CTIME, dt_ctime, 31), REG_NZ }, + { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, + { DRDATA (ACTIME, dt_actime, 31), REG_NZ }, + { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, + { ORDATA (SUBSTATE, dt_substate, 1) }, + { ORDATA (LOG, dt_log, 4), REG_HIDDEN }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { DRDATA (POS0, dt_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, dt_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, dt_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, dt_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, dt_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, dt_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, dt_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, dt_unit[7].pos, 31), PV_LEFT + REG_RO }, + { ORDATA (STATT0, dt_unit[0].STATE, 18), REG_RO }, + { ORDATA (STATT1, dt_unit[1].STATE, 18), REG_RO }, + { ORDATA (STATT2, dt_unit[2].STATE, 18), REG_RO }, + { ORDATA (STATT3, dt_unit[3].STATE, 18), REG_RO }, + { ORDATA (STATT4, dt_unit[4].STATE, 18), REG_RO }, + { ORDATA (STATT5, dt_unit[5].STATE, 18), REG_RO }, + { ORDATA (STATT6, dt_unit[6].STATE, 18), REG_RO }, + { ORDATA (STATT7, dt_unit[7].STATE, 18), REG_RO }, + { DRDATA (LASTT0, dt_unit[0].LASTT, 32), REG_HRO }, + { DRDATA (LASTT1, dt_unit[1].LASTT, 32), REG_HRO }, + { DRDATA (LASTT2, dt_unit[2].LASTT, 32), REG_HRO }, + { DRDATA (LASTT3, dt_unit[3].LASTT, 32), REG_HRO }, + { DRDATA (LASTT4, dt_unit[4].LASTT, 32), REG_HRO }, + { DRDATA (LASTT5, dt_unit[5].LASTT, 32), REG_HRO }, + { DRDATA (LASTT6, dt_unit[6].LASTT, 32), REG_HRO }, + { DRDATA (LASTT7, dt_unit[7].LASTT, 32), REG_HRO }, + { GRDATA (FLG0, dt_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, dt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, dt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, dt_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, dt_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, dt_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, dt_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, dt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_DTA), REG_HRO }, + { NULL } }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT, 0, "16b/18b", NULL, NULL }, + { UNIT_8FMT, UNIT_8FMT, "12b", NULL, NULL }, + { 0 } }; + +DEVICE dt_dev = { + "TC", dt_unit, dt_reg, dt_mod, + DT_NUMDR + 1, 8, 24, 1, 8, 18, + NULL, NULL, &dt_reset, + &dt_boot, &dt_attach, &dt_detach }; + +/* IO dispatch routines, I/O addresses 17777340 - 17777350 */ + +t_stat dt_rd (int32 *data, int32 PA, int32 access) +{ +int32 j, unum, mot, fnc; + +j = (PA >> 1) & 017; /* get reg offset */ +unum = CSR_GETUNIT (tccm); /* get drive */ +switch (j) { +case 000: /* TCST */ + mot = DTS_GETMOT (dt_unit[unum].STATE); /* get motion */ + if (mot >= DTS_ATSF) tcst = tcst | STA_UPS; /* set/clr speed */ + else tcst = tcst & ~STA_UPS; + *data = tcst; + break; +case 001: /* TCCM */ + if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR; /* set/clr error */ + else tccm = tccm & ~CSR_ERR; + *data = tccm; + break; +case 002: /* TCWC */ + *data = tcwc; + break; +case 003: /* TCBA */ + *data = tcba; + break; +case 004: /* TCDT */ + fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */ + if (fnc == FNC_RALL) { /* read all? */ + DT_CLRDONE; } /* clear done */ + *data = tcdt; + break; } +return SCPE_OK; +} + +t_stat dt_wr (int32 data, int32 PA, int32 access) +{ +int32 i, j, unum, old_tccm, fnc; +UNIT *uptr; + +j = (PA >> 1) & 017; /* get reg offset */ +switch (j) { +case 000: /* TCST */ + if ((access == WRITEB) && (PA & 1)) break; + tcst = (tcst & ~STA_RW) | (data & STA_RW); + break; +case 001: /* TCCM */ + old_tccm = tccm; /* save prior */ + if (access == WRITEB) data = (PA & 1)? + (tccm & 0377) | (data << 8): (tccm & ~0377) | data; + if ((data & CSR_IE) == 0) int_req = int_req & ~INT_DTA; + else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) || + (data & CSR_DONE)) int_req = int_req | INT_DTA; + tccm = (tccm & ~CSR_RW) | (data & CSR_RW); + if ((data & CSR_GO) && (tccm & CSR_DONE)) { /* new cmd? */ + tcst = tcst & ~STA_ALLERR; /* clear errors */ + tccm = tccm & ~(CSR_ERR | CSR_DONE); /* clear done, err */ + int_req = int_req & ~INT_DTA; /* clear int */ + if ((old_tccm ^ tccm) & CSR_UNIT) dt_deselect (old_tccm); + unum = CSR_GETUNIT (tccm); /* get drive */ + fnc = CSR_GETFNC (tccm); /* get function */ + if (fnc == FNC_STOP) { /* stop all? */ + sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); + for (i = 0; i < DT_NUMDR; i++) + dt_stopunit (dt_dev.units + i); /* stop unit */ + break; } + uptr = dt_dev.units + unum; + if (uptr -> flags & UNIT_DIS) /* disabled? */ + dt_seterr (uptr, STA_SEL); /* select err */ + if ((fnc == FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WALL) && (uptr -> flags & UNIT_WLK)) || + ((fnc == FNC_WRIT) && (uptr -> flags & UNIT_WLK))) + dt_seterr (uptr, STA_ILO); /* illegal op */ + if (!(tccm & CSR_ERR)) dt_newsa (tccm); } + else if ((tccm & CSR_ERR) == 0) { /* clear err? */ + tcst = tcst & ~STA_RWERR; + if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR; } + break; +case 002: /* TCWC */ + tcwc = data; /* word write only! */ + break; +case 003: /* TCBA */ + tcba = data; /* word write only! */ + break; +case 004: /* TCDT */ + unum = CSR_GETUNIT (tccm); /* get drive */ + fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */ + if (fnc == FNC_WALL) { /* write all? */ + DT_CLRDONE; } /* clear done */ + tcdt = data; /* word write only! */ + break; } +return SCPE_OK; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit = CSR_GETUNIT (oldf); +UNIT *uptr = dt_dev.units + old_unit; +int32 old_mot = DTS_GETMOT (uptr -> STATE); + +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; } + +/* New operation + + 1. If function = stop + - if not already stopped or decelerating, schedule deceleration + - schedule command completion + 2. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 3. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 4. If not yet at speed, + - set function as next state + 5. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, prev_fnc, new_fnc; +int32 prev_dir, new_dir; +UNIT *uptr; + +new_unit = CSR_GETUNIT (newf); /* new, old units */ +uptr = dt_dev.units + new_unit; +if ((uptr -> flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, STA_SEL); /* no, error */ + return; } +prev_mot = DTS_GETMOT (uptr -> STATE); /* previous motion */ +prev_fnc = DTS_GETFNC (uptr -> STATE); /* prev function */ +prev_dir = prev_mot & DTS_DIR; /* previous dir */ +new_fnc = CSR_GETFNC (newf); /* new function */ +new_dir = (newf & CSR_DIR) != 0; /* new di? */ + +if (new_fnc == FNC_SSEL) { /* stop unit? */ + sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */ + dt_stopunit (uptr); /* stop unit */ + return; } + +if (prev_mot == DTS_STOP) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_actime); /* schedule accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +if (prev_dir ^ new_dir) { /* dir chg? */ + dt_stopunit (uptr); /* stop unit */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_actime); /* schedule accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, relpos, newpos; +uint32 oldpos; + +oldpos = uptr -> pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr -> STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr -> STATE); /* set variables */ +dir = DTS_GETMOT (uptr -> STATE) & DTS_DIR; +unum = uptr - dt_dev.units; +if (oldpos == uptr -> pos) + uptr -> pos = uptr -> pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr -> pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, STA_END); /* set ez flag, stop */ + return; } +dt_substate = 0; /* substate = normal */ +sim_cancel (uptr); /* cancel cur op */ +switch (fnc) { /* case function */ +case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; +case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (dt_log & LOG_MS) printf ("[DT%d: searching %s]\n", unum, + (dir? "backward": "forward")); + break; +case FNC_WRIT: /* write */ +case FNC_READ: /* read */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, STA_BLKM); + return; } + if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + if ((dt_log & LOG_RW) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: %s block %d %s]\n", + unum, ((fnc == FNC_READ)? "read": "write"), + blk, (dir? "backward": "forward")); + break; +case FNC_RALL: /* read all */ +case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; + else newpos = DT_EZLIN + (DT_WSIZE - 1); } + else { relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + if (dir? (relpos < (DTU_LPERB (uptr) - DT_CSMLN)): /* switch in time? */ + (relpos >= DT_CSMLN)) { + dt_seterr (uptr, STA_BLKM); + return; } + if (dir) newpos = DT_BLK2LN (blk + 1, uptr) - DT_CSMLN - DT_WSIZE; + else newpos = DT_BLK2LN (blk, uptr) + DT_CSMLN + (DT_WSIZE - 1); } + if (fnc == FNC_WALL) sim_activate /* write all? */ + (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */ + if ((dt_log & LOG_RA) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: read all block %d %s]\n", + unum, blk, (dir? "backward": "forward")); + break; +default: + dt_seterr (uptr, STA_SEL); /* bad state */ + return; } +sim_activate (uptr, ABS (newpos - ((int32) uptr -> pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr -> LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr -> LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ +case DTS_STOP: /* stop */ + delta = 0; + break; +case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; +case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; udelt = dt_actime / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; +case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; } +if (mot & DTS_DIR) uptr -> pos = uptr -> pos - delta; /* update pos */ +else uptr -> pos = uptr -> pos + delta; +if ((uptr -> pos < 0) || + (uptr -> pos > ((uint32) (DTU_FWDEZ (uptr) + DT_EZLIN)))) { + detach_unit (uptr); /* off reel? */ + uptr -> STATE = uptr -> pos = 0; + unum = uptr - dt_dev.units; + if ((unum == CSR_GETUNIT (tccm)) && (CSR_GETFNC (tccm) != FNC_STOP)) + dt_seterr (uptr, STA_SEL); /* error */ + return TRUE; } +return FALSE; +} + +/* Command timer service after stop - set done but not interrupt */ + +t_stat dt_svcdone (UNIT *uptr) +{ +tccm = tccm | CSR_DONE; +return SCPE_OK; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr -> STATE); +int32 *bptr = uptr -> filebuf; +int32 unum = uptr - dt_dev.units; +int32 blk, wrd, relpos, dat; +t_addr ma, ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { +case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) return SCPE_OK; /* update pos */ + uptr -> STATE = DTS_NXTSTA (uptr -> STATE); /* advance state */ + if (uptr -> STATE) /* not stopped? */ + sim_activate (uptr, dt_actime); /* must be reversing */ + return SCPE_OK; +case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr -> STATE)); /* adv state, sched */ + return SCPE_OK; +case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ +default: /* other */ + dt_seterr (uptr, STA_SEL); /* state error */ + return SCPE_OK; } + +/* Functional cases + + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) return SCPE_OK; /* update pos */ +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, STA_END); /* end zone error */ + return SCPE_OK; } +blk = DT_LIN2BL (uptr -> pos, uptr); /* get block # */ + +switch (fnc) { /* at speed, check fnc */ +case FNC_SRCH: /* search */ + tcdt = blk; /* set block # */ + dt_schedez (uptr, dir); /* sched end zone */ + DT_SETDONE; /* set done */ + break; +case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr -> STATE = uptr -> pos = 0; /* no visible action */ + break; + +/* Read + + If wc ovf has not occurred, inc ma, wc and copy word from tape to memory + If wc ovf, set flag + If not end of block, schedule next word + If end of block and not wc ovf, schedule next block + If end of block and wc ovf, set done, schedule end zone +*/ + +case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr -> pos, uptr); /* get word # */ + if (!dt_substate) { /* !wc ovf? */ + tcwc = tcwc & DMASK; /* incr MA, WC */ + tcba = tcba & DMASK; + ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */ + if (ma >= MEMSIZE) { /* nx mem? */ + dt_seterr (uptr, STA_NXM); + break; } + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + M[ma >> 1] = tcdt = bptr[ba] & DMASK; /* read word */ + tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */ + tcba = (tcba + 2) & DMASK; + if (tcba <= 1) tccm = CSR_INCMEX (tccm); + if (tcwc == 0) dt_substate = 1; } + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else if (dt_substate) { /* wc ovf? */ + dt_schedez (uptr, dir); /* sched end zone */ + DT_SETDONE; } /* set done */ + else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + break; + +/* Write + + If wc ovf has not occurred, inc ma, wc + Copy word from memory (or 0, to fill block) to tape + If wc ovf, set flag + If not end of block, schedule next word + If end of block and not wc ovf, schedule next block + If end of block and wc ovf, set done, schedule end zone +*/ + +case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr -> pos, uptr); /* get word # */ + if (dt_substate) tcdt = 0; /* wc ovf? fill */ + else { ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */ + if (ma >= MEMSIZE) { /* nx mem? */ + dt_seterr (uptr, STA_NXM); + break; } + else tcdt = M[ma >> 1]; /* get word */ + tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */ + tcba = (tcba + 2) & DMASK; + if (tcba <= 1) tccm = CSR_INCMEX (tccm); } + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + bptr[ba] = tcdt; /* write word */ + if (ba >= uptr -> hwmark) uptr -> hwmark = ba + 1; + if (tcwc == 0) dt_substate = 1; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else if (dt_substate) { /* wc ovf? */ + dt_schedez (uptr, dir); /* sched end zone */ + DT_SETDONE; } + else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + break; + +/* Read all - read current header or data word */ + +case FNC_RALL: + if (tccm & CSR_DONE) { /* done set? */ + dt_seterr (uptr, STA_DATM); /* data miss */ + break; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr -> pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = bptr[ba]; } /* get tape word */ + else dat = dt_gethdr (uptr, blk, relpos); /* get hdr */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + tcdt = dat & DMASK; /* low 16b */ + tcst = (tcst & ~STA_M_XD) | ((dat >> 16) & STA_M_XD); + sim_activate (uptr, DT_WSIZE * dt_ltime); + DT_SETDONE; /* set done */ + break; + +/* Write all - write current header or data word */ + +case FNC_WALL: + if (tccm & CSR_DONE) { /* done set? */ + dt_seterr (uptr, STA_DATM); /* data miss */ + break; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr -> pos, uptr); + dat = (STA_GETXD (tcst) << 16) | tcdt; /* get data word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + bptr[ba] = dat; /* write word */ + if (ba >= uptr -> hwmark) uptr -> hwmark = ba + 1; } +/* else /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + DT_SETDONE; /* set done */ + break; +default: + dt_seterr (uptr, STA_SEL); /* impossible state */ + break; } +return SCPE_OK; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); + +tcst = tcst | e; /* set error flag */ +tccm = tccm | CSR_ERR; +if (!(tccm & CSR_DONE)) { /* not done? */ + DT_SETDONE; } +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); } /* state = decel */ +return; +} + +/* Stop unit */ + +void dt_stopunit (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 dir = mot & DTS_DIR; + +if (mot == DTS_STOP) return; /* already stopped? */ +if ((mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); } /* schedule decel */ +DTS_SETSTA (DTS_DECF | dir, 0); /* state = decel */ +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr -> pos)) * dt_ltime); +return; +} + +/* Complement obverse routine (18b) */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 0777777; /* compl obverse */ +dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | + ((dat >> 3) & 0700) | ((dat & 0700) << 3) | + ((dat & 070) << 9) | ((dat & 07) << 15); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int32 *bptr = uptr -> filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = bptr[ba + i] ^ 0777777; /* get ~word */ + csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd; } +return (csum & 077); +} + +/* Get header word (18b) */ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) +{ +int32 wrd = relpos / DT_WSIZE; + +if (wrd == DT_BLKWD) return blk; /* fwd blknum */ +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ + return (dt_csum (uptr, blk) << 12); +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ + return dt_comobv (blk); +return 0; /* all others */ +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* RESET? */ + prev_mot = DTS_GETMOT (uptr -> STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } } + else { sim_cancel (uptr); /* sim reset */ + uptr -> STATE = 0; + uptr -> LASTT = sim_grtime (); } } +tcst = tcwc = tcba = tcdt = 0; /* clear reg */ +tccm = CSR_DONE; +int_req = int_req & ~INT_DTA; /* clear int req */ +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 */ + 0012701, 0177342, /* MOV #TCCM, R1 ; csr */ + 0012702, 0004003, /* RW: MOV #4003, R2 ; rev+rnum+go */ + 0050302, /* BIS R3, R2 */ + 0010211, /* MOV R2, (R1) ; load csr */ + 0032711, 0100200, /* BIT #100200, (R1) ; wait */ + 0001775, /* BEQ .-4 */ + 0100370, /* BPL RW ; no err, cont */ + 0005737, 0177340, /* TST TCST ; end zone? */ + 0100036, /* BPL ER ; no, err */ + 0012702, 0000003, /* MOV #3, R2 ; rnum+go */ + 0050302, /* BIS R3, R2 */ + 0010211, /* MOV R2, (R1) ; load csr */ + 0032711, 0100200, /* BIT #100200, (R1) ; wait */ + 0001775, /* BEQ .-4 */ + 0100426, /* BMI ER ; err, die */ + 0005737, 0177350, /* TST TCDT ; blk 0? */ + 0001023, /* BNE ER ; no, die */ + 0012737, 0177000, 0177344, /* MOV #-256.*2, TCWC ; load wc */ + 0005037, 0177346, /* CLR TCBA ; clear ba */ + 0012702, 0000005, /* MOV #READ+GO, R2 ; read & go */ + 0050302, /* BIS R3, R2 */ + 0010211, /* MOV R2, (R1) ; load csr */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0005004, /* CLR R4 */ + 0012705, 0052104, /* MOV #"DT, R5 */ + 0032711, 0100200, /* BIT #100200, (R1) ; wait */ + 0001775, /* BEQ .-4 */ + 0100401, /* BMI ER ; err, die */ + 0005007, /* CLR PC */ + 0012711, 0000001, /* ER: MOV #1, (R1) ; stop all */ + 0000000 /* HALT */ +}; + +t_stat dt_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +dt_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & DT_M_NUMDR; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Attach routine + + Determine native or PDP8 format + Allocate buffer + If PDP8, read 12b format and convert to 18b in buffer + If native, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint16 pdp8b[D8_NBSIZE]; +int32 k, p, *bptr; +t_stat r; +t_addr ba; + +uptr -> flags = uptr -> flags & ~UNIT_8FMT; +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if (sim_switches & SWMASK ('F')) /* att foreign? */ + uptr -> flags = uptr -> flags | UNIT_8FMT; /* PDP8 = T */ +else if (!(sim_switches & SWMASK ('N'))) { /* autosize? */ + if ((fseek (uptr -> fileref, 0, SEEK_END) == 0) && + (p = ftell (uptr -> fileref)) && + (p == D8_FILSIZ)) uptr -> flags = uptr -> flags | UNIT_8FMT; } +uptr -> capac = DTU_CAPAC (uptr); /* set capacity */ +uptr -> filebuf = calloc (uptr -> capac, sizeof (int32)); +if (uptr -> filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; } +printf ("%TC: buffering file in memory\n"); +rewind (uptr -> fileref); /* start of file */ +if (uptr -> flags & UNIT_8FMT) { /* PDP-8? */ + bptr = uptr -> filebuf; /* file buffer */ + for (ba = 0; ba < uptr -> capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (int16), D8_NBSIZE, uptr -> fileref); + if (k == 0) break; + for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + bptr[ba] = ((pdp8b[k] & 07777) << 6) | + ((pdp8b[k + 1] >> 6) & 077); + bptr[ba + 1] = ((pdp8b[k + 1] & 077) << 12) | + (pdp8b[k + 2] & 07777); + ba = ba + 2; } /* end blk loop */ + } /* end file loop */ + uptr -> hwmark = ba; } /* end if */ +else uptr -> hwmark = fxread (uptr -> filebuf, sizeof (int32), + uptr -> capac, uptr -> fileref); +uptr -> flags = uptr -> flags | UNIT_BUF; /* set buf flag */ +uptr -> pos = DT_EZLIN; /* beyond leader */ +uptr -> LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If PDP8, convert 18b buffer to 12b and write to file + If native, write buffer to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint16 pdp8b[D8_NBSIZE]; +int32 k, *bptr; +int32 unum = uptr - dt_dev.units; +t_addr ba; + +if (!(uptr -> flags & UNIT_ATT)) return SCPE_OK; +if (sim_is_active (uptr)) { /* active? cancel op */ + sim_cancel (uptr); + if ((unum == CSR_GETUNIT (tccm)) && ((tccm & CSR_DONE) == 0)) { + tcst = tcst | STA_SEL; + tccm = tccm | CSR_ERR | CSR_DONE; + if (tccm & CSR_IE) int_req = int_req | INT_DTA; } + uptr -> STATE = uptr -> pos = 0; } +if (uptr -> hwmark) { /* any data? */ + printf ("TC: writing buffer to file\n"); + rewind (uptr -> fileref); /* start of file */ + if (uptr -> flags & UNIT_8FMT) { /* PDP8? */ + bptr = uptr -> filebuf; /* file buffer */ + for (ba = 0; ba < uptr -> hwmark; ) { /* loop thru buf */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (bptr[ba] >> 6) & 07777; + pdp8b[k + 1] = ((bptr[ba] & 077) << 6) | + ((bptr[ba + 1] >> 12) & 077); + pdp8b[k + 2] = bptr[ba + 1] & 07777; + ba = ba + 2; } /* end loop blk */ + fxwrite (pdp8b, sizeof (int16), D8_NBSIZE, uptr -> fileref); + if (ferror (uptr -> fileref)) break; } /* end loop file */ + } /* end if PDP8 */ + else fxwrite (uptr -> filebuf, sizeof (int32), /* write file */ + uptr -> hwmark, uptr -> fileref); + if (ferror (uptr -> fileref)) perror ("I/O error"); } /* end if hwmark */ +free (uptr -> filebuf); /* release buf */ +uptr -> flags = uptr -> flags & ~UNIT_BUF; /* clear buf flag */ +uptr -> filebuf = NULL; /* clear buf ptr */ +uptr -> flags = uptr -> flags & ~UNIT_8FMT; /* default fmt */ +uptr -> capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/pdp11_tm.c b/pdp11_tm.c index 2739c70b..804a6046 100644 --- a/pdp11_tm.c +++ b/pdp11_tm.c @@ -1,6 +1,6 @@ -/* PDP-11 magnetic tape simulator +/* pdp11_tm.c: PDP-11 magnetic tape simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -25,6 +25,8 @@ tm TM11/TU10 magtape + 26-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot 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) @@ -123,8 +125,8 @@ STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM) /* set error */ -extern unsigned int16 *M; /* memory */ -extern int32 int_req; +extern uint16 *M; /* memory */ +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 tm_sta = 0; /* status register */ int32 tm_cmd = 0; /* command register */ @@ -142,11 +144,6 @@ 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 @@ -210,6 +207,7 @@ REG tm_reg[] = { REG_HRO }, { GRDATA (FLG7, tm_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_TM), REG_HRO }, { NULL } }; MTAB tm_mod[] = { @@ -340,12 +338,12 @@ return; t_stat tm_svc (UNIT *uptr) { -int f, i, p, err; +int32 f, i, p, err; t_addr xma; t_stat rval; t_mtrlnt tbc, cbc; -unsigned int16 c; -unsigned int8 dbuf[DBSIZE]; +uint16 c; +static uint8 dbuf[DBSIZE]; static t_mtrlnt bceof = { 0 }; if (uptr -> USTAT & STA_REW) { /* rewind? */ @@ -523,6 +521,7 @@ t_stat tm_reset (DEVICE *dptr) int32 u; UNIT *uptr; +if (dev_enb & INT_TM) dev_enb = dev_enb & ~INT_TS; /* TM or TS */ tm_cmd = MTC_DONE; /* set done */ tm_bc = tm_ca = tm_db = tm_sta = 0; int_req = int_req & ~INT_TM; /* clear interrupt */ @@ -635,6 +634,7 @@ int32 i; extern int32 saved_PC; extern int32 sim_switches; +tm_unit[unitno].pos = 0; if (sim_switches & SWMASK ('O')) { for (i = 0; i < BOOT1_LEN; i++) M[(BOOT_START >> 1) + i] = boot1_rom[i]; } diff --git a/pdp11_ts.c b/pdp11_ts.c new file mode 100644 index 00000000..ddb436d4 --- /dev/null +++ b/pdp11_ts.c @@ -0,0 +1,901 @@ +/* pdp11_ts.c: TS11/TSV05 magnetic tape simulator + + Copyright (c) 1993-2001, 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. + + ts TS11/TSV05 magtape + + 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 UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define DBSIZE (1 << 16) /* data buffer */ + +/* TSBA/TSDB - 17772520: base address/data buffer register + + read: most recent memory address + write word: initiate command + write byte: diagnostic use +*/ + +/* TSSR - 17772522: subsystem status register + TSDBX - 17772523: extended address register + + read: return status + write word: initialize + write byte: if odd, set extended packet address register +*/ + +#define TSSR_SC 0100000 /* special condition */ +#define TSSR_RMR 0010000 /* reg mod refused */ +#define TSSR_NXM 0004000 /* nxm */ +#define TSSR_NBA 0002000 /* need buf addr */ +#define TSSR_V_EMA 8 /* mem addr<17:16> */ +#define TSSR_EMA 0001400 +#define TSSR_SSR 0000200 /* subsystem ready */ +#define TSSR_OFL 0000100 /* offline */ +#define TSSR_V_TC 1 /* term class */ +#define TSSR_TC (07 << TSSR_V_TC) +#define TC0 (0 << TSSR_V_TC) /* ok */ +#define TC1 (1 << TSSR_V_TC) /* attention */ +#define TC2 (2 << TSSR_V_TC) /* status alert */ +#define TC3 (3 << TSSR_V_TC) /* func reject */ +#define TC4 (4 << TSSR_V_TC) /* retry, moved */ +#define TC5 (5 << TSSR_V_TC) /* retry */ +#define TC6 (6 << TSSR_V_TC) /* pos lost */ +#define TC7 (7 << TSSR_V_TC) /* fatal err */ +#define TSSR_MBZ 0060060 + +#define TSDBX_M_XA 017 /* ext addr */ +#define TSDBX_BOOT 0000200 /* boot */ + +/* Command packet offsets */ + +#define CMD_PLNT 4 /* cmd pkt length */ +#define cmdhdr tscmdp[0] /* header */ +#define cmdadl tscmdp[1] /* address low */ +#define cmdadh tscmdp[2] /* address high */ +#define cmdlnt tscmdp[3] /* length */ + +/* Command packet header */ + +#define CMD_ACK 0100000 /* acknowledge */ +#define CMD_CVC 0040000 /* clear vol chk */ +#define CMD_OPP 0020000 /* opposite */ +#define CMD_SWP 0010000 /* swap bytes */ +#define CMD_V_MODE 8 /* mode */ +#define CMD_M_MODE 017 +#define CMD_IE 0000200 /* int enable */ +#define CMD_V_FNC 0 /* function */ +#define CMD_M_FNC 037 /* function */ +#define CMD_N_FNC (CMD_M_FNC + 1) +#define FNC_READ 001 /* read */ +#define FNC_WCHR 004 /* write char */ +#define FNC_WRIT 005 /* write */ +#define FNC_WSSM 006 /* write mem */ +#define FNC_POS 010 /* position */ +#define FNC_FMT 011 /* format */ +#define FNC_CTL 012 /* control */ +#define FNC_INIT 013 /* init */ +#define FNC_GSTA 017 /* get status */ +#define CMD_MBZ 0000140 +#define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC) +#define GET_MOD(x) (((x) >> CMD_V_MODE) & CMD_M_MODE) + +/* Function test flags */ + +#define FLG_MO 001 /* motion */ +#define FLG_WR 002 /* write */ +#define FLG_AD 004 /* addr mem */ + +/* Message packet offsets */ + +#define MSG_PLNT 8 /* packet length */ +#define msghdr tsmsgp[0] /* header */ +#define msglnt tsmsgp[1] /* length */ +#define msgrfc tsmsgp[2] /* residual frame */ +#define msgxs0 tsmsgp[3] /* ext status 0 */ +#define msgxs1 tsmsgp[4] /* ext status 1 */ +#define msgxs2 tsmsgp[5] /* ext status 2 */ +#define msgxs3 tsmsgp[6] /* ext status 3 */ +#define msgxs4 tsmsgp[7] /* ext status 4 */ + +/* Message packet header */ + +#define MSG_ACK 0100000 /* acknowledge */ +#define MSG_MATN 0000000 /* attention */ +#define MSG_MILL 0000400 /* illegal */ +#define MSG_MNEF 0001000 /* non exec fnc */ +#define MSG_CEND 0000020 /* end */ +#define MSG_CFAIL 0000021 /* fail */ +#define MSG_CERR 0000022 /* error */ +#define MSG_CATN 0000023 /* attention */ + +/* Extended status register 0 */ + +#define XS0_TMK 0100000 /* tape mark */ +#define XS0_RLS 0040000 /* rec lnt short */ +#define XS0_LET 0020000 /* log end tape */ +#define XS0_RLL 0010000 /* rec lnt long */ +#define XS0_WLE 0004000 /* write lock err */ +#define XS0_NEF 0002000 /* non exec fnc */ +#define XS0_ILC 0001000 /* illegal cmd */ +#define XS0_ILA 0000400 /* illegal addr */ +#define XS0_MOT 0000200 /* motion */ +#define XS0_ONL 0000100 /* online */ +#define XS0_IE 0000040 /* int enb */ +#define XS0_VCK 0000020 /* volume check */ +#define XS0_PET 0000010 /* 1600 bpi */ +#define XS0_WLK 0000004 /* write lock */ +#define XS0_BOT 0000002 /* BOT */ +#define XS0_EOT 0000001 /* EOT */ +#define XS0_ALLERR 0177600 /* all errors */ + +/* Extended status register 1 - none of these errors are ever set */ + +/* Extended status register 2 - none of these errors are ever set */ + +/* Extended status register 3 */ + +#define XS3_XTF 0000200 /* ext features */ +#define XS3_OPI 0000100 /* op incomplete */ +#define XS3_REV 0000040 /* reverse */ +#define XS3_RIB 0000001 /* reverse to BOT */ + +/* Extended status register 4 - none of these errors are ever set */ + +/* Write characteristics packet offsets */ + +#define WCH_PLNT 4 /* packet length */ +#define wchadl tswchp[0] /* address low */ +#define wchadh tswchp[1] /* address high */ +#define wchlnt tswchp[2] /* length */ +#define wchopt tswchp[3] /* options */ + +/* Write characteristics options */ + +#define WCH_ESS 0000200 /* stop dbl tmk */ +#define WCH_ENB 0000100 /* BOT = tmk */ +#define WCH_EAI 0000040 /* enb attn int */ +#define WCH_ERI 0000020 /* enb mrls int */ + +#define MAX(a,b) (((a) >= (b))? (a): (b)) +#define READ_BYTE(p) ((M[(p) >> 1] >> (((p) & 1)? 8: 0)) & 0377) +#define WRITE_BYTE(d,p) M[(p) >> 1] = (p & 1)? \ + ((M[(p) >> 1] & 0377) | ((d) << 8)): \ + ((M[(p) >> 1] & ~0377) | (d)) + +extern uint16 *M; /* memory */ +extern int32 int_req, dev_enb; +extern UNIT cpu_unit; +int32 tssr = 0; /* status register */ +int32 tsba = 0; /* mem addr */ +int32 tsdbx = 0; /* data buf ext */ +int32 tscmdp[CMD_PLNT] = { 0 }; /* command packet */ +int32 tsmsgp[MSG_PLNT] = { 0 }; /* message packet */ +int32 tswchp[WCH_PLNT] = { 0 }; /* wr char packet */ +int32 ts_ownc = 0; /* tape owns cmd */ +int32 ts_ownm = 0; /* tape owns msg */ +int32 ts_qatn = 0; /* queued attn */ +int32 ts_bcmd = 0; /* boot cmd */ +int32 ts_time = 10; /* record latency */ +int32 ts_log = 0; +static uint8 dbuf[DBSIZE]; + +t_stat ts_svc (UNIT *uptr); +t_stat ts_reset (DEVICE *dptr); +t_stat ts_attach (UNIT *uptr, char *cptr); +t_stat ts_detach (UNIT *uptr); +t_stat ts_boot (int32 unitno); +int32 ts_updtssr (int32 t); +int32 ts_updxs0 (int32 t); +void ts_cmpendcmd (int32 s0, int32 s1); +void ts_endcmd (int32 ssf, int32 xs0f, int32 msg); + +/* TS data structures + + ts_dev TS device descriptor + ts_unit TS unit list + ts_reg TS register list + ts_mod TS modifier list +*/ + +UNIT ts_unit = { UDATA (&ts_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) }; + +REG ts_reg[] = { + { ORDATA (TSSR, tssr, 16) }, + { ORDATA (TSBA, tsba, 22) }, + { ORDATA (TSDBX, tsdbx, 8) }, + { ORDATA (CHDR, cmdhdr, 16) }, + { ORDATA (CADL, cmdadl, 16) }, + { ORDATA (CADH, cmdadh, 16) }, + { ORDATA (CLNT, cmdlnt, 16) }, + { ORDATA (MHDR, msghdr, 16) }, + { ORDATA (MRFC, msgrfc, 16) }, + { ORDATA (MXS0, msgxs0, 16) }, + { ORDATA (MXS1, msgxs1, 16) }, + { ORDATA (MXS2, msgxs2, 16) }, + { ORDATA (MXS3, msgxs3, 16) }, + { ORDATA (MSX4, msgxs4, 16) }, + { ORDATA (WADL, wchadl, 16) }, + { ORDATA (WADH, wchadh, 16) }, + { ORDATA (WLNT, wchlnt, 16) }, + { ORDATA (WOPT, wchopt, 16) }, + { FLDATA (ATTN, ts_qatn, 0) }, + { FLDATA (BOOT, ts_bcmd, 0) }, + { FLDATA (OWNC, ts_ownc, 0) }, + { FLDATA (OWNM, ts_ownm, 0) }, + { DRDATA (TIME, ts_time, 24), PV_LEFT + REG_NZ }, + { DRDATA (POS, ts_unit.pos, 31), PV_LEFT + REG_RO }, + { FLDATA (WLK, ts_unit.flags, UNIT_V_WLK), REG_HRO }, + { FLDATA (LOG, ts_log, 0), REG_HIDDEN }, + { FLDATA (*DEVENB, dev_enb, INT_V_TS), REG_HRO }, + { NULL } }; + +MTAB ts_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE ts_dev = { + "TS", &ts_unit, ts_reg, ts_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ts_reset, + &ts_boot, &ts_attach, &ts_detach }; + +/* I/O dispatch routine, I/O addresses 17772520 - 17772522 + + 17772520 TSBA read/write + 17772522 TSSR read/write +*/ + +t_stat ts_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ +case 0: /* TSBA */ + *data = tsba & DMASK; /* low 16b of ba */ + break; +case 1: /* TSSR */ + *data = tssr = ts_updtssr (tssr); /* update tssr */ + break; } +return SCPE_OK; +} + +t_stat ts_wr (int32 data, int32 PA, int32 access) +{ +int32 i; + +switch ((PA >> 1) & 01) { /* decode PA<1> */ +case 0: /* TSDB */ + if ((tssr & TSSR_SSR) == 0) { /* ready? */ + tssr = tssr | TSSR_RMR; /* no, refuse */ + break; } + tsba = ((tsdbx & TSDBX_M_XA) << 18) | /* form pkt addr */ + ((data & 03) << 16) | (data & 0177774); + tsdbx = 0; /* clr tsdbx */ + tssr = ts_updtssr (tssr & TSSR_NBA); /* clr ssr, err */ + msgxs0 = ts_updxs0 (msgxs0 & ~XS0_ALLERR); /* clr err, upd xs0 */ + msgrfc = msgxs1 = msgxs2 = msgxs3 = msgxs4 = 0; /* clr status */ + int_req = int_req & ~INT_TS; /* clr int req */ + for (i = 0; i < CMD_PLNT; i++) { /* get cmd pkt */ + if (ADDR_IS_MEM (tsba)) tscmdp[i] = M[(tsba >> 1)]; + else { ts_endcmd (TSSR_NXM + TC3, 0, MSG_ACK|MSG_MNEF|MSG_CFAIL); + return SCPE_OK; } + tsba = tsba + 2; } /* incr tsba */ + ts_ownc = ts_ownm = 1; /* tape owns all */ + sim_activate (&ts_unit, ts_time); /* activate */ + break; +case 1: /* TSSR */ + if (PA & 1) { /* TSDBX */ + if (tssr & TSSR_SSR) { /* ready? */ + tsdbx = data; /* save */ + if (data & TSDBX_BOOT) { + ts_bcmd = 1; + sim_activate (&ts_unit, ts_time); } } + else tssr = tssr | TSSR_RMR; } /* no, err */ + else if (access == WRITE) ts_reset (&ts_dev); /* reset */ + break; } +return SCPE_OK; +} + +/* Tape motion routines */ + +#define XTC(x,t) (((unsigned) (x) << 16) | (t)) +#define GET_X(x) (((x) >> 16) & 0177777) +#define GET_T(x) ((x) & 0177777) + +int32 ts_rdlntf (UNIT *uptr, t_mtrlnt *tbc) +{ +fseek (uptr -> fileref, uptr -> pos, SEEK_SET); /* set pos */ +fxread (tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); /* read rec lnt */ +if (ferror (uptr -> fileref)) return (XTC (XS0_EOT | XS0_RLS, TC2)); +if (feof (uptr -> fileref)) return (XTC (XS0_TMK | XS0_RLS, TC2)); +return 0; +} + +int32 ts_spacef (UNIT *uptr, int32 fc, t_bool upd) +{ +int32 st; +t_mtrlnt tbc; + +do { if (st = ts_rdlntf (uptr, &tbc)) return st; /* read rec lnt */ + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update pos */ + if (tbc == 0) return (XTC (XS0_TMK | XS0_RLS, TC2)); + uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + sizeof (t_mtrlnt); + fc = (fc - 1) & DMASK; /* decr wc */ + if (upd) msgrfc = fc; } +while (fc != 0); +return 0; +} + +int32 ts_skipf (UNIT *uptr, int32 fc) +{ +int32 tc; +t_mtrlnt prvp; +t_bool tmkprv = FALSE; + +msgrfc = fc; +if ((uptr -> pos == 0) && (wchopt & WCH_ENB)) tmkprv = TRUE; +do { prvp = uptr -> pos; /* save cur pos */ + tc = ts_spacef (uptr, 0, FALSE); /* space fwd */ + if (GET_X (tc) & XS0_TMK) { /* tape mark? */ + msgrfc = (msgrfc - 1) & DMASK; /* decr count */ + if (tmkprv && (wchopt & WCH_ESS) && + (uptr -> pos - prvp == sizeof (t_mtrlnt))) + return (XTC ((msgrfc? XS0_RLS: 0) | + XS0_TMK | XS0_LET, TC2)); + tmkprv = TRUE; } + else { if (tc) return tc; /* other err? */ + tmkprv = FALSE; } } +while (msgrfc != 0); +return 0; +} + +int32 ts_rdlntr (UNIT *uptr, t_mtrlnt *tbc) +{ +msgxs3 = msgxs3 | XS3_REV; /* set rev op */ +fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), SEEK_SET); +fxread (tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); +if (ferror (uptr -> fileref) || /* error or eof? */ + feof (uptr -> fileref)) { + msgxs3 = msgxs3 | XS3_OPI; /* fatal err */ + return (XTC (XS0_RLS, TC6)); } +return 0; +} + +int32 ts_spacer (UNIT *uptr, int32 fc, t_bool upd) +{ +int32 st; +t_mtrlnt tbc; + +do { if (uptr -> pos == 0) break; /* BOT? */ + if (st = ts_rdlntr (uptr, &tbc)) return st; /* read rec lnt */ + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); /* update pos */ + if (tbc == 0) return (XTC (XS0_TMK | XS0_RLS, TC2)); + uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - sizeof (t_mtrlnt); + fc = (fc - 1) & DMASK; /* decr wc */ + if (upd) msgrfc = fc; } +while (fc != 0); +if (uptr -> pos == 0) { + msgxs3 = msgxs3 | XS3_RIB; + return (XTC (XS0_BOT | (fc? XS0_RLS: 0), TC2)); } +return 0; +} + +int32 ts_skipr (UNIT *uptr, int32 fc) +{ +int32 tc; +t_mtrlnt prvp; +t_bool tmkprv = FALSE; + +msgrfc = fc; +do { tc = ts_spacer (uptr, 0, FALSE); /* space rev */ + if (GET_X (tc) & XS0_TMK) { /* tape mark? */ + msgrfc = (msgrfc - 1) & DMASK; /* decr wc */ + if (tmkprv && (wchopt & WCH_ESS) && + (prvp - uptr -> pos == sizeof (t_mtrlnt))) + return (XTC ((msgrfc? XS0_RLS: 0) | + XS0_TMK | XS0_LET, TC2)); + tmkprv = TRUE; } + else { if (tc) return tc; /* other err? */ + tmkprv = FALSE; } } +while (msgrfc != 0); +return 0; +} + +int32 ts_readf (UNIT *uptr, int32 fc) +{ +int32 i, st; +t_mtrlnt tbc, wbc; +t_addr wa; + +msgrfc = fc; +if (st = ts_rdlntf (uptr, &tbc)) return st; /* read rec lnt */ +if (tbc == 0) { /* tape mark? */ + uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update pos */ + return (XTC (XS0_TMK | XS0_RLS, TC2)); } +if (fc == 0) fc = 0200000; /* byte count */ +tsba = (cmdadh << 16) | cmdadl; /* buf addr */ +tbc = MTRL (tbc); /* ignore err flag */ +wbc = (tbc > DBSIZE)? DBSIZE: tbc; /* cap rec size */ +wbc = (wbc > fc)? fc: wbc; /* cap buf size */ +i = fxread (dbuf, sizeof (uint8), wbc, uptr -> fileref); /* read record */ +if (ferror (uptr -> fileref)) return XTC (XS0_EOT | XS0_RLS, TC2); +for ( ; i < wbc; i++) dbuf[i] = 0; /* fill with 0's */ +uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt)); +for (i = 0; i < wbc; i++) { /* copy buffer */ + wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */ + if (ADDR_IS_MEM (wa)) WRITE_BYTE (dbuf[i], wa); /* nxm? */ + else { tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ + return (XTC (XS0_RLS, TC4)); } + tsba = tsba + 1; + msgrfc = (msgrfc - 1) & DMASK; } +if (msgrfc) return (XTC (XS0_RLS, TC2)); /* buf too big? */ +if (tbc > wbc) return (XTC (XS0_RLL, TC2)); /* rec too big? */ +return 0; +} + +int32 ts_readr (UNIT *uptr, int32 fc) +{ +int32 i, st; +t_mtrlnt tbc, wbc; +t_addr wa; + +msgrfc = fc; +if (uptr -> pos == 0) { /* BOT? */ + msgxs3 = msgxs3 | XS3_RIB; /* nothing to do */ + return (XTC (XS0_BOT | XS0_RLS, TC2)); } +if (st = ts_rdlntr (uptr, &tbc)) return st; /* read rec lnt */ +if (tbc == 0) { /* tape mark? */ + uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); /* update pos */ + return XTC (XS0_TMK | XS0_RLS, TC2); } +if (fc == 0) fc = 0200000; /* byte count */ +tsba = (cmdadh << 16) | cmdadl + fc; /* buf addr */ +tbc = MTRL (tbc); /* ignore err flag */ +wbc = (tbc > DBSIZE)? DBSIZE: tbc; /* cap rec size */ +wbc = (wbc > fc)? fc: wbc; /* cap buf size */ +fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt) - wbc, SEEK_SET); +i = fxread (dbuf, sizeof (uint8), wbc, uptr -> fileref); +for ( ; i < wbc; i++) dbuf[i] = 0; /* fill with 0's */ +if (ferror (uptr -> fileref)) { /* error? */ + msgxs3 = msgxs3 | XS3_OPI; + return XTC (XS0_RLS, TC6); } +uptr -> pos = uptr -> pos - ((tbc + 1) & ~1) - (2 * sizeof (t_mtrlnt)); +for (i = wbc; i > 0; i--) { /* copy buffer */ + tsba = tsba - 1; + wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; + if (ADDR_IS_MEM (wa)) WRITE_BYTE (dbuf[i - 1], wa); + else { tssr = ts_updtssr (tssr | TSSR_NXM); + return (XTC (XS0_RLS, TC4)); } + msgrfc = (msgrfc - 1) & DMASK; } +if (msgrfc) return (XTC (XS0_RLS, TC2)); /* buf too big? */ +if (tbc > wbc) return (XTC (XS0_RLL, TC2)); /* rec too big? */ +return 0; +} + +int32 ts_write (UNIT *uptr, int32 fc) +{ +int32 i; +t_addr wa; + +msgrfc = fc; +if (fc == 0) fc = 0200000; /* byte count */ +tsba = (cmdadh << 16) | cmdadl; /* buf addr */ +for (i = 0; i < fc; i++) { /* copy mem to buf */ + wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply opp */ + if (ADDR_IS_MEM (wa)) dbuf[i] = READ_BYTE (wa); /* nxm? */ + else { tssr = ts_updtssr (tssr | TSSR_NXM); + return TC5; } + tsba = tsba + 1; } +fseek (uptr -> fileref, uptr -> pos, SEEK_SET); /* position */ +fxwrite (&fc, sizeof (t_mtrlnt), 1, uptr -> fileref); +fxwrite (dbuf, sizeof (uint8), fc, uptr -> fileref); +fxwrite (&fc, sizeof (t_mtrlnt), 1, uptr -> fileref); +uptr -> pos = uptr -> pos + ((fc + 1) & ~1) + (2 * sizeof (t_mtrlnt)); +msgrfc = 0; +if (ferror (uptr -> fileref)) { /* error? */ + msgxs3 = msgxs3 | XS3_OPI; + return TC6; } +return 0; +} + +int32 ts_wtmk (UNIT *uptr) +{ +t_mtrlnt bceof = 0; + +fseek (uptr -> fileref, uptr -> pos, SEEK_SET); /* set pos */ +fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); +uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update position */ +if (ferror (uptr -> fileref)) return TC6; +return XTC (XS0_TMK, TC0); +} + +/* Unit service */ + +t_stat ts_svc (UNIT *uptr) +{ +int32 i, fnc, mod, st0, st1; +static const int32 fnc_mod[CMD_N_FNC] = { /* max mod+1 0 ill */ + 0, 4, 0, 0, 1, 2, 1, 0, /* 00 - 07 */ + 5, 3, 5, 1, 0, 0, 0, 1, /* 10 - 17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 0, 0, 0, 0, 0, 0, 0 }; /* 30 - 37 */ +static const int32 fnc_flg[CMD_N_FNC] = { + 0, FLG_MO+FLG_AD, 0, 0, 0, FLG_MO+FLG_WR+FLG_AD, FLG_AD, 0, + FLG_MO, FLG_MO+FLG_WR, FLG_MO, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 0, 0, 0, 0, 0, 0, 0 }; /* 30 - 37 */ + +if (ts_bcmd) { /* boot? */ + ts_bcmd = 0; /* clear flag */ + uptr -> pos = 0; /* rewind */ + if (uptr -> flags & UNIT_ATT) { /* attached? */ + cmdlnt = cmdadh = cmdadl = 0; /* defang rd */ + ts_spacef (uptr, 1, FALSE); /* space fwd */ + ts_readf (uptr, 512); /* read blk */ + tssr = ts_updtssr (tssr | TSSR_SSR); } + else tssr = ts_updtssr (tssr | TSSR_SSR | TC3); + if (cmdhdr & CMD_IE) int_req = int_req | INT_TS; + return SCPE_OK; } + +if (!(cmdhdr & CMD_ACK)) { /* no acknowledge? */ + tssr = ts_updtssr (tssr | TSSR_SSR); /* set rdy, int */ + if (cmdhdr & CMD_IE) int_req = int_req | INT_TS; + ts_ownc = ts_ownm = 0; /* CPU owns all */ + return SCPE_OK; } +fnc = GET_FNC (cmdhdr); /* get fnc+mode */ +mod = GET_MOD (cmdhdr); +if ((fnc != FNC_WCHR) && (tssr & TSSR_NBA)) { /* ~wr chr & nba? */ + ts_endcmd (TC3, 0, 0); /* error */ + return SCPE_OK; } +if (ts_qatn && (wchopt & WCH_EAI)) { /* attn pending? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn msg */ + int_req = int_req | INT_TS; /* set interrupt */ + ts_qatn = 0; /* not pending */ + return SCPE_OK; } +if (cmdhdr & CMD_CVC) /* cvc? clr vck */ + msgxs0 = msgxs0 & ~XS0_VCK; +if ((cmdhdr & CMD_MBZ) || (mod >= fnc_mod[fnc])) { /* test mbz */ + ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; } +if ((fnc_flg[fnc] & FLG_MO) && /* mot+(vck|!att)? */ + ((msgxs0 & XS0_VCK) || !(uptr -> flags & UNIT_ATT))) { + ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; } +if ((fnc_flg[fnc] & FLG_WR) && /* write? */ + (uptr -> flags & UNIT_WLK)) { /* write lck? */ + ts_endcmd (TC3, XS0_WLE | XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; } +if ((((fnc == FNC_READ) && (mod == 1)) || /* read rev */ + ((fnc == FNC_POS) && (mod & 1))) && /* space rev */ + (uptr -> pos == 0)) { /* BOT? */ + ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; } +if ((fnc_flg[fnc] & FLG_AD) && (cmdadh & 0177700)) { /* buf addr > 22b? */ + ts_endcmd (TC3, XS0_ILA, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; } + +st0 = st1 = 0; +switch (fnc) { /* case on func */ +case FNC_INIT: /* init */ + uptr -> pos = 0; /* rewind */ +case FNC_WSSM: /* write mem */ +case FNC_GSTA: /* get status */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* send end packet */ + return SCPE_OK; +case FNC_WCHR: /* write char */ + if ((cmdadh & 0177700) || (cmdadl & 1) || (cmdlnt < 6)) { + ts_endcmd (TSSR_NBA | TC3, XS0_ILA, 0); + break; } + tsba = (cmdadh << 16) | cmdadl; + for (i = 0; (i < WCH_PLNT) && (i < (cmdlnt / 2)); i++) { + if (ADDR_IS_MEM (cmdadl)) tswchp[i] = M[tsba >> 1]; + else { ts_endcmd (TSSR_NBA | TSSR_NXM | TC3, 0, 0); + return SCPE_OK; } + tsba = tsba + 2; } + if ((wchlnt < ((MSG_PLNT - 1) * 2)) || (wchadh & 0177700) || + (wchadl & 1)) ts_endcmd (TSSR_NBA | TC3, 0, 0); + else { msgxs3 = msgxs3 | XS3_XTF | 1; + tssr = ts_updtssr (tssr & ~TSSR_NBA); + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); } + return SCPE_OK; +case FNC_CTL: /* control */ + switch (mod) { /* case mode */ + case 00: /* msg buf rls */ + tssr = ts_updtssr (tssr | TSSR_SSR); /* set SSR */ + if (wchopt & WCH_ERI) int_req = int_req | INT_TS; + ts_ownc = 0; ts_ownm = 1; /* keep msg */ + break; + case 01: /* clean */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* nop */ + break; + case 02: /* rewind and unload */ + detach_unit (uptr); /* unload */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); + break; + case 03: /* undefined */ + ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + case 04: /* rewind */ + ts_unit.pos = 0; + ts_endcmd (TC0, XS0_BOT, MSG_ACK | MSG_CEND); + break; } + break; + +case FNC_READ: /* read */ + switch (mod) { /* case mode */ + case 00: /* fwd */ + st0 = ts_readf (uptr, cmdlnt); /* read */ + break; + case 01: /* back */ + st0 = ts_readr (uptr, cmdlnt); /* read */ + break; + case 02: /* reread fwd */ + if (cmdhdr & CMD_OPP) { /* opposite? */ + st0 = ts_readr (uptr, cmdlnt); + st1 = ts_spacef (uptr, 1, FALSE); } + else { st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_readf (uptr, cmdlnt); } + break; + case 03: /* reread back */ + if (cmdhdr & CMD_OPP) { /* opposite */ + st0 = ts_readf (uptr, cmdlnt); + st1 = ts_spacer (uptr, 1, FALSE); } + else { st0 = ts_spacef (uptr, 1, FALSE); + st1 = ts_readr (uptr, cmdlnt); } + break; } + ts_cmpendcmd (st0, st1); + break; +case FNC_WRIT: /* write */ + switch (mod) { /* case mode */ + case 00: /* write */ + st0 = ts_write (uptr, cmdlnt); + break; + case 01: /* rewrite */ + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_write (uptr, cmdlnt); + break; } + ts_cmpendcmd (st0, st1); + break; +case FNC_FMT: /* format */ + switch (mod) { /* case mode */ + case 00: /* write tmk */ + st0 = ts_wtmk (uptr); + break; + case 01: /* erase */ + break; + case 02: /* retry tmk */ + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_wtmk (uptr); + break; } + ts_cmpendcmd (st0, st1); + break; +case FNC_POS: + switch (mod) { /* case mode */ + case 00: /* space fwd */ + st0 = ts_spacef (uptr, cmdadl, TRUE); + break; + case 01: /* space rev */ + st0 = ts_spacer (uptr, cmdadl, TRUE); + break; + case 02: /* space ffwd */ + st0 = ts_skipf (uptr, cmdadl); + break; + case 03: /* space frev */ + st0 = ts_skipr (uptr, cmdadl); + break; + case 04: /* rewind */ + ts_unit.pos = 0; + break; } + ts_cmpendcmd (st0, 0); + break; } +if (ts_log) printf ("Cmd=%o, mod=%o, buf=%o, lnt=%o, sta = %o, tc=%o, pos=%d\n", + fnc, mod, cmdadl, cmdlnt, msgxs0, (tssr & TSSR_TC) >> 1, ts_unit.pos); +return SCPE_OK; +} + +/* Utility routines */ + +int32 ts_updtssr (int32 t) +{ +t = (t & ~TSSR_EMA) | ((tsba >> (16 - TSSR_V_EMA)) & TSSR_EMA); +if (ts_unit.flags & UNIT_ATT) t = t & ~TSSR_OFL; +else t = t | TSSR_OFL; +return (t & ~TSSR_MBZ); +} + +int32 ts_updxs0 (int32 t) +{ +t = (t & ~(XS0_ONL | XS0_WLK | XS0_BOT | XS0_IE)) | XS0_PET; +if (ts_unit.flags & UNIT_ATT) { + t = t | XS0_ONL; + if (ts_unit.flags & UNIT_WLK) t = t | XS0_WLK; + if (ts_unit.pos == 0) t = (t | XS0_BOT) & ~XS0_EOT; } +else t = t & ~XS0_EOT; +if (cmdhdr & CMD_IE) t = t | XS0_IE; +return t; +} + +void ts_cmpendcmd (int32 s0, int32 s1) +{ +int32 xs0, ssr, tc; +static const int32 msg[8] = { + MSG_ACK | MSG_CEND, MSG_ACK | MSG_MATN | MSG_CATN, + MSG_ACK | MSG_CEND, MSG_ACK | MSG_CFAIL, + MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR, + MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR }; + +xs0 = GET_X (s0 | s1); /* or XS0 errs */ +ssr = GET_T (s0 | s1) & ~TSSR_TC; /* or SSR errs */ +tc = MAX (s0 & TSSR_TC, s1 & TSSR_TC); /* max term code */ +ts_endcmd (ssr | tc, xs0, msg[tc]); /* end cmd */ +return; +} + +void ts_endcmd (int32 tc, int32 xs0, int32 msg) +{ +int32 i; + +msgxs0 = ts_updxs0 (msgxs0 | xs0); /* update XS0 */ +if (msg && !(tssr & TSSR_NBA)) { /* send end pkt */ + msghdr = msg; + msglnt = wchlnt - 4; /* exclude hdr, bc */ + tsba = (wchadh << 16) | wchadl; + for (i = 0; (i < MSG_PLNT) && (i < (wchlnt / 2)); i++) { + if (ADDR_IS_MEM (tsba)) M[tsba >> 1] = tsmsgp[i]; + else { tssr = tssr | TSSR_NXM; + tc = (tc & ~TSSR_TC) | TC4; + break; } + tsba = tsba + 2; } } +tssr = ts_updtssr (tssr | tc | TSSR_SSR | (tc? TSSR_SC: 0)); +if (cmdhdr & CMD_IE) int_req = int_req | INT_TS; +ts_ownm = 0; ts_ownc = 0; +return; +} + +/* Device reset */ + +t_stat ts_reset (DEVICE *dptr) +{ +int32 i; + +if (dev_enb & INT_TS) dev_enb = dev_enb & ~INT_TM; /* TM or TS */ +ts_unit.pos = 0; +tsba = tsdbx = 0; +ts_ownc = ts_ownm = 0; +ts_bcmd = 0; +ts_qatn = 0; +tssr = ts_updtssr (TSSR_NBA | TSSR_SSR); +for (i = 0; i < CMD_PLNT; i++) tscmdp[i] = 0; +for (i = 0; i < WCH_PLNT; i++) tswchp[i] = 0; +for (i = 0; i < MSG_PLNT; i++) tsmsgp[i] = 0; +msgxs0 = ts_updxs0 (XS0_VCK); +int_req = int_req & ~INT_TS; +return SCPE_OK; +} + +/* Attach */ + +t_stat ts_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +tssr = tssr & ~TSSR_OFL; /* clr offline */ +if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) return r; /* attn msg? */ +if (ts_ownm) { /* own msg buf? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ + int_req = int_req | INT_TS; /* set interrupt */ + ts_qatn = 0; } /* don't queue */ +else ts_qatn = 1; /* else queue */ +return r; +} + +/* Detach routine */ + +t_stat ts_detach (UNIT* uptr) +{ +t_stat r; + +r = detach_unit (uptr); /* detach unit */ +if (r != SCPE_OK) return r; /* error? */ +tssr = tssr | TSSR_OFL; /* set offline */ +if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) return r; /* attn msg? */ +if (ts_ownm) { /* own msg buf? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ + int_req = int_req | INT_TS; /* set interrupt */ + ts_qatn = 0; } /* don't queue */ +else ts_qatn = 1; /* else queue */ +return r; +} + +/* Boot */ + +#define BOOT_START 01000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0012706, 0001000, /* mov #boot_start, sp */ + 0012700, 0172520, /* mov #tsba, r0 */ + 0012701, 0172522, /* mov #tssr, r1 */ + 0005011, /* clr (r1) ; init, rew */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001064, /* mov #pkt1, (r0) ; set char */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001104, /* mov #pkt2, (r0) ; read, skip */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001104, /* mov #pkt2, (r0) ; read */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0005711, /* tst (r1) ; err? */ + 0100421, /* bmi hlt */ + 0005000, /* clr r0 */ + 0012705, 0051515, /* mov #MS, r5 */ + 0005007, /* clr r7 */ + 0046523, /* pad */ + 0140004, /* pkt1: 140004, wcpk, 0, 8. */ + 0001074, + 0000000, + 0000010, + 0001116, /* wcpk: msg, 0, 14., 0 */ + 0000000, + 0000016, + 0000000, + 0140001, /* pkt2: 140001, 0, 0, 512. */ + 0000000, + 0000000, + 0001000, + 0000000 /* hlt: halt */ +}; + +t_stat ts_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +ts_unit.pos = 0; +for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/pdp15.c b/pdp15.c deleted file mode 100644 index d6778bb2..00000000 --- a/pdp15.c +++ /dev/null @@ -1 +0,0 @@ -#define PDP15 0 diff --git a/pdp18b_cpu.c b/pdp18b_cpu.c index a7069f0c..79a33cb7 100644 --- a/pdp18b_cpu.c +++ b/pdp18b_cpu.c @@ -1,6 +1,6 @@ /* pdp18b_cpu.c: 18b PDP CPU simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,10 @@ 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. + cpu PDP-4/7/9/15 central processor + + 26-Apr-01 RMS Added device enable/disable support + 25-Jan-01 RMS Added DECtape support 18-Dec-00 RMS Added PDP-9,-15 memm init register 30-Nov-00 RMS Fixed numerous PDP-15 bugs 14-Apr-99 RMS Changed t_addr to unsigned @@ -276,6 +280,7 @@ 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 */ +int32 dev_enb = -1; /* device enables */ 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); @@ -347,6 +352,7 @@ REG cpu_reg[] = { { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, { ORDATA (BREAK, ibkpt_addr, ADDRSIZE + 1) }, { ORDATA (WRU, sim_int_char, 8) }, + { ORDATA (DEVENB, dev_enb, 32), REG_HRO }, { NULL } }; MTAB cpu_mod[] = { @@ -387,6 +393,8 @@ extern int32 sim_interval; register int32 PC, LAC, MQ; int32 iot_data, device, pulse; t_stat reason; +extern UNIT clk_unit; +extern int32 sim_rtc_init (int32 time); extern int32 tti (int32 pulse, int32 AC); extern int32 tto (int32 pulse, int32 AC); extern int32 ptr (int32 pulse, int32 AC); @@ -410,6 +418,10 @@ extern int32 rp64 (int32 pulse, int32 AC); #if defined (MTA) extern int32 mt (int32 pulse, int32 AC); #endif +#if defined (DTA) +extern int32 dt75 (int32 pulse, int32 AC); +extern int32 dt76 (int32 pulse, int32 AC); +#endif #define JMS_WORD(t) (((LAC & 01000000) >> 1) | ((memm & 1) << 16) | \ (((t) & 1) << 15) | ((PC) & 077777)) @@ -433,6 +445,7 @@ PC = saved_PC & ADDRMASK; /* load local copies */ LAC = saved_LAC & 01777777; MQ = saved_MQ & 0777777; reason = 0; +sim_rtc_init (clk_unit.wait); /* init calibration */ /* Main instruction fetch/decode loop: check trap and interrupt */ @@ -795,7 +808,7 @@ case 031: /* JMP, indir */ if (rest_pending) { /* restore pending? */ LAC = ((M[MA] << 1) & 01000000) | (LAC & 0777777); memm = (M[MA] >> 16) & 1; - usmd = (M[MA] >> 15) & 1); } + usmd = (M[MA] >> 15) & 1; } #endif INDIRECT; /* complete indirect */ emir_pending = rest_pending = 0; @@ -1304,21 +1317,26 @@ case 034: /* IOT */ break; #if defined (DRM) case 060: /* drum */ - iot_data = drm60 (pulse, iot_data); + if (dev_enb & INT_DRM) iot_data = drm60 (pulse, iot_data); + else reason = stop_inst; break; case 061: - iot_data = drm61 (pulse, iot_data); + if (dev_enb & INT_DRM) iot_data = drm61 (pulse, iot_data); + else reason = stop_inst; break; case 062: - iot_data = drm62 (pulse, iot_data); + if (dev_enb & INT_DRM) iot_data = drm62 (pulse, iot_data); + else reason = stop_inst; break; #endif #if defined (RP) case 063: /* RP15 */ - iot_data = rp63 (pulse, iot_data); + if (dev_enb & INT_RP) iot_data = rp63 (pulse, iot_data); + else reason = stop_inst; break; case 064: - iot_data = rp64 (pulse, iot_data); + if (dev_enb & INT_RP) iot_data = rp64 (pulse, iot_data); + else reason = stop_inst; break; #endif case 065: /* LPT */ @@ -1329,15 +1347,28 @@ case 034: /* IOT */ break; #if defined (RF) case 070: /* RF09 */ - iot_data = rf70 (pulse, iot_data); + if (dev_enb & INT_RF) iot_data = rf70 (pulse, iot_data); + else reason = stop_inst; break; case 072: - iot_data = rf72 (pulse, iot_data); + if (dev_enb & INT_RF) iot_data = rf72 (pulse, iot_data); + else reason = stop_inst; break; #endif #if defined (MTA) case 073: /* TC59 */ - iot_data = mt (pulse, iot_data); + if (dev_enb & INT_MTA) iot_data = mt (pulse, iot_data); + else reason = stop_inst; + break; +#endif +#if defined (DTA) + case 075: /* TC02/TC15 */ + if (dev_enb & INT_DTA) iot_data = dt75 (pulse, iot_data); + else reason = stop_inst; + break; + case 076: + if (dev_enb & INT_DTA) iot_data = dt76 (pulse, iot_data); + else reason = stop_inst; break; #endif default: /* unknown device */ diff --git a/pdp18b_defs.h b/pdp18b_defs.h index 2fe52b3c..8c950c8b 100644 --- a/pdp18b_defs.h +++ b/pdp18b_defs.h @@ -1,6 +1,6 @@ /* pdp18b_defs.h: 18b PDP simulator definitions - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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. + 21-Jan-01 RMS Added DECtape support 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 @@ -39,7 +40,7 @@ model memory CPU options I/O options - PDP4 8K none Type 65 KSR-28 Teletype (Baudot) + PDP4 8K ??Type 18 EAE Type 65 KSR-28 Teletype (Baudot) integral paper tape reader Type 75 paper tape punch integral real time clock @@ -58,6 +59,7 @@ KX09A mem protection Type 647D/E line printer (sixbit) RF09/RS09 fixed head disk TC59 magnetic tape + TC02/TU55 DECtape PDP15 128K KE15 EAE KSR-35 Teletype KF15 power detection PC15 paper tape reader and punch @@ -66,6 +68,7 @@ RP15 disk pack RF15/RF09 fixed head disk TC59D magnetic tape + TC15/TU56 DECtape ??Indicates not implemented. The PDP-4 manual refers to both an EAE ??and a memory extension control; there is no documentation on either. @@ -97,12 +100,14 @@ #define TYPE647 0 /* sixbit printer */ #define RF 0 /* fixed head disk */ #define MTA 0 /* magtape */ +#define DTA 0 /* DECtape */ #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 DTA 0 /* DECtape */ #endif /* Memory */ @@ -120,6 +125,8 @@ #define LINK (DMASK + 1) /* link */ #define LACMASK (LINK | DMASK) /* link + data */ #define SIGN 0400000 /* sign bit */ +#define OP_JMP 0600000 /* JMP */ +#define OP_HLT 0740040 /* HLT */ /* IOT subroutine return codes */ diff --git a/pdp18b_drm.c b/pdp18b_drm.c index a4a17d58..f2075784 100644 --- a/pdp18b_drm.c +++ b/pdp18b_drm.c @@ -1,6 +1,6 @@ /* pdp18b_drm.c: drum/fixed head disk simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -26,6 +26,7 @@ drm (PDP-7) Type 24 serial drum (PDP-9) RM09 serial drum + 26-Apr-01 RMS Added device enable/disable support 14-Apr-99 RMS Changed t_addr to unsigned */ @@ -52,7 +53,7 @@ ((double) DRM_NUMWDT))) extern int32 M[]; -extern int32 int_req; +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 drm_da = 0; /* track address */ int32 drm_ma = 0; /* memory address */ @@ -63,8 +64,6 @@ 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 @@ -86,6 +85,7 @@ REG drm_reg[] = { { ORDATA (WLK, drm_wlk, 32) }, { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, { FLDATA (STOP_IOE, drm_stopioe, 0) }, + { FLDATA (*DEVENB, dev_enb, INT_V_DRM), REG_HRO }, { NULL } }; DEVICE drm_dev = { diff --git a/pdp18b_dt.c b/pdp18b_dt.c new file mode 100644 index 00000000..75c9a5f2 --- /dev/null +++ b/pdp18b_dt.c @@ -0,0 +1,1072 @@ +/* pdp18b_dt.c: PDP-9/15 DECtape simulator + + Copyright (c) 1993-2001, 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. + + dt (PDP-9) TC02/TU55 DECtape + (PDP-15) TC15/TU56 DECtape + + 11-May-01 RMS Fixed bug in reset + 26-Apr-01 RMS Added device enable/disable support + 15-Mar-01 RMS Added 129th word to PDP-8 format + + 18b DECtapes are represented by fixed length data blocks of 18b words. Two + tape formats are supported: + + 16b/18b/36b 256 words per block + 12b 86 words per block [129 x 12b] + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape is + + reverse end zone 36000 lines ~ 10 feet + block 0 + : + block n + forward end zone 36000 lines ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 0 + : + trailer word 4 checksum + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. +*/ + +#include "pdp18b_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_W_UF 3 /* saved flag width */ +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define DT_WC 030 /* word count */ +#define DT_CA 031 /* current addr */ + +/* System independent DECtape constants */ + +#define DT_EZLIN 36000 /* end zone length */ +#define DT_HTLIN 30 /* header/trailer lines */ +#define DT_BLKLN 6 /* blk no line in h/t */ +#define DT_CSMLN 24 /* checksum line in h/t */ +#define DT_HTWRD (DT_HTLIN / DT_WSIZE) /* header/trailer words */ +#define DT_BLKWD (DT_BLKLN / DT_WSIZE) /* blk no word in h/t */ +#define DT_CSMWD (DT_CSMLN / DT_WSIZE) /* checksum word in h/t */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u) -> flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u) -> flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u) -> flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u) -> flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u) -> flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u) -> pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u) -> pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ + +#define DTA_V_UNIT 15 /* unit select */ +#define DTA_M_UNIT 07 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 13 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_MODE 12 /* mode */ +#define DTA_V_FNC 9 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_WRIT 04 /* write */ +#define FNC_WALL 05 /* write all */ +#define FNC_WMRK 06 /* write timing */ +#define DTA_V_ENB 8 /* int enable */ +#define DTA_V_CERF 7 /* clr error flag */ +#define DTA_V_CDTF 6 /* clr DECtape flag */ +#define DTA_FWDRV (1u << (DTA_V_MOT + 1)) +#define DTA_STSTP (1u << DTA_V_MOT) +#define DTA_MODE (1u << DTA_V_MODE) +#define DTA_ENB (1u << DTA_V_ENB) +#define DTA_CERF (1u << DTA_V_CERF) +#define DTA_CDTF (1u << DTA_V_CDTF) +#define DTA_RW (0777700 & ~(DTA_CERF | DTA_CDTF)) + +#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) +#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) +#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) + +/* Status register B */ + +#define DTB_V_ERF 17 /* error flag */ +#define DTB_V_MRK 16 /* mark trk err */ +#define DTB_V_END 15 /* end zone err */ +#define DTB_V_SEL 14 /* select err */ +#define DTB_V_PAR 13 /* parity err */ +#define DTB_V_TIM 12 /* timing err */ +#define DTB_V_DTF 6 /* DECtape flag */ +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_END (1u << DTB_V_END) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_PAR (1u << DTB_V_PAR) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ + DTB_PAR | DTB_TIM) + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR 7 /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr -> STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr -> STATE = (uptr -> STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr -> STATE = (uptr -> STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Operation substates */ + +#define DTO_WCO 1 /* wc overflow */ +#define DTO_SOB 2 /* start of block */ + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read, write */ +#define LOG_RA 004 /* read all */ +#define LOG_BL 010 /* block # lblk */ + +#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ + int_req = int_req | INT_DTA; \ + else int_req = int_req & ~INT_DTA; +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern int32 M[]; +extern int32 int_req, dev_enb; +extern UNIT cpu_unit; +extern int32 sim_switches; +int32 dtsa = 0; /* status A */ +int32 dtsb = 0; /* status B */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_actime = 54000; /* accel time */ +int32 dt_dctime = 72000; /* decel time */ +int32 dt_substate = 0; +int32 dt_log = 0; +int32 dt_logblk = 0; +t_stat dt_svc (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); +extern uint32 sim_grtime (void); +extern int32 sim_is_running; + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) } }; + +REG dt_reg[] = { + { GRDATA (DTSA, dtsa, 8, 12, 6) }, + { GRDATA (DTSB, dtsb, 8, 12, 6) }, + { FLDATA (INT, int_req, INT_V_DTA) }, + { FLDATA (ENB, dtsa, DTA_V_ENB) }, + { FLDATA (DTF, dtsb, DTB_V_DTF) }, + { FLDATA (ERF, dtsb, DTB_V_ERF) }, + { ORDATA (WC, M[DT_WC], 18) }, + { ORDATA (CA, M[DT_CA], 18) }, + { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, + { DRDATA (ACTIME, dt_actime, 31), REG_NZ }, + { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, + { ORDATA (SUBSTATE, dt_substate, 2) }, + { ORDATA (LOG, dt_log, 4), REG_HIDDEN }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { DRDATA (POS0, dt_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, dt_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, dt_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, dt_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, dt_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, dt_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, dt_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, dt_unit[7].pos, 31), PV_LEFT + REG_RO }, + { ORDATA (STATE0, dt_unit[0].STATE, 18), REG_RO }, + { ORDATA (STATE1, dt_unit[1].STATE, 18), REG_RO }, + { ORDATA (STATE2, dt_unit[2].STATE, 18), REG_RO }, + { ORDATA (STATE3, dt_unit[3].STATE, 18), REG_RO }, + { ORDATA (STATE4, dt_unit[4].STATE, 18), REG_RO }, + { ORDATA (STATE5, dt_unit[5].STATE, 18), REG_RO }, + { ORDATA (STATE6, dt_unit[6].STATE, 18), REG_RO }, + { ORDATA (STATE7, dt_unit[7].STATE, 18), REG_RO }, + { DRDATA (LASTT0, dt_unit[0].LASTT, 32), REG_HRO }, + { DRDATA (LASTT1, dt_unit[1].LASTT, 32), REG_HRO }, + { DRDATA (LASTT2, dt_unit[2].LASTT, 32), REG_HRO }, + { DRDATA (LASTT3, dt_unit[3].LASTT, 32), REG_HRO }, + { DRDATA (LASTT4, dt_unit[4].LASTT, 32), REG_HRO }, + { DRDATA (LASTT5, dt_unit[5].LASTT, 32), REG_HRO }, + { DRDATA (LASTT6, dt_unit[6].LASTT, 32), REG_HRO }, + { DRDATA (LASTT7, dt_unit[7].LASTT, 32), REG_HRO }, + { GRDATA (FLG0, dt_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, dt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, dt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, dt_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, dt_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, dt_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, dt_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, dt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_DTA), REG_HRO }, + { NULL } }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT, 0, "16b/18b", NULL, NULL }, + { UNIT_8FMT, UNIT_8FMT, "12b", NULL, NULL }, + { 0 } }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &dt_reset, + NULL, &dt_attach, &dt_detach }; + +/* IOT routines */ + +int32 dt75 (int32 pulse, int32 AC) +{ +int32 old_dtsa = dtsa, fnc; +UNIT *uptr; + +if (((pulse & 060) == 040) && (pulse & 05)) { /* select */ + if (pulse & 01) dtsa = 0; /* DTCA */ + if (pulse & 02) AC = dtsa; /* DTRA!... */ + if (pulse & 04) { /* DTXA */ + if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; + if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; + dtsa = dtsa ^ (AC & DTA_RW); } + if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); + uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if (((uptr -> flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WRIT) && (uptr -> flags & UNIT_WLK)) || + ((fnc == FNC_WALL) && (uptr -> flags & UNIT_WLK))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); /* new func */ + DT_UPDINT; + return AC; } +if ((pulse & 067) == 042) return dtsa; /* DTRA */ +if ((pulse & 067) == 061) /* DTEF */ + return ((dtsb & DTB_ERF)? IOT_SKP + AC: AC); +if ((pulse & 067) == 062) return dtsb; /* DTRB */ +if ((pulse & 067) == 063) /* DTEF!DTRB */ + return ((dtsb & DTB_ERF)? IOT_SKP + dtsb: dtsb); +return AC; +} + +int32 dt76 (int32 pulse, int32 AC) +{ +if ((pulse & 01) && (dtsb & DTB_DTF)) /* DTDF */ + return IOT_SKP + AC; +return AC; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit = DTA_GETUNIT (oldf); +UNIT *uptr = dt_dev.units + old_unit; +int32 old_mot = DTS_GETMOT (uptr -> STATE); + +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; } + +/* Command register change + + 1. If change in motion, stop to start + - schedule acceleration + - set function as next state + 2. If change in motion, start to stop + - if not already decelerating (could be reversing), + schedule deceleration + 3. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 4. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 5. If not yet at speed, + - set function as next state + 6. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, prev_fnc, new_fnc; +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +new_unit = DTA_GETUNIT (newf); /* new, old units */ +uptr = dt_dev.units + new_unit; +if ((uptr -> flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, DTB_SEL); /* no, error */ + return; } +prev_mot = DTS_GETMOT (uptr -> STATE); /* previous motion */ +prev_mving = prev_mot != DTS_STOP; /* previous moving? */ +prev_dir = prev_mot & DTS_DIR; /* previous dir? */ +prev_fnc = DTS_GETFNC (uptr -> STATE); /* prev function? */ +new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ +new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ +new_fnc = DTA_GETFNC (newf); /* new function? */ + +if ((prev_mving | new_mving) == 0) return; /* stop to stop */ + +if (new_mving & ~prev_mving) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_actime); /* schedule accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +if (prev_mving & ~new_mving) { /* stop? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); } /* schedule decel */ + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + return; } + +if (prev_dir ^ new_dir) { /* dir chg? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); } /* schedule decel */ + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_actime); /* schedule accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, relpos, newpos; +uint32 oldpos; + +oldpos = uptr -> pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr -> STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr -> STATE); /* set variables */ +dir = DTS_GETMOT (uptr -> STATE) & DTS_DIR; +unum = uptr - dt_dev.units; +if (oldpos == uptr -> pos) /* bump pos */ + uptr -> pos = uptr -> pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr -> pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, DTB_END); /* set ez flag, stop */ + return; } +sim_cancel (uptr); /* cancel cur op */ +dt_substate = DTO_SOB; /* substate = block start */ +switch (fnc) { /* case function */ +case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; +case FNC_MOVE: /* move */ + dt_schedez (uptr, dir); /* sched end zone */ + if (dt_log & LOG_MS) printf ("[DT%d: moving %s]\n", unum, (dir? + "backward": "forward")); + return; /* done */ +case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (dt_log & LOG_MS) printf ("[DT%d: searching %s]\n", unum, + (dir? "backward": "forward")); + break; +case FNC_WRIT: /* write */ +case FNC_READ: /* read */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, DTB_SEL); + return; } + if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + break; +case FNC_RALL: /* read all */ +case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; + else newpos = DT_EZLIN + (DT_WSIZE - 1); } + else { newpos = ((uptr -> pos) / DT_WSIZE) * DT_WSIZE; + if (!dir) newpos = newpos + (DT_WSIZE - 1); } + if ((dt_log & LOG_RA) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: read all block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous]": "]")); + break; +default: + dt_seterr (uptr, DTB_SEL); /* bad state */ + return; } +sim_activate (uptr, ABS (newpos - ((int32) uptr -> pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr -> LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr -> LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ +case DTS_STOP: /* stop */ + delta = 0; + break; +case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; +case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; udelt = dt_actime / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; +case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; } +if (mot & DTS_DIR) uptr -> pos = uptr -> pos - delta; /* update pos */ +else uptr -> pos = uptr -> pos + delta; +if ((uptr -> pos < 0) || + (uptr -> pos > ((uint32) (DTU_FWDEZ (uptr) + DT_EZLIN)))) { + detach_unit (uptr); /* off reel? */ + uptr -> STATE = uptr -> pos = 0; + unum = uptr - dt_dev.units; + if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ + dt_seterr (uptr, DTB_SEL); /* error */ + return TRUE; } +return FALSE; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr -> STATE); +int32 *bptr = uptr -> filebuf; +int32 unum = uptr - dt_dev.units; +int32 blk, wrd, ma, relpos, dat; +t_addr ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { +case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) return SCPE_OK; /* update pos */ + uptr -> STATE = DTS_NXTSTA (uptr -> STATE); /* advance state */ + if (uptr -> STATE) /* not stopped? */ + sim_activate (uptr, dt_actime); /* must be reversing */ + return SCPE_OK; +case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr -> STATE)); /* adv state, sched */ + return SCPE_OK; +case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ +default: /* other */ + dt_seterr (uptr, DTB_SEL); /* state error */ + return SCPE_OK; } + +/* Functional cases + + Move - must be at end zone + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) return SCPE_OK; /* update pos */ +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; } +blk = DT_LIN2BL (uptr -> pos, uptr); /* get block # */ +switch (fnc) { /* at speed, check fnc */ +case FNC_MOVE: /* move */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; +case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* inc WC */ + ma = M[DT_CA] & ADDRMASK; /* get mem addr */ + if (MEM_ADDR_OK (ma)) M[ma] = blk; /* store block # */ + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; +case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr -> STATE = uptr -> pos = 0; /* no visible action */ + break; + +/* Read has four subcases + + Start of block, not wc ovf - check that DTF is clear, otherwise normal + Normal - increment MA, WC, copy word from tape to memory + if read dir != write dir, bits must be scrambled + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - if end of block reached, timing error, + otherwise, continue to next word +*/ + +case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr -> pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + case DTO_SOB: /* start of block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + if ((dt_log & LOG_RW) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: reading block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous]": "]")); + dt_substate = 0; /* fall through */ + case 0: /* normal read */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + ma = M[DT_CA] & ADDRMASK; /* mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = bptr[ba]; /* get tape word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */ + case DTO_WCO: /* wc ovf, not sob */ + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; } /* set DTF */ + break; + case DTO_WCO | DTO_SOB: /* next block */ + if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + else sim_activate (uptr, DT_WSIZE * dt_ltime); + break; } + break; + +/* Write has four subcases + + Start of block, not wc ovf - check that DTF is clear, set block direction + Normal - increment MA, WC, copy word from memory to tape + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + copy 0 to tape + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - schedule end zone +*/ + +case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr -> pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + case DTO_SOB: /* start block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + if ((dt_log & LOG_RW) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: writing block %d %s%s\n", unum, blk, + (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous]": "]")); + dt_substate = 0; /* fall through */ + case 0: /* normal write */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + case DTO_WCO: /* wc ovflo */ + ma = M[DT_CA] & ADDRMASK; /* mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = dt_substate? 0: M[ma]; /* get word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + bptr[ba] = dat; /* write word */ + if (ba >= uptr -> hwmark) uptr -> hwmark = ba + 1; + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; } /* set DTF */ + break; + case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; } + break; + +/* Read all has two subcases + + Not word count overflow - increment MA, WC, copy word from tape to memory + Word count overflow - schedule end zone +*/ + +case FNC_RALL: + switch (dt_substate) { /* case on substate */ + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + ma = M[DT_CA] & ADDRMASK; /* mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr -> pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dat = bptr[ba]; } /* get tape word */ + else dat = dt_gethdr (uptr, blk, relpos); /* get hdr */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; } /* end case substate */ + break; + +/* Write all has two subcases + + Not word count overflow - increment MA, WC, copy word from memory to tape + Word count overflow - schedule end zone +*/ + +case FNC_WALL: + switch (dt_substate) { /* case on substate */ + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + ma = M[DT_CA] & ADDRMASK; /* mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dat = M[ma]; /* get mem word */ + if (dir) dat = dt_comobv (dat); + wrd = DT_LIN2WD (uptr -> pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + bptr[ba] = dat; /* write word */ + if (ba >= uptr -> hwmark) uptr -> hwmark = ba + 1; } +/* /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; } /* end case substate */ + break; +default: + dt_seterr (uptr, DTB_SEL); /* impossible state */ + break; } +DT_UPDINT; /* update interrupts */ +return SCPE_OK; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); + +dtsa = dtsa & ~DTA_STSTP; /* clear go */ +dtsb = dtsb | DTB_ERF | e; /* set error flag */ +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); } /* state = decel */ +DT_UPDINT; +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr -> pos)) * dt_ltime); +return; +} + +/* Complement obverse routine */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 0777777; /* compl obverse */ +dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | + ((dat >> 3) & 0700) | ((dat & 0700) << 3) | + ((dat & 070) << 9) | ((dat & 07) << 15); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int32 *bptr = uptr -> filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = bptr[ba + i] ^ 0777777; /* get ~word */ + csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd; } +return (csum & 077); +} + +/* Get header word */ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) +{ +int32 wrd = relpos / DT_WSIZE; + +if (wrd == DT_BLKWD) return blk; /* fwd blknum */ +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ + return (dt_csum (uptr, blk) << 12); +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ + return dt_comobv (blk); +return 0; /* all others */ +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all drives */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* CAF? */ + prev_mot = DTS_GETMOT (uptr -> STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } } + else { sim_cancel (uptr); /* sim reset */ + uptr -> STATE = 0; + uptr -> LASTT = sim_grtime (); } } +dtsa = dtsb = 0; /* clear status */ +DT_UPDINT; /* reset interrupt */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 dt_iors (void) +{ +return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0); +} + +/* Attach routine + + Determine native or PDP8 format + Allocate buffer + If PDP8, read 12b format and convert to 18b in buffer + If native, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint16 pdp8b[D8_NBSIZE]; +int32 k, p, *bptr; +t_stat r; +t_addr ba; + +uptr -> flags = uptr -> flags & ~UNIT_8FMT; +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if (sim_switches & SWMASK ('F')) /* att foreign? */ + uptr -> flags = uptr -> flags | UNIT_8FMT; /* PDP8 = T */ +else if (!(sim_switches & SWMASK ('N'))) { /* autosize? */ + if ((fseek (uptr -> fileref, 0, SEEK_END) == 0) && + (p = ftell (uptr -> fileref)) && + (p == D8_FILSIZ)) uptr -> flags = uptr -> flags | UNIT_8FMT; } +uptr -> capac = DTU_CAPAC (uptr); /* set capacity */ +uptr -> filebuf = calloc (uptr -> capac, sizeof (int32)); +if (uptr -> filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; } +printf ("%DT: buffering file in memory\n"); +rewind (uptr -> fileref); /* start of file */ +if (uptr -> flags & UNIT_8FMT) { /* PDP-8? */ + bptr = uptr -> filebuf; /* file buffer */ + for (ba = 0; ba < uptr -> capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (int16), D8_NBSIZE, uptr -> fileref); + if (k == 0) break; + for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + bptr[ba] = ((pdp8b[k] & 07777) << 6) | + ((pdp8b[k + 1] >> 6) & 077); + bptr[ba + 1] = ((pdp8b[k + 1] & 077) << 12) | + (pdp8b[k + 2] & 07777); + ba = ba + 2; } /* end blk loop */ + } /* end file loop */ + uptr -> hwmark = ba; } /* end if */ +else uptr -> hwmark = fxread (uptr -> filebuf, sizeof (int32), + uptr -> capac, uptr -> fileref); +uptr -> flags = uptr -> flags | UNIT_BUF; /* set buf flag */ +uptr -> pos = DT_EZLIN; /* beyond leader */ +uptr -> LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If PDP8, convert 18b buffer to 12b and write to file + If native, write buffer to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint16 pdp8b[D8_NBSIZE]; +int32 k, *bptr; +int32 unum = uptr - dt_dev.units; +t_addr ba; + +if (!(uptr -> flags & UNIT_ATT)) return SCPE_OK; +if (sim_is_active (uptr)) { + sim_cancel (uptr); + if ((unum == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { + dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; + DT_UPDINT; } + uptr -> STATE = uptr -> pos = 0; } +if (uptr -> hwmark) { /* any data? */ + printf ("DT: writing buffer to file\n"); + rewind (uptr -> fileref); /* start of file */ + if (uptr -> flags & UNIT_8FMT) { /* PDP8? */ + bptr = uptr -> filebuf; /* file buffer */ + for (ba = 0; ba < uptr -> hwmark; ) { /* loop thru buf */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (bptr[ba] >> 6) & 07777; + pdp8b[k + 1] = ((bptr[ba] & 077) << 6) | + ((bptr[ba + 1] >> 12) & 077); + pdp8b[k + 2] = bptr[ba + 1] & 07777; + ba = ba + 2; } /* end loop blk */ + fxwrite (pdp8b, sizeof (int16), D8_NBSIZE, uptr -> fileref); + if (ferror (uptr -> fileref)) break; } /* end loop file */ + } /* end if PDP8 */ + else fxwrite (uptr -> filebuf, sizeof (int32), /* write file */ + uptr -> hwmark, uptr -> fileref); + if (ferror (uptr -> fileref)) perror ("I/O error"); } /* end if hwmark */ +free (uptr -> filebuf); /* release buf */ +uptr -> flags = uptr -> flags & ~UNIT_BUF; /* clear buf flag */ +uptr -> filebuf = NULL; /* clear buf ptr */ +uptr -> flags = uptr -> flags & ~UNIT_8FMT; /* default fmt */ +uptr -> capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/pdp18b_lp.c b/pdp18b_lp.c index 6f73fb63..689d0948 100644 --- a/pdp18b_lp.c +++ b/pdp18b_lp.c @@ -1,6 +1,6 @@ /* pdp18b_lp.c: 18b PDP's line printer simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,13 +23,15 @@ 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 (PDP-4) Type 62 line printer + (PDP-7,9) Type 647 line printer + (PDP-15) LP15 line printer + + 13-Feb-01 RMS Revised for register arrays + 15-Feb-01 RMS Fixed 3 cycle data break sequence 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" @@ -45,8 +47,6 @@ 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 @@ -68,7 +68,7 @@ REG lpt_reg[] = { { 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 }, + { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE) }, { NULL } }; DEVICE lpt_dev = { @@ -195,8 +195,6 @@ 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 @@ -221,7 +219,7 @@ REG lpt_reg[] = { { 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 }, + { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE) }, { NULL } }; DEVICE lpt_dev = { @@ -377,7 +375,7 @@ return detach_unit (uptr); #define LPT_BSIZE 132 /* line size */ #define LPT_WC 034 /* word count */ -#define LPT_MA 035 /* mem address */ +#define LPT_CA 035 /* current addr */ /* Status register */ @@ -400,9 +398,6 @@ char lpt_buf[LPT_BSIZE] = { 0 }; t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); 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 @@ -416,7 +411,7 @@ UNIT lpt_unit = { REG lpt_reg[] = { { ORDATA (STA, lpt_sta, 18) }, - { ORDATA (MA, M[LPT_MA], 18) }, + { ORDATA (CA, M[LPT_CA], 18) }, { FLDATA (INT, int_req, INT_V_LPT) }, { FLDATA (ENABLE, lpt_ie, 0) }, { DRDATA (LCNT, lcnt, 9) }, @@ -425,7 +420,7 @@ REG lpt_reg[] = { { 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 }, + { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE) }, { NULL } }; DEVICE lpt_dev = { @@ -443,8 +438,8 @@ 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; + header = M[(M[LPT_CA] + 1) & ADDRMASK]; /* get first word */ + M[LPT_CA] = (M[LPT_CA] + 2) & 0777777; mode = header & 1; /* mode */ if (pulse == 041) lcnt = 1; /* line count */ else lcnt = (header >> 9) & 0377; @@ -486,9 +481,9 @@ if ((lpt_unit.flags & UNIT_ATT) == 0) { /* not attached? */ 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 */ + w0 = M[(M[LPT_CA] + 1) & ADDRMASK]; /* get first word */ + w1 = M[(M[LPT_CA] + 2) & ADDRMASK]; /* get second word */ + M[LPT_CA] = (M[LPT_CA] + 2) & 0777777; /* advance mem addr */ if (mode) { /* unpacked? */ c[0] = w0 & 0177; c[1] = w1 & 0177; diff --git a/pdp18b_mt.c b/pdp18b_mt.c index 59947832..326cd1c4 100644 --- a/pdp18b_mt.c +++ b/pdp18b_mt.c @@ -1,6 +1,6 @@ -/* 18b PDP magnetic tape simulator +/* pdp18b_mt.c: 18b PDP magnetic tape simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,12 +23,14 @@ 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 + mt (PDP-9) TC59 magtape + (PDP-15) TC59D magtape + 26-Apr-01 RMS Added device enable/disable support + 15-Feb-01 RMS Fixed 3-cycle data break sequence 04-Oct-98 RMS V2.4 magtape format 22-Jan-97 RMS V2.3 magtape format - 29-Jun-96 RMS Added unit disable support + 29-Jun-96 RMS Added unit enable/disable support Magnetic tapes are represented as a series of variable records of the form: @@ -55,8 +57,8 @@ #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 +#define MT_WC 032 /* word count */ +#define MT_CA 033 /* current addr */ /* Command/unit - mt_cu */ @@ -107,7 +109,7 @@ /* error flags */ extern int32 M[]; -extern int32 int_req; +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 mt_cu = 0; /* command/unit */ int32 mt_sta = 0; /* status register */ @@ -119,11 +121,6 @@ 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 @@ -146,8 +143,8 @@ UNIT mt_unit[] = { 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) }, + { ORDATA (CA, M[MT_CA], 18) }, { FLDATA (INT, int_req, INT_V_MTA) }, { FLDATA (STOP_IOE, mt_stopioe, 0) }, { DRDATA (TIME, mt_time, 24), PV_LEFT }, @@ -183,6 +180,7 @@ REG mt_reg[] = { REG_HRO }, { GRDATA (FLG7, mt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_MTA), REG_HRO }, { NULL } }; MTAB mt_mod[] = { @@ -244,7 +242,7 @@ 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 uint8 dbuf[(3 * DBSIZE)]; static t_mtrlnt bceof = { 0 }; u = uptr -> UNUM; /* get unit number */ @@ -297,8 +295,9 @@ case FN_CMPARE: /* read/compare */ 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; + M[MT_WC] = (M[MT_WC] + 1) & 0777777; /* inc WC, CA */ + M[MT_CA] = (M[MT_CA] + 1) & 0777777; + xma = M[MT_CA] & ADDRMASK; if (PACKED (mt_cu)) { /* packed? */ c1 = dbuf[p++] & 077; c2 = dbuf[p++] & 077; @@ -311,8 +310,7 @@ case FN_CMPARE: /* read/compare */ 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; } + break; } } uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt)); break; @@ -321,16 +319,16 @@ case FN_WRITE: /* write */ 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; + for (i = p = 0; i < wc; i++) { /* copy buf to tape */ + M[MT_WC] = (M[MT_WC] + 1) & 0777777; /* inc WC, CA */ + M[MT_CA] = (M[MT_CA] + 1) & 0777777; + xma = M[MT_CA] & 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; } + dbuf[p++] = M[xma] & 0377; } } fxwrite (dbuf, sizeof (char), (tbc + 1) & ~1, uptr -> fileref); fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); err = ferror (uptr -> fileref); @@ -348,7 +346,6 @@ case FN_WREOF: 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? */ @@ -364,7 +361,6 @@ case FN_SPACEF: /* space forward */ 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; } diff --git a/pdp18b_rf.c b/pdp18b_rf.c index 86d863b8..a3b643be 100644 --- a/pdp18b_rf.c +++ b/pdp18b_rf.c @@ -1,6 +1,6 @@ /* pdp18b_rf.c: fixed head disk simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,9 +23,11 @@ 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 + rf (PDP-9) RF09/RF09 + (PDP-15) RF15/RS09 + 26-Apr-01 RMS Added device enable/disable support + 15-Feb-01 RMS Fixed 3 cycle data break sequencing 30-Nov-99 RMS Added non-zero requirement to rf_time 14-Apr-99 RMS Changed t_addr to unsigned @@ -50,7 +52,7 @@ #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 */ +#define RF_CA 037 /* current addr */ /* Function/status register */ @@ -83,7 +85,7 @@ #define RF_BUSY (sim_is_active (&rf_unit)) extern int32 M[]; -extern int32 int_req; +extern int32 int_req, dev_enb; extern UNIT cpu_unit; int32 rf_sta = 0; /* status register */ int32 rf_da = 0; /* disk address */ @@ -95,9 +97,6 @@ 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 @@ -113,8 +112,8 @@ UNIT rf_unit = 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 (CA, M[RF_CA], 18) }, { ORDATA (BUF, rf_dbuf, 18) }, { FLDATA (INT, int_req, INT_V_RF) }, { ORDATA (WLK0, rf_wlk[0], 16) }, @@ -128,6 +127,7 @@ REG rf_reg[] = { { DRDATA (TIME, rf_time, 24), PV_LEFT + REG_NZ }, { FLDATA (BURST, rf_burst, 0) }, { FLDATA (STOP_IOE, rf_stopioe, 0) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RF), REG_HRO }, { NULL } }; DEVICE rf_dev = { @@ -207,7 +207,8 @@ if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ 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 */ +do { M[RF_WC] = (M[RF_WC] + 1) & 0777777; /* incr word count */ + pa = M[RF_CA] = (M[RF_CA] + 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? */ @@ -227,8 +228,7 @@ do { pa = M[RF_MA] = (M[RF_MA] + 1) & ADDRMASK; /* incr mem 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 */ + break; } } 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? */ diff --git a/pdp18b_rp.c b/pdp18b_rp.c index 24b3b566..68e8958e 100644 --- a/pdp18b_rp.c +++ b/pdp18b_rp.c @@ -1,6 +1,6 @@ -/* RP15/RP02 disk pack simulator +/* pdp18b_rp.c: RP15/RP02 disk pack simulator - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -25,8 +25,9 @@ rp RP15/RP02 disk pack + 26-Apr-01 RMS Added device enable/disable support 14-Apr-99 RMS Changed t_addr to unsigned - 29-Jun-96 RMS Added unit disable support + 29-Jun-96 RMS Added unit enable/disable support */ #include "pdp18b_defs.h" @@ -123,7 +124,7 @@ #define MAX(x,y) (((x) > (y))? (x): (y)) extern int32 M[]; -extern int32 int_req, nexm; +extern int32 int_req, dev_enb, nexm; extern UNIT cpu_unit; int32 rp_sta = 0; /* status A */ int32 rp_stb = 0; /* status B */ @@ -139,11 +140,6 @@ 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 @@ -190,6 +186,7 @@ REG rp_reg[] = { REG_HRO }, { GRDATA (FLG7, rp_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_RP), REG_HRO }, { NULL } }; MTAB rp_mod[] = { diff --git a/pdp18b_stddev.c b/pdp18b_stddev.c index 0e39e647..b4d5ad4f 100644 --- a/pdp18b_stddev.c +++ b/pdp18b_stddev.c @@ -1,6 +1,6 @@ /* pdp18b_stddev.c: 18b PDP's standard devices - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,30 +23,33 @@ 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. - 22-Dec-00 RMS Added PDP-9/15 half duplex support - 30-Nov-00 RMS Fixed PDP-4/7 bootstrap loader for 4K systems - 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 + + 10-Mar-01 RMS Added funny format loader support + 05-Mar-01 RMS Added clock calibration support + 22-Dec-00 RMS Added PDP-9/15 half duplex support + 30-Nov-00 RMS Fixed PDP-4/7 bootstrap loader for 4K systems + 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 */ #include "pdp18b_defs.h" #include -extern int32 int_req, saved_PC; extern int32 M[]; +extern int32 int_req, saved_PC; extern UNIT cpu_unit; 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; +int32 clk_tps = 60; t_stat clk_svc (UNIT *uptr); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); @@ -62,8 +65,8 @@ 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 int32 sim_rtc_init (int32 time); +extern int32 sim_rtc_calb (int32 tps); extern t_stat sim_poll_kbd (void); extern t_stat sim_putchar (int32 out); @@ -74,13 +77,14 @@ extern t_stat sim_putchar (int32 out); clk_reg CLK register list */ -UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 5000 }; +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; 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 }, + { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, { NULL } }; DEVICE clk_dev = { @@ -281,7 +285,9 @@ 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 */ + if (!sim_is_active (&clk_unit)) /* already on? */ + sim_activate (&clk_unit, /* start */ + sim_rtc_init (clk_unit.wait)); } /* init calibr */ return AC; } @@ -292,7 +298,8 @@ 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 */ + sim_activate (&clk_unit, /* reactivate unit */ + sim_rtc_calb (clk_tps)); } /* calibr delay */ return SCPE_OK; } @@ -414,35 +421,154 @@ return detach_unit (uptr); used to remove addr<5> for a 4K system. */ -#define BOOT_START 017762 -#define BOOT_PC 017770 +#define BOOT_START 017577 +#define BOOT_FPC 017577 +#define BOOT_RPC 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 */ + 0117762, /* ff, jsb r1b */ + 0057666, /* dac done 1 */ + 0117762, /* jms r1b */ + 0057667, /* dac done 2 */ + 0117762, /* jms r1b */ + 0040007, /* dac conend */ + 0057731, /* dac conbeg */ + 0440007, /* isz conend */ + 0117762, /* blk, jms r1b */ + 0057673, /* dac cai */ + 0741100, /* spa */ + 0617665, /* jmp done */ + 0117762, /* jms r1b */ + 0057777, /* dac tem1 */ + 0317673, /* add cai */ + 0057775, /* dac cks */ + 0117713, /* jms r1a */ + 0140010, /* dzm word */ + 0457777, /* cont, isz tem1 */ + 0617632, /* jmp cont1 */ + 0217775, /* lac cks */ + 0740001, /* cma */ + 0740200, /* sza */ + 0740040, /* hlt */ + 0700144, /* rsb */ + 0617610, /* jmp blk */ + 0117713, /* cont1, jms r1a */ + 0057762, /* dac tem2 */ + 0117713, /* jms r1a */ + 0742010, /* rtl */ + 0742010, /* rtl */ + 0742010, /* rtl */ + 0742010, /* rtl */ + 0317762, /* add tem2 */ + 0057762, /* dac tem2 */ + 0117713, /* jms r1a */ + 0742020, /* rtr */ + 0317726, /* add cdsp */ + 0057713, /* dac r1a */ + 0517701, /* and ccma */ + 0740020, /* rar */ + 0317762, /* add tem2 */ + 0437713, /* xct i r1a */ + 0617622, /* jmp cont */ + 0617672, /* dsptch, jmp code0 */ + 0617670, /* jmp code1 */ + 0617700, /* jmp code2 */ + 0617706, /* jmp code3 */ + 0417711, /* xct code4 */ + 0617732, /* jmp const */ + 0740000, /* nop */ + 0740000, /* nop */ + 0740000, /* nop */ + 0200007, /* done, lac conend */ + 0740040, /* xx */ + 0740040, /* xx */ + 0517727, /* code1, and imsk */ + 0337762, /* add i tem2 */ + 0300010, /* code0, add word */ + 0740040, /* cai, xx */ + 0750001, /* clc */ + 0357673, /* tad cai */ + 0057673, /* dac cai */ + 0617621, /* jmp cont-1 */ + 0711101, /* code2, spa cla */ + 0740001, /* ccma, cma */ + 0277762, /* xor i tem2 */ + 0300010, /* add word */ + 0040010, /* code2a, dac word */ + 0617622, /* jmp cont */ + 0057711, /* code3, dac code4 */ + 0217673, /* lac cai */ + 0357701, /* tad ccma */ + 0740040, /* code4, xx */ + 0617622, /* jmp cont */ + 0000000, /* r1a, 0 */ + 0700101, /* rsf */ + 0617714, /* jmp .-1 */ + 0700112, /* rrb */ + 0700104, /* rsa */ + 0057730, /* dac tem */ + 0317775, /* add cks */ + 0057775, /* dac cks */ + 0217730, /* lac tem */ + 0744000, /* cll */ + 0637713, /* jmp i r1a */ + 0017654, /* cdsp, dsptch */ + 0760000, /* imsk, 760000 */ + 0000000, /* tem, 0 */ + 0000000, /* conbeg, 0 */ + 0300010, /* const, add word */ + 0060007, /* dac i conend */ + 0217731, /* lac conbeg */ + 0040010, /* dac index */ + 0220007, /* lac i conend */ + 0560010, /* con1, sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0617737, /* jmp con1 */ + 0200010, /* find, lac index */ + 0540007, /* sad conend */ + 0440007, /* isz conend */ + 0617704, /* jmp code2a */ + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, /* r1b, 0 */ + 0700101, /* rsf */ + 0617763, /* jmp .-1 */ + 0700112, /* rrb */ + 0700144, /* rsb */ + 0637762, /* jmp i r1b */ + 0700144, /* go, rsb */ + 0117762, /* g, jms r1b */ + 0057775, /* dac cks */ + 0417775, /* xct cks */ + 0117762, /* jms r1b */ + 0000000, /* cks, 0 */ + 0617771 /* jmp g */ }; t_stat ptr_boot (int32 unitno) { -int32 i, mask; +int32 i, mask, wd; +extern int32 sim_switches; if (MEMSIZE < 8192) mask = 0767777; /* 4k? */ else mask = 0777777; -for (i = 0; i < BOOT_LEN; i++) - M[(BOOT_START & mask) + i] = boot_rom[i] & mask; -saved_PC = BOOT_PC & mask; +for (i = 0; i < BOOT_LEN; i++) { + wd = boot_rom[i]; + if ((wd >= 0040000) && (wd < 0640000)) wd = wd & mask; + M[(BOOT_START & mask) + i] = wd; } +saved_PC = ((sim_switches & SWMASK ('F'))? BOOT_FPC: BOOT_RPC) & mask; return SCPE_OK; } diff --git a/pdp18b_sys.c b/pdp18b_sys.c index e96012a1..5f642956 100644 --- a/pdp18b_sys.c +++ b/pdp18b_sys.c @@ -1,6 +1,6 @@ /* pdp18b_sys.c: 18b PDP's simulator interface - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,9 @@ 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-May-01 RMS Fixed bug in RIM loaders + 14-Mar-01 RMS Added extension detection of RIM format tapes + 21-Jan-01 RMS Added DECtape support 30-Nov-00 RMS Added PDP-9,-15 RIM/BIN loader format 30-Oct-00 RMS Added support for examine to file 27-Oct-98 RMS V2.4 load interface @@ -50,6 +53,9 @@ extern DEVICE rp_dev; #if defined (MTA) extern DEVICE mt_dev; #endif +#if defined (DTA) +extern DEVICE dt_dev; +#endif extern UNIT cpu_unit; extern REG cpu_reg[]; extern int32 M[]; @@ -92,6 +98,9 @@ DEVICE *sim_devices[] = { &cpu_dev, #if defined (RP) &rp_dev, #endif +#if defined (DTA) + &dt_dev, +#endif #if defined (MTA) &mt_dev, #endif @@ -134,7 +143,7 @@ return word; jmp addr or hlt */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 origin, val; @@ -145,10 +154,10 @@ for (;;) { origin = val & 017777; if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; if (MEM_ADDR_OK (origin)) M[origin++] = val; } - else if ((val & 0760000) == 0600000) { /* JMP? */ - saved_PC = val & 017777; + else if ((val & 0760000) == OP_JMP) { /* JMP? */ + saved_PC = ((origin - 1) & 060000) | (val & 017777); return SCPE_OK; } - else if (val == 0740040) return SCPE_OK; /* HLT? */ + else if (val == OP_HLT) return SCPE_OK; /* HLT? */ else return SCPE_FMT; } /* error */ return SCPE_FMT; /* error */ } @@ -175,16 +184,18 @@ return SCPE_FMT; /* error */ endblock/ origin (< 0) */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { extern int32 sim_switches; int32 i, bits, origin, count, cksum, val; t_stat r; char gbuf[CBUFSIZE]; +extern t_bool match_ext (char *fnm, char *ext); /* RIM loader */ -if (sim_switches & SWMASK ('R')) { /* RIM load? */ +if ((sim_switches & SWMASK ('R')) || /* RIM format? */ + (match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B')))) { if (*cptr != 0) { /* more input? */ cptr = get_glyph (cptr, gbuf, 0); /* get origin */ origin = get_uint (gbuf, 8, ADDRMASK, &r); @@ -192,11 +203,15 @@ if (sim_switches & SWMASK ('R')) { /* RIM load? */ if (*cptr != 0) return SCPE_ARG; } /* no more */ else origin = 0200; /* default 200 */ - for (bits = 0; (bits & 1) == 0; ) { /* word loop */ + for (;;) { /* word loop */ if ((val = getword (fileref, &bits)) < 0) return SCPE_FMT; - saved_PC = origin & ADDRMASK; /* save start */ - if (MEM_ADDR_OK (origin)) M[origin++] = val; } /* store word */ - return SCPE_OK; } /* found word + if (bits & 1) { /* end of tape? */ + if ((val & 0760000) == OP_JMP) saved_PC = + ((origin - 1) & 060000) | (val & 017777); + else if (val != OP_HLT) return SCPE_FMT; + break; } + else if (MEM_ADDR_OK (origin)) M[origin++] = val; } + return SCPE_OK; } /* Binary loader */ @@ -331,6 +346,10 @@ static const char *opcode[] = { "MTTR", "MTCR", "MTSF", "MTRC", "MTAF", "MTRS", "MTGO", "MTCM", "MTLC", #endif +#if defined (DTA) + "DTCA", "DTRA", "DTXA", "DTLA", + "DTEF", "DTRB", "DTDF", +#endif #if defined (PDP7) "ITON", "TTS", "SKP7", "CAF", "SEM", "EEM", "EMIR", "LEM", @@ -470,6 +489,10 @@ static const int32 opc_val[] = { 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 (DTA) + 0707541+I_NPI, 0707552+I_NPN, 0707544+I_NPI, 0707545+I_NPI, + 0707561+I_NPI, 0707572+I_NPN, 0707601+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, diff --git a/pdp1_cpu.c b/pdp1_cpu.c index 832f4fe2..26cec4a5 100644 --- a/pdp1_cpu.c +++ b/pdp1_cpu.c @@ -1,6 +1,6 @@ /* pdp1_cpu.c: PDP-1 CPU simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,8 @@ 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. + cpu PDP-1 central processor + 16-Dec-00 RMS Fixed bug in XCT address calculation 14-Apr-99 RMS Changed t_addr to unsigned @@ -46,11 +48,13 @@ SS<1:6> sense switches TW<0:17> test word (switch register) - Unresolved questions: + 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? + cks: which bits are line printer print done and space done? + cks: is there a bit for sequence break enabled (yes, according + to the 1963 Handbook) + sbs: do sequence breaks accumulate while the system is disabled + (yes, according to the Maintenance Manual) */ /* The PDP-1 has six instruction formats: memory reference, skips, @@ -188,9 +192,10 @@ 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. + PDP-1 has a single break request (flop b2, here sbs). + If sequence breaks are enabled (flop sbm, here sbs), + and one is not already in progress (flop b4, here sbs), + 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 diff --git a/pdp1_defs.h b/pdp1_defs.h index 79d2e2a8..6cf7aa78 100644 --- a/pdp1_defs.h +++ b/pdp1_defs.h @@ -1,6 +1,6 @@ /* pdp1_defs.h: 18b PDP simulator definitions - Copyright (c) 1993-1999, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -27,7 +27,7 @@ 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 is based on the 1962 maintenance manual. This simulator implements the following options: @@ -61,7 +61,7 @@ #define EPCMASK (AMASK & ~DAMASK) /* extended addr */ #define IA 010000 /* indirect flag */ #define IO_WAIT 010000 /* I/O sync wait */ -#define IO_CPLS 004000 /* cmopletion pulse */ +#define IO_CPLS 004000 /* completion pulse */ #define GEN_CPLS(x) (((x) ^ ((x) << 1)) & IO_WAIT) /* completion pulse? */ /* IOT subroutine return codes */ diff --git a/pdp1_lp.c b/pdp1_lp.c index 4ed133e8..cc6f68aa 100644 --- a/pdp1_lp.c +++ b/pdp1_lp.c @@ -1,6 +1,6 @@ /* pdp1_lp.c: PDP-1 line printer simulator - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -24,6 +24,8 @@ in this Software without prior written authorization from Robert M Supnik. lpt Type 62 line printer for the PDP-1 + + 13-Apr-01 RMS Revised for register arrays */ #include "pdp1_defs.h" @@ -32,12 +34,9 @@ #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 }; - +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 @@ -59,7 +58,7 @@ REG lpt_reg[] = { { 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 }, + { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE) }, { NULL } }; DEVICE lpt_dev = { diff --git a/pdp1_stddev.c b/pdp1_stddev.c index a8cc55bd..d35843a3 100644 --- a/pdp1_stddev.c +++ b/pdp1_stddev.c @@ -1,6 +1,6 @@ /* pdp1_stddev.c: PDP-1 standard devices - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -54,8 +54,6 @@ 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); diff --git a/pdp1_sys.c b/pdp1_sys.c index 6a837dd1..fbc23590 100644 --- a/pdp1_sys.c +++ b/pdp1_sys.c @@ -1,6 +1,6 @@ /* pdp1_sys.c: PDP-1 simulator interface - Copyright (c) 1993-2000, Robert M. Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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-Mar-01 RMS Revised load/dump interface (again) 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 @@ -90,7 +91,7 @@ for (i = 0; i < 3;) { return word; } -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 origin, val; diff --git a/pdp4.c b/pdp4.c deleted file mode 100644 index 05342b04..00000000 --- a/pdp4.c +++ /dev/null @@ -1 +0,0 @@ -#define PDP4 0 diff --git a/pdp7.c b/pdp7.c deleted file mode 100644 index 39fb8f88..00000000 --- a/pdp7.c +++ /dev/null @@ -1 +0,0 @@ -#define PDP7 0 diff --git a/pdp8_clk.c b/pdp8_clk.c index 6e97b1e3..fa09d9ae 100644 --- a/pdp8_clk.c +++ b/pdp8_clk.c @@ -1,6 +1,6 @@ /* pdp8_clk.c: PDP-8 real-time clock simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,18 +23,20 @@ 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 + clk real time clock + + 05-Mar-01 RMS Added clock calibration support 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; +extern int32 int_req, int_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); +extern int32 sim_rtc_calb (int32 tps); +int32 clk_tps = 60; /* CLK data structures @@ -43,13 +45,14 @@ extern t_stat sim_cancel (UNIT *uptr); clk_reg CLK register list */ -UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 5000 }; +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; REG clk_reg[] = { { FLDATA (DONE, dev_done, INT_V_CLK) }, - { FLDATA (ENABLE, dev_enable, INT_V_CLK) }, + { FLDATA (ENABLE, int_enable, INT_V_CLK) }, { FLDATA (INT, int_req, INT_V_CLK) }, { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, { NULL } }; DEVICE clk_dev = { @@ -68,11 +71,11 @@ 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_enable = int_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_enable = int_enable & ~INT_CLK; /* disable clk ints */ int_req = int_req & ~INT_CLK; /* update interrupts */ return AC; case 3: /* CLSC */ @@ -82,8 +85,8 @@ case 3: /* CLSC */ 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; + if (AC & 1) int_enable = int_enable | INT_CLK; /* test AC<11> */ + else int_enable = int_enable & ~INT_CLK; int_req = INT_UPDATE; /* update interrupts */ return AC; case 6: /* CLCL */ @@ -102,7 +105,7 @@ 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 */ +sim_activate (&clk_unit, sim_rtc_calb (clk_tps)); /* reactivate unit */ return SCPE_OK; } @@ -112,7 +115,7 @@ 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 */ +int_enable = int_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 index 16766c1a..af7b9a1d 100644 --- a/pdp8_cpu.c +++ b/pdp8_cpu.c @@ -1,6 +1,6 @@ /* pdp8_cpu.c: PDP-8 CPU simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,12 @@ 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. + cpu central processor + + 25-Apr-01 RMS Added device enable/disable support + 18-Mar-01 RMS Added DF32 support + 05-Mar-01 RMS Added clock calibration support + 15-Feb-01 RMS Added DECtape support 14-Apr-99 RMS Changed t_addr to unsigned The register state for the PDP-8 is: @@ -143,7 +149,7 @@ 2. Interrupts. Interrupts are maintained by three parallel variables: dev_done device done flags - dev_enable device interrupt enable flags + int_enable interrupt enable flags int_req interrupt requests In addition, int_req contains the interrupt enable flag, the @@ -173,7 +179,7 @@ #define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) -unsigned int16 M[MAXMEMSIZE] = { 0 }; /* main memory */ +uint16 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 */ @@ -187,9 +193,10 @@ 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_enable = INT_INIT_ENABLE; /* intr enables */ int32 int_req = 0; /* intr requests */ +int32 dev_enb = -1 & ~INT_DF; /* device enables */ int32 ibkpt_addr = ILL_ADR_FLAG | ADDRMASK; /* breakpoint addr */ int32 stop_inst = 0; /* trap on ill inst */ extern int32 sim_int_char; @@ -199,7 +206,6 @@ 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 @@ -233,12 +239,13 @@ REG cpu_reg[] = { { 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 }, + { ORDATA (ENABLE, int_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) }, + { ORDATA (DEVENB, dev_enb, 32), REG_HRO }, { NULL } }; MTAB cpu_mod[] = { @@ -267,6 +274,8 @@ register int32 IR, MB, IF, DF, LAC, MQ; register t_addr PC, MA; int32 device, pulse, temp, iot_data; t_stat reason; +extern UNIT clk_unit; +extern int32 sim_rtc_init (int32 time); extern int32 tti (int32 pulse, int32 AC); extern int32 tto (int32 pulse, int32 AC); extern int32 ptr (int32 pulse, int32 AC); @@ -275,6 +284,9 @@ 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 df60 (int32 pulse, int32 AC); +extern int32 df61 (int32 pulse, int32 AC); +extern int32 df62 (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); @@ -282,6 +294,8 @@ 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); +extern int32 dt76 (int32 pulse, int32 AC); +extern int32 dt77 (int32 pulse, int32 AC); /* Restore register state */ @@ -292,6 +306,7 @@ LAC = saved_LAC & 017777; MQ = saved_MQ & 07777; int_req = INT_UPDATE; reason = 0; +sim_rtc_init (clk_unit.wait); /* init calibration */ /* Main instruction fetch/decode loop */ @@ -898,7 +913,7 @@ case 030:case 031:case 032:case 033: /* IOT */ emode = 0; int_req = int_req & INT_NO_CIF_PENDING; dev_done = 0; - dev_enable = INT_INIT_ENABLE; + int_enable = INT_INIT_ENABLE; LAC = 0; break; } /* end switch pulse */ continue; /* skip rest of IOT */ @@ -993,35 +1008,54 @@ case 030:case 031:case 032:case 033: /* IOT */ case 013: /* CLK */ iot_data = clk (pulse, iot_data); break; - case 060: /* RF08 */ - iot_data = rf60 (pulse, iot_data); + case 060: /* DF32/RF08 */ + if (dev_enb & INT_DF) iot_data = df60 (pulse, iot_data); + else if (dev_enb & INT_RF) iot_data = rf60 (pulse, iot_data); + else reason = stop_inst; break; case 061: - iot_data = rf61 (pulse, iot_data); + if (dev_enb & INT_DF) iot_data = df61 (pulse, iot_data); + else if (dev_enb & INT_RF) iot_data = rf61 (pulse, iot_data); + else reason = stop_inst; break; case 062: - iot_data = rf62 (pulse, iot_data); + if (dev_enb & INT_DF) iot_data = df62 (pulse, iot_data); + else if (dev_enb & INT_RF) iot_data = rf62 (pulse, iot_data); + else reason = stop_inst; break; case 064: - iot_data = rf64 (pulse, iot_data); + if (dev_enb & INT_RF) iot_data = rf64 (pulse, iot_data); + else reason = stop_inst; break; case 066: /* LPT */ iot_data = lpt (pulse, iot_data); break; case 070: /* TM8E */ - iot_data = mt70 (pulse, iot_data); + if (dev_enb & INT_MT) iot_data = mt70 (pulse, iot_data); + else reason = stop_inst; break; case 071: - iot_data = mt71 (pulse, iot_data); + if (dev_enb & INT_MT) iot_data = mt71 (pulse, iot_data); + else reason = stop_inst; break; case 072: - iot_data = mt72 (pulse, iot_data); + if (dev_enb & INT_MT) iot_data = mt72 (pulse, iot_data); + else reason = stop_inst; break; case 074: /* RK8E */ - iot_data = rk (pulse, iot_data); + if (dev_enb & INT_RK) iot_data = rk (pulse, iot_data); + else reason = stop_inst; break; case 075: /* RX8E */ - iot_data = rx (pulse, iot_data); + if (dev_enb & INT_RX) iot_data = rx (pulse, iot_data); + break; + case 076: /* TC01/TC08 */ + if (dev_enb & INT_DTA) iot_data = dt76 (pulse, iot_data); + else reason = stop_inst; + break; + case 077: + if (dev_enb & INT_DTA) iot_data = dt77 (pulse, iot_data); + else reason = stop_inst; break; } /* end switch device */ LAC = (LAC & 010000) | (iot_data & 07777); if (iot_data & IOT_SKP) PC = (PC + 1) & 07777; diff --git a/pdp8_defs.h b/pdp8_defs.h index 7ac848d2..68206c1a 100644 --- a/pdp8_defs.h +++ b/pdp8_defs.h @@ -1,6 +1,6 @@ /* pdp8_defs.h: PDP-8 simulator definitions - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,9 +23,11 @@ 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-Mar-01 RMS Added DF32 support + 15-Feb-01 RMS Added DECtape support 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. + 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 @@ -84,10 +86,12 @@ #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_DF (INT_V_DIRECT+3) /* DF32 */ +#define INT_V_MT (INT_V_DIRECT+4) /* TM8E */ +#define INT_V_DTA (INT_V_DIRECT+5) /* TC08 */ +#define INT_V_PWR (INT_V_DIRECT+6) /* power int */ +#define INT_V_UF (INT_V_DIRECT+7) /* user int */ +#define INT_V_OVHD (INT_V_DIRECT+8) /* 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 */ @@ -101,7 +105,9 @@ #define INT_RX (1 << INT_V_RX) #define INT_RK (1 << INT_V_RK) #define INT_RF (1 << INT_V_RF) +#define INT_DF (1 << INT_V_DF) #define INT_MT (1 << INT_V_MT) +#define INT_DTA (1 << INT_V_DTA) #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) @@ -111,4 +117,4 @@ #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)) +#define INT_UPDATE ((int_req & ~INT_DEV_ENABLE) | (dev_done & int_enable)) diff --git a/pdp8_df.c b/pdp8_df.c new file mode 100644 index 00000000..d6421fa0 --- /dev/null +++ b/pdp8_df.c @@ -0,0 +1,278 @@ +/* pdp8_df.c: DF32 fixed head disk simulator + + Copyright (c) 1993-2001, 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. + + df DF32 fixed head disk + + 25-Apr-01 RMS Added device enable/disable support + + The DF32 is a head-per-track disk. It uses the three cycle data break + facility. To minimize overhead, the entire DF32 is buffered in memory. + + Two timing parameters are provided: + + df_time Interword timing, must be non-zero + df_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst +*/ + +#include "pdp8_defs.h" +#include + +/* Constants */ + +#define DF_NUMWD 2048 /* words/track */ +#define DF_NUMTR 16 /* tracks/disk */ +#define DF_NUMDK 4 /* disks/controller */ +#define DF_SIZE (DF_NUMDK * DF_NUMTR * DF_NUMWD) /* words/drive */ +#define DF_WC 07750 /* word count */ +#define DF_MA 07751 /* mem address */ +#define DF_WMASK (DF_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define DF_READ 2 /* read */ +#define DF_WRITE 4 /* write */ + +/* Status register */ + +#define DFS_PCA 04000 /* photocell status */ +#define DFS_DEX 03700 /* disk addr extension */ +#define DFS_MEX 00070 /* mem addr extension */ +#define DFS_DRL 00004 /* data late error */ +#define DFS_WLS 00002 /* write lock error */ +#define DFS_PER 00001 /* parity error */ +#define DFS_ERR (DFS_DRL + DFS_WLS + DFS_PER) +#define DFS_V_DEX 6 +#define DFS_V_MEX 3 + +#define GET_MEX(x) (((x) & DFS_MEX) << (12 - DFS_V_MEX)) +#define GET_DEX(x) (((x) & DFS_DEX) << (12 - DFS_V_DEX)) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DF_NUMWD))) +#define UPDATE_PCELL if (GET_POS (df_time) < 6) df_sta = df_sta | DFS_PCA; \ + else df_sta = df_sta & ~DFS_PCA + +extern uint16 M[]; +extern int32 int_req, dev_enb, stop_inst; +extern UNIT cpu_unit; +extern int32 rf_devenb; +int32 df_sta = 0; /* status register */ +int32 df_da = 0; /* disk address */ +int32 df_done = 0; /* done flag */ +int32 df_wlk = 0; /* write lock */ +int32 df_time = 10; /* inter-word time */ +int32 df_burst = 1; /* burst mode flag */ +int32 df_stopioe = 1; /* stop on error */ +t_stat df_svc (UNIT *uptr); +t_stat pcell_svc (UNIT *uptr); +t_stat df_reset (DEVICE *dptr); +t_stat df_boot (int32 unitno); + +/* DF32 data structures + + df_dev RF device descriptor + df_unit RF unit descriptor + pcell_unit photocell timing unit (orphan) + df_reg RF register list +*/ + +UNIT df_unit = + { UDATA (&df_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DF_SIZE) }; + +REG df_reg[] = { + { ORDATA (STA, df_sta, 12) }, + { ORDATA (DA, df_da, 12) }, + { ORDATA (WC, M[DF_WC], 12) }, + { ORDATA (MA, M[DF_MA], 12) }, + { FLDATA (DONE, df_done, 0) }, + { FLDATA (INT, int_req, INT_V_DF) }, + { ORDATA (WLS, df_wlk, 8) }, + { DRDATA (TIME, df_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (BURST, df_burst, 0) }, + { FLDATA (STOP_IOE, df_stopioe, 0) }, + { FLDATA (*DEVENB, dev_enb, INT_V_DF), REG_HRO }, + { NULL } }; + +DEVICE df_dev = { + "DF", &df_unit, df_reg, NULL, + 1, 8, 17, 1, 8, 12, + NULL, NULL, &df_reset, + &df_boot, NULL, NULL }; + +/* IOT routines */ + +int32 df60 (int32 pulse, int32 AC) +{ +int32 t; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DCMA */ + df_da = 0; /* clear disk addr */ + df_done = 0; /* clear done */ + df_sta = df_sta & ~DFS_ERR; /* clear errors */ + int_req = int_req & ~INT_DF; } /* clear int req */ +if (pulse & 6) { /* DMAR, DMAW */ + df_da = df_da | AC; /* disk addr |= AC */ + df_unit.FUNC = pulse & ~1; /* save function */ + t = (df_da & DF_WMASK) - GET_POS (df_time); /* delta to new loc */ + if (t < 0) t = t + DF_NUMWD; /* wrap around? */ + sim_activate (&df_unit, t * df_time); /* schedule op */ + AC = 0; } /* clear AC */ +return AC; +} + +/* Based on the hardware implementation. DEAL and DEAC work as follows: + + 6615 pulse 1 = clear df_sta + pulse 4 = df_sta = df_sta | AC + AC = AC | old_df_sta + 6616 pulse 2 = clear AC, skip if address confirmed + pulse 4 = df_sta = df_sta | AC = 0 (nop) + AC = AC | old_df_sta +*/ + +int32 df61 (int32 pulse, int32 AC) +{ +int32 old_df_sta = df_sta; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) /* DCEA */ + df_sta = df_sta & ~(DFS_DEX | DFS_MEX); /* clear dex, mex */ +if (pulse & 2) /* DSAC */ + AC = ((df_da & DF_WMASK) == GET_POS (df_time))? IOT_SKP: 0; +if (pulse & 4) { + df_sta = df_sta | (AC & (DFS_DEX | DFS_MEX)); /* DEAL */ + AC = AC | old_df_sta; } /* DEAC */ +return AC; +} + +int32 df62 (int32 pulse, int32 AC) +{ +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DFSE */ + if ((df_sta & DFS_ERR) == 0) AC = AC | IOT_SKP; } +if (pulse & 2) { /* DFSC */ + if (pulse & 4) AC = AC & ~07777; /* for DMAC */ + else if (df_done) AC = AC | IOT_SKP; } +if (pulse & 4) AC = AC | df_da; /* DMAC */ +return AC; +} + +/* 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 df_svc (UNIT *uptr) +{ +int32 pa, t, mex; +t_addr da; + +UPDATE_PCELL; /* update photocell */ +if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ + df_done = 1; + int_req = int_req | INT_DF; /* update int req */ + return IORETURN (df_stopioe, SCPE_UNATT); } + +mex = GET_MEX (df_sta); +da = GET_DEX (df_sta) | df_da; /* form disk addr */ +do { M[DF_WC] = (M[DF_WC] + 1) & 07777; /* incr word count */ + M[DF_MA] = (M[DF_MA] + 1) & 07777; /* incr mem addr */ + pa = mex | M[DF_MA]; /* add extension */ + if (uptr -> FUNC == DF_READ) { + if (MEM_ADDR_OK (pa)) /* read, check nxm */ + M[pa] = *(((int16 *) uptr -> filebuf) + da); } + else { t = (da >> 14) & 07; + if ((df_wlk >> t) & 1) df_sta = df_sta | DFS_WLS; + else { *(((int16 *) uptr -> filebuf) + da) = M[pa]; + if (da >= uptr -> hwmark) + uptr -> hwmark = da + 1; } } + da = (da + 1) & 0377777; } /* incr disk addr */ +while ((M[DF_WC] != 0) && (df_burst != 0)); /* brk if wc, no brst */ + +if (M[DF_WC] != 0) /* more to do? */ + sim_activate (&df_unit, df_time); /* sched next */ +else { if (uptr -> FUNC != DF_READ) da = (da - 1) & 0377777; + df_done = 1; /* done */ + int_req = int_req | INT_DF; } /* update int req */ +df_sta = (df_sta & ~DFS_DEX) | ((da >> (12 - DFS_V_DEX)) & DFS_DEX); +df_da = da & 07777; /* separate disk addr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat df_reset (DEVICE *dptr) +{ +if (dev_enb & INT_DF) dev_enb = dev_enb & ~INT_RF; /* either DF or RF */ +df_sta = df_da = 0; +df_done = 1; +int_req = int_req & ~INT_DF; /* clear interrupt */ +sim_cancel (&df_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define OS8_START 07750 +#define OS8_LEN (sizeof (os8_rom) / sizeof (int32)) +#define DM4_START 00200 +#define DM4_LEN (sizeof (dm4_rom) / sizeof (int32)) + +static const int32 os8_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 */ +}; + +static const int32 dm4_rom[] = { + 00200, 07600, /* 0200, CLA CLL */ + 00201, 06603, /* 0201, DMAR ; read */ + 00202, 06622, /* 0202, DFSC ; done? */ + 00203, 05202, /* 0203, JMP .-1 ; no */ + 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ + 07750, 07576, /* 7750, 7576 ; word count */ + 07751, 07576 /* 7751, 7576 ; address */ +}; + +t_stat df_boot (int32 unitno) +{ +int32 i; +extern int32 sim_switches, saved_PC; + +if (sim_switches & SWMASK ('D')) { + for (i = 0; i < DM4_LEN; i = i + 2) + M[dm4_rom[i]] = dm4_rom[i + 1]; + saved_PC = DM4_START; } +else { for (i = 0; i < OS8_LEN; i++) + M[OS8_START + i] = os8_rom[i]; + saved_PC = OS8_START; } +return SCPE_OK; +} diff --git a/pdp8_dt.c b/pdp8_dt.c new file mode 100644 index 00000000..c7acac3e --- /dev/null +++ b/pdp8_dt.c @@ -0,0 +1,1150 @@ +/* pdp8_dt.c: PDP-8 DECtape simulator + + Copyright (c) 1993-2001, 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. + + dt TC08/TU56 DECtape + + 11-May-01 RMS Fixed bug in reset + 25-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor + 15-Mar-01 RMS Added 129th word to PDP-8 format + + PDP-8 DECtapes are represented by fixed length data blocks of 12b words. Two + tape formats are supported: + + 12b 129 words per block + 16b/18b/36b 384 words per block + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape is + + reverse end zone 36000 lines ~ 10 feet + block 0 + : + block n + forward end zone 36000 lines ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 0 + : + trailer word 4 checksum + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. +*/ + +#include "pdp8_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_W_UF 3 /* saved flag width */ +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define DT_WC 07754 /* word count */ +#define DT_CA 07755 /* current addr */ + +/* System independent DECtape constants */ + +#define DT_EZLIN 36000 /* end zone length */ +#define DT_HTLIN 30 /* header/trailer lines */ +#define DT_BLKLN 6 /* blk no line in h/t */ +#define DT_CSMLN 24 /* checksum line in h/t */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 384 /* block size in 12b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) +#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 129 /* block size in 12b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +/* This controller */ + +#define DT_CAPAC D8_CAPAC /* default */ +#define DT_WSIZE D8_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u) -> flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u) -> flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u) -> flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u) -> flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u) -> flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u) -> pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u) -> pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ + +#define DTA_V_UNIT 9 /* unit select */ +#define DTA_M_UNIT 07 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 7 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_MODE 6 /* mode */ +#define DTA_V_FNC 3 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_WRIT 04 /* write */ +#define FNC_WALL 05 /* write all */ +#define FNC_WMRK 06 /* write timing */ +#define DTA_V_ENB 2 /* int enable */ +#define DTA_V_CERF 1 /* clr error flag */ +#define DTA_V_CDTF 0 /* clr DECtape flag */ +#define DTA_FWDRV (1u << (DTA_V_MOT + 1)) +#define DTA_STSTP (1u << DTA_V_MOT) +#define DTA_MODE (1u << DTA_V_MODE) +#define DTA_ENB (1u << DTA_V_ENB) +#define DTA_CERF (1u << DTA_V_CERF) +#define DTA_CDTF (1u << DTA_V_CDTF) +#define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF)) + +#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) +#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) +#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) + +/* Status register B */ + +#define DTB_V_ERF 11 /* error flag */ +#define DTB_V_MRK 10 /* mark trk err */ +#define DTB_V_END 9 /* end zone err */ +#define DTB_V_SEL 8 /* select err */ +#define DTB_V_PAR 7 /* parity err */ +#define DTB_V_TIM 6 /* timing err */ +#define DTB_V_MEX 3 /* memory extension */ +#define DTB_M_MEX 07 +#define DTB_MEX (DTB_M_MEX << DTB_V_MEX) +#define DTB_V_DTF 0 /* DECtape flag */ +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_END (1u << DTB_V_END) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_PAR (1u << DTB_V_PAR) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ + DTB_PAR | DTB_TIM) +#define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX)) + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR 7 /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr -> STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr -> STATE = (uptr -> STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr -> STATE = (uptr -> STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Operation substates */ + +#define DTO_WCO 1 /* wc overflow */ +#define DTO_SOB 2 /* start of block */ + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read, write */ +#define LOG_RA 004 /* read all */ +#define LOG_BL 010 /* block # lblk */ + +#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ + int_req = int_req | INT_DTA; \ + else int_req = int_req & ~INT_DTA; +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern uint16 M[]; +extern int32 int_req, dev_enb; +extern UNIT cpu_unit; +extern int32 sim_switches; +int32 dtsa = 0; /* status A */ +int32 dtsb = 0; /* status B */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_actime = 54000; /* accel time */ +int32 dt_dctime = 72000; /* decel time */ +int32 dt_substate = 0; +int32 dt_log = 0; /* debug */ +int32 dt_logblk = 0; +t_stat dt_svc (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +t_stat dt_boot (int32 unitno); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir); +extern uint32 sim_grtime (void); +extern int32 sim_is_running; + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DT_CAPAC) } }; + +REG dt_reg[] = { + { ORDATA (DTSA, dtsa, 12) }, + { ORDATA (DTSB, dtsb, 12) }, + { FLDATA (INT, int_req, INT_V_DTA) }, + { FLDATA (ENB, dtsa, DTA_V_ENB) }, + { FLDATA (DTF, dtsb, DTB_V_DTF) }, + { FLDATA (ERF, dtsb, DTB_V_ERF) }, + { ORDATA (WC, M[DT_WC], 18) }, + { ORDATA (CA, M[DT_CA], 18) }, + { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, + { DRDATA (ACTIME, dt_actime, 31), REG_NZ }, + { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, + { ORDATA (SUBSTATE, dt_substate, 2) }, + { ORDATA (LOG, dt_log, 4), REG_HIDDEN }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { DRDATA (POS0, dt_unit[0].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS1, dt_unit[1].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS2, dt_unit[2].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS3, dt_unit[3].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS4, dt_unit[4].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS5, dt_unit[5].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS6, dt_unit[6].pos, 31), PV_LEFT + REG_RO }, + { DRDATA (POS7, dt_unit[7].pos, 31), PV_LEFT + REG_RO }, + { ORDATA (STATT0, dt_unit[0].STATE, 18), REG_RO }, + { ORDATA (STATT1, dt_unit[1].STATE, 18), REG_RO }, + { ORDATA (STATT2, dt_unit[2].STATE, 18), REG_RO }, + { ORDATA (STATT3, dt_unit[3].STATE, 18), REG_RO }, + { ORDATA (STATT4, dt_unit[4].STATE, 18), REG_RO }, + { ORDATA (STATT5, dt_unit[5].STATE, 18), REG_RO }, + { ORDATA (STATT6, dt_unit[6].STATE, 18), REG_RO }, + { ORDATA (STATT7, dt_unit[7].STATE, 18), REG_RO }, + { DRDATA (LASTT0, dt_unit[0].LASTT, 32), REG_HRO }, + { DRDATA (LASTT1, dt_unit[1].LASTT, 32), REG_HRO }, + { DRDATA (LASTT2, dt_unit[2].LASTT, 32), REG_HRO }, + { DRDATA (LASTT3, dt_unit[3].LASTT, 32), REG_HRO }, + { DRDATA (LASTT4, dt_unit[4].LASTT, 32), REG_HRO }, + { DRDATA (LASTT5, dt_unit[5].LASTT, 32), REG_HRO }, + { DRDATA (LASTT6, dt_unit[6].LASTT, 32), REG_HRO }, + { DRDATA (LASTT7, dt_unit[7].LASTT, 32), REG_HRO }, + { GRDATA (FLG0, dt_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG1, dt_unit[1].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG2, dt_unit[2].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG3, dt_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG4, dt_unit[4].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG5, dt_unit[5].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG6, dt_unit[6].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { GRDATA (FLG7, dt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), + REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_DTA), REG_HRO }, + { NULL } }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "ENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT, 0, "16b/18b", NULL, NULL }, + { UNIT_8FMT, UNIT_8FMT, "12b", NULL, NULL }, + { 0 } }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &dt_reset, + &dt_boot, &dt_attach, &dt_detach }; + +/* IOT routines */ + +int32 dt76 (int32 pulse, int32 AC) +{ +int32 old_dtsa = dtsa, fnc; +UNIT *uptr; + +if (pulse & 01) AC = AC | dtsa; /* DTRA */ +if (pulse & 06) { /* select */ + if (pulse & 02) dtsa = 0; /* DTCA */ + if (pulse & 04) { /* DTXA */ + if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; + if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; + dtsa = dtsa ^ (AC & DTA_RW); + AC = 0; } /* clr AC */ + if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); + uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if (((uptr -> flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WALL) && (uptr -> flags & UNIT_WLK)) || + ((fnc == FNC_WRIT) && (uptr -> flags & UNIT_WLK))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); + DT_UPDINT; } +return AC; +} + +int32 dt77 (int32 pulse, int32 AC) +{ +if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */ + AC = IOT_SKP | AC; +if (pulse & 02) AC = AC | dtsb; /* DTRB */ +if (pulse & 04) { /* DTLB */ + dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX); + AC = AC & ~07777; } /* clear AC */ +return AC; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit = DTA_GETUNIT (oldf); +UNIT *uptr = dt_dev.units + old_unit; +int32 old_mot = DTS_GETMOT (uptr -> STATE); + +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; } + +/* Command register change + + 1. If change in motion, stop to start + - schedule acceleration + - set function as next state + 2. If change in motion, start to stop + - if not already decelerating (could be reversing), + schedule deceleration + 3. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 4. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 5. If not yet at speed, + - set function as next state + 6. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, prev_fnc, new_fnc; +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +new_unit = DTA_GETUNIT (newf); /* new, old units */ +uptr = dt_dev.units + new_unit; +if ((uptr -> flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, DTB_SEL); /* no, error */ + return; } +prev_mot = DTS_GETMOT (uptr -> STATE); /* previous motion */ +prev_mving = prev_mot != DTS_STOP; /* previous moving? */ +prev_dir = prev_mot & DTS_DIR; /* previous dir? */ +prev_fnc = DTS_GETFNC (uptr -> STATE); /* prev function? */ +new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ +new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ +new_fnc = DTA_GETFNC (newf); /* new function? */ + +if ((prev_mving | new_mving) == 0) return; /* stop to stop */ + +if (new_mving & ~prev_mving) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_actime); /* schedule accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +if (prev_mving & ~new_mving) { /* stop? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); } /* schedule decel */ + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + return; } + +if (prev_dir ^ new_dir) { /* dir chg? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); } /* schedule decel */ + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_actime); /* schedule accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, relpos, newpos; +uint32 oldpos; + +oldpos = uptr -> pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr -> STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr -> STATE); /* set variables */ +dir = DTS_GETMOT (uptr -> STATE) & DTS_DIR; +unum = uptr - dt_dev.units; +if (oldpos == uptr -> pos) /* bump pos */ + uptr -> pos = uptr -> pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr -> pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, DTB_END); /* set ez flag, stop */ + return; } +sim_cancel (uptr); /* cancel cur op */ +dt_substate = DTO_SOB; /* substate = block start */ +switch (fnc) { /* case function */ +case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; +case FNC_MOVE: /* move */ + dt_schedez (uptr, dir); /* sched end zone */ + if (dt_log & LOG_MS) printf ("[DT%d: moving %s]\n", unum, (dir? + "backward": "forward")); + return; /* done */ +case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (dt_log & LOG_MS) printf ("[DT%d: searching %s]\n", unum, + (dir? "backward": "forward")); + break; +case FNC_WRIT: /* write */ +case FNC_READ: /* read */ +case FNC_RALL: /* read all */ +case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, DTB_SEL); + return; } + if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + break; +default: + dt_seterr (uptr, DTB_SEL); /* bad state */ + return; } +sim_activate (uptr, ABS (newpos - ((int32) uptr -> pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr -> LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr -> LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ +case DTS_STOP: /* stop */ + delta = 0; + break; +case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; +case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; udelt = dt_actime / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; +case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; } +if (mot & DTS_DIR) uptr -> pos = uptr -> pos - delta; /* update pos */ +else uptr -> pos = uptr -> pos + delta; +if ((uptr -> pos < 0) || + (uptr -> pos > ((uint32) (DTU_FWDEZ (uptr) + DT_EZLIN)))) { + detach_unit (uptr); /* off reel? */ + uptr -> STATE = uptr -> pos = 0; + unum = uptr - dt_dev.units; + if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ + dt_seterr (uptr, DTB_SEL); /* error */ + return TRUE; } +return FALSE; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr -> STATE); +int16 *bptr = uptr -> filebuf; +int32 unum = uptr - dt_dev.units; +int32 blk, wrd, ma, relpos, dat; +t_addr ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { +case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) return SCPE_OK; /* update pos */ + uptr -> STATE = DTS_NXTSTA (uptr -> STATE); /* advance state */ + if (uptr -> STATE) /* not stopped? */ + sim_activate (uptr, dt_actime); /* must be reversing */ + return SCPE_OK; +case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr -> STATE)); /* adv state, sched */ + return SCPE_OK; +case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ +default: /* other */ + dt_seterr (uptr, DTB_SEL); /* state error */ + return SCPE_OK; } + +/* Functional cases + + Move - must be at end zone + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) return SCPE_OK; /* update pos */ +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; } +blk = DT_LIN2BL (uptr -> pos, uptr); /* get block # */ +switch (fnc) { /* at speed, check fnc */ +case FNC_MOVE: /* move */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; +case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */ + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if (MEM_ADDR_OK (ma)) M[ma] = blk & 07777; /* store block # */ + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; +case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr -> STATE = uptr -> pos = 0; /* no visible action */ + break; + +/* Read has four subcases + + Start of block, not wc ovf - check that DTF is clear, otherwise normal + Normal - increment MA, WC, copy word from tape to memory + if read dir != write dir, bits must be scrambled + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - if end of block reached, timing error, + otherwise, continue to next word +*/ + +case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr -> pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + case DTO_SOB: /* start of block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + if ((dt_log & LOG_RW) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: reading block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous]": "]")); + dt_substate = 0; /* fall through */ + case 0: /* normal read */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = bptr[ba]; /* get tape word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */ + case DTO_WCO: /* wc ovf, not sob */ + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; } /* set DTF */ + break; + case DTO_WCO | DTO_SOB: /* next block */ + if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + else sim_activate (uptr, DT_WSIZE * dt_ltime); + break; } + break; + +/* Write has four subcases + + Start of block, not wc ovf - check that DTF is clear, set block direction + Normal - increment MA, WC, copy word from memory to tape + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + copy 0 to tape + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - schedule end zone +*/ + +case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr -> pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + case DTO_SOB: /* start block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + if ((dt_log & LOG_RW) || ((dt_log & LOG_BL) && (blk == dt_logblk))) + printf ("[DT%d: writing block %d %s%s\n", unum, blk, + (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous]": "]")); + dt_substate = 0; /* fall through */ + case 0: /* normal write */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + case DTO_WCO: /* wc ovflo */ + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = dt_substate? 0: M[ma]; /* get word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + bptr[ba] = dat; /* write word */ + if (ba >= uptr -> hwmark) uptr -> hwmark = ba + 1; + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; } /* set DTF */ + break; + case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; } + break; + +/* Read all has two subcases + + Not word count overflow - increment MA, WC, copy word from tape to memory + Word count overflow - schedule end zone +*/ + +case FNC_RALL: + switch (dt_substate) { /* case on substate */ + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr -> pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dat = bptr[ba]; /* get tape word */ + if (dir) dat = dt_comobv (dat); } /* rev? comp obv */ + else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; } /* end case substate */ + break; + +/* Write all has two subcases + + Not word count overflow - increment MA, WC, copy word from memory to tape + Word count overflow - schedule end zone +*/ + +case FNC_WALL: + switch (dt_substate) { /* case on substate */ + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; } + relpos = DT_LIN2OF (uptr -> pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dat = M[ma]; /* get mem word */ + if (dir) dat = dt_comobv (dat); + wrd = DT_LIN2WD (uptr -> pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + bptr[ba] = dat; /* write word */ + if (ba >= uptr -> hwmark) uptr -> hwmark = ba + 1; } +/* /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; } /* end case substate */ + break; +default: + dt_seterr (uptr, DTB_SEL); /* impossible state */ + break; } +DT_UPDINT; /* update interrupts */ +return SCPE_OK; +} + +/* Reading the header is complicated, because 18b words are being parsed + out 12b at a time. The sequence of word numbers is directionally + sensitive + + Forward Reverse + Word Word Content Word Word Content + (abs) (rel) (abs) (rel) + + 137 8 rev csm'00 6 6 fwd csm'00 + 138 9 0000 5 5 0000 + 139 10 0000 4 4 0000 + 140 11 0000 3 3 0000 + 141 12 00'lo rev blk 2 2 00'lo fwd blk + 142 13 hi rev blk 1 1 hi fwd blk + 143 14 0000 0 0 0000 + 0 0 0000 143 14 0000 + 1 1 0000 142 13 0000 + 2 2 hi fwd blk 141 12 hi rev blk + 3 3 lo fwd blk'00 140 11 lo rev blk'00 + 4 4 0000 139 10 0000 + 5 5 0000 138 9 0000 + 6 6 0000 137 8 0000 + 7 7 00'fwd csm 136 7 00'rev csm +*/ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir) +{ +if (relpos >= DT_HTLIN) relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr)); +if (dir) { /* reverse */ + switch (relpos / DT_WSIZE) { + case 6: /* fwd csum */ + return (dt_comobv (dt_csum (uptr, blk))); + case 2: /* lo fwd blk */ + return dt_comobv ((blk & 077) << 6); + case 1: /* hi fwd blk */ + return dt_comobv (blk >> 6); + case 12: /* hi rev blk */ + return (blk >> 6) & 07777; + case 11: /* lo rev blk */ + return ((blk & 077) << 6); + default: /* others */ + return 077; } } +else { /* forward */ + switch (relpos / DT_WSIZE) { + case 8: /* rev csum */ + return (dt_csum (uptr, blk) << 6); + case 12: /* lo rev blk */ + return dt_comobv ((blk & 077) << 6); + case 13: /* hi rev blk */ + return dt_comobv (blk >> 6); + case 2: /* hi fwd blk */ + return ((blk >> 6) & 07777); + case 3: /* lo fwd blk */ + return ((blk & 077) << 6); + default: /* others */ + break; } } +return 0; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr -> STATE); + +dtsa = dtsa & ~DTA_STSTP; /* clear go */ +dtsb = dtsb | DTB_ERF | e; /* set error flag */ +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); } /* state = decel */ +DT_UPDINT; +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr -> pos)) * dt_ltime); +return; +} + +/* Complement obverse routine */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 07777; /* compl obverse */ +dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) | + ((dat & 070) << 3) | ((dat & 07) << 9); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int16 *bptr = uptr -> filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = bptr[ba + i] ^ 07777; /* get ~word */ + csum = csum ^ (wrd >> 6) ^ wrd; } +return (csum & 077); +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* CAF? */ + prev_mot = DTS_GETMOT (uptr -> STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } } + else { sim_cancel (uptr); /* sim reset */ + uptr -> STATE = 0; + uptr -> LASTT = sim_grtime (); } } +dtsa = dtsb = 0; /* clear status */ +DT_UPDINT; /* reset interrupt */ +return SCPE_OK; +} + +/* Bootstrap routine + + This is actually the 4K disk monitor bootstrap, which also + works with OS/8. The reverse is not true - the OS/8 bootstrap + doesn't work with the disk monitor. +*/ + +#define BOOT_START 0200 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 07600, /* 200, CLA CLL */ + 01216, /* TAD MVB ; move back */ + 04210, /* JMS DO ; action */ + 01217, /* TAD K7577 ; addr */ + 03620, /* DCA I CA */ + 01222, /* TAD RDF ; read fwd */ + 04210, /* JMS DO ; action */ + 05600, /* JMP I 200 ; enter boot */ + 00000, /* DO, 0 */ + 06766, /* DTCA!DTXA ; start tape */ + 03621, /* DCA I WC ; clear wc */ + 06771, /* DTSF ; wait */ + 05213, /* JMP .-1 */ + 05610, /* JMP I DO */ + 00600, /* MVB, 0600 */ + 07577, /* K7577, 7757 */ + 07755, /* CA, 7755 */ + 07754, /* WC, 7754 */ + 00220 /* RF, 0220 */ +}; + +t_stat dt_boot (int32 unitno) +{ +int32 i; +extern int32 saved_PC; + +if (unitno) return SCPE_ARG; /* only unit 0 */ +dt_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Attach routine + + Determine native or PDP11/15 format + Allocate buffer + If native, read data into buffer + If PDP9/11/15, convert 18b data to 12b and read into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint32 pdp18b[D18_NBSIZE]; +int32 k, p; +int16 *bptr; +t_stat r; +t_addr ba; + +uptr -> flags = uptr -> flags | UNIT_8FMT; +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if (sim_switches & SWMASK ('F')) /* att foreign? */ + uptr -> flags = uptr -> flags & ~UNIT_8FMT; /* PDP8 = F */ +else if (!(sim_switches & SWMASK ('N'))) { /* autosize? */ + if ((fseek (uptr -> fileref, 0, SEEK_END) == 0) && + (p = ftell (uptr -> fileref)) && + (p == D18_FILSIZ)) uptr -> flags = uptr -> flags & ~UNIT_8FMT; } +uptr -> capac = DTU_CAPAC (uptr); /* set capacity */ +uptr -> filebuf = calloc (uptr -> capac, sizeof (int16)); +if (uptr -> filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; } +printf ("%DT: buffering file in memory\n"); +rewind (uptr -> fileref); /* start of file */ +bptr = uptr -> filebuf; /* file buffer */ +if (uptr -> flags & UNIT_8FMT) /* PDP8? */ + uptr -> hwmark = fxread (uptr -> filebuf, sizeof (int16), + uptr -> capac, uptr -> fileref); +else { /* PDP9/11/15 */ + for (ba = 0; ba < uptr -> capac; ) { /* loop thru file */ + k = fxread (pdp18b, sizeof (int32), D18_NBSIZE, uptr -> fileref); + if (k == 0) break; + for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; + for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ + bptr[ba] = (pdp18b[k] >> 6) & 07777; + bptr[ba + 1] = ((pdp18b[k] & 077) << 6) | + ((pdp18b[k + 1] >> 12) & 077); + bptr[ba + 2] = pdp18b[k + 1] & 07777; + ba = ba + 3; } /* end blk loop */ + } /* end file loop */ + uptr -> hwmark = ba; } /* end else */ +uptr -> flags = uptr -> flags | UNIT_BUF; /* set buf flag */ +uptr -> pos = DT_EZLIN; /* beyond leader */ +uptr -> LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If native, write buffer to file + If PDP9/11/15, convert 12b buffer to 18b and write to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint32 pdp18b[D18_NBSIZE]; +int32 k; +int32 unum = uptr - dt_dev.units; +int16 *bptr; +t_addr ba; + +if (!(uptr -> flags & UNIT_ATT)) return SCPE_OK; +if (sim_is_active (uptr)) { + sim_cancel (uptr); + if ((unum == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { + dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; + DT_UPDINT; } + uptr -> STATE = uptr -> pos = 0; } +if (uptr -> hwmark) { /* any data? */ + printf ("DT: writing buffer to file\n"); + rewind (uptr -> fileref); /* start of file */ + bptr = uptr -> filebuf; /* file buffer */ + if (uptr -> flags & UNIT_8FMT) /* PDP8? */ + fxwrite (uptr -> filebuf, sizeof (int16), /* write file */ + uptr -> hwmark, uptr -> fileref); + else { /* PDP9/11/15 */ + for (ba = 0; ba < uptr -> hwmark; ) { /* loop thru buf */ + for (k = 0; k < D18_NBSIZE; k = k + 2) { + pdp18b[k] = ((bptr[ba] & 07777) << 6) | + ((bptr[ba + 1] >> 6) & 077); + pdp18b[k + 1] = ((bptr[ba + 1] & 077) << 12) | + (bptr[ba + 2] & 07777); + ba = ba + 3; } /* end loop blk */ + fxwrite (pdp18b, sizeof (int32), + D18_NBSIZE, uptr -> fileref); + } /* end loop buf */ + } /* end else */ + if (ferror (uptr -> fileref)) perror ("I/O error"); + } /* end if hwmark */ +free (uptr -> filebuf); /* release buf */ +uptr -> flags = uptr -> flags & ~UNIT_BUF; /* clear buf flag */ +uptr -> filebuf = NULL; /* clear buf ptr */ +uptr -> flags = uptr -> flags | UNIT_8FMT; /* default fmt */ +uptr -> capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/pdp8_lp.c b/pdp8_lp.c index f322a578..3705d3f2 100644 --- a/pdp8_lp.c +++ b/pdp8_lp.c @@ -1,6 +1,6 @@ /* pdp8_lp.c: PDP-8 line printer simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,20 +23,18 @@ 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 + lpt LP8E line printer */ #include "pdp8_defs.h" -extern int32 int_req, dev_done, dev_enable, stop_inst; +extern int32 int_req, int_enable, dev_done, 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 @@ -52,7 +50,7 @@ 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 (ENABLE, int_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 }, @@ -89,11 +87,11 @@ case 4: /* PSTB */ return AC; } return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC; case 5: /* PSIE */ - dev_enable = dev_enable | INT_LPT; /* set enable */ + int_enable = int_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_enable = int_enable & ~INT_LPT; /* clear enable */ int_req = int_req & ~INT_LPT; /* clear int req */ return AC; default: @@ -124,7 +122,7 @@ 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 */ +int_enable = int_enable | INT_LPT; /* set enable */ lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; sim_cancel (&lpt_unit); /* deactivate unit */ return SCPE_OK; diff --git a/pdp8_mt.c b/pdp8_mt.c index 9656b28a..31cc9ce5 100644 --- a/pdp8_mt.c +++ b/pdp8_mt.c @@ -1,6 +1,6 @@ -/* PDP-8 magnetic tape simulator +/* pdp8_mt.c: PDP-8 magnetic tape simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,9 @@ 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 TM8E/TU10 magtape + + 25-Apr-01 RMS Added device enable/disable support 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 @@ -114,8 +117,8 @@ /* set error */ #define TUR(u) (!sim_is_active (u)) /* tape unit ready */ -extern unsigned int16 M[]; -extern int32 int_req, stop_inst; +extern uint16 M[]; +extern int32 int_req, dev_enb, stop_inst; extern UNIT cpu_unit; int32 mt_cu = 0; /* command/unit */ int32 mt_fn = 0; /* function */ @@ -135,11 +138,6 @@ 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 @@ -203,6 +201,7 @@ REG mt_reg[] = { REG_HRO }, { GRDATA (FLG7, mt_unit[7].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, + { FLDATA (*DEVENB, dev_enb, INT_V_MT), REG_HRO }, { NULL } }; MTAB mt_mod[] = { @@ -276,10 +275,8 @@ case 7: /* LDBR */ 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; + return 0; } /* end switch */ +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ } /* IOTs, continued */ @@ -305,10 +302,8 @@ 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; + return mt_db; } /* read data buffer */ +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ } int32 mt72 (int32 pulse, int32 AC) @@ -330,10 +325,8 @@ case 5: /* CLF */ 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; + return AC; } /* end switch */ +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ } /* Unit service @@ -347,8 +340,8 @@ 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)]; +uint16 c, c1, c2; +uint8 dbuf[(2 * DBSIZE)]; static t_mtrlnt bceof = { 0 }; u = uptr -> UNUM; /* get unit number */ diff --git a/pdp8_pt.c b/pdp8_pt.c index 6575be96..2fded04f 100644 --- a/pdp8_pt.c +++ b/pdp8_pt.c @@ -1,6 +1,6 @@ /* pdp8_pt.c: PDP-8 paper tape reader/punch simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,24 +23,20 @@ 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,ptp PC8E paper tape reader/punch - ptr paper tape reader - ptp paper tape punch + 30-Mar-98 RMS Added RIM loader as PTR bootstrap */ #include "pdp8_defs.h" -extern int32 int_req, dev_done, dev_enable, stop_inst; -extern unsigned int16 M[]; +extern int32 int_req, int_enable, dev_done, stop_inst; 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 @@ -55,7 +51,7 @@ UNIT ptr_unit = { REG ptr_reg[] = { { ORDATA (BUF, ptr_unit.buf, 8) }, { FLDATA (DONE, dev_done, INT_V_PTR) }, - { FLDATA (ENABLE, dev_enable, INT_V_PTR) }, + { FLDATA (ENABLE, int_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 }, @@ -81,7 +77,7 @@ UNIT ptp_unit = { REG ptp_reg[] = { { ORDATA (BUF, ptp_unit.buf, 8) }, { FLDATA (DONE, dev_done, INT_V_PTP) }, - { FLDATA (ENABLE, dev_enable, INT_V_PTP) }, + { FLDATA (ENABLE, int_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 }, @@ -100,7 +96,7 @@ 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_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* RSF */ @@ -149,7 +145,7 @@ 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 */ +int_enable = int_enable | INT_PTR; /* set enable */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } @@ -160,7 +156,7 @@ 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_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* PSF */ @@ -203,7 +199,7 @@ 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 */ +int_enable = int_enable | INT_PTP; /* set enable */ sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } @@ -238,6 +234,7 @@ t_stat ptr_boot (int32 unitno) { int32 i; extern int32 saved_PC; +extern uint16 M[]; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; saved_PC = BOOT_START; diff --git a/pdp8_rf.c b/pdp8_rf.c index e25624c0..c952cde4 100644 --- a/pdp8_rf.c +++ b/pdp8_rf.c @@ -1,6 +1,6 @@ /* pdp8_rf.c: RF08 fixed head disk simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,11 @@ 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 RF08 fixed head disk + + 25-Apr-01 RMS Added device enable/disable support + 19-Mar-01 RMS Added disk monitor bootstrap, fixed IOT decoding + 15-Feb-01 RMS Fixed 3 cycle data break sequence 14-Apr-99 RMS Changed t_addr to unsigned 30-Mar-98 RMS Fixed bug in RF bootstrap. @@ -52,8 +57,8 @@ /* Parameters in the unit descriptor */ #define FUNC u4 /* function */ -#define RF_READ 3 /* read */ -#define RF_WRITE 5 /* write */ +#define RF_READ 2 /* read */ +#define RF_WRITE 4 /* write */ /* Status register */ @@ -81,9 +86,10 @@ int_req = int_req | INT_RF; \ else int_req = int_req & ~INT_RF -extern int32 int_req, stop_inst; -extern unsigned int16 M[]; +extern uint16 M[]; +extern int32 int_req, dev_enb, stop_inst; extern UNIT cpu_unit; +extern int32 df_devenb; int32 rf_sta = 0; /* status register */ int32 rf_da = 0; /* disk address */ int32 rf_done = 0; /* done flag */ @@ -95,8 +101,6 @@ 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 @@ -115,14 +119,15 @@ 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) }, + { ORDATA (MA, M[RF_MA], 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) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RF), REG_HRO }, { NULL } }; DEVICE rf_dev = { @@ -138,22 +143,19 @@ int32 rf60 (int32 pulse, int32 AC) int32 t; UPDATE_PCELL; /* update photocell */ -switch (pulse) { /* decode IR<9:11> */ -case 1: /* DCMA */ +if (pulse & 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 */ + RF_INT_UPDATE; } /* update int req */ +if (pulse & 6) { /* DMAR, DMAW */ + rf_da = rf_da | AC; /* DAR<8:19> |= AC */ + rf_unit.FUNC = pulse & ~1; /* 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 */ + AC = 0; } /* clear AC */ +return AC; } int32 rf61 (int32 pulse, int32 AC) @@ -176,9 +178,8 @@ case 5: /* DIML */ 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 */ + return rf_sta; } /* AC <- STA<0:11> */ +return AC; } /* IOT's, continued */ @@ -186,17 +187,13 @@ default: 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 */ +if (pulse & 1) { /* DFSE */ + if (rf_sta & RFS_ERR) AC = AC | IOT_SKP; } +if (pulse & 2) { /* DFSC */ + if (pulse & 4) AC = AC & ~07777; /* for DMAC */ + else if (rf_done) AC = AC | IOT_SKP; } +if (pulse & 4) AC = AC | (rf_da & 07777); /* DMAC */ +return AC; } int32 rf64 (int32 pulse, int32 AC) @@ -233,7 +230,8 @@ if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */ return IORETURN (rf_stopioe, SCPE_UNATT); } mex = GET_MEX (rf_sta); -do { M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */ +do { M[RF_WC] = (M[RF_WC] + 1) & 07777; /* incr word count */ + 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 */ @@ -243,8 +241,7 @@ do { M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */ 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 */ + rf_da = (rf_da + 1) & 03777777; } /* incr disk addr */ while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ if (M[RF_WC] != 0) /* more to do? */ @@ -269,6 +266,7 @@ return SCPE_OK; t_stat rf_reset (DEVICE *dptr) { +if (dev_enb & INT_RF) dev_enb = dev_enb & ~INT_DF; /* either DF or RF */ rf_sta = rf_da = 0; rf_done = 1; int_req = int_req & ~INT_RF; /* clear interrupt */ @@ -279,10 +277,12 @@ return SCPE_OK; /* Bootstrap routine */ -#define BOOT_START 07750 -#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) +#define OS8_START 07750 +#define OS8_LEN (sizeof (os8_rom) / sizeof (int32)) +#define DM4_START 00200 +#define DM4_LEN (sizeof (dm4_rom) / sizeof (int32)) -static const int32 boot_rom[] = { +static const int32 os8_rom[] = { 07600, /* 7750, CLA CLL ; also word count */ 06603, /* 7751, DMAR ; also address */ 06622, /* 7752, DFSC ; done? */ @@ -290,12 +290,27 @@ static const int32 boot_rom[] = { 05752 /* 7754, JMP @.-2 ; enter boot */ }; +static const int32 dm4_rom[] = { + 00200, 07600, /* 0200, CLA CLL */ + 00201, 06603, /* 0201, DMAR ; read */ + 00202, 06622, /* 0202, DFSC ; done? */ + 00203, 05202, /* 0203, JMP .-1 ; no */ + 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ + 07750, 07576, /* 7750, 7576 ; word count */ + 07751, 07576 /* 7751, 7576 ; address */ +}; + t_stat rf_boot (int32 unitno) { int32 i; -extern int32 saved_PC; +extern int32 sim_switches, saved_PC; -for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; -saved_PC = BOOT_START; +if (sim_switches & SWMASK ('D')) { + for (i = 0; i < DM4_LEN; i = i + 2) + M[dm4_rom[i]] = dm4_rom[i + 1]; + saved_PC = DM4_START; } +else { for (i = 0; i < OS8_LEN; i++) + M[OS8_START + i] = os8_rom[i]; + saved_PC = OS8_START; } return SCPE_OK; } diff --git a/pdp8_rk.c b/pdp8_rk.c index fed86b9a..05d835dc 100644 --- a/pdp8_rk.c +++ b/pdp8_rk.c @@ -1,6 +1,6 @@ /* pdp8_rk.c: RK8E cartridge disk simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,7 +23,10 @@ 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 + rk RK8E/RK05 cartridge disk + + 25-Apr-01 RMS Added device enable/disable support + 29-Jun-96 RMS Added unit enable/disable support */ #include "pdp8_defs.h" @@ -115,8 +118,8 @@ #define RK_MIN 10 #define MAX(x,y) (((x) > (y))? (x): (y)) -extern int32 int_req, stop_inst; -extern unsigned int16 M[]; +extern uint16 M[]; +extern int32 int_req, dev_enb, stop_inst; extern UNIT cpu_unit; int32 rk_busy = 0; /* controller busy */ int32 rk_sta = 0; /* status register */ @@ -129,11 +132,6 @@ 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 @@ -167,6 +165,7 @@ REG rk_reg[] = { { GRDATA (FLG3, rk_unit[3].flags, 8, UNIT_W_UF, UNIT_V_UF - 1), REG_HRO }, { FLDATA (STOP_IOE, rk_stopioe, 0) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RK), REG_HRO }, { NULL } }; MTAB rk_mod[] = { @@ -291,7 +290,7 @@ return; Note that memory addresses wrap around in the current field. */ -static unsigned int16 fill[RK_NUMWD/2] = { 0 }; +static uint16 fill[RK_NUMWD/2] = { 0 }; t_stat rk_svc (UNIT *uptr) { int32 err, wc, wc1, awc, swc, pa, da; diff --git a/pdp8_rx.c b/pdp8_rx.c index 76697d8b..bc2976b9 100644 --- a/pdp8_rx.c +++ b/pdp8_rx.c @@ -1,6 +1,6 @@ /* pdp8_rx.c: RX8E/RX01 floppy disk simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,7 +23,12 @@ 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 + rx RX8E/RX01 floppy disk + + 26-Apr-01 RMS Added device enable/disable support + 13-Apr-01 RMS Revised for register arrays + 14-Apr-99 RMS Changed t_addr to unsigned + 15-Aug-96 RMS Fixed bug in LCD 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 @@ -31,9 +36,6 @@ 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" @@ -82,7 +84,7 @@ #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; +extern int32 int_req, int_enable, dev_done, dev_enb; int32 rx_tr = 0; /* xfer ready flag */ int32 rx_err = 0; /* error flag */ int32 rx_csr = 0; /* control/status */ @@ -96,13 +98,11 @@ 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 */ +uint8 rx_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 @@ -130,7 +130,7 @@ REG rx_reg[] = { { 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 (ENABLE, int_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 }, @@ -138,7 +138,8 @@ REG rx_reg[] = { { 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 }, + { BRDATA (SBUF, rx_buf, 8, 8, RX_NUMBY) }, + { FLDATA (*DEVENB, dev_enb, INT_V_RX), REG_HRO }, { NULL } }; MTAB rx_mod[] = { @@ -226,8 +227,8 @@ case 5: /* SDN */ 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; + if (AC & 1) int_enable = int_enable | INT_RX; + else int_enable = int_enable & ~INT_RX; int_req = INT_UPDATE; return AC; case 7: /* INIT */ @@ -240,10 +241,10 @@ case 7: /* INIT */ 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 + FILL copy dbr to rx_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 + copy rx_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 @@ -270,34 +271,34 @@ case EMPTY: /* empty buffer */ if (bufptr >= RX_NUMBY) { /* done? */ rx_done (rx_esr, 0); /* set done */ break; } /* and exit */ - rx_dbr = buf[bufptr]; } /* else get data */ + rx_dbr = rx_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); } + ((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]: + (rx_buf[byptr] << 4) | ((rx_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 */ + rx_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; } + rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017); + rx_buf[byptr + 1] = rx_dbr & 0377; } else { - buf[byptr] = (rx_dbr >> 4) & 0377; - buf[byptr + 1] = (rx_dbr & 017) << 4; } + rx_buf[byptr] = (rx_dbr >> 4) & 0377; + rx_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_buf[i] = 0; /* else fill sector */ rx_done (rx_esr, 0); } } /* set done */ break; case RWDS: /* wait for sector */ @@ -320,13 +321,13 @@ case RWDT: /* wait for track */ 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); } + rx_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]; + *(((int8 *) uptr -> filebuf) + da + i) = rx_buf[i]; da = da + RX_NUMBY; if (da > uptr -> hwmark) uptr -> hwmark = da; } rx_done (rx_esr, 0); /* done */ @@ -344,7 +345,7 @@ case INIT_COMPLETE: /* init complete */ 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_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 */ @@ -377,11 +378,13 @@ 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; +int_enable = int_enable & ~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)); +if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ + rx_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); } +else rx_done (rx_esr | RXES_ID, 0010); /* no, error */ return SCPE_OK; } @@ -427,7 +430,7 @@ t_stat rx_boot (int32 unitno) { int32 i; extern int32 saved_PC; -extern unsigned int16 M[]; +extern uint16 M[]; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; M[BOOT_INST] = unitno? 07024: 07004; diff --git a/pdp8_sys.c b/pdp8_sys.c index e11c6d2f..f0682172 100644 --- a/pdp8_sys.c +++ b/pdp8_sys.c @@ -1,6 +1,6 @@ /* pdp8_sys.c: PDP-8 simulator interface - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,9 @@ 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-Mar-01 RMS Added DF32 support + 14-Mar-01 RMS Added extension detection of RIM binary tapes + 15-Feb-01 RMS Added DECtape support 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 @@ -38,10 +41,10 @@ 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 DEVICE df_dev, rf_dev; +extern DEVICE dt_dev, mt_dev; extern REG cpu_reg[]; -extern unsigned int16 M[]; +extern uint16 M[]; extern int32 sim_switches; /* SCP data structures and interface routines @@ -60,11 +63,14 @@ REG *sim_PC = &cpu_reg[0]; int32 sim_emax = 4; -DEVICE *sim_devices[] = { &cpu_dev, - &ptr_dev, &ptp_dev, &tti_dev, &tto_dev, +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, + &rk_dev, &rx_dev, + &df_dev, &rf_dev, + &dt_dev, &mt_dev, NULL }; const char *sim_stop_messages[] = { @@ -75,9 +81,9 @@ const char *sim_stop_messages[] = { /* Binary loader - Two loader formats are supported: RIM loader (-r) and BIN loader. + Two loader formats are supported: RIM loader (-r) and BIN (-b) loader. - RIM loader format cosists of alternating pairs of addresses and 12-bit + RIM loader format consists 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 @@ -86,14 +92,16 @@ const char *sim_stop_messages[] = { a character > 0200 indicates a change of field. */ -t_stat sim_load (FILE *fileref, char *cptr, int flag) +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) { int32 rubout, word, low, high, csum, newf, state, i; t_addr origin, field; +extern t_bool match_ext (char *fnam, char *ext); if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; rubout = state = field = newf = origin = csum = 0; -if (sim_switches & SWMASK ('R')) { /* RIM format? */ +if ((sim_switches & SWMASK ('R')) || /* RIM format? */ + (match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B')))) { while ((i = getc (fileref)) != EOF) { switch (state) { case 0: /* leader */ @@ -190,6 +198,7 @@ static const char *opcode[] = { "ADSK", "ADSE", "ADLE", "ADRS", "DCMA", "DMAR", "DMAW", "DCIM", "DSAC", "DIML", "DIMA", + "DCEA", "DEAL", "DEAC", "DFSE", "DFSC", "DISK", "DMAC", "DCXA", "DXAL", "DXAC", "PSKF", "PCLF", "PSKE", @@ -241,6 +250,7 @@ static const int32 opc_val[] = { 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, + 06611+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, diff --git a/pdp8_tt.c b/pdp8_tt.c index 6a0bddca..73d74ee8 100644 --- a/pdp8_tt.c +++ b/pdp8_tt.c @@ -1,6 +1,6 @@ /* pdp8_tt.c: PDP-8 console terminal simulator - Copyright (c) 1993-1999, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,8 +23,7 @@ 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 + tti,tto KL8E terminal input/output */ #include "pdp8_defs.h" @@ -32,13 +31,11 @@ #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; +extern int32 int_req, int_enable, dev_done, 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); @@ -55,7 +52,7 @@ 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 (ENABLE, int_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 }, @@ -85,7 +82,7 @@ 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 (ENABLE, int_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 }, @@ -115,8 +112,8 @@ case 2: /* KCC */ 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); + if (AC & 1) int_enable = int_enable | (INT_TTI+INT_TTO); + else int_enable = int_enable & ~(INT_TTI+INT_TTO); int_req = INT_UPDATE; /* update interrupts */ return AC; case 6: /* KRB */ @@ -152,7 +149,7 @@ 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 */ +int_enable = int_enable | INT_TTI; /* set enable */ sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ return SCPE_OK; } @@ -205,7 +202,7 @@ 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 */ +int_enable = int_enable | INT_TTO; /* set enable */ sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } diff --git a/pdp9.c b/pdp9.c deleted file mode 100644 index 99c2be08..00000000 --- a/pdp9.c +++ /dev/null @@ -1 +0,0 @@ -#define PDP9 0 diff --git a/scp.c b/scp.c index 9607d097..d2ea2a69 100644 --- a/scp.c +++ b/scp.c @@ -1,6 +1,6 @@ /* scp.c: simulator control program - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,13 @@ 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-May-01 RMS Fixed missing return in disable_cmd + 25-Mar-01 RMS Added ENABLE/DISABLE + 14-Mar-01 RMS Revised LOAD/DUMP interface (again) + 05-Mar-01 RMS Added clock calibration support + 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0 + 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine + 21-Jan-01 RMS Added relative time 22-Dec-00 RMS Fixed find_device for devices ending in numbers 08-Dec-00 RMS V2.5a changes 30-Oct-00 RMS Added output file option to examine @@ -64,13 +71,14 @@ #define SCH_GE 6 #define SCH_LE 7 #define SWHIDE (1u << 26) /* enable hiding */ -#define SRBUFSIZ 1024 /* save/restore buffer */ +#define SRBSIZ 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); \ + sim_rtime = sim_rtime + ((uint32) (x - sim_interval)); \ x = sim_interval extern char sim_name[]; @@ -78,7 +86,7 @@ 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 t_stat sim_load (FILE *ptr, char *cptr, char *fnam, int flag); extern int32 sim_emax; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); @@ -88,10 +96,13 @@ extern t_stat ttinit (void); extern t_stat ttrunstate (void); extern t_stat ttcmdstate (void); extern t_stat ttclose (void); +extern uint32 sim_os_msec (void); UNIT *sim_clock_queue = NULL; int32 sim_interval = 0; int32 sim_switches = 0; +int32 sim_is_running = 0; static double sim_time; +static uint32 sim_rtime; static int32 noqueue_time; volatile int32 stop_cpu = 0; t_value *sim_eval = NULL; @@ -124,7 +135,8 @@ unsigned char sim_flip[FLIP_SIZE]; else *(((uint32 *) mb) + j) = v; #endif #define GET_SWITCHES(cp,gb) \ - for (sim_switches = 0; *cp == '-'; ) { \ + sim_switches = 0; \ + while (*cp == '-') { \ int32 lsw; \ cp = get_glyph (cp, gb, 0); \ if ((lsw = get_switches (gb)) <= 0) return SCPE_ARG; \ @@ -137,26 +149,31 @@ unsigned char sim_flip[FLIP_SIZE]; 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); +void put_rval (REG *rptr, int idx, t_value val); 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 (char *ptr); +DEVICE *find_unit (char *ptr, int32 *iptr); DEVICE *find_dev_from_unit (UNIT *uptr); REG *find_reg (char *ptr, char **optr, DEVICE *dptr); +t_bool qdisable (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_reg (FILE *ofile, t_value val, int flag, REG *rptr, t_addr idx); +t_stat dep_reg (int flag, char *cptr, REG *rptr, t_addr idx); 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); +char *get_range (char *cptr, t_addr *lo, t_addr *hi, int rdx, + t_addr max, char term); 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 save_vercur[] = "V2.6"; +const char save_ver25[] = "V2.5"; const char *scp_error_messages[] = { "Address space exceeded", "Unit not attached", @@ -178,7 +195,14 @@ const char *scp_error_messages[] = { "End of file", "Relocation error", "No settable parameters", - "Unit already attached" }; + "Unit already attached", + "Hardware timer error", + "SIGINT handler setup error", + "Console terminal setup error", + "Subscript out of range", + "Command not allowed", + "Unit disabled" +}; const size_t size_map[] = { sizeof (int8), sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32) @@ -229,6 +253,8 @@ 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 enable_cmd (int flag, char *ptr); +t_stat disable_cmd (int flag, char *ptr); t_stat help_cmd (int flag, char *ptr); static CTAB cmd_table[] = { @@ -254,6 +280,8 @@ static CTAB cmd_table[] = { { "BYE", &exit_cmd, 0 }, { "SET", &set_cmd, 0 }, { "SHOW", &show_cmd, 0 }, + { "ENABLE", &enable_cmd, 0 }, + { "DISABLE", &disable_cmd, 0 }, { "ADD", &add_cmd, 0 }, { "REMOVE", &remove_cmd, 0 }, { "HELP", &help_cmd, 0 }, @@ -261,8 +289,8 @@ static CTAB cmd_table[] = { /* Main command loop */ -printf ("\n%s simulator V2.5a\n", sim_name); -end_test.i = 1; /* test endian-ness */ +printf ("\n%s simulator V2.6\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) { @@ -274,9 +302,10 @@ if ((stat = ttinit ()) != SCPE_OK) { return 0; } stop_cpu = 0; sim_interval = 0; -sim_time = 0; +sim_time = sim_rtime = 0; noqueue_time = 0; sim_clock_queue = NULL; +sim_is_running = 0; if ((stat = reset_all (0)) != SCPE_OK) { printf ("Fatal simulator initialization error\n%s\n", scp_error_messages[stat - SCPE_BASE]); @@ -349,6 +378,8 @@ 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 ("en{able} enable device\n"); +printf ("di{sable} disable device\n"); printf ("ad{d} add unit to configuration\n"); printf ("rem{ove} remove unit from configuration\n"); printf ("h{elp} type this message\n"); @@ -374,13 +405,13 @@ static CTAB set_table[] = { GET_SWITCHES (cptr, gbuf); /* test for switches */ cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ -dptr = find_device (gbuf, &unitno); /* find device */ +dptr = find_unit (gbuf, &unitno); /* find dev+unit */ if ((dptr == NULL) || (dptr -> units == NULL) || - (*cptr == 0)) return SCPE_ARG; /* argument? */ + (*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? */ +if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* 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); } @@ -416,9 +447,10 @@ 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); +t_stat show_device (DEVICE *dptr, int flag); static CTAB show_table[] = { { "CONFIGURATION", &show_config, 0 }, + { "DEVICES", &show_config, 1 }, { "QUEUE", &show_queue, 0 }, { "TIME", &show_time, 0 }, { "MODIFIERS", &show_modifiers, 0 }, @@ -430,14 +462,14 @@ 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); +dptr = find_dev (gbuf); /* locate device */ +if (dptr == NULL) return SCPE_ARG; /* not found? */ +return show_device (dptr, 0); } /* Show processors */ -t_stat show_device (DEVICE *dptr) +t_stat show_device (DEVICE *dptr, int flag) { int32 j, ucnt; t_addr kval; @@ -445,12 +477,17 @@ UNIT *uptr; MTAB *mptr; printf ("%s", dptr -> name); /* print dev name */ -for (j = ucnt = 0; j < dptr -> numunits; j++) { +if (qdisable (dptr)) { /* disabled? */ + printf (", disabled\n"); + return SCPE_OK; } +for (j = ucnt = 0; j < dptr -> numunits; j++) { /* count units */ 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); } + else if (ucnt > 1) printf (", %d units\n", ucnt); + else if (flag) printf ("\n"); } +if (flag) return SCPE_OK; /* dev only? */ for (j = 0; j < dptr -> numunits; j++) { uptr = (dptr -> units) + j; kval = (uptr -> flags & UNIT_BINK)? 1024: 1000; @@ -480,7 +517,8 @@ int32 i; DEVICE *dptr; printf ("%s simulator configuration\n\n", sim_name); -for (i = 0; (dptr = sim_devices[i]) != NULL; i++) show_device (dptr); +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_device (dptr, flag); return SCPE_OK; } @@ -529,6 +567,63 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { return SCPE_OK; } +/* Enable and disable commands and routines + + en[able] enable device + di[sable] disable device +*/ + +t_stat enable_cmd (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; +REG *rptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +dptr = find_dev (gbuf); /* locate device */ +if ((dptr == NULL) || (*cptr != 0)) return SCPE_ARG; /* found it? */ +rptr = find_reg ("*DEVENB", NULL, dptr); /* locate enable */ +if (rptr == NULL) return SCPE_NOFNC; /* found it? */ +put_rval (rptr, 0, 1); /* enable */ +if (dptr -> reset) dptr -> reset (dptr); /* reset */ +return SCPE_OK; +} + +t_stat disable_cmd (int flag, char *cptr) +{ +int32 i; +char gbuf[CBUFSIZE]; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +GET_SWITCHES (cptr, gbuf); /* test for switches */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +dptr = find_dev (gbuf); /* locate device */ +if ((dptr == NULL) || (*cptr != 0)) return SCPE_ARG; /* found it? */ +rptr = find_reg ("*DEVENB", NULL, dptr); /* locate enable */ +if (rptr == NULL) return SCPE_NOFNC; /* found it? */ +for (i = 0; i < dptr -> numunits; i++) { /* check units */ + uptr = (dptr -> units) + i; + if (uptr -> flags & UNIT_ATT || sim_is_active (uptr)) + return SCPE_NOFNC; } +put_rval (rptr, 0, 0); /* disable */ +if (dptr -> reset) dptr -> reset (dptr); /* reset */ +return SCPE_OK; +} + +/* Test for disabled device */ + +t_bool qdisable (DEVICE *dptr) +{ +REG *rptr; + +rptr = find_reg ("*DEVENB", NULL, dptr); /* locate enable */ +if (rptr == NULL) return FALSE; /* found it? */ +return (get_rval (rptr, 0)? FALSE: TRUE); /* return flag */ +} + /* Add and remove commands and routines ad[d] add unit to configuration @@ -544,14 +639,13 @@ 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 */ +dptr = find_unit (gbuf, &unitno); /* locate dev+unit# */ if ((dptr == NULL) || (dptr -> units == NULL) || - (*cptr != 0)) return SCPE_ARG; /* found it? */ + (*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 */ +if (!(uptr -> flags & UNIT_DISABLE)) return SCPE_NOFNC; /* allowed? */ +uptr -> flags = uptr -> flags & ~UNIT_DIS; /* enable it */ +return SCPE_OK; } t_stat remove_cmd (int flag, char *cptr) @@ -563,15 +657,15 @@ 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 */ +dptr = find_unit (gbuf, &unitno); /* locate dev+unit# */ if ((dptr == NULL) || (dptr -> units == NULL) || - (*cptr != 0)) return SCPE_ARG; /* found it? */ + (*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 */ +if (!(uptr -> flags & UNIT_DISABLE) || /* allowed? */ + (uptr -> flags & UNIT_ATT) || sim_is_active (uptr)) + return SCPE_NOFNC; +uptr -> flags = uptr -> flags | UNIT_DIS; /* disable it */ +return SCPE_OK; } /* Reset command and routines @@ -591,7 +685,8 @@ 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; +dptr = find_dev (gbuf); /* locate device */ +if (dptr == NULL) return SCPE_ARG; /* found it? */ if (dptr -> reset != NULL) return dptr -> reset (dptr); else return SCPE_OK; } @@ -634,10 +729,10 @@ 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 */ +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 */ +reason = sim_load (loadfile, cptr, gbuf, flag); /* load or dump */ fclose (loadfile); return reason; } @@ -658,7 +753,7 @@ 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); +dptr = find_unit (gbuf, &unitno); /* locate dev+unit# */ if ((dptr == NULL) || (dptr -> units == NULL)) return SCPE_ARG; uptr = (dptr -> units) + unitno; if (dptr -> attach != NULL) return dptr -> attach (uptr, cptr); @@ -714,7 +809,7 @@ 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); +dptr = find_unit (gbuf, &unitno); /* locate dev+unit# */ if ((dptr == NULL) || (dptr -> units == NULL)) return SCPE_ARG; uptr = (dptr -> units) + unitno; if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT; @@ -753,15 +848,16 @@ 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 ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK; 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"); + if (uptr -> hwmark) { + printf ("%s: writing buffer to file\n", dptr -> name); + 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 -> flags = uptr -> flags & ~UNIT_BUF; uptr -> filebuf = NULL; } free (uptr -> filename); uptr -> filename = NULL; @@ -793,11 +889,12 @@ REG *rptr; 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 */ +fputs (save_vercur, sfile); /* save format version */ fputc ('\n', sfile); fputs (sim_name, sfile); /* sim name */ fputc ('\n', sfile); WRITE_I (sim_time); /* sim time */ +WRITE_I (sim_rtime); /* sim relative time */ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ fputs (dptr -> name, sfile); /* device name */ @@ -816,12 +913,12 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ ((high = uptr -> capac) != 0)) { /* memory-like unit? */ WRITE_I (high); /* write size */ sz = SZ_D (dptr); - if ((mbuf = calloc (SRBUFSIZ, sz)) == NULL) { + if ((mbuf = calloc (SRBSIZ, sz)) == NULL) { fclose (sfile); return SCPE_MEM; } for (k = 0; k < high; ) { zeroflg = TRUE; - for (l = 0; (l < SRBUFSIZ) && (k < high); + for (l = 0; (l < SRBSIZ) && (k < high); l++, k = k + (dptr -> aincr)) { r = dptr -> examine (&val, k, uptr, 0); if (r != SCPE_OK) return r; @@ -871,7 +968,7 @@ t_addr k, high; t_value val, mask, vzro = 0; t_stat r; size_t sz; -t_bool v25 = FALSE; +t_bool v26 = FALSE, v25 = FALSE; DEVICE *dptr; UNIT *uptr; REG *rptr; @@ -885,7 +982,10 @@ 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? */ +if (strcmp (buf, save_vercur) == 0) { /* version 2.6? */ + v26 = v25 = TRUE; /* set flag */ + READ_S (buf); } /* read name */ +else if (strcmp (buf, save_ver25) == 0) { /* version 2.5? */ v25 = TRUE; /* set flag */ READ_S (buf); } /* read name */ if (strcmp (buf, sim_name)) { /* name match? */ @@ -893,11 +993,12 @@ if (strcmp (buf, sim_name)) { /* name match? */ fclose (rfile); return SCPE_OK; } READ_I (sim_time); /* sim time */ +if (v26) { READ_I (sim_rtime); } /* sim relative time */ for ( ;; ) { /* device loop */ READ_S (buf); /* read device name */ if (buf[0] == 0) break; /* last? */ - if ((dptr = find_device (buf, NULL)) == NULL) { + if ((dptr = find_dev (buf)) == NULL) { /* locate device */ printf ("Invalid device name: %s\n", buf); fclose (rfile); return SCPE_INCOMP; } @@ -918,7 +1019,8 @@ for ( ;; ) { /* device loop */ READ_S (buf); /* attached file */ if (buf[0] != 0) { uptr -> flags = uptr -> flags & ~UNIT_DIS; - r = attach_unit (uptr, buf); + if (dptr -> attach != NULL) r = dptr -> attach (uptr, buf); + else r = attach_unit (uptr, buf); if (r != SCPE_OK) return r; } READ_I (high); /* memory capacity */ if (high > 0) { /* any memory? */ @@ -929,7 +1031,7 @@ for ( ;; ) { /* device loop */ return SCPE_INCOMP; } if (v25) { sz = SZ_D (dptr); - if ((mbuf = calloc (SRBUFSIZ, sz)) == NULL) { + if ((mbuf = calloc (SRBSIZ, sz)) == NULL) { fclose (rfile); return SCPE_MEM; } for (k = 0; k < high; ) { @@ -973,7 +1075,7 @@ for ( ;; ) { /* device loop */ READ_I (val); /* read value */ if (val > mask) printf ("Invalid register value: %s\n", buf); - else put_rval (rptr, i, val, mask); } } + else put_rval (rptr, i, val); } } } /* end device loop */ fclose (rfile); return SCPE_OK; @@ -1003,7 +1105,7 @@ 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 ((r = dep_reg (0, gbuf, sim_PC, 0)) != SCPE_OK) return r; } if (flag == RU_STEP) { /* step */ if (*cptr == 0) step = 1; @@ -1014,7 +1116,7 @@ if (flag == RU_STEP) { /* step */ 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); + dptr = find_unit (gbuf, &unitno); /* locate dev+unit# */ if ((dptr == NULL) || (dptr -> units == NULL) || (dptr -> boot == NULL)) return SCPE_ARG; uptr = dptr -> units + unitno; @@ -1027,7 +1129,7 @@ if (*cptr != 0) return SCPE_ARG; if ((flag == RU_RUN) || (flag == RU_BOOT)) { /* run or boot */ sim_interval = 0; /* reset queue */ - sim_time = 0; + sim_time = sim_rtime = 0; noqueue_time = 0; sim_clock_queue = NULL; if ((r = reset_all (0)) != SCPE_OK) return r; } @@ -1039,22 +1141,22 @@ for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { 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; } + return SCPE_SIGERR; } if (ttrunstate () != SCPE_OK) { /* set console */ ttcmdstate (); - printf ("Simulator terminal setup failed\n"); - return SCPE_OK; } + return SCPE_TTYERR; } if (step) sim_activate (&step_unit, step); /* set step timer */ +sim_is_running = 1; /* flag running */ r = sim_instr(); +sim_is_running = 0; /* flag idle */ 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 +#if defined (VMS) printf ("\n"); #endif if (r >= SCPE_BASE) printf ("\n%s, %s: ", scp_error_messages[r - SCPE_BASE], @@ -1136,7 +1238,7 @@ 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); + REG *lowr, REG *highr, t_addr lows, t_addr highs); if (*cptr == 0) return SCPE_ARG; /* err if no args */ ofile = NULL; /* no output file */ @@ -1166,61 +1268,50 @@ for (;;) { /* loop through modifiers */ 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 */ + else if ((tdptr = find_unit (gbuf, &t)) != NULL) { /* try for unit */ dptr = tdptr; /* set as default */ unitno = t; } - else break; } /* not recognized, break out */ + else break; } /* not rec, break out */ if ((*cptr == 0) == (flag == 0)) return SCPE_ARG; /* eol if needed? */ -if (dptr -> units == NULL) return SCPE_ARG; /* got a unit? */ - +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 (*tptr && (*tptr++ != ',')) return SCPE_ARG; + 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, 0, 0); + continue; } if (lowr = find_reg (gptr, &tptr, dptr)) { - highr = lowr; + low = high = 0; 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; } } + else { highr = lowr; + if (*tptr == '[') { + if (lowr -> depth <= 1) return SCPE_ARG; + tptr = get_range (tptr + 1, &low, &high, + 10, lowr -> depth - 1, ']'); + if (tptr == NULL) return SCPE_ARG; } } + if (*tptr && (*tptr++ != ',')) return SCPE_ARG; + reason = exdep_reg_loop (ofile, schptr, flag, cptr, + lowr, highr, low, high); + 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 */ + tptr = get_range (gptr, &low, &high, dptr -> aradix, + (((uptr -> capac == 0) | (flag == EX_E))? 0: + uptr -> capac - dptr -> aincr), 0); + if (tptr == NULL) return SCPE_ARG; + if (*tptr && (*tptr++ != ',')) return SCPE_ARG; + reason = exdep_addr_loop (ofile, schptr, flag, cptr, low, high, + dptr, uptr); } /* end for */ if (log) fclose (ofile); /* close output file */ return reason; @@ -1233,24 +1324,28 @@ return reason; */ t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int flag, char *cptr, - REG *lowr, REG *highr) + REG *lowr, REG *highr, t_addr lows, t_addr highs) { t_stat reason; +t_addr idx; 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; } } + if ((sim_switches & SWHIDE) && + ((rptr -> flags & REG_HIDDEN) || (rptr -> depth > 1))) continue; + for (idx = lows; idx <= highs; idx++) { + if (idx >= (t_addr) rptr -> depth) return SCPE_SUB; + val = get_rval (rptr, idx); + if (schptr && !test_search (val, schptr)) continue; + if (flag != EX_D) { + reason = ex_reg (ofile, val, flag, rptr, idx); + if (reason != SCPE_OK) return reason; } + if (flag != EX_E) { + reason = dep_reg (flag, cptr, rptr, idx); + if (reason != SCPE_OK) return reason; } } } return SCPE_OK; } @@ -1285,16 +1380,18 @@ return SCPE_OK; val = current register value flag = type of ex/mod command (ex, iex, idep) rptr = pointer to register descriptor + idx = index Outputs: return = error status */ -t_stat ex_reg (FILE *ofile, t_value val, int flag, REG *rptr) +t_stat ex_reg (FILE *ofile, t_value val, int flag, REG *rptr, t_addr idx) { int32 rdx; if (rptr == NULL) return SCPE_ARG; -fprintf (ofile, "%s: ", rptr -> name); +if (rptr -> depth > 1) fprintf (ofile, "%s[%d]: ", rptr -> name, idx); +else 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); @@ -1307,7 +1404,7 @@ return SCPE_OK; Inputs: rptr = pointer to register descriptor - idx = index (SAVE register buffers only) + idx = index Outputs: return = register value */ @@ -1339,11 +1436,12 @@ return val; flag = type of deposit (normal/interactive) cptr = pointer to input string rptr = pointer to register descriptor + idx = index Outputs: return = error status */ -t_stat dep_reg (int flag, char *cptr, REG *rptr) +t_stat dep_reg (int flag, char *cptr, REG *rptr, t_addr idx) { t_stat r; t_value val, mask; @@ -1356,13 +1454,12 @@ 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); +put_rval (rptr, idx, val); return SCPE_OK; } @@ -1377,9 +1474,10 @@ return SCPE_OK; none */ -void put_rval (REG *rptr, int idx, t_value val, t_value mask) +void put_rval (REG *rptr, int idx, t_value val) { size_t sz; +t_value mask; #define PUT_RVAL(sz,rp,id,val,msk) \ *(((sz *) rp -> loc) + id) = \ @@ -1387,6 +1485,7 @@ size_t sz; ~((msk) << (rp) -> offset)) | ((val) << (rp) -> offset) sz = SZ_R (rptr); +mask = width_mask[rptr -> width]; if ((rptr -> depth > 1) && (sz == sizeof (uint8))) PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask); else if ((rptr -> depth > 1) && (sz == sizeof (uint16))) @@ -1641,37 +1740,95 @@ if ((cptr == tptr) || (val > max) || (*tptr != 0)) *status = SCPE_ARG; else *status = SCPE_OK; return val; } + +/* get_range range specification + + Inputs: + cptr = pointer to input string + *lo = pointer to low result + *hi = pointer to high result + aradix = address radix + max = default high value + term = terminating character, 0 if none + Outputs: + tptr = input pointer after processing + NULL if error +*/ + +char *get_range (char *cptr, t_addr *lo, t_addr *hi, int rdx, + t_addr max, char term) +{ +char *tptr; + +*lo = *hi = 0; +if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */ + tptr = cptr + strlen ("ALL"); + *hi = max; } +else { errno = 0; + *lo = strtoul (cptr, &tptr, rdx); /* get low */ + if (errno || (cptr == tptr)) return NULL; /* error? */ + if ((*tptr == '-') || (*tptr == ':')) { /* more to do? */ + cptr = tptr + 1; + errno = 0; + *hi = strtoul (cptr, &tptr, rdx); /* get high */ + if (errno || (cptr == tptr)) return NULL; + if (*lo > *hi) return NULL; } + else *hi = *lo; } +if (term && (*tptr++ != term)) return NULL; +return tptr; +} /* 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) +DEVICE *find_dev (char *cptr) { -int32 i, lenn, unitno; +int32 i; +DEVICE *dptr; + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + if (strcmp (cptr, dptr -> name) == 0) return dptr; } +return NULL; +} + +/* Find_unit find unit matching input string + + Inputs: + cptr = pointer to input string + iptr = pointer to unit number + Outputs: + result = pointer to device + *iptr = unit number +*/ + +DEVICE *find_unit (char *cptr, int32 *iptr) +{ +int32 i, lenn; t_stat r; DEVICE *dptr; +if (iptr == NULL) return NULL; /* arg error? */ +*iptr = 0; /* assume no unit */ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* exact match? */ - if (strcmp (cptr, dptr -> name) != 0) continue; - if (iptr != NULL) *iptr = 0; - return sim_devices[i]; } + if (strcmp (cptr, dptr -> name) == 0) { + if (qdisable (dptr)) return NULL; /* disabled? */ + return dptr; } } for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */ 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 ((dptr -> numunits == 0) || /* no units? */ + (strncmp (cptr, dptr -> name, lenn) != 0)) continue; + cptr = cptr + lenn; /* skip devname */ + if (*cptr) { /* unit #? */ + *iptr = (int32) get_uint (cptr, 10, dptr -> numunits - 1, &r); if (r != SCPE_OK) return NULL; } - if (iptr != NULL) *iptr = unitno; - return sim_devices[i]; } + if (qdisable (dptr)) return NULL; /* disabled? */ + return dptr; } return NULL; } @@ -1743,6 +1900,19 @@ for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) { sw = sw | SWMASK (*cptr); } return sw; } + +t_bool match_ext (char *fnam, char *ext) +{ +char *fptr, *eptr; + +if ((fnam == NULL) || (ext == NULL)) return FALSE; +fptr = strrchr (fnam, '.'); +if (fptr == NULL) return FALSE; +for (fptr++, eptr = ext; *fptr; fptr++, eptr++) { + if (toupper (*fptr) != toupper (*eptr)) return FALSE; } +if (*eptr) return FALSE; +return TRUE; +} /* Get search specification @@ -1825,7 +1995,9 @@ case SCH_LE: return 0; } -/* General radix input routine +/* Radix independent input/output package + + strtotv - general radix input routine Inputs: inptr = string to convert @@ -1860,7 +2032,7 @@ if (nodigit) return 0; /* no digits? */ return val; } -/* General radix printing routine +/* fprint_val - general radix printing routine Inputs: stream = stream designator @@ -1901,7 +2073,7 @@ if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR; return SCPE_OK; } -/* Event queue routines +/* Event queue package sim_activate add entry to event queue sim_cancel remove entry from event queue @@ -1921,7 +2093,7 @@ return SCPE_OK; The event queue is maintained in clock order; entry timeouts are RELATIVE to the time in the previous entry. - Sim_process_event - process event + sim_process_event - process event Inputs: none @@ -1956,7 +2128,7 @@ do { uptr = sim_clock_queue; /* get first */ return reason; } -/* Activate (queue) event +/* sim_activate - activate (queue) event Inputs: uptr = pointer to unit @@ -1992,7 +2164,7 @@ sim_interval = sim_clock_queue -> time; return SCPE_OK; } -/* Cancel (dequeue) event +/* sim_cancel - cancel (dequeue) event Inputs: uptr = pointer to unit @@ -2021,7 +2193,7 @@ else sim_interval = noqueue_time = NOQUEUE_WAIT; return SCPE_OK; } -/* Test for entry in queue, return activation time +/* sim_is_active - test for entry in queue, return activation time Inputs: uptr = pointer to unit @@ -2041,7 +2213,8 @@ for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) { return 0; } -/* Return global time +/* sim_gtime - return global time + sim_grtime - return global time with rollover Inputs: none Outputs: @@ -2054,6 +2227,13 @@ if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } else { UPDATE_SIM_TIME (sim_clock_queue -> time); } return sim_time; } + +uint32 sim_grtime (void) +{ +if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } +else { UPDATE_SIM_TIME (sim_clock_queue -> time); } +return sim_rtime; +} /* Endian independent binary I/O package @@ -2123,4 +2303,55 @@ for (i = nbuf; i > 0; i--) { total = total + c; } return total; } + +/* OS independent clock calibration package + + sim_rtc_init initialize calibration + sim_rtc_calb calibrate clock +*/ + +static int32 rtc_ticks = 0; /* ticks */ +static uint32 rtc_realtime = 0; /* real time */ +static uint32 rtc_virttime = 0; /* virtual time */ +static uint32 rtc_nextintv = 0; /* next interval */ +static int32 rtc_basedelay = 0; /* base delay */ +static int32 rtc_currdelay = 0; /* current delay */ +extern t_bool rtc_avail; +#define TMAX 500 /* max makeup per sec */ + +int32 sim_rtc_init (int32 time) +{ +rtc_realtime = sim_os_msec (); +rtc_virttime = rtc_realtime; +rtc_nextintv = 1000; +rtc_ticks = 0; +rtc_basedelay = time; +rtc_currdelay = time; +return rtc_currdelay; +} + +int32 sim_rtc_calb (int32 ticksper) +{ +uint32 new_realtime, delta_realtime; +int32 delta_virttime; + +rtc_ticks = rtc_ticks + 1; /* count ticks */ +if (rtc_ticks < ticksper) return rtc_currdelay; /* 1 sec yet? */ +rtc_ticks = 0; /* reset ticks */ +if (!rtc_avail) return rtc_currdelay; /* no timer? */ +new_realtime = sim_os_msec (); /* wall time */ +delta_realtime = new_realtime - rtc_realtime; /* elapsed wtime */ +if (delta_realtime == 0) return rtc_currdelay; /* can't calibr */ +rtc_basedelay = (int32) (((double) rtc_basedelay * (double) rtc_nextintv) / + ((double) delta_realtime)); /* new base rate */ +rtc_realtime = new_realtime; /* adv wall time */ +rtc_virttime = rtc_virttime + 1000; /* adv sim time */ +delta_virttime = rtc_virttime - rtc_realtime; /* gap */ +if (delta_virttime > TMAX) delta_virttime = TMAX; /* limit gap */ +else if (delta_virttime < -TMAX) delta_virttime = -TMAX; +rtc_nextintv = 1000 + delta_virttime; /* next wtime */ +rtc_currdelay = (int32) (((double) rtc_basedelay * (double) rtc_nextintv) / + 1000.0); /* next delay */ +return rtc_currdelay; +} diff --git a/scp_tty.c b/scp_tty.c index 0a4a84f3..ba528189 100644 --- a/scp_tty.c +++ b/scp_tty.c @@ -1,6 +1,6 @@ -/* scp_tty.c: Operating system-dependent terminal I/O routines. +/* scp_tty.c: operating system-dependent I/O routines - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,30 +23,39 @@ 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-Mar-01 RMS Added clock calibration support 08-Dec-00 BKR Added OS/2 support 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 + This module implements the following routines to support terminal I/O: + 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 + sim_putchar - output character to terminal - Versions are included for generic (BSD) UNIX and for VMS. - The generic UNIX version works with LINUX. + This module implements the following routines to support clock calibration: + + sim_os_msec - return elapsed time in msec + + Versions are included for VMS, Windows, OS/2, BSD UNIX, and POSIX UNIX. + The POSIX UNIX version works with LINUX. */ +#undef USE_INT64 /* hack for Windows */ #include "sim_defs.h" int32 sim_int_char = 005; /* interrupt character */ /* VMS routines */ -#ifdef VMS +#if defined (VMS) #define __TTYROUTINES 0 + #include #include #include @@ -145,13 +154,49 @@ status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT, if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTOERR; return SCPE_OK; } + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +uint32 quo, htod, tod[2]; +int32 i; + +sys$gettim (tod); /* time 0.1usec */ + +/* To convert to msec, must divide 64b quantity by 10000. This is actually done + by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the + high 32b of which are discared. This can probably be done by a clever multiply... +*/ + +quo = htod = 0; +for (i = 0; i < 64; i++) { /* 64b quo */ + htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */ + tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1); + tod[0] = tod[0] << 1; + quo = quo << 1; /* shift quo */ + if (htod >= 10000) { /* divd work? */ + htod = htod - 10000; /* subtract */ + quo = quo | 1; } } /* set quo bit */ +return quo; +} + #endif /* Win32 routines */ -#ifdef _WIN32 +#if defined (WIN32) #define __TTYROUTINES 0 #include +#include +#include +static volatile int sim_win_ctlc = 0; + +void win_handler (int sig) +{ +sim_win_ctlc = 1; +return; +} t_stat ttinit (void) { @@ -160,6 +205,8 @@ return SCPE_OK; t_stat ttrunstate (void) { +sim_win_ctlc = 0; +if ((int) signal (SIGINT, win_handler) == -1) return SCPE_SIGERR; return SCPE_OK; } @@ -177,6 +224,10 @@ t_stat sim_poll_kbd (void) { int c; +if (sim_win_ctlc) { + sim_win_ctlc = 0; + signal (SIGINT, win_handler); + return 003 | SCPE_KFLAG; } if (!kbhit ()) return SCPE_OK; c = _getch (); if ((c & 0177) == '\b') c = 0177; @@ -186,10 +237,17 @@ return c | SCPE_KFLAG; t_stat sim_putchar (int32 c) { -_putch (c); +if (c != 0177) _putch (c); return SCPE_OK; } +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +return GetTickCount (); +} + #endif /* OS/2 routines */ @@ -231,19 +289,28 @@ return c | SCPE_KFLAG; t_stat sim_putchar (int32 c) { -putch (c); -fflush (stdout) ; +if (c != 0177) { + putch (c); + fflush (stdout) ; } return SCPE_OK; } +const t_bool rtc_avail = FALSE; + +uint32 sim_os_msec () +{ +return 0; +} + #endif /* BSD UNIX routines */ -#ifdef BSDTTY +#if defined (BSDTTY) #define __TTYROUTINES 0 #include #include +#include struct sgttyb cmdtty,runtty; /* V6/V7 stty data */ struct tchars cmdtchars,runtchars; /* V7 editing */ @@ -316,12 +383,27 @@ c = out; write (1, &c, 1); return SCPE_OK; } + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +struct timeval cur; +struct timezone foo; +uint32 msec; + +gettimeofday (&cur, &foo); +msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000); +return msec; +} + #endif /* POSIX UNIX routines */ -#ifndef __TTYROUTINES +#if !defined (__TTYROUTINES) #include +#include struct termios cmdtty, runtty; @@ -400,5 +482,17 @@ c = out; write (1, &c, 1); return SCPE_OK; } -#endif +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +struct timeval cur; +uint32 msec; + +gettimeofday (&cur, NULL); +msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000); +return msec; +} + +#endif diff --git a/sim_defs.h b/sim_defs.h index 1c66e9f0..9f6d0317 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -1,6 +1,6 @@ /* sim_defs.h: simulator definitions - Copyright (c) 1993-2000, Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -23,6 +23,7 @@ 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. + 25-Feb-01 RMS Revisions for V2.6 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 @@ -68,10 +69,12 @@ 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) +#if defined (USE_INT64) && defined (WIN32) #define int64 __int64 /* for Windows */ -#elif defined (_INT64) -#define int64 long +#elif defined (USE_INT64) && defined (__digital__) && defined (__unix__) +#define int64 long /* for DUNIX */ +#elif defined (USE_INT64) +#define int64 long long /* for GCC */ #endif #if defined (int64) typedef unsigned int64 uint64, t_value; /* value */ @@ -119,6 +122,12 @@ typedef int32 t_mtrlnt; /* magtape rec lnt */ #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_TIMER (SCPE_BASE + 21) /* hwre timer err */ +#define SCPE_SIGERR (SCPE_BASE + 22) /* signal err */ +#define SCPE_TTYERR (SCPE_BASE + 23) /* tty setup err */ +#define SCPE_SUB (SCPE_BASE + 24) /* subscript err */ +#define SCPE_NOFNC (SCPE_BASE + 25) /* func not imp */ +#define SCPE_UDIS (SCPE_BASE + 26) /* unit disabled */ #define SCPE_KFLAG 01000 /* tti data flag */ /* Print value format codes */ @@ -243,10 +252,10 @@ struct mtab { /* Search table */ struct schtab { - int logic; /* logical operator */ + int logic; /* logical operator */ int bool; /* boolean operator */ - t_value mask; /* mask for logical */ - t_value comp; /* comparison for boolean */ + t_value mask; /* mask for logical */ + t_value comp; /* comparison for boolean */ }; /* The following macros define structure contents */ diff --git a/simh.doc b/simh.doc index 9a262c0c..099a127b 100644 --- a/simh.doc +++ b/simh.doc @@ -15,13 +15,14 @@ \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 +\jclisttab\tx720 }{\listname ;}\listid717362522}{\list\listtemplateid67698689\listsimple{\listlevel\levelnfc23\leveljc0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li360\jclisttab\tx360 } +{\listname ;}\listid1064328194}{\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 @@ -40,13 +41,13 @@ \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\mo12\dy16\hr23\min48}{\version12}{\edmins414}{\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-Nov-00 for V2.5a +\listoverridecount0\ls11}{\listoverride\listid22442644\listoverridecount0\ls12}{\listoverride\listid1502426668\listoverridecount0\ls13}{\listoverride\listid1645162521\listoverridecount0\ls14}{\listoverride\listid1064328194\listoverridecount0\ls15}}{\info +{\title Writing a Simulator for the SIMH System}{\author Bob Supnik}{\operator Bob Supnik}{\creatim\yr1997\mo8\dy30\hr19}{\revtim\yr2001\mo4\dy14\hr22\min39}{\version16}{\edmins431}{\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-Apr-01 for V2.6 \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 @@ -67,8 +68,8 @@ How to Write a Virtual Machine for the MIMIC Simulation System\rdblquote , by Le \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 Data Types \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 SIMH is written in C. The host s +ystem 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 32-bit C \par @@ -102,9 +103,8 @@ in C. The host system must support (at least) 32-bit data types (64-bit data ty \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 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 h +ave 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: @@ -122,7 +122,7 @@ s command and control functions. The interfaces between a VM and SCP are relati \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): +s; pdp8 for the PDP-8; pdp11 for the PDP-11; nova for Nova; hp2100 for the HP 21XX; id4 for the Interdata 4; pdp10 for the PDP-10): \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 definitions for the particular simulator @@ -132,12 +132,12 @@ _sys.c contains all the SCP interfaces except the instruction simulator _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 +_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 }\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 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 @@ -155,34 +155,35 @@ _mt.c contains the mag tape controller and drives, etc. \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. T -his can be accurate (for example, nanoseconds of execution) 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 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 execution) or arbitrary (for example, number of instructions executed), but it must be consistent +ly 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 crit -erion 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, bu -t it is a 16-bit machine, and its memory is defined as uint16 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 underly -ing word size of the real machine, and byte manipulation should be done explicitly. Examples: +\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 crit +erion 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 uint16 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 unsigned int8 -\par \tab PDP-8\tab \tab \tab 12-bit\tab \tab \tab unsigned int16 -\par \tab PDP-11, Nova\tab \tab 16-bit\tab \tab \tab unsigned int16 -\par \tab PDP-10\tab \tab \tab 36-bit\tab \tab \tab unsigned int64 +\par \tab IBM 1401\tab \tab 6-bit\tab \tab \tab uint8 +\par \tab PDP-8\tab \tab \tab 12-bit\tab \tab \tab uint16 +\par \tab PDP-11, Nova\tab \tab 16-bit\tab \tab \tab uint16 +\par \tab PDP-1\tab \tab \tab 18-bit\tab \tab \tab uint32 +\par \tab PDP-10\tab \tab \tab 36-bit\tab \tab \tab uint64 \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 interrupts, even if this complicates the (much less frequent) evaluation of the i -nterrupt system following an I/O operation or asynchronous event. 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 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 inter +rupts, even if this complicates the (much less frequent) evaluation of the interrupt system following an I/O operation or asynchronous event. Another is not to over-generalize; even if the real hardware could support 64 or 256 interrupting devices, the s +imulators 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 one 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 In the simplest structure, interrupt requests correspond to device flags and are kept in an interrupt reques +t variable, with one 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 t +est is very fast: \par \par \tab if (int_enable && int_requests) \{ \'85process interrupt\'85 \} \par @@ -195,35 +196,35 @@ consists of two steps: are interrupts enabled, and is there an interrupt outstan \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 does not change -; however, the evaluation of whether an interrupt is pending now requires an extra step: +\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 does not cha +nge; 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 highest 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 If required for interrupt processing, the highest priori +ty 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 interrupt +ing 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 occur (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 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 occur (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 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 in -line code. +\par Analyzing an I/O command is usually easy. Most systems have one or more explicit I/O instructions contain +ing 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 representing non-existent devices. Before calling the device routine, the CPU usually breaks down the I/O command into standard fields. This simplifies writing the peripheral simulator. @@ -271,20 +272,21 @@ The VM should provide some debugging aids. The existing CPU\rquote s all provid \par }\pard \widctlpar\adjustright {\f1 \par The basic elements of a VM are devices, ea ch corresponding roughly to a real chunk of hardware. A device consists 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 r -epresenting 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 device, such as the console, may be broken up for convenience into separate in -put and output devices. +e +presenting 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 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 sources 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 simulat -ed as host-resident files. SCP allows the user to make an explicit association between a host-resident file and a simulated hardware entity. +\par In general, units correspond to individual sources 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 hos +t-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, a -nd the device performs similar operations on all of them, depending on which one has been selected by the program being simulated. +, 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 de +vice performs similar operations on all of them, depending on which one has been selected by the program being simulated. \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 4. +\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 st +ructures are described in detail in section 4. \par \par The primary functions of a peripheral are: \par @@ -293,9 +295,9 @@ nd the device performs similar operations on all of them, depending on which one \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 un -derstanding of device timing. +\par Command decoding is fairly obvious. At least one section of the peripheral code module wi +ll 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 understandi +ng 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 @@ -335,16 +337,41 @@ eriod. A waiting period of 0 is legal; negative waits cause an error. If the u \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 uint32 }{\b\f1 sim_grtime}{\f1 + (void). This routine returns the low-order 32b of 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 {\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 Clock Calibration \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 The timing mechanism described in the previous section is approximate. Devices, such as real-time clocks, which track wall time will be inaccurate. SCP provides routines to synchronize a simulated real-time clock to wall time. +\par +\par {\pntext\pard\plain\f3\fs20\cgrid \loch\af3\dbch\af0\hich\f3 \'b7\tab}}\pard \fi-360\li360\widctlpar\jclisttab\tx360{\*\pn \pnlvlblt\ilvl0\ls15\pnrnot0\pnf3\pnstart1\pnindent360\pnhang{\pntxtb \'b7}}\ls15\adjustright {\f1 int32 }{\b\f1 sim_rtc_init}{\f1 + (int32 clock_interval). This routine initializes the clock calibration mechanism. The argument is returned as the result. +\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\li360\widctlpar\jclisttab\tx360{\*\pn \pnlvlblt\ilvl0\ls15\pnrnot0\pnf3\pnstart1\pnindent360\pnhang{\pntxtb \'b7}}\ls15\adjustright {\f1 int32 }{\b\f1 sim_rtc_calb}{\f1 + (int32 tickspersecond). This routine calibrates the real-time clock. The argument is the number of clock ticks expected per second. +\par }\pard \widctlpar\adjustright {\f1 +\par The simulator calls }{\b\f1 sim_rtc_init}{\f1 in the prolog of }{\b\f1 sim_instr}{\f1 , before instruction execution starts, and whenever the real-time clock is started. The simulator calls }{\b\f1 sim_rtc_calb}{\f1 + to calculate the actual interval delay when the real-time clock is serviced: +\par +\par \tab /* clock start */ +\par +\par \tab if (!sim_is_active (&clk_unit)) sim_activate (&clk_unit, sim_rtc_init (clk_delay)); +\par \tab etc. +\par +\par \tab /* clock service */ +\par +\par \tab sim_activate (&clk_unit, sim_rtc_calb (clk_ticks_per_second); +\par +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.3\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: @@ -352,33 +379,35 @@ eriod. A waiting period of 0 is legal; negative waits cause an error. If the u \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 {\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 consi -sts 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. +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 data 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 differ -s 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 former buffers its data set in h -ost 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 Data I/O v +aries 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 l +a +tter may expand indefinitely. A buffered device differs from a non-buffered device in that the former 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 {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.2.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl3\adjustright {\f1 Reading and Writing Data +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.3.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl3\adjustright {\f1 Reading and Writing Data \par }\pard \widctlpar\adjustright {\f1 \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. + 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 {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.2.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl3\adjustright {\f1 Console I/O +\par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 3.2.3.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl3\adjustright {\f1 Console I/O \par }\pard \widctlpar\adjustright {\f1 \par SCP provides two routines for console I/O. \par @@ -437,8 +466,8 @@ ost memory, while the latter maintains it as a file. Most variable capacity dev \par }\pard \widctlpar\adjustright {\f1 \par {\listtext\pard\plain\f1\fs20\cgrid \hich\af1\dbch\af0\loch\f1 4.1.1\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\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 data sets as private state (typically just the CPU) must supply special examine and deposit routines. -The calling sequences are: +\par For devices which maintain their data sets as host files, SCP implements the examine and deposit data functions. However, devices which maintain their data sets as private state (typically just the CPU) must supply s +pecial 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. @@ -551,9 +580,8 @@ The calling sequences are: \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). Device-specific flags and UNIT_DIS are not automatically saved and restored; the device must supply a register cover -ing these bits. +\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). Devi +ce-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 4.2.2\tab}}\pard \fi-720\li720\widctlpar\jclisttab\tx720\ls1\ilvl2\adjustright {\f1 Service Routine \par }\pard \widctlpar\adjustright {\f1 @@ -587,8 +615,8 @@ ing these bits. \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 th -e values in the array will be saved and restored by SAVE and RESTORE. +\par The }{\b\f1 depth}{\f1 field is only used with special \ldblquote arrayed registers\rdblquote , like the data buffer in t +he 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 @@ -679,8 +707,8 @@ e values in the array will be saved and restored by SAVE and RESTORE. \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 \li720\widctlpar\adjustright {\f1 t_stat }{\b\f1 sim_load}{\f1 (FILE *fptr, char *buf, char *fnam, 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, and }{\i\f1 fnam}{\f1 contains the file name. \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 @@ -694,8 +722,8 @@ e values in the array will be saved and restored by SAVE and RESTORE. \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 consume -d more than a single word, then these routines should return extra number of words (not bytes) consumed as a }{\b\f1 negative}{\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 consumed 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 diff --git a/simh_doc.txt b/simh_doc.txt index a1c57ced..273fb9bb 100644 --- a/simh_doc.txt +++ b/simh_doc.txt @@ -1,14 +1,14 @@ To: Users From: Bob Supnik -Subj: Simulator Usage, V2.5a -Date: 1-Jan-01 +Subj: Simulator Usage, V2.6 +Date: 3-May-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 + Original code published in 1993-2001, written by Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -32,11 +32,11 @@ The following copyright notice applies to both the SIMH source and binary: 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. +IBM 1401, HP 2100, Interdata 4, and PDP-10 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 +bsupnik@us.inter.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 @@ -98,6 +98,17 @@ 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. +The PDP-10 simulator requires 64b support in the simulator and in the +simulator control package (SCP). To turn on 64b support, add the symbol +USE_INT64 to the command line: + + % cc -DUSE_INT64 pdp10_*.c,scp*.c -lm -o pdp10 + +Since 64b integer declarations vary, sim_defs.h has conditional +declarations for Windows (_int64) and Digital UNIX (long). The default +is GNU C (long long). If your compiler uses a different convention, +you will have to edit sim_defs.h and modify the conditionals. + 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, @@ -111,7 +122,8 @@ for example, disk sizes. % pdp{4,7,9,15} (cr) or % i1401 (cr) or % hp2100 (cr) or - % id4 (cr) + % id4 (cr) or + % pdp10 (cr) The simulator types out its name and version, executes the commands in the startup file, if any, and then prompts for input with @@ -267,6 +279,11 @@ The "state list" consists of one or more of the following, separated by commas: register the specified register + register[sub1-sub2] the specified register array locations, + start at location sub1 up to and including + location sub2 + register[ALL] all locations in the specified register + array register1-register2 all the registers starting at register1 up to and including register2 address the specified location @@ -294,8 +311,9 @@ Examples: 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> ex rx sbuf[3-6] -- examine SBUF[3] to SBUF[6] in RX sim> de all 0 -- set main memory to 0 - sim> de &77 >0 0 -- set all addresses whose low order + 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 @@ -373,7 +391,9 @@ All devices recognize the following parameters: 3.9 Displaying Parameters and Status The SHOW CONFIGURATION command shows the simulator configuration and the -status of all simulated devices. +status of all simulated devices and units. + +The SHOW DEVICES command shows the configuration of all simulated devices. The SHOW MODIFIERS command shows the modifiers available on all simulated devices. @@ -389,10 +409,14 @@ the last RUN command. 3.10 Altering the Simulated Configuration +For most mass storage, the DISABLE command removes the specified +device from the configuration. A DISABLEd device is invisible to running +programs. The device can still be RESET but it cannot be ATTAChed, DETACHed, +or BOOTed. ENABLE restores a disabled device to a 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 @@ -414,17 +438,27 @@ 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 +RF RF08/RS08 fixed head disk controller with four platters, or +DF DF32/DS32 fixed head disk controller with four platters RX RX8E/RX01 floppy disk controller with two drives +DT TC08/TU56 DECtape controller with eight drives MT TM8E/TU10 magnetic tape controller with eight drives +The RK, RF, DF, RX, DT, and MT devices can be DISABLEd. The PDP-8 can +support either a DF32 or an RF08, but not both, since they use the same +IOT's. The simulator defaults to the RF08. To change the fixed head disk, + + ENABLE DF32 enable DF32, disable RF08 + ENABLE RF08 enable RF08, disable DF32 + 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. +The PDP-8 loader supports both RIM format and BIN format tapes. If the file +extension is .RIM, or the -r switch is specified with LOAD, the file is +assumed to be RIM format; if the file extension is not .RIM, or if the -b +switch is specified, the file is assumed to be BIN format. 4.1 CPU @@ -609,6 +643,10 @@ The real-time clock (CLK) implements these registers: ENABLE 1 interrupt enable flag INT 1 interrupt pending flag TIME 24 clock interval + TPS 8 ticks per second (60 or 50) + +The real-time clock autocalibrates; the clock interval is adjusted up or +down so that the clock tracks actual elapsed time. 4.3 RK8E Cartridge Disk (RK) @@ -617,7 +655,8 @@ 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. +Units can also be REMOVEd or ADDed to the configuration. The RK8E supports +the BOOT command. The RK8E implements these registers: @@ -651,6 +690,8 @@ 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 supports the BOOT command. + The RX8E implements these registers: name size comments @@ -671,6 +712,7 @@ The RX8E implements these registers: STIME 24 seek time, per track XTIME 24 transfer ready delay STOP_IOE 1 stop on I/O error + SBUF[0:127] 8 sector buffer array Error handling is as follows: @@ -682,7 +724,12 @@ Error handling is as follows: 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) +4.5 Fixed Head Disks + +Either the RF08 or the DF32 can be present in a configuration, but +not both. + +4.5.1 RF08/RS08 Fixed Head Disk (RF) The RF08 implements these registers: @@ -699,6 +746,9 @@ The RF08 implements these registers: BURST 1 burst flag STOP_IOE 1 stop on I/O error +The RF08 supports the BOOT command. The default bootstrap is for OS/8. To +bootstrap the 4K Disk Monitor, use the BOOT -D command. + 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. @@ -713,7 +763,93 @@ Error handling is as follows: RF08 data files are buffered in memory; therefore, end of file and OS I/O errors cannot occur. -4.6 TM8E Magnetic Tape (MT) +4.5.2 DF32/DS32 Fixed Head Disk (RF) + +The DF32 implements these registers: + + name size comments + + STA 12 status, disk and memory address extension + DA 12 low order disk address + MA 12 memory address (in memory) + WC 12 word count (in memory) + WLK 16 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 DF32 supports the BOOT command. The default bootstrap is for OS/8. To +bootstrap the 4K Disk Monitor, use the BOOT -D command. + +The DF32 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 + +DF32 data files are buffered in memory; therefore, end of file and OS +I/O errors cannot occur. + +4.6 TC08/TU56 DECtape (DT) + +DECtapes drives are numbered 1-8; in the simulator, drive 8 is unit 0. +DECtape options include the ability to make units write enabled or write +locked. + + SET DTn LOCKED set unit n write locked + SET DTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. The TC08 supports +the BOOT command. + +The TC08 supports both PDP-8 format and PDP-9/11/15 format DECtape images. +ATTACH tries to determine the tape format from the DECtape image; the user +can force a particular format with switches: + + -f foreign (PDP-9/11/15) format + -n native (PDP-8) format + +The DECtape controller is a data-only simulator; the timing and mark +track, and block header and trailer, are not stored. Thus, the WRITE +TIMING AND MARK TRACK function is not supported; the READ ALL function +always returns the hardware standard block header and trailer; and the +WRITE ALL function dumps non-data words into the bit bucket. + +The DECtape controller implements these registers: + + name size comments + + DTSA 12 status register A + DTSB 12 status register B + INT 1 interrupt pending flag + ENB 1 interrupt enable flag + DTF 1 DECtape flag + ERF 1 error flag + CA 12 current address (memory location 7754) + WC 12 word count (memory location 7755) + LTIME 31 time between lines + ACTIME 31 time to accelerate to full speed + DCTIME 31 time to decelerate to a full stop + SUBSTATE 2 read/write command substate + POS0..7 31 position, in lines, units 0..7 + STATE0..7 31 unit state, units 0-7 + +It is critically important to maintain certain timing relationships +among the DECtape parameters, or the DECtape simulator will fail to +operate correctly. + + - LTIME must be at least 6 + - ACTIME must be less than DCTIME, and both need to be at + least 100 times LTIME + +4.7 TM8E Magnetic Tape (MT) Magnetic tape options include the ability to make units write enabled or or write locked. @@ -752,7 +888,7 @@ Error handling is as follows: OS I/O error report error and stop -4.7 Symbolic Display and Input +4.8 Symbolic Display and Input The PDP-8 simulator implements symbolic display and input. Display is controlled by command line switches: @@ -826,7 +962,16 @@ 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 +TC TC11/TU56 DECtape controller with eight drives TM TM11/TU10 magnetic tape controller with eight drives +TS TS11/TSV05 magnetic tape controller with one drive + +The RK, RL, RP, RX, TC, TM, and TS devices can be DISABLEd. The PDP-11 can +support either a TM11 or a TS11, but not both, since they use the same I/O +addresses. The simulator defaults to the TM11. To change the magtape, + + ENABLE TM11 enable TM11 and disable TS11 + ENABLE TS11 enable TS11 and disable TM11 The PDP-11 simulator implements several unique stop conditions: @@ -1061,6 +1206,10 @@ The clock (CLK) implements these registers: DONE 1 device done flag (CSR<7>) IE 1 interrupt enable flag (CSR<6>) TIME 24 clock frequency + TPS 8 ticks per second (60 or 50) + +The real-time clock autocalibrates; the clock interval is adjusted up or +down so that the clock tracks actual elapsed time. 5.3 RK11/RK05 Cartridge Disk (RK) @@ -1069,7 +1218,8 @@ 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. +Units can also be REMOVEd or ADDed to the configuration. The RK11 supports +the BOOT command. The RK11 implements these registers: @@ -1110,6 +1260,8 @@ 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 supports the BOOT command. + The RX11 implements these registers: name size comments @@ -1131,6 +1283,7 @@ The RX11 implements these registers: STIME 24 seek time, per track XTIME 24 transfer ready delay STOP_IOE 1 stop on I/O error + SBUF[0:127] 8 sector buffer array Error handling is as follows: @@ -1157,7 +1310,8 @@ to set the drive size to RL01, RL02, or autosize, and to write a DEC standard 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. +can also be REMOVEd or ADDed to the configuration. The RL11 supports the +BOOT command. The RL11 implements these registers: @@ -1212,7 +1366,8 @@ a DEC standard 044 compliant bad block table on the 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. +can also be REMOVEd or ADDed to the configuration. The RP controller supports +the BOOT command. The RP controller implements these registers: @@ -1252,22 +1407,75 @@ Error handling is as follows: OS I/O error x report error and stop -5.7 TM11 Magnetic Tape (TM) +5.7 TC11/TU56 DECtape (DT) -Magnetic tape options include the ability to make units write enabled or -or write locked. +DECtapes drives are numbered 1-8; in the simulator, drive 8 is unit 0. +DECtape options include the ability to make units write enabled or write +locked. + + SET DTn LOCKED set unit n write locked + SET DTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. The TC11 supports +the BOOT command. + +The TC11 supports both PDP-8 format and PDP-9/11/15 format DECtape images. +ATTACH tries to determine the tape format from the DECtape image; the user +can force a particular format with switches: + + -f foreign (PDP-8) format + -n native (PDP-9/11/15) format + +The DECtape controller is a data-only simulator; the timing and mark +track, and block header and trailer, are not stored. Thus, the WRITE +TIMING AND MARK TRACK function is not supported; the READ ALL function +always returns the hardware standard block header and trailer; and the +WRITE ALL function dumps non-data words into the bit bucket. + +The DECtape controller implements these registers: + + name size comments + + TCST 16 status register + TCCM 16 command register + TCWC 16 word count register + TCBA 16 bus address register + TCDT 16 data register + INT 1 interrupt pending flag + ERR 1 error flag + DONE 1 done flag + IE 1 interrupt enable flag + CTIME 31 time to complete transport stop + LTIME 31 time between lines + ACTIME 31 time to accelerate to full speed + DCTIME 31 time to decelerate to a full stop + SUBSTATE 2 read/write command substate + POS0..7 31 position, in lines, units 0..7 + STATE0..7 31 unit state, units 0-7 + +It is critically important to maintain certain timing relationships +among the DECtape parameters, or the DECtape simulator will fail to +operate correctly. + + - LTIME must be at least 6 + - ACTIME must be less than DCTIME, and both need to be at + least 100 times LTIME + +5.8 TM11 Magnetic Tape (TM) + +TM options include the ability to make units write enabled 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 TM11 supports the BOOT command. The 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. The magnetic tape controller implements these registers: @@ -1297,7 +1505,60 @@ Error handling is as follows: OS I/O error report error and stop -5.8 Symbolic Display and Input +5.8 TS11/TSV05 Magnetic Tape (TS) + +The TS actually implements the TSV05, with 22-bit addressing, but will +work with TS11 drivers. TS options include the ability to make the unit +write enabled or write locked. + + SET TS LOCKED set unit write locked + SET TS ENABLED set unit write enabled + +The TS11 supports the BOOT command. The bootstrap supports only DEC +standard boot formats. To allow for ANSI labels, the DEC standard bootstrap +skipped the first record and read and executed the second. + +The magnetic tape controller implements these registers: + + name size comments + + TSSR 16 status register + TSBA 16 bus address register + TSDBX 16 data buffer extension register + CHDR 16 command packet header + CADL 16 command packet low address or count + CADH 16 command packet high address + CLNT 16 command packet length + MHDR 16 message packet header + MRFC 16 message packet residual frame count + MXS0 16 message packet extended status 0 + MXS1 16 message packet extended status 1 + MXS2 16 message packet extended status 2 + MXS3 16 message packet extended status 3 + MXS4 16 message packet extended status 4 + WADL 16 write char packet low address + WADH 16 write char packet high address + WLNT 16 write char packet length + WOPT 16 write char packet options + ATTN 1 attention message pending + BOOT 1 boot request pending + OWNC 1 if set, tape owns command buffer + OWNM 1 if set, tape owns message buffer + TIME 24 delay + POS 31 position + +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 fatal tape error + +5.10 Symbolic Display and Input The PDP-11 simulator implements symbolic display and input. Display is controlled by command line switches: @@ -1379,6 +1640,8 @@ DK head-per-track disk controller DP moving head disk controller with four drives MT magnetic tape controller with eight drives +The TTI1/TTO1, PLT, DK, DP, and MT devices can be DISABLEd. + The Nova simulator implements these unique stop conditions: - reference to undefined I/O device, and STOP_DEV is set @@ -1596,6 +1859,9 @@ The real-time clock (CLK) implements these registers: TIME2 24 clock frequency, select = 2 TIME3 24 clock frequency, select = 3 +The real-time clock autocalibrates; the clock interval is adjusted up or +down so that the clock tracks actual elapsed time. + 6.2.7 Plotter (PTP) The plotter (PLT) writes data to a disk file. The POS register @@ -1704,6 +1970,8 @@ The fixed head disk controller implements these registers: TIME 24 rotational delay, per sector STOP_IOE 1 stop on I/O error +The fixed head disk controller supports the BOOT command. + Error handling is as follows: error STOP_IOE processed as @@ -1733,7 +2001,8 @@ write locked, and to select the type of drive: 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. +Units can also be REMOVEd or ADDed to the configuration. The moving head +disk controller supports the BOOT command. All drives have 256 16b words per sector. The other disk parameters are: @@ -1784,7 +2053,8 @@ 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. +Units can also be REMOVEd or ADDed to the configuration. The magnetic +tape controller supports the BOOT command. The magnetic tape controller implements these registers: @@ -1953,6 +2223,9 @@ 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 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 @@ -2045,6 +2318,7 @@ The line printer implements these registers: POS 31 position in the output file TIME 24 time from I/O initiation to interrupt STOP_IOE 1 stop on I/O error + LBUF[0:119] 8 line buffer Error handling is as follows: @@ -2262,6 +2536,7 @@ PDP-9 CPU PDP-9 CPU with 32KW of memory LPT Type 647E line printer CLK integral real-time clock RF RF09/RS09 fixed-head disk + DT TC02/TU55 DECtape MT TC59/TU10 magnetic tape PDP-15 CPU PDP-15 CPU with 32KW of memory @@ -2274,8 +2549,11 @@ PDP-15 CPU PDP-15 CPU with 32KW of memory CLK integral real-time clock RP RP15/RP02 disk pack RF RF15/RS09 fixed-head disk + DT TC15/TU56 DECtape MT TC59/TU10 magnetic tape +The DRM, RF, RP, DT, and MT devices can be DISABLEd. + The 18b PDP simulators implement several unique stop conditions: - an unimplemented instruction is decoded, and register @@ -2284,9 +2562,10 @@ The 18b PDP simulators implement several unique stop conditions: instruction execution The PDP-4 and PDP-7 loaders support only RIM format tapes. The PDP-9 -and PDP-15 support both RIM 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. +and PDP-15 support both RIM and BIN format tapes. If the file extension +is .RIM, or the -r switch is specified with LOAD, the file is assumed to +be RIM format; if the file extension is not .RIM, or if the -b switch is +specified, the file is assumed to be BIN format. 8.1 CPU @@ -2365,6 +2644,11 @@ 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. +On the PDP-4 and PDP-7, the paper tape reader supports the BOOT command. +BOOT PTR copies the RIM loader into memory and starts it running, while +BOOT -F PTR copies the funny format binary loader into memory and starts +it running. + The paper tape reader implements these registers: name size comments @@ -2468,6 +2752,7 @@ The PDP-4 used a Type 62 printer controller, with these registers: POS 31 position in the output file TIME 24 time from I/O initiation to interrupt STOP_IOE 1 stop on I/O error + LBUF[0:119] 8 line buffer The PDP-7 and PDP-7 used a Type 647 printer controller, with these registers: @@ -2483,6 +2768,7 @@ registers: POS 31 position in the output file TIME 24 time from I/O initiation to interrupt STOP_IOE 1 stop on I/O error + LBUF[0:119] 8 line buffer The PDP-15 used an LP15 printer controller, with these registers: @@ -2497,6 +2783,7 @@ The PDP-15 used an LP15 printer controller, with these registers: POS 31 position in the output file TIME 24 time from I/O initiation to interrupt STOP_IOE 1 stop on I/O error + LBUF[0:131] 8 line buffer For all three models, error handling is as follows: @@ -2517,6 +2804,10 @@ The real-time clock (CLK) implements these registers: DONE 1 device done flag ENABLE 1 clock enable TIME 24 clock frequency + TPS 8 ticks per second (60 or 50) + +The real-time clock autocalibrates; the clock interval is adjusted up or +down so that the clock tracks actual elapsed time. 8.3 RP15/RP02 Disk Pack (RP) @@ -2609,7 +2900,58 @@ Error handling is as follows: 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) +8.6 TC02/TU55 and TC15/TU56 DECtape (DT) + +DECtapes drives are numbered 1-8; in the simulator, drive 8 is unit 0. +DECtape options include the ability to make units write enabled or write +locked. + + SET DTn LOCKED set unit n write locked + SET DTn ENABLED set unit n write enabled + +Units can also be REMOVEd or ADDed to the configuration. + +The TC02/TC15 supports both PDP-8 format and PDP-9/11/15 format DECtape +images. ATTACH tries to determine the tape format from the DECtape image; +the user can force a particular format with switches: + + -f foreign (PDP-8) format + -n native (PDP-9/11/15) format + +The DECtape controller is a data-only simulator; the timing and mark +track, and block header and trailer, are not stored. Thus, the WRITE +TIMING AND MARK TRACK function is not supported; the READ ALL function +always returns the hardware standard block header and trailer; and the +WRITE ALL function dumps non-data words into the bit bucket. + +The DECtape controller implements these registers: + + name size comments + + DTSA 12 status register A + DTSB 12 status register B + INT 1 interrupt pending flag + ENB 1 interrupt enable flag + DTF 1 DECtape flag + ERF 1 error flag + CA 18 current address (memory location 30) + WC 18 word count (memory location 31) + LTIME 31 time between lines + ACTIME 31 time to accelerate to full speed + DCTIME 31 time to decelerate to a full stop + SUBSTATE 2 read/write command substate + POS0..7 31 position, in lines, units 0..7 + STATE0..7 31 unit state, units 0-7 + +It is critically important to maintain certain timing relationships +among the DECtape parameters, or the DECtape simulator will fail to +operate correctly. + + - LTIME must be at least 6 + - ACTIME must be less than DCTIME, and both need to be at + least 100 times LTIME + +8.7 TC59/TU10 Magnetic Tape (MT) Magnetic tape options include the ability to make units write enabled or or write locked. @@ -2644,7 +2986,7 @@ Error handling is as follows: OS I/O error report error and stop -8.7 Symbolic Display and Input +8.8 Symbolic Display and Input The 18b PDP simulators implement symbolic display and input. Display is controlled by command line switches: @@ -2926,6 +3268,10 @@ 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 supports the BOOT command. BOOT CDR reads a card image +into locations 1-80, sets a word mark under location 1, clears storage, +and then transfers control to location 1. + 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, @@ -2943,6 +3289,7 @@ The reader/punch registers are: S2 1 stacker 2 select flag POS 31 position TIME 24 delay window for stacker select + BUF[0:79] 8 reader buffer CDP ERR 1 error indicator S4 1 stacker 4 select flag @@ -3009,6 +3356,7 @@ The line printer registers are: CCTL 8 carriage control tape length (read only) ERR 1 error indicator POS 31 position + CCT[0:131] 32 carriage control tape array Error handling is as follows: @@ -3050,7 +3398,7 @@ or write locked. SET MTn ENABLED set unit n write enabled Units can also be REMOVEd or ADDed to the configuration. The magnetic -tape simulator supports the BOOT command. The bootstrap reads the first +tape simulator supports the BOOT command. BOOT MT reads the first record off tape, starting at location 1, and then branches to it. The magnetic tape controller implements these registers: @@ -3783,6 +4131,425 @@ 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. + +12. PDP-10 Features + +The PDP-10 simulator is configured as follows: + +device simulates +name(s) + +CPU KS10 CPU with 1MW of memory +PAG paging unit (translation maps) +UBA Unibus adapters (translation maps) +FE console +TIM timer +PTR,PTP PC11 paper tape reader/punch +LP20 LP20 line printer +RP RH11/RP04/RP05/RP06/RP07/RM03/RM05/RM80 controller with + eight drives +TU RH11/TM02/TU45 controller with eight drives + +The PTR/PTP are initially DISABLEd. No other device can be DISABLEd. + +The PDP-10 simulator implements several unique stop condition: + + - illegal instruction (000) in kernel mode + - indirect addressing nesting exceeds limit + - execute chaining exceeds limit + - page fail or other error in interrupt sequence + - illegal instruction in interrupt sequence + - invalid vector pointer in interrupt sequence + - invalid Unibus adapter number + - non-existent exec or user page table address + +The PDP-10 loader supports RIM10B format paper tapes, SAV binary files, and +EXE binary files. LOAD switches -r, -s, -e specify RIM10, SAV, EXE format, +respectively. If no switch is specified, the LOAD command checks the file +extension; .RIM, .SAV, .EXE specify RIM10, SAV, EXE format, respectively. +If no switch specified, and no extension matches, the LOAD command checks +the file format to try to determine the file type. + +12.1 CPU + +The only CPU option is the choice of standard or ITS compatible microcode. + + SET CPU STANDARD Standard microcode + SET CPU ITS ITS compatible microcode + +CPU registers include the visible state of the processor as well as the +control registers for the interrupt system. + + name size comments + + PC 18 program counter + FLAGS 18 processor flags (<13:17> unused) + AC0..AC17 36 accumulators + IR 36 instruction register + EBR 18 executive base register + PGON 1 paging enabled flag + T20P 1 TOPS-20 paging + UBR 18 user base register + CURAC 3 current AC block + PRVAC 3 previous AC block + SPT 36 shared pointer table + CST 36 core status table + PUR 36 process update register + CSTM 36 CST mask + HSB 18 halt status block address + DBR1 18 descriptor base register 1 (ITS) + DBR2 18 descriptor base register 2 (ITS) + DBR3 18 descriptor base register 3 (ITS) + DBR4 18 descriptor base register 4 (ITS) + PIENB 7 PI levels enabled + PIACT 7 PI levels active + PIPRQ 7 PI levels with program requests + PIIOQ 7 PI levels with IO requests + PIAPR 7 PI levels with APR requests + APRENB 8 APR flags enabled + APRFLG 8 APR flags active + APRLVL 3 PI level for APR interrupt + IND_MAX 8 indirect address nesting limit + XCT_MAX 8 execute chaining limit + OLDPC 18 PC prior to last transfer instruction + BREAK 19 breakpoint address (1777777 to disable) + WRU 8 interrupt character + REG[0:127] 36 fast memory blocks + +12.2 Pager + +The pager contains the page maps for executive and user mode. The +executive page map is the memory space for unit 0, the user page map the +memory space for unit 1. A page map entry is 32 bits wide and has the +following format: + + bit content + --- ------- + 31 page is writeable + 30 entry is valid + 29:19 mbz + 18:9 physical page base address + 8:0 mbz + +The pager has no registers. + +12.3 Unibus Adapters + +The Unibus adapters link the system I/O devices to the CPU. Unibus +adapter 1 (UBA1) is unit 0, and Unibus adapter 3 is unit 1. The +adapter's Unibus map is the memory space of the corresponding unit. + +The Unibus Adapter has the following registers: + + name size comments + + INTREQ 32 interrupt requests + UB1CS 16 Unibus adapter 1 control/status + UB3CS 16 Unibus adapter 3 control/status + +12.4 Front End (FE) + +The front end is the system console. The keyboard input is unit 0, +the console output is unit 1. It supports two options: + + SET FE STOP halts the PDP-10 operating system + SET FE CTLC simulates typing ^C (for Windoze) + +The front end has the following registers: + + name size comments + + IBUF 8 input buffer + ICOUNT 31 count of input characters + ITIME 24 keyboard polling interval + OBUF 8 output buffer + OCOUNT 31 count of output characters + OTIME 24 console output response time + +12.5 Timer (TIM) + +The timer (TIM) implements the system timer, the interval timer, and +the time of day clock used to get the date and time at system startup. +Because most PDP-10 software is not Y2K compliant, the timer implements +one option + + SET TIM NOY2K software not Y2K compliant, limit time of + day clock to 1999 (default) + SET TIM Y2K software is Y2K compliant + +The timer has the following registers: + + name size comments + + TIMBASE 59 time base (double precision) + TTG 36 time to go (remaining time) for interval + PERIOD 36 reset value for interval + QUANT 36 quantum timer (ITS only) + TIME 24 tick delay + DIAG 1 use fixed tick delay instead of autocalibration + +Unless the DIAG flag is set, the timer autocalibrates; the tick delay +is adjusted up or down so that the time base tracks actual elapsed time. +This may cause time-dependent diagnostics to report errors. + +12.6 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 + + +12.7 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 input or 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 + +12.8 RH11 Adapter, RM02/03/05/80, RP04/05/06/07 drives (RP) + +The RP controller implements the Massbus 18b (RH11) 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: + + 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 + +The size options can be used only when a unit is not attached to a file. +Note that TOPS-10 V7.03 only supported the RP06 and RM03; V7.04 added +support for the RP07. Units can 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 + +12.9 RH11/TM02/TU45 Magnetic Tape (TU) + +The magnetic tape simulator simulates an RH11 Massbus adapter with one +TM02 formatter and up to eight TU45. Magnetic tape options include the +ability to make units write enabled or locked. + + SET TUn LOCKED set unit n write locked + SET TUn 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 + + MTCS1 16 control/status 1 + MTBA 16 memory address + MTWC 16 word count + MTFC 16 frame count + MTCS2 16 control/status 2 + MTFS 16 formatter status + MTER 16 error status + MTCC 16 check character + MTDB 16 data buffer + MTMR 16 maintenance register + MTTC 16 tape control register + INT 1 interrupt pending 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 + +12.10 LP20 DMA Line Printer (LP20) + +The LP20 is a DMA-based line printer controller. There is one +line printer option to clear the vertical forms unit (VFU). + + SET LP20 VFUCLEAR clear the vertical forms unit + +The LP20 implements these registers: + + name size comments + + LPCSA 16 control/status register A + LPCSB 16 control/status register B + LPBA 16 bus address register + LPBC 12 byte count register + LPPAGC 12 page count register + LPRDAT 12 RAM data register + LPCBUF 8 character buffer register + LPCOLC 8 column counter register + LPPDAT 8 printer data register + LPCSUM 8 checksum register + DVPTR 7 vertical forms unit pointer + DVLNT 7 vertical forms unit length + INT 1 interrupt request + ERR 1 error flag + DONE 1 done flag + IE 1 interrupt enable flag + POS 31 position in output file + TIME 24 response time + STOP_IOE 1 stop on I/O error + TXRAM[0:255] 12 translation RAM + DAVFU[0:142] 12 vertical forms unit array + +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 + +12.11 Symbolic Display and Input + +The PDP-10 simulator implements symbolic display and input. Display is +controlled by command line switches: + + -a display as ASCII character + -c display as (sixbit) character string + -p display as packed (seven bit) string + -m display instruction mnemonics + -v interpret address as virtual + -e force executive mode + -u force user mode + +Input parsing is controlled by the first character typed in or by command +line switches: + + ' or -a ASCII character + " or -c sixbit string + # or -p packed seven bit string + alphabetic instruction mnemonic + numeric octal number + +Instruction input uses standard PDP-10 assembler syntax. There are three +instruction classes: memory reference, memory reference with AC, and I/O. + +Memory reference instructions have the format + + memref {@}address{(index)} + +memory reference with AC instructions have the format + + memac ac,{@}address{(index)} + +and I/O instructions have the format + + io device,{@}address{(index)} + +where @ signifies indirect. The address is a signed octal number in the +range 0 - 0777777. The ac and index are unsigned octal numbers in the +range 0-17. The device is either a recognized device mnemonic (APR, PI, +TIM) or an octal number in the range 0 - 0177. + +The simulator recognizes the standard MACRO alternate mnemonics (CLEAR +for SETZ, OR for IORI), the individual definitions for JRST and JFCL +variants, and the extended instruction mnemonics. Appendix 1: File Representations @@ -3821,6 +4588,36 @@ have its 16b words byte swapped if read by the PDP-11 simulator. 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. + +5. DECtapes + +DECtapes are structured as fixed length blocks. PDP-9/11/15 DECtapes +use 578 blocks of 256 32b words. Each 32b word contains 18b (6 lines) +of data. PDP-8 DECtapes use 1474 blocks of 129 16b words. Each 16b +word contains 12b (4 lines) of data. Note that PDP-8 OS/8 does not use +the 129th word of each block, and OS/8 DECtape dumps contain only 128 +words per block. A utility, DSOS8CVT.C, is provided to convert OS/8 +DECtape dumps to simulator format. + +A known problem in DECtape format is that when a block is recorded in +one direction and read in the other, the bits in a word are scrambled +(to the complement obverse). The PDP-11 deals with this problem by +performing an automatic complement obverse on reverse writes and reads. +The other systems leave this problem to software. + +The simulator represents this difference as follows. On the PDP-11, all +data is represented in normal form. Data reads and writes are not direction +sensitive; read all and write all are direction sensitive. Real DECtapes +that are read forward will generate images with the correct representation +of the data. + +On the other systems, forward write creates data in normal form, while +reverse write creates data in complement obverse form. Forward read (and +read all) performs no transformations, while reverse read (and read all) +changes data to the complement obverse. Real DECtapes that are read forward +will generate data in normal form for blocks written forward, and complement +obverse data for blocks written in reverse, corresponding to the simulator +format. Appendix 2: Debug Status @@ -3837,24 +4634,26 @@ 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 +fixed disk y/both - h - h floppy disk y y y - - -mag tape h y y - h +DECtape y y - - d +mag tape h y/both y - h - system 1401 2100 Id4 + system 1401 2100 Id4 PDP-10 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 - +CPU d y h y +FPU - - h y +CIS - - - y +console h y h y +paper tape - h h h +card reader h h - - +line printer h - - y +clock - h - y +hard disk - h - y +fixed disk - h - - +floppy disk - - - - +DECtape - - - - +mag tape h h - y legend: y = runs operating system or sample program d = runs diagnostics @@ -3864,6 +4663,39 @@ legend: y = runs operating system or sample program Revision History (since Rev 1.1) +Rev 2.6a, Jul, 01 + Fixed DECtape reset in PDP-8, PDP-11, PDP-9/15 + Fixed RIM loader PC handling in PDP-9/15 + Fixed indirect pointers in PDP-10 paging + Fixed SSC handling in PDP-10 TM02/TU45 + Added FE CTRL-C option for Windoze + +Rev 2.6, May, 01 + Added ENABLE/DISABLE devices + Added SHOW DEVICES + Added examination/modification of register arrays + Added PDP-10 simulator + Added clock autocalibration to SCP, Nova, PDP-8, PDP-11, + PDP-18b + Added PDP-8, PDP-11, PDP-9/15 DECtape + Added PDP-8 DF32 + Added 4k Disk Monitor boot to PDP-8 RF08 and DF32 + Added PDP-4/7 funny format loader support + Added extension handling to the PDP-8 and -9/15 loaders + Added PDP-11 TS11/TSV05 + Added integer interval timer to SCP + Added filename argument to LOAD/DUMP + Revised magtape and DECtape bootstraps to rewind + before first instruction + Fixed 3 cycle data break sequence in PDP-8 RF + Fixed 3 cycle data break sequence in 18b PDP LP, MT, RF + Fixed CS1.TRE write, CS2.MXF,UPE write, and CS2.UAI in + PDP-11 RP + Fixed 4M memory size definition in PDP-11 + Fixed attach bug in RESTORE + Fixed detach bug for buffered devices + Updated copyright notices, fixed comments + Rev 2.5a, Dec, 00 Fixed SCP handling of devices without units Fixed FLG, FBF initialization in many HP peripherals @@ -4030,22 +4862,31 @@ 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 +Dave Gesswein PDP-8/9/15 documentation and diagnostics 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 +Doug Gwyn Portability debugging +Kevin Handy TS11/TSV05 documentation Ken Harrenstein PDP-10 simulator Bill Haygood PDP-8 information, simulator, and software -Jim Jaeger IBM 1401 information +Wolfgang Helbig DZ11 simulator implementation +Mark Hittinger PDP-10 debugging +Jay Jaeger IBM 1401 information Doug Jones PDP-8 information, simulator, and software Al Kossow HP 21xx, Varian 620, TI 990, DEC documentation and software Don Lewine Nova documentation and legal permissions +Tim Litt PDP-10 hardware documentation and schematics, + tape images, and software sources 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, +Thord Nilson DZ11 implementation +Charles Owen Nova moving head disk debugging, Altair simulator, IBM 1401 diagnostics, debugging, and magtape boot Paul Pierce IBM 1401 diagnostics, media recovery +Hans Pufal PDP-10 debugging Bruce Ray Software, documentation, bug fixes, and new devices for the Nova Craig St Clair PDP documentation @@ -4053,15 +4894,18 @@ 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 +Tim Shoppa Nova documentation, RDOS software, PDP-10 and PDP-11 + software archive Michael Somos PDP-1 debugging Hans-Michael Stahl OS/2 port testing, TERMIOS implementation +Tim Stark PDP-10 simulator 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 +Deb Toivonen DEC documentation +Mike Umbricht DEC 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 diff --git a/simh_swre.txt b/simh_swre.txt index bf5f1f42..1de967c6 100644 --- a/simh_swre.txt +++ b/simh_swre.txt @@ -1,7 +1,7 @@ To: Users From: Bob Supnik Subj: Sample Software Packages -Date: 1-Jan-01 +Date: 31-May-01 This memorandum documents the sample software packages available to run with the SIMH simulators. Many of these packages are available under @@ -10,8 +10,8 @@ software. 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 + Original code published in 1993-2001, written by Robert M Supnik + Copyright (c) 1993-2001, 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"), @@ -238,7 +238,9 @@ To load and run SIM8: sim> run AC/ 0000 -6. PDP-15 FOCAL +6. PDP-15 + +6.1 FOCAL FOCAL15 is an interactive program for technical computations. It can execute both immediate commands and stored programs (like BASIC). FOCAL15 @@ -251,6 +253,54 @@ Kossow, who provided the binary image. To load and run FOCAL15: = 1.4142 * +6.2 Advanced Software System/Keyboard Monitor + +The Advanced Software System Keyboard Monitor is the simplest mass storage +monitor for the PDP-15. It offers single-user program development and +execution capabilities. To load and run ADSS/KM-15: + +- Load the paper-tape bootstrap into upper memory: + + sim> load dec-15u.rim 77637 + + You specify the load address. + +- Verify that the bootstrap loaded correctly: + + sim> ex pc + PC: 077646 + +- Mount the Advanced Software System DECtape image on DECtape unit 0: + + sim> attach dt adss15_32k.dtp + +- Run the bootstrap: + + sim> run + +- The DECtape will boot and print out + + KMS9-15 V5B000 + $ + + and is now ready for commands. Recognized commands include: + + D list system device directory + I list available commands + R list device assignments + SCOM list systems communication region + +- To run Focal, assign unused DAT slot 10 and then load Focal + + $A LPA0 10 + $GLOAD + LOADER V5B000 + >_FOCAL + FOCAL V9A + *TYPE 2+2,! + 4.0000 + * + 7. IBM 1401 7.1 Single Card "Koans" @@ -407,4 +457,9 @@ To load and run BASIC: RUN 1.41421 +9. PDP-10 TOPS-10 7.03 + +TOPS-10 was the primary time-shared operating system for the PDP-10. +Installation and distribution tapes for TOPS-10 7.03 are available at +http://pdp-10.trailing-edge.com. [end simh_swre.txt]