1
0
mirror of https://github.com/simh/simh.git synced 2026-01-26 04:01:38 +00:00

Notes For V2.10-2

1. New Features in 2.10-2

The build procedures have changed.  There is only one UNIX makefile.
To compile without Ethernet support, simply type

	gmake {target|all}

To compile with Ethernet support, type

	gmake USE_NETWORK=1 {target|all}

The Mingw batch files require Mingw release 2 and invoke the Unix
makefile.  There are still separate batch files for compilation
with or without Ethernet support.

1.1 SCP and Libraries

- The EVAL command will evaluate a symbolic type-in and display
  it in numeric form.
- The ! command (with no arguments) will launch the host operating
  system command shell.  The ! command (with an argument) executes
  the argument as a host operating system command.  (Code from
  Mark Pizzolato)
- Telnet sessions now recognize BREAK.  How a BREAK is transmitted
  dependent on the particular Telnet client.  (Code from Mark
  Pizzolato)
- The sockets library includes code for active connections as
  well as listening connections.
- The RESTORE command will restore saved memory size, if the
  simulator supports dynamic memory resizing.

1.2 PDP-1

- The PDP-1 supports the Type 24 serial drum (based on recently
  discovered documents).

1.3 18b PDP's

- The PDP-4 supports the Type 24 serial drum (based on recently
  discovered documents).

1.4 PDP-11

- The PDP-11 implements a stub DEUNA/DELUA (XU).  The real XU
  module will be included in a later release.

1.5 PDP-10

- The PDP-10 implements a stub DEUNA/DELUA (XU).  The real XU
  module will be included in a later release.

1.6 HP 2100

- The IOP microinstruction set is supported for the 21MX as well
  as the 2100.
- The HP2100 supports the Access Interprocessor Link (IPL).

1.7 VAX

- If the VAX console is attached to a Telnet session, BREAK is
  interpreted as console halt.
- The SET/SHOW HISTORY commands enable and display a history of
  the most recently executed instructions.  (Code from Mark
  Pizzolato)

1.8 Terminals Multiplexors

- BREAK detection was added to the HP, DEC, and Interdata terminal
  multiplexors.

1.9 Interdata 16b and 32b

- First release.  UNIX is not yet working.

1.10 SDS 940

- First release.

2. Bugs Fixed in 2.10-2

- PDP-11 console must default to 7b for early UNIX compatibility.
- PDP-11/VAX TMSCP emulator was using the wrong packet length for
  read/write end packets.
- Telnet IAC+IAC processing was fixed, both for input and output
  (found by Mark Pizzolato).
- PDP-11/VAX Ethernet setting flag bits wrong for chained
  descriptors (found by Mark Pizzolato).

3. New Features in 2.10 vs prior releases

3.1 SCP and Libraries

- The VT emulation package has been replaced by the capability
  to remote the console to a Telnet session.  Telnet clients
  typically have more complete and robust VT100 emulation.
- Simulated devices may now have statically allocated buffers,
  in addition to dynamically allocated buffers or disk-based
  data stores.
- The DO command now takes substitutable arguments (max 9).
  In command files, %n represents substitutable argument n.
- The initial command line is now interpreted as the command
  name and substitutable arguments for a DO command.  This is
  backward compatible to prior versions.
- The initial command line parses switches.  -Q is interpreted
  as quiet mode; informational messages are suppressed.
- The HELP command now takes an optional argument.  HELP <cmd>
  types help on the specified command.
- Hooks have been added for implementing GUI-based consoles,
  as well as simulator-specific command extensions.  A few
  internal data structures and definitions have changed.
- Two new routines (tmxr_open_master, tmxr_close_master) have
  been added to sim_tmxr.c.  The calling sequence for
  sim_accept_conn has been changed in sim_sock.c.
- The calling sequence for the VM boot routine has been modified
  to add an additional parameter.
- SAVE now saves, and GET now restores, controller and unit flags.
- Library sim_ether.c has been added for Ethernet support.

3.2 VAX

- Non-volatile RAM (NVR) can behave either like a memory or like
  a disk-based peripheral.  If unattached, it behaves like memory
  and is saved and restored by SAVE and RESTORE, respectively.
  If attached, its contents are loaded from disk by ATTACH and
  written back to disk at DETACH and EXIT.
- SHOW <device> VECTOR displays the device's interrupt vector.
  A few devices allow the vector to be changed with SET
  <device> VECTOR=nnn.
- SHOW CPU IOSPACE displays the I/O space address map.
- The TK50 (TMSCP tape) has been added.
- The DEQNA/DELQA (Qbus Ethernet controllers) have been added.
- Autoconfiguration support has been added.
- The paper tape reader has been removed from vax_stddev.c and
  now references a common implementation file, dec_pt.h.
- Examine and deposit switches now work on all devices, not just
  the CPU.
- Device address conflicts are not detected until simulation starts.

3.3 PDP-11

- SHOW <device> VECTOR displays the device's interrupt vector.
  Most devices allow the vector to be changed with SET
  <device> VECTOR=nnn.
- SHOW CPU IOSPACE displays the I/O space address map.
- The TK50 (TMSCP tape), RK611/RK06/RK07 (cartridge disk),
  RX211 (double density floppy), and KW11P programmable clock
  have been added.
- The DEQNA/DELQA (Qbus Ethernet controllers) have been added.
- Autoconfiguration support has been added.
- The paper tape reader has been removed from pdp11_stddev.c and
  now references a common implementation file, dec_pt.h.
- Device bootstraps now use the actual CSR specified by the
  SET ADDRESS command, rather than just the default CSR.  Note
  that PDP-11 operating systems may NOT support booting with
  non-standard addresses.
- Specifying more than 256KB of memory, or changing the bus
  configuration, causes all peripherals that are not compatible
  with the current bus configuration to be disabled.
- Device address conflicts are not detected until simulation starts.

3.4 PDP-10

- SHOW <device> VECTOR displays the device's interrupt vector.
  A few devices allow the vector to be changed with SET
  <device> VECTOR=nnn.
- SHOW CPU IOSPACE displays the I/O space address map.
- The RX211 (double density floppy) has been added; it is off
  by default.
- The paper tape now references a common implementation file,
  dec_pt.h.
- Device address conflicts are not detected until simulation starts.

3.5 PDP-1

- DECtape (then known as MicroTape) support has been added.
- The line printer and DECtape can be disabled and enabled.

3.6 PDP-8

- The RX28 (double density floppy) has been added as an option to
  the existing RX8E controller.
- SHOW <device> DEVNO displays the device's device number.  Most
  devices allow the device number to be changed with SET <device>
  DEVNO=nnn.
- Device number conflicts are not detected until simulation starts.

3.7 IBM 1620

- The IBM 1620 simulator has been released.

3.8 AltairZ80

- A hard drive has been added for increased storage.
- Several bugs have been fixed.

3.9 HP 2100

- The 12845A has been added and made the default line printer (LPT).
  The 12653A has been renamed LPS and is off by default.  It also
  supports the diagnostic functions needed to run the DCPC and DMS
  diagnostics.
- The 12557A/13210A disk defaults to the 13210A (7900/7901).
- The 12559A magtape is off by default.
- New CPU options (EAU/NOEAU) enable/disable the extended arithmetic
  instructions for the 2116.  These instructions are standard on
  the 2100 and 21MX.
- New CPU options (MPR/NOMPR) enable/disable memory protect for the
  2100 and 21MX.
- New CPU options (DMS/NODMS) enable/disable the dynamic mapping
  instructions for the 21MX.
- The 12539 timebase generator autocalibrates.

3.10 Simulated Magtapes

- Simulated magtapes recognize end of file and the marker
  0xFFFFFFFF as end of medium.  Only the TMSCP tape simulator
  can generate an end of medium marker.
- The error handling in simulated magtapes was overhauled to be
  consistent through all simulators.

3.11 Simulated DECtapes

- Added support for RT11 image file format (256 x 16b) to DECtapes.

4. Bugs Fixed in 2.10 vs prior releases

- TS11/TSV05 was not simulating the XS0_MOT bit, causing failures
  under VMS.  In addition, two of the CTL options were coded
  interchanged.
- IBM 1401 tape was not setting a word mark under group mark for
  load mode reads.  This caused the diagnostics to crash.
- SCP bugs in ssh_break and set_logon were fixed (found by Dave
  Hittner).
- Numerous bugs in the HP 2100 extended arithmetic, floating point,
  21MX, DMS, and IOP instructions were fixed.  Bugs were also fixed
  in the memory protect and DMS functions.  The moving head disks
  (DP, DQ) were revised to simulate the hardware more accurately.
  Missing functions in DQ (address skip, read address) were added.
- PDP-10 tape wouldn't boot, and then wouldn't read (reported by
  Michael Thompson and Harris Newman, respectively)
- PDP-1 typewriter is half duplex, with only one shift state for
  both input and output (found by Derek Peschel)

5. General Notes

WARNING: V2.10 has reorganized and renamed some of the definition
files for the PDP-10, PDP-11, and VAX.  Be sure to delete all
previous source files before you unpack the Zip archive, or
unpack it into a new directory structure.

WARNING: V2.10 has a new, more comprehensive save file format.
Restoring save files from previous releases will cause 'invalid
register' errors and loss of CPU option flags, device enable/
disable flags, unit online/offline flags, and unit writelock
flags.

WARNING: If you are using Visual Studio .NET through the IDE,
be sure to turn off the /Wp64 flag in the project settings, or
dozens of spurious errors will be generated.

WARNING: Compiling Ethernet support under Windows requires
extra steps; see the Ethernet readme file.  Ethernet support is
currently available only for Windows, Linux, NetBSD, and OpenBSD.
This commit is contained in:
Bob Supnik
2003-01-17 18:35:00 -08:00
committed by Mark Pizzolato
parent 4ea745b3ad
commit 2bcd1e7c4c
206 changed files with 30253 additions and 12114 deletions

1247
SDS/sds_cpu.c Normal file

File diff suppressed because it is too large Load Diff

401
SDS/sds_defs.h Normal file
View File

@@ -0,0 +1,401 @@
/* sds_defs.h: SDS 940 simulator definitions
Copyright (c) 2001-2002, 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 */
/* Simulator stop codes */
#define STOP_IONRDY 1 /* I/O dev not ready */
#define STOP_HALT 2 /* HALT */
#define STOP_IBKPT 3 /* breakpoint */
#define STOP_INVDEV 4 /* invalid dev */
#define STOP_INVINS 5 /* invalid instr */
#define STOP_INVIOP 6 /* invalid I/O op */
#define STOP_INDLIM 7 /* indirect limit */
#define STOP_EXULIM 8 /* EXU limit */
#define STOP_MMINT 9 /* mm in intr */
#define STOP_MMTRP 10 /* mm in trap */
#define STOP_TRPINS 11 /* trap inst not BRM */
#define STOP_RTCINS 12 /* rtc inst not MIN/SKR */
#define STOP_ILLVEC 13 /* zero vector */
#define STOP_CCT 14 /* runaway CCT */
/* Trap codes */
#define MM_PRVINS -040 /* privileged */
#define MM_NOACC -041 /* no access */
#define MM_WRITE -043 /* write protect */
#define MM_MONUSR -044 /* mon to user */
/* Conditional error returns */
#define CRETINS return ((stop_invins)? STOP_INVINS: SCPE_OK)
#define CRETDEV return ((stop_invdev)? STOP_INVDEV: SCPE_OK)
#define CRETIOP return ((stop_inviop)? STOP_INVIOP: SCPE_OK)
#define CRETIOE(f,c) return ((f)? c: SCPE_OK)
/* Architectural constants */
#define SIGN 040000000 /* sign */
#define DMASK 077777777 /* data mask */
#define EXPS 0400 /* exp sign */
#define EXPMASK 0777 /* exp mask */
#define SXT(x) ((int32) (((x) & SIGN)? ((x) | ~DMASK): \
((x) & DMASK)))
#define SXT_EXP(x) ((int32) (((x) & EXPS)? ((x) | ~EXPMASK): \
((x) & EXPMASK)))
/* Memory */
#define MAXMEMSIZE (1 << 16) /* max memory size */
#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */
#define MEMSIZE (cpu_unit.capac) /* actual memory size */
#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE)
#define ReadP(x) M[x]
#define WriteP(x,y) if (MEM_ADDR_OK (x)) M[x] = y
/* Virtual addressing */
#define VA_SIZE (1 << 14) /* virtual addr size */
#define VA_MASK (VA_SIZE - 1) /* virtual addr mask */
#define VA_V_PN 11 /* page number */
#define VA_M_PN 07
#define VA_GETPN(x) (((x) >> VA_V_PN) & VA_M_PN)
#define VA_POFF ((1 << VA_V_PN) - 1) /* offset */
#define VA_USR (I_USR) /* user flag in addr */
#define XVA_MASK (VA_USR | VA_MASK)
/* Arithmetic */
#define TSTS(x) ((x) & SIGN)
#define NEG(x) (-((int32) (x)) & DMASK)
#define ABS(x) (TSTS (x)? NEG(x): (x))
/* Memory map */
#define MAP_PROT (040 << VA_V_PN) /* protected */
#define MAP_PAGE (037 << VA_V_PN) /* phys page number */
/* Instruction format */
#define I_USR (1 << 23) /* user */
#define I_IDX (1 << 22) /* indexed */
#define I_POP (1 << 21) /* programmed op */
#define I_V_TAG 21 /* tag */
#define I_V_OP 15 /* opcode */
#define I_M_OP 077
#define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP)
#define I_IND (1 << 14) /* indirect */
#define I_V_SHFOP 11 /* shift op */
#define I_M_SHFOP 07
#define I_GETSHFOP(x) (((x) >> I_V_SHFOP) & I_M_SHFOP)
#define I_SHFMSK 0777 /* shift count */
#define I_V_IOMD 12 /* IO inst mode */
#define I_M_IOMD 03
#define I_GETIOMD(x) (((x) >> I_V_IOMD) & I_M_IOMD)
#define I_V_SKCND 7 /* SKS skip cond */
#define I_M_SKCND 037
#define I_GETSKCND(x) (((x) >> I_V_SKCND) & I_M_SKCND)
#define I_EOB2 000400000 /* chan# bit 2 */
#define I_SKB2 000040000 /* skschan# bit 2 */
#define I_EOB1 020000000 /* chan# bit 1 */
#define I_EOB0 000000100 /* chan# bit 0 */
#define I_GETEOCH(x) ((((x) & I_EOB2)? 4: 0) | \
(((x) & I_EOB1)? 2: 0) | \
(((x) & I_EOB0)? 1: 0))
#define I_SETEOCH(x) ((((x) & 4)? I_EOB2: 0) | \
(((x) & 2)? I_EOB1: 0) | \
(((x) & 1)? I_EOB0: 0))
#define I_GETSKCH(x) ((((x) & I_SKB2)? 4: 0) | \
(((x) & I_EOB1)? 2: 0) | \
(((x) & I_EOB0)? 1: 0))
#define I_SETSKCH(x) ((((x) & 4)? I_SKB2: 0) | \
(((x) & 2)? I_EOB1: 0) | \
(((x) & 1)? I_EOB0: 0))
/* Globally visible flags */
#define UNIT_V_GENIE (UNIT_V_UF + 0)
#define UNIT_GENIE (1 << UNIT_V_GENIE)
/* Timers */
#define TMR_RTC 0 /* clock */
#define TMR_MUX 1 /* mux */
/* I/O routine functions */
#define IO_CONN 0 /* connect */
#define IO_EOM1 1 /* EOM mode 1 */
#define IO_DISC 2 /* disconnect */
#define IO_READ 3 /* read */
#define IO_WRITE 4 /* write */
#define IO_WREOR 5 /* write eor */
#define IO_SKS 6 /* skip signal */
/* Dispatch template */
struct sdsdspt {
uint32 num; /* # entries */
uint32 off; /* offset from base */
};
typedef struct sdsdspt DSPT;
/* Device information block */
struct sdsdib {
int32 chan; /* channel */
int32 dev; /* base dev no */
int32 xfr; /* xfer flag */
DSPT *tplt; /* dispatch templates */
t_stat (*iop) (uint32 fnc, uint32 dev, uint32 *dat);
};
typedef struct sdsdib DIB;
/* Channels */
#define NUM_CHAN 8 /* max num chan */
#define CHAN_W 0 /* TMCC */
#define CHAN_Y 1
#define CHAN_C 2
#define CHAN_D 3
#define CHAN_E 4 /* DACC */
#define CHAN_F 5
#define CHAN_G 6
#define CHAN_H 7
/* I/O control EOM */
#define CHC_REV 04000 /* reverse */
#define CHC_NLDR 02000 /* no leader */
#define CHC_BIN 01000 /* binary */
#define CHC_V_CPW 7 /* char/word */
#define CHC_M_CPW 03
#define CHC_GETCPW(x) (((x) >> CHC_V_CPW) & CHC_M_CPW)
/* Buffer control (extended) EOM */
#define CHM_CE 04000 /* compat/ext */
#define CHM_ER 02000 /* end rec int */
#define CHM_ZC 01000 /* zero wc int */
#define CHM_V_FNC 7 /* term func */
#define CHM_M_FNC 03
#define CHM_GETFNC(x) (((x) & CHM_CE)? (((x) >> CHM_V_FNC) & CHM_M_FNC): CHM_COMP)
#define CHM_IORD 0 /* record, disc */
#define CHM_IOSD 1 /* signal, disc */
#define CHM_IORP 2 /* record, proc */
#define CHM_IOSP 3 /* signal, proc */
#define CHM_COMP 5 /* compatible */
#define CHM_SGNL 1 /* signal bit */
#define CHM_PROC 2 /* proceed bit */
#define CHM_V_HMA 5 /* hi mem addr */
#define CHM_M_HMA 03
#define CHM_GETHMA(x) (((x) >> CHM_V_HMA) & CHM_M_HMA)
#define CHM_V_HWC 0 /* hi word count */
#define CHM_M_HWC 037
#define CHM_GETHWC(x) (((x) >> CHM_V_HWC) & CHM_M_HWC)
/* Channel flags word */
#define CHF_ERR 00001 /* error */
#define CHF_IREC 00002 /* interrecord */
#define CHF_ILCE 00004 /* interlace */
#define CHF_DCHN 00010 /* data chain */
#define CHF_EOR 00020 /* end of record */
#define CHF_12B 00040 /* 12 bit mode */
#define CHF_24B 00100 /* 24 bit mode */
#define CHF_OWAK 00200 /* output wake */
#define CHF_SCAN 00400 /* scan */
#define CHF_TOP 01000 /* TOP pending */
#define CHF_N_FLG 9 /* <= 16 */
/* Interrupts and vectors (0 is reserved), highest bit is highest priority */
#define INT_V_PWRO 31 /* power on */
#define INT_V_PWRF 30 /* power off */
#define INT_V_CPAR 29 /* CPU parity err */
#define INT_V_IPAR 28 /* IO parity err */
#define INT_V_RTCS 27 /* clock sync */
#define INT_V_RTCP 26 /* clock pulse */
#define INT_V_YZWC 25 /* chan Y zero wc */
#define INT_V_WZWC 24 /* chan W zero wc */
#define INT_V_YEOR 23 /* chan Y end rec */
#define INT_V_WEOR 22 /* chan W end rec */
#define INT_V_CZWC 21 /* chan C */
#define INT_V_CEOR 20
#define INT_V_DZWC 19 /* chan D */
#define INT_V_DEOR 18
#define INT_V_EZWC 17 /* chan E */
#define INT_V_EEOR 16
#define INT_V_FZWC 15 /* chan F */
#define INT_V_FEOR 14
#define INT_V_GZWC 13 /* chan G */
#define INT_V_GEOR 12
#define INT_V_HZWC 11 /* chan H */
#define INT_V_HEOR 10
#define INT_V_MUXR 9 /* mux receive */
#define INT_V_MUXT 8 /* mux transmit */
#define INT_V_MUXCO 7 /* SDS carrier on */
#define INT_V_MUXCF 6 /* SDS carrier off */
#define INT_V_DRM 5 /* Genie drum */
#define INT_V_FORK 4 /* fork */
#define INT_PWRO (1 << INT_V_PWRO)
#define INT_PWRF (1 << INT_V_PWRF)
#define INT_CPAR (1 << INT_V_CPAR)
#define INT_IPAR (1 << INT_V_IPAR)
#define INT_RTCS (1 << INT_V_RTCS)
#define INT_RTCP (1 << INT_V_RTCP)
#define INT_YZWC (1 << INT_V_YZWC)
#define INT_WZWC (1 << INT_V_WZWC)
#define INT_YEOR (1 << INT_V_YEOR)
#define INT_WEOR (1 << INT_V_WEOR)
#define INT_CZWC (1 << INT_V_CZWC)
#define INT_CEOR (1 << INT_V_CEOR)
#define INT_DZWC (1 << INT_V_DZWC)
#define INT_DEOR (1 << INT_V_DEOR)
#define INT_EZWC (1 << INT_V_EZWC)
#define INT_EEOR (1 << INT_V_EEOR)
#define INT_FZWC (1 << INT_V_FZWC)
#define INT_FEOR (1 << INT_V_FEOR)
#define INT_GZWC (1 << INT_V_GZWC)
#define INT_GEOR (1 << INT_V_GEOR)
#define INT_HZWC (1 << INT_V_HZWC)
#define INT_HEOR (1 << INT_V_HEOR)
#define INT_MUXR (1 << INT_V_MUXR)
#define INT_MUXT (1 << INT_V_MUXT)
#define INT_MUXCO (1 << INT_V_MUXCO)
#define INT_MUXCF (1 << INT_V_MUXCF)
#define INT_DRM (1 << INT_V_DRM)
#define INT_FORK (1 << INT_V_FORK)
#define VEC_PWRO 0036
#define VEC_PWRF 0037
#define VEC_CPAR 0056
#define VEC_IPAR 0057
#define VEC_RTCS 0074
#define VEC_RTCP 0075
#define VEC_YZWC 0030
#define VEC_WZWC 0031
#define VEC_YEOR 0032
#define VEC_WEOR 0033
#define VEC_CZWC 0060
#define VEC_CEOR 0061
#define VEC_DZWC 0062
#define VEC_DEOR 0063
#define VEC_EZWC 0064
#define VEC_EEOR 0065
#define VEC_FZWC 0066
#define VEC_FEOR 0067
#define VEC_GZWC 0070
#define VEC_GEOR 0071
#define VEC_HZWC 0072
#define VEC_HEOR 0073
#define VEC_MUXR 0200 /* term mux rcv */
#define VEC_MUXT 0201 /* term mux xmt */
#define VEC_MUXCO 0202 /* SDS: mux carrier on */
#define VEC_MUXCF 0203 /* SDS: mux carrier off */
#define VEC_DRM 0202 /* Genie: drum */
#define VEC_FORK 0216 /* "fork" */
/* Device constants */
#define DEV_MASK 077 /* device mask */
#define DEV_TTI 001 /* teletype */
#define DEV_PTR 004 /* paper tape rdr */
#define DEV_MT 010 /* magtape */
#define DEV_RAD 026 /* fixed head disk */
#define DEV_DSK 026 /* moving head disk */
#define DEV_TTO 041 /* teletype */
#define DEV_PTP 044 /* paper tape punch */
#define DEV_LPT 060 /* line printer */
#define DEV_MTS 020 /* MT scan/erase */
#define DEV_OUT 040 /* output flag */
#define DEV3_GDRM 004 /* Genie drum */
#define DEV3_GMUX 001 /* Genie mux */
#define DEV3_SMUX (DEV_MASK) /* standard mux */
#define LPT_WIDTH 132 /* line print width */
#define CCT_LNT 132 /* car ctrl length */
/* Transfer request flags for devices (0 is reserved) */
#define XFR_V_TTI 1 /* console */
#define XFR_V_TTO 2
#define XFR_V_PTR 3 /* paper tape */
#define XFR_V_PTP 4
#define XFR_V_LPT 5 /* line printer */
#define XFR_V_RAD 6 /* fixed hd disk */
#define XFR_V_DSK 7 /* mving hd disk */
#define XFR_V_MT0 8 /* magtape */
#define XFR_TTI (1 << XFR_V_TTI)
#define XFR_TTO (1 << XFR_V_TTO)
#define XFR_PTR (1 << XFR_V_PTR)
#define XFR_PTP (1 << XFR_V_PTP)
#define XFR_LPT (1 << XFR_V_LPT)
#define XFR_RAD (1 << XFR_V_RAD)
#define XFR_DSK (1 << XFR_V_DSK)
#define XFR_MT0 (1 << XFR_V_MT0)
/* PIN/POT ordinals (0 is reserved) */
#define POT_ILCY 1 /* interlace */
#define POT_DCRY (POT_ILCY + NUM_CHAN) /* data chain */
#define POT_ADRY (POT_DCRY + NUM_CHAN) /* address reg */
#define POT_RL1 (POT_ADRY + NUM_CHAN) /* RL1 */
#define POT_RL2 (POT_RL1 + 1) /* RL2 */
#define POT_RL4 (POT_RL2 + 1) /* RL4 */
#define POT_RADS (POT_RL4 + 1) /* fhd sector */
#define POT_RADA (POT_RADS + 1) /* fhd addr */
#define POT_DSK (POT_RADA + 1) /* mhd sec/addr */
#define POT_SYSI (POT_DSK + 1) /* sys intr */
#define POT_MUX (POT_SYSI + 1) /* multiplexor */
/* Opcodes */
enum opcodes {
HLT, BRU, EOM, EOD = 006,
MIY = 010, BRI, MIW, POT, ETR, MRG = 016, EOR,
NOP, OVF = 022, EXU,
YIM = 030, WIM = 032, PIN, STA = 035, STB, STX,
SKS, BRX, BRM = 043, RCH = 046,
SKE = 050, BRR, SKB, SKN, SUB, ADD, SUC, ADC,
SKR, MIN, XMA, ADM, MUL, DIV, RSH, LSH,
SKM, LDX, SKA, SKG, SKD, LDB, LDA, EAX };
/* Channel function prototypes */
void chan_set_flag (int32 ch, uint32 fl);
void chan_set_ordy (int32 ch);
void chan_disc (int32 ch);
void chan_set_uar (int32 ch, uint32 dev);
t_stat set_chan (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat chan_process (void);
t_bool chan_testact (void);

114
SDS/sds_diag.txt Normal file
View File

@@ -0,0 +1,114 @@
SDS Diagnostics, using the SDS 930/940 Master Diagnostic Tape image (D930X4A.TAP)
Summary
930 0-16K Memory Test passed
930 16K-32K Memory Test passed
930 Instruction Test passed
930 P&S Register Test passed
---
930 0-16K Memory Test
sim> att mt diag.tap
sim> d a 1
sim> d bpt4 1 ; stop every 1/2 cycle
sim> boot mt
HALT instruction, P: 00050 (STA 122,4)
sim> ex a
A: 00000000 ; error count
sim> c
HALT instruction, P: 37650 (STA 37722,4)
sim> ex a
A: 00000000 ; error count
---
930 16K-32K Memory Test
sim> att mt diag.tap
sim> d a 2
sim> d bpt4 2 ; stop every 1/2 cycle
sim> boot mt
HALT instruction, P: 00050 (STA 6)
sim> ex a
A: 00000000 ; error count
sim> c
HALT instruction, P: 37650 (STA 37406)
sim> ex a
A: 00000000 ; error count
sim> c
---
930 Instruction Diagnostic
sim> att mt diag.tap
sim> d a 3
sim> br 17 ; catch start of diagnostic
sim> boot mt
Breakpoint, P: 00017 (BRR 12,2)
sim> nobr 17
sim> br 112 ; catch end of diagnostic
sim> c
Breakpoint, P: 00112 (BRU 3)
---
930 P&S Register Test
sim> att mt diag.tap
sim> d a 4
sim> br 60 ; catch end of pass
sim> boot mt
Breakpoint, P: 00060 (BRU 22)
---
Bugs
1. IO: Channel WAR not cleared after memory store
2. IO: dev_map should contain _flags, not _v_flags
3. SYS: Errors in system tables
4. SYS: Character conversion table had 0 (space) as illegal, should be -1
5. IO: Channel CPW calculation wrong for 12b mode
6. RAD, DSK, MT: Instruction masks wrong for RAD, DSK, MT
7. IO: Missing subscripts in dev_disp references
8. RAD: typos referencing DSK
9. IO: SKS 3 call incorrect
10. DRM: Drum track mask width incorrect
11. CPU: Memory management trap left reason in bogus state, stopped simulator
12. CPU: Interrupts require api_lvl as well as api_lvlhi, like PDP-10, PDP-15
13. CPU: Bug in find interrupt request
14. CPU: Interrupt priority scheme recoded for left to right priority
15. CPU: overflow test coded backwards
16. CPU: Rotates operate mod 48, not with upper limit of 48 (manual incorrect)
17. CPU: RSH not handling >= 48 correctly
18. CPU: CNA is 2's complement not 1's complement
19. CPU: MUL failed to mask cross product correctly
20. CPU: EM2, EM3 test using wrong 'channel'
21. CPU: EM3 test tested EM2 instead
22. CPU: POP must save EM2, EM3 like BRM (manual incorrect)
23. CPU: Shifts need special EA calculation, direct cycles using 9b indexing
24. CPU: Shifts ignore addr<13:14>
25. CPU: Diagnostic uses undefined shift 'normalize cyclic'
26. CPU: Divide 2'c complement of AB leaves B<23> unchanged
27. CPU: Divide overflow test requires special cases for divd.h == divr
28. CPU: Divide uses non-restoring algorithm
29. CPU: Channel terminate output must be deferred until channel buffer clears
30. CPU: Channel terminate output to magtape is convert to scan, must be
handled in channel logic
31. SYS: duplicate entries for shifts
32. SYS: mask for shifts did not include indirect flag
33. MUX: Genie/SDS use inverted meanings for line enable flag
34. MT: missing fseek before write eof
35. MT: displayed characters only 7b wide instead of 8b
36. CPU: EOD 20000 used by diagnostic (EM change is NOP)
37. CPU: SKD sets all 24b of X, not just exponent
38. CPU: reset should not clear A, B, X

527
SDS/sds_doc.txt Normal file
View File

@@ -0,0 +1,527 @@
To: Users
From: Bob Supnik
Subj: SDS 940 Simulator Usage
Date: 15-Nov-2002
COPYRIGHT NOTICE
The following copyright notice applies to both the SIMH source and binary:
Original code published in 1993-2002, written by Robert M Supnik
Copyright (c) 1993-2002, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
This memorandum documents the SDS 940 simulator.
1. Simulator Files
sim/ sim_defs.h
sim_rev.h
sim_sock.h
sim_tmxr.h
scp.c
scp_tty.c
sim_sock.c
sim_tmxr.c
sim/sds/ sds_defs.h
sds_cpu.c
sds_drm.c
sds_dsk.c
sds_io.c
sds_lp.c
sds_mt.c
sds_mux.c
sds_rad.c
sds_stddev.c
sds_sys.c
2. SDS 940 Features
The SDS-940 simulator is configured as follows:
device simulates
name(s)
CPU SDS-940 CPU with 16KW to 64KW of memory
CHAN I/O channels
PTR paper tape reader
PTP paper tape punch
TTI console input
TTO console output
LPT line printer
RTC real-time clock
MUX terminal multiplexor
DRM Project Genie drum
RAD fixed head disk
DSK 9164/9165 rapid access (moving head) disk
MT magnetic tape
Most devices can be disabled or enabled with the SET <dev> DISABLED and
SET <dev> ENABLED commands, respectively.
2.1 CPU
The CPU options set the size of main memory and the configuration of
peripherals.
SET CPU 16K set memory size = 16KW
SET CPU 32K set memory size = 32KW
SET CPU 48K set memory size = 48KW
SET CPU 64K set memory size = 64KW
SET CPU GENIE enable DRM, set terminal mux
to GENIE mode
SET CPU SDS disable DRM, set terminal mux
to SDS mode
If memory size is being reduced, and the memory being truncated contains
non-zero data, the simulator asks for confirmation. Data in the truncated
portion of memory is lost. Initial memory size is 64KW.
CPU registers include the visible state of the processor as well as the
control registers for the interrupt system.
name size comments
P 14 program counter
A 24 accumulator A
B 24 accumulator B
X 24 index register
OV 1 overflow indicator
EM2 3 memory extension, quadrant 2
EM3 3 memory extension, quadrant 3
RL1 24 user relocation register 1
RL2 24 user relocation register 2
RL4 12 kernel relocation register
NML 1 normal mode flag
USR 1 user mode flag
MONUSR 1 monitor-to-user trap enable
ION 1 interrupt enable
INTDEF 1 interrupt defer
INTREQ 32 interrupt request flags
APIACT 5 highest active API level
APIREQ 5 highest requesting API level
XFRREQ 32 device transfer request flags
BPT 4 breakpoint switches
ALERT 6 outstanding alert number
STOP_INVINS 1 stop on invalid instruction
STOP_INVDEV 1 stop on invalid device number
STOP_INVIOP 1 stop on invalid I/O operation
INDLIM 8 maximum indirect nesting depth
EXULIM 8 maximum execute nesting depth
PCQ[0:63] 14 P prior to last branch or interrupt;
most recent P change first
WRU 8 interrupt character
2.2 Channels (CHAN)
The SDS 940 has up to eight I/O channels, designated W, Y, C, D, E, F, G,
and H. W, Y, C, and D are time-multiplexed communications channels (TMCC);
E, F, G, and H are direct access communications channels (DACC). Unlike
real SDS 940 channels, the simulated channels handle 6b, 12b, and 24b transfers
simultaneously. The association between a device and a channel is displayed
by the SHOW <dev> CHAN command:
SIM> SHOW LPT CHAN
channel=W
The user can change the association with the SET <dev> CHAN=<chan> command,
where <chan> is a channel letter:
SIM> SET LPT CHAN=E
SIM> SHOW LPT CHAN
channel=E
Each channel has nine registers. The registers are arrays, with entry [0]
for channel W, entry [1] for channel Y, etc.
name size comments
UAR[0:7] 6 unit address register
WCR[0:7] 15 word count register
MAR[0:7] 16 memory address register
DCR[0:7] 6 data chaining register
WAR[0:7] 24 word assembly register
CPW[0:7] 2 characters per word
CNT[0:7] 3 character count
MODE[0:7] 12 channel mode (from EOM instruction)
FLAG[0:7] 9 channel flags
The user can display all the registers in a channel with the command SHOW
CHAN <chan>.
2.3 Console Input (TTI)
The console input (TTI) polls the console keyboard for input. It
implements these registers:
name size comments
BUF 6 data buffer
XFR 1 transfer ready flag
POS 32 number of characters input
TIME 24 polling interval
By default, the console input is assigned to channel W.
2.4 Console Output (TTO)
The console output (TTO) writes to the simulator console window. It
implements these registers:
name size comments
BUF 6 data buffer
XFR 1 transfer ready flag
POS 32 number of characters input
TIME 24 time from I/O initiation to interrupt
By default, the console output is assigned to channel W.
2.5 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 6 data buffer
XFR 1 transfer ready flag
SOR 1 start of record flag
CHAN 4 active channel
POS 32 number of characters input
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
By default, the paper tape reader is assigned to channel W.
2.6 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 6 data buffer
XFR 1 transfer ready flag
LDR 1 punch leader flag
CHAN 4 active channel
POS 32 number of characters input
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
By default, the paper tape punch is assigned to channel W.
2.7 Line Printer (LPT)
The line printer (LPT) writes data to a disk file. The POS register
specifies the number of the next data item to be written. Thus,
by changing POS, the user can backspace or advance the printer.
The line printer implements these registers:
name size comments
BUF[0:131] 8 data buffer
BPTR 8 buffer pointer
XFR 1 transfer ready flag
ERR 1 error flag
CHAN 4 active channel
CCT[0:131] 8 carriage control tape
CCTP 8 pointer into carriage control tape
CCTL 8 length of carriage control tape
SPCINST 24 spacing instruction
POS 32 number of characters input
CTIME 24 intercharacter time
PTIME 24 print time
STIME 24 space time
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of paper
OS I/O error x report error and stop
By default, the line printer is assigned to channel W.
2.8 Real Time Clock (RTC)
The real time clock (RTC) implements these registers:
name size comments
PIE 1 interrupt enable
TIME 24 tick interval
TPS 8 ticks per second
The real-time clock autocalibrates; the clock interval is adjusted up or
down so that the clock tracks actual elapsed time.
2.9 Terminal Multiplexor (MUX)
The terminal multiplexor provides 32 asynchronous interfaces. In Genie
mode, the interfaces are hard-wired; in SDS mode, they implement modem
control. The multiplexor has two controllers: MUX for the scanner, and
MUXL for the individual lines. The terminal multiplexor performs input
and output through Telnet sessions connected to a user-specified port.
The ATTACH command specifies the port to be used:
ATTACH MUX <port> set up listening port
where port is a decimal number between 1 and 65535 that is not being used
for other TCP/IP activities.
Each line (each unit of MUXL) supports one option: UC, when set, causes
lower case input characters to be automatically converted to upper case.
Once MUX is attached and the simulator is running, the multiplexor listens
for connections on the specified port. It assumes that the incoming
connections are Telnet connections. The connections remain open until
disconnected either by the Telnet client, a SET MUX DISCONNECT command,
or a DETACH MUX command.
The SHOW MUX CONNECTIONS command displays the current connections to the
extra terminals. The SHOW MUX STATISTICS command displays statistics for
active connections. The SET MUX DISCONNECT=linenumber disconnects the
specified line.
The controller (MUX) implements these registers:
name size comments
STA[0:31] 6 status, lines 0 to 31
RBUF[0:31] 8 receive buffer, lines 0 to 31
XBUF[0:31] 8 transmit buffer, lines 0 to 31
FLAGS[0:127] 1 line flags, 0 to 3 for line 0,
4 to 7 for line 1, etc
SCAN 7 scanner current flag number
SLCK 1 scanner locked flag
TPS 8 character polls per second
The lines (MUXL) implements these registers:
name size comments
TIME[0:31] 24 transmit time, lines 0 to 31
The additional terminals do not support save and restore. All open
connections are lost when the simulator shuts down or MUX is detached.
2.10 Project Genie Drum (DRM)
The Project Genie drum (DRM) implements these registers:
name size comments
DA 19 drum address
CA 16 core address
WC 14 word count
PAR 12 cumulative sector parity
RW 1 read/write flag
ERR 1 error flag
STA 2 drum state
FTIME 24 channel program fetch time
XTIME 24 interword transfer time
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 drum not ready
Drum data files are buffered in memory; therefore, end of file and OS
I/O errors cannot occur. Unlike conventional SDS 940 devices, the Project
Genie drum does not use a channel.
2.11 Rapid Access (fixed head) Disk (RAD)
The rapid access disk (RAD) implements these registers:
name size comments
DA 15 disk address
SA 6 sector word address
BP 1 sector byte pointer
XFR 1 data transfer flag
NOBD 1 inhibit increment across track
ERR 1 error flag
CHAN 4 active channel
PROT 8 write protect switches
TIME 24 interval between halfword transfers
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
By default, the rapid access disk is assigned to channel E.
2.12 Moving Head Disk (DSK)
DSK options include the ability to make the drive write enabled or write
locked:
SET RAD LOCKED set write locked
SET RAD WRITEENABLED set write enabled
The moving head disk implements these registers:
name size comments
BUF[0:63] 8 transfer buffer
BPTR 9 buffer pointer
BLNT 9 buffer length
DA 21 disk address
INST 24 disk instruction
XFR 1 data transfer flag
ERR 1 error flag
CHAN 4 active channel
WTIME 24 interval between character transfers
STIME 24 seek interval
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
Rapid access disk data files are buffered in memory; therefore, end of file
and OS I/O errors cannot occur. By default, the rapid access disk is
assigned to channel F.
2.13 Magnetic Tape (MT)
MT options include the ability to make units write enabled or write locked.
SET MTn LOCKED set unit n write locked
SET MTn WRITEENABLED set unit n write enabled
Units can also be set ONLINE or OFFLINE.
The magnetic tape implements these registers:
name size comments
BUF[0:131071] 8 transfer buffer
BPTR 18 buffer pointer
BLNT 18 buffer length
XFR 1 data transfer flag
CHAN 4 active channel
INST 24 magtape instruction
EOF 1 end-of-file flag
GAP 1 inter-record gap flag
SKIP 1 skip data flag
CTIME 24 interval between character transfers
GTIME 24 gap interval
POS[0:7] 32 position, drives 0:7
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error processed as
not attached tape not ready; if STOP_IOE, stop
end of file end of tape
OS I/O error end of tape; if STOP_IOE, stop
By default, the magnetic tape is assigned to channel W.
2.13 Symbolic Display and Input
The SDS 940 simulator implements symbolic display and input. Display is
controlled by command line switches:
-a display as ASCII character
-c display as four character SDS string
-m display instruction mnemonics
Input parsing is controlled by the first character typed in or by command
line switches:
' or -a ASCII character
" or -c four character SDS string
alphabetic instruction mnemonic
numeric octal number
Instruction input uses (more or less) standard SDS 940 assembler syntax.
There are eight instruction classes:
class operands examples comments
no operand none EIR
POP (prog op) op,addr{,tag} POP 66,100
I/O addr{,tag} EOM 1266
mem reference addr{,tag} LDA 400,2
STA* 300 indirect addr
reg change op op op... CLA CLB opcodes OR
shift cnt{,tag} LSH 10
chan command chan ALC W
chan test chan CAT Y
All numbers are octal. Channel designators can be alphabetic (W, Y, C, D, E,
F, G, H) or numeric (0-7). Tags must be 0-7, with 2 indicating indexing.

270
SDS/sds_drm.c Normal file
View File

@@ -0,0 +1,270 @@
/* sds_drm.c: SDS 940 Project Genie drum simulator
Copyright (c) 2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
drm drum
The drum is buffered in memory.
Note: the Project Genie documentation and the actual monitor sources disagree
on the I/O instruction definitions for the drum. The simulator follows the
monitor sources, as follows:
DCC OP 00230404B RESET DRUM CHANNEL
DSC OP 00230204B START DRUM CHANNEL (NO CHAIN)
DRA OP 00230504B READ DRUM TIMING COUNTER INTO 21B
DSR OP 04030204B SKIP IF DRUM NOT BUSY
DSE OP 04037404B SKIP IF NO DRUM ERROR
*/
#include "sds_defs.h"
#include <math.h>
/* Constants */
#define DRM_N_WD 11 /* word addr width */
#define DRM_V_WD 0 /* position */
#define DRM_M_WD ((1 << DRM_N_WD) - 1) /* word mask */
#define DRM_NUMWD (1 << DRM_N_WD) /* words/sector */
#define DRM_NUMGP 236 /* gap/sector */
#define DRM_PHYWD (DRM_NUMWD + DRM_NUMGP) /* phys wds/sector */
#define DRM_N_SC 3 /* sect addr width */
#define DRM_V_SC (DRM_N_WD) /* position */
#define DRM_M_SC ((1 << DRM_N_SC) - 1) /* sector mask */
#define DRM_NUMSC (1 << DRM_N_SC) /* sectors/track */
#define DRM_N_TR 7 /* track addr width */
#define DRM_V_TR (DRM_N_WD+DRM_N_SC) /* position */
#define DRM_M_TR ((1 << DRM_N_TR) - 1) /* track mask */
#define DRM_NUMTR 84 /* tracks/drum */
#define DRM_N_ADDR (DRM_N_WD+DRM_N_SC+DRM_N_TR) /* drum addr width */
#define DRM_SWMASK ((1 << (DRM_N_WD+DRM_N_SC)) - 1)/* sector+word mask */
#define DRM_DAMASK ((1 << DRM_N_ADDR) - 1) /* drum addr mask */
#define DRM_SIZE (DRM_NUMTR*DRM_NUMSC*DRM_NUMWD) /* words/disk */
#define DRM_WCMASK 037777 /* wc mask */
#define DRM_GETSC(x) (((x) >> DRM_V_SC) & DRM_M_SC)
#define DRM_PC 020
#define DRM_AD 021
#define DRM_ADAT (1 << (DRM_N_WD + DRM_N_SC)) /* data flag */
#define DRM_SFET 0 /* fetch state */
#define DRM_SFCA 1 /* fetch CA */
#define DRM_SFDA 2 /* fetch DA */
#define DRM_SXFR 3 /* xfer */
#define DRM_V_OP 21 /* drum op */
#define DRM_M_OP 07
#define DRM_V_RW 20
#define DRM_GETOP(x) (((x) >> DRM_V_OP) & DRM_M_OP)
#define DRM_GETRW(x) (((x) >> DRM_V_RW) & 1)
#define DRM_OXF 0 /* xfer */
#define DRM_OCX 1 /* cond xfer */
#define DRM_OBR 2 /* branch */
#define DRM_ORS 3 /* reset error */
#define DRM_END 4 /* end prog */
#define DRM_EIE 5 /* end int if err */
#define DRM_EIU 7 /* end int uncond */
#define GET_TWORD(x) ((int32) fmod (sim_gtime() / ((double) (x)), \
((double) (DRM_NUMSC * DRM_PHYWD))))
extern uint32 M[]; /* memory */
extern uint32 alert, int_req;
extern int32 stop_invins, stop_invdev, stop_inviop;
uint32 drm_da = 0; /* disk address */
uint32 drm_ca = 0; /* core address */
uint32 drm_wc = 0; /* word count */
int32 drm_par = 0; /* cumulative par */
int32 drm_err = 0; /* error */
int32 drm_rw = 0; /* read/write */
int32 drm_sta = 0; /* drum state */
int32 drm_ftime = 3; /* time to fetch */
int32 drm_xtime = 1; /* time to xfr */
int32 drm_stopioe = 1; /* stop on error */
DEVICE drm_dev;
t_stat drm (uint32 fnc, uint32 inst, uint32 *dat);
t_stat drm_svc (UNIT *uptr);
t_stat drm_reset (DEVICE *dptr);
/* DRM data structures
drm_dev device descriptor
drm_unit unit descriptor
drm_reg register list
*/
DIB drm_dib = { -1, DEV3_GDRM, 0, NULL, &drm };
UNIT drm_unit =
{ UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
DRM_SIZE) };
REG drm_reg[] = {
{ ORDATA (DA, drm_da, DRM_N_ADDR) },
{ ORDATA (CA, drm_ca, 16) },
{ ORDATA (WC, drm_wc, 14) },
{ ORDATA (PAR, drm_par, 12) },
{ FLDATA (RW, drm_rw, 0) },
{ FLDATA (ERR, drm_err, 0) },
{ ORDATA (STA, drm_sta, 2) },
{ DRDATA (FTIME, drm_ftime, 24), REG_NZ + PV_LEFT },
{ DRDATA (XTIME, drm_xtime, 24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, drm_stopioe, 0) },
{ NULL } };
DEVICE drm_dev = {
"DRM", &drm_unit, drm_reg, NULL,
1, 8, DRM_N_ADDR, 1, 8, 24,
NULL, NULL, &drm_reset,
NULL, NULL, NULL,
&drm_dib, DEV_DISABLE | DEV_DIS };
/* Drum routine - EOM/SKS 3xx04 */
t_stat drm (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 t, op = inst & 07700;
switch (fnc) {
case IO_CONN: /* connect */
if (op == 00400) return drm_reset (&drm_dev); /* EOM 404 = reset */
if (op == 00500) { /* EOM 504 = read DA */
if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */
t = GET_TWORD (drm_xtime); /* get position */
if (t < DRM_NUMGP) M[DRM_AD] = DRM_NUMWD - t; /* in gap? */
else M[DRM_AD] = (t - DRM_NUMGP) | DRM_ADAT; } /* in data */
else if (op == 00200) { /* EOM 204 = start */
if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */
drm_sta = DRM_SFET; /* state = fetch */
sim_activate (&drm_unit, drm_ftime); } /* activate */
else CRETINS;
break;
case IO_SKS: /* SKS */
if (((op == 07400) && !drm_err) || /* 37404: no err */
((op == 00200) && !sim_is_active (&drm_unit))) /* 30204: idle */
*dat = 1;
break;
default:
return SCPE_IERR; } /* can't get here */
return SCPE_OK;
}
/* Unit service */
t_stat drm_svc (UNIT *uptr)
{
int32 t, rda;
uint32 dpc, dwd;
if (drm_sta != DRM_SXFR) { /* fetch drum prog? */
dpc = M[DRM_PC]; /* get drum PC */
dwd = M[dpc & PAMASK]; /* get drum inst */
M[DRM_PC] = (dpc + 1) & PAMASK; /* update drum PC */
if (drm_sta == DRM_SFCA) { /* fetch core addr? */
drm_rw = DRM_GETRW (dwd); /* set op */
drm_ca = dwd & PAMASK; /* set core addr */
drm_sta = DRM_SFDA; } /* next is disk addr */
else if (drm_sta == DRM_SFDA) { /* fetch disk addr? */
drm_da = dwd & DRM_DAMASK; /* set disk addr */
drm_sta = DRM_SXFR; /* next is xfer */
drm_par = 0; /* init parity */
rda = (drm_da & DRM_SWMASK) + (DRM_GETSC (drm_da) * DRM_NUMGP);
t = rda - GET_TWORD (drm_xtime); /* difference */
if (t <= 0) t = t + (DRM_NUMSC * DRM_PHYWD); /* add trk lnt */
sim_activate (&drm_unit, t * drm_xtime); } /* activate */
else {
switch (DRM_GETOP (dwd)) {
case DRM_OCX: /* cond xfr */
if (drm_err) { /* error? */
int_req = int_req | INT_DRM; /* req int */
return SCPE_OK; } /* done */
case DRM_OXF: /* transfer */
drm_wc = dwd & DRM_WCMASK; /* save wc */
drm_sta = DRM_SFCA; /* next state */
break;
case DRM_OBR: /* branch */
M[DRM_PC] = dwd & PAMASK; /* new drum PC */
break;
case DRM_END: /* end */
return SCPE_OK;
case DRM_EIE: /* end, int if err */
if (!drm_err) return SCPE_OK;
case DRM_EIU: /* end, int uncond */
int_req = int_req | INT_DRM;
return SCPE_OK; } /* end switch */
} /* end else sta */
sim_activate (uptr, drm_ftime); /* fetch next word */
} /* end if !xfr */
else { /* transfer word */
if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */
drm_err = 1; /* error */
CRETIOE (drm_stopioe, SCPE_UNATT); }
if (drm_rw) { /* write? */
dwd = M[drm_ca]; /* get mem word */
*(((uint32 *) uptr->filebuf) + drm_da) = dwd; /* write to drum */
if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; }
else { /* read */
dwd = *(((uint32 *) uptr->filebuf) + drm_da); /* get drum word */
M[drm_ca] = dwd; } /* write to mem */
drm_da = drm_da + 1; /* inc drum addr */
if (drm_da >= DRM_SIZE) drm_da = 0; /* wrap */
drm_ca = (drm_ca + 1) & PAMASK; /* inc core addr */
drm_wc = (drm_wc - 1) & DRM_WCMASK; /* dec word cnt */
drm_par = drm_par ^ (dwd >> 12); /* parity */
drm_par = ((drm_par << 1) | (drm_par >> 11)) & 07777;
drm_par = drm_par ^ (dwd & 07777);
if (drm_wc) { /* more to do */
if (drm_da & DRM_M_WD) sim_activate (uptr, drm_xtime);
else sim_activate (uptr, drm_xtime * DRM_NUMGP); }
else { /* end xfr */
#if defined (DRM_PAR)
if ((drm_da & DRM_M_WD) && drm_rw) { /* wr end mid sector? */
*(((uint32 *) uptr->filebuf) + drm_da) = drm_par << 12;
if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; }
#endif
drm_sta = DRM_SFET; /* back to fetch */
sim_activate (uptr, drm_ftime); /* schedule */
} /* end else end xfr */
} /* end else xfr */
return SCPE_OK;
}
/* Reset routine */
t_stat drm_reset (DEVICE *dptr)
{
drm_da = 0; /* clear state */
drm_ca = 0;
drm_wc = 0;
drm_par = 0;
drm_sta = 0;
drm_err = 0;
drm_rw = 0;
int_req = int_req & ~INT_DRM; /* clear intr */
sim_cancel (&drm_unit); /* deactivate */
return SCPE_OK;
}

357
SDS/sds_dsk.c Normal file
View File

@@ -0,0 +1,357 @@
/* sds_dsk.c: SDS 940 moving head disk simulator
Copyright (c) 2001-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
dsk moving head disk
The SDS 9164 disk has a subsector feature, allowing each 64W sector to be
viewed as 16W packets. In addition, it has a chaining feature, allowing
records to be extended beyond a sector boundary. To accomodate this, the
first word of each sector has 3 extra bits:
<26> = end of chain flag
<25:24> = 4 - number of packets
These values were chosen so that 000 = continue chain, full sector.
*/
#include "sds_defs.h"
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
#define DSK_PKTWD 16 /* words/packet */
#define DSK_NUMPKT 4 /* packets/sector */
#define DSK_NUMWD (DSK_PKTWD*DSK_NUMPKT) /* words/sector */
#define DSK_N_SC 5 /* sect addr width */
#define DSK_V_SC 0 /* position */
#define DSK_M_SC ((1 << DSK_N_SC) - 1) /* mask */
#define DSK_NUMSC (1 << DSK_N_SC) /* sectors/track */
#define DSK_N_TR 8 /* track addr width */
#define DSK_V_TR (DSK_N_SC) /* position */
#define DSK_M_TR ((1 << DSK_N_TR) - 1) /* mask */
#define DSK_NUMTR (1 << DSK_N_TR) /* tracks/surface */
#define DSK_N_SF 5 /* surf addr width */
#define DSK_V_SF (DSK_N_SC + DSK_N_TR) /* position */
#define DSK_M_SF ((1 << DSK_N_SF) - 1) /* mask */
#define DSK_NUMSF (1 << DSK_N_SF) /* surfaces/drive */
#define DSK_SCSIZE (DSK_NUMSF*DSK_NUMTR*DSK_NUMSC) /* sectors/drive */
#define DSK_AMASK (DSK_SCSIZE - 1) /* address mask */
#define DSK_SIZE (DSK_SCSIZE * DSK_NUMWD) /* words/drive */
#define DSK_GETTR(x) (((x) >> DSK_V_TR) & DSK_M_TR)
#define cyl u3 /* curr cylinder */
#define DSK_SIP (1 << (DSK_N_TR + 2))
#define DSK_V_PKT 24
#define DSK_M_PKT 03
#define DSK_V_CHN 26
#define DSK_GETPKT(x) (4 - (((x) >> DSK_V_PKT) & DSK_M_PKT))
#define DSK_ENDCHN(x) ((x) & (1 << DSK_V_CHN))
extern uint32 xfr_req;
extern uint32 alert;
extern int32 stop_invins, stop_invdev, stop_inviop;
int32 dsk_da = 0; /* disk addr */
int32 dsk_op = 0; /* operation */
int32 dsk_err = 0; /* error flag */
uint32 dsk_buf[DSK_NUMWD]; /* sector buf */
int32 dsk_bptr = 0; /* byte ptr */
int32 dsk_blnt = 0; /* byte lnt */
int32 dsk_time = 5; /* time per char */
int32 dsk_stime = 200; /* seek time */
int32 dsk_stopioe = 1;
DSPT dsk_tplt[] = { /* template */
{ 1, 0 }, { 1, DEV_OUT }, { 0, 0 } };
DEVICE dsk_dev;
t_stat dsk_svc (UNIT *uptr);
t_stat dsk_reset (DEVICE *dptr);
t_stat dsk_fill (uint32 dev);
t_stat dsk_read_buf (uint32 dev);
t_stat dsk_write_buf (uint32 dev);
void dsk_end_op (uint32 fl);
t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat);
/* DSK data structures
dsk_dev device descriptor
dsk_unit unit descriptor
dsk_reg register list
*/
DIB dsk_dib = { CHAN_F, DEV_DSK, XFR_DSK, dsk_tplt, &dsk };
UNIT dsk_unit =
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE, DSK_SIZE) };
REG dsk_reg[] = {
{ BRDATA (BUF, dsk_buf, 8, 24, DSK_NUMWD) },
{ DRDATA (BPTR, dsk_bptr, 9), PV_LEFT },
{ DRDATA (BLNT, dsk_bptr, 9), PV_LEFT },
{ ORDATA (DA, dsk_da, 21) },
{ ORDATA (INST, dsk_op, 24) },
{ FLDATA (XFR, xfr_req, XFR_V_DSK) },
{ FLDATA (ERR, dsk_err, 0) },
{ DRDATA (WTIME, dsk_time, 24), REG_NZ + PV_LEFT },
{ DRDATA (STIME, dsk_stime,24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, dsk_stopioe, 0) },
{ NULL } };
MTAB dsk_mod[] = {
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, NULL },
{ 0 } };
DEVICE dsk_dev = {
"DSK", &dsk_unit, dsk_reg, dsk_mod,
1, 8, 24, 1, 8, 27,
NULL, NULL, &dsk_reset,
NULL, NULL, NULL,
&dsk_dib, DEV_DISABLE };
/* Moving head disk routine
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
*/
t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 i, t, new_ch, dsk_wptr, dsk_byte;
t_stat r;
switch (fnc) { /* case on function */
case IO_CONN: /* connect */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */
dsk_op = inst; /* save instr */
dsk_bptr = dsk_blnt = 0; /* init ptrs */
for (i = 0; i < DSK_NUMWD; i++) dsk_buf[i] = 0; /* clear buffer */
xfr_req = xfr_req & ~XFR_DSK; /* clr xfr flg */
sim_activate (&dsk_unit, dsk_stime); /* activate */
break;
case IO_EOM1: /* EOM mode 1 */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */
if (inst & 07600) CRETIOP; /* inv inst? */
alert = POT_DSK; /* alert */
break;
case IO_DISC: /* disconnect */
dsk_end_op (0); /* normal term */
if (inst & DEV_OUT) return dsk_fill (inst); /* fill write */
break;
case IO_WREOR: /* write eor */
dsk_end_op (CHF_EOR); /* eor term */
return dsk_fill (inst); /* fill write */
case IO_SKS: /* SKS */
new_ch = I_GETSKCH (inst); /* sks chan */
if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */
t = I_GETSKCND (inst); /* sks cond */
if (((t == 000) && !sim_is_active (&dsk_unit) && /* 10026: ready */
(dsk_unit.flags & UNIT_ATT)) ||
((t == 004) && !dsk_err && /* 11026: !err */
(dsk_unit.flags & UNIT_ATT)) ||
((t == 010) && ((dsk_unit.cyl & DSK_SIP) == 0)) || /* 12026: on trk */
((t == 014) && !(dsk_unit.flags & UNIT_WPRT)) ||/* 13026: !wrprot */
((t == 001) && (dsk_unit.flags & UNIT_ATT))) /* 10226: online */
*dat = 1;
break;
case IO_READ:
xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */
if (dsk_bptr >= dsk_blnt) { /* no more data? */
if (r = dsk_read_buf (inst)) return r; } /* read sector */
dsk_wptr = dsk_bptr >> 2; /* word pointer */
dsk_byte = dsk_bptr & 03; /* byte in word */
*dat = (dsk_buf[dsk_wptr] >> ((3 - dsk_byte) * 6)) & 077;
dsk_bptr = dsk_bptr + 1; /* incr buf ptr */
if ((dsk_bptr >= dsk_blnt) && /* end sector, */
((dsk_op & CHC_BIN) || DSK_ENDCHN (dsk_buf[0])))/* sec mode | eoch? */
dsk_end_op (CHF_EOR); /* eor term */
break;
case IO_WRITE:
xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */
if (dsk_bptr >= (DSK_NUMWD * 4)) { /* full? */
if (r = dsk_write_buf (inst)) return r; } /* write sector */
dsk_wptr = dsk_bptr >> 2; /* word pointer */
dsk_buf[dsk_wptr] = ((dsk_buf[dsk_wptr] << 6) | (*dat & 077)) & DMASK;
dsk_bptr = dsk_bptr + 1; /* incr buf ptr */
break;
default:
CRETINS; }
return SCPE_OK;
}
/* PIN routine - return disk address */
t_stat pin_dsk (uint32 num, uint32 *dat)
{
*dat = dsk_da; /* ret disk addr */
return SCPE_OK;
}
/* POT routine - start seek */
t_stat pot_dsk (uint32 num, uint32 *dat)
{
int32 st;
if (sim_is_active (&dsk_unit)) return STOP_IONRDY; /* busy? wait */
dsk_da = (*dat) & DSK_AMASK; /* save dsk addr */
st = abs (DSK_GETTR (dsk_da) - /* calc seek time */
(dsk_unit.cyl & DSK_M_TR)) * dsk_stime;
if (st == 0) st = dsk_stime; /* min time */
sim_activate (&dsk_unit, st); /* set timer */
dsk_unit.cyl = dsk_unit.cyl | DSK_SIP; /* seeking */
return SCPE_OK;
}
/* Unit service and read/write */
t_stat dsk_svc (UNIT *uptr)
{
if (uptr->cyl & DSK_SIP) { /* end seek? */
uptr->cyl = DSK_GETTR (dsk_da); /* on cylinder */
if (dsk_op) sim_activate (&dsk_unit, dsk_stime); } /* sched r/w */
else {
xfr_req = xfr_req | XFR_DSK; /* set xfr req */
sim_activate (&dsk_unit, dsk_time); } /* activate */
return SCPE_OK;
}
/* Read sector */
t_stat dsk_read_buf (uint32 dev)
{
int32 da, pkts, awc;
if ((dsk_unit.flags & UNIT_ATT) == 0) { /* !attached? */
dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */
CRETIOE (dsk_stopioe, SCPE_UNATT); }
da = dsk_da * DSK_NUMWD * sizeof (uint32);
fseek (dsk_unit.fileref, da, SEEK_SET); /* locate sector */
awc = fxread (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref);
if (ferror (dsk_unit.fileref)) { /* error? */
dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */
return SCPE_IOERR; }
for ( ; awc < DSK_NUMWD; awc++) dsk_buf[awc] = 0;
pkts = DSK_GETPKT (dsk_buf[0]); /* get packets */
dsk_blnt = pkts * DSK_PKTWD * 4; /* new buf size */
dsk_bptr = 0; /* init bptr */
dsk_da = (dsk_da + 1) & DSK_AMASK; /* incr disk addr */
return SCPE_OK;
}
/* Write sector. If this routine is called directly, then the sector
buffer is full, and there is at least one more character to write;
therefore, there are 4 packets in the sector, and the sector is not
the end of the chain.
*/
t_stat dsk_write_buf (uint32 dev)
{
int32 i, da;
if ((dsk_unit.flags & UNIT_ATT) == 0) { /* !attached? */
dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */
CRETIOE (dsk_stopioe, SCPE_UNATT); }
if (dsk_unit.flags & UNIT_WPRT) { /* write prot? */
dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */
return SCPE_OK; }
da = dsk_da * DSK_NUMWD * sizeof (uint32);
fseek (dsk_unit.fileref, da, SEEK_SET); /* locate sector */
fxwrite (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref);
if (ferror (dsk_unit.fileref)) { /* error? */
dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */
return SCPE_IOERR; }
dsk_bptr = 0; /* init bptr */
dsk_da = (dsk_da + 1) & DSK_AMASK; /* incr disk addr */
for (i = 0; i < DSK_NUMWD; i++) dsk_buf[i] = 0; /* clear buffer */
return SCPE_OK;
}
/* Fill incomplete sector at end of operation. Calculate the number
of packets and set the end of chain flag.
*/
t_stat dsk_fill (uint32 dev)
{
int32 nochn = (dsk_op & CHC_BIN)? 0: 1; /* chain? */
int32 pktend = (dsk_bptr + ((DSK_PKTWD * 4) - 1)) & /* end pkt */
~((DSK_PKTWD * 4) - 1);
int32 pkts = pktend / (DSK_PKTWD * 4); /* # packets */
if (dsk_bptr == 0) return SCPE_OK; /* no fill? */
for ( ; dsk_bptr < pktend; dsk_bptr++) { /* fill packet */
int32 dsk_wptr = dsk_bptr >> 2;
dsk_buf[dsk_wptr] = (dsk_buf[dsk_wptr] << 6) & DMASK; }
dsk_buf[0] = dsk_buf[0] | (nochn << DSK_V_CHN) | /* insert chain, */
((4 - pkts) << DSK_V_PKT); /* num pkts */
return dsk_write_buf (dev); /* write sec */
}
/* Terminate DSK operation */
void dsk_end_op (uint32 fl)
{
if (fl) chan_set_flag (dsk_dib.chan, fl); /* set flags */
dsk_op = 0; /* clear op */
xfr_req = xfr_req & ~XFR_DSK; /* clear xfr */
sim_cancel (&dsk_unit); /* stop */
if (fl & CHF_ERR) { /* error? */
chan_disc (dsk_dib.chan); /* disconnect */
dsk_err = 1; } /* set disk err */
return;
}
/* Reset routine */
t_stat dsk_reset (DEVICE *dptr)
{
int32 i;
chan_disc (dsk_dib.chan); /* disconnect */
dsk_da = 0; /* clear state */
dsk_op = 0;
dsk_err = 0;
dsk_bptr = dsk_blnt = 0;
xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */
sim_cancel (&dsk_unit); /* deactivate */
dsk_unit.cyl = 0;
for (i = 0; i < DSK_NUMWD; i++) dsk_buf[i] = 0; /* clear buffer */
return SCPE_OK;
}

886
SDS/sds_io.c Normal file
View File

@@ -0,0 +1,886 @@
/* sds_io.c: SDS 940 I/O simulator
Copyright (c) 2001-2002, 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 "sds_defs.h"
/* Data chain word */
#define CHD_INT 040 /* int on chain */
#define CHD_PAGE 037 /* new page # */
/* Interlace POT */
#define CHI_V_WC 14 /* word count */
#define CHI_M_WC 01777
#define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC)
#define CHI_V_MA 0 /* mem address */
#define CHI_M_MA 037777
#define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA)
/* System interrupt POT */
#define SYI_V_GRP 18 /* group */
#define SYI_M_GRP 077
#define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP)
#define SYI_DIS (1 << 17) /* disarm if 0 */
#define SYI_ARM (1 << 16) /* arm if 1 */
#define SYI_M_INT 0177777 /* interrupt */
/* Pseudo-device number for EOM/SKS mode 3 */
#define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK)
#define TST_XFR(d,c) (xfr_req && dev_map[d][c])
#define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c]
#define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c]
#define INV_DEV(d,c) (dev_dsp[d][c] == NULL)
#define VLD_DEV(d,c) (dev_dsp[d][c] != NULL)
#define TST_EOR(c) (chan_flag[c] & CHF_EOR)
#define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN)))
uint8 chan_uar[NUM_CHAN]; /* unit addr */
uint16 chan_wcr[NUM_CHAN]; /* word count */
uint16 chan_mar[NUM_CHAN]; /* mem addr */
uint8 chan_dcr[NUM_CHAN]; /* data chain */
uint32 chan_war[NUM_CHAN]; /* word assembly */
uint8 chan_cpw[NUM_CHAN]; /* char per word */
uint8 chan_cnt[NUM_CHAN]; /* char count */
uint16 chan_mode[NUM_CHAN]; /* mode */
uint16 chan_flag[NUM_CHAN]; /* flags */
static const char *chname[NUM_CHAN] =
{ "W", "Y", "C", "D", "E", "F", "G", "H" };
extern uint32 M[MAXMEMSIZE]; /* memory */
extern uint32 int_req; /* int req */
extern uint32 xfr_req; /* xfer req */
extern uint32 alert; /* pin/pot alert */
extern uint32 X, EM2, EM3, OV, ion, bpt;
extern uint32 nml_mode, usr_mode, rtc_pie;
extern int32 stop_invins, stop_invdev, stop_inviop;
extern int32 mon_usr_trap;
extern UNIT cpu_unit;
extern FILE *sim_log;
extern DEVICE *sim_devices[];
t_stat chan_reset (DEVICE *dptr);
t_stat chan_read (int32 ch);
t_stat chan_write (int32 ch);
void chan_write_mem (int32 ch);
void chan_flush_war (int32 ch);
uint32 chan_mar_inc (int32 ch);
t_stat chan_eor (int32 ch);
t_stat pot_ilc (uint32 num, uint32 *dat);
t_stat pot_dcr (uint32 num, uint32 *dat);
t_stat pin_adr (uint32 num, uint32 *dat);
t_stat pot_fork (uint32 num, uint32 *dat);
t_stat dev_disc (uint32 ch, uint32 dev);
t_stat dev_wreor (uint32 ch, uint32 dev);
extern t_stat pot_RL1 (uint32 num, uint32 *dat);
extern t_stat pot_RL2 (uint32 num, uint32 *dat);
extern t_stat pot_RL4 (uint32 num, uint32 *dat);
extern t_stat pin_rads (uint32 num, uint32 *dat);
extern t_stat pot_rada (uint32 num, uint32 *dat);
extern t_stat pin_dsk (uint32 num, uint32 *dat);
extern t_stat pot_dsk (uint32 num, uint32 *dat);
t_stat pin_mux (uint32 num, uint32 *dat);
t_stat pot_mux (uint32 num, uint32 *dat);
extern void set_dyn_map (void);
/* SDS I/O model
A device is modeled by its interactions with a channel. Devices can only be
accessed via channels. Each channel has its own device address space. This
means devices can only be accessed from a specific channel.
I/O operations start with a channel connect. The EOM instruction is passed
to the device via the conn routine. This routine is also used for non-channel
EOM's to the device. For channel connects, the device must remember the
channel number.
The device responds (after a delay) by setting its XFR_RDY flag. This causes
the channel to invoke either the read or write routine (for input or output)
to get or put the next character. If the device is an asynchronous output
device, it calls routine chan_set_ordy to see if there is output available.
If there is, XFR_RDY is set; if not, the channel is marked to wake the
attached device when output is available. This prevents invalid rate errors.
Output may be terminated by a write end of record, a disconnect, or both.
Write end of record occurs when the word count reaches zero on an IORD or IORP
operation. It also occurs if a TOP instruction is issued. The device is
expected to respond by setting the end of record indicator in the channel,
which will in turn trigger an end of record interrupt.
When the channel operation completes, the channel disconnects and calls the
disconnect processor to perform any device specific cleanup. The differences
between write end of record and disconnect are subtle. On magtape output,
for example, both signal end of record; but write end of record allows the
magtape to continue moving, while disconnect halts its motion.
Valid devices supply a routine to handle potentially all I/O operations
(connect, disconnect, read, write, write end of record, sks). There are
separate routines for PIN and POT.
Channels could, optionally, handle 12b or 24b characters. The simulator can
support all widths.
*/
t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc);
struct aldisp {
t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */
t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */
};
/* Channel data structures
chan_dev channel device descriptor
chan_unit channel unit descriptor
chan_reg channel register list
*/
UNIT chan_unit = { UDATA (NULL, 0, 0) };
REG chan_reg[] = {
{ BRDATA (UAR, chan_uar, 8, 6, NUM_CHAN) },
{ BRDATA (WCR, chan_wcr, 8, 15, NUM_CHAN) },
{ BRDATA (MAR, chan_mar, 8, 16, NUM_CHAN) },
{ BRDATA (DCR, chan_dcr, 8, 6, NUM_CHAN) },
{ BRDATA (WAR, chan_war, 8, 24, NUM_CHAN) },
{ BRDATA (CPW, chan_cpw, 8, 2, NUM_CHAN) },
{ BRDATA (CNT, chan_cnt, 8, 3, NUM_CHAN) },
{ BRDATA (MODE, chan_mode, 8, 12, NUM_CHAN) },
{ BRDATA (FLAG, chan_flag, 8, CHF_N_FLG, NUM_CHAN) },
{ NULL } };
MTAB chan_mod[] = {
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_W, "W", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_Y, "Y", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_C, "C", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_D, "D", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_E, "E", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_F, "F", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_G, "G", NULL,
NULL, &chan_show_reg, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_H, "H", NULL,
NULL, &chan_show_reg, NULL } };
DEVICE chan_dev = {
"CHAN", &chan_unit, chan_reg, chan_mod,
1, 8, 8, 1, 8, 8,
NULL, NULL, &chan_reset,
NULL, NULL, NULL };
/* Tables */
static const uint32 int_zc[8] = {
INT_WZWC, INT_YZWC, INT_CZWC, INT_DZWC,
INT_EZWC, INT_FZWC, INT_GZWC, INT_HZWC };
static const uint32 int_er[8] = {
INT_WEOR, INT_YEOR, INT_CEOR, INT_DEOR,
INT_EEOR, INT_FEOR, INT_GEOR, INT_HEOR };
/* dev_map maps device and channel numbers to a transfer flag masks */
uint32 dev_map[64][NUM_CHAN];
/* dev_dsp maps device and channel numbers to dispatch routines */
t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL };
/* dev_dsp maps system device numbers to dispatch routines */
t_stat (*dev3_dsp[64])() = { NULL };
/* dev_alt maps alert numbers to dispatch routines */
struct aldisp dev_alt[] = {
{ NULL, NULL },
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
{ NULL, &pot_ilc }, { NULL, &pot_ilc },
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
{ NULL, &pot_dcr }, { NULL, &pot_dcr },
{ &pin_adr, NULL }, { &pin_adr, NULL },
{ &pin_adr, NULL }, { &pin_adr, NULL },
{ &pin_adr, NULL }, { &pin_adr, NULL },
{ &pin_adr, NULL }, { &pin_adr, NULL },
{ NULL, &pot_RL1 }, { NULL, &pot_RL2 },
{ NULL, &pot_RL4 },
{ &pin_rads, NULL }, { NULL, &pot_rada },
{ &pin_dsk, &pot_dsk }, { NULL, &pot_fork },
{ &pin_mux, &pot_mux } };
/* Single word I/O instructions */
t_stat op_wyim (uint32 inst, uint32 *dat)
{
int32 ch, dev;
ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */
dev = chan_uar[ch] & DEV_MASK; /* get dev # */
if (chan_cnt[ch] <= chan_cpw[ch]) { /* buffer empty? */
if (dev == 0) return STOP_INVIOP; /* no device? dead */
return STOP_IONRDY; } /* hang until full */
*dat = chan_war[ch]; /* get data */
chan_war[ch] = 0; /* reset war */
chan_cnt[ch] = 0; /* reset cnt */
return SCPE_OK;
}
t_stat op_miwy (uint32 inst, uint32 dat)
{
int32 ch, dev;
ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */
dev = chan_uar[ch] & DEV_MASK; /* get dev # */
if (chan_cnt[ch] != 0) { /* buffer full? */
if (dev == 0) return STOP_INVIOP; /* no device? dead */
return STOP_IONRDY; } /* hang until full */
chan_war[ch] = dat; /* get data */
chan_cnt[ch] = chan_cpw[ch] + 1; /* buffer full */
if (chan_flag[ch] & CHF_OWAK) { /* output wake? */
if (VLD_DEV (dev, ch)) SET_XFR (dev, ch); /* wake channel */
chan_flag[ch] = chan_flag[ch] & ~CHF_OWAK; } /* clear wake */
return SCPE_OK;
}
t_stat op_pin (uint32 *dat)
{
uint32 al = alert; /* local copy */
alert = 0; /* clear alert */
if ((al == 0) || (dev_alt[al].pin == NULL)) CRETIOP; /* inv alert? */
return dev_alt[al].pin (al, dat); /* PIN from dev */
}
t_stat op_pot (uint32 dat)
{
uint32 al = alert; /* local copy */
alert = 0; /* clear alert */
if ((al == 0) || (dev_alt[al].pot == NULL)) CRETIOP; /* inv alert? */
return dev_alt[al].pot (al, &dat); /* POT to dev */
}
/* EOM/EOD */
t_stat op_eomd (uint32 inst)
{
uint32 mod = I_GETIOMD (inst); /* get mode */
uint32 ch = I_GETEOCH (inst); /* get chan # */
uint32 dev = inst & DEV_MASK; /* get dev # */
uint32 ch_dev = chan_uar[ch] & DEV_MASK; /* chan curr dev # */
t_stat r;
switch (mod) {
case 0: /* IO control */
if (dev) { /* new dev? */
if (ch_dev) CRETIOP; /* chan act? err */
if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */
chan_war[ch] = chan_cnt[ch] = 0; /* init chan */
chan_flag[ch] = chan_dcr[ch] = 0;
chan_mode[ch] = chan_uar[ch] = 0;
if (ch > CHAN_E) chan_mode[ch] = CHM_CE;
if (r = dev_dsp[dev][ch] (IO_CONN, inst, NULL)) /* connect */
return r;
if ((inst & I_IND) || (ch >= CHAN_C)) { /* C-H? alert ilc */
alert = POT_ILCY + ch;
chan_mar[ch] = chan_wcr[ch] = 0; }
if (chan_flag[ch] & CHF_24B) chan_cpw[ch] = 0; /* 24B? 1 ch/wd */
else if (chan_flag[ch] & CHF_12B) /* 12B? 2 ch/wd */
chan_cpw[ch] = CHC_GETCPW (inst) & 1;
else chan_cpw[ch] = CHC_GETCPW (inst); /* 6b, 1-4 ch/wd */
chan_uar[ch] = dev; /* connected */
if ((dev & DEV_OUT) && ion && !QAILCE (alert)) /* out, prog IO? */
int_req = int_req | int_zc[ch]; } /* initial intr */
else return dev_disc (ch, ch_dev); /* disconnect */
break;
case 1: /* buf control */
if (QAILCE (alert)) { /* ilce alerted? */
ch = alert - POT_ILCY; /* derive chan */
if (ch >= CHAN_E) inst = inst | CHM_CE; /* DACC? ext */
chan_mode[ch] = inst; /* save mode */
chan_mar[ch] = (CHM_GETHMA (inst) << 14) | /* get hi mar */
(chan_mar[ch] & CHI_M_MA);
chan_wcr[ch] = (CHM_GETHWC (inst) << 10) | /* get hi wc */
(chan_wcr[ch] & CHI_M_WC); }
else if (dev) { /* dev EOM */
if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */
return dev_dsp[dev][ch] (IO_EOM1, inst, NULL); }
else { /* chan EOM */
inst = inst & 047677;
if (inst == 040000) { /* alert ilce */
alert = POT_ILCY + ch;
chan_mar[ch] = chan_wcr[ch] = 0; }
else if (inst == 002000) alert = POT_ADRY + ch; /* alert addr */
else if (inst == 001000) alert = POT_DCRY + ch; /* alert DCR */
else if (inst == 004000) { /* term output */
if (ch_dev & DEV_OUT) { /* to output dev? */
if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* busy, DMA in prog? */
chan_flag[ch] = chan_flag[ch] | CHF_TOP; /* TOP pending */
else return dev_wreor (ch, ch_dev); /* idle, write EOR */
} /* end else TOP */
else if (ch_dev & DEV_MT) { /* change to scan? */
chan_uar[ch] = chan_uar[ch] | DEV_MTS; /* change dev addr */
chan_flag[ch] = chan_flag[ch] | CHF_SCAN; /* set scan flag */
} /* end else change scan */
} /* end else term output */
} /* end else chan EOM */
break;
case 2: /* internal */
if (ch >= CHAN_E) { /* EOD? */
if (inst & 00300) { /* set EM? */
if (inst & 00100) EM2 = inst & 07;
if (inst & 00200) EM3 = (inst >> 3) & 07;
set_dyn_map (); }
break; } /* end if EOD */
if (inst & 00001) OV = 0; /* clr OV */
if (inst & 00002) ion = 1; /* ion */
else if (inst & 00004) ion = 0; /* iof */
if ((inst & 00010) && (((X >> 1) ^ X) & EXPS)) OV = 1;
if (inst & 00020) alert = POT_SYSI; /* alert sys int */
if (inst & 00100) rtc_pie = 1; /* arm clk pls */
else if (inst & 00200) rtc_pie = 0; /* disarm pls */
if ((inst & 01400) == 01400) alert = POT_RL4; /* alert RL4 */
else if (inst & 00400) alert = POT_RL1; /* alert RL1 */
else if (inst & 01000) alert = POT_RL2; /* alert RL2 */
if (inst & 02000) { /* nml to mon */
nml_mode = usr_mode = 0;
if (inst & 00400) mon_usr_trap = 1; }
break;
case 3: /* special */
dev = I_GETDEV3 (inst); /* special device */
if (dev3_dsp[dev]) /* defined? */
return dev3_dsp[dev] (IO_CONN, inst, NULL);
CRETINS; } /* end case */
return SCPE_OK;
}
/* Skip if not signal */
t_stat op_sks (uint32 inst, uint32 *dat)
{
uint32 mod = I_GETIOMD (inst); /* get mode */
uint32 ch = I_GETSKCH (inst); /* get chan # */
uint32 dev = inst & DEV_MASK; /* get dev # */
*dat = 0;
if ((ch == 4) && !(inst & 037774)) { /* EM test */
if (((inst & 0001) && (EM2 != 2)) ||
((inst & 0002) && (EM3 != 3))) *dat = 1;
return SCPE_OK; }
switch (mod) {
case 1: /* ch, dev */
if (dev) { /* device */
if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */
dev_dsp[dev][ch] (IO_SKS, inst, dat); } /* do test */
else { /* channel */
if (((inst & 04000) && (chan_uar[ch] == 0)) ||
((inst & 02000) && (chan_wcr[ch] == 0)) ||
((inst & 01000) && ((chan_flag[ch] & CHF_ERR) == 0)) ||
((inst & 00400) && (chan_flag[ch] & CHF_IREC))) *dat = 1; }
break;
case 2: /* internal test */
if (inst & 0001) { /* test OV */
*dat = OV ^ 1; /* skip if off */
OV = 0; /* and reset */
break; }
if (((inst & 00002) && !ion) || /* ion, bpt test */
((inst & 00004) && ion) ||
((inst & 00010) && ((chan_flag[CHAN_W] & CHF_ERR) == 0)) ||
((inst & 00020) && ((chan_flag[CHAN_Y] & CHF_ERR) == 0)) ||
((inst & 00040) && ((bpt & 001) == 0)) ||
((inst & 00100) && ((bpt & 002) == 0)) ||
((inst & 00200) && ((bpt & 004) == 0)) ||
((inst & 00400) && ((bpt & 010) == 0)) ||
((inst & 01000) && (chan_uar[CHAN_W] == 0)) ||
((inst & 02000) && (chan_uar[CHAN_Y] == 0))) *dat = 1;
break;
case 3: /* special */
dev = I_GETDEV3 (inst); /* special device */
if (dev3_dsp[dev]) dev3_dsp[dev] (IO_SKS, inst, dat);
else CRETINS; } /* end case */
return SCPE_OK;
}
/* PIN/POT routines */
t_stat pot_ilc (uint32 num, uint32 *dat)
{
uint32 ch = num - POT_ILCY;
chan_mar[ch] = (chan_mar[ch] & ~CHI_M_MA) | CHI_GETMA (*dat);
chan_wcr[ch] = (chan_wcr[ch] & ~CHI_M_WC) | CHI_GETWC (*dat);
chan_flag[ch] = chan_flag[ch] | CHF_ILCE;
return SCPE_OK;
}
t_stat pot_dcr (uint32 num, uint32 *dat)
{
uint32 ch = num - POT_DCRY;
chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE);
chan_flag[ch] = chan_flag[ch] | CHF_DCHN;
return SCPE_OK;
}
t_stat pin_adr (uint32 num, uint32 *dat)
{
uint32 ch = num - POT_ADRY;
*dat = chan_mar[ch] & PAMASK;
return SCPE_OK;
}
/* System interrupt POT.
The SDS 940 timesharing system uses a permanently asserted
system interrupt as a way of forking the teletype input
interrupt handler to a lower priority. The interrupt is
armed to set up the fork, and disarmed in the fork routine */
t_stat pot_fork (uint32 num, uint32 *dat)
{
uint32 igrp = SYI_GETGRP (*dat); /* get group */
uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */
if (igrp == (VEC_FORK / 020)) { /* right group? */
if ((*dat & SYI_ARM) && (*dat & fbit)) /* arm, bit set? */
int_req = int_req | INT_FORK;
if ((*dat & SYI_DIS) && !(*dat & fbit)) /* disarm, bit clr? */
int_req = int_req & ~INT_FORK; }
return SCPE_OK;
}
/* Channel read invokes the I/O device to get the next character and,
if not end of record, assembles it into the word assembly register.
If the interlace is on, the full word is stored in memory.
The key difference points for the various terminal functions are
end of record comp: EOT interrupt
IORD, IOSD: EOR interrupt, disconnect
IORP, IOSP: EOR interrupt, interrecord
interlace off: comp: EOW interrupt
IORD, IORP: ignore
IOSD, IOSP: overrun error
--wcr == 0: comp: clear interlace
IORD, IORP, IOSP: ZWC interrupt
IOSD: ZWC interrupt, EOR interrupt, disconnect
Note that the channel can be disconnected if CHN_EOR is set, but must
not be if XFR_REQ is set */
t_stat chan_read (int32 ch)
{
uint32 dat = 0;
uint32 dev = chan_uar[ch] & DEV_MASK;
uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
t_stat r = SCPE_OK;
if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */
if (INV_DEV (dev, ch)) CRETIOP; /* can't read? */
r = dev_dsp[dev][ch] (IO_READ, dev, &dat); /* read data */
if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */
if (chan_flag[ch] & CHF_24B) chan_war[ch] = dat; /* 24B? */
else if (chan_flag[ch] & CHF_12B) /* 12B? */
chan_war[ch] = ((chan_war[ch] << 12) | (dat & 07777)) & DMASK;
else chan_war[ch] = ((chan_war[ch] << 6) | (dat & 077)) & DMASK;
if (chan_flag[ch] & CHF_SCAN) /* scanning? */
chan_cnt[ch] = chan_cpw[ch]; /* never full */
else chan_cnt[ch] = chan_cnt[ch] + 1; /* insert char */
if (chan_cnt[ch] > chan_cpw[ch]) { /* full? */
if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */
chan_write_mem (ch); /* write to mem */
if (chan_wcr[ch] == 0) { /* wc zero? */
chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* clr interlace */
if ((tfnc != CHM_COMP) && (chan_mode[ch] & CHM_ZC))
int_req = int_req | int_zc[ch]; /* zwc interrupt */
if (tfnc == CHM_IOSD) { /* IOSD? also EOR */
if (chan_mode[ch] & CHM_ER) int_req = int_req | int_er[ch];
dev_disc (ch, dev); /* disconnect */
} /* end if IOSD */
} /* end if wcr == 0 */
} /* end if ilce on */
else { /* interlace off */
if (TST_EOR (ch)) return chan_eor (ch); /* eor? */
if (tfnc == CHM_COMP) { /* C: EOW, intr */
if (ion) int_req = int_req | int_zc[ch]; }
else if (tfnc & CHM_SGNL) /* Sx: error */
chan_flag[ch] = chan_flag[ch] | CHF_ERR;
else chan_cnt[ch] = chan_cpw[ch]; /* Rx: ignore */
} /* end else ilce */
} /* end if full */
} /* end if xfr */
if (TST_EOR (ch)) { /* end record? */
if (tfnc == CHM_COMP) chan_flush_war (ch); /* C: fill war */
else if (chan_cnt[ch]) { /* RX, CX: fill? */
chan_flush_war (ch); /* fill war */
if (chan_flag[ch] & CHF_ILCE) /* ilce on? store */
chan_write_mem (ch); } /* end else if cnt */
return chan_eor (ch); } /* eot/eor int */
return r;
}
void chan_write_mem (int32 ch)
{
WriteP (chan_mar[ch], chan_war[ch]); /* write to mem */
chan_mar[ch] = chan_mar_inc (ch); /* incr mar */
chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr wcr */
chan_war[ch] = 0; /* reset war */
chan_cnt[ch] = 0; /* reset cnt */
return;
}
void chan_flush_war (int32 ch)
{
int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1;
if (i) {
if (chan_flag[ch] & CHF_24B) chan_war[ch] = 0;
else if (chan_flag[ch] & CHF_12B)
chan_war[ch] = (chan_war[ch] << 12) & DMASK;
else chan_war[ch] = (chan_war[ch] << (i * 6)) & DMASK;
chan_cnt[ch] = chan_cpw[ch] + 1; }
return;
}
/* Channel write gets the next character and sends it to the I/O device.
If this is the last character in an interlace operation, the end of
record operation is invoked.
The key difference points for the various terminal functions are
end of record: comp: EOT interrupt
IORD, IOSD: EOR interrupt, disconnect
IORP, IOSP: EOR interrupt, interrecord
interlace off: if not end of record, EOW interrupt
--wcr == 0: comp: EOT interrupt, disconnect
IORD, IORP: ignore
IOSD: ZWC interrupt, disconnect
IOSP: ZWC interrupt, interrecord
*/
t_stat chan_write (int32 ch)
{
uint32 dat = 0;
uint32 dev = chan_uar[ch] & DEV_MASK;
uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
t_stat r = SCPE_OK;
if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */
if (INV_DEV (dev, ch)) CRETIOP; /* invalid dev? */
if (chan_cnt[ch] == 0) { /* buffer empty? */
if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */
chan_war[ch] = ReadP (chan_mar[ch]);
chan_mar[ch] = chan_mar_inc (ch); /* incr mar */
chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr mar */
chan_cnt[ch] = chan_cpw[ch] + 1; } /* set cnt */
else { /* ilce off */
CLR_XFR (dev, ch); /* cant xfr */
if (TST_EOR (dev)) return chan_eor (ch); /* EOR? */
chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* rate err */
return SCPE_OK; } /* end else ilce */
} /* end if cnt */
chan_cnt[ch] = chan_cnt[ch] - 1; /* decr cnt */
if (chan_flag[ch] & CHF_24B) dat = chan_war[ch]; /* 24B? */
else if (chan_flag[ch] & CHF_12B) { /* 12B? */
dat = (chan_war[ch] >> 12) & 07777; /* get halfword */
chan_war[ch] = (chan_war[ch] << 12) & DMASK; } /* remove from war */
else { /* 6B */
dat = (chan_war[ch] >> 18) & 077; /* get char */
chan_war[ch] = (chan_war[ch] << 6) & DMASK; } /* remove from war */
r = dev_dsp[dev][ch] (IO_WRITE, dev, &dat); /* write */
if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */
if (chan_cnt[ch] == 0) { /* buf empty? */
if (chan_flag[ch] & CHF_ILCE) { /* ilce on? */
if (chan_wcr[ch] == 0) { /* wc now 0? */
chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* ilc off */
if (tfnc == CHM_COMP) { /* compatible? */
if (ion) int_req = int_req | int_zc[ch];
dev_disc (ch, dev); /* disconnnect */
} /* end if comp */
else { /* extended */
if (chan_mode[ch] & CHM_ZC) /* ZWC int */
int_req = int_req | int_zc[ch];
if (tfnc == CHM_IOSD) { /* SD */
if (chan_mode[ch] & CHM_ER) /* EOR int */
int_req = int_req | int_er[ch];
dev_disc (ch, dev); /* disconnnect */
} /* end if SD */
else if (!(tfnc && CHM_SGNL) || /* IORx or IOSP TOP? */
(chan_flag[ch] & CHF_TOP))
dev_wreor (ch, dev); /* R: write EOR */
chan_flag[ch] = chan_flag[ch] & ~CHF_TOP;
} /* end else comp */
} /* end if wcr */
} /* end if ilce */
else if (chan_flag[ch] & CHF_TOP) { /* off, TOP pending? */
chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; /* clear TOP */
dev_wreor (ch, dev); } /* write EOR */
else if (ion) int_req = int_req | int_zc[ch]; /* no TOP, EOW intr */
} /* end if cnt */
} /* end if xfr */
if (TST_EOR (ch)) return chan_eor (ch); /* eor rcvd? */
return r;
}
/* MAR increment */
uint32 chan_mar_inc (int32 ch)
{
uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */
if ((chan_flag[ch] & CHF_DCHN) && ((t & VA_POFF) == 0)) { /* chain? */
chan_flag[ch] = chan_flag[ch] & ~CHF_DCHN; /* clr flag */
if (chan_dcr[ch] & CHD_INT) /* if armed, intr */
int_req = int_req | int_zc[ch];
t = (chan_dcr[ch] & CHD_PAGE) << VA_V_PN; } /* new mar */
return t;
}
/* End of record action */
t_stat chan_eor (int32 ch)
{
uint32 tfnc = CHM_GETFNC (chan_mode[ch]);
uint32 dev = chan_uar[ch] & DEV_MASK;
chan_flag[ch] = chan_flag[ch] & ~(CHF_EOR | CHF_ILCE); /* clr eor, ilce */
if (((tfnc == CHM_COMP) && ion) || (chan_mode[ch] & CHM_ER))
int_req = int_req | int_er[ch]; /* EOT/EOR? */
if (dev && (tfnc & CHM_PROC)) /* P, still conn? */
chan_flag[ch] = chan_flag[ch] | CHF_IREC; /* interrecord */
else return dev_disc (ch, dev); /* disconnect */
return SCPE_OK;
}
/* Utility routines */
t_stat dev_disc (uint32 ch, uint32 dev)
{
chan_uar[ch] = 0; /* disconnect */
if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_DISC, dev, NULL);
return SCPE_OK;
}
t_stat dev_wreor (uint32 ch, uint32 dev)
{
if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_WREOR, dev, NULL);
chan_flag[ch] = chan_flag[ch] | CHF_EOR; /* set eor */
return SCPE_OK;
}
/* Externally visible routines */
/* Channel driver */
t_stat chan_process (void)
{
int32 i, dev;
t_stat r;
for (i = 0; i < NUM_CHAN; i++) { /* loop thru */
dev = chan_uar[i] & DEV_MASK; /* get dev */
if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) { /* chan active? */
if (dev & DEV_OUT) r = chan_write (i); /* write */
else r = chan_read (i); /* read */
if (r) return r; } }
return SCPE_OK;
}
/* Test for channel active */
t_bool chan_testact (void)
{
int32 i, dev;
for (i = 0; i < NUM_CHAN; i++) {
dev = chan_uar[i] & DEV_MASK;
if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) return 1; }
return 0;
}
/* Async output device ready for more data */
void chan_set_ordy (int32 ch)
{
if ((ch >= 0) && (ch < NUM_CHAN)) {
int32 dev = chan_uar[ch] & DEV_MASK; /* get dev */
if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* buf or ilce? */
SET_XFR (dev, ch); /* set xfr flg */
else chan_flag[ch] = chan_flag[ch] | CHF_OWAK; } /* need wakeup */
return;
}
/* Set flag in channel */
void chan_set_flag (int32 ch, uint32 fl)
{
if ((ch >= 0) && (ch < NUM_CHAN)) chan_flag[ch] = chan_flag[ch] | fl;
return;
}
/* Set UAR in channel */
void chan_set_uar (int32 ch, uint32 dev)
{
if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = dev & DEV_MASK;
return;
}
/* Disconnect channel */
void chan_disc (int32 ch)
{
if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = 0;
return;
}
/* Reset channels */
t_stat chan_reset (DEVICE *dptr)
{
int32 i;
xfr_req = 0;
for (i = 0; i < NUM_CHAN; i++) {
chan_uar[i] = 0;
chan_wcr[i] = 0;
chan_mar[i] = 0;
chan_dcr[i] = 0;
chan_war[i] = 0;
chan_cpw[i] = 0;
chan_cnt[i] = 0;
chan_mode[i] = 0;
chan_flag[i] = 0; }
return SCPE_OK;
}
/* Channel assignment routines */
t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
int32 i;
if (sptr == NULL) return SCPE_ARG; /* valid args? */
if (uptr == NULL) return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL) return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL) return SCPE_IERR;
for (i = 0; i < NUM_CHAN; i++) { /* match input */
if (strcmp (sptr, chname[i]) == 0) { /* find string */
if (val && !(val & (1 << i))) return SCPE_ARG; /* legal? */
dibp->chan = i; /* store new */
return SCPE_OK; } }
return SCPE_ARG;
}
t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc)
{
DEVICE *dptr;
DIB *dibp;
if (uptr == NULL) return SCPE_IERR;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL) return SCPE_IERR;
dibp = (DIB *) dptr->ctxt;
if (dibp == NULL) return SCPE_IERR;
fprintf (st, "channel=%s", chname[dibp->chan]);
return SCPE_OK;
}
/* Init device tables */
t_bool io_init (void)
{
DEVICE *dptr;
DIB *dibp;
DSPT *tplp;
int32 ch;
uint32 i, j, dev, doff;
/* Clear dispatch table, device map */
for (i = 0; i < NUM_CHAN; i++) {
for (j = 0; j < (DEV_MASK + 1); j++) {
dev_dsp[j][i] = NULL;
dev_map[j][i] = 0; } }
/* Test each device for conflict; add to map; init tables */
for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */
dibp = (DIB *) dptr->ctxt; /* get DIB */
if ((dibp == NULL) || (dptr->flags & DEV_DIS)) continue; /* exist, enabled? */
ch = dibp->chan; /* get channel */
dev = dibp->dev; /* get device num */
if (ch < 0) dev3_dsp[dev] = dibp->iop; /* special device */
else {
if (dibp->tplt == NULL) return TRUE; /* must have template */
for (tplp = dibp->tplt; tplp->num; tplp++) { /* loop thru templates */
for (j = 0; j < tplp->num; j++) { /* repeat as needed */
doff = dev + tplp->off + j; /* get offset dnum */
if (dev_map[doff][ch]) { /* slot in use? */
printf ("Device number conflict, chan = %s, devno = %02o\n",
chname[ch], doff);
if (sim_log) fprintf (sim_log,
"Device number conflict, chan = %s, dev = %02o\n",
chname[ch], doff);
return TRUE; }
dev_map[doff][ch] = dibp->xfr; /* set xfr flag */
dev_dsp[doff][ch] = dibp->iop; /* set dispatch */
} /* end for j */
} /* end for tplt */
} /* end else */
} /* end for i */
return FALSE;
}
/* Display channel state */
t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc)
{
if ((val < 0) || (val >= NUM_CHAN)) return SCPE_IERR;
fprintf (st, "UAR: %02o\n", chan_uar[val]);
fprintf (st, "WCR: %05o\n", chan_wcr[val]);
fprintf (st, "MAR: %06o\n", chan_mar[val]);
fprintf (st, "DCR: %02o\n", chan_dcr[val]);
fprintf (st, "WAR: %08o\n", chan_war[val]);
fprintf (st, "CPW: %o\n", chan_cpw[val]);
fprintf (st, "CNT: %o\n", chan_cnt[val]);
fprintf (st, "MODE: %03o\n", chan_mode[val]);
fprintf (st, "FLAG: %04o\n", chan_flag[val]);
return SCPE_OK;
}

283
SDS/sds_lp.c Normal file
View File

@@ -0,0 +1,283 @@
/* sds_lp.c: SDS 940 line printer simulator
Copyright (c) 2001-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
lpt line printer
*/
#include "sds_defs.h"
#define LPT_V_LN 9
#define LPT_M_LN 07
#define LPT_GETLN(x) (((x) >> LPT_V_LN) & LPT_M_LN)
#define CHP(ch,val) ((val) & (1 << (ch))) /* CCL chan test */
#define SET_XFR 1 /* set xfr */
#define SET_EOR 2 /* print, set eor */
#define SET_SPC 4 /* space */
extern char sds_to_ascii[64];
extern uint32 xfr_req;
extern int32 stop_invins, stop_invdev, stop_inviop;
int32 lpt_spc = 0; /* space instr */
int32 lpt_sta = 0; /* timeout state */
int32 lpt_bptr = 0; /* line buf ptr */
int32 lpt_err = 0; /* error */
int32 lpt_ccl = 1, lpt_ccp = 0; /* cctl lnt, ptr */
int32 lpt_ctime = 10; /* char time */
int32 lpt_ptime = 1000; /* print time */
int32 lpt_stime = 10000; /* space time */
int32 lpt_stopioe = 1; /* stop on err */
char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */
uint8 lpt_cct[CCT_LNT] = { 0377 }; /* car ctl tape */
DSPT lpt_tplt[] = { { 1, 0 }, { 0, 0 } }; /* template */
DEVICE lpt_dev;
t_stat lpt_svc (UNIT *uptr);
t_stat lpt_reset (DEVICE *dptr);
t_stat lpt_attach (UNIT *uptr, char *cptr);
t_stat lpt_crctl (int32 ch);
t_stat lpt_status (UNIT *uptr);
t_stat lpt_bufout (UNIT *uptr);
void lpt_end_op (int32 fl);
t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat);
/* LPT data structures
lpt_dev LPT device descriptor
lpt_unit LPT unit descriptor
lpt_reg LPT register list
*/
DIB lpt_dib = { CHAN_W, DEV_LPT, XFR_LPT, lpt_tplt, &lpt };
UNIT lpt_unit = {
UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0) };
REG lpt_reg[] = {
{ BRDATA (BUF, lpt_buf, 8, 8, LPT_WIDTH) },
{ DRDATA (BPTR, lpt_bptr, 8), PV_LEFT },
{ FLDATA (XFR, xfr_req, XFR_V_LPT) },
{ FLDATA (ERR, lpt_err, 0) },
{ ORDATA (STA, lpt_sta, 3) },
{ BRDATA (CCT, lpt_cct, 8, 8, CCT_LNT) },
{ DRDATA (CCTP, lpt_ccp, 8), PV_LEFT },
{ DRDATA (CCTL, lpt_ccl, 8), REG_RO + PV_LEFT },
{ ORDATA (SPCINST, lpt_spc, 24) },
{ DRDATA (POS, lpt_unit.pos, 32), PV_LEFT },
{ DRDATA (CTIME, lpt_ctime, 24), REG_NZ + PV_LEFT },
{ DRDATA (PTIME, lpt_ptime, 24), REG_NZ + PV_LEFT },
{ DRDATA (STIME, lpt_stime, 24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, lpt_stopioe, 0) },
{ NULL } };
MTAB lpt_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, NULL },
{ 0 } };
DEVICE lpt_dev = {
"LPT", &lpt_unit, lpt_reg, lpt_mod,
1, 10, 31, 1, 8, 7,
NULL, NULL, &lpt_reset,
NULL, &lpt_attach, NULL,
&lpt_dib, DEV_DISABLE };
/* Line printer routine
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
The line printer is an asynchronous output device, that is, it
can never set the channel rate error flag.
*/
t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 i, t, new_ch;
char asc;
switch (fnc) { /* case function */
case IO_CONN: /* connect */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != lpt_dib.chan) return SCPE_IERR; /* wrong chan? */
for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = 0; /* clr buffer */
lpt_bptr = 0; /* clr buf ptr */
lpt_err = 0; /* err = 0 */
xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */
lpt_sta = lpt_sta | SET_XFR; /* need xfr */
sim_activate (&lpt_unit, lpt_ctime); /* start timer */
break;
case IO_EOM1: /* EOM mode 1 */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != lpt_dib.chan) CRETIOP; /* wrong chan? */
if (inst & 0400) { /* space? */
lpt_spc = inst; /* save instr */
lpt_sta = lpt_sta | SET_SPC; /* need space */
sim_cancel (&lpt_unit); /* cancel timer */
sim_activate (&lpt_unit, lpt_stime); } /* start timer */
break;
case IO_DISC: /* disconnect */
lpt_end_op (0); /* normal term */
return lpt_bufout (&lpt_unit); /* dump output */
case IO_WREOR: /* write eor */
lpt_sta = (lpt_sta | SET_EOR) & ~SET_XFR; /* need eor */
sim_activate (&lpt_unit, lpt_ptime); /* start timer */
break;
case IO_SKS: /* SKS */
new_ch = I_GETSKCH (inst); /* sks chan */
if (new_ch != lpt_dib.chan) return SCPE_IERR; /* wrong chan? */
t = I_GETSKCND (inst); /* sks cond */
if (((t == 020) && (!CHP (7, lpt_cct[lpt_ccp]))) || /* 14062: !ch 7 */
((t == 010) && (lpt_unit.flags & UNIT_ATT)) || /* 12062: !online */
(t == 004) && !lpt_err) *dat = 1; /* 11062: !err */
break;
case IO_WRITE: /* write */
asc = sds_to_ascii[(*dat) & 077]; /* convert data */
xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */
if (lpt_bptr < LPT_WIDTH) lpt_buf[lpt_bptr++] = asc;/* store data */
lpt_sta = lpt_sta | SET_XFR; /* need xfr */
sim_activate (&lpt_unit, lpt_ctime); /* start ch timer */
break;
default:
CRETINS; }
return SCPE_OK;
}
/* Unit service and write */
t_stat lpt_svc (UNIT *uptr)
{
t_stat r = SCPE_OK;
static const char *lpt_stabl[] = {
"\r", "\n", "\n\n", "\n\n\n",
"\n\n\n\n", "\n\n\n\n\n",
"\n\n\n\n\n\n", "\n\n\n\n\n\n\n" };
if (lpt_sta & SET_XFR) chan_set_ordy (lpt_dib.chan); /* need lpt xfr? */
if (lpt_sta & SET_EOR) { /* printing? */
chan_set_flag (lpt_dib.chan, CHF_EOR); /* set eor flg */
r = lpt_bufout (uptr); } /* output buf */
if (lpt_sta & SET_SPC) { /* spacing? */
if (uptr->flags & UNIT_ATT) { /* attached? */
int32 ln = LPT_GETLN (lpt_spc); /* get lines, ch */
if (lpt_spc & 0200) /* n lines? */
fputs (lpt_stabl[ln], uptr->fileref); /* upspace */
else lpt_crctl (ln); } /* carriage ctl */
r = lpt_status (uptr); } /* update status */
lpt_sta = 0; /* clear state */
return r;
}
/* Trim and output buffer */
t_stat lpt_bufout (UNIT *uptr)
{
int32 i;
if ((uptr->flags & UNIT_ATT) && lpt_bptr) { /* attached? */
for (i = LPT_WIDTH - 1; (i >= 0) && (lpt_buf[i] == ' '); i--)
lpt_buf[i] = 0; /* trim line */
fputs (lpt_buf, uptr->fileref); /* write line */
lpt_bptr = 0; }
return lpt_status (uptr); /* return status */
}
/* Status update after I/O */
t_stat lpt_status (UNIT *uptr)
{
if (uptr->flags & UNIT_ATT) { /* attached? */
uptr->pos = ftell (uptr->fileref); /* update position */
if (ferror (uptr->fileref)) { /* I/O error? */
lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */
perror ("LPT I/O error"); /* print msg */
clearerr (uptr->fileref);
return SCPE_IOERR; } } /* ret error */
else {
lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */
CRETIOE (lpt_stopioe, SCPE_UNATT); } /* ret error */
return SCPE_OK;
}
/* Terminate LPT operation */
void lpt_end_op (int32 fl)
{
if (fl) chan_set_flag (lpt_dib.chan, fl); /* set flags */
xfr_req = xfr_req & ~XFR_LPT; /* clear xfr */
sim_cancel (&lpt_unit); /* stop */
if (fl & CHF_ERR) { /* error? */
chan_disc (lpt_dib.chan); /* disconnect */
lpt_err = 1; } /* set lpt err */
return;
}
/* Carriage control */
t_stat lpt_crctl (int32 ch)
{
int32 i, j;
if ((ch == 1) && CHP (ch, lpt_cct[0])) { /* top of form? */
fputs ("\f\n", lpt_unit.fileref); /* ff + nl */
lpt_ccp = 0; /* top of page */
return SCPE_OK; }
for (i = 1; i < lpt_ccl + 1; i++) { /* sweep thru cct */
lpt_ccp = (lpt_ccp + 1) %lpt_ccl; /* adv pointer */
if (CHP (ch, lpt_cct[lpt_ccp])) { /* chan punched? */
for (j = 0; j < i; j++) fputc ('\n', lpt_unit.fileref);
return SCPE_OK; } }
return STOP_CCT; /* runaway channel */
}
/* Reset routine */
t_stat lpt_reset (DEVICE *dptr)
{
chan_disc (lpt_dib.chan); /* disconnect */
lpt_spc = 0; /* clr state */
lpt_sta = 0;
xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */
sim_cancel (&lpt_unit); /* deactivate */
return SCPE_OK;
}
/* Attach routine */
t_stat lpt_attach (UNIT *uptr, char *cptr)
{
lpt_ccp = 0; /* top of form */
return attach_unit (uptr, cptr);
}

489
SDS/sds_mt.c Normal file
View File

@@ -0,0 +1,489 @@
/* sds_mt.c: SDS 940 magnetic tape simulator
Copyright (c) 2001-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
mt 7 track magnetic tape
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 "sds_defs.h"
#define MT_MAXFR (32768 * 4)
#define MT_NUMDR 8 /* number drives */
#define MT_UNIT 07
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define botf u3 /* bot tape flag */
#define eotf u4 /* eot tape flag */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
#define MTR_BOT 0xFFFFFFFE /* BOT pseudo mark */
extern uint32 xfr_req;
extern int32 stop_invins, stop_invdev, stop_inviop;
int32 mt_inst = 0; /* saved instr */
int32 mt_eof = 0; /* end of file */
int32 mt_gap = 0; /* in gap */
int32 mt_skip = 0; /* skip rec */
int32 mt_bptr = 0; /* buf ptr */
int32 mt_blnt = 0; /* buf length */
int32 mt_ctime = 10; /* char time */
int32 mt_gtime = 1000; /* gap time */
int32 mt_stopioe = 1; /* stop on err */
uint8 mtxb[MT_MAXFR]; /* record buffer */
DSPT mt_tplt[] = { /* template */
{ MT_NUMDR, 0 }, { MT_NUMDR, DEV_MTS },
{ MT_NUMDR, DEV_OUT }, { MT_NUMDR, DEV_MTS+DEV_OUT },
{ 0, 0 } };
DEVICE mt_dev;
t_stat mt_svc (UNIT *uptr);
t_stat mt_reset (DEVICE *dptr);
t_stat mt_boot (int32 unitno, DEVICE *dptr);
t_stat mt_attach (UNIT *uptr, char *cptr);
t_stat mt_detach (UNIT *uptr);
t_stat mt_readrec (UNIT *uptr);
t_mtrlnt mt_readbc (UNIT *uptr);
void mt_readend (UNIT *uptr);
t_stat mt_wrend (uint32 dev);
void mt_set_err (UNIT *uptr);
t_stat mt (uint32 fnc, uint32 inst, uint32 *dat);
static const char sds_to_bcd[64] = {
012, 001, 002, 003, 004, 005, 006, 007,
010, 011, 012, 013, 014, 015, 016, 017,
060, 061, 062, 063, 064, 065, 066, 067,
070, 071, 072, 073, 074, 075, 076, 077,
040, 041, 042, 043, 044, 045, 046, 047,
050, 051, 052, 053, 054, 055, 056, 057,
020, 021, 022, 023, 024, 025, 026, 027,
030, 031, 032, 033, 034, 035, 036, 037 };
static const char bcd_to_sds[64] = {
000, 001, 002, 003, 004, 005, 006, 007,
010, 011, 000, 013, 014, 015, 016, 017,
060, 061, 062, 063, 064, 065, 066, 067,
070, 071, 072, 073, 074, 075, 076, 077,
040, 041, 042, 043, 044, 045, 046, 047,
050, 051, 052, 053, 054, 055, 056, 057,
020, 021, 022, 023, 024, 025, 026, 027,
030, 031, 032, 033, 034, 035, 036, 037 };
/* MT data structures
mt_dev MT device descriptor
mt_unit MT unit descriptor
mt_reg MT register list
*/
DIB mt_dib = { CHAN_W, DEV_MT, XFR_MT0, mt_tplt, &mt };
UNIT mt_unit[] = {
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&mt_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } };
REG mt_reg[] = {
{ BRDATA (BUF, mtxb, 8, 8, MT_MAXFR) },
{ DRDATA (BPTR, mt_bptr, 18), PV_LEFT },
{ DRDATA (BLNT, mt_blnt, 18), PV_LEFT },
{ FLDATA (XFR, xfr_req, XFR_V_MT0) },
{ ORDATA (INST, mt_inst, 24) },
{ FLDATA (EOF, mt_eof, 0) },
{ FLDATA (GAP, mt_gap, 0) },
{ FLDATA (SKIP, mt_skip, 0) },
{ DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT },
{ DRDATA (GTIME, mt_gtime, 24), REG_NZ + PV_LEFT },
{ URDATA (POS, mt_unit[0].pos, 10, 32, 0,
MT_NUMDR, PV_LEFT | REG_RO) },
{ URDATA (BOT, mt_unit[0].botf, 10, 1, 0, MT_NUMDR, REG_RO) },
{ URDATA (EOT, mt_unit[0].eotf, 10, 1, 0, MT_NUMDR, REG_RO) },
{ FLDATA (STOP_IOE, mt_stopioe, 0) },
{ NULL } };
MTAB mt_mod[] = {
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, NULL },
{ 0 } };
DEVICE mt_dev = {
"MT", mt_unit, mt_reg, mt_mod,
MT_NUMDR, 10, 31, 1, 8, 8,
NULL, NULL, &mt_reset,
&mt_boot, &mt_attach, NULL,
&mt_dib, DEV_DISABLE };
/* Mag tape routine
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
*/
t_stat mt (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 u = inst & MT_UNIT; /* get unit */
UNIT *uptr = mt_dev.units + u; /* get unit ptr */
int32 t, new_ch;
uint8 chr;
t_stat r;
switch (fnc) { /* case function */
case IO_CONN: /* connect */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != mt_dib.chan) return SCPE_IERR; /* wrong chan? */
if (mt_gap) { /* in gap? */
mt_gap = 0; /* clr gap flg */
sim_cancel (uptr); } /* cancel timer */
else if (sim_is_active (uptr)) CRETIOP; /* busy? */
uptr->eotf = 0; /* clr eot flag */
mt_eof = 0; /* clr eof flag */
mt_skip = 0; /* clr skp flag */
mt_bptr = mt_blnt = 0; /* init buffer */
if ((inst & DEV_MTS)? (CHC_GETCPW (inst) < 2): /* scn & cpw<3? */
(inst & CHC_REV)) return STOP_INVIOP; /* rw & rev? */
mt_inst = inst; /* save inst */
if ((inst & DEV_MTS) && !(inst && DEV_OUT)) /* scanning? */
chan_set_flag (mt_dib.chan, CHF_SCAN); /* set chan flg */
xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
sim_activate (uptr, mt_gtime); /* start timer */
break;
case IO_EOM1: /* EOM mode 1 */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != mt_dib.chan) CRETIOP; /* wrong chan? */
t = inst & 07670; /* get command */
if ((t == 04010) && !sim_is_active (uptr)) { /* rewind? */
uptr->pos = uptr->eotf = 0; /* clr pos, eot */
uptr->botf = 1; } /* set bot */
else if ((t == 03610) && sim_is_active (uptr) && /* skip rec? */
((mt_inst & DEV_OUT) == 0)) mt_skip = 1; /* set flag */
else CRETINS;
break;
case IO_DISC: /* disconnect */
sim_cancel (uptr); /* no more xfr's */
if (inst & DEV_OUT) { /* write? */
if (r = mt_wrend (inst)) return r; } /* end record */
break;
case IO_WREOR: /* write eor */
chan_set_flag (mt_dib.chan, CHF_EOR); /* set eor flg */
if (r = mt_wrend (inst)) return r; /* end record */
mt_gap = 1; /* in gap */
sim_activate (uptr, mt_gtime); /* start timer */
break;
case IO_SKS: /* SKS */
new_ch = I_GETSKCH (inst); /* get chan # */
if (new_ch != mt_dib.chan) return SCPE_IERR; /* wrong chan? */
if ((inst & (DEV_OUT | DEV_MTS)) == 0) { /* not sks 1n? */
t = I_GETSKCND (inst); /* get skip cond */
switch (t) { /* case sks cond */
case 001: /* sks 1021n */
*dat = 1; /* not magpak */
break;
case 002: /* sks 1041n */
if (!(uptr->flags & UNIT_ATT) || /* not ready */
sim_is_active (uptr)) *dat = 1;
break;
case 004: /* sks 1101n */
if (!uptr->eotf) *dat = 1; /* not EOT */
break;
case 010: /* sks 1201n */
if (!uptr->botf) *dat = 1; /* not BOT */
break;
case 013: /* sks 12610 */
if (!mt_gap) *dat = 1; /* not in gap */
break;
case 017: /* sks 13610 */
if (!mt_eof) *dat = 1; /* not EOF */
break;
case 020: /* sks 1401n */
if (!(uptr->flags & UNIT_WPRT)) *dat = 1; /* not wrp */
break;
case 031: /* sks 1621n */
case 033: /* sks 1661n */
*dat = 1; /* not 556bpi */
case 035: /* sks 1721n */
break; } /* not 800bpi */
} /* end if */
break;
case IO_READ: /* read */
xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
if (mt_blnt == 0) { /* first read? */
r = mt_readrec (uptr); /* get data */
if ((r != SCPE_OK) || (mt_blnt == 0)) return r; } /* err, inv reclnt? */
uptr->botf = 0; /* off BOT */
if (mt_inst & CHC_REV) chr = mtxb[--mt_bptr] & 077; /* get next rev */
else chr = mtxb[mt_bptr++] & 077; /* get next fwd */
if (!(mt_inst & CHC_BIN)) chr = bcd_to_sds[chr]; /* bcd? */
*dat = chr & 077; /* give to chan */
if ((mt_inst & CHC_REV)? (mt_bptr <= 0): /* rev or fwd, */
(mt_bptr >= mt_blnt)) mt_readend (uptr); /* recd done? */
break;
case IO_WRITE: /* write */
uptr->botf = 0; /* off BOT */
chr = (*dat) & 077;
xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
if (!(mt_inst & CHC_BIN)) chr = sds_to_bcd[chr]; /* bcd? */
if (mt_bptr < MT_MAXFR) mtxb[mt_bptr++] = chr; /* insert in buf */
break;
default:
CRETINS; }
return SCPE_OK;
}
/* Unit service */
t_stat mt_svc (UNIT *uptr)
{
if (mt_gap) { /* gap timeout */
mt_gap = 0; /* clr gap flg */
chan_disc (mt_dib.chan); } /* disc chan */
else if (mt_skip) mt_readend (uptr); /* skip record */
else { /* normal xfr */
xfr_req = xfr_req | XFR_MT0; /* set xfr req */
sim_activate (uptr, mt_ctime); } /* reactivate */
return SCPE_OK;
}
/* Read start (get new record) */
t_stat mt_readrec (UNIT *uptr)
{
t_mtrlnt abc, tbc;
if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */
mt_set_err (uptr); /* no, err, disc */
return SCPE_UNATT; }
tbc = mt_readbc (uptr); /* get bc */
if (tbc == MTR_EOM) { /* end of med? */
uptr->eotf = 1; /* end of tape */
mt_set_err (uptr); /* err, disc */
return SCPE_OK; }
if (tbc == MTR_BOT) { /* BOT? */
mt_set_err (uptr); /* err, disc */
return SCPE_OK; }
if (tbc == MTR_TMK) { /* tape mark? */
mt_eof = 1; /* set eof flag */
mtxb[0] = mtxb[1] = 017; /* EOR char */
mt_blnt = 2; /* store 2 */
uptr->pos += sizeof (t_mtrlnt); /* update position */
return SCPE_OK; }
mt_blnt = tbc; /* set buf lnt */
if (tbc > MT_MAXFR) return SCPE_MTRLNT; /* record too long? */
if (mt_inst & CHC_REV) { /* reverse? */
fseek (uptr->fileref, uptr->pos - ((tbc + 1) & ~1) - sizeof (t_mtrlnt), SEEK_SET);
mt_bptr = mt_blnt; }
abc = fxread (mtxb, sizeof (uint8), tbc, uptr->fileref);/* read record */
for (; abc < tbc; abc++) mtxb[abc] = 0; /* zero fill */
if (ferror (uptr->fileref)) { /* I/O error */
mt_set_err (uptr); /* no, err, disc */
perror ("MT I/O error");
clearerr (uptr->fileref);
return SCPE_IOERR; }
if (mt_inst & CHC_REV) /* update pos */
uptr->pos -= (((tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt)));
else uptr->pos += (((tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt)));
return SCPE_OK;
}
/* Read record byte count */
t_mtrlnt mt_readbc (UNIT *uptr)
{
t_mtrlnt tbc;
if (mt_inst & CHC_REV) {
if (uptr->pos < sizeof (t_mtrlnt)) {
uptr->botf = 1;
return MTR_BOT; }
fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET); }
else fseek (uptr->fileref, uptr->pos, SEEK_SET);
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr->fileref);
if (ferror (uptr->fileref) || feof (uptr->fileref) || /* err, eof, eom? */
(tbc == MTR_EOM)) return MTR_EOM; /* return EOM */
if (MTRF (tbc)) chan_set_flag (mt_dib.chan, CHF_ERR); /* rec err? set flag */
return MTRL (tbc);
}
/* Read done (eof, end of record) */
void mt_readend (UNIT *uptr)
{
sim_cancel (uptr); /* stop timer */
mt_skip = 0; /* clr skp flg */
chan_set_flag (mt_dib.chan, CHF_EOR); /* end record */
if (mt_eof) chan_disc (mt_dib.chan); /* EOF? */
else {
mt_gap = 1; /* no, in gap */
sim_activate (uptr, mt_gtime); } /* start timer */
return;
}
/* Write complete (end of record or disconnect) */
t_stat mt_wrend (uint32 dev)
{
static t_mtrlnt bceom = MTR_EOM;
static t_mtrlnt bceof = MTR_TMK;
UNIT *uptr = mt_dev.units + (dev & MT_UNIT);
t_addr old_pos = uptr->pos;
t_mtrlnt tbc;
sim_cancel (uptr); /* no more xfr's */
if (mt_bptr == 0) return SCPE_OK; /* buf empty? */
if (!(uptr->flags & UNIT_ATT)) { /* attached? */
mt_set_err (uptr); /* no, err, disc */
return SCPE_UNATT; }
if (uptr->flags & UNIT_WPRT) { /* write lock? */
mt_set_err (uptr); /* yes, err, disc */
return SCPE_OK; }
if (dev & DEV_MTS) { /* erase? */
if (mt_inst & CHC_REV) { /* reverse? */
tbc = mt_readbc (uptr); /* get bc */
if ((tbc == MTR_TMK) || (tbc == MTR_EOM)) /* tmk, eom? */
fseek (uptr->fileref, uptr->pos -= sizeof (t_mtrlnt), SEEK_SET);
else if (tbc != MTR_BOT) { /* not BOT? */
tbc = MTRL (tbc); /* clear error */
fseek (uptr->fileref, uptr->pos -=
(((tbc + 1) & ~1) - sizeof (t_mtrlnt)), SEEK_SET); } }
fxwrite (&bceom, sizeof (t_mtrlnt), 1, uptr->fileref);
}
else {
fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set position */
if ((mt_bptr == 1) && (mtxb[0] == 017) && /* wr eof? */
((mt_inst & 01670) == 00050)) {
fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr->fileref);
uptr->pos += sizeof (t_mtrlnt); }
else { /* normal wr */
fxwrite (&mt_bptr, sizeof (t_mtrlnt), 1, uptr->fileref);
fxwrite (mtxb, sizeof (uint8), (mt_bptr + 1) & ~1, uptr->fileref);
fxwrite (&mt_bptr, sizeof (t_mtrlnt), 1, uptr->fileref);
uptr->pos += ((mt_bptr + 1) & ~1) + (2 * sizeof (t_mtrlnt));
}
}
mt_bptr = 0;
if (ferror (uptr->fileref)) { /* I/O error */
uptr->pos = old_pos; /* restore pos */
mt_set_err (uptr); /* no, err, disc */
perror ("MT I/O error");
clearerr (uptr->fileref);
return SCPE_IOERR; }
return SCPE_OK;
}
/* Fatal error */
void mt_set_err (UNIT *uptr)
{
chan_set_flag (mt_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */
chan_disc (mt_dib.chan); /* disconnect */
xfr_req = xfr_req & ~XFR_MT0; /* clear xfr */
sim_cancel (uptr); /* stop */
mt_bptr = 0; /* buf empty */
return;
}
/* Reset routine */
t_stat mt_reset (DEVICE *dptr)
{
int32 i;
chan_disc (mt_dib.chan); /* disconnect */
mt_eof = 0; /* clear state */
mt_gap = 0;
mt_skip = 0;
mt_inst = 0;
mt_bptr = mt_blnt = 0;
xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */
for (i = 0; i < MT_NUMDR; i++) { /* deactivate */
sim_cancel (&mt_unit[i]);
mt_unit[i].eotf = 0; }
return SCPE_OK;
}
/* Attach and detach routines */
t_stat mt_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK) return r;
uptr->botf = 1;
uptr->eotf = 0;
return SCPE_OK;
}
t_stat mt_detach (UNIT *uptr)
{
uptr->botf = uptr->eotf = 0;
return detach_unit (uptr);
}
/* Boot routine - simulate FILL console command */
t_stat mt_boot (int32 unitno, DEVICE *dptr)
{
extern uint32 P, M[];
if (unitno) return SCPE_ARG; /* only unit 0 */
M[0] = 077777771; /* -7B */
M[1] = 007100000; /* LDX 0 */
M[2] = 000203610; /* EOM 3610B */
M[3] = 003200002; /* WIM 2 */
M[4] = 000100002; /* BRU 2 */
P = 1; /* start at 1 */
return SCPE_OK;
}

504
SDS/sds_mux.c Normal file
View File

@@ -0,0 +1,504 @@
/* sds_mux.c: SDS 940 terminal multiplexor simulator
Copyright (c) 2001-2002, 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.
mux terminal multiplexor
This module implements up to 32 individual serial interfaces, representing
either the project Genie terminal multiplexor or the SDS 940 CTE option.
*/
#include "sds_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#include <ctype.h>
#define PROJ_GENIE (cpu_unit.flags & UNIT_GENIE)
#define MUX_NUMLIN mux_desc.lines
#define MUX_LINES 32 /* lines */
#define MUX_FLAGS 4 /* intr per line */
#define MUX_FLAGMASK (MUX_FLAGS - 1)
#define MUX_SCANMAX (MUX_LINES * MUX_FLAGS) /* flags to scan */
#define MUX_SCANMASK (MUX_SCANMAX - 1)
#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */
#define UNIT_UC (1 << UNIT_V_UC)
#define MUX_INIT_POLL 8000
#define MUXL_WAIT 500
#define MUX_SETFLG(l,x) mux_flags[((l) * MUX_FLAGS) + (x)] = 1
#define MUX_SETINT(x) int_req = int_req | (INT_MUXR >> (x))
#define MUX_CLRINT(x) int_req = int_req & ~(INT_MUXR >> (x))
/* PIN/POT */
#define P_V_CHAR 16 /* char */
#define P_M_CHAR 0377
#define P_CHAR(x) (((x) >> P_V_CHAR) & P_M_CHAR)
#define PIN_OVR 000100000 /* overrun */
#define POT_NOX 000100000 /* no xmit */
#define POT_XMI 000040000 /* xmit int */
#define POT_GLNE 000020000 /* Genie: enable */
#define POT_SCDT 000020000 /* 940: clr DTR */
#define P_V_CHAN 0 /* channel */
#define P_M_CHAN (MUX_LINES - 1)
#define P_CHAN(x) (((x) >> P_V_CHAN) & P_M_CHAN)
/* SKS 940 */
#define SKS_XBE 000001000 /* xmt buf empty */
#define SKS_CRO 000000400 /* carrier on */
#define SKS_DSR 000000200 /* data set ready */
#define SKS_CHAN(x) P_CHAN(x)
/* SKS Genie */
#define SKG_V_CHAN 7
#define SKG_M_CHAN (MUX_LINES - 1)
#define SKG_CHAN(x) (((x) >> SKG_V_CHAN) & SKG_M_CHAN)
/* Flags */
#define MUX_FRCV 0 /* receive */
#define MUX_FXMT 1 /* transmit */
#define MUX_FCRN 2 /* carrier on */
#define MUX_FCRF 3 /* carrier off */
/* Line status */
#define MUX_SCHP 001 /* char pending */
#define MUX_SOVR 002 /* overrun */
#define MUX_SLNE 004 /* line enabled */
#define MUX_SXIE 010 /* xmt int enab */
#define MUX_SCRO 020 /* carrier on */
#define MUX_SDSR 040 /* data set ready */
/* Data */
extern uint32 alert, int_req;
extern int32 stop_invins, stop_invdev, stop_inviop;
extern UNIT cpu_unit;
uint8 mux_rbuf[MUX_LINES]; /* rcv buf */
uint8 mux_xbuf[MUX_LINES]; /* xmt buf */
uint8 mux_sta[MUX_LINES]; /* status */
uint8 mux_flags[MUX_SCANMAX]; /* flags */
uint32 mux_tps = 100; /* polls/second */
uint32 mux_scan = 0; /* scanner */
uint32 mux_slck = 0; /* scanner locked */
TMLN mux_ldsc[MUX_LINES] = { 0 }; /* line descriptors */
TMXR mux_desc = { MUX_LINES, 0, 0, NULL }; /* mux descriptor */
t_stat mux (uint32 fnc, uint32 inst, uint32 *dat);
t_stat muxi_svc (UNIT *uptr);
t_stat muxo_svc (UNIT *uptr);
t_stat mux_reset (DEVICE *dptr);
t_stat mux_attach (UNIT *uptr, char *cptr);
t_stat mux_detach (UNIT *uptr);
t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat mux_vlines (UNIT *uptr, int32 val, char *cptr, void *desc);
void mux_reset_ln (int32 ln);
void mux_scan_next (void);
/* MUX data structures
mux_dev MUX device descriptor
mux_unit MUX unit descriptor
mux_reg MUX register list
mux_mod MUX modifiers list
*/
DIB mux_dib = { -1, DEV3_GMUX, 0, NULL, &mux };
REG mux_nlreg = { DRDATA (NLINES, MUX_NUMLIN, 6), PV_LEFT };
UNIT mux_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), MUX_INIT_POLL };
REG mux_reg[] = {
{ BRDATA (STA, mux_sta, 8, 6, MUX_LINES) },
{ BRDATA (RBUF, mux_rbuf, 8, 8, MUX_LINES) },
{ BRDATA (XBUF, mux_xbuf, 8, 8, MUX_LINES) },
{ BRDATA (INT, mux_flags, 8, 1, MUX_SCANMAX) },
{ ORDATA (SCAN, mux_scan, 7) },
{ FLDATA (SLCK, mux_slck, 0) },
{ DRDATA (TPS, mux_tps, 8), REG_NZ + PV_LEFT },
{ NULL } };
MTAB mux_mod[] = {
{ MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES",
&mux_vlines, NULL, &mux_nlreg },
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &mux_desc },
{ UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &mux_summ },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &mux_show, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &mux_show, NULL },
{ 0 } };
DEVICE mux_dev = {
"MUX", &mux_unit, mux_reg, mux_mod,
1, 10, 31, 1, 8, 8,
&tmxr_ex, &tmxr_dep, &mux_reset,
NULL, &mux_attach, &mux_detach,
&mux_dib, DEV_DISABLE };
/* MUXL data structures
muxl_dev MUXL device descriptor
muxl_unit MUXL unit descriptor
muxl_reg MUXL register list
muxl_mod MUXL modifiers list
*/
UNIT muxl_unit[] = {
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT },
{ UDATA (&muxo_svc, UNIT_UC, 0), MUXL_WAIT } };
MTAB muxl_mod[] = {
{ UNIT_UC, 0, "lower case", "LC", NULL },
{ UNIT_UC, UNIT_UC, "upper case", "UC", NULL },
{ 0 } };
REG muxl_reg[] = {
{ URDATA (TIME, muxl_unit[0].wait, 10, 24, 0,
MUX_LINES, REG_NZ + PV_LEFT) },
{ NULL } };
DEVICE muxl_dev = {
"MUXL", muxl_unit, muxl_reg, muxl_mod,
MUX_LINES, 10, 31, 1, 8, 8,
NULL, NULL, &mux_reset,
NULL, NULL, NULL,
NULL, 0 };
/* MUX: IO routine */
/* Mux routine - EOM 30001 or EOM 77777,2 */
t_stat mux (uint32 fnc, uint32 inst, uint32 *dat)
{
uint32 ln;
switch (fnc) {
case IO_CONN: /* connect */
if ((PROJ_GENIE && (inst == 000230001)) || /* set alert */
(!PROJ_GENIE && (inst == 020277777))) alert = POT_MUX;
else CRETINS;
break;
case IO_SKS: /* skip */
if (PROJ_GENIE && ((inst & 077770077) == 004030001)) {
ln = SKG_CHAN (inst); /* get line */
if (!sim_is_active (&muxl_unit[ln])) *dat = 1; }
else if (!PROJ_GENIE && ((inst & 077776000) == 024076000)) {
ln = SKS_CHAN (inst); /* get line */
if (inst & (SKS_XBE|SKS_CRO|SKS_DSR)) *dat = 1;
if (((inst & SKS_XBE) && sim_is_active (&muxl_unit[ln])) ||
((inst & SKS_CRO) && !(mux_sta[ln] & MUX_SCRO)) ||
((inst & SKS_DSR) && !(mux_sta[ln] & MUX_SDSR)))
*dat = 0; } /* no skip if fail */
else CRETINS;
default:
return SCPE_IERR; } /* end case */
return SCPE_OK;
}
/* PIN routine */
t_stat pin_mux (uint32 num, uint32 *dat)
{
uint32 ln = mux_scan >> 2;
uint32 flag = mux_scan & MUX_FLAGMASK;
mux_scan = mux_scan & MUX_SCANMASK; /* mask scan */
mux_flags[mux_scan] = 0; /* clear flag */
if (flag == MUX_FRCV) { /* rcv event? */
*dat = ln | ((uint32) mux_rbuf[ln] << P_V_CHAR) | /* line + char + */
((mux_sta[ln] & MUX_SOVR)? PIN_OVR: 0); /* overrun */
mux_sta[ln] = mux_sta[ln] & ~(MUX_SCHP | MUX_SOVR); }
else *dat = ln; /* just line */
mux_slck = 0; /* unlock scanner */
mux_scan_next (); /* kick scanner */
return SCPE_OK;
}
t_stat pot_mux (uint32 num, uint32 *dat)
{
uint32 ln = P_CHAN (*dat);
uint32 chr = P_CHAR (*dat);
if (PROJ_GENIE && ((*dat & POT_GLNE) == 0)) { /* Genie disable? */
mux_sta[ln] = mux_sta[ln] & ~MUX_SLNE; /* clear status */
mux_ldsc[ln].rcve = 0; }
else if (!PROJ_GENIE && (*dat & POT_SCDT)) { /* SDS disable? */
if (mux_ldsc[ln].conn) { /* connected? */
tmxr_msg (mux_ldsc[ln].conn, "\r\nLine hangup\r\n");
tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */
mux_reset_ln (ln); /* reset state */
MUX_SETFLG (ln, MUX_FCRF); /* set carrier off */
mux_scan_next (); } /* kick scanner */
mux_sta[ln] = mux_sta[ln] & ~MUX_SLNE; /* clear status */
mux_ldsc[ln].rcve = 0; }
else { /* enabled */
if ((*dat & POT_NOX) == 0) { /* output char? */
mux_xbuf[ln] = chr; /* store char */
sim_activate (&muxl_unit[ln], muxl_unit[ln].wait); }
if (*dat & POT_XMI) mux_sta[ln] = mux_sta[ln] | MUX_SXIE;
else mux_sta[ln] = mux_sta[ln] & ~MUX_SXIE;
mux_sta[ln] = mux_sta[ln] | MUX_SLNE; /* line is enabled */
mux_ldsc[ln].rcve = 1; }
return SCPE_OK;
}
/* Unit service - receive side
Poll all active lines for input
Poll for new connections
*/
t_stat muxi_svc (UNIT *uptr)
{
int32 ln, c, t;
if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */
t = sim_rtcn_calb (mux_tps, TMR_MUX); /* calibrate */
sim_activate (uptr, t); /* continue poll */
ln = tmxr_poll_conn (&mux_desc); /* look for connect */
if (ln >= 0) { /* got one? */
if (!PROJ_GENIE && (mux_sta[ln] & MUX_SLNE)) { /* modem & DTR? */
mux_sta[ln] = mux_sta[ln] | (MUX_SCRO|MUX_SDSR);/* carrier on */
MUX_SETFLG (ln, MUX_FCRN); /* set carr on flag */
mux_scan_next (); } /* kick scanner */
mux_ldsc[ln].rcve = 1; } /* set rcv enable */
tmxr_poll_rx (&mux_desc); /* poll for input */
for (ln = 0; ln < MUX_NUMLIN; ln++) { /* loop thru lines */
if (mux_ldsc[ln].conn) { /* connected? */
if (c = tmxr_getc_ln (&mux_ldsc[ln])) { /* get char */
if (mux_sta[ln] & MUX_SCHP) /* already got one? */
mux_sta[ln] = mux_sta[ln] | MUX_SOVR; /* overrun */
else mux_sta[ln] = mux_sta[ln] | MUX_SCHP; /* char pending */
c = c & 0177; /* mask to 7b */
if ((muxl_unit[ln].flags & UNIT_UC) && /* cvt to UC? */
islower (c & 0x7F)) c = toupper (c);
mux_rbuf[ln] = c; /* save char */
MUX_SETFLG (ln, MUX_FRCV); /* set rcv flag */
mux_scan_next (); } } /* kick scanner */
else mux_sta[ln] = 0; /* disconnected */
} /* end for */
return SCPE_OK;
}
/* Unit service - transmit side */
t_stat muxo_svc (UNIT *uptr)
{
int32 c;
uint32 ln = uptr - muxl_unit; /* line # */
if (mux_ldsc[ln].conn) { /* connected? */
if (mux_ldsc[ln].xmte) { /* xmt enabled? */
c = mux_xbuf[ln] & 0177; /* get char */
if ((muxl_unit[ln].flags & UNIT_UC) && islower (c))
c = toupper (c); /* cvt to UC? */
tmxr_putc_ln (&mux_ldsc[ln], c); /* output char */
tmxr_poll_tx (&mux_desc); } /* poll xmt */
else { /* buf full */
tmxr_poll_tx (&mux_desc); /* poll xmt */
sim_activate (uptr, muxl_unit[ln].wait); /* wait */
return SCPE_OK; } }
if (mux_sta[ln] & MUX_SXIE) {
MUX_SETFLG (ln, MUX_FXMT); /* set flag */
mux_scan_next (); } /* kick scanner */
return SCPE_OK;
}
/* Kick scanner */
void mux_scan_next (void)
{
int32 i;
if (mux_slck) return; /* locked? */
for (i = 0; i < MUX_SCANMAX; i++) { /* scan flags */
mux_scan = (mux_scan + 1) & MUX_SCANMASK; /* next flag */
if (mux_flags[mux_scan]) { /* flag set? */
mux_slck = 1; /* lock scanner */
MUX_SETINT (mux_scan & MUX_FLAGMASK); /* request int */
return; }
}
return;
}
/* Reset routine */
t_stat mux_reset (DEVICE *dptr)
{
int32 i, t;
if (mux_dev.flags & DEV_DIS) /* master disabled? */
muxl_dev.flags = muxl_dev.flags | DEV_DIS; /* disable lines */
else muxl_dev.flags = muxl_dev.flags & ~DEV_DIS;
if (mux_unit.flags & UNIT_ATT) { /* master att? */
if (!sim_is_active (&mux_unit)) {
t = sim_rtcn_init (mux_unit.wait, TMR_MUX);
sim_activate (&mux_unit, t); } } /* activate */
else sim_cancel (&mux_unit); /* else stop */
for (i = 0; i < MUX_LINES; i++) {
mux_desc.ldsc[i] = &mux_ldsc[i];
mux_reset_ln (i); }
for (i = 0; i < MUX_FLAGS; i++) MUX_CLRINT (i); /* clear all ints */
return SCPE_OK;
}
/* Attach master unit */
t_stat mux_attach (UNIT *uptr, char *cptr)
{
t_stat r;
int32 t;
r = tmxr_attach (&mux_desc, uptr, cptr); /* attach */
if (r != SCPE_OK) return r; /* error */
t = sim_rtcn_init (mux_unit.wait, TMR_MUX);
sim_activate (uptr, t); /* start poll */
return SCPE_OK;
}
/* Detach master unit */
t_stat mux_detach (UNIT *uptr)
{
int32 i;
t_stat r;
r = tmxr_detach (&mux_desc, uptr); /* detach */
for (i = 0; i < MUX_LINES; i++) mux_ldsc[i].rcve = 0; /* disable rcv */
sim_cancel (uptr); /* stop poll */
return r;
}
/* Show summary processor */
t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc)
{
int32 i, t;
for (i = t = 0; i < MUX_LINES; i++) t = t + (mux_ldsc[i].conn != 0);
if (t == 1) fprintf (st, "1 connection");
else fprintf (st, "%d connections", t);
return SCPE_OK;
}
/* SHOW CONN/STAT processor */
t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc)
{
int32 i;
for (i = 0; (i < MUX_LINES) && (mux_ldsc[i].conn == 0); i++) ;
if (i < MUX_LINES) {
for (i = 0; i < MUX_LINES; i++) {
if (mux_ldsc[i].conn)
if (val) tmxr_fconns (st, &mux_ldsc[i], i);
else tmxr_fstats (st, &mux_ldsc[i], i); } }
else fprintf (st, "all disconnected\n");
return SCPE_OK;
}
/* Change number of lines */
t_stat mux_vlines (UNIT *uptr, int32 val, char *cptr, void *desc)
{
int32 newln, i, t;
t_stat r;
if (cptr == NULL) return SCPE_ARG;
newln = get_uint (cptr, 10, MUX_LINES, &r);
if ((r != SCPE_OK) || (newln == MUX_NUMLIN)) return r;
if (newln == 0) return SCPE_ARG;
if (newln < MUX_NUMLIN) {
for (i = newln, t = 0; i < MUX_NUMLIN; i++) t = t | mux_ldsc[i].conn;
if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
return SCPE_OK;
for (i = newln; i < MUX_NUMLIN; i++) {
if (mux_ldsc[i].conn) {
tmxr_msg (mux_ldsc[i].conn, "\r\nOperator disconnected line\r\n");
tmxr_reset_ln (&mux_ldsc[i]); } /* reset line */
muxl_unit[i].flags = muxl_unit[i].flags | UNIT_DIS;
mux_reset_ln (i); }
}
else {
for (i = MUX_NUMLIN; i < newln; i++) {
muxl_unit[i].flags = muxl_unit[i].flags & ~UNIT_DIS;
mux_reset_ln (i); }
}
MUX_NUMLIN = newln;
return SCPE_OK;
}
/* Reset an individual line */
void mux_reset_ln (int32 ln)
{
int32 flg = ln * MUX_FLAGS;
if (mux_ldsc[ln].conn) mux_sta[ln] = MUX_SCRO | MUX_SDSR;
else mux_sta[ln] = 0;
sim_cancel (&muxl_unit[ln]);
mux_flags[flg + MUX_FRCV] = 0;
mux_flags[flg + MUX_FXMT] = 0;
mux_flags[flg + MUX_FCRN] = 0;
mux_flags[flg + MUX_FCRF] = 0;
return;
}

290
SDS/sds_rad.c Normal file
View File

@@ -0,0 +1,290 @@
/* sds_rad.c: SDS 940 fixed head disk simulator
Copyright (c) 2001-2002, 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.
rad fixed head disk
The fixed head disk is a head-per-track disk, with up to four disks. Each
disk is divided into two logical units. Reads and writes cannot cross logical
unit boundaries. The fixed head disk transfers 12b characters, rather than 6b
characters. To minimize overhead, the disk is buffered in memory.
*/
#include "sds_defs.h"
#include <math.h>
/* Constants */
#define RAD_NUMWD 64 /* words/sector */
#define RAD_NUMSC 64 /* sectors/track */
#define RAD_NUMTR 64 /* tracks/log unit */
#define RAD_NUMLU 8 /* log units/ctrl */
#define RAD_SCSIZE (RAD_NUMLU*RAD_NUMTR*RAD_NUMSC) /* sectors/disk */
#define RAD_AMASK (RAD_SCSIZE - 1) /* sec addr mask */
#define RAD_SIZE (RAD_SCSIZE * RAD_NUMWD) /* words/disk */
#define RAD_GETLUN(x) ((x) / (RAD_NUMTR * RAD_NUMSC))
#define RAD_SCMASK (RAD_NUMSC - 1) /* sector mask */
#define RAD_TRSCMASK ((RAD_NUMSC * RAD_NUMTR) - 1) /* track/sec mask */
#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \
((double) RAD_NUMSC)))
extern uint32 xfr_req;
extern uint32 alert;
extern int32 stop_invins, stop_invdev, stop_inviop;
int32 rad_err = 0; /* error */
int32 rad_nobi = 0; /* !incr x track */
int32 rad_da = 0; /* disk address */
int32 rad_sba = 0; /* sec byte addr */
int32 rad_wrp = 0; /* write prot */
int32 rad_time = 2; /* time per 12b */
int32 rad_stopioe = 1; /* stop on error */
DSPT rad_tplt[] = { /* template */
{ 1, 0 }, { 1, DEV_OUT }, { 0, 0 } };
DEVICE rad_dev;
t_stat rad_svc (UNIT *uptr);
t_stat rad_reset (DEVICE *dptr);
t_stat rad_fill (int32 sba);
void rad_end_op (int32 fl);
int32 rad_adjda (int32 sba, int32 inc);
t_stat rad (uint32 fnc, uint32 inst, uint32 *dat);
/* RAD data structures
rad_dev device descriptor
rad_unit unit descriptor
rad_reg register list
*/
DIB rad_dib = { CHAN_E, DEV_RAD, XFR_RAD, rad_tplt, &rad };
UNIT rad_unit =
{ UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
RAD_SIZE) };
REG rad_reg[] = {
{ ORDATA (DA, rad_da, 15) },
{ GRDATA (SA, rad_sba, 8, 6, 1) },
{ FLDATA (BP, rad_sba, 0) },
{ FLDATA (XFR, xfr_req, XFR_V_RAD) },
{ FLDATA (NOBD, rad_nobi, 0) },
{ FLDATA (ERR, rad_err, 0) },
{ ORDATA (PROT, rad_wrp, 8) },
{ DRDATA (TIME, rad_time, 24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, rad_stopioe, 0) },
{ NULL } };
MTAB rad_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, NULL },
{ 0 } };
DEVICE rad_dev = {
"RAD", &rad_unit, rad_reg, rad_mod,
1, 8, 21, 1, 8, 24,
NULL, NULL, &rad_reset,
NULL, NULL, NULL,
&rad_dib, DEV_DISABLE };
/* Fixed head disk routine
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
*/
t_stat rad (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 t, lun, new_ch;
uint32 *wptr;
t_addr p;
switch (fnc) { /* case function */
case IO_CONN: /* connect */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */
if (CHC_GETCPW (inst) > 1) return STOP_INVIOP; /* 1-2 char/word? */
if (sim_is_active (&rad_unit) || (alert == POT_RADA)) /* protocol viol? */
return STOP_INVIOP;
rad_err = 0; /* clr error */
rad_sba = 0; /* clr sec bptr */
chan_set_flag (rad_dib.chan, CHF_12B); /* 12B mode */
t = (rad_da & RAD_SCMASK) - GET_SECTOR (rad_time * RAD_NUMWD);
if (t <= 0) t = t + RAD_NUMSC; /* seek */
sim_activate (&rad_unit, t * rad_time * (RAD_NUMWD / 2));
xfr_req = xfr_req & ~XFR_RAD; /* clr xfr flg */
break;
case IO_EOM1: /* EOM mode 1 */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */
if ((inst & 00600) == 00200) alert = POT_RADS; /* alert for sec */
else if ((inst & 06600) == 0) { /* alert for addr */
if (sim_is_active (&rad_unit)) rad_err = 1; /* busy? */
else {
rad_nobi = (inst & 01000)? 1: 0; /* save inc type */
alert = POT_RADA; } } /* set alert */
break;
case IO_DISC: /* disconnect */
rad_end_op (0); /* normal term */
if (inst & DEV_OUT) return rad_fill (rad_sba); /* fill write */
break;
case IO_WREOR: /* write eor */
rad_end_op (CHF_EOR); /* eor term */
return rad_fill (rad_sba); /* fill write */
case IO_SKS: /* SKS */
new_ch = I_GETSKCH (inst); /* sks chan */
if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */
t = I_GETSKCND (inst); /* sks cond */
lun = RAD_GETLUN (rad_da);
if (((t == 000) && !sim_is_active (&rad_unit)) || /* 10026: ready */
((t == 004) && !rad_err) || /* 11026: !err */
((t == 014) && !(rad_wrp & (1 << lun)))) /* 13026: !wrprot */
*dat = 1;
break;
case IO_READ: /* read */
p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); /* buf wd addr */
xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */
if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */
rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
CRETIOE (rad_stopioe, SCPE_UNATT); }
if (p >= rad_unit.capac) { /* end of disk? */
rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
return SCPE_OK; }
wptr = ((uint32 *) rad_unit.filebuf) + p; /* ptr to word */
if (rad_sba & 1) *dat = *wptr & 07777; /* odd byte? */
else *dat = (*wptr >> 12) & 07777; /* even */
rad_sba = rad_adjda (rad_sba, 1); /* next byte */
break;
case IO_WRITE:
p = (rad_da * RAD_NUMWD) + (rad_sba >> 1);
xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */
if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */
rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
CRETIOE (rad_stopioe, SCPE_UNATT); }
if ((p >= rad_unit.capac) || /* end of disk? */
(rad_wrp & (1 << RAD_GETLUN (rad_da)))) { /* write prot? */
rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */
return SCPE_OK; }
wptr = ((uint32 *) rad_unit.filebuf) + p; /* ptr to word */
if (rad_sba & 1) *wptr = *wptr | (*dat & 07777); /* odd byte? */
else *wptr = (*dat & 07777) << 12; /* even */
if (p >= rad_unit.hwmark) rad_unit.hwmark = p + 1; /* mark hiwater */
rad_sba = rad_adjda (rad_sba, 1); /* next byte */
break;
default:
CRETINS; }
return SCPE_OK;
}
/* PIN routine */
t_stat pin_rads (uint32 num, uint32 *dat)
{
*dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */
return SCPE_OK;
}
/* POT routine */
t_stat pot_rada (uint32 num, uint32 *dat)
{
rad_da = (*dat) & RAD_AMASK; /* save dsk addr */
return SCPE_OK;
}
/* Unit service and read/write */
t_stat rad_svc (UNIT *uptr)
{
xfr_req = xfr_req | XFR_RAD; /* set xfr req */
sim_activate (&rad_unit, rad_time); /* activate */
return SCPE_OK;
}
/* Fill incomplete sector */
t_stat rad_fill (int32 sba)
{
t_addr p = rad_da * RAD_NUMWD;
int32 wa = (sba + 1) >> 1; /* whole words */
if (sba && (p < rad_unit.capac)) { /* fill needed? */
for ( ; wa < RAD_NUMWD; wa++)
*(((uint32 *) rad_unit.filebuf) + p + wa) = 0;
if ((p + wa) >= rad_unit.hwmark) rad_unit.hwmark = p + wa + 1;
rad_adjda (sba, RAD_NUMWD - 1); } /* inc da */
return SCPE_OK;
}
/* Adjust disk address */
int32 rad_adjda (int32 sba, int32 inc)
{
sba = sba + inc;
if (rad_sba >= (RAD_NUMWD * 2)) { /* next sector? */
if (rad_nobi) rad_da = (rad_da & ~RAD_SCMASK) + /* within band? */
((rad_da + 1) & RAD_SCMASK);
else rad_da = (rad_da & ~RAD_TRSCMASK) + /* cross band */
((rad_da + 1) & RAD_TRSCMASK);
sba = 0; } /* start new sec */
return sba;
}
/* Terminate disk operation */
void rad_end_op (int32 fl)
{
if (fl) chan_set_flag (rad_dib.chan, fl); /* set flags */
xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */
sim_cancel (&rad_unit); /* stop */
if (fl & CHF_ERR) { /* error? */
chan_disc (rad_dib.chan); /* disconnect */
rad_err = 1; } /* set rad err */
return;
}
/* Reset routine */
t_stat rad_reset (DEVICE *dptr)
{
chan_disc (rad_dib.chan); /* disconnect */
rad_nobi = 0; /* clear state */
rad_da = 0;
rad_sba = 0;
xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */
sim_cancel (&rad_unit); /* deactivate */
return SCPE_OK;
}

555
SDS/sds_stddev.c Normal file
View File

@@ -0,0 +1,555 @@
/* sds_stddev.c: SDS 940 standard devices
Copyright (c) 2001-2002, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
ptr paper tape reader
ptp paper tape punch
tti keyboard
tto teleprinter
*/
#include "sds_defs.h"
#define TT_CR 052 /* typewriter */
#define TT_TB 072
#define TT_BS 032
extern uint32 xfr_req;
extern int32 stop_invins, stop_invdev, stop_inviop;
int32 ptr_sor = 0; /* start of rec */
int32 ptr_stopioe = 1; /* stop on err */
int32 ptp_ldr = 0; /* no leader */
int32 ptp_stopioe = 1;
DSPT std_tplt[] = { { 1, 0 }, { 0, 0 } }; /* template */
DEVICE ptr_dev, ptp_dev;
t_stat ptr (uint32 fnc, uint32 inst, uint32 *dat);
t_stat ptr_svc (UNIT *uptr);
t_stat ptr_reset (DEVICE *dptr);
t_stat ptr_boot (int32 unitno, DEVICE *dptr);
void ptr_set_err (void);
t_stat ptp (uint32 fnc, uint32 inst, uint32 *dat);
t_stat ptp_svc (UNIT *uptr);
t_stat ptp_reset (DEVICE *dptr);
t_stat ptp_out (int32 dat);
void ptp_set_err (void);
t_stat tti (uint32 fnc, uint32 inst, uint32 *dat);
t_stat tti_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr);
t_stat tto (uint32 fnc, uint32 inst, uint32 *dat);
t_stat tto_svc (UNIT *uptr);
t_stat tto_reset (DEVICE *dptr);
extern const char ascii_to_sds[128];
extern const char sds_to_ascii[64];
extern const char odd_par[64];
/* PTR data structures
ptr_dev PTR device descriptor
ptr_unit PTR unit
ptr_reg PTR register list
*/
DIB ptr_dib = { CHAN_W, DEV_PTR, XFR_PTR, std_tplt, &ptr };
UNIT ptr_unit = {
UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0),
SERIAL_IN_WAIT };
REG ptr_reg[] = {
{ ORDATA (BUF, ptr_unit.buf, 7) },
{ FLDATA (XFR, xfr_req, XFR_V_PTR) },
{ FLDATA (SOR, ptr_sor, 0) },
{ DRDATA (POS, ptr_unit.pos, 32), PV_LEFT },
{ DRDATA (TIME, ptr_unit.wait, 24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, ptr_stopioe, 0) },
{ NULL } };
MTAB ptr_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL",
"CHANNEL", &set_chan, &show_chan, NULL },
{ 0 } };
DEVICE ptr_dev = {
"PTR", &ptr_unit, ptr_reg, ptr_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptr_reset,
&ptr_boot, NULL, NULL,
&ptr_dib, DEV_DISABLE };
/* PTP data structures
ptp_dev PTP device descriptor
ptp_unit PTP unit
ptp_reg PTP register list
*/
DIB ptp_dib = { CHAN_W, DEV_PTP, XFR_PTP, std_tplt, &ptp };
UNIT ptp_unit = {
UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
REG ptp_reg[] = {
{ ORDATA (BUF, ptp_unit.buf, 7) },
{ FLDATA (XFR, xfr_req, XFR_V_PTP) },
{ FLDATA (LDR, ptp_ldr, 0) },
{ DRDATA (POS, ptp_unit.pos, 32), PV_LEFT },
{ DRDATA (TIME, ptp_unit.wait, 24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, ptp_stopioe, 0) },
{ NULL } };
MTAB ptp_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, NULL },
{ 0 } };
DEVICE ptp_dev = {
"PTP", &ptp_unit, ptp_reg, ptp_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptp_reset,
NULL, NULL, NULL,
&ptp_dib, DEV_DISABLE };
/* TTI data structures
tti_dev TTI device descriptor
tti_unit TTI unit
tti_reg TTI register list
*/
DIB tti_dib = { CHAN_W, DEV_TTI, XFR_TTI, std_tplt, &tti };
UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT };
REG tti_reg[] = {
{ ORDATA (BUF, tti_unit.buf, 6) },
{ FLDATA (XFR, xfr_req, XFR_V_TTI) },
{ DRDATA (POS, tti_unit.pos, 32), PV_LEFT },
{ DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT },
{ NULL } };
MTAB tti_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, &tti_dib },
{ 0 } };
DEVICE tti_dev = {
"TTI", &tti_unit, tti_reg, tti_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tti_reset,
NULL, NULL, NULL,
&tti_dib, 0 };
/* TTO data structures
tto_dev TTO device descriptor
tto_unit TTO unit
tto_reg TTO register list
*/
DIB tto_dib = { CHAN_W, DEV_TTO, XFR_TTO, std_tplt, &tto };
UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT };
REG tto_reg[] = {
{ ORDATA (BUF, tto_unit.buf, 6) },
{ FLDATA (XFR, xfr_req, XFR_V_TTO) },
{ DRDATA (POS, tto_unit.pos, 32), PV_LEFT },
{ DRDATA (TIME, tto_unit.wait, 24), REG_NZ + PV_LEFT },
{ NULL } };
MTAB tto_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL",
&set_chan, &show_chan, &tto_dib },
{ 0 } };
DEVICE tto_dev = {
"TTO", &tto_unit, tto_reg, tto_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tto_reset,
NULL, NULL, NULL,
&tto_dib, 0 };
/* Paper tape reader
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
The paper tape reader is a streaming input device. Once started, it
continues to read until disconnected. Leader before the current record
is ignored; leader after the current record sets channel EndOfRecord.
*/
t_stat ptr (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 new_ch;
switch (fnc) { /* case function */
case IO_CONN: /* connect */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != ptr_dib.chan) return SCPE_IERR; /* inv conn? err */
ptr_sor = 1; /* start of rec */
xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */
sim_activate (&ptr_unit, ptr_unit.wait); /* activate */
break;
case IO_DISC: /* disconnect */
ptr_sor = 0; /* clear state */
xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */
sim_cancel (&ptr_unit); /* deactivate unit */
break;
case IO_READ: /* read */
xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */
*dat = ptr_unit.buf & 077; /* get buf data */
if (ptr_unit.buf != odd_par[*dat]) /* good parity? */
chan_set_flag (ptr_dib.chan, CHF_ERR); /* no, error */
break;
case IO_WREOR: /* write eor */
break;
case IO_EOM1: /* EOM mode 1*/
case IO_WRITE: /* write */
CRETINS; } /* error */
return SCPE_OK;
}
/* Unit service */
t_stat ptr_svc (UNIT *uptr)
{
int32 temp;
if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */
ptr_set_err (); /* no, err, disc */
CRETIOE (ptr_stopioe, SCPE_UNATT); }
if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */
ptr_set_err (); /* yes, err, disc */
if (feof (ptr_unit.fileref)) { /* end of file? */
if (ptr_stopioe) printf ("PTR end of file\n");
else return SCPE_OK; }
else perror ("PTR I/O error"); /* I/O error */
clearerr (ptr_unit.fileref);
return SCPE_IOERR; }
ptr_unit.pos = ptr_unit.pos + 1; /* inc position */
if (temp) { /* leader/gap? */
ptr_unit.buf = temp & 0177; /* no, save char */
xfr_req = xfr_req | XFR_PTR; /* set xfr flag */
ptr_sor = 0; } /* in record */
else if (!ptr_sor) /* end record? */
chan_set_flag (ptr_dib.chan, CHF_EOR); /* ignore leader */
sim_activate (&ptr_unit, ptr_unit.wait); /* get next char */
return SCPE_OK;
}
/* Fatal error */
void ptr_set_err (void)
{
chan_set_flag (ptr_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */
chan_disc (ptr_dib.chan); /* disconnect */
xfr_req = xfr_req & ~XFR_PTR; /* clear xfr */
sim_cancel (&ptr_unit); /* stop */
return;
}
/* Reset routine */
t_stat ptr_reset (DEVICE *dptr)
{
chan_disc (ptr_dib.chan); /* disconnect */
ptr_sor = 0; /* clear state */
ptr_unit.buf = 0;
xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */
sim_cancel (&ptr_unit); /* deactivate unit */
return SCPE_OK;
}
/* Boot routine - simulate FILL console command */
t_stat ptr_boot (int32 unitno, DEVICE *dptr)
{
extern uint32 P, M[];
M[0] = 077777771; /* -7B */
M[1] = 007100000; /* LDX 0 */
M[2] = 000203604; /* EOM 3604B */
M[3] = 003200002; /* WIM 2 */
M[4] = 000100002; /* BRU 2 */
P = 1; /* start at 1 */
return SCPE_OK;
}
/* Paper tape punch
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
The paper tape punch is an asynchronous streaming output device. That is,
it can never cause a channel rate error; if no data is available, it waits.
*/
t_stat ptp (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 new_ch;
switch (fnc) { /* case function */
case IO_CONN:
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != ptp_dib.chan) return SCPE_IERR; /* inv conn? err */
ptp_ldr = (inst & CHC_NLDR)? 0: 1; /* leader? */
xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */
sim_activate (&ptp_unit, ptp_unit.wait); /* activate */
break;
case IO_DISC: /* disconnect */
ptp_ldr = 0; /* clear state */
xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */
sim_cancel (&ptp_unit); /* deactivate unit */
break;
case IO_WRITE: /* write */
xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */
sim_activate (&ptp_unit, ptp_unit.wait); /* activate */
ptp_unit.buf = odd_par[(*dat) & 077]; /* save data */
return ptp_out (ptp_unit.buf); /* punch w/ par */
case IO_WREOR: /* write eor */
break;
case IO_EOM1: /* EOM mode 1*/
case IO_READ: /* read */
CRETINS; } /* error */
return SCPE_OK;
}
/* Unit service */
t_stat ptp_svc (UNIT *uptr)
{
int32 i;
t_stat r = SCPE_OK;
if (ptp_ldr) { /* need leader? */
for (i = 0; i < 12; i++) { /* punch leader */
if (r = ptp_out (0)) break; } }
ptp_ldr = 0; /* clear flag */
chan_set_ordy (ptp_dib.chan); /* ptp ready */
return r;
}
/* Punch I/O */
t_stat ptp_out (int32 dat)
{
if ((ptp_unit.flags & UNIT_ATT) == 0) { /* attached? */
ptp_set_err (); /* no, disc, err */
CRETIOE (ptp_stopioe, SCPE_UNATT); }
if (putc (dat, ptp_unit.fileref) == EOF) { /* I/O error? */
ptp_set_err (); /* yes, disc, err */
perror ("PTP I/O error"); /* print msg */
clearerr (ptp_unit.fileref);
return SCPE_IOERR; }
ptp_unit.pos = ptp_unit.pos + 1; /* inc position */
return SCPE_OK;
}
/* Fatal error */
void ptp_set_err (void)
{
chan_set_flag (ptp_dib.chan, CHF_ERR); /* error */
chan_disc (ptp_dib.chan); /* disconnect */
xfr_req = xfr_req & ~XFR_PTP; /* clear xfr */
sim_cancel (&ptp_unit); /* stop */
return;
}
/* Reset routine */
t_stat ptp_reset (DEVICE *dptr)
{
chan_disc (ptp_dib.chan); /* disconnect */
ptp_ldr = 0; /* clear state */
ptp_unit.buf = 0;
xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */
sim_cancel (&ptp_unit); /* deactivate unit */
return SCPE_OK;
}
/* Typewriter input
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
The typewriter input is an asynchronous input device. That is, it can
never cause a channel rate error; if no data is available, it waits.
*/
t_stat tti (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 new_ch;
switch (fnc) { /* case function */
case IO_CONN: /* connect */
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != tti_dib.chan) return SCPE_IERR; /* inv conn? err */
xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */
break;
case IO_DISC: /* disconnect */
xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */
break;
case IO_READ: /* read */
xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */
*dat = tti_unit.buf; /* get buf data */
break;
case IO_WREOR: /* write eor */
break;
case IO_EOM1: /* EOM mode 1*/
case IO_WRITE: /* write */
CRETINS; } /* error */
return SCPE_OK;
}
/* 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? */
if (temp & SCPE_BREAK) return SCPE_OK; /* ignore break */
temp = temp & 0177;
tti_unit.pos = tti_unit.pos + 1;
if (ascii_to_sds[temp] >= 0) {
tti_unit.buf = ascii_to_sds[temp]; /* internal rep */
sim_putchar (temp); /* echo */
if (temp == 015) sim_putchar (012); /* lf after cr */
xfr_req = xfr_req | XFR_TTI; } /* set xfr flag */
else sim_putchar (007); /* ding! */
return SCPE_OK;
}
t_stat tti_reset (DEVICE *dptr)
{
chan_disc (tti_dib.chan); /* disconnect */
tti_unit.buf = 0; /* clear state */
xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */
sim_activate (&tti_unit, tti_unit.wait); /* start poll */
return SCPE_OK;
}
/* Typewriter output
conn - inst = EOM0, dat = NULL
eom1 - inst = EOM1, dat = NULL
sks - inst = SKS, dat = ptr to result
disc - inst = device number, dat = NULL
wreor - inst = device number, dat = NULL
read - inst = device number, dat = ptr to data
write - inst = device number, dat = ptr to result
The typewriter output is an asynchronous streaming output device. That is,
it can never cause a channel rate error; if no data is available, it waits.
*/
t_stat tto (uint32 fnc, uint32 inst, uint32 *dat)
{
int32 asc, new_ch;
switch (fnc) { /* case function */
case IO_CONN:
new_ch = I_GETEOCH (inst); /* get new chan */
if (new_ch != tto_dib.chan) return SCPE_IERR; /* inv conn? err */
xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */
sim_activate (&tto_unit, tto_unit.wait); /* activate */
break;
case IO_DISC: /* disconnect */
xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */
sim_cancel (&tto_unit); /* deactivate unit */
break;
case IO_WRITE: /* write */
xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */
tto_unit.buf = (*dat) & 077; /* save data */
if (tto_unit.buf == TT_CR) { /* control chars? */
sim_putchar (015); /* CR generates LF */
tto_unit.pos = tto_unit.pos + 1;
asc = 012; }
else if (tto_unit.buf == TT_BS) asc = '\b';
else if (tto_unit.buf == TT_TB) asc = '\t';
else asc = sds_to_ascii[tto_unit.buf]; /* translate */
tto_unit.pos = tto_unit.pos + 1; /* inc position */
sim_activate (&tto_unit, tto_unit.wait); /* activate */
return sim_putchar (asc); /* output */
case IO_WREOR: /* write eor */
break;
case IO_EOM1: /* EOM mode 1*/
case IO_READ: /* read */
CRETINS; } /* error */
return SCPE_OK;
}
/* Unit service */
t_stat tto_svc (UNIT *uptr)
{
chan_set_ordy (tto_dib.chan); /* tto rdy */
return SCPE_OK;
}
/* Reset routine */
t_stat tto_reset (DEVICE *dptr)
{
chan_disc (tto_dib.chan); /* disconnect */
tto_unit.buf = 0; /* clear state */
xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */
sim_cancel (&tto_unit); /* deactivate unit */
return SCPE_OK;
}

576
SDS/sds_sys.c Normal file
View File

@@ -0,0 +1,576 @@
/* sds_sys.c: SDS 940 simulator interface
Copyright (c) 2001-2002, 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 "sds_defs.h"
#include <ctype.h>
#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x)
extern DEVICE cpu_dev;
extern DEVICE chan_dev;
extern DEVICE ptr_dev;
extern DEVICE ptp_dev;
extern DEVICE tti_dev;
extern DEVICE tto_dev;
extern DEVICE lpt_dev;
extern DEVICE rtc_dev;
extern DEVICE drm_dev;
extern DEVICE rad_dev;
extern DEVICE dsk_dev;
extern DEVICE mt_dev;
extern DEVICE mux_dev, muxl_dev;
extern UNIT cpu_unit;
extern REG cpu_reg[];
extern uint32 M[MAXMEMSIZE];
/* 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[] = "SDS 940";
REG *sim_PC = &cpu_reg[0];
int32 sim_emax = 1;
DEVICE *sim_devices[] = {
&cpu_dev,
&chan_dev,
&ptr_dev,
&ptp_dev,
&tti_dev,
&tto_dev,
&lpt_dev,
&rtc_dev,
&drm_dev,
&rad_dev,
&dsk_dev,
&mt_dev,
&mux_dev,
&muxl_dev,
NULL };
const char *sim_stop_messages[] = {
"Unknown error",
"IO device not ready",
"HALT instruction",
"Breakpoint",
"Invalid IO device",
"Invalid instruction",
"Invalid I/O operation",
"Nested indirects exceed limit",
"Nested EXU's exceed limit",
"Memory management trap during interrupt",
"Memory management trap during trap",
"Trap instruction not BRM",
"RTC instruction not MIN or SKR",
"Interrupt vector zero",
"Runaway carriage control tape" };
/* Character conversion tables */
const char sds_to_ascii[64] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ' ', '=', '\'', ':', '>', '%', /* 17 = check mark */
'+', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', '?', '.', ')', '[', '<', '@', /* 37 = stop code */
'-', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', '!', '$', '*', ']', ';', '^', /* 57 = triangle */
'_', '/', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '?', ',', '(', '~', '\\', '#' }; /* 72 = rec mark */
/* 75 = squiggle, 77 = del */
const char ascii_to_sds[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */
032, 072, -1, -1, -1, 052, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
012, 052, -1, 077, 053, 017, -1, 014, /* 40 - 77 */
074, 034, 054, 020, 073, 040, 033, 061,
000, 001, 002, 003, 004, 005, 006, 007,
010, 011, 015, 056, 036, 013, 016, 072,
037, 021, 022, 023, 024, 025, 026, 027, /* 100 - 137 */
030, 031, 041, 042, 043, 044, 045, 046,
047, 050, 051, 062, 063, 064, 065, 066,
067, 070, 071, 035, 076, 055, 057, 060,
000, 021, 022, 023, 024, 025, 026, 027, /* 140 - 177 */
030, 031, 041, 042, 043, 044, 045, 046,
047, 050, 051, 062, 063, 064, 065, 066,
067, 070, 071, -1, -1, -1, -1, -1 };
const char odd_par[64] = {
0100, 0001, 0002, 0103, 0004, 0105, 0106, 0007,
0010, 0111, 0112, 0013, 0114, 0015, 0016, 0117,
0020, 0121, 0122, 0023, 0124, 0025, 0026, 0127,
0130, 0031, 0032, 0133, 0034, 0135, 0136, 0037,
0040, 0141, 0142, 0043, 0144, 0045, 0046, 0147,
0150, 0051, 0052, 0153, 0054, 0155, 0156, 0057,
0160, 0061, 0062, 0163, 0064, 0165, 0166, 0067,
0070, 0171, 0172, 0073, 0174, 0075, 0076, 0177 };
/* Load carriage control tape
A carriage control tape consists of entries of the form
(repeat count) column number,column number,column number,...
The CCT entries are stored in lpt_cct[0:lnt-1], lpt_ccl contains the
number of entries
*/
t_stat sim_load_cct (FILE *fileref)
{
int32 col, rpt, ptr, mask, cctbuf[CCT_LNT];
t_stat r;
extern int32 lpt_ccl, lpt_ccp, lpt_cct[CCT_LNT];
char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE];
ptr = 0;
for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */
mask = 0;
if (*cptr == '(') { /* repeat count? */
cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */
rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */
if (r != SCPE_OK) return SCPE_FMT; }
else rpt = 1;
while (*cptr != 0) { /* get col no's */
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
col = get_uint (gbuf, 10, 7, &r); /* column number */
if (r != SCPE_OK) return SCPE_FMT;
mask = mask | (1 << col); } /* set bit */
for ( ; rpt > 0; rpt--) { /* store vals */
if (ptr >= CCT_LNT) return SCPE_FMT;
cctbuf[ptr++] = mask; } }
if (ptr == 0) return SCPE_FMT;
lpt_ccl = ptr;
lpt_ccp = 0;
for (rpt = 0; rpt < lpt_ccl; rpt++) lpt_cct[rpt] = cctbuf[rpt];
return SCPE_OK;
}
/* Load command. -l means load a line printer tape. Otherwise, load
a bootstrap paper tape.
*/
int32 get_word (FILE *fileref, int32 *ldr)
{
int32 i, c, wd;
for (i = wd = 0; i < 4; ) {
if ((c = fgetc (fileref)) == EOF) return -1;
if ((c == 0) && (*ldr == 0)) return -1;
if (c == 0) continue;
*ldr = 0;
wd = (wd << 6) | (c & 077);
i++; }
return wd;
}
t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
{
int32 i, wd, buf[8];
int32 ldr = 1;
extern int32 sim_switches;
extern uint32 P;
if ((*cptr != 0) || (flag != 0)) return SCPE_ARG;
if (sim_switches & SWMASK ('L')) return sim_load_cct (fileref);
for (i = 0; i < 8; i++) { /* read boot */
if ((wd = get_word (fileref, &ldr)) < 0) return SCPE_FMT;
buf[i] = wd; }
if ((buf[0] != 023200012) || /* 2 = WIM 12,2 */
(buf[1] != 004100002) || /* 3 = BRX 2 */
(buf[2] != 007100011) || /* 4 = LDX 11 */
(buf[3] != 023200000) || /* 5 = WIM 0,2 */
(buf[4] != 004021000) || /* 6 = SKS 21000 */
(buf[5] != 004100005)) return SCPE_FMT; /* 7 = BRX 5 */
for (i = 0; i < 8; i++) M[i + 2] = buf[i]; /* copy boot */
if (I_GETOP (buf[6]) == BRU) P = buf[6] & VA_MASK;
for (i = buf[7] & VA_MASK; i <= VA_MASK; i++) { /* load data */
if ((wd = get_word (fileref, &ldr)) < 0) return SCPE_OK;
M[i] = wd; }
return SCPE_NXM;
}
/* Symbol tables */
#define I_V_FL 24 /* inst class */
#define I_M_FL 017 /* class mask */
#define I_V_NPN 000 /* no operand */
#define I_V_PPO 001 /* POP */
#define I_V_IOI 002 /* IO */
#define I_V_MRF 003 /* memory reference */
#define I_V_REG 004 /* register change */
#define I_V_SHF 005 /* shift */
#define I_V_OPO 006 /* opcode only */
#define I_V_CHC 007 /* chan cmd */
#define I_V_CHT 010 /* chan test */
#define I_NPN (I_V_NPN << I_V_FL)
#define I_PPO (I_V_PPO << I_V_FL)
#define I_IOI (I_V_IOI << I_V_FL)
#define I_MRF (I_V_MRF << I_V_FL)
#define I_REG (I_V_REG << I_V_FL)
#define I_SHF (I_V_SHF << I_V_FL)
#define I_OPO (I_V_OPO << I_V_FL)
#define I_CHC (I_V_CHC << I_V_FL)
#define I_CHT (I_V_CHT << I_V_FL)
static const int32 masks[] = {
037777777, 010000000, 017700000,
017740000, 017700000, 017774000,
017700000, 017377677, 027737677 };
static const char *opcode[] = {
"POP", "EIR", "DIR",
"ROV", "REO", "OTO", "OVT",
"IDT", "IET",
"BPT4", "BPT3", "BPT2", "BPT1",
"CLAB", "ABC", "BAC", "XAB",
"XXB", "STE", "LDE", "XEE",
"CLEAR",
"HLT", "BRU", "EOM", "EOD",
"MIY", "BRI", "MIW", "POT",
"ETR", "MRG", "EOR",
"NOP", "EXU",
"YIM", "WIM", "PIN",
"STA", "STB", "STX",
"SKS", "BRX", "BRM",
"SKE", "BRR", "SKB", "SKN",
"SUB", "ADD", "SUC", "ADC",
"SKR", "MIN", "XMA", "ADM",
"MUL", "DIV",
"SKM", "LDX", "SKA", "SKG",
"SKD", "LDB", "LDA", "EAX",
"BRU*",
"MIY*", "BRI*", "MIW*", "POT*",
"ETR*", "MRG*", "EOR*",
"EXU*",
"YIM*", "WIM*", "PIN*",
"STA*", "STB*", "STX*",
"BRX*", "BRM*",
"SKE*", "BRR*", "SKB*", "SKN*",
"SUB*", "ADD*", "SUC*", "ADC*",
"SKR*", "MIN*", "XMA*", "ADM*",
"MUL*", "DIV*",
"SKM*", "LDX*", "SKA*", "SKG*",
"SKD*", "LDB*", "LDA*", "EAX*",
"RSH", "RCY", "LRSH",
"LSH", "NOD", "LCY",
"RSH*", "LSH*",
"ALC", "DSC", "ASC", "TOP",
"CAT", "CET", "CZT", "CIT",
"CLA", "CLB", "CAB", /* encode only */
"CBA", "CBX", "CXB",
"XPO", "CXA", "CAX",
"CNA", "CLX", NULL,
NULL };
static const int32 opc_val[] = {
010000000+I_PPO, 000220002+I_NPN, 000220004+I_NPN,
002200001+I_NPN, 002200010+I_NPN, 002200100+I_NPN, 002200101+I_NPN,
004020002+I_NPN, 004020004+I_NPN,
004020040+I_NPN, 004020100+I_NPN, 004020200+I_NPN, 004020400+I_NPN,
004600003+I_NPN, 004600005+I_NPN, 004600012+I_NPN, 004600014+I_NPN,
004600060+I_NPN, 004600122+I_NPN, 004600140+I_NPN, 004600160+I_NPN,
024600003+I_NPN,
000000000+I_NPN, 000100000+I_MRF, 000200000+I_IOI, 000600000+I_IOI,
001000000+I_MRF, 001100000+I_MRF, 001200000+I_MRF, 001300000+I_MRF,
001400000+I_MRF, 001600000+I_MRF, 001700000+I_MRF,
002000000+I_OPO, 002300000+I_MRF,
003000000+I_MRF, 003200000+I_MRF, 003300000+I_MRF,
003500000+I_MRF, 003600000+I_MRF, 003700000+I_MRF,
004000000+I_IOI, 004100000+I_MRF, 004300000+I_MRF,
005000000+I_MRF, 005100000+I_MRF, 005200000+I_MRF, 005300000+I_MRF,
005400000+I_MRF, 005500000+I_MRF, 005600000+I_MRF, 005700000+I_MRF,
006000000+I_MRF, 006100000+I_MRF, 006200000+I_MRF, 006300000+I_MRF,
006400000+I_MRF, 006500000+I_MRF,
007000000+I_MRF, 007100000+I_MRF, 007200000+I_MRF, 007300000+I_MRF,
007400000+I_MRF, 007500000+I_MRF, 007600000+I_MRF, 007700000+I_MRF,
000140000+I_MRF,
001040000+I_MRF, 001140000+I_MRF, 001240000+I_MRF, 001340000+I_MRF,
001440000+I_MRF, 001640000+I_MRF, 001740000+I_MRF,
002340000+I_MRF,
003040000+I_MRF, 003240000+I_MRF, 003340000+I_MRF,
003540000+I_MRF, 003640000+I_MRF, 003740000+I_MRF,
004140000+I_MRF, 004340000+I_MRF,
005040000+I_MRF, 005140000+I_MRF, 005240000+I_MRF, 005340000+I_MRF,
005440000+I_MRF, 005540000+I_MRF, 005640000+I_MRF, 005740000+I_MRF,
006040000+I_MRF, 006140000+I_MRF, 006240000+I_MRF, 006340000+I_MRF,
006440000+I_MRF, 006540000+I_MRF,
007040000+I_MRF, 007140000+I_MRF, 007240000+I_MRF, 007340000+I_MRF,
007440000+I_MRF, 007540000+I_MRF, 007640000+I_MRF, 007740000+I_MRF,
006600000+I_SHF, 006620000+I_SHF, 006624000+I_SHF,
006700000+I_SHF, 006710000+I_SHF, 006720000+I_SHF,
006640000+I_MRF, 006740000+I_MRF,
000250000+I_CHC, 000200000+I_CHC, 000212000+I_CHC, 000214000+I_CHC,
004014000+I_CHT, 004011000+I_CHT, 004012000+I_CHT, 004010400+I_CHT,
004600001+I_REG, 004600002+I_REG, 004600004+I_REG,
004600010+I_REG, 004600020+I_REG, 004600040+I_REG,
004600100+I_REG, 004600200+I_REG, 004600400+I_REG,
004601000+I_REG, 024600000+I_REG, 004600000+I_REG,
-1 };
static const char *chname[] = {
"W", "Y", "C", "D", "E", "F", "G", "H", NULL };
/* Register change decode
Inputs:
*of = output stream
inst = mask bits
*/
void fprint_reg (FILE *of, int32 inst)
{
int32 i, j, sp;
inst = inst & ~(I_M_OP << I_V_OP); /* clear opcode */
for (i = sp = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
if ((j == I_V_REG) && (opc_val[i] & inst)) { /* reg class? */
inst = inst & ~opc_val[i]; /* mask bit set? */
fprintf (of, (sp? " %s": "%s"), opcode[i]);
sp = 1; } }
return;
}
/* Symbolic decode
Inputs:
*of = output stream
addr = current PC
*val = pointer to values
*uptr = pointer to unit
sw = switches
Outputs:
return = status code
*/
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
UNIT *uptr, int32 sw)
{
int32 i, j, ch;
int32 inst, op, tag, va, shf, nonop;
inst = val[0]; /* get inst */
op = I_GETOP (inst); /* get fields */
tag = (inst >> 21) & 06;
va = inst & VA_MASK;
shf = inst & I_SHFMSK;
nonop = inst & 077777;
if (sw & SWMASK ('A')) { /* ASCII? */
if (inst > 0377) return SCPE_ARG;
fprintf (of, FMTASC (inst & 0177));
return SCPE_OK; }
if (sw & SWMASK ('C')) { /* character? */
fprintf (of, "%c", sds_to_ascii[(inst >> 18) & 077]);
fprintf (of, "%c", sds_to_ascii[(inst >> 12) & 077]);
fprintf (of, "%c", sds_to_ascii[(inst >> 6) & 077]);
fprintf (of, "%c", sds_to_ascii[inst & 077]);
return SCPE_OK; }
if (!(sw & SWMASK ('M'))) return SCPE_ARG;
/* Instruction decode */
for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */
switch (j) { /* case on class */
case I_V_NPN: /* no operands */
case I_V_OPO: /* opcode only */
fprintf (of, "%s", opcode[i]); /* opcode */
break;
case I_V_SHF: /* shift */
fprintf (of, "%s %-o", opcode[i], shf);
if (tag) fprintf (of, ",%-o", tag);
break;
case I_V_PPO: /* pop */
fprintf (of, "POP %-o,%-o", op, nonop);
if (tag) fprintf (of, ",%-o", tag);
break;
case I_V_IOI: /* I/O */
fprintf (of, "%s %-o", opcode[i], nonop);
if (tag) fprintf (of, ",%-o", tag);
break;
case I_V_MRF: /* mem ref */
fprintf (of, "%s %-o", opcode[i], va);
if (tag) fprintf (of, ",%-o", tag);
break;
case I_V_REG: /* reg change */
fprint_reg (of, inst); /* decode */
break;
case I_V_CHC: /* chan cmd */
ch = I_GETEOCH (inst); /* get chan */
fprintf (of, "%s %s", opcode[i], chname[ch]);
break;
case I_V_CHT: /* chan test */
ch = I_GETSKCH (inst); /* get chan */
fprintf (of, "%s %s", opcode[i], chname[ch]);
break;
} /* end case */
return SCPE_OK;
} /* end if */
} /* end for */
return SCPE_ARG;
}
/* Get (optional) tag
Inputs:
*cptr = pointer to input string
*tag = pointer to tag
Outputs:
cptr = updated pointer to input string
*/
char *get_tag (char *cptr, t_value *tag)
{
char *tptr, gbuf[CBUFSIZE];
t_stat r;
tptr = get_glyph (cptr, gbuf, 0); /* get next field */
*tag = get_uint (gbuf, 8, 07, &r) << I_V_TAG; /* parse */
if (r == SCPE_OK) return tptr; /* ok? advance */
*tag = 0;
return cptr; /* no change */
}
/* 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 i, j, k;
t_value d, tag;
t_stat r;
char gbuf[CBUFSIZE];
while (isspace (*cptr)) cptr++;
for (i = 1; (i < 4) && (cptr[i] != 0); i++)
if (cptr[i] == 0) for (j = i + 1; j <= 4; j++) cptr[j] = 0;
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
val[0] = (t_value) cptr[0] | 0200;
return SCPE_OK; }
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* string? */
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
for (i = j = 0, val[0] = 0; i < 4; i++) {
if (cptr[i] == 0) j = 1; /* latch str end */
k = ascii_to_sds[cptr[i] & 0177]; /* cvt char */
if (j || (k < 0)) k = 0; /* bad, end? spc */
val[0] = (val[0] << 6) | k; }
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 = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
switch (j) { /* case on class */
case I_V_NPN: case I_V_OPO: /* opcode only */
break;
case I_V_SHF: /* shift */
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
d = get_uint (gbuf, 8, I_SHFMSK, &r); /* shift count */
if (r != SCPE_OK) return SCPE_ARG;
cptr = get_tag (cptr, &tag); /* get opt tag */
val[0] = val[0] | d | tag;
break;
case I_V_PPO: /* pop */
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
d = get_uint (gbuf, 8, 077, &r); /* opcode */
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | d; /* fall thru */
case I_V_IOI: /* I/O */
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
d = get_uint (gbuf, 8, 077777, &r); /* 15b address */
if (r != SCPE_OK) return SCPE_ARG;
cptr = get_tag (cptr, &tag); /* get opt tag */
val[0] = val[0] | d | tag;
break;
case I_V_MRF: /* mem ref */
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
d = get_uint (gbuf, 8, VA_MASK, &r); /* virt address */
if (r != SCPE_OK) return SCPE_ARG;
cptr = get_tag (cptr, &tag); /* get opt tag */
val[0] = val[0] | d | tag;
break;
case I_V_REG: /* register */
for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0;
cptr = get_glyph (cptr, gbuf, 0)) {
for (i = 0; (opcode[i] != NULL) &&
(strcmp (opcode[i], gbuf) != 0); i++) ;
if (opcode[i] != NULL) {
k = opc_val[i] & DMASK;;
if (I_GETOP (k) != RCH) return SCPE_ARG;
val[0] = val[0] | k; }
else {
d = get_uint (gbuf, 8, 077777, &r);
if (r != SCPE_OK) return SCPE_ARG;
else val[0] = val[0] | d; } }
break;
case I_V_CHC: case I_V_CHT: /* channel */
cptr = get_glyph (cptr, gbuf, ','); /* get next field */
for (i = 0; (chname[i] != NULL) && (strcmp (chname[i], gbuf) != 0);
i++);
if (chname[i] != NULL) d = i; /* named chan */
else {
d = get_uint (gbuf, 8, NUM_CHAN - 1, &r);
if (r != SCPE_OK) return SCPE_ARG; } /* numbered chan */
val[0] = val[0] | ((j == I_V_CHC)? I_SETEOCH (d): I_SETSKCH (d));
break; } /* end case */
if (*cptr != 0) return SCPE_ARG; /* junk at end? */
return SCPE_OK;
}