1
0
mirror of https://github.com/simh/simh.git synced 2026-01-25 19:56:25 +00:00

Notes For V2.8

1. New Features

1.1 Directory and documentation

- Only common files (SCP and libraries) are in the top level
  directory.  Individual simulator files are in their individual
  directories.
- simh_doc.txt has been split up.  simh_doc.txt now documents
  only SCP.  The individual simulators are documented in separate
  text files in their own directories.
- mingw_build.bat is a batch file for the MINGW/gcc environment
  that will build all the simulators, assuming the root directory
  structure is at c:\sim.
- Makefile is a UNIX make file for the gcc environment that will
  build all the simulators, assuming the root directory is at
  c:\sim.

1.2 SCP

- DO <file name> executes the SCP commands in the specified file.
- Replicated registers in unit structures can now be declared as
  arrays for examine, modify, save, and restore.  Most replicated
  unit registers (for example, mag tape position registers) have
  been changed to arrays.
- The ADD/REMOVE commands have been replaced by SET unit ONLINE
  and SET unit OFFLINE, respectively.
- Register names that are unique within an entire simulator do
  not have to be prefaced with the device name.
- The ATTACH command can attach files read only, either under
  user option (-r), or because the attached file is ready only.
- The SET/SHOW capabilities have been extended.  New forms include:

	SET <dev> param{=value}{ param ...}
	SET <unit> param{=value}{ param ...}
	SHOW <dev> {param param ...}
	SHOW <unit> {param param ...}

- Multiple breakpoints have been implemented.  Breakpoints are
  set/cleared/displayed by:

	BREAK addr_list{[count]}
	NOBREAK addr_list
	SHOW BREAK addr_list

1.3 PDP-11 simulator

- Unibus map implemented, with 22b RP controller (URH70) or 18b
  RP controller (URH11) (in debug).
- All DMA peripherals rewritten to use map.
- Many peripherals modified for source sharing with VAX.
- RQDX3 implemented.
- Bugs fixed in RK11 and RL11 write check.

1.4 PDP-10 simulator

- ITS 1-proceed implemented.
- Bugs fixed in ITS PC sampling and LPMR

1.5 18b PDP simulator

- Interrupts split out to multiple levels to allow easier
  expansion.

1.5 IBM System 3 Simulator

- Written by Charles (Dutch) Owen.

1.6 VAX Simulator (in debug)

- Simulates MicroVAX 3800 (KA655) with 16MB-64MB memory, RQDX3,
  RLV12, TSV11, DZV11, LPV11, PCV11.
- CDROM capability has been added to the RQDX3, to allow testing
  with VMS hobbyist images.

1.7 SDS 940 Simulator (not tested)

- Simulates SDS 940, 16K-64K memory, fixed and moving head
  disk, magtape, line printer, console.

1.8 Altair Z80

- Revised from Charles (Dutch) Owen's original by Peter Schorn.
- MITS 8080 with full Z80 simulation.
- 4K and 8K BASIC packages, Prolog package.

1.9 Interdata

The I4 simulator has been withdrawn for major rework.  Look for
a complete 16b/32b Interdata simulator sometime next year.

2. Release Notes

2.1 SCP

SCP now allows replicated registers in unit structures to be
modelled as arrays.  All replicated register declarations have
been replaced by register array declarations.  As a result,
save files from prior revisions will generate errors after
restoring main memory.

2.2 PDP-11

The Unibus map code is in debug.  The map was implemented primarily
to allow source sharing with the VAX, which requires a DMA map.
DMA devices work correctly with the Unibus map disabled.

The RQDX3 simulator has run a complete RSTS/E SYSGEN, with multiple
drives, and booted the completed system from scratch.

2.3 VAX

The VAX simulator will run the boot code up to the >>> prompt.  It
can successfully process a SHOW DEVICE command.  It runs the HCORE
instruction diagnostic.  It can boot the hobbyist CD through SYSBOOT
and through the date/time dialog and restore the hobbyist CD, using
standalone backup.  On the boot of the restored disk, it gets to the
date/time dialog, and then crashes.

2.4 SDS 940

The SDS 940 is untested, awaiting real code.

2.5 GCC Optimization

At -O2 and above, GCC does not correctly compile the simulators which
use setjmp-longjmp (PDP-11, PDP-10, VAX).  A working hypothesis is
that optimized state maintained in registers is being used in the
setjmp processing routine.  On the PDP-11 and PDP-10, all of this
state has been either made global, or volatile, to encourage GCC to
keep the state up to date in memory.  The VAX is still vulnerable.

3. Work list

3.1 SCP

- Better ENABLE/DISABLE.

3.2 PDP-11 RQDX3

Software mapped mode, RCT read simulation, VMS debug.
This commit is contained in:
Bob Supnik
2001-12-26 09:38:00 -08:00
committed by Mark Pizzolato
parent 654937fc88
commit 701f0fe028
145 changed files with 23190 additions and 9807 deletions

1259
PDP11/pdp11_cis.c Normal file

File diff suppressed because it is too large Load Diff

2322
PDP11/pdp11_cpu.c Normal file

File diff suppressed because it is too large Load Diff

454
PDP11/pdp11_defs.h Normal file
View File

@@ -0,0 +1,454 @@
/* pdp11_defs.h: PDP-11 simulator definitions
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
The author gratefully acknowledges the help of Max Burnet, Megan Gentry,
and John Wilson in resolving questions about the PDP-11
09-Nov-01 RMS Added bus map support
07-Nov-01 RMS Added RQDX3 support
26-Oct-01 RMS Added symbolic definitions for IO page
19-Oct-01 RMS Added DZ definitions
15-Oct-01 RMS Added logging capabilities
07-Sep-01 RMS Revised for multilevel interrupts
01-Jun-01 RMS Added DZ11 support
23-Apr-01 RMS Added RK611 support
05-Apr-01 RMS Added TS11/TSV05 support
10-Feb-01 RMS Added DECtape support
*/
#include "sim_defs.h" /* simulator defns */
#include <setjmp.h>
/* Architectural constants */
#define STKLIM 0400 /* stack limit */
#define VASIZE 0200000 /* 2**16 */
#define VAMASK (VASIZE - 1) /* 2**16 - 1 */
#define INIMEMSIZE 001000000 /* 2**18 */
#define IOPAGEBASE 017760000 /* 2**22 - 2**13 */
#define MAXMEMSIZE 020000000 /* 2**22 */
#define PAMASK (MAXMEMSIZE - 1) /* 2**22 - 1 */
#define MEMSIZE (cpu_unit.capac)
#define ADDR_IS_MEM(x) (((t_addr) (x)) < MEMSIZE)
#define DMASK 0177777
/* Protection modes */
#define KERNEL 0
#define SUPER 1
#define UNUSED 2
#define USER 3
/* I/O access modes */
#define READ 0
#define READC 1 /* read console */
#define WRITE 2
#define WRITEC 3 /* write console */
#define WRITEB 4
/* PSW */
#define PSW_V_C 0 /* condition codes */
#define PSW_V_V 1
#define PSW_V_Z 2
#define PSW_V_N 3
#define PSW_V_TBIT 4 /* trace trap */
#define PSW_V_IPL 5 /* int priority */
#define PSW_V_RS 11 /* register set */
#define PSW_V_PM 12 /* previous mode */
#define PSW_V_CM 14 /* current mode */
#define PSW_RW 0174357 /* read/write bits */
/* FPS */
#define FPS_V_C 0 /* condition codes */
#define FPS_V_V 1
#define FPS_V_Z 2
#define FPS_V_N 3
#define FPS_V_T 5 /* truncate */
#define FPS_V_L 6 /* long */
#define FPS_V_D 7 /* double */
#define FPS_V_IC 8 /* ic err int */
#define FPS_V_IV 9 /* overflo err int */
#define FPS_V_IU 10 /* underflo err int */
#define FPS_V_IUV 11 /* undef var err int */
#define FPS_V_ID 14 /* int disable */
#define FPS_V_ER 15 /* error */
/* PIRQ */
#define PIRQ_PIR1 0001000
#define PIRQ_PIR2 0002000
#define PIRQ_PIR3 0004000
#define PIRQ_PIR4 0010000
#define PIRQ_PIR5 0020000
#define PIRQ_PIR6 0040000
#define PIRQ_PIR7 0100000
#define PIRQ_IMP 0177356 /* implemented bits */
#define PIRQ_RW 0177000 /* read/write bits */
/* MMR0 */
#define MMR0_MME 0000001 /* mem mgt enable */
#define MMR0_V_PAGE 1 /* offset to pageno */
#define MMR0_RO 0020000 /* read only error */
#define MMR0_PL 0040000 /* page lnt error */
#define MMR0_NR 0100000 /* no access error */
#define MMR0_FREEZE 0160000 /* if set, no update */
#define MMR0_IMP 0160177 /* implemented bits */
#define MMR0_RW 0160001 /* read/write bits */
/* MMR3 */
#define MMR3_UDS 001 /* user dspace enbl */
#define MMR3_SDS 002 /* super dspace enbl */
#define MMR3_KDS 004 /* krnl dspace enbl */
#define MMR3_CSM 010 /* CSM enable */
#define MMR3_M22E 020 /* 22b mem mgt enbl */
#define MMR3_BME 040 /* DMA bus map enbl */
#define MMR3_IMP 077 /* implemented bits */
#define MMR3_RW 077 /* read/write bits */
/* PDR */
#define PDR_NR 0000002 /* non-resident ACF */
#define PDR_ED 0000010 /* expansion dir */
#define PDR_W 0000100 /* written flag */
#define PDR_PLF 0077400 /* page lnt field */
#define PDR_IMP 0177516 /* implemented bits */
#define PDR_RW 0177416 /* read/write bits */
/* Virtual address */
#define VA_DF 0017777 /* displacement */
#define VA_BN 0017700 /* block number */
#define VA_V_APF 13 /* offset to APF */
#define VA_V_DS 16 /* offset to space */
#define VA_V_MODE 17 /* offset to mode */
#define VA_DS (1u << VA_V_DS) /* data space flag */
/* Unibus map (if present) */
#define UBM_LNT_LW 32 /* size in LW */
#define UBM_V_PN 13 /* page number */
#define UBM_M_PN 037
#define UBM_V_OFF 0 /* offset */
#define UBM_M_OFF 017777
#define UBM_GETPN(x) (((x) >> UBM_V_PN) & UBM_M_PN)
#define UBM_GETOFF(x) ((x) & UBM_M_OFF)
/* CPUERR */
#define CPUE_RED 0004 /* red stack */
#define CPUE_YEL 0010 /* yellow stack */
#define CPUE_TMO 0020 /* IO page nxm */
#define CPUE_NXM 0040 /* memory nxm */
#define CPUE_ODD 0100 /* odd address */
#define CPUE_HALT 0200 /* HALT not kernel */
#define CPUE_IMP 0374 /* implemented bits */
/* Maintenance register */
#define MAINT_V_UQ 9 /* Q/U flag */
#define MAINT_Q (0 << MAINT_V_UQ) /* Qbus */
#define MAINT_U (1 << MAINT_V_UQ)
#define MAINT_V_FPA 8 /* FPA flag */
#define MAINT_NOFPA (0 << MAINT_V_FPA)
#define MAINT_FPA (1 << MAINT_V_FPA)
#define MAINT_V_TYP 4 /* system type */
#define MAINT_KDJ (4 << MAINT_V_TYP)
/* Floating point accumulators */
struct fpac {
unsigned int32 l; /* low 32b */
unsigned int32 h; /* high 32b */
};
typedef struct fpac fpac_t;
/* Device CSRs */
#define CSR_V_GO 0 /* go */
#define CSR_V_IE 6 /* interrupt enable */
#define CSR_V_DONE 7 /* done */
#define CSR_V_BUSY 11 /* busy */
#define CSR_V_ERR 15 /* error */
#define CSR_GO (1u << CSR_V_GO)
#define CSR_IE (1u << CSR_V_IE)
#define CSR_DONE (1u << CSR_V_DONE)
#define CSR_BUSY (1u << CSR_V_BUSY)
#define CSR_ERR (1u << CSR_V_ERR)
/* Trap masks, descending priority order, following J-11
An interrupt summary bit is kept with traps, to minimize overhead
*/
#define TRAP_V_RED 0 /* red stk abort 4 */
#define TRAP_V_ODD 1 /* odd address 4 */
#define TRAP_V_MME 2 /* mem mgt 250 */
#define TRAP_V_NXM 3 /* nx memory 4 */
#define TRAP_V_PAR 4 /* parity err 114 */
#define TRAP_V_PRV 5 /* priv inst 4 */
#define TRAP_V_ILL 6 /* illegal inst 10 */
#define TRAP_V_BPT 7 /* BPT 14 */
#define TRAP_V_IOT 8 /* IOT 20 */
#define TRAP_V_EMT 9 /* EMT 30 */
#define TRAP_V_TRAP 10 /* TRAP 34 */
#define TRAP_V_TRC 11 /* T bit 14 */
#define TRAP_V_YEL 12 /* stack 4 */
#define TRAP_V_PWRFL 13 /* power fail 24 */
#define TRAP_V_FPE 14 /* fpe 244 */
#define TRAP_V_MAX 15 /* intr = max trp # */
#define TRAP_RED (1u << TRAP_V_RED)
#define TRAP_ODD (1u << TRAP_V_ODD)
#define TRAP_MME (1u << TRAP_V_MME)
#define TRAP_NXM (1u << TRAP_V_NXM)
#define TRAP_PAR (1u << TRAP_V_PAR)
#define TRAP_PRV (1u << TRAP_V_PRV)
#define TRAP_ILL (1u << TRAP_V_ILL)
#define TRAP_BPT (1u << TRAP_V_BPT)
#define TRAP_IOT (1u << TRAP_V_IOT)
#define TRAP_EMT (1u << TRAP_V_EMT)
#define TRAP_TRAP (1u << TRAP_V_TRAP)
#define TRAP_TRC (1u << TRAP_V_TRC)
#define TRAP_YEL (1u << TRAP_V_YEL)
#define TRAP_PWRFL (1u << TRAP_V_PWRFL)
#define TRAP_FPE (1u << TRAP_V_FPE)
#define TRAP_INT (1u << TRAP_V_MAX)
#define TRAP_ALL ((1u << TRAP_V_MAX) - 1) /* all traps */
#define VEC_RED 0004 /* trap vectors */
#define VEC_ODD 0004
#define VEC_MME 0250
#define VEC_NXM 0004
#define VEC_PAR 0114
#define VEC_PRV 0004
#define VEC_ILL 0010
#define VEC_BPT 0014
#define VEC_IOT 0020
#define VEC_EMT 0030
#define VEC_TRAP 0034
#define VEC_TRC 0014
#define VEC_YEL 0004
#define VEC_PWRFL 0024
#define VEC_FPE 0244
/* Simulator stop codes; codes 1:TRAP_V_MAX correspond to traps 0:TRAPMAX-1 */
#define STOP_HALT (TRAP_V_MAX + 1) /* HALT instruction */
#define STOP_IBKPT (TRAP_V_MAX + 2) /* instruction bkpt */
#define STOP_WAIT (TRAP_V_MAX + 3) /* wait, no events */
#define STOP_VECABORT (TRAP_V_MAX + 4) /* abort vector read */
#define STOP_SPABORT (TRAP_V_MAX + 5) /* abort trap push */
#define STOP_RQ (TRAP_V_MAX + 6) /* RQDX3 panic */
#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */
/* DZ11 parameters */
#define DZ_MUXES 1 /* # of muxes */
#define DZ_LINES 8 /* lines per mux */
/* I/O page layout */
#define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */
#define IOLN_DZ (010 * DZ_MUXES)
#define IOBA_UBM (IOPAGEBASE + 010200) /* Unibus map */
#define IOLN_UBM (UBM_LNT_LW * sizeof (int32))
#define IOBA_RQ (IOPAGEBASE + 012150) /* RQDX3 */
#define IOLN_RQ 004
#define IOBA_APR (IOPAGEBASE + 012200) /* APRs */
#define IOLN_APR 0200
#define IOBA_MMR3 (IOPAGEBASE + 012516) /* MMR3 */
#define IOLN_MMR3 002
#define IOBA_TM (IOPAGEBASE + 012520) /* TM11 */
#define IOLN_TM 014
#define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */
#define IOLN_TS 004
#define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */
#define IOLN_RL 012
#define IOBA_RP (IOPAGEBASE + 016700) /* RP/RM */
#define IOLN_RP 054
#define IOBA_RX (IOPAGEBASE + 017170) /* RX11 */
#define IOLN_RX 004
#define IOBA_TC (IOPAGEBASE + 017340) /* TC11 */
#define IOLN_TC 012
#define IOBA_RK (IOPAGEBASE + 017400) /* RK11 */
#define IOLN_RK 020
#define IOBA_RK6 (IOPAGEBASE + 017440) /* RK611 */
#define IOLN_RK6 040
#define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */
#define IOLN_LPT 004
#define IOBA_STD (IOPAGEBASE + 017546) /* KW11L, DL11, PC11 */
#define IOLN_STD 022
#define IOBA_SRMM (IOPAGEBASE + 017570) /* SR, MMR0-2 */
#define IOLN_SRMM 010
#define IOBA_APR1 (IOPAGEBASE + 017600) /* APRs */
#define IOLN_APR1 0100
#define IOBA_CPU (IOPAGEBASE + 017740) /* CPU reg */
#define IOLN_CPU 040
/* Interrupt assignments; within each level, priority is right to left */
#define IPL_HLVL 8 /* # int levels */
#define INT_V_PIR7 0 /* BR7 */
#define INT_V_CLK 0 /* BR6 */
#define INT_V_DTA 1
#define INT_V_PIR6 2
#define INT_V_RK 0 /* BR5 */
#define INT_V_RL 1
#define INT_V_RX 2
#define INT_V_TM 3
#define INT_V_RP 4
#define INT_V_TS 5
#define INT_V_HK 6
#define INT_V_RQ 7
#define INT_V_DZRX 8
#define INT_V_DZTX 9
#define INT_V_PIR5 10
#define INT_V_TTI 0 /* BR4 */
#define INT_V_TTO 1
#define INT_V_PTR 2
#define INT_V_PTP 3
#define INT_V_LPT 4
#define INT_V_PIR4 5
#define INT_V_PIR3 0 /* BR3 */
#define INT_V_PIR2 0 /* BR2 */
#define INT_V_PIR1 0 /* BR1 */
#define INT_PIR7 (1u << INT_V_PIR7)
#define INT_CLK (1u << INT_V_CLK)
#define INT_DTA (1u << INT_V_DTA)
#define INT_PIR6 (1u << INT_V_PIR6)
#define INT_RK (1u << INT_V_RK)
#define INT_RL (1u << INT_V_RL)
#define INT_RX (1u << INT_V_RX)
#define INT_TM (1u << INT_V_TM)
#define INT_RP (1u << INT_V_RP)
#define INT_TS (1u << INT_V_TS)
#define INT_RK6 (1u << INT_V_HK)
#define INT_RQ (1u << INT_V_RQ)
#define INT_DZRX (1u << INT_V_DZRX)
#define INT_DZTX (1u << INT_V_DZTX)
#define INT_PIR5 (1u << INT_V_PIR5)
#define INT_PTR (1u << INT_V_PTR)
#define INT_PTP (1u << INT_V_PTP)
#define INT_TTI (1u << INT_V_TTI)
#define INT_TTO (1u << INT_V_TTO)
#define INT_LPT (1u << INT_V_LPT)
#define INT_PIR4 (1u << INT_V_PIR4)
#define INT_PIR3 (1u << INT_V_PIR3)
#define INT_PIR2 (1u << INT_V_PIR2)
#define INT_PIR1 (1u << INT_V_PIR1)
#define IPL_CLK 6 /* int pri levels */
#define IPL_DTA 6
#define IPL_RK 5
#define IPL_RL 5
#define IPL_RX 5
#define IPL_TM 5
#define IPL_RP 5
#define IPL_TS 5
#define IPL_RK6 5
#define IPL_RQ 5
#define IPL_DZRX 5
#define IPL_DZTX 5
#define IPL_PTR 4
#define IPL_PTP 4
#define IPL_TTI 4
#define IPL_TTO 4
#define IPL_LPT 4
#define IPL_PIR7 7
#define IPL_PIR6 6
#define IPL_PIR5 5
#define IPL_PIR4 4
#define IPL_PIR3 3
#define IPL_PIR2 2
#define IPL_PIR1 1
#define VEC_Q 0000 /* vector base */
#define VEC_PIRQ 0240
#define VEC_TTI 0060
#define VEC_TTO 0064
#define VEC_PTR 0070
#define VEC_PTP 0074
#define VEC_CLK 0100
#define VEC_RQ 0154
#define VEC_RL 0160
#define VEC_LPT 0200
#define VEC_RK6 0210
#define VEC_RK 0220
#define VEC_DTA 0214
#define VEC_TM 0224
#define VEC_TS 0224
#define VEC_RP 0254
#define VEC_RX 0264
#define VEC_DZRX 0310
#define VEC_DZTX 0314
/* Interrupt macros */
#define IREQ(dv) int_req[IPL_##dv]
#define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv)
#define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv)
/* CPU and FPU macros */
#define update_MM ((MMR0 & (MMR0_FREEZE + MMR0_MME)) == MMR0_MME)
#define setTRAP(name) trap_req = trap_req | (name)
#define setCPUERR(name) CPUERR = CPUERR | (name)
#define ABORT(val) longjmp (save_env, (val))
#define SP R[6]
#define PC R[7]
/* Logging */
#define LOG_CPU_I 00000001
#define LOG_RP 00000010
#define LOG_TS 00000020
#define LOG_RQ 00000040
#define LOG_TC_MS 00000100
#define LOG_TC_RW 00000200
#define LOG_TC_RA 00000400
#define LOG_TC_BL 00001000
#define DBG_LOG(x) (sim_log && (cpu_log & (x)))
/* Function prototypes */
#define QB 0 /* Q22 native */
#define UB 1 /* Unibus */
t_bool Map_Addr (t_addr qa, t_addr *ma);
int32 Map_ReadB (t_addr ba, int32 bc, uint8 *buf, t_bool ub);
int32 Map_ReadW (t_addr ba, int32 bc, uint16 *buf, t_bool ub);
int32 Map_WriteB (t_addr ba, int32 bc, uint8 *buf, t_bool ub);
int32 Map_WriteW (t_addr ba, int32 bc, uint16 *buf, t_bool ub);

868
PDP11/pdp11_doc.txt Normal file
View File

@@ -0,0 +1,868 @@
To: Users
From: Bob Supnik
Subj: PDP-11 Simulator Usage
Date: 1-Dec-01
COPYRIGHT NOTICE
The following copyright notice applies to both the SIMH source and binary:
Original code published in 1993-2001, written by Robert M Supnik
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
This memorandum documents the PDP-11 simulator.
1. Simulator Files
sim/ sim_defs.h
sim_sock.h
sim_tmxr.h
dec_dz.h
dec_mscp.h
dec_uqssp.h
scp.c
scp_tty.c
sim_rev.c
sim_sock.c
sim_tmxr.c
sim/pdp11/ pdp11_defs.h
pdp11_cpu.c
pdp11_dz.c
pdp11_fp.c
pdp11_io.c
pdp11_lp.c
pdp11_rk.c
pdp11_rl.c
pdp11_rp.c
pdp11_rq.c
pdp11_rx.c
pdp11_stddev.c
pdp11_sys.c
pdp11_tc.c
pdp11_tm.c
pdp11_ts.c
2. PDP-11 Features
The PDP-11 simulator is configured as follows:
device simulates
name(s)
CPU J-11 CPU with 256KB of memory
- FP11 floating point unit (FPA)
- CIS11 commercial instruction set (CIS, off by default)
PTR,PTP PC11 paper tape reader/punch
TTI,TTO DL11 console terminal
LPT LP11 line printer
CLK line frequency clock
DZ DZ11 8-line terminal multiplexor
RK RK11/RK05 cartridge disk controller with eight drives
RL RLV12/RL01(2) cartridge disk controller with four drives
RP RM02/03/05/80, RP04/05/06/07 Massbus style controller
with eight drives
RQ RQDX3 MSCP controller with four drives
RX RX11/RX01 floppy disk controller with two drives
TC TC11/TU56 DECtape controller with eight drives
TM TM11/TU10 magnetic tape controller with eight drives
TS TS11/TSV05 magnetic tape controller with one drive
The DZ, RK, RL, RP, RQ, RX, TC, TM, and TS devices can be DISABLEd. The
PDP-11 can support either a TM11 or a TS11, but not both, since they use
the same I/O addresses. The simulator defaults to the TM11. To change
the magtape,
ENABLE TM11 enable TM11 and disable TS11
ENABLE TS11 enable TS11 and disable TM11
The PDP-11 simulator implements several unique stop conditions:
- abort during exception vector fetch, and register STOP_VEC is set
- abort during exception stack push, and register STOP_SPA is set
- trap condition 'n' occurs, and register STOP_TRAP<n> is set
- wait state entered, and no I/O operations outstanding
(ie, no interrupt can ever occur)
The PDP-11 loader supports standard binary format tapes. The DUMP command
is not implemented.
2.1 CPU
The CPU options include CPU mapping configuration (18b Unibus, 22b Unibus
with RH70-style controllers, 22b Unibus with RH11 style controllers, and
22b Qbus), the CIS instruction set, and the size of main memory.
SET CPU 18B 18b addressing, no I/O map
SET CPU URH11 22b addresssing, Unibus I/O map,
18b mapped RH11 controller
SET CPU URH70 22b addressing, Unibus I/O map,
22b unmapped RH70 controller
SET CPU 22B 22b addressing, no I/O map (Qbus)
SET CPU NOCIS disable CIS instructions (default)
SET CPU CIS enable CIS instructions
SET CPU 16K set memory size = 16KB
SET CPU 32K set memory size = 32KB
SET CPU 48K set memory size = 48KB
SET CPU 64K set memory size = 64KB
SET CPU 96K set memory size = 96KB
SET CPU 128K set memory size = 128KB
SET CPU 192K set memory size = 192KB
SET CPU 256K set memory size = 256KB
SET CPU 384K set memory size = 384KB
SET CPU 512K set memory size = 512KB
SET CPU 768K set memory size = 768KB
SET CPU 1024K (or 1M) set memory size = 1024KB
SET CPU 2048K (or 2M) set memory size = 2048KB
SET CPU 3072K (or 3M) set memory size = 3072KB
SET CPU 4096K (or 4M) set memory size = 4096KB
If memory size is being reduced, and the memory being truncated contains
non-zero data, the simulator asks for confirmation. Data in the truncated
portion of memory is lost. Initial memory size is 256KB.
These switches are recognized when examining or depositing in CPU memory:
-v interpret address as virtual
-d if mem mgt enabled, force data space
-k if mem mgt enabled, force kernel mode
-s if mem mgt enabled, force supervisor mode
-u if mem mgt enabled, force user mode
-p if mem mgt enabled, force previous mode
CPU registers include the visible state of the processor as well as the
control registers for the interrupt system.
name size comments
PC 16 program counter
R0..R5 16 R0..R5, first register set
R10..R15 16 R0..R5, second register set
KSP 16 kernel stack pointer
SSP 16 supervisor stack pointer
USP 16 user stack pointer
PSW 16 processor status word
CM 2 current mode, PSW<15:14>
PM 2 previous mode, PSW<13:12>
RS 2 register set, PSW<11>
IPL 3 interrupt priority level, PSW<7:5>
T 1 trace bit, PSW<4>
N 1 negative flag, PSW<3>
Z 1 zero flag, PSW<2>
V 1 overflow flag, PSW<1>
C 1 carry flag, PSW<0>
SR 16 front panel switches
DR 16 front panel display
MEMERR 16 memory error register
CCR 16 cache control register
MAINT 16 maintenance register
HITMISS 16 hit/miss register
CPUERR 16 CPU error register
PIRQ 16 programmed interrupt requests
FAC0H..FAC5H 32 FAC0..FAC5, high 32 bits
FAC0L..FAC5L 32 FAC0..FAC5, low 32 bits
FPS 16 floating point status
FEA 16 floating exception address
FEC 4 floating exception code
MMR0..3 16 memory management registers 0..3
{K/S/U}{I/D}{PAR/PDR}{0..7}
16 memory management registers
UBMAP[0:63] 16 Unibus map registers
INT 32 interrupt pending flags
TRAP 18 trap pending flags
WAIT 0 wait state flag
WAIT_ENABLE 0 wait state enable flag
STOP_TRAPS 18 stop on trap flags
STOP_VECA 1 stop on read abort in trap or interrupt
STOP_SPA 1 stop on stack push abort in trap or interrupt
OLDPC 16 PC prior to last JMP, JMS, or interrupt
WRU 8 interrupt character
2.2 Programmed I/O Devices
2.2.1 PC11 Paper Tape Reader (PTR)
The paper tape reader (PTR) reads data from a disk file. The POS
register specifies the number of the next data item to be read. Thus,
by changing POS, the user can backspace or advance the reader.
The paper tape reader implements these registers:
name size comments
BUF 8 last data item processed
CSR 16 control/status register
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
BUSY 1 busy flag (CSR<11>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
POS 31 position in the input file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of tape
end of file 1 report error and stop
0 out of tape
OS I/O error x report error and stop
2.2.2 PC11 Paper Tape Punch (PTP)
The paper tape punch (PTP) writes data to a disk file. The POS
register specifies the number of the next data item to be written.
Thus, by by changing POS, the user can backspace or advance the punch.
The paper tape punch implements these registers:
name size comments
BUF 8 last data item processed
CSR 16 control/status register
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
POS 31 position in the output file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of tape
OS I/O error x report error and stop
2.2.3 DL11 Terminal Input (TTI)
The terminal input (TTI) polls the console keyboard for input. It
implements these registers:
name size comments
BUF 8 last data item processed
CSR 16 control/status register
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
POS 31 number of characters input
TIME 24 keyboard polling interval
2.2.4 DL11 Terminal Output (TTO)
The terminal output (TTO) writes to the simulator console window. It
implements these registers:
name size comments
BUF 8 last data item processed
CSR 16 control/status register
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
POS 31 number of characters input
TIME 24 time from I/O initiation to interrupt
2.2.5 LP11 Line Printer (LPT)
The line printer (LPT) writes data to a disk file. The POS register
specifies the number of the next data item to be written. Thus,
by changing POS, the user can backspace or advance the printer.
The line printer implements these registers:
name size comments
BUF 8 last data item processed
CSR 16 control/status register
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
POS 31 position in the output file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of paper
OS I/O error x report error and stop
2.2.6 Line-Time Clock (CLK)
The clock (CLK) implements these registers:
name size comments
CSR 16 control/status register
INT 1 interrupt pending flag
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
TIME 24 clock frequency
TPS 8 ticks per second (60 or 50)
The real-time clock autocalibrates; the clock interval is adjusted up or
down so that the clock tracks actual elapsed time.
2.2.7 DZ11 Terminal Multiplexor (DZ)
The DZ11 is an 8-line terminal multiplexor. The terminal lines perform
input and output through Telnet sessions connected to a user-specified
port. The ATTACH command specifies the port to be used:
ATTACH {-am} DZ <port>(cr) -- set up listening port
where port is a decimal number between 1 and 65535 that is not being used
for other TCP/IP activities. The optional switch -m turns on the DZ11's
modem controls; the optional switch -a turns on active disconnects
(disconnect session if computer clears Data Terminal Ready).
Once the DZ is attached and the simulator is running, the DZ will listen
for connections on the specified port. It assumes that the incoming
connections are Telnet connections. The connection remains open until
disconnected either by the simulated program or by the Telnet client.
The SHOW DZ LINESTATUS command displays the current connections to the DZ.
The DZ11 implements these registers:
name size comments
CSR 16 control/status register
RBUF 16 receive buffer
LPR 16 line parameter register
TCR 16 transmission control register
MSR 16 modem status register
TDR 16 transmit data register
SAENB 1 silo alarm enabled
MDMTCL 1 modem control enabled
AUTODS 1 autodisconnect enabled
RPOS0..7 32 count of characters received
TPOS0..7 32 count of characters transmitted
The DZ11 does not support save and restore. All open connections are
lost when the simulator shuts down or the DZ is detached.
2.3 RK11/RK05 Cartridge Disk (RK)
RK11 options include the ability to make units write enabled or write locked:
SET RKn LOCKED set unit n write locked
SET RKn ENABLED set unit n write enabled
Units can also be REMOVEd or ADDed to the configuration. The RK11 supports
the BOOT command.
The RK11 implements these registers:
name size comments
RKCS 16 control/status
RKDA 16 disk address
RKBA 16 memory address
RKWC 16 word count
RKDS 16 drive status
RKER 16 error status
INTQ 9 interrupt queue
DRVN 3 number of last selected drive
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
INT 1 interrupt pending flag
STIME 24 seek time, per cylinder
RTIME 24 rotational delay
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 disk not ready
end of file x assume rest of disk is zero
OS I/O error x report error and stop
2.4 RX11/RX01 Floppy Disk (RX)
RX11 options include the ability to make units write enabled or write locked:
SET RXn LOCKED set unit n write locked
SET RXn ENABLED set unit n write enabled
The RX11 supports the BOOT command.
The RX11 implements these registers:
name size comments
RXCS 12 status
RXDB 8 data buffer
RXES 8 error status
RXERR 8 error code
RXTA 8 current track
RXSA 8 current sector
STAPTR 3 controller state
BUFPTR 3 buffer pointer
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
TR 1 transfer ready flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
DONE 1 device done flag (CSR<5>)
CTIME 24 command completion time
STIME 24 seek time, per track
XTIME 24 transfer ready delay
STOP_IOE 1 stop on I/O error
SBUF[0:127] 8 sector buffer array
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 disk not ready
RX01 data files are buffered in memory; therefore, end of file and OS
I/O errors cannot occur.
2.5 RL11(V12)/RL01,RL02 Cartridge Disk (RL)
RL11 options include the ability to set units write enabled or write locked,
to set the drive size to RL01, RL02, or autosize, and to write a DEC standard
044 compliant bad block table on the last track:
SET RLn LOCKED set unit n write locked
SET RLn ENABLED set unit n write enabled
SET RLn RL01 set size to RL01
SET RLn RL02 set size to RL02
SET RLn AUTOSIZE set size based on file size at attach
SET RLn BADBLOCK write bad block table on last track
The size options can be used only when a unit is not attached to a file. The
bad block option can be used only when a unit is attached to a file. Units
can also be REMOVEd or ADDed to the configuration. The RL11 supports the
BOOT command.
The RL11 implements these registers:
name size comments
RLCS 16 control/status
RLDA 16 disk address
RLBA 16 memory address
RLBAE 6 memory address extension (RLV12)
RLMP..RLMP2 16 multipurpose register queue
INT 1 interrupt pending flag
ERR 1 error flag (CSR<15>)
DONE 1 device done flag (CSR<7>)
IE 1 interrupt enable flag (CSR<6>)
STIME 24 seek time, per cylinder
RTIME 24 rotational delay
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 disk not ready
end of file x assume rest of disk is zero
OS I/O error x report error and stop
2.6 RM02/03/05/80, RP04/05/06/07 Disk Pack Drives (RP)
The RP controller implements a "Massbus style" 22b direct interface for
large disk drives. It is more abstract than other device simulators, with
just enough detail to run operating system drivers. In addition, the RP
controller conflates the details of the RM series controllers with the RP
series controllers, although there were detailed differences.
RP options include the ability to set units write enabled or write locked,
to set the drive type to one of six disk types, or autosize, and to write
a DEC standard 044 compliant bad block table on the last track:
SET RPn LOCKED set unit n write locked
SET RPn ENABLED set unit n write enabled
SET RPn RM03 set type to RM03
SET RPn RM05 set type to RM05
SET RPn RM80 set type to RM80
SET RPn RP04 set type to RP04
SET RPn RP06 set type to RP06
SET RPn RP07 set type to RP07
SET RPn AUTOSIZE set type based on file size at attach
SET RPn BADBLOCK write bad block table on last track
The type options can be used only when a unit is not attached to a file. The
bad block option can be used only when a unit is attached to a file. Units
can also be REMOVEd or ADDed to the configuration. The RP controller supports
the BOOT command.
The RP controller implements these registers:
name size comments
RPCS1 16 control/status 1
RPWC 16 word count
RPBA 16 bus address
RPDA 16 desired surface, sector
RPCS2 16 control/status 2
RPDS[0:7] 16 drive status, drives 0-7
RPER1[0:7] 16 drive errors, drives 0-7
RPOF 16 offset
RPDC 8 desired cylinder
RPER2 16 error status 2
RPER3 16 error status 3
RPEC1 16 ECC syndrome 1
RPEC2 16 ECC syndrome 2
RPMR 16 maintenance register
RPDB 16 data buffer
RPBAE 6 bus address extension
RPCS3 16 control/status 3
IFF 1 transfer complete interrupt request flop
INT 1 interrupt pending flag
SC 1 special condition (CSR1<15>)
DONE 1 device done flag (CSR1<7>)
IE 1 interrupt enable flag (CSR1<6>)
STIME 24 seek time, per cylinder
RTIME 24 rotational delay
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 disk not ready
end of file x assume rest of disk is zero
OS I/O error x report error and stop
2.7 RQDX3 MSCP Disk Controller (RQ)
The RQ controller simulates the RQDX3 MSCP disk controller. RQ options
include the ability to set units write enabled or write locked, and to
set the drive type to one of eleven disk types:
SET RQn LOCKED set unit n write locked
SET RQn ENABLED set unit n write enabled
SET RQn RX50 set type to RX50
SET RQn RX33 set type to RX33
SET RQn RD51 set type to RD51
SET RQn RD52 set type to RD52
SET RQn RD53 set type to RD53
SET RQn RD54 set type to RD54
SET RQn RD31 set type to RD31
SET RQn RA82 set type to RA82
SET RQn RA72 set type to RA72
SET RQn RA90 set type to RA90
SET RQn RA92 set type to RA92
The type options can be used only when a unit is not attached to a file.
Units can also be REMOVEd or ADDed to the configuration. The RQ controller
supports the BOOT command.
The RQ controller implements the following special SHOW commands:
SHOW RQ RINGS show command and response rings
SHOW RQ FREEQ show packet free queue
SHOW RQ RESPQ show packet response queue
SHOW RQ UNITQ show unit queues
SHOW RQ ALL show all ring and queue state
SHOW RQn UNITQ show unit queues for unit n
The RQ controller implements these registers:
name size comments
SA 16 status/address register
S1DAT 16 step 1 init host data
CQBA 22 command queue base address
CQLNT 8 command queue length
CQIDX 8 command queue index
RQBA 22 request queue base address
RQLNT 8 request queue length
RQIDX 8 request queue index
FREE 5 head of free packet list
RESP 5 head of response packet list
PBSY 5 number of busy packets
CFLGS 16 controller flags
CSTA 4 controller state
PERR 9 port error number
CRED 5 host credits
HAT 16 host available timer
HTMO 17 host timeout value
CPKT[0:3] 5 current packet, units 0-3
PKTQ[0:3] 5 packet queue, units 0-3
UFLG[0:3] 16 unit flags, units 0-3
INT 1 interrupt request
QTIME 24 response time for 'immediate' packets
XTIME 24 response time for data transfers
PKTS[33*32] 16 packet buffers, 33W each,
32 entries
Error handling is as follows:
error processed as
not attached disk not ready
end of file assume rest of disk is zero
OS I/O error report error and stop
2.8 TC11/TU56 DECtape (DT)
DECtapes drives are numbered 1-8; in the simulator, drive 8 is unit 0.
DECtape options include the ability to make units write enabled or write
locked.
SET DTn LOCKED set unit n write locked
SET DTn ENABLED set unit n write enabled
Units can also be REMOVEd or ADDed to the configuration. The TC11 supports
the BOOT command.
The TC11 supports both PDP-8 format and PDP-9/11/15 format DECtape images.
ATTACH tries to determine the tape format from the DECtape image; the user
can force a particular format with switches:
-f foreign (PDP-8) format
-n native (PDP-9/11/15) format
The DECtape controller is a data-only simulator; the timing and mark
track, and block header and trailer, are not stored. Thus, the WRITE
TIMING AND MARK TRACK function is not supported; the READ ALL function
always returns the hardware standard block header and trailer; and the
WRITE ALL function dumps non-data words into the bit bucket.
The DECtape controller implements these registers:
name size comments
TCST 16 status register
TCCM 16 command register
TCWC 16 word count register
TCBA 16 bus address register
TCDT 16 data register
INT 1 interrupt pending flag
ERR 1 error flag
DONE 1 done flag
IE 1 interrupt enable flag
CTIME 31 time to complete transport stop
LTIME 31 time between lines
ACTIME 31 time to accelerate to full speed
DCTIME 31 time to decelerate to a full stop
SUBSTATE 2 read/write command substate
POS[0:7] 31 position, in lines, units 0-7
STATT[0-7] 31 unit state, units 0-7
It is critically important to maintain certain timing relationships
among the DECtape parameters, or the DECtape simulator will fail to
operate correctly.
- LTIME must be at least 6
- ACTIME must be less than DCTIME, and both need to be at
least 100 times LTIME
2.9 TM11 Magnetic Tape (TM)
TM options include the ability to make units write enabled or write locked.
SET TMn LOCKED set unit n write locked
SET TMn ENABLED set unit n write enabled
Units can also be REMOVEd or ADDed to the configuration.
The TM11 supports the BOOT command. The bootstrap supports both original
and DEC standard boot formats. Originally, a tape bootstrap read and
executed the first record on tape. To allow for ANSI labels, the DEC
standard bootstrap skipped the first record and read and executed the second.
The DEC standard is the default; to bootstrap an original format tape, use
the -o switch.
The magnetic tape controller implements these registers:
name size comments
MTS 16 status
MTC 16 command
MTCMA 16 memory address
MTBRC 16 byte/record count
INT 1 interrupt pending flag
ERR 1 error flag
DONE 1 device done flag
IE 1 interrupt enable flag
STOP_IOE 1 stop on I/O error
TIME 24 delay
UST[0:7] 16 unit status, units 0-7
POS[0:7] 31 position, units 0-7
Error handling is as follows:
error processed as
not attached tape not ready
end of file (read or space) end of physical tape
(write) ignored
OS I/O error report error and stop
2.10 TS11/TSV05 Magnetic Tape (TS)
The TS actually implements the TSV05, with 22-bit addressing, but will
work with TS11 drivers. TS options include the ability to make the unit
write enabled or write locked.
SET TS LOCKED set unit write locked
SET TS ENABLED set unit write enabled
The TS11 supports the BOOT command. The bootstrap supports only DEC
standard boot formats. To allow for ANSI labels, the DEC standard bootstrap
skipped the first record and read and executed the second.
The magnetic tape controller implements these registers:
name size comments
TSSR 16 status register
TSBA 16 bus address register
TSDBX 16 data buffer extension register
CHDR 16 command packet header
CADL 16 command packet low address or count
CADH 16 command packet high address
CLNT 16 command packet length
MHDR 16 message packet header
MRFC 16 message packet residual frame count
MXS0 16 message packet extended status 0
MXS1 16 message packet extended status 1
MXS2 16 message packet extended status 2
MXS3 16 message packet extended status 3
MXS4 16 message packet extended status 4
WADL 16 write char packet low address
WADH 16 write char packet high address
WLNT 16 write char packet length
WOPT 16 write char packet options
WXOPT 16 write char packet extended options
ATTN 1 attention message pending
BOOT 1 boot request pending
OWNC 1 if set, tape owns command buffer
OWNM 1 if set, tape owns message buffer
TIME 24 delay
POS 31 position
Error handling is as follows:
error processed as
not attached tape not ready
end of file (read or space) end of physical tape
(write) ignored
OS I/O error fatal tape error
2.11 Symbolic Display and Input
The PDP-11 simulator implements symbolic display and input. Display is
controlled by command line switches:
-a display as ASCII character
-c display as two character ASCII string
-m display instruction mnemonics
Input parsing is controlled by the first character typed in or by command
line switches:
' or -a ASCII character
" or -c two character ASCII string
alphabetic instruction mnemonic
numeric octal number
Instruction input uses standard PDP-11 assembler syntax. There are sixteen
instruction classes:
class operands examples comments
no operands none HALT, RESET
3b literal literal, 0 - 7 SPL
6b literal literal, 0 - 077 MARK
8b literal literal, 0 - 0377 EMT, TRAP
register register RTS
sop specifier SWAB, CLR, ASL
reg-sop register, specifier JSR, XOR, MUL
fop flt specifier ABSf, NEGf
ac-fop flt reg, flt specifier LDf, MULf
ac-sop flt reg, specifier LDEXP, STEXP
ac-moded sop flt reg, specifier LDCif, STCfi
dop specifier, specifier MOV, ADD, BIC
cond branch address BR, BCC, BNE
sob register, address SOB
cc clear cc clear instructions CLC, CLV, CLZ, CLN combinable
cc set cc set instructions SEC, SEV, SEZ, SEN combinable
For floating point opcodes, F and D variants, and I and L variants, may be
specified regardless of the state of FPS.
The syntax for specifiers is as follows:
syntax specifier displacement comments
Rn 0n -
Fn 0n - only in flt reg classes
(Rn) 1n -
@(Rn) 7n 0 equivalent to @0(Rn)
(Rn)+ 2n -
@(Rn)+ 3n -
-(Rn) 4n -
@-(Rn) 5n -
{+/-}d(Rn) 6n {+/-}d
@{+/-}d(Rn) 7n {+/-}d
#n 27 n
@#n 37 n
.+/-n 67 +/-n - 4
@.+/-n 77 +/-n - 4
{+/-}n 67 {+/-}n - PC - 4 if on disk, 37 and n
@{+/-}n 77 {+/-}n - PC - 4 if on disk, invalid

42
PDP11/pdp11_dz.c Normal file
View File

@@ -0,0 +1,42 @@
/* pdp11_dz.c: DZ11 terminal multiplexor simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
dz DZ11 terminal multiplexor
09-Nov-01 RMS Added VAX support
*/
#if defined (USE_INT64)
#define VM_VAX 1
#include "vax_defs.h"
#define DZ_RDX 16
#else
#define VM_PDP11 1
#include "pdp11_defs.h"
#define DZ_RDX 8
#endif
#include "dec_dz.h"

998
PDP11/pdp11_fp.c Normal file
View File

@@ -0,0 +1,998 @@
/* pdp11_fp.c: PDP-11 floating point simulator (32b version)
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
05-Jun-98 RMS Fixed implementation specific shift bugs
20-Apr-98 RMS Fixed bug in MODf integer truncation
17-Apr-98 RMS Fixed bug in STCfi range check
16-Apr-98 RMS Fixed bugs in STEXP, STCfi, round/pack
09-Apr-98 RMS Fixed bug in LDEXP
04-Apr-98 RMS Fixed bug in MODf condition codes
This module simulates the PDP-11 floating point unit (FP11 series).
It is called from the instruction decoder for opcodes 170000:177777.
The floating point unit recognizes three instruction formats:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand
| 1 1 1 1| 0 0 0 0 0 0| opcode | 170000:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170077
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ one operand
| 1 1 1 1| 0 0 0| opcode | dest spec | 170100:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170777
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand
| 1 1 1 1| opcode | fac | dest spec | 171000:
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 177777
The instruction space is further extended through use of the floating
point status register (FPS) mode bits. Three mode bits affect how
instructions are interpreted:
FPS_D if 0, floating registers are single precision
if 1, floating registers are double precision
FPS_L if 0, integer operands are word
if 1, integer operands are longword
FPS_T if 0, floating operations are rounded
if 1, floating operations are truncated
FPS also contains the condition codes for the floating point unit,
and exception enable bits for individual error conditions. Exceptions
cause a trap through 0244, unless the individual exception, or all
exceptions, are disabled. Illegal address mode, undefined variable,
and divide by zero abort the current instruction; all other exceptions
permit the instruction to complete. (Aborts are implemented as traps
that request an "interrupt" trap. If an interrupt is pending, it is
serviced; if not, trap_req is updated and processing continues.)
Floating point specifiers are similar to integer specifiers, with
the length of the operand being up to 8 bytes. In two specific cases,
the floating point unit reads or writes only two bytes, rather than
the length specified by the operand type:
register for integers, only 16b are accessed; if the
operand is 32b, these are the high order 16b
of the operand
immediate for integers or floating point, only 16b are
accessed; if the operand is 32b or 64b, these
are the high order 16b of the operand
*/
#include "pdp11_defs.h"
/* Floating point status register */
#define FPS_ER (1u << FPS_V_ER) /* error */
#define FPS_ID (1u << FPS_V_ID) /* interrupt disable */
#define FPS_IUV (1u << FPS_V_IUV) /* int on undef var */
#define FPS_IU (1u << FPS_V_IU) /* int on underflow */
#define FPS_IV (1u << FPS_V_IV) /* int on overflow */
#define FPS_IC (1u << FPS_V_IC) /* int on conv error */
#define FPS_D (1u << FPS_V_D) /* single/double */
#define FPS_L (1u << FPS_V_L) /* word/long */
#define FPS_T (1u << FPS_V_T) /* round/truncate */
#define FPS_N (1u << FPS_V_N)
#define FPS_Z (1u << FPS_V_Z)
#define FPS_V (1u << FPS_V_V)
#define FPS_C (1u << FPS_V_C)
#define FPS_CC (FPS_N + FPS_Z + FPS_V + FPS_C)
#define FPS_RW (FPS_ER + FPS_ID + FPS_IUV + FPS_IU + FPS_IV + \
FPS_IC + FPS_D + FPS_L + FPS_T + FPS_CC)
/* Floating point exception codes */
#define FEC_OP 2 /* illegal op/mode */
#define FEC_DZRO 4 /* divide by zero */
#define FEC_ICVT 6 /* conversion error */
#define FEC_OVFLO 8 /* overflow */
#define FEC_UNFLO 10 /* underflow */
#define FEC_UNDFV 12 /* undef variable */
/* Floating point format, all assignments 32b relative */
#define FP_V_SIGN (63 - 32) /* high lw: sign */
#define FP_V_EXP (55 - 32) /* exponent */
#define FP_V_HB FP_V_EXP /* hidden bit */
#define FP_V_F0 (48 - 32) /* fraction 0 */
#define FP_V_F1 (32 - 32) /* fraction 1 */
#define FP_V_FROUND (31 - 32) /* f round point */
#define FP_V_F2 16 /* low lw: fraction 2 */
#define FP_V_F3 0 /* fraction 3 */
#define FP_V_DROUND (-1) /* d round point */
#define FP_M_EXP 0377
#define FP_SIGN (1u << FP_V_SIGN)
#define FP_EXP (FP_M_EXP << FP_V_EXP)
#define FP_HB (1u << FP_V_HB)
#define FP_FRACH ((1u << FP_V_HB) - 1)
#define FP_FRACL 0xFFFFFFFF
#define FP_BIAS 0200 /* exponent bias */
#define FP_GUARD 3 /* guard bits */
/* Data lengths */
#define WORD 2
#define LONG 4
#define QUAD 8
/* Double precision operations on 64b quantities */
#define F_LOAD(qd,ac,ds) ds.h = ac.h; ds.l = (qd)? ac.l: 0
#define F_LOAD_P(qd,ac,ds) ds -> h = ac.h; ds -> l = (qd)? ac.l: 0
#define F_LOAD_FRAC(qd,ac,ds) ds.h = (ac.h & FP_FRACH) | FP_HB; \
ds.l = (qd)? ac.l: 0
#define F_STORE(qd,sr,ac) ac.h = sr.h; if ((qd)) ac.l = sr.l
#define F_STORE_P(qd,sr,ac) ac.h = sr -> h; if ((qd)) ac.l = sr -> l
#define F_GET_FRAC_P(sr,ds) ds.l = sr -> l; \
ds.h = (sr -> h & FP_FRACH) | FP_HB
#define F_ADD(s2,s1,ds) ds.l = (s1.l + s2.l) & 0xFFFFFFFF; \
ds.h = (s1.h + s2.h + (ds.l < s2.l)) & 0xFFFFFFFF
#define F_SUB(s2,s1,ds) ds.h = (s1.h - s2.h - (s1.l < s2.l)) & 0xFFFFFFFF; \
ds.l = (s1.l - s2.l) & 0xFFFFFFFF
#define F_LT(x,y) ((x.h < y.h) || ((x.h == y.h) && (x.l < y.l)))
#define F_LT_AP(x,y) (((x -> h & ~FP_SIGN) < (y -> h & ~FP_SIGN)) || \
(((x -> h & ~FP_SIGN) == (y -> h & ~FP_SIGN)) && (x -> l < y -> l)))
#define F_LSH_V(sr,n,ds) \
ds.h = (((n) >= 32)? (sr.l << ((n) - 32)): \
(sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \
& 0xFFFFFFFF; \
ds.l = ((n) >= 32)? 0: (sr.l << (n)) & 0xFFFFFFFF
#define F_RSH_V(sr,n,ds) \
ds.l = (((n) >= 32)? (sr.h >> ((n) - 32)) & and_mask[64 - (n)]: \
((sr.l >> (n)) & and_mask[32 - (n)]) | \
(sr.h << (32 - (n)))) & 0xFFFFFFFF; \
ds.h = ((n) >= 32)? 0: \
((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF
/* For the constant shift macro, arguments must in the range [2,31] */
#define F_LSH_1(ds) ds.h = ((ds.h << 1) | ((ds.l >> 31) & 1)) & 0xFFFFFFFF; \
ds.l = (ds.l << 1) & 0xFFFFFFFF
#define F_RSH_1(ds) ds.l = ((ds.l >> 1) & 0x7FFFFFFF) | ((ds.h & 1) << 31); \
ds.h = ((ds.h >> 1) & 0x7FFFFFFF)
#define F_LSH_K(sr,n,ds) \
ds.h = ((sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \
& 0xFFFFFFFF; \
ds.l = (sr.l << (n)) & 0xFFFFFFFF
#define F_RSH_K(sr,n,ds) \
ds.l = (((sr.l >> (n)) & and_mask[32 - (n)]) | \
(sr.h << (32 - (n)))) & 0xFFFFFFFF; \
ds.h = ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF
#define F_LSH_GUARD(ds) F_LSH_K(ds,FP_GUARD,ds)
#define F_RSH_GUARD(ds) F_RSH_K(ds,FP_GUARD,ds)
#define GET_BIT(ir,n) (((ir) >> n) & 1)
#define GET_SIGN(ir) GET_BIT((ir), FP_V_SIGN)
#define GET_EXP(ir) (((ir) >> FP_V_EXP) & FP_M_EXP)
#define GET_SIGN_L(ir) GET_BIT((ir), 31)
#define GET_SIGN_W(ir) GET_BIT((ir), 15)
extern jmp_buf save_env;
extern int32 FEC, FEA, FPS;
extern int32 CPUERR, trap_req;
extern int32 N, Z, V, C;
extern int32 R[8];
extern fpac_t FR[6];
extern int32 GeteaW (int32 spec);
extern int32 ReadW (int32 addr);
extern void WriteW (int32 data, int32 addr);
fpac_t zero_fac = { 0, 0 };
fpac_t one_fac = { 1, 0 };
fpac_t fround_fac = { (1u << (FP_V_FROUND + 32)), 0 };
fpac_t fround_guard_fac = { 0, (1u << (FP_V_FROUND + FP_GUARD)) };
fpac_t dround_guard_fac = { (1u << (FP_V_DROUND + FP_GUARD)), 0 };
fpac_t fmask_fac = { 0xFFFFFFFF, (1u << (FP_V_HB + FP_GUARD + 1)) - 1 };
static const uint32 and_mask[33] = { 0,
0x1, 0x3, 0x7, 0xF,
0x1F, 0x3F, 0x7F, 0xFF,
0x1FF, 0x3FF, 0x7FF, 0xFFF,
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF };
int32 backup_PC;
int32 fpnotrap (int32 code);
int32 GeteaFP (int32 spec, int32 len);
unsigned int32 ReadI (int32 addr, int32 spec, int32 len);
void ReadFP (fpac_t *fac, int32 addr, int32 spec, int32 len);
void WriteI (int32 data, int32 addr, int32 spec, int32 len);
void WriteFP (fpac_t *data, int32 addr, int32 spec, int32 len);
int32 setfcc (int32 old_status, int32 result_high, int32 newV);
int32 addfp11 (fpac_t *src1, fpac_t *src2);
int32 mulfp11 (fpac_t *src1, fpac_t *src2);
int32 divfp11 (fpac_t *src1, fpac_t *src2);
int32 modfp11 (fpac_t *src1, fpac_t *src2, fpac_t *frac);
void frac_mulfp11 (fpac_t *src1, fpac_t *src2);
int32 roundfp11 (fpac_t *src);
int32 round_and_pack (fpac_t *fac, int32 exp, fpac_t *frac, int r);
/* Set up for instruction decode and execution */
void fp11 (int32 IR)
{
int32 dst, ea, ac, dstspec;
int32 i, qdouble, lenf, leni;
int32 newV, exp, sign;
fpac_t fac, fsrc, modfrac;
static const unsigned int32 i_limit[2][2] =
{ { 0x80000000, 0x80010000 }, { 0x80000000, 0x80000001 } };
backup_PC = PC; /* save PC for FEA */
ac = (IR >> 6) & 03; /* fac is IR<7:6> */
dstspec = IR & 077;
qdouble = FPS & FPS_D;
lenf = qdouble? QUAD: LONG;
switch ((IR >> 8) & 017) { /* decode IR<11:8> */
case 0:
switch (ac) { /* decode IR<7:6> */
case 0: /* specials */
if (IR == 0170000) { /* CFCC */
N = (FPS >> PSW_V_N) & 1;
Z = (FPS >> PSW_V_Z) & 1;
V = (FPS >> PSW_V_V) & 1;
C = (FPS >> PSW_V_C) & 1; }
else if (IR == 0170001) /* SETF */
FPS = FPS & ~FPS_D;
else if (IR == 0170002) /* SETI */
FPS = FPS & ~FPS_L;
else if (IR == 0170011) /* SETD */
FPS = FPS | FPS_D;
else if (IR == 0170012) /* SETL */
FPS = FPS | FPS_L;
else fpnotrap (FEC_OP);
break;
case 1: /* LDFPS */
dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec));
FPS = dst & FPS_RW;
break;
case 2: /* STFPS */
FPS = FPS & FPS_RW;
if (dstspec <= 07) R[dstspec] = FPS;
else WriteW (FPS, GeteaW (dstspec));
break;
case 3: /* STST */
if (dstspec <= 07) R[dstspec] = FEC;
else WriteI ((FEC << 16) | FEA, GeteaFP (dstspec, LONG),
dstspec, LONG);
break; } /* end switch <7:6> */
break; /* end case 0 */
/* "Easy" instructions */
case 1:
switch (ac) { /* decode IR<7:6> */
case 0: /* CLRf */
WriteFP (&zero_fac, GeteaFP (dstspec, lenf), dstspec, lenf);
FPS = (FPS & ~FPS_CC) | FPS_Z;
break;
case 1: /* TSTf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
FPS = setfcc (FPS, fsrc.h, 0);
break;
case 2: /* ABSf */
ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf);
if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;
else fsrc.h = fsrc.h & ~FP_SIGN;
WriteFP (&fsrc, ea, dstspec, lenf);
FPS = setfcc (FPS, fsrc.h, 0);
break;
case 3: /* NEGf */
ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf);
if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;
else fsrc.h = fsrc.h ^ FP_SIGN;
WriteFP (&fsrc, ea, dstspec, lenf);
FPS = setfcc (FPS, fsrc.h, 0);
break; } /* end switch <7:6> */
break; /* end case 1 */
case 5: /* LDf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_STORE (qdouble, fsrc, FR[ac]);
FPS = setfcc (FPS, fsrc.h, 0);
break;
case 010: /* STf */
F_LOAD (qdouble, FR[ac], fac);
WriteFP (&fac, GeteaFP (dstspec, lenf), dstspec, lenf);
break;
case 017: /* LDCff' */
ReadFP (&fsrc, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf);
if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;
if ((FPS & (FPS_D + FPS_T)) == 0) newV = roundfp11 (&fsrc);
else newV = 0;
F_STORE (qdouble, fsrc, FR[ac]);
FPS = setfcc (FPS, fsrc.h, newV);
break;
case 014: /* STCff' */
F_LOAD (qdouble, FR[ac], fac);
if (GET_EXP (fac.h) == 0) fac = zero_fac;
if ((FPS & (FPS_D + FPS_T)) == FPS_D) newV = roundfp11 (&fac);
else newV = 0;
WriteFP (&fac, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf);
FPS = setfcc (FPS, fac.h, newV);
break;
/* Compare instruction */
case 7: /* CMPf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_LOAD (qdouble, FR[ac], fac);
if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac;
if (GET_EXP (fac.h) == 0) fac = zero_fac;
if ((fsrc.h == fac.h) && (fsrc.l == fac.l)) { /* equal? */
FPS = (FPS & ~FPS_CC) | FPS_Z;
if ((fsrc.h | fsrc.l) == 0) { /* zero? */
F_STORE (qdouble, zero_fac, FR[ac]); }
break; }
FPS = (FPS & ~FPS_CC) | ((fsrc.h >> (FP_V_SIGN - PSW_V_N)) & FPS_N);
if ((GET_SIGN (fsrc.h ^ fac.h) == 0) && (fac.h != 0) &&
F_LT (fsrc, fac)) FPS = FPS ^ FPS_N;
break;
/* Load and store exponent instructions */
case 015: /* LDEXP */
dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec));
F_LOAD (qdouble, FR[ac], fac);
fac.h = (fac.h & ~FP_EXP) | (((dst + FP_BIAS) & FP_M_EXP) << FP_V_EXP);
newV = 0;
if ((dst > 0177) && (dst <= 0177600)) {
if (dst < 0100000) {
if (fpnotrap (FEC_OVFLO)) fac = zero_fac;
newV = FPS_V; }
else { if (fpnotrap (FEC_UNFLO)) fac = zero_fac; } }
F_STORE (qdouble, fac, FR[ac]);
FPS = setfcc (FPS, fac.h, newV);
break;
case 012: /* STEXP */
dst = (GET_EXP (FR[ac].h) - FP_BIAS) & 0177777;
N = GET_SIGN_W (dst);
Z = (dst == 0);
V = 0;
C = 0;
FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | (Z << PSW_V_Z);
if (dstspec <= 07) R[dstspec] = dst;
else WriteW (dst, GeteaW (dstspec));
break;
/* Integer convert instructions */
case 016: /* LDCif */
leni = FPS & FPS_L? LONG: WORD;
if (dstspec <= 07) fac.l = R[dstspec] << 16;
else fac.l = ReadI (GeteaFP (dstspec, leni), dstspec, leni);
fac.h = 0;
if (fac.l) {
if (sign = GET_SIGN_L (fac.l)) fac.l = (fac.l ^ 0xFFFFFFFF) + 1;
for (i = 0; GET_SIGN_L (fac.l) == 0; i++) fac.l = fac.l << 1;
exp = ((FPS & FPS_L)? FP_BIAS + 32: FP_BIAS + 16) - i;
fac.h = (sign << FP_V_SIGN) | (exp << FP_V_EXP) |
((fac.l >> (31 - FP_V_HB)) & FP_FRACH);
fac.l = (fac.l << (FP_V_HB + 1)) & FP_FRACL;
if ((FPS & (FPS_D + FPS_T)) == 0) roundfp11 (&fac); }
F_STORE (qdouble, fac, FR[ac]);
FPS = setfcc (FPS, fac.h, 0);
break;
case 013: /* STCfi */
sign = GET_SIGN (FR[ac].h); /* get sign, */
exp = GET_EXP (FR[ac].h); /* exponent, */
F_LOAD_FRAC (qdouble, FR[ac], fac); /* fraction */
if (FPS & FPS_L) {
leni = LONG;
i = FP_BIAS + 32; }
else { leni = WORD;
i = FP_BIAS + 16; }
C = 0;
if (exp <= FP_BIAS) dst = 0;
else if (exp > i) {
dst = 0;
C = 1; }
else { F_RSH_V (fac, FP_V_HB + 1 + i - exp, fsrc);
if (leni == WORD) fsrc.l = fsrc.l & ~0177777;
if (fsrc.l >= i_limit[leni == LONG][sign]) {
dst = 0;
C = 1; }
else { dst = fsrc.l;
if (sign) dst = -dst; } }
N = GET_SIGN_L (dst);
Z = (dst == 0);
V = 0;
if (C) fpnotrap (FEC_ICVT);
FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) |
(Z << PSW_V_Z) | (C << PSW_V_C);
if (dstspec <= 07) R[dstspec] = (dst >> 16) & 0177777;
else WriteI (dst, GeteaFP (dstspec, leni), dstspec, leni);
break;
/* Calculation instructions */
case 2: /* MULf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_LOAD (qdouble, FR[ac], fac);
newV = mulfp11 (&fac, &fsrc);
F_STORE (qdouble, fac, FR[ac]);
FPS = setfcc (FPS, fac.h, newV);
break;
case 3: /* MODf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_LOAD (qdouble, FR[ac], fac);
newV = modfp11 (&fac, &fsrc, &modfrac);
F_STORE (qdouble, fac, FR[ac | 1]);
F_STORE (qdouble, modfrac, FR[ac]);
FPS = setfcc (FPS, modfrac.h, newV);
break;
case 4: /* ADDf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_LOAD (qdouble, FR[ac], fac);
newV = addfp11 (&fac, &fsrc);
F_STORE (qdouble, fac, FR[ac]);
FPS = setfcc (FPS, fac.h, newV);
break;
case 6: /* SUBf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_LOAD (qdouble, FR[ac], fac);
if (GET_EXP (fsrc.h) != 0) fsrc.h = fsrc.h ^ FP_SIGN;
newV = addfp11 (&fac, &fsrc);
F_STORE (qdouble, fac, FR[ac]);
FPS = setfcc (FPS, fac.h, newV);
break;
case 011: /* DIVf */
ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf);
F_LOAD (qdouble, FR[ac], fac);
newV = divfp11 (&fac, &fsrc);
F_STORE (qdouble, fac, FR[ac]);
FPS = setfcc (FPS, fac.h, newV);
break; } /* end switch fop */
return;
}
/* Effective address calculation for fp operands
Inputs:
spec = specifier
len = length
Outputs:
VA = virtual address
Warnings:
- Do not call this routine for integer mode 0 operands
- Do not call this routine more than once per instruction
*/
int32 GeteaFP (int32 spec, int32 len)
{
int32 adr, reg, ds;
extern int32 cm, isenable, dsenable, MMR0, MMR1;
reg = spec & 07; /* reg number */
ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */
switch (spec >> 3) { /* case on spec */
case 0: /* floating AC */
if (reg >= 06) { fpnotrap (FEC_OP); ABORT (TRAP_INT); }
return 0;
case 1: /* (R) */
return (R[reg] | ds);
case 2: /* (R)+ */
if (reg == 7) len = 2;
R[reg] = ((adr = R[reg]) + len) & 0177777;
if (update_MM) MMR1 = (len << 3) | reg;
return (adr | ds);
case 3: /* @(R)+ */
R[reg] = ((adr = R[reg]) + 2) & 0177777;
if (update_MM) MMR1 = 020 | reg;
adr = ReadW (adr | ds);
return (adr | dsenable);
case 4: /* -(R) */
adr = R[reg] = (R[reg] - len) & 0177777;
if (update_MM) MMR1 = (((-len) & 037) << 3) | reg;
if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
return (adr | ds);
case 5: /* @-(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
if (update_MM) MMR1 = 0360 | reg;
if ((adr < STKLIM) && (reg == 6) && (cm == KERNEL)) {
setTRAP (TRAP_YEL);
setCPUERR (CPUE_YEL); }
adr = ReadW (adr | ds);
return (adr | dsenable);
case 6: /* d(r) */
adr = ReadW (PC | isenable);
PC = (PC + 2) & 0177777;
return (((R[reg] + adr) & 0177777) | dsenable);
case 7: /* @d(R) */
adr = ReadW (PC | isenable);
PC = (PC + 2) & 0177777;
adr = ReadW (((R[reg] + adr) & 0177777) | dsenable);
return (adr | dsenable); } /* end switch */
return 0;
}
/* Read integer operand
Inputs:
VA = virtual address, VA<18:16> = mode, I/D space
spec = specifier
len = length (2/4 bytes)
Outputs:
data = data read from memory or I/O space
*/
unsigned int32 ReadI (int32 VA, int32 spec, int32 len)
{
if ((len == WORD) || (spec == 027)) return (ReadW (VA) << 16);
return ((ReadW (VA) << 16) | ReadW ((VA & ~0177777) | ((VA + 2) & 0177777)));
}
/* Read floating operand
Inputs:
fptr = pointer to output
VA = virtual address, VA<18:16> = mode, I/D space
spec = specifier
len = length (4/8 bytes)
*/
void ReadFP (fpac_t *fptr, int32 VA, int32 spec, int32 len)
{
int32 exta;
if (spec <= 07) {
F_LOAD_P (len == QUAD, FR[spec], fptr);
return; }
if (spec == 027) {
fptr -> h = (ReadW (VA) << FP_V_F0);
fptr -> l = 0; }
else { exta = VA & ~0177777;
fptr -> h = (ReadW (VA) << FP_V_F0) |
(ReadW (exta | ((VA + 2) & 0177777)) << FP_V_F1);
if (len == QUAD) fptr -> l =
(ReadW (exta | ((VA + 4) & 0177777)) << FP_V_F2) |
(ReadW (exta | ((VA + 6) & 0177777)) << FP_V_F3);
else fptr -> l = 0; }
if ((GET_SIGN (fptr -> h) != 0) && (GET_EXP (fptr -> h) == 0) &&
(fpnotrap (FEC_UNDFV) == 0)) ABORT (TRAP_INT);
return;
}
/* Write integer result
Inputs:
data = data to be written
VA = virtual address, VA<18:16> = mode, I/D space
spec = specifier
len = length
Outputs: none
*/
void WriteI (int32 data, int32 VA, int32 spec, int32 len)
{
WriteW ((data >> 16) & 0177777, VA);
if ((len == WORD) || (spec == 027)) return;
WriteW (data & 0177777, (VA & ~0177777) | ((VA + 2) & 0177777));
return;
}
/* Write floating result
Inputs:
fptr = pointer to data to be written
VA = virtual address, VA<18:16> = mode, I/D space
spec = specifier
len = length
Outputs: none
*/
void WriteFP (fpac_t *fptr, int32 VA, int32 spec, int32 len)
{
int32 exta;
if (spec <= 07) {
F_STORE_P (len == QUAD, fptr, FR[spec]);
return; }
WriteW ((fptr -> h >> FP_V_F0) & 0177777, VA);
if (spec == 027) return;
exta = VA & ~0177777;
WriteW ((fptr -> h >> FP_V_F1) & 0177777, exta | ((VA + 2) & 0177777));
if (len == LONG) return;
WriteW ((fptr -> l >> FP_V_F2) & 0177777, exta | ((VA + 4) & 0177777));
WriteW ((fptr -> l >> FP_V_F3) & 0177777, exta | ((VA + 6) & 0177777));
return;
}
/* Floating point add
Inputs:
facp = pointer to src1 (output)
fsrcp = pointer to src2
Outputs:
ovflo = overflow variable
*/
int32 addfp11 (fpac_t *facp, fpac_t *fsrcp)
{
int32 facexp, fsrcexp, ediff;
fpac_t facfrac, fsrcfrac;
if (F_LT_AP (facp, fsrcp)) { /* if !fac! < !fsrc! */
facfrac = *facp;
*facp = *fsrcp; /* swap operands */
*fsrcp = facfrac; }
facexp = GET_EXP (facp -> h); /* get exponents */
fsrcexp = GET_EXP (fsrcp -> h);
if (facexp == 0) { /* fac = 0? */
*facp = fsrcexp? *fsrcp: zero_fac; /* result fsrc or 0 */
return 0; }
if (fsrcexp == 0) return 0; /* fsrc = 0? no op */
ediff = facexp - fsrcexp; /* exponent diff */
if (ediff >= 60) return 0; /* too big? no op */
F_GET_FRAC_P (facp, facfrac); /* get fractions */
F_GET_FRAC_P (fsrcp, fsrcfrac);
F_LSH_GUARD (facfrac); /* guard fractions */
F_LSH_GUARD (fsrcfrac);
if (GET_SIGN (facp -> h) != GET_SIGN (fsrcp -> h)) { /* signs different? */
if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); } /* sub, shf fsrc */
F_SUB (fsrcfrac, facfrac, facfrac); /* sub fsrc from fac */
if ((facfrac.h | facfrac.l) == 0) { /* result zero? */
*facp = zero_fac; /* no overflow */
return 0; }
if (ediff <= 1) { /* big normalize? */
if ((facfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) {
F_LSH_K (facfrac, 24, facfrac);
facexp = facexp - 24; }
if ((facfrac.h & (0x00FFF000 << FP_GUARD)) == 0) {
F_LSH_K (facfrac, 12, facfrac);
facexp = facexp - 12; }
if ((facfrac.h & (0x00FC0000 << FP_GUARD)) == 0) {
F_LSH_K (facfrac, 6, facfrac);
facexp = facexp - 6; } }
while (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) {
F_LSH_1 (facfrac);
facexp = facexp - 1; } }
else { if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); } /* add, shf fsrc */
F_ADD (fsrcfrac, facfrac, facfrac); /* add fsrc to fac */
if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD + 1)) {
F_RSH_1 (facfrac); /* carry out, shift */
facexp = facexp + 1; } }
return round_and_pack (facp, facexp, &facfrac, 1);
}
/* Floating point multiply
Inputs:
facp = pointer to src1 (output)
fsrcp = pointer to src2
Outputs:
ovflo = overflow indicator
*/
int32 mulfp11 (fpac_t *facp, fpac_t *fsrcp)
{
int32 facexp, fsrcexp;
fpac_t facfrac, fsrcfrac;
facexp = GET_EXP (facp -> h); /* get exponents */
fsrcexp = GET_EXP (fsrcp -> h);
if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */
*facp = zero_fac;
return 0; }
F_GET_FRAC_P (facp, facfrac); /* get fractions */
F_GET_FRAC_P (fsrcp, fsrcfrac);
facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */
facp -> h = facp -> h ^ fsrcp -> h; /* calculate sign */
frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */
/* Multiplying two numbers in the range [.5,1) produces a result in the
range [.25,1). Therefore, at most one bit of normalization is required
to bring the result back to the range [.5,1).
*/
if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) {
F_LSH_1 (facfrac);
facexp = facexp - 1; }
return round_and_pack (facp, facexp, &facfrac, 1);
}
/* Floating point mod
Inputs:
facp = pointer to src1 (integer result)
fsrcp = pointer to src2
fracp = pointer to fractional result
Outputs:
ovflo = overflow indicator
See notes on multiply for initial operation
*/
int32 modfp11 (fpac_t *facp, fpac_t *fsrcp, fpac_t *fracp)
{
int32 facexp, fsrcexp;
fpac_t facfrac, fsrcfrac, fmask;
facexp = GET_EXP (facp -> h); /* get exponents */
fsrcexp = GET_EXP (fsrcp -> h);
if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */
*fracp = zero_fac;
*facp = zero_fac;
return 0; }
F_GET_FRAC_P (facp, facfrac); /* get fractions */
F_GET_FRAC_P (fsrcp, fsrcfrac);
facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */
fracp -> h = facp -> h = facp -> h ^ fsrcp -> h; /* calculate sign */
frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */
/* Multiplying two numbers in the range [.5,1) produces a result in the
range [.25,1). Therefore, at most one bit of normalization is required
to bring the result back to the range [.5,1).
*/
if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) {
F_LSH_1 (facfrac);
facexp = facexp - 1; }
/* There are three major cases of MODf:
1. Exp <= FP_BIAS (all fraction). Return 0 as integer, product as
fraction. Underflow can occur.
2. Exp > FP_BIAS + #fraction bits (all integer). Return product as
integer, 0 as fraction. Overflow can occur.
3. FP_BIAS < exp <= FP_BIAS + #fraction bits. Separate integer and
fraction and return both. Neither overflow nor underflow can occur.
*/
if (facexp <= FP_BIAS) { /* case 1 */
*facp = zero_fac;
return round_and_pack (fracp, facexp, &facfrac, 1); }
if (facexp > ((FPS & FPS_D)? FP_BIAS + 56: FP_BIAS + 24)) {
*fracp = zero_fac; /* case 2 */
return round_and_pack (facp, facexp, &facfrac, 0); }
F_RSH_V (fmask_fac, facexp - FP_BIAS, fmask); /* shift mask */
fsrcfrac.l = facfrac.l & fmask.l; /* extract fraction */
fsrcfrac.h = facfrac.h & fmask.h;
if ((fsrcfrac.h | fsrcfrac.l) == 0) *fracp = zero_fac;
else { F_LSH_V (fsrcfrac, facexp - FP_BIAS, fsrcfrac);
fsrcexp = FP_BIAS;
if ((fsrcfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) {
F_LSH_K (fsrcfrac, 24, fsrcfrac);
fsrcexp = fsrcexp - 24; }
if ((fsrcfrac.h & (0x00FFF000 << FP_GUARD)) == 0) {
F_LSH_K (fsrcfrac, 12, fsrcfrac);
fsrcexp = fsrcexp - 12; }
if ((fsrcfrac.h & (0x00FC0000 << FP_GUARD)) == 0) {
F_LSH_K (fsrcfrac, 6, fsrcfrac);
fsrcexp = fsrcexp - 6; }
while (GET_BIT (fsrcfrac.h, FP_V_HB + FP_GUARD) == 0) {
F_LSH_1 (fsrcfrac);
fsrcexp = fsrcexp - 1; }
round_and_pack (fracp, fsrcexp, &fsrcfrac, 1); }
facfrac.l = facfrac.l & ~fmask.l;
facfrac.h = facfrac.h & ~fmask.h;
return round_and_pack (facp, facexp, &facfrac, 0);
}
/* Fraction multiply
Inputs:
f1p = pointer to multiplier (output)
f2p = pointer to multiplicand fraction
Note: the inputs are unguarded; the output is guarded.
This routine performs a classic shift-and-add multiply. The low
order bit of the multiplier is tested; if 1, the multiplicand is
added into the high part of the double precision result. The
result and the multiplier are both shifted right 1.
For the 24b x 24b case, this routine develops 48b of result.
For the 56b x 56b case, this routine only develops the top 64b
of the the result. Because the inputs are normalized fractions,
the interesting part of the result is the high 56+guard bits.
Everything shifted off to the right, beyond 64b, plays no part
in rounding or the result.
There are many possible optimizations in this routine: scanning
for groups of zeroes, particularly in the 56b x 56b case; using
"extended multiply" capability if available in the hardware.
*/
void frac_mulfp11 (fpac_t *f1p, fpac_t *f2p)
{
fpac_t result, mpy, mpc;
int32 i;
result = zero_fac; /* clear result */
mpy = *f1p; /* get operands */
mpc = *f2p;
F_LSH_GUARD (mpc); /* guard multipicand */
if ((mpy.l | mpc.l) == 0) { /* 24b x 24b? */
for (i = 0; i < 24; i++) {
if (mpy.h & 1) result.h = result.h + mpc.h;
F_RSH_1 (result);
mpy.h = mpy.h >> 1; } }
else { if (mpy.l != 0) { /* 24b x 56b? */
for (i = 0; i < 32; i++) {
if (mpy.l & 1) { F_ADD (mpc, result, result); }
F_RSH_1 (result);
mpy.l = mpy.l >> 1; } }
for (i = 0; i < 24; i++) {
if (mpy.h & 1) { F_ADD (mpc, result, result); }
F_RSH_1 (result);
mpy.h = mpy.h >> 1; } }
*f1p = result;
return;
}
/* Floating point divide
Inputs:
facp = pointer to dividend (output)
fsrcp = pointer to divisor
Outputs:
ovflo = overflow indicator
*/
int32 divfp11 (fpac_t *facp, fpac_t *fsrcp)
{
int32 facexp, fsrcexp, i, count, qd;
fpac_t facfrac, fsrcfrac, quo;
fsrcexp = GET_EXP (fsrcp -> h); /* get divisor exp */
if (fsrcexp == 0) { /* divide by zero? */
fpnotrap (FEC_DZRO);
ABORT (TRAP_INT); }
facexp = GET_EXP (facp -> h); /* get dividend exp */
if (facexp == 0) { /* test for zero */
*facp = zero_fac; /* result zero */
return 0; }
F_GET_FRAC_P (facp, facfrac); /* get fractions */
F_GET_FRAC_P (fsrcp, fsrcfrac);
F_LSH_GUARD (facfrac); /* guard fractions */
F_LSH_GUARD (fsrcfrac);
facexp = facexp - fsrcexp + FP_BIAS + 1; /* calculate exp */
facp -> h = facp -> h ^ fsrcp -> h; /* calculate sign */
qd = FPS & FPS_D;
count = FP_V_HB + FP_GUARD + (qd? 33: 1); /* count = 56b/24b */
quo = zero_fac;
for (i = count; (i > 0) && ((facfrac.h | facfrac.l) != 0); i--) {
F_LSH_1 (quo); /* shift quotient */
if (!F_LT (facfrac, fsrcfrac)) { /* divd >= divr? */
F_SUB (fsrcfrac, facfrac, facfrac); /* divd - divr */
if (qd) quo.l = quo.l | 1; /* double or single? */
else quo.h = quo.h | 1; }
F_LSH_1 (facfrac); } /* shift divd */
if (i > 0) { F_LSH_V (quo, i, quo); } /* early exit? */
/* Dividing two numbers in the range [.5,1) produces a result in the
range [.5,2). Therefore, at most one bit of normalization is required
to bring the result back to the range [.5,1). The choice of counts
and quotient bit positions makes this work correctly.
*/
if (GET_BIT (quo.h, FP_V_HB + FP_GUARD) == 0) {
F_LSH_1 (quo);
facexp = facexp - 1; }
return round_and_pack (facp, facexp, &quo, 1);
}
/* Update floating condition codes
Note that FC is only set by STCfi via the integer condition codes
Inputs:
oldst = current status
result = high result
newV = new V
Outputs:
newst = new status
*/
int32 setfcc (int32 oldst, int32 result, int32 newV)
{
oldst = (oldst & ~FPS_CC) | newV;
if (GET_SIGN (result)) oldst = oldst | FPS_N;
if (GET_EXP (result) == 0) oldst = oldst | FPS_Z;
return oldst;
}
/* Round (in place) floating point number to f_floating
Inputs:
fptr = pointer to floating number
Outputs:
ovflow = overflow
*/
int32 roundfp11 (fpac_t *fptr)
{
fpac_t outf;
outf = *fptr; /* get argument */
F_ADD (fround_fac, outf, outf); /* round */
if (GET_SIGN (outf.h ^ fptr -> h)) { /* flipped sign? */
outf.h = (outf.h ^ FP_SIGN) & 0xFFFFFFFF; /* restore sign */
if (fpnotrap (FEC_OVFLO)) *fptr = zero_fac; /* if no int, clear */
else *fptr = outf; /* return rounded */
return FPS_V; } /* overflow */
else { *fptr = outf; /* round was ok */
return 0; } /* no overflow */
}
/* Round result of calculation, test overflow, pack
Input:
facp = pointer to result, sign in place
exp = result exponent, right justified
fracp = pointer to result fraction, right justified with
guard bits
r = round (1) or truncate (0)
Outputs:
ovflo = overflow indicator
*/
int32 round_and_pack (fpac_t *facp, int32 exp, fpac_t *fracp, int r)
{
fpac_t frac;
frac = *fracp; /* get fraction */
if (r && ((FPS & FPS_T) == 0)) {
if (FPS & FPS_D) { F_ADD (dround_guard_fac, frac, frac); }
else { F_ADD (fround_guard_fac, frac, frac); }
if (GET_BIT (frac.h, FP_V_HB + FP_GUARD + 1)) {
F_RSH_1 (frac);
exp = exp + 1; } }
F_RSH_GUARD (frac);
facp -> l = frac.l & FP_FRACL;
facp -> h = (facp -> h & FP_SIGN) | ((exp & FP_M_EXP) << FP_V_EXP) |
(frac.h & FP_FRACH);
if (exp > 0377) {
if (fpnotrap (FEC_OVFLO)) *facp = zero_fac;
return FPS_V; }
if ((exp <= 0) && (fpnotrap (FEC_UNFLO))) *facp = zero_fac;
return 0;
}
/* Process floating point exception
Inputs:
code = exception code
Outputs:
int = FALSE if interrupt enabled, TRUE if disabled
*/
int32 fpnotrap (int32 code)
{
static const int32 test_code[] = { 0, 0, 0, FPS_IC, FPS_IV, FPS_IU, FPS_IUV };
if ((code >= FEC_ICVT) && (code <= FEC_UNDFV) &&
((FPS & test_code[code >> 1]) == 0)) return TRUE;
FPS = FPS | FPS_ER;
FEC = code;
FEA = (backup_PC - 2) & 0177777;
if ((FPS & FPS_ID) == 0) setTRAP (TRAP_FPE);
return FALSE;
}

361
PDP11/pdp11_io.c Normal file
View File

@@ -0,0 +1,361 @@
/* pdp11_io.c: PDP-11 I/O simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
11-Dec-01 RMS Moved interrupt debug code
08-Nov-01 RMS Cloned from cpu sources
*/
#include "pdp11_defs.h"
extern uint16 *M;
extern int32 int_req[IPL_HLVL];
extern int32 ub_map[UBM_LNT_LW];
extern UNIT cpu_unit;
extern int32 cpu_bme, cpu_ubm;
extern int32 trap_req, ipl;
extern int32 cpu_log;
extern FILE *sim_log;
int32 calc_ints (int32 nipl, int32 trq);
extern t_stat CPU_rd (int32 *data, int32 addr, int32 access);
extern t_stat CPU_wr (int32 data, int32 addr, int32 access);
extern t_stat APR_rd (int32 *data, int32 addr, int32 access);
extern t_stat APR_wr (int32 data, int32 addr, int32 access);
extern t_stat SR_MMR012_rd (int32 *data, int32 addr, int32 access);
extern t_stat SR_MMR012_wr (int32 data, int32 addr, int32 access);
extern t_stat MMR3_rd (int32 *data, int32 addr, int32 access);
extern t_stat MMR3_wr (int32 data, int32 addr, int32 access);
extern t_stat ubm_rd (int32 *data, int32 addr, int32 access);
extern t_stat ubm_wr (int32 data, int32 addr, int32 access);
extern t_stat std_rd (int32 *data, int32 addr, int32 access);
extern t_stat std_wr (int32 data, int32 addr, int32 access);
extern t_stat lpt_rd (int32 *data, int32 addr, int32 access);
extern t_stat lpt_wr (int32 data, int32 addr, int32 access);
extern t_stat dz_rd (int32 *data, int32 addr, int32 access);
extern t_stat dz_wr (int32 data, int32 addr, int32 access);
extern t_stat rk_rd (int32 *data, int32 addr, int32 access);
extern t_stat rk_wr (int32 data, int32 addr, int32 access);
extern int32 rk_inta (void);
extern int32 rk_enb;
/* extern t_stat rk6_rd (int32 *data, int32 addr, int32 access);
extern t_stat rk6_wr (int32 data, int32 addr, int32 access);
extern int32 rk6_inta (void);
extern int32 rk6_enb; */
extern t_stat rl_rd (int32 *data, int32 addr, int32 access);
extern t_stat rl_wr (int32 data, int32 addr, int32 access);
extern int32 rl_enb;
extern t_stat rp_rd (int32 *data, int32 addr, int32 access);
extern t_stat rp_wr (int32 data, int32 addr, int32 access);
extern int32 rp_inta (void);
extern int32 rp_enb;
extern t_stat rq_rd (int32 *data, int32 addr, int32 access);
extern t_stat rq_wr (int32 data, int32 addr, int32 access);
extern int32 rq_inta (void);
extern int32 rq_enb;
extern t_stat rx_rd (int32 *data, int32 addr, int32 access);
extern t_stat rx_wr (int32 data, int32 addr, int32 access);
extern int32 rx_enb;
extern t_stat dt_rd (int32 *data, int32 addr, int32 access);
extern t_stat dt_wr (int32 data, int32 addr, int32 access);
extern int32 dt_enb;
extern t_stat tm_rd (int32 *data, int32 addr, int32 access);
extern t_stat tm_wr (int32 data, int32 addr, int32 access);
extern int32 tm_enb;
extern t_stat ts_rd (int32 *data, int32 addr, int32 access);
extern t_stat ts_wr (int32 data, int32 addr, int32 access);
extern int32 ts_enb;
/* I/O data structures */
struct iolink { /* I/O page linkage */
int32 low; /* low I/O addr */
int32 high; /* high I/O addr */
int32 *enb; /* enable flag */
t_stat (*read)(); /* read routine */
t_stat (*write)(); }; /* write routine */
struct iolink iotable[] = {
{ IOBA_CPU, IOBA_CPU+IOLN_CPU, NULL, &CPU_rd, &CPU_wr },
{ IOBA_STD, IOBA_STD+IOLN_STD, NULL, &std_rd, &std_wr },
{ IOBA_LPT, IOBA_LPT+IOLN_LPT, NULL, &lpt_rd, &lpt_wr },
{ IOBA_DZ, IOBA_DZ +IOLN_DZ, NULL, &dz_rd, &dz_wr },
{ IOBA_RK, IOBA_RK +IOLN_RK, &rk_enb, &rk_rd, &rk_wr },
{ IOBA_RL, IOBA_RL +IOLN_RL, &rl_enb, &rl_rd, &rl_wr },
{ IOBA_RP, IOBA_RP +IOLN_RP, &rp_enb, &rp_rd, &rp_wr },
{ IOBA_RQ, IOBA_RQ +IOLN_RQ, &rq_enb, &rq_rd, &rq_wr },
{ IOBA_RX, IOBA_RX +IOLN_RX, &rx_enb, &rx_rd, &rx_wr },
{ IOBA_TC, IOBA_TC +IOLN_TC, &dt_enb, &dt_rd, &dt_wr },
{ IOBA_TM, IOBA_TM +IOLN_TM, &tm_enb, &tm_rd, &tm_wr },
{ IOBA_TS, IOBA_TS +IOLN_TS, &ts_enb, &ts_rd, &ts_wr },
/* { IOBA_RK6, IOBA_RK6+IOLN_RK6, &rk6_enb, &rk6_rd, &rk6_wr }, */
{ IOBA_APR, IOBA_APR+IOLN_APR, NULL, &APR_rd, &APR_wr },
{ IOBA_APR1, IOBA_APR1+IOLN_APR1, NULL, &APR_rd, &APR_wr },
{ IOBA_SRMM, IOBA_SRMM+IOLN_SRMM, NULL, &SR_MMR012_rd, &SR_MMR012_wr },
{ IOBA_MMR3, IOBA_MMR3+IOLN_MMR3, NULL, &MMR3_rd, &MMR3_wr },
{ IOBA_UBM, IOBA_UBM+IOLN_UBM, NULL, &ubm_rd, &ubm_wr },
{ 0, 0, NULL, NULL, NULL } };
int32 int_vec[IPL_HLVL][32] = { /* int req to vector */
{ 0 }, /* IPL 0 */
{ VEC_PIRQ }, /* IPL 1 */
{ VEC_PIRQ }, /* IPL 2 */
{ VEC_PIRQ }, /* IPL 3 */
{ VEC_TTI, VEC_TTO, VEC_PTR, VEC_PTP, /* IPL 4 */
VEC_LPT, VEC_PIRQ },
{ VEC_RK, VEC_RL, VEC_RX, VEC_TM, /* IPL 5 */
VEC_RP, VEC_TS, VEC_RK6, VEC_RQ,
VEC_DZRX, VEC_DZTX, VEC_PIRQ },
{ VEC_CLK, VEC_DTA, VEC_PIRQ }, /* IPL 6 */
{ VEC_PIRQ } }; /* IPL 7 */
int32 (*int_ack[IPL_HLVL][32])() = { /* int ack routines */
{ NULL }, /* IPL 0 */
{ NULL }, /* IPL 1 */
{ NULL }, /* IPL 2 */
{ NULL }, /* IPL 3 */
{ NULL }, /* IPL 4 */
{ &rk_inta, NULL, NULL, NULL, /* IPL 5 */
&rp_inta, NULL, NULL, &rq_inta },
{ NULL }, /* IPL 6 */
{ NULL } }; /* IPL 7 */
/* I/O page lookup and linkage routines
Inputs:
*data = pointer to data to read, if READ
data = data to store, if WRITE or WRITEB
pa = address
access = READ, WRITE, or WRITEB
Outputs:
status = SCPE_OK or SCPE_NXM
*/
t_stat iopageR (int32 *data, int32 pa, int32 access)
{
t_stat stat;
struct iolink *p;
for (p = &iotable[0]; p -> low != 0; p++ ) {
if ((pa >= p -> low) && (pa < p -> high) &&
((p -> enb == NULL) || *p -> enb)) {
stat = p -> read (data, pa, access);
trap_req = calc_ints (ipl, trap_req);
return stat; } }
return SCPE_NXM;
}
t_stat iopageW (int32 data, int32 pa, int32 access)
{
t_stat stat;
struct iolink *p;
for (p = &iotable[0]; p -> low != 0; p++ ) {
if ((pa >= p -> low) && (pa < p -> high) &&
((p -> enb == NULL) || *p -> enb)) {
stat = p -> write (data, pa, access);
trap_req = calc_ints (ipl, trap_req);
return stat; } }
return SCPE_NXM;
}
/* Calculate interrupt outstanding */
int32 calc_ints (int32 nipl, int32 trq)
{
int32 i;
for (i = IPL_HLVL - 1; i > nipl; i--) {
if (int_req[i]) return (trq | TRAP_INT); }
return (trq & ~TRAP_INT);
}
/* Find vector for highest priority interrupt */
int32 get_vector (int32 nipl)
{
int32 i, j, t, vec;
for (i = IPL_HLVL - 1; i > nipl; i--) { /* loop thru lvls */
t = int_req[i]; /* get level */
for (j = 0; t && (j < 32); j++) { /* srch level */
if ((t >> j) & 1) { /* irq found? */
int_req[i] = int_req[i] & ~(1u << j); /* clr irq */
if (int_ack[i][j]) vec = int_ack[i][j]();
else vec = int_vec[i][j];
if (DBG_LOG (LOG_CPU_I)) fprintf (sim_log,
">>INT: lvl=%d, flag=%d, vec=%o\n", i, j, vec);
return vec; /* return vector */
} /* end if t */
} /* end for j */
} /* end for i */
return 0;
}
/* Read and write Unibus map registers
In any even/odd pair
even = low 16b, bit <0> clear
odd = high 6b
The Unibus map is stored as an array of longwords
*/
t_stat ubm_rd (int32 *data, int32 addr, int32 access)
{
if (cpu_ubm) {
int32 pg = (addr >> 2) & UBM_M_PN;
*data = (addr & 2)? ((ub_map[pg] >> 16) & 077):
(ub_map[pg] & 0177776);
return SCPE_OK; }
return SCPE_NXM;
}
t_stat ubm_wr (int32 data, int32 addr, int32 access)
{
if (cpu_ubm) {
int32 sc, pg = (addr >> 2) & UBM_M_PN;
if (access == WRITEB) {
sc = (addr & 3) << 3;
ub_map[pg] = (ub_map[pg] & ~(0377 << sc)) |
((data & 0377) << sc); }
else { sc = (addr & 2) << 3;
ub_map[pg] = (ub_map[pg] & ~(0177777 << sc)) |
((data & 0177777) << sc); }
ub_map[pg] = ub_map[pg] & 017777776;
return SCPE_OK; }
return SCPE_NXM;
}
/* Mapped memory access routines for DMA devices */
/* Map I/O address to memory address */
t_bool Map_Addr (t_addr ba, t_addr *ma)
{
if (cpu_bme) { /* bus map on? */
int32 pg = UBM_GETPN (ba); /* map entry */
int32 off = UBM_GETOFF (ba); /* offset */
if (pg != UBM_M_PN) /* last page? */
*ma = (ub_map[pg] + off) & PAMASK; /* no, use map */
else *ma = (IOPAGEBASE + off) & PAMASK; } /* yes, use fixed */
else *ma = ba; /* else physical */
return TRUE;
}
/* I/O buffer routines, aligned access
Map_ReadB - fetch byte buffer from memory
Map_ReadW - fetch word buffer from memory
Map_WriteB - store byte buffer into memory
Map_WriteW - store word buffer into memory
*/
int32 Map_ReadB (t_addr ba, int32 bc, uint8 *buf, t_bool ub)
{
t_addr alim, lim, ma;
lim = ba + bc;
if (ub && cpu_bme) { /* UB, map on? */
for ( ; ba < lim; ba++) { /* by bytes */
Map_Addr (ba, &ma); /* map addr */
if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
if (ma & 1) *buf++ = (M[ma >> 1] >> 8) & 0377; /* get byte */
else *buf++ = M[ma >> 1] & 0377; }
return 0; }
else { /* physical */
if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
else if (ADDR_IS_MEM (ba)) alim = MEMSIZE; /* no, strt ok? */
else return bc; /* no, err */
for ( ; ba < alim; ba++) { /* by bytes */
if (ba & 1) *buf++ = (M[ba >> 1] >> 8) & 0377; /* get byte */
else *buf++ = M[ba >> 1] & 0377; }
return (lim - alim); }
}
int32 Map_ReadW (t_addr ba, int32 bc, uint16 *buf, t_bool ub)
{
t_addr alim, lim, ma;
ba = ba & ~01; /* align start */
lim = ba + (bc & ~01);
if (ub && cpu_bme) { /* UB, map on? */
for (; ba < lim; ba = ba + 2) { /* by words */
Map_Addr (ba, &ma); /* map addr */
if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
*buf++ = M[ma >> 1]; }
return 0; }
else { /* physical */
if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
else if (ADDR_IS_MEM (ba)) alim = MEMSIZE; /* no, strt ok? */
else return bc; /* no, err */
for ( ; ba < alim; ba = ba + 2) { /* by words */
*buf++ = M[ba >> 1]; }
return (lim - alim); }
}
int32 Map_WriteB (t_addr ba, int32 bc, uint8 *buf, t_bool ub)
{
t_addr alim, lim, ma;
lim = ba + bc;
if (ub && cpu_bme) { /* UB, map on? */
for ( ; ba < lim; ba++) { /* by bytes */
Map_Addr (ba, &ma); /* map addr */
if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
if (ma & 1) M[ma >> 1] = (M[ma >> 1] & 0377) |
((uint16) *buf++ << 8);
else M[ma >> 1] = (M[ma >> 1] & ~0377) | *buf++; }
return 0; }
else { /* physical */
if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
else if (ADDR_IS_MEM (ba)) alim = MEMSIZE; /* no, strt ok? */
else return bc; /* no, err */
for ( ; ba < alim; ba++) { /* by bytes */
if (ba & 1) M[ba >> 1] = (M[ba >> 1] & 0377) |
((uint16) *buf++ << 8);
else M[ba >> 1] = (M[ba >> 1] & ~0377) | *buf++; }
return (lim - alim); }
}
int32 Map_WriteW (t_addr ba, int32 bc, uint16 *buf, t_bool ub)
{
t_addr alim, lim, ma;
ba = ba & ~01; /* align start */
lim = ba + (bc & ~01);
if (ub && cpu_bme) { /* UB, map on? */
for (; ba < lim; ba = ba + 2) { /* by words */
Map_Addr (ba, &ma); /* map addr */
if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */
M[ma >> 1] = *buf++; } /* store word */
return 0; }
else { /* physical */
if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */
else if (ADDR_IS_MEM (ba)) alim = MEMSIZE; /* no, strt ok? */
else return bc; /* no, err */
for ( ; ba < alim; ba = ba + 2) { /* by words */
M[ba >> 1] = *buf++; }
return (lim - alim); }
}

156
PDP11/pdp11_lp.c Normal file
View File

@@ -0,0 +1,156 @@
/* pdp11_lp.c: PDP-11 line printer simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
lpt LP11 line printer
09-Nov-01 RMS Added VAX support
07-Sep-01 RMS Revised interrupt mechanism
30-Oct-00 RMS Standardized register naming
*/
#if defined (USE_INT64)
#define VM_VAX 1
#include "vax_defs.h"
#define LPT_DRDX 16
#else
#define VM_PDP11 1
#include "pdp11_defs.h"
#define LPT_DRDX 8
#endif
#define LPTCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* implemented */
#define LPTCSR_RW (CSR_IE) /* read/write */
extern int32 int_req[IPL_HLVL];
int32 lpt_csr = 0; /* control/status */
int32 lpt_stopioe = 0; /* stop on error */
t_stat lpt_svc (UNIT *uptr);
t_stat lpt_reset (DEVICE *dptr);
t_stat lpt_attach (UNIT *uptr, char *ptr);
t_stat lpt_detach (UNIT *uptr);
/* LPT data structures
lpt_dev LPT device descriptor
lpt_unit LPT unit descriptor
lpt_reg LPT register list
*/
UNIT lpt_unit = {
UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
REG lpt_reg[] = {
{ GRDATA (BUF, lpt_unit.buf, LPT_DRDX, 8, 0) },
{ GRDATA (CSR, lpt_csr, LPT_DRDX, 16, 0) },
{ FLDATA (INT, IREQ (LPT), INT_V_LPT) },
{ FLDATA (ERR, lpt_csr, CSR_V_ERR) },
{ FLDATA (DONE, lpt_csr, CSR_V_DONE) },
{ FLDATA (IE, lpt_csr, CSR_V_IE) },
{ DRDATA (POS, lpt_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, lpt_stopioe, 0) },
{ NULL } };
DEVICE lpt_dev = {
"LPT", &lpt_unit, lpt_reg, NULL,
1, 10, 31, 1, LPT_DRDX, 8,
NULL, NULL, &lpt_reset,
NULL, &lpt_attach, &lpt_detach };
/* Line printer routines
lpt_rd I/O page read
lpt_wr I/O page write
lpt_svc process event (printer ready)
lpt_reset process reset
lpt_attach process attach
lpt_detach process detach
*/
t_stat lpt_rd (int32 *data, int32 PA, int32 access)
{
if ((PA & 02) == 0) *data = lpt_csr & LPTCSR_IMP; /* csr */
else *data = lpt_unit.buf; /* buffer */
return SCPE_OK;
}
t_stat lpt_wr (int32 data, int32 PA, int32 access)
{
if ((PA & 02) == 0) { /* csr */
if (PA & 1) return SCPE_OK;
if ((data & CSR_IE) == 0) CLR_INT (LPT);
else if ((lpt_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
SET_INT (LPT);
lpt_csr = (lpt_csr & ~LPTCSR_RW) | (data & LPTCSR_RW); }
else { if ((PA & 1) == 0) lpt_unit.buf = data & 0177; /* buffer */
lpt_csr = lpt_csr & ~CSR_DONE;
CLR_INT (LPT);
if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) ||
(lpt_unit.buf == 012)) sim_activate (&lpt_unit, lpt_unit.wait);
else sim_activate (&lpt_unit, 0); }
return SCPE_OK;
}
t_stat lpt_svc (UNIT *uptr)
{
lpt_csr = lpt_csr | CSR_ERR | CSR_DONE;
if (lpt_csr & CSR_IE) SET_INT (LPT);
if ((lpt_unit.flags & UNIT_ATT) == 0)
return IORETURN (lpt_stopioe, SCPE_UNATT);
if (putc (lpt_unit.buf & 0177, lpt_unit.fileref) == EOF) {
perror ("LPT I/O error");
clearerr (lpt_unit.fileref);
return SCPE_IOERR; }
lpt_csr = lpt_csr & ~CSR_ERR;
lpt_unit.pos = ftell (lpt_unit.fileref);
return SCPE_OK;
}
t_stat lpt_reset (DEVICE *dptr)
{
lpt_unit.buf = 0;
lpt_csr = CSR_DONE;
if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR;
CLR_INT (LPT);
sim_cancel (&lpt_unit); /* deactivate unit */
return SCPE_OK;
}
t_stat lpt_attach (UNIT *uptr, char *cptr)
{
t_stat reason;
lpt_csr = lpt_csr & ~CSR_ERR;
reason = attach_unit (uptr, cptr);
if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR;
return reason;
}
t_stat lpt_detach (UNIT *uptr)
{
lpt_csr = lpt_csr | CSR_ERR;
return detach_unit (uptr);
}

592
PDP11/pdp11_rk.c Normal file
View File

@@ -0,0 +1,592 @@
/* pdp11_rk.c: RK11 cartridge disk simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
rk RK11/RK05 cartridge disk
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
24-Nov-01 RMS Converted FLG to array
09-Nov-01 RMS Added bus map support
07-Sep-01 RMS Revised device disable and interrupt mechanisms
26-Apr-01 RMS Added device enable/disable support
25-Mar-01 RMS Fixed block fill calculation
15-Feb-01 RMS Corrected bootstrap string
29-Jun-96 RMS Added unit disable support
The RK11 is an eight drive cartridge disk subsystem. An RK05 drive
consists of 203 cylinders, each with 2 surfaces containing 12 sectors
of 512 bytes.
The most complicated part of the RK11 controller is the concept of
interrupt "polling". While only one read or write can occur at a
time, the controller supports multiple seeks. When a seek completes,
if done is set the drive attempts to interrupt. If an interrupt is
already pending, the interrupt is "queued" until it can be processed.
When an interrupt occurs, RKDS<15:13> is loaded with the number of the
interrupting drive.
To implement this structure, and to assure that read/write interrupts
take priority over seek interrupts, the controller contains an
interrupt queue, rkintq, with a bit for a controller interrupt and
then one for each drive. In addition, the drive number of the last
non-seeking drive is recorded in last_drv.
*/
#include "pdp11_defs.h"
/* Constants */
#define RK_NUMWD 256 /* words/sector */
#define RK_NUMSC 12 /* sectors/surface */
#define RK_NUMSF 2 /* surfaces/cylinder */
#define RK_NUMCY 203 /* cylinders/drive */
#define RK_NUMTR (RK_NUMCY * RK_NUMSF) /* tracks/drive */
#define RK_NUMDR 8 /* drives/controller */
#define RK_M_NUMDR 07
#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */
#define RK_CTLI 1 /* controller int */
#define RK_SCPI(x) (2u << (x)) /* drive int */
#define RK_MAXFR (1 << 16) /* max transfer */
/* Flags in the unit flags word */
#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */
#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */
#define UNIT_W_UF 3 /* user flags width */
#define UNIT_HWLK (1u << UNIT_V_HWLK)
#define UNIT_SWLK (1u << UNIT_V_SWLK)
#define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write prot */
/* Parameters in the unit descriptor */
#define CYL u3 /* current cylinder */
#define FUNC u4 /* function */
/* RKDS */
#define RKDS_SC 0000017 /* sector counter */
#define RKDS_ON_SC 0000020 /* on sector */
#define RKDS_WLK 0000040 /* write locked */
#define RKDS_RWS 0000100 /* rd/wr/seek ready */
#define RKDS_RDY 0000200 /* drive ready */
#define RKDS_SC_OK 0000400 /* SC valid */
#define RKDS_INC 0001000 /* seek incomplete */
#define RKDS_UNSAFE 0002000 /* unsafe */
#define RKDS_RK05 0004000 /* RK05 */
#define RKDS_PWR 0010000 /* power low */
#define RKDS_ID 0160000 /* drive ID */
#define RKDS_V_ID 13
/* RKER */
#define RKER_WCE 0000001 /* write check */
#define RKER_CSE 0000002 /* checksum */
#define RKER_NXS 0000040 /* nx sector */
#define RKER_NXC 0000100 /* nx cylinder */
#define RKER_NXD 0000200 /* nx drive */
#define RKER_TE 0000400 /* timing error */
#define RKER_DLT 0001000 /* data late */
#define RKER_NXM 0002000 /* nx memory */
#define RKER_PGE 0004000 /* programming error */
#define RKER_SKE 0010000 /* seek error */
#define RKER_WLK 0020000 /* write lock */
#define RKER_OVR 0040000 /* overrun */
#define RKER_DRE 0100000 /* drive error */
#define RKER_IMP 0177743 /* implemented */
#define RKER_SOFT (RKER_WCE+RKER_CSE) /* soft errors */
#define RKER_HARD 0177740 /* hard errors */
/* RKCS */
#define RKCS_M_FUNC 0000007 /* function */
#define RKCS_CTLRESET 0
#define RKCS_WRITE 1
#define RKCS_READ 2
#define RKCS_WCHK 3
#define RKCS_SEEK 4
#define RKCS_RCHK 5
#define RKCS_DRVRESET 6
#define RKCS_WLK 7
#define RKCS_V_FUNC 1
#define RKCS_MEX 0000060 /* memory extension */
#define RKCS_V_MEX 4
#define RKCS_SSE 0000400 /* stop on soft err */
#define RKCS_FMT 0002000 /* format */
#define RKCS_INH 0004000 /* inhibit increment */
#define RKCS_SCP 0020000 /* search complete */
#define RKCS_HERR 0040000 /* hard error */
#define RKCS_ERR 0100000 /* error */
#define RKCS_REAL 0026776 /* kept here */
#define RKCS_RW 0006576 /* read/write */
#define GET_FUNC(x) (((x) >> RKCS_V_FUNC) & RKCS_M_FUNC)
/* RKDA */
#define RKDA_V_SECT 0 /* sector */
#define RKDA_M_SECT 017
#define RKDA_V_TRACK 4 /* track */
#define RKDA_M_TRACK 0777
#define RKDA_V_CYL 5 /* cylinder */
#define RKDA_M_CYL 0377
#define RKDA_V_DRIVE 13 /* drive */
#define RKDA_M_DRIVE 07
#define RKDA_DRIVE (RKDA_M_DRIVE << RKDA_V_DRIVE)
#define GET_SECT(x) (((x) >> RKDA_V_SECT) & RKDA_M_SECT)
#define GET_CYL(x) (((x) >> RKDA_V_CYL) & RKDA_M_CYL)
#define GET_TRACK(x) (((x) >> RKDA_V_TRACK) & RKDA_M_TRACK)
#define GET_DRIVE(x) (((x) >> RKDA_V_DRIVE) & RKDA_M_DRIVE)
#define GET_DA(x) ((GET_TRACK (x) * RK_NUMSC) + GET_SECT (x))
/* RKBA */
#define RKBA_IMP 0177776 /* implemented */
#define RK_MIN 10
#define MAX(x,y) (((x) > (y))? (x): (y))
extern uint16 *M; /* memory */
extern int32 int_req[IPL_HLVL];
uint16 *rkxb = NULL; /* xfer buffer */
int32 rkcs = 0; /* control/status */
int32 rkds = 0; /* drive status */
int32 rkba = 0; /* memory address */
int32 rkda = 0; /* disk address */
int32 rker = 0; /* error status */
int32 rkwc = 0; /* word count */
int32 rkintq = 0; /* interrupt queue */
int32 last_drv = 0; /* last r/w drive */
int32 rk_stopioe = 1; /* stop on error */
int32 rk_swait = 10; /* seek time */
int32 rk_rwait = 10; /* rotate time */
int32 rk_enb = 1; /* device enable */
t_stat rk_svc (UNIT *uptr);
t_stat rk_reset (DEVICE *dptr);
void rk_go (void);
void rk_set_done (int32 error);
void rk_clr_done (void);
t_stat rk_boot (int32 unitno);
/* RK11 data structures
rk_dev RK device descriptor
rk_unit RK unit list
rk_reg RK register list
rk_mod RK modifier list
*/
UNIT rk_unit[] = {
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) },
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE, RK_SIZE) } };
REG rk_reg[] = {
{ ORDATA (RKCS, rkcs, 16) },
{ ORDATA (RKDA, rkda, 16) },
{ ORDATA (RKBA, rkba, 16) },
{ ORDATA (RKWC, rkwc, 16) },
{ ORDATA (RKDS, rkds, 16) },
{ ORDATA (RKER, rker, 16) },
{ ORDATA (INTQ, rkintq, 9) },
{ ORDATA (DRVN, last_drv, 3) },
{ FLDATA (INT, IREQ (RK), INT_V_RK) },
{ FLDATA (ERR, rkcs, CSR_V_ERR) },
{ FLDATA (DONE, rkcs, CSR_V_DONE) },
{ FLDATA (IE, rkcs, CSR_V_IE) },
{ DRDATA (STIME, rk_swait, 24), PV_LEFT },
{ DRDATA (RTIME, rk_rwait, 24), PV_LEFT },
{ URDATA (FLG, rk_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
RK_NUMDR, REG_HRO) },
{ FLDATA (STOP_IOE, rk_stopioe, 0) },
{ FLDATA (*DEVENB, rk_enb, 0), REG_HRO },
{ NULL } };
MTAB rk_mod[] = {
{ UNIT_HWLK, 0, "write enabled", "ENABLED", NULL },
{ UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL },
{ 0 } };
DEVICE rk_dev = {
"RK", rk_unit, rk_reg, rk_mod,
RK_NUMDR, 8, 24, 1, 8, 16,
NULL, NULL, &rk_reset,
&rk_boot, NULL, NULL };
/* I/O dispatch routine, I/O addresses 17777400 - 17777416
17777400 RKDS read only, constructed from "id'd drive"
plus current drive status flags
17777402 RKER read only, set as operations progress,
cleared by INIT or CONTROL RESET
17777404 RKCS read/write
17777406 RKWC read/write
17777410 RKBA read/write
17777412 RKDA read/write
17777414 RKMR read/write, unimplemented
17777416 RKDB read only, unimplemented
*/
t_stat rk_rd (int32 *data, int32 PA, int32 access)
{
UNIT *uptr;
switch ((PA >> 1) & 07) { /* decode PA<3:1> */
case 0: /* RKDS: read only */
rkds = (rkds & RKDS_ID) | RKDS_RK05 | RKDS_SC_OK |
(rand () % RK_NUMSC); /* random sector */
uptr = rk_dev.units + GET_DRIVE (rkda); /* selected unit */
if (uptr -> flags & UNIT_ATT) rkds = rkds | RKDS_RDY; /* attached? */
if (!sim_is_active (uptr)) rkds = rkds | RKDS_RWS; /* idle? */
if (uptr -> flags & UNIT_WPRT) rkds = rkds | RKDS_WLK;
if (GET_SECT (rkda) == (rkds & RKDS_SC)) rkds = rkds | RKDS_ON_SC;
*data = rkds;
return SCPE_OK;
case 1: /* RKER: read only */
*data = rker & RKER_IMP;
return SCPE_OK;
case 2: /* RKCS */
rkcs = rkcs & RKCS_REAL;
if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */
if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR;
*data = rkcs;
return SCPE_OK;
case 3: /* RKWC */
*data = rkwc;
return SCPE_OK;
case 4: /* RKBA */
*data = rkba & RKBA_IMP;
return SCPE_OK;
case 5: /* RKDA */
*data = rkda;
return SCPE_OK;
default:
*data = 0;
return SCPE_OK; } /* end switch */
}
t_stat rk_wr (int32 data, int32 PA, int32 access)
{
switch ((PA >> 1) & 07) { /* decode PA<3:1> */
case 0: /* RKDS: read only */
return SCPE_OK;
case 1: /* RKER: read only */
return SCPE_OK;
case 2: /* RKCS */
rkcs = rkcs & RKCS_REAL;
if (access == WRITEB) data = (PA & 1)?
(rkcs & 0377) | (data << 8): (rkcs & ~0377) | data;
if ((data & CSR_IE) == 0) { /* int disable? */
rkintq = 0; /* clr int queue */
CLR_INT (RK); } /* clr int request */
else if ((rkcs & (CSR_DONE + CSR_IE)) == CSR_DONE) {
rkintq = rkintq | RK_CTLI; /* queue ctrl int */
SET_INT (RK); } /* set int request */
rkcs = (rkcs & ~RKCS_RW) | (data & RKCS_RW);
if ((rkcs & CSR_DONE) && (data & CSR_GO)) rk_go (); /* new function? */
return SCPE_OK;
case 3: /* RKWC */
if (access == WRITEB) data = (PA & 1)?
(rkwc & 0377) | (data << 8): (rkwc & ~0377) | data;
rkwc = data;
return SCPE_OK;
case 4: /* RKBA */
if (access == WRITEB) data = (PA & 1)?
(rkba & 0377) | (data << 8): (rkba & ~0377) | data;
rkba = data & RKBA_IMP;
return SCPE_OK;
case 5: /* RKDA */
if ((rkcs & CSR_DONE) == 0) return SCPE_OK;
if (access == WRITEB) data = (PA & 1)?
(rkda & 0377) | (data << 8): (rkda & ~0377) | data;
rkda = data;
return SCPE_OK;
default:
return SCPE_OK; } /* end switch */
}
/* Initiate new function */
void rk_go (void)
{
int32 i, sect, cyl, func;
UNIT *uptr;
func = GET_FUNC (rkcs); /* get function */
if (func == RKCS_CTLRESET) { /* control reset? */
rker = 0; /* clear errors */
rkda = 0;
rkba = 0;
rkcs = CSR_DONE;
rkintq = 0; /* clr int queue */
CLR_INT (RK); /* clr int request */
return; }
rker = rker & ~RKER_SOFT; /* clear soft errors */
if (rker == 0) rkcs = rkcs & ~RKCS_ERR; /* redo summary */
rkcs = rkcs & ~RKCS_SCP; /* clear sch compl*/
rk_clr_done (); /* clear done */
last_drv = GET_DRIVE (rkda); /* get drive no */
uptr = rk_dev.units + last_drv; /* select unit */
if (uptr -> flags & UNIT_DIS) { /* not present? */
rk_set_done (RKER_NXD);
return; }
if (((uptr -> flags & UNIT_ATT) == 0) || sim_is_active (uptr)) {
rk_set_done (RKER_DRE); /* not att or busy */
return; }
if (rkcs & (RKCS_INH + RKCS_FMT)) { /* format? */
rk_set_done (RKER_PGE);
return; }
if ((func == RKCS_WRITE) && (uptr -> flags & UNIT_WPRT)) {
rk_set_done (RKER_WLK); /* write and locked? */
return; }
if (func == RKCS_WLK) { /* write lock? */
uptr -> flags = uptr -> flags | UNIT_SWLK;
rk_set_done (0);
return; }
if (func == RKCS_DRVRESET) { /* drive reset? */
uptr -> flags = uptr -> flags & ~UNIT_SWLK;
cyl = sect = 0;
func = RKCS_SEEK; }
else { sect = GET_SECT (rkda);
cyl = GET_CYL (rkda); }
if (sect >= RK_NUMSC) { /* bad sector? */
rk_set_done (RKER_NXS);
return; }
if (cyl >= RK_NUMCY) { /* bad cyl? */
rk_set_done (RKER_NXC);
return; }
i = abs (cyl - uptr -> CYL) * rk_swait; /* seek time */
if (func == RKCS_SEEK) { /* seek? */
rk_set_done (0); /* set done */
sim_activate (uptr, MAX (RK_MIN, i)); } /* schedule */
else sim_activate (uptr, i + rk_rwait);
uptr -> FUNC = func; /* save func */
uptr -> CYL = cyl; /* put on cylinder */
return;
}
/* Service unit timeout
If seek in progress, complete seek command
Else complete data transfer command
The unit control block contains the function and disk address for
the current command.
*/
t_stat rk_svc (UNIT *uptr)
{
int32 i, drv, err, awc, wc, t;
int32 da, track, sect;
t_addr ma;
uint16 comp;
drv = uptr - rk_dev.units; /* get drv number */
if (uptr -> FUNC == RKCS_SEEK) { /* seek */
rkcs = rkcs | RKCS_SCP; /* set seek done */
if (rkcs & CSR_IE) { /* ints enabled? */
rkintq = rkintq | RK_SCPI (drv); /* queue request */
if (rkcs & CSR_DONE) SET_INT (RK); }
else { rkintq = 0; /* clear queue */
CLR_INT (RK); } /* clear interrupt */
return SCPE_OK; }
if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */
rk_set_done (RKER_DRE);
return IORETURN (rk_stopioe, SCPE_UNATT); }
ma = ((rkcs & RKCS_MEX) << (16 - RKCS_V_MEX)) | rkba; /* get mem addr */
da = GET_DA (rkda) * RK_NUMWD; /* get disk addr */
wc = 0200000 - rkwc; /* get wd cnt */
err = fseek (uptr -> fileref, da * sizeof (int16), SEEK_SET);
if ((uptr -> FUNC == RKCS_READ) && (err == 0)) { /* read? */
i = fxread (rkxb, sizeof (int16), wc, uptr -> fileref);
err = ferror (uptr -> fileref);
for ( ; i < wc; i++) rkxb[i] = 0; /* fill buf */
if (t = Map_WriteW (ma, wc << 1, rkxb, UB)) { /* store buf */
rker = rker | RKER_NXM; /* NXM? set flg */
wc = wc - t; } /* adj wd cnt */
} /* end read */
if ((uptr -> FUNC == RKCS_WRITE) && (err == 0)) { /* write? */
if (t = Map_ReadW (ma, wc << 1, rkxb, UB)) { /* get buf */
rker = rker | RKER_NXM; /* NXM? set flg */
wc = wc - t; } /* adj wd cnt */
if (wc) { /* any xfer? */
awc = (wc + (RK_NUMWD - 1)) & ~(RK_NUMWD - 1); /* clr to */
for (i = wc; i < awc; i++) rkxb[i] = 0; /* end of blk */
fxwrite (rkxb, sizeof (int16), awc, uptr -> fileref);
err = ferror (uptr -> fileref); }
} /* end write */
if ((uptr -> FUNC == RKCS_WCHK) && (err == 0)) { /* write check? */
i = fxread (rkxb, sizeof (int16), wc, uptr -> fileref);
err = ferror (uptr -> fileref);
for ( ; i < wc; i++) rkxb[i] = 0; /* fill buf */
awc = wc; /* save wc */
for (wc = 0; (err == 0) && (wc < awc); wc++) { /* loop thru buf */
if (Map_ReadW (ma + (wc << 1), 2, &comp, UB)) { /* mem wd */
rker = rker | RKER_NXM; /* NXM? set flg */
break; }
if (comp != rkxb[wc]) { /* match to disk? */
rker = rker | RKER_WCE; /* no, err */
if (rkcs & RKCS_SSE) break; } }
} /* end wcheck */
rkwc = (rkwc + wc) & 0177777; /* final word count */
ma = ma + (wc << 1); /* final byte addr */
rkba = ma & RKBA_IMP; /* lower 16b */
rkcs = (rkcs & ~RKCS_MEX) | ((ma >> (16 - RKCS_V_MEX)) & RKCS_MEX);
da = da + wc + (RK_NUMWD - 1);
track = (da / RK_NUMWD) / RK_NUMSC;
sect = (da / RK_NUMWD) % RK_NUMSC;
rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT);
rk_set_done (0);
if (err != 0) { /* error? */
perror ("RK I/O error");
clearerr (uptr -> fileref);
return SCPE_IOERR; }
return SCPE_OK;
}
/* Interrupt state change routines
rk_set_done set done and possibly errors
rk_clr_done clear done
rk_inta acknowledge intererupt
*/
void rk_set_done (int32 error)
{
rkcs = rkcs | CSR_DONE; /* set done */
if (error != 0) {
rker = rker | error; /* update error */
if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */
if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; }
if (rkcs & CSR_IE) { /* int enable? */
rkintq = rkintq | RK_CTLI; /* set ctrl int */
SET_INT (RK); } /* request int */
else { rkintq = 0; /* clear queue */
CLR_INT (RK); }
return;
}
void rk_clr_done (void)
{
rkcs = rkcs & ~CSR_DONE; /* clear done */
rkintq = rkintq & ~RK_CTLI; /* clear ctl int */
CLR_INT (RK); /* clear int req */
return;
}
int32 rk_inta (void)
{
int32 i;
for (i = 0; i <= RK_NUMDR; i++) { /* loop thru intq */
if (rkintq & (1u << i)) { /* bit i set? */
rkintq = rkintq & ~(1u << i); /* clear bit i */
if (rkintq) SET_INT (RK); /* queue next */
rkds = (rkds & ~RKDS_ID) | /* id drive */
(((i == 0)? last_drv: i - 1) << RKDS_V_ID);
return VEC_RK; } } /* return vector */
rkintq = 0; /* clear queue */
return 0; /* passive release */
}
/* Device reset */
t_stat rk_reset (DEVICE *dptr)
{
int32 i;
UNIT *uptr;
rkcs = CSR_DONE;
rkda = rkba = rker = rkds = 0;
rkintq = last_drv = 0;
CLR_INT (RK);
for (i = 0; i < RK_NUMDR; i++) {
uptr = rk_dev.units + i;
sim_cancel (uptr);
uptr -> CYL = uptr -> FUNC = 0;
uptr -> flags = uptr -> flags & ~UNIT_SWLK; }
if (rkxb == NULL) rkxb = calloc (RK_MAXFR, sizeof (unsigned int16));
if (rkxb == NULL) return SCPE_MEM;
return SCPE_OK;
}
/* Device bootstrap */
#define BOOT_START 02000 /* start */
#define BOOT_UNIT 02006 /* unit number */
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32))
static const int32 boot_rom[] = {
0012706, 0002000, /* MOV #2000, SP */
0012700, 0000000, /* MOV #unit, R0 ; unit number */
0010003, /* MOV R0, R3 */
0000303, /* SWAB R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0012701, 0177412, /* MOV #RKDA, R1 ; csr */
0010311, /* MOV R3, (R1) ; load da */
0005041, /* CLR -(R1) ; clear ba */
0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */
0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */
0005002, /* CLR R2 */
0005003, /* CLR R3 */
0005004, /* CLR R4 */
0012705, 0045504, /* MOV #"DK, R5 */
0105711, /* TSTB (R1) */
0100376, /* BPL .-2 */
0105011, /* CLRB (R1) */
0005007 /* CLR PC */
};
t_stat rk_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
M[BOOT_UNIT >> 1] = unitno & RK_M_NUMDR;
saved_PC = BOOT_START;
return SCPE_OK;
}

627
PDP11/pdp11_rl.c Normal file
View File

@@ -0,0 +1,627 @@
/* pdp11_rl.c: RL11 (RLV12) cartridge disk simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
rl RL11(RLV12)/RL01/RL02 cartridge disk
30-Nov-01 MRS Added read only, extended SET/SHOW support
26-Nov-01 RMS Fixed per-drive error handling
24-Nov-01 RMS Converted FLG, CAPAC to arrays
19-Nov-01 RMS Fixed signed/unsigned mismatch in write check
09-Nov-01 RMS Added bus map, VAX support
07-Sep-01 RMS Revised device disable and interrupt mechanisms
20-Aug-01 RMS Added bad block option in attach
17-Jul-01 RMS Fixed warning from VC++ 6.0
26-Apr-01 RMS Added device enable/disable support
25-Mar-01 RMS Fixed block fill calculation
15-Feb-01 RMS Corrected bootstrap string
12-Nov-97 RMS Added bad block table command
25-Nov-96 RMS Default units to autosize
29-Jun-96 RMS Added unit disable support
The RL11 is a four drive cartridge disk subsystem. An RL01 drive
consists of 256 cylinders, each with 2 surfaces containing 40 sectors
of 256 bytes. An RL02 drive has 512 cylinders. The RLV12 is a
controller variant which supports 22b direct addressing.
The most complicated part of the RL11 controller is the way it does
seeks. Seeking is relative to the current disk address; this requires
keeping accurate track of the current cylinder. The RL11 will not
switch heads or cross cylinders during transfers.
The RL11 functions in three environments:
- PDP-11 Q22 systems - the I/O map is one for one, so it's safe to
go through the I/O map
- PDP-11 Unibus 22b systems - the RL11 behaves as an 18b Unibus
peripheral and must go through the I/O map
- VAX Q22 systems - the RL11 must go through the I/O map
*/
#if defined (USE_INT64) /* VAX version */
#include "vax_defs.h"
#define VM_VAX 1
#define RL_RDX 16
#define RL_18B FALSE /* always 22b */
#else /* PDP11 version */
#include "pdp11_defs.h"
#define VM_PDP11 1
#define RL_RDX 8
#define RL_18B (cpu_18b || cpu_ubm)
extern int32 cpu_18b, cpu_ubm;
#endif
/* Constants */
#define RL_NUMWD 128 /* words/sector */
#define RL_NUMSC 40 /* sectors/surface */
#define RL_NUMSF 2 /* surfaces/cylinder */
#define RL_NUMCY 256 /* cylinders/drive */
#define RL_NUMDR 4 /* drives/controller */
#define RL_MAXFR (1 << 16) /* max transfer */
#define RL01_SIZE (RL_NUMCY * RL_NUMSF * RL_NUMSC * RL_NUMWD) /* words/drive */
#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */
/* Flags in the unit flags word */
#define UNIT_V_WLK (UNIT_V_UF) /* hwre write lock */
#define UNIT_V_RL02 (UNIT_V_UF+1) /* RL01 vs RL02 */
#define UNIT_V_AUTO (UNIT_V_UF+2) /* autosize enable */
#define UNIT_W_UF 4 /* saved flags width */
#define UNIT_V_DUMMY (UNIT_V_UF + UNIT_W_UF) /* dummy flag */
#define UNIT_DUMMY (1 << UNIT_V_DUMMY)
#define UNIT_WLK (1u << UNIT_V_WLK)
#define UNIT_RL02 (1u << UNIT_V_RL02)
#define UNIT_AUTO (1u << UNIT_V_AUTO)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protected */
/* Parameters in the unit descriptor */
#define TRK u3 /* current track */
#define STAT u4 /* status */
/* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */
#define RLDS_LOAD 0 /* no cartridge */
#define RLDS_LOCK 5 /* lock on */
#define RLDS_BHO 0000010 /* brushes home NI */
#define RLDS_HDO 0000020 /* heads out NI */
#define RLDS_CVO 0000040 /* cover open NI */
#define RLDS_HD 0000100 /* head select ^ */
#define RLDS_RL02 0000200 /* RL02 */
#define RLDS_DSE 0000400 /* drv sel err NI */
#define RLDS_VCK 0001000 /* vol check * */
#define RLDS_WGE 0002000 /* wr gate err * */
#define RLDS_SPE 0004000 /* spin err * */
#define RLDS_STO 0010000 /* seek time out NI */
#define RLDS_WLK 0020000 /* wr locked */
#define RLDS_HCE 0040000 /* hd curr err NI */
#define RLDS_WDE 0100000 /* wr data err NI */
#define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */
#define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */
#define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \
RLDS_VCK+RLDS_DSE) /* errors bits */
/* RLCS */
#define RLCS_DRDY 0000001 /* drive ready */
#define RLCS_M_FUNC 0000007 /* function */
#define RLCS_NOP 0
#define RLCS_WCHK 1
#define RLCS_GSTA 2
#define RLCS_SEEK 3
#define RLCS_RHDR 4
#define RLCS_WRITE 5
#define RLCS_READ 6
#define RLCS_RNOHDR 7
#define RLCS_V_FUNC 1
#define RLCS_M_MEX 03 /* memory extension */
#define RLCS_V_MEX 4
#define RLCS_MEX (RLCS_M_MEX << RLCS_V_MEX)
#define RLCS_M_DRIVE 03
#define RLCS_V_DRIVE 8
#define RLCS_INCMP 0002000 /* incomplete */
#define RLCS_CRC 0004000 /* CRC error */
#define RLCS_HDE 0010000 /* header error */
#define RLCS_NXM 0020000 /* non-exist memory */
#define RLCS_DRE 0040000 /* drive error */
#define RLCS_ERR 0100000 /* error summary */
#define RLCS_ALLERR (RLCS_ERR+RLCS_DRE+RLCS_NXM+RLCS_HDE+RLCS_CRC+RLCS_INCMP)
#define RLCS_RW 0001776 /* read/write */
#define GET_FUNC(x) (((x) >> RLCS_V_FUNC) & RLCS_M_FUNC)
#define GET_DRIVE(x) (((x) >> RLCS_V_DRIVE) & RLCS_M_DRIVE)
/* RLDA */
#define RLDA_SK_DIR 0000004 /* direction */
#define RLDA_GS_CLR 0000010 /* clear errors */
#define RLDA_SK_HD 0000020 /* head select */
#define RLDA_V_SECT 0 /* sector */
#define RLDA_M_SECT 077
#define RLDA_V_TRACK 6 /* track */
#define RLDA_M_TRACK 01777
#define RLDA_HD0 (0 << RLDA_V_TRACK)
#define RLDA_HD1 (1u << RLDA_V_TRACK)
#define RLDA_V_CYL 7 /* cylinder */
#define RLDA_M_CYL 0777
#define RLDA_TRACK (RLDA_M_TRACK << RLDA_V_TRACK)
#define RLDA_CYL (RLDA_M_CYL << RLDA_V_CYL)
#define GET_SECT(x) (((x) >> RLDA_V_SECT) & RLDA_M_SECT)
#define GET_CYL(x) (((x) >> RLDA_V_CYL) & RLDA_M_CYL)
#define GET_TRACK(x) (((x) >> RLDA_V_TRACK) & RLDA_M_TRACK)
#define GET_DA(x) ((GET_TRACK (x) * RL_NUMSC) + GET_SECT (x))
/* RLBA */
#define RLBA_IMP 0177776 /* implemented */
/* RLBAE */
#define RLBAE_IMP 0000077 /* implemented */
extern int32 int_req[IPL_HLVL];
uint16 *rlxb = NULL; /* xfer buffer */
int32 rlcs = 0; /* control/status */
int32 rlba = 0; /* memory address */
int32 rlbae = 0; /* mem addr extension */
int32 rlda = 0; /* disk addr */
int32 rlmp = 0, rlmp1 = 0, rlmp2 = 0; /* mp register queue */
int32 rl_swait = 10; /* seek wait */
int32 rl_rwait = 10; /* rotate wait */
int32 rl_stopioe = 1; /* stop on error */
int32 rl_enb = 1; /* device enable */
t_stat rl_svc (UNIT *uptr);
t_stat rl_reset (DEVICE *dptr);
void rl_set_done (int32 error);
t_stat rl_boot (int32 unitno);
t_stat rl_attach (UNIT *uptr, char *cptr);
t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc);
extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds);
/* RL11 data structures
rl_dev RL device descriptor
rl_unit RL unit list
rl_reg RL register list
rl_mod RL modifier list
*/
UNIT rl_unit[] = {
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) },
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) },
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) },
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) } };
REG rl_reg[] = {
{ GRDATA (RLCS, rlcs, RL_RDX, 16, 0) },
{ GRDATA (RLDA, rlda, RL_RDX, 16, 0) },
{ GRDATA (RLBA, rlba, RL_RDX, 16, 0) },
{ GRDATA (RLBAE, rlbae, RL_RDX, 6, 0) },
{ GRDATA (RLMP, rlmp, RL_RDX, 16, 0) },
{ GRDATA (RLMP1, rlmp1, RL_RDX, 16, 0) },
{ GRDATA (RLMP2, rlmp2, RL_RDX, 16, 0) },
{ FLDATA (INT, IREQ (RL), INT_V_RL) },
{ FLDATA (ERR, rlcs, CSR_V_ERR) },
{ FLDATA (DONE, rlcs, CSR_V_DONE) },
{ FLDATA (IE, rlcs, CSR_V_IE) },
{ DRDATA (STIME, rl_swait, 24), PV_LEFT },
{ DRDATA (RTIME, rl_rwait, 24), PV_LEFT },
{ URDATA (FLG, rl_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
RL_NUMDR, REG_HRO) },
{ URDATA (CAPAC, rl_unit[0].capac, 10, 31, 0,
RL_NUMDR, PV_LEFT + REG_HRO) },
{ FLDATA (STOP_IOE, rl_stopioe, 0) },
{ FLDATA (*DEVENB, rl_enb, 0), REG_HRO },
{ NULL } };
MTAB rl_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad },
{ (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL },
{ (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL },
{ (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL },
{ (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL },
{ (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
{ UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
{ (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size },
{ (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size },
{ 0 } };
DEVICE rl_dev = {
"RL", rl_unit, rl_reg, rl_mod,
RL_NUMDR, RL_RDX, 24, 1, RL_RDX, 16,
NULL, NULL, &rl_reset,
&rl_boot, &rl_attach, NULL };
/* I/O dispatch routine, I/O addresses 17774400 - 17774407
17774400 RLCS read/write
17774402 RLBA read/write
17774404 RLDA read/write
17774406 RLMP read/write
17774410 RLBAE read/write
*/
t_stat rl_rd (int32 *data, int32 PA, int32 access)
{
UNIT *uptr;
switch ((PA >> 1) & 07) { /* decode PA<2:1> */
case 0: /* RLCS */
rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR;
uptr = rl_dev.units + GET_DRIVE (rlcs);
if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY;
else rlcs = rlcs | RLCS_DRDY; /* see if ready */
*data = rlcs;
break;
case 1: /* RLBA */
*data = rlba & RLBA_IMP;
break;
case 2: /* RLDA */
*data = rlda;
break;
case 3: /* RLMP */
*data = rlmp;
rlmp = rlmp1; /* ripple data */
rlmp1 = rlmp2;
break;
case 4: /* RLBAE */
if (RL_18B) return SCPE_NXM; /* not in RL11 */
*data = rlbae & RLBAE_IMP;
break; } /* end switch */
return SCPE_OK;
}
t_stat rl_wr (int32 data, int32 PA, int32 access)
{
int32 curr, offs, newc, maxc;
UNIT *uptr;
switch ((PA >> 1) & 07) { /* decode PA<2:1> */
case 0: /* RLCS */
rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR;
uptr = rl_dev.units + GET_DRIVE (data); /* get new drive */
if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY;
else rlcs = rlcs | RLCS_DRDY; /* see if ready */
if (access == WRITEB) data = (PA & 1)?
(rlcs & 0377) | (data << 8): (rlcs & ~0377) | data;
rlcs = (rlcs & ~RLCS_RW) | (data & RLCS_RW);
rlbae = (rlbae & ~RLCS_M_MEX) | ((rlcs >> RLCS_V_MEX) & RLCS_M_MEX);
if (data & CSR_DONE) { /* ready set? */
if ((data & CSR_IE) == 0) CLR_INT (RL);
else if ((rlcs & (CSR_DONE + CSR_IE)) == CSR_DONE)
SET_INT (RL);
return SCPE_OK; }
CLR_INT (RL); /* clear interrupt */
rlcs = rlcs & ~RLCS_ALLERR; /* clear errors */
switch (GET_FUNC (rlcs)) { /* case on RLCS<3:1> */
case RLCS_NOP: /* nop */
rl_set_done (0);
break;
case RLCS_SEEK: /* seek */
curr = GET_CYL (uptr -> TRK); /* current cylinder */
offs = GET_CYL (rlda); /* offset */
if (rlda & RLDA_SK_DIR) { /* in or out? */
newc = curr + offs; /* out */
maxc = (uptr -> flags & UNIT_RL02)?
RL_NUMCY * 2: RL_NUMCY;
if (newc >= maxc) newc = maxc - 1; }
else { newc = curr - offs; /* in */
if (newc < 0) newc = 0; }
uptr -> TRK = (newc << RLDA_V_CYL) | /* put on track */
((rlda & RLDA_SK_HD)? RLDA_HD1: RLDA_HD0);
sim_activate (uptr, rl_swait * abs (newc - curr));
break;
default: /* data transfer */
sim_activate (uptr, rl_swait); /* activate unit */
break; } /* end switch func */
break; /* end case RLCS */
case 1: /* RLBA */
if (access == WRITEB) data = (PA & 1)?
(rlba & 0377) | (data << 8): (rlba & ~0377) | data;
rlba = data & RLBA_IMP;
break;
case 2: /* RLDA */
if (access == WRITEB) data = (PA & 1)?
(rlda & 0377) | (data << 8): (rlda & ~0377) | data;
rlda = data;
break;
case 3: /* RLMP */
if (access == WRITEB) data = (PA & 1)?
(rlmp & 0377) | (data << 8): (rlmp & ~0377) | data;
rlmp = rlmp1 = rlmp2 = data;
break;
case 4: /* RLBAE */
if (RL_18B) return SCPE_NXM; /* not in RL11 */
if (PA & 1) return SCPE_OK;
rlbae = data & RLBAE_IMP;
rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
break; } /* end switch */
return SCPE_OK;
}
/* Service unit timeout
If seek in progress, complete seek command
Else complete data transfer command
The unit control block contains the function and cylinder for
the current command.
*/
t_stat rl_svc (UNIT *uptr)
{
int32 err, wc, maxwc, t;
int32 i, func, da, awc;
t_addr ma;
uint16 comp;
func = GET_FUNC (rlcs); /* get function */
if (func == RLCS_GSTA) { /* get status */
if (rlda & RLDA_GS_CLR) uptr -> STAT = uptr -> STAT & ~RLDS_ERR;
rlmp = uptr -> STAT | (uptr -> TRK & RLDS_HD) |
((uptr -> flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT);
if (uptr -> flags & UNIT_RL02) rlmp = rlmp | RLDS_RL02;
if (uptr -> flags & UNIT_WPRT) rlmp = rlmp | RLDS_WLK;
rlmp2 = rlmp1 = rlmp;
rl_set_done (0); /* done */
return SCPE_OK; }
if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */
rlcs = rlcs & ~RLCS_DRDY; /* clear drive ready */
uptr -> STAT = uptr -> STAT | RLDS_SPE; /* spin error */
rl_set_done (RLCS_ERR | RLCS_INCMP); /* flag error */
return IORETURN (rl_stopioe, SCPE_UNATT); }
if ((func == RLCS_WRITE) && (uptr -> flags & UNIT_WPRT)) {
uptr -> STAT = uptr -> STAT | RLDS_WGE; /* write and locked */
rl_set_done (RLCS_ERR | RLCS_DRE);
return SCPE_OK; }
if (func == RLCS_SEEK) { /* seek? */
rl_set_done (0); /* done */
return SCPE_OK; }
if (func == RLCS_RHDR) { /* read header? */
rlmp = (uptr -> TRK & RLDA_TRACK) | GET_SECT (rlda);
rlmp1 = rlmp2 = 0;
rl_set_done (0); /* done */
return SCPE_OK; }
if (((func != RLCS_RNOHDR) && ((uptr -> TRK & RLDA_CYL) != (rlda & RLDA_CYL)))
|| (GET_SECT (rlda) >= RL_NUMSC)) { /* bad cyl or sector? */
rl_set_done (RLCS_ERR | RLCS_HDE | RLCS_INCMP); /* wrong cylinder? */
return SCPE_OK; }
ma = (rlbae << 16) | rlba; /* get mem addr */
da = GET_DA (rlda) * RL_NUMWD; /* get disk addr */
wc = 0200000 - rlmp; /* get true wc */
maxwc = (RL_NUMSC - GET_SECT (rlda)) * RL_NUMWD; /* max transfer */
if (wc > maxwc) wc = maxwc; /* track overrun? */
err = fseek (uptr -> fileref, da * sizeof (int16), SEEK_SET);
if ((func >= RLCS_READ) && (err == 0)) { /* read (no hdr)? */
i = fxread (rlxb, sizeof (int16), wc, uptr -> fileref);
err = ferror (uptr -> fileref);
for ( ; i < wc; i++) rlxb[i] = 0; /* fill buffer */
if (t = Map_WriteW (ma, wc << 1, rlxb, UB)) { /* store buffer */
rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */
wc = wc - t; } /* adjust wc */
} /* end read */
if ((func == RLCS_WRITE) && (err == 0)) { /* write? */
if (t = Map_ReadW (ma, wc << 1, rlxb, UB)) { /* fetch buffer */
rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */
wc = wc - t; } /* adj xfer lnt */
if (wc) { /* any xfer? */
awc = (wc + (RL_NUMWD - 1)) & ~(RL_NUMWD - 1); /* clr to */
for (i = wc; i < awc; i++) rlxb[i] = 0; /* end of blk */
fxwrite (rlxb, sizeof (int16), awc, uptr -> fileref);
err = ferror (uptr -> fileref); }
} /* end write */
if ((func == RLCS_WCHK) && (err == 0)) { /* write check? */
i = fxread (rlxb, sizeof (int16), wc, uptr -> fileref);
err = ferror (uptr -> fileref);
for ( ; i < wc; i++) rlxb[i] = 0; /* fill buffer */
awc = wc; /* save wc */
for (wc = 0; (err == 0) && (wc < awc); wc++) { /* loop thru buf */
if (Map_ReadW (ma + (wc << 1), 2, &comp, UB)) { /* mem wd */
rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */
break; }
if (comp != rlxb[wc]) /* check to buf */
rlcs = rlcs | RLCS_ERR | RLCS_CRC;
} /* end for */
} /* end wcheck */
rlmp = (rlmp + wc) & 0177777; /* final word count */
if (rlmp != 0) rlcs = rlcs | RLCS_ERR | RLCS_INCMP; /* completed? */
ma = ma + (wc << 1); /* final byte addr */
rlbae = (ma >> 16) & RLBAE_IMP; /* upper 6b */
rlba = ma & RLBA_IMP; /* lower 16b */
rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX);
rlda = rlda + ((wc + (RL_NUMWD - 1)) / RL_NUMWD);
rl_set_done (0);
if (err != 0) { /* error? */
perror ("RL I/O error");
clearerr (uptr -> fileref);
return SCPE_IOERR; }
return SCPE_OK;
}
/* Set done and possibly errors */
void rl_set_done (int32 status)
{
rlcs = rlcs | status | CSR_DONE; /* set done */
if (rlcs & CSR_IE) SET_INT (RL);
else CLR_INT (RL);
return;
}
/* Device reset
Note that the RL11 does NOT recalibrate its drives on RESET
*/
t_stat rl_reset (DEVICE *dptr)
{
int32 i;
UNIT *uptr;
rlcs = CSR_DONE;
rlda = rlba = rlbae = rlmp = rlmp1 = rlmp2 = 0;
CLR_INT (RL);
for (i = 0; i < RL_NUMDR; i++) {
uptr = rl_dev.units + i;
sim_cancel (uptr);
uptr -> STAT = 0; }
if (rlxb == NULL) rlxb = calloc (RL_MAXFR, sizeof (unsigned int16));
if (rlxb == NULL) return SCPE_MEM;
return SCPE_OK;
}
/* Attach routine */
t_stat rl_attach (UNIT *uptr, char *cptr)
{
int32 p;
t_stat r;
uptr -> capac = (uptr -> flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
r = attach_unit (uptr, cptr);
if ((r != SCPE_OK) || ((uptr -> flags & UNIT_AUTO) == 0)) return r;
uptr -> TRK = 0; /* cylinder 0 */
uptr -> STAT = RLDS_VCK; /* new volume */
if (fseek (uptr -> fileref, 0, SEEK_END)) return SCPE_OK;
if ((p = ftell (uptr -> fileref)) == 0) {
if (uptr -> flags & UNIT_RO) return SCPE_OK;
return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); }
if (p > (RL01_SIZE * sizeof (int16))) {
uptr -> flags = uptr -> flags | UNIT_RL02;
uptr -> capac = RL02_SIZE; }
else { uptr -> flags = uptr -> flags & ~UNIT_RL02;
uptr -> capac = RL01_SIZE; }
return SCPE_OK;
}
/* Set size routine */
t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if (uptr -> flags & UNIT_ATT) return SCPE_ALATT;
uptr -> capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE;
return SCPE_OK;
}
/* Set bad block routine */
t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc)
{
return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD);
}
/* Device bootstrap */
#if defined (VM_PDP11)
#define BOOT_START 02000 /* start */
#define BOOT_UNIT 02006 /* unit number */
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32))
static const int32 boot_rom[] = {
0012706, 0002000, /* MOV #2000, SP */
0012700, 0000000, /* MOV #UNIT, R0 */
0010003, /* MOV R0, R3 */
0000303, /* SWAB R3 */
0012701, 0174400, /* MOV #RLCS, R1 ; csr */
0012761, 0000013, 0000004, /* MOV #13, 4(R1) ; clr err */
0052703, 0000004, /* BIS #4, R3 ; unit+gstat */
0010311, /* MOV R3, (R1) ; issue cmd */
0105711, /* TSTB (R1) ; wait */
0100376, /* BPL .-2 */
0105003, /* CLRB R3 */
0052703, 0000010, /* BIS #10, R3 ; unit+rdhdr */
0010311, /* MOV R3, (R1) ; issue cmd */
0105711, /* TSTB (R1) ; wait */
0100376, /* BPL .-2 */
0016102, 0000006, /* MOV 6(R1), R2 ; get hdr */
0042702, 0000077, /* BIC #77, R2 ; clr sector */
0005202, /* INC R2 ; magic bit */
0010261, 0000004, /* MOV R2, 4(R1) ; seek to 0 */
0105003, /* CLRB R3 */
0052703, 0000006, /* BIS #6, R3 ; unit+seek */
0010311, /* MOV R3, (R1) ; issue cmd */
0105711, /* TSTB (R1) ; wait */
0100376, /* BPL .-2 */
0005061, 0000002, /* CLR 2(R1) ; clr ba */
0005061, 0000004, /* CLR 4(R1) ; clr da */
0012761, 0177000, 0000006, /* MOV #-512., 6(R1) ; set wc */
0105003, /* CLRB R3 */
0052703, 0000014, /* BIS #14, R3 ; unit+read */
0010311, /* MOV R3, (R1) ; issue cmd */
0105711, /* TSTB (R1) ; wait */
0100376, /* BPL .-2 */
0042711, 0000377, /* BIC #377, (R1) */
0005002, /* CLR R2 */
0005003, /* CLR R3 */
0005004, /* CLR R4 */
0012705, 0046104, /* MOV "DL, R5 */
0005007 /* CLR PC */
};
t_stat rl_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
extern uint16 *M;
for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
M[BOOT_UNIT >> 1] = unitno & RLCS_M_DRIVE;
saved_PC = BOOT_START;
return SCPE_OK;
}
#else
t_stat rl_boot (int32 unitno)
{
return SCPE_NOFNC;
}
#endif

1155
PDP11/pdp11_rp.c Normal file

File diff suppressed because it is too large Load Diff

1849
PDP11/pdp11_rq.c Normal file

File diff suppressed because it is too large Load Diff

435
PDP11/pdp11_rx.c Normal file
View File

@@ -0,0 +1,435 @@
/* pdp11_rx.c: RX11/RX01 floppy disk simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
rx RX11/RX01 floppy disk
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
24-Nov-01 RMS Converted FLG to array
07-Sep-01 RMS Revised device disable and interrupt mechanisms
17-Jul-01 RMS Fixed warning from VC++ 6.0
26-Apr-01 RMS Added device enable/disable support
13-Apr-01 RMS Revised for register arrays
15-Feb-01 RMS Corrected bootstrap string
14-Apr-99 RMS Changed t_addr to unsigned
An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B.
Tracks are numbered 0-76, sectors 1-26.
*/
#include "pdp11_defs.h"
#define RX_NUMTR 77 /* tracks/disk */
#define RX_M_TRACK 0377
#define RX_NUMSC 26 /* sectors/track */
#define RX_M_SECTOR 0177
#define RX_NUMBY 128 /* bytes/sector */
#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */
#define RX_NUMDR 2 /* drives/controller */
#define RX_M_NUMDR 01
#define UNIT_V_WLK (UNIT_V_UF) /* write locked */
#define UNIT_WLK (1u << UNIT_V_UF)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
#define IDLE 0 /* idle state */
#define RWDS 1 /* rw, sect next */
#define RWDT 2 /* rw, track next */
#define FILL 3 /* fill buffer */
#define EMPTY 4 /* empty buffer */
#define CMD_COMPLETE 5 /* set done next */
#define INIT_COMPLETE 6 /* init compl next */
#define RXCS_V_FUNC 1 /* function */
#define RXCS_M_FUNC 7
#define RXCS_FILL 0 /* fill buffer */
#define RXCS_EMPTY 1 /* empty buffer */
#define RXCS_WRITE 2 /* write sector */
#define RXCS_READ 3 /* read sector */
#define RXCS_RXES 5 /* read status */
#define RXCS_WRDEL 6 /* write del data */
#define RXCS_ECODE 7 /* read error code */
#define RXCS_V_DRV 4 /* drive select */
#define RXCS_V_DONE 5 /* done */
#define RXCS_V_TR 7 /* xfer request */
#define RXCS_V_INIT 14 /* init */
#define RXCS_FUNC (RXCS_M_FUNC << RXCS_V_FUNC)
#define RXCS_DRV (1u << RXCS_V_DRV)
#define RXCS_DONE (1u << RXCS_V_DONE)
#define RXCS_TR (1u << RXCS_V_TR)
#define RXCS_INIT (1u << RXCS_V_INIT)
#define RXCS_ROUT (CSR_ERR+RXCS_TR+CSR_IE+RXCS_DONE)
#define RXCS_IMP (RXCS_ROUT+RXCS_DRV+RXCS_FUNC)
#define RXCS_RW (CSR_IE) /* read/write */
#define RXES_CRC 0001 /* CRC error */
#define RXES_PAR 0002 /* parity error */
#define RXES_ID 0004 /* init done */
#define RXES_WLK 0010 /* write protect */
#define RXES_DD 0100 /* deleted data */
#define RXES_DRDY 0200 /* drive ready */
#define TRACK u3 /* current track */
#define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY
extern int32 int_req[IPL_HLVL];
int32 rx_csr = 0; /* control/status */
int32 rx_dbr = 0; /* data buffer */
int32 rx_esr = 0; /* error status */
int32 rx_ecode = 0; /* error code */
int32 rx_track = 0; /* desired track */
int32 rx_sector = 0; /* desired sector */
int32 rx_state = IDLE; /* controller state */
int32 rx_stopioe = 1; /* stop on error */
int32 rx_cwait = 100; /* command time */
int32 rx_swait = 10; /* seek, per track */
int32 rx_xwait = 1; /* tr set time */
unsigned int8 rx_buf[RX_NUMBY] = { 0 }; /* sector buffer */
int32 bptr = 0; /* buffer pointer */
int32 rx_enb = 1; /* device enable */
t_stat rx_svc (UNIT *uptr);
t_stat rx_reset (DEVICE *dptr);
t_stat rx_boot (int32 unitno);
/* RX11 data structures
rx_dev RX device descriptor
rx_unit RX unit list
rx_reg RX register list
rx_mod RX modifier list
*/
UNIT rx_unit[] = {
{ UDATA (&rx_svc,
UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) },
{ UDATA (&rx_svc,
UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) } };
REG rx_reg[] = {
{ ORDATA (RXCS, rx_csr, 16) },
{ ORDATA (RXDB, rx_dbr, 8) },
{ ORDATA (RXES, rx_esr, 8) },
{ ORDATA (RXERR, rx_ecode, 8) },
{ ORDATA (RXTA, rx_track, 8) },
{ ORDATA (RXSA, rx_sector, 8) },
{ ORDATA (STAPTR, rx_state, 3), REG_RO },
{ ORDATA (BUFPTR, bptr, 7) },
{ FLDATA (INT, IREQ (RX), INT_V_RX) },
{ FLDATA (ERR, rx_csr, CSR_V_ERR) },
{ FLDATA (TR, rx_csr, RXCS_V_TR) },
{ FLDATA (IE, rx_csr, CSR_V_IE) },
{ FLDATA (DONE, rx_csr, RXCS_V_DONE) },
{ DRDATA (CTIME, rx_cwait, 24), PV_LEFT },
{ DRDATA (STIME, rx_swait, 24), PV_LEFT },
{ DRDATA (XTIME, rx_xwait, 24), PV_LEFT },
{ URDATA (FLG, rx_unit[0].flags, 2, 1, UNIT_V_WLK, RX_NUMDR, REG_HRO) },
{ FLDATA (STOP_IOE, rx_stopioe, 0) },
{ BRDATA (SBUF, rx_buf, 8, 8, RX_NUMBY) },
{ FLDATA (*DEVENB, rx_enb, 0), REG_HRO },
{ NULL } };
MTAB rx_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ 0 } };
DEVICE rx_dev = {
"RX", rx_unit, rx_reg, rx_mod,
RX_NUMDR, 8, 20, 1, 8, 8,
NULL, NULL, &rx_reset,
&rx_boot, NULL, NULL };
/* I/O dispatch routine, I/O addresses 17777170 - 17777172
17777170 floppy CSR
17777172 floppy data register
*/
t_stat rx_rd (int32 *data, int32 PA, int32 access)
{
switch ((PA >> 1) & 1) { /* decode PA<1> */
case 0: /* RXCS */
rx_csr = rx_csr & RXCS_IMP; /* clear junk */
*data = rx_csr & RXCS_ROUT;
break;
case 1: /* RXDB */
if (rx_state == EMPTY) { /* empty? */
sim_activate (&rx_unit[0], rx_xwait);
rx_csr = rx_csr & ~RXCS_TR; } /* clear xfer */
*data = rx_dbr; /* return data */
break; } /* end switch PA */
return SCPE_OK;
}
t_stat rx_wr (int32 data, int32 PA, int32 access)
{
int32 drv;
switch ((PA >> 1) & 1) { /* decode PA<1> */
/* Writing RXCS, three cases:
1. Writing INIT, reset device
2. Idle and writing new function
- clear error, done, transfer ready, int req
- save int enable, function, drive
- start new function
3. Otherwise, write IE and update interrupts
*/
case 0: /* RXCS */
rx_csr = rx_csr & RXCS_IMP; /* clear junk */
if (access == WRITEB) data = (PA & 1)? /* write byte? */
(rx_csr & 0377) | (data << 8): (rx_csr & ~0377) | data;
if (data & RXCS_INIT) { /* initialize? */
rx_reset (&rx_dev); /* reset device */
return SCPE_OK; } /* end if init */
if ((data & CSR_GO) && (rx_state == IDLE)) { /* new function? */
rx_csr = data & (CSR_IE + RXCS_DRV + RXCS_FUNC);
bptr = 0; /* clear buf pointer */
switch ((data >> RXCS_V_FUNC) & RXCS_M_FUNC) {
case RXCS_FILL:
rx_state = FILL; /* state = fill */
rx_csr = rx_csr | RXCS_TR; /* xfer is ready */
break;
case RXCS_EMPTY:
rx_state = EMPTY; /* state = empty */
sim_activate (&rx_unit[0], rx_xwait);
break;
case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
rx_state = RWDS; /* state = get sector */
rx_csr = rx_csr | RXCS_TR; /* xfer is ready */
rx_esr = rx_esr & RXES_ID; /* clear errors */
break;
default:
rx_state = CMD_COMPLETE; /* state = cmd compl */
drv = (data & RXCS_DRV) > 0; /* get drive number */
sim_activate (&rx_unit[drv], rx_cwait);
break; } /* end switch func */
return SCPE_OK; } /* end if GO */
if ((data & CSR_IE) == 0) CLR_INT (RX);
else if ((rx_csr & (RXCS_DONE + CSR_IE)) == RXCS_DONE)
SET_INT (RX);
rx_csr = (rx_csr & ~RXCS_RW) | (data & RXCS_RW);
break; /* end case RXCS */
/* Accessing RXDB, two cases:
1. Write idle, write
2. Write not idle and TR set, state dependent
*/
case 1: /* RXDB */
if ((PA & 1) || ((rx_state != IDLE) && ((rx_csr & RXCS_TR) == 0)))
return SCPE_OK; /* if ~IDLE, need tr */
rx_dbr = data & 0377; /* save data */
if ((rx_state == FILL) || (rx_state == RWDS)) { /* fill or sector? */
sim_activate (&rx_unit[0], rx_xwait); /* sched event */
rx_csr = rx_csr & ~RXCS_TR; } /* clear xfer */
if (rx_state == RWDT) { /* track? */
drv = (rx_csr & RXCS_DRV) > 0; /* get drive number */
sim_activate (&rx_unit[drv], /* sched done */
rx_swait * abs (rx_track - rx_unit[drv].TRACK));
rx_csr = rx_csr & ~RXCS_TR; } /* clear xfer */
break; /* end case RXDB */
} /* end switch PA */
return SCPE_OK;
}
/* Unit service; the action to be taken depends on the transfer state:
IDLE Should never get here, treat as unknown command
RWDS Just transferred sector, wait for track, set tr
RWDT Just transferred track, do read or write, finish command
FILL copy ir to rx_buf[bptr], advance ptr
if bptr > max, finish command, else set tr
EMPTY if bptr > max, finish command, else
copy rx_buf[bptr] to ir, advance ptr, set tr
CMD_COMPLETE copy requested data to ir, finish command
INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command
For RWDT and CMD_COMPLETE, the input argument is the selected drive;
otherwise, it is drive 0.
*/
t_stat rx_svc (UNIT *uptr)
{
int32 i, func;
t_addr da;
t_stat rval;
void rx_done (int new_dbr, int new_ecode);
rval = SCPE_OK; /* assume ok */
func = (rx_csr >> RXCS_V_FUNC) & RXCS_M_FUNC; /* get function */
switch (rx_state) { /* case on state */
case IDLE: /* idle */
rx_done (rx_esr, 0); /* done */
break;
case EMPTY: /* empty buffer */
if (bptr >= RX_NUMBY) rx_done (rx_esr, 0); /* done all? */
else { rx_dbr = rx_buf[bptr]; /* get next */
bptr = bptr + 1;
rx_csr = rx_csr | RXCS_TR; } /* set xfer */
break;
case FILL: /* fill buffer */
rx_buf[bptr] = rx_dbr; /* write next */
bptr = bptr + 1;
if (bptr < RX_NUMBY) rx_csr = rx_csr | RXCS_TR; /* if more, set xfer */
else rx_done (rx_esr, 0); /* else done */
break;
case RWDS: /* wait for sector */
rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */
rx_csr = rx_csr | RXCS_TR; /* set xfer */
rx_state = RWDT; /* advance state */
break;
case RWDT: /* wait for track */
rx_track = rx_dbr & RX_M_TRACK; /* save track */
if (rx_track >= RX_NUMTR) { /* bad track? */
rx_done (rx_esr, 0040); /* done, error */
break; }
uptr -> TRACK = rx_track; /* now on track */
if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */
rx_done (rx_esr, 0070); /* done, error */
break; }
if ((uptr -> flags & UNIT_BUF) == 0) { /* not buffered? */
rx_done (rx_esr, 0110); /* done, error */
rval = SCPE_UNATT; /* return error */
break; }
da = CALC_DA (rx_track, rx_sector); /* get disk address */
if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */
if (func == RXCS_READ) { /* read? */
for (i = 0; i < RX_NUMBY; i++)
rx_buf[i] = *(((int8 *) uptr -> filebuf) + da + i); }
else { if (uptr -> flags & UNIT_WPRT) { /* write and locked? */
rx_esr = rx_esr | RXES_WLK; /* flag error */
rx_done (rx_esr, 0100); /* done, error */
break; }
for (i = 0; i < RX_NUMBY; i++) /* write */
*(((int8 *) uptr -> filebuf) + da + i) = rx_buf[i];
da = da + RX_NUMBY;
if (da > uptr -> hwmark) uptr -> hwmark = da; }
rx_done (rx_esr, 0); /* done */
break;
case CMD_COMPLETE: /* command complete */
if (func == RXCS_ECODE) rx_done (rx_ecode, 0);
else if (uptr -> flags & UNIT_ATT) rx_done (rx_esr | RXES_DRDY, 0);
else rx_done (rx_esr, 0);
break;
case INIT_COMPLETE: /* init complete */
rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */
rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */
if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */
rx_done (rx_esr | RXES_ID, 0010); /* init done, error */
break; }
da = CALC_DA (1, 1); /* track 1, sector 1 */
for (i = 0; i < RX_NUMBY; i++) /* read sector */
rx_buf[i] = *(((int8 *) uptr -> filebuf) + da + i);
rx_done (rx_esr | RXES_ID | RXES_DRDY, 0); /* set done */
if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020;
break; } /* end case state */
return IORETURN (rx_stopioe, rval);
}
/* Command complete. Set done and put final value in interface register,
request interrupt if needed, return to IDLE state.
*/
void rx_done (int32 new_dbr, int32 new_ecode)
{
rx_csr = rx_csr | RXCS_DONE; /* set done */
if (rx_csr & CSR_IE) SET_INT (RX); /* if ie, intr */
rx_dbr = new_dbr; /* update RXDB */
if (new_ecode != 0) { /* test for error */
rx_ecode = new_ecode;
rx_csr = rx_csr | CSR_ERR; }
rx_state = IDLE; /* now idle */
return;
}
/* Device initialization. The RX is one of the few devices that schedules
an I/O transfer as part of its initialization.
*/
t_stat rx_reset (DEVICE *dptr)
{
rx_csr = rx_dbr = 0; /* clear regs */
rx_esr = rx_ecode = 0; /* clear error */
CLR_INT (RX); /* clear int req */
sim_cancel (&rx_unit[1]); /* cancel drive 1 */
if (rx_unit[0].flags & UNIT_BUF) { /* attached? */
rx_state = INIT_COMPLETE; /* yes, sched init */
sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); }
else rx_done (rx_esr | RXES_ID, 0010); /* no, error */
return SCPE_OK;
}
/* Device bootstrap */
#define BOOT_START 02000
#define BOOT_UNIT 02006
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32))
static const int32 boot_rom[] = {
0012706, 0002000, /* MOV #2000, SP */
0012700, 0000000, /* MOV #unit, R0 ; unit number */
0010003, /* MOV R0, R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0006303, /* ASL R3 */
0012701, 0177170, /* MOV #RXCS, R1 ; csr */
0032711, 0000040, /* BITB #40, (R1) ; ready? */
0001775, /* BEQ .-4 */
0052703, 0000007, /* BIS #READ+GO, R3 */
0010311, /* MOV R3, (R1) ; read & go */
0105711, /* TSTB (R1) ; xfr ready? */
0100376, /* BPL .-2 */
0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; sector */
0105711, /* TSTB (R1) ; xfr ready? */
0100376, /* BPL .-2 */
0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */
0005003, /* CLR R3 */
0032711, 0000040, /* BITB #40, (R1) ; ready? */
0001775, /* BEQ .-4 */
0012711, 0000003, /* MOV #EMPTY+GO, (R1) ; empty & go */
0105711, /* TSTB (R1) ; xfr, done? */
0001776, /* BEQ .-2 */
0100003, /* BPL .+010 */
0116123, 0000002, /* MOVB 2(R1), (R3)+ ; move byte */
0000772, /* BR .-012 */
0005002, /* CLR R2 */
0005003, /* CLR R3 */
0005004, /* CLR R4 */
0012705, 0054104, /* MOV #"DX, R5 */
0005007 /* CLR R7 */
};
t_stat rx_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
extern uint16 *M;
for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i];
M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR;
saved_PC = BOOT_START;
return SCPE_OK;
}

520
PDP11/pdp11_stddev.c Normal file
View File

@@ -0,0 +1,520 @@
/* pdp11_stddev.c: PDP-11 standard I/O devices simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
ptr,ptp PC11 paper tape reader/punch
tti,tto DL11 terminal input/output
clk KW11L line frequency clock
29-Nov-01 RMS Added read only unit support
09-Nov-01 RMS Added RQDX3 support
07-Oct-01 RMS Upgraded clock to full KW11L for RSTS/E autoconfigure
07-Sep-01 RMS Moved function prototypes, revised interrupt mechanism
17-Jul-01 RMS Moved function prototype
04-Jul-01 RMS Added DZ11 support
05-Mar-01 RMS Added clock calibration support
30-Oct-00 RMS Standardized register order
25-Jun-98 RMS Fixed bugs in paper tape error handling
*/
#include "pdp11_defs.h"
#define PTRCSR_IMP (CSR_ERR+CSR_BUSY+CSR_DONE+CSR_IE) /* paper tape reader */
#define PTRCSR_RW (CSR_IE)
#define PTPCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* paper tape punch */
#define PTPCSR_RW (CSR_IE)
#define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */
#define TTICSR_RW (CSR_IE)
#define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */
#define TTOCSR_RW (CSR_IE)
#define CLKCSR_IMP (CSR_DONE + CSR_IE) /* real-time clock */
#define CLKCSR_RW (CSR_DONE + CSR_IE)
#define CLK_DELAY 8000
extern int32 int_req[IPL_HLVL];
int32 ptr_csr = 0; /* control/status */
int32 ptr_stopioe = 0; /* stop on error */
int32 ptp_csr = 0; /* control/status */
int32 ptp_stopioe = 0; /* stop on error */
int32 tti_csr = 0; /* control/status */
int32 tto_csr = 0; /* control/status */
int32 clk_csr = 0; /* control/status */
int32 clk_tps = 60; /* ticks/second */
int32 tmxr_poll = CLK_DELAY; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* timer poll */
t_stat ptr_svc (UNIT *uptr);
t_stat ptp_svc (UNIT *uptr);
t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr);
t_stat clk_svc (UNIT *uptr);
t_stat ptr_reset (DEVICE *dptr);
t_stat ptp_reset (DEVICE *dptr);
t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr);
t_stat clk_reset (DEVICE *dptr);
t_stat ptr_attach (UNIT *uptr, char *ptr);
t_stat ptr_detach (UNIT *uptr);
t_stat ptp_attach (UNIT *uptr, char *ptr);
t_stat ptp_detach (UNIT *uptr);
/* PTR data structures
ptr_dev PTR device descriptor
ptr_unit PTR unit descriptor
ptr_reg PTR register list
*/
UNIT ptr_unit = {
UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0),
SERIAL_IN_WAIT };
REG ptr_reg[] = {
{ ORDATA (BUF, ptr_unit.buf, 8) },
{ ORDATA (CSR, ptr_csr, 16) },
{ FLDATA (INT, IREQ (PTR), INT_V_PTR) },
{ FLDATA (ERR, ptr_csr, CSR_V_ERR) },
{ FLDATA (BUSY, ptr_csr, CSR_V_BUSY) },
{ FLDATA (DONE, ptr_csr, CSR_V_DONE) },
{ FLDATA (IE, ptr_csr, CSR_V_IE) },
{ DRDATA (POS, ptr_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, ptr_stopioe, 0) },
{ NULL } };
DEVICE ptr_dev = {
"PTR", &ptr_unit, ptr_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptr_reset,
NULL, &ptr_attach, &ptr_detach };
/* PTP data structures
ptp_dev PTP device descriptor
ptp_unit PTP unit descriptor
ptp_reg PTP register list
*/
UNIT ptp_unit = {
UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
REG ptp_reg[] = {
{ ORDATA (BUF, ptp_unit.buf, 8) },
{ ORDATA (CSR, ptp_csr, 16) },
{ FLDATA (INT, IREQ (PTP), INT_V_PTP) },
{ FLDATA (ERR, ptp_csr, CSR_V_ERR) },
{ FLDATA (DONE, ptp_csr, CSR_V_DONE) },
{ FLDATA (IE, ptp_csr, CSR_V_IE) },
{ DRDATA (POS, ptp_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, ptp_stopioe, 0) },
{ NULL } };
DEVICE ptp_dev = {
"PTP", &ptp_unit, ptp_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptp_reset,
NULL, &ptp_attach, &ptp_detach };
/* TTI data structures
tti_dev TTI device descriptor
tti_unit TTI unit descriptor
tti_reg TTI register list
*/
UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT };
REG tti_reg[] = {
{ ORDATA (BUF, tti_unit.buf, 8) },
{ ORDATA (CSR, tti_csr, 16) },
{ FLDATA (INT, IREQ (TTI), INT_V_TTI) },
{ FLDATA (ERR, tti_csr, CSR_V_ERR) },
{ FLDATA (DONE, tti_csr, CSR_V_DONE) },
{ FLDATA (IE, tti_csr, CSR_V_IE) },
{ DRDATA (POS, tti_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT },
{ NULL } };
DEVICE tti_dev = {
"TTI", &tti_unit, tti_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tti_reset,
NULL, NULL, NULL };
/* TTO data structures
tto_dev TTO device descriptor
tto_unit TTO unit descriptor
tto_reg TTO register list
*/
UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT };
REG tto_reg[] = {
{ ORDATA (BUF, tto_unit.buf, 8) },
{ ORDATA (CSR, tto_csr, 16) },
{ FLDATA (INT, IREQ (TTO), INT_V_TTO) },
{ FLDATA (ERR, tto_csr, CSR_V_ERR) },
{ FLDATA (DONE, tto_csr, CSR_V_DONE) },
{ FLDATA (IE, tto_csr, CSR_V_IE) },
{ DRDATA (POS, tto_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, tto_unit.wait, 24), PV_LEFT },
{ NULL } };
DEVICE tto_dev = {
"TTO", &tto_unit, tto_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tto_reset,
NULL, NULL, NULL };
/* CLK data structures
clk_dev CLK device descriptor
clk_unit CLK unit descriptor
clk_reg CLK register list
*/
UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 8000 };
REG clk_reg[] = {
{ ORDATA (CSR, clk_csr, 16) },
{ FLDATA (INT, IREQ (CLK), INT_V_CLK) },
{ FLDATA (DONE, clk_csr, CSR_V_DONE) },
{ FLDATA (IE, clk_csr, CSR_V_IE) },
{ DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT },
{ DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT },
{ NULL } };
DEVICE clk_dev = {
"CLK", &clk_unit, clk_reg, NULL,
1, 0, 0, 0, 0, 0,
NULL, NULL, &clk_reset,
NULL, NULL, NULL };
/* Standard I/O dispatch routine, I/O addresses 17777546-17777567
17777546 clock CSR
17777550 ptr CSR
17777552 ptr buffer
17777554 ptp CSR
17777556 ptp buffer
17777560 tti CSR
17777562 tti buffer
17777564 tto CSR
17777566 tto buffer
Note: Word access routines filter out odd addresses. Thus,
an odd address implies an (odd) byte access.
*/
t_stat std_rd (int32 *data, int32 PA, int32 access)
{
switch ((PA >> 1) & 017) { /* decode PA<4:1> */
case 03: /* clk csr */
*data = clk_csr & CLKCSR_IMP;
return SCPE_OK;
case 04: /* ptr csr */
*data = ptr_csr & PTRCSR_IMP;
return SCPE_OK;
case 05: /* ptr buf */
ptr_csr = ptr_csr & ~CSR_DONE;
CLR_INT (PTR);
*data = ptr_unit.buf & 0377;
return SCPE_OK;
case 06: /* ptp csr */
*data = ptp_csr & PTPCSR_IMP;
return SCPE_OK;
case 07: /* ptp buf */
*data = ptp_unit.buf;
return SCPE_OK;
case 010: /* tti csr */
*data = tti_csr & TTICSR_IMP;
return SCPE_OK;
case 011: /* tti buf */
tti_csr = tti_csr & ~CSR_DONE;
CLR_INT (TTI);
*data = tti_unit.buf & 0377;
return SCPE_OK;
case 012: /* tto csr */
*data = tto_csr & TTOCSR_IMP;
return SCPE_OK;
case 013: /* tto buf */
*data = tto_unit.buf;
return SCPE_OK; } /* end switch PA */
return SCPE_NXM;
}
t_stat std_wr (int32 data, int32 PA, int32 access)
{
switch ((PA >> 1) & 017) { /* decode PA<4:1> */
case 03: /* clk csr */
if (PA & 1) return SCPE_OK;
if (((data & CSR_IE) == 0) || /* clr IE, DONE? */
((data & CSR_DONE) == 0)) CLR_INT (CLK); /* clr intr */
else if (((clk_csr & CSR_IE) == 0) || /* setting both */
((clk_csr & CSR_DONE) == 0)) SET_INT (CLK); /* if prv clr, intr */
clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW);
return SCPE_OK;
case 04: /* ptr csr */
if (PA & 1) return SCPE_OK;
if ((data & CSR_IE) == 0) CLR_INT (PTR);
else if (((ptr_csr & CSR_IE) == 0) && (ptr_csr & (CSR_ERR | CSR_DONE)))
SET_INT (PTR);
if (data & CSR_GO) {
ptr_csr = (ptr_csr & ~CSR_DONE) | CSR_BUSY;
CLR_INT (PTR);
if (ptr_unit.flags & UNIT_ATT) /* data to read? */
sim_activate (&ptr_unit, ptr_unit.wait);
else sim_activate (&ptr_unit, 0); } /* error if not */
ptr_csr = (ptr_csr & ~PTRCSR_RW) | (data & PTRCSR_RW);
return SCPE_OK;
case 05: /* ptr buf */
return SCPE_OK;
case 06: /* ptp csr */
if (PA & 1) return SCPE_OK;
if ((data & CSR_IE) == 0) CLR_INT (PTP);
else if (((ptp_csr & CSR_IE) == 0) && (ptp_csr & (CSR_ERR | CSR_DONE)))
SET_INT (PTP);
ptp_csr = (ptp_csr & ~PTPCSR_RW) | (data & PTPCSR_RW);
return SCPE_OK;
case 07: /* ptp buf */
if ((PA & 1) == 0) ptp_unit.buf = data & 0377;
ptp_csr = ptp_csr & ~CSR_DONE;
CLR_INT (PTP);
if (ptp_unit.flags & UNIT_ATT) /* file to write? */
sim_activate (&ptp_unit, ptp_unit.wait);
else sim_activate (&ptp_unit, 0); /* error if not */
return SCPE_OK;
case 010: /* tti csr */
if (PA & 1) return SCPE_OK;
if ((data & CSR_IE) == 0) CLR_INT (TTI);
else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
SET_INT (TTI);
tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW);
return SCPE_OK;
case 011: /* tti buf */
return SCPE_OK;
case 012: /* tto csr */
if (PA & 1) return SCPE_OK;
if ((data & CSR_IE) == 0) CLR_INT (TTO);
else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
SET_INT (TTO);
tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW);
return SCPE_OK;
case 013: /* tto buf */
if ((PA & 1) == 0) tto_unit.buf = data & 0377;
tto_csr = tto_csr & ~CSR_DONE;
CLR_INT (TTO);
sim_activate (&tto_unit, tto_unit.wait);
return SCPE_OK; } /* end switch PA */
return SCPE_NXM;
}
/* Paper tape reader routines
ptr_svc process event (character ready)
ptr_reset process reset
ptr_attach process attach
ptr_detach process detach
*/
t_stat ptr_svc (UNIT *uptr)
{
int32 temp;
ptr_csr = (ptr_csr | CSR_ERR) & ~CSR_BUSY;
if (ptr_csr & CSR_IE) SET_INT (PTR);
if ((ptr_unit.flags & UNIT_ATT) == 0)
return IORETURN (ptr_stopioe, SCPE_UNATT);
if ((temp = getc (ptr_unit.fileref)) == EOF) {
if (feof (ptr_unit.fileref)) {
if (ptr_stopioe) printf ("PTR end of file\n");
else return SCPE_OK; }
else perror ("PTR I/O error");
clearerr (ptr_unit.fileref);
return SCPE_IOERR; }
ptr_csr = (ptr_csr | CSR_DONE) & ~CSR_ERR;
ptr_unit.buf = temp & 0377;
ptr_unit.pos = ptr_unit.pos + 1;
return SCPE_OK;
}
t_stat ptr_reset (DEVICE *dptr)
{
ptr_unit.buf = 0;
ptr_csr = 0;
if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR;
CLR_INT (PTR);
sim_cancel (&ptr_unit);
return SCPE_OK;
}
t_stat ptr_attach (UNIT *uptr, char *cptr)
{
t_stat reason;
reason = attach_unit (uptr, cptr);
if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR;
else ptr_csr = ptr_csr & ~CSR_ERR;
return reason;
}
t_stat ptr_detach (UNIT *uptr)
{
ptr_csr = ptr_csr | CSR_ERR;
return detach_unit (uptr);
}
/* Paper tape punch routines
ptp_svc process event (character punched)
ptp_reset process reset
ptp_attach process attach
ptp_detach process detach
*/
t_stat ptp_svc (UNIT *uptr)
{
ptp_csr = ptp_csr | CSR_ERR | CSR_DONE;
if (ptp_csr & CSR_IE) SET_INT (PTP);
if ((ptp_unit.flags & UNIT_ATT) == 0)
return IORETURN (ptp_stopioe, SCPE_UNATT);
if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) {
perror ("PTP I/O error");
clearerr (ptp_unit.fileref);
return SCPE_IOERR; }
ptp_csr = ptp_csr & ~CSR_ERR;
ptp_unit.pos = ptp_unit.pos + 1;
return SCPE_OK;
}
t_stat ptp_reset (DEVICE *dptr)
{
ptp_unit.buf = 0;
ptp_csr = CSR_DONE;
if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR;
CLR_INT (PTP);
sim_cancel (&ptp_unit); /* deactivate unit */
return SCPE_OK;
}
t_stat ptp_attach (UNIT *uptr, char *cptr)
{
t_stat reason;
reason = attach_unit (uptr, cptr);
if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR;
else ptp_csr = ptp_csr & ~CSR_ERR;
return reason;
}
t_stat ptp_detach (UNIT *uptr)
{
ptp_csr = ptp_csr | CSR_ERR;
return detach_unit (uptr);
}
/* Terminal input routines
tti_svc process event (character ready)
tti_reset process reset
*/
t_stat tti_svc (UNIT *uptr)
{
int32 temp;
sim_activate (&tti_unit, tti_unit.wait); /* continue poll */
if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */
tti_unit.buf = temp & 0377;
tti_unit.pos = tti_unit.pos + 1;
tti_csr = tti_csr | CSR_DONE;
if (tti_csr & CSR_IE) SET_INT (TTI);
return SCPE_OK;
}
t_stat tti_reset (DEVICE *dptr)
{
tti_unit.buf = 0;
tti_csr = 0;
CLR_INT (TTI);
sim_activate (&tti_unit, tti_unit.wait); /* activate unit */
return SCPE_OK;
}
/* Terminal output routines
tto_svc process event (character typed)
tto_reset process reset
*/
t_stat tto_svc (UNIT *uptr)
{
int32 temp;
tto_csr = tto_csr | CSR_DONE;
if (tto_csr & CSR_IE) SET_INT (TTO);
if ((temp = sim_putchar (tto_unit.buf & 0177)) != SCPE_OK) return temp;
tto_unit.pos = tto_unit.pos + 1;
return SCPE_OK;
}
t_stat tto_reset (DEVICE *dptr)
{
tto_unit.buf = 0;
tto_csr = CSR_DONE;
CLR_INT (TTO);
sim_cancel (&tto_unit); /* deactivate unit */
return SCPE_OK;
}
/* Clock routines
clk_svc process event (clock tick)
clk_reset process reset
*/
t_stat clk_svc (UNIT *uptr)
{
int32 t;
clk_csr = clk_csr | CSR_DONE; /* set done */
if (clk_csr & CSR_IE) SET_INT (CLK);
t = sim_rtc_calb (clk_tps); /* calibrate clock */
sim_activate (&clk_unit, t); /* reactivate unit */
tmr_poll = t; /* set timer poll */
tmxr_poll = t; /* set mux poll */
return SCPE_OK;
}
t_stat clk_reset (DEVICE *dptr)
{
clk_csr = 0;
CLR_INT (CLK);
sim_activate (&clk_unit, clk_unit.wait); /* activate unit */
tmr_poll = clk_unit.wait; /* set timer poll */
tmxr_poll = clk_unit.wait; /* set mux poll */
return SCPE_OK;
}

859
PDP11/pdp11_sys.c Normal file
View File

@@ -0,0 +1,859 @@
/* pdp11_sys.c: PDP-11 simulator interface
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
29-Nov-01 RMS Added read only unit support
17-Sep-01 RMS Removed multiconsole support
26-Aug-01 RMS Added DZ11
20-Aug-01 RMS Updated bad block inquiry
17-Jul-01 RMS Fixed warning from VC++ 6.0
27-May-01 RMS Added multiconsole support
05-Apr-01 RMS Added support for TS11/TSV05
14-Mar-01 RMS Revised load/dump interface (again)
11-Feb-01 RMS Added DECtape support
30-Oct-00 RMS Added support for examine to file
14-Apr-99 RMS Changed t_addr to unsigned
09-Nov-98 RMS Fixed assignments of ROR/ROL (John Wilson)
27-Oct-98 RMS V2.4 load interface
08-Oct-98 RMS Fixed bug in bad block routine
30-Mar-98 RMS Fixed bug in floating point display
12-Nov-97 RMS Added bad block table routine
*/
#include "pdp11_defs.h"
#include <ctype.h>
extern DEVICE cpu_dev;
extern DEVICE ptr_dev, ptp_dev;
extern DEVICE tti_dev, tto_dev;
extern DEVICE lpt_dev, clk_dev;
extern DEVICE dz_dev;
extern DEVICE rk_dev, rl_dev;
extern DEVICE rp_dev, rq_dev;
extern DEVICE rx_dev, dt_dev;
extern DEVICE tm_dev, ts_dev;
/* extern DEVICE hk_dev; */
extern UNIT cpu_unit;
extern REG cpu_reg[];
extern uint16 *M;
extern int32 saved_PC;
/* SCP data structures and interface routines
sim_name simulator name string
sim_PC pointer to saved PC register descriptor
sim_emax number of words for examine
sim_devices array of pointers to simulated devices
sim_stop_messages array of pointers to stop messages
sim_load binary loader
*/
char sim_name[] = "PDP-11";
REG *sim_PC = &cpu_reg[0];
int32 sim_emax = 4;
DEVICE *sim_devices[] = {
&cpu_dev,
&ptr_dev, &ptp_dev,
&tti_dev, &tto_dev,
&lpt_dev, &clk_dev,
&dz_dev,
&rk_dev, &rl_dev,
&rp_dev, &rq_dev,
&rx_dev, &dt_dev,
&tm_dev, &ts_dev,
NULL };
const char *sim_stop_messages[] = {
"Unknown error",
"Red stack trap",
"Odd address trap",
"Memory management trap",
"Non-existent memory trap",
"Parity error trap",
"Privilege trap",
"Illegal instruction trap",
"BPT trap",
"IOT trap",
"EMT trap",
"TRAP trap",
"Trace trap",
"Yellow stack trap",
"Powerfail trap",
"Floating point exception",
"HALT instruction",
"Breakpoint",
"Wait state",
"Trap vector fetch abort",
"Trap stack push abort",
"RQDX3 consistency error" };
/* Binary loader.
Loader format consists of blocks, optionally preceded, separated, and
followed by zeroes. Each block consists of:
001 ---
xxx |
lo_count |
hi_count |
lo_origin > count bytes
hi_origin |
data byte |
: |
data byte ---
checksum
If the byte count is exactly six, the block is the last on the tape, and
there is no checksum. If the origin is not 000001, then the origin is
the PC at which to start the program.
*/
t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
{
int32 csum, count, state, i;
t_addr origin;
if ((*cptr != 0) || (flag != 0)) return SCPE_ARG;
state = csum = 0;
while ((i = getc (fileref)) != EOF) {
csum = csum + i; /* add into chksum */
switch (state) {
case 0: /* leader */
if (i == 1) state = 1;
else csum = 0;
break;
case 1: /* ignore after 001 */
state = 2;
break;
case 2: /* low count */
count = i;
state = 3;
break;
case 3: /* high count */
count = (i << 8) | count;
state = 4;
break;
case 4: /* low origin */
origin = i;
state = 5;
break;
case 5: /* high origin */
origin = (i << 8) | origin;
if (count == 6) {
if (origin != 1) saved_PC = origin & 0177776;
return SCPE_OK; }
count = count - 6;
state = 6;
break;
case 6: /* data */
if (origin >= MEMSIZE) return SCPE_NXM;
M[origin >> 1] = (origin & 1)?
(M[origin >> 1] & 0377) | (i << 8):
(M[origin >> 1] & 0177400) | i;
origin = origin + 1;
count = count - 1;
state = state + (count == 0);
break;
case 7: /* checksum */
if (csum & 0377) return SCPE_CSUM;
csum = state = 0;
break; } /* end switch */
} /* end while */
return SCPE_FMT; /* unexpected eof */
}
/* Factory bad block table creation routine
This routine writes a DEC standard 044 compliant bad block table on the
last track of the specified unit. The bad block table consists of 10
repetitions of the same table, formatted as follows:
words 0-1 pack id number
words 2-3 cylinder/sector/surface specifications
:
words n-n+1 end of table (-1,-1)
Inputs:
uptr = pointer to unit
sec = number of sectors per surface
wds = number of words per sector
Outputs:
sta = status code
*/
t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds)
{
int32 i, da;
int16 *buf;
if ((sec < 2) || (wds < 16)) return SCPE_ARG;
if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_UNATT;
if (uptr -> flags & UNIT_RO) return SCPE_RO;
if (!get_yn ("Create bad block table on last track? [N]", FALSE)) return SCPE_OK;
da = (uptr -> capac - (sec * wds)) * sizeof (int16);
if (fseek (uptr -> fileref, da, SEEK_SET)) return SCPE_IOERR;
if ((buf = malloc (wds * sizeof (int16))) == NULL) return SCPE_MEM;
buf[0] = buf[1] = 012345u;
buf[2] = buf[3] = 0;
for (i = 4; i < wds; i++) buf[i] = 0177777u;
for (i = 0; (i < sec) && (i < 10); i++)
fxwrite (buf, sizeof (int16), wds, uptr -> fileref);
free (buf);
if (ferror (uptr -> fileref)) return SCPE_IOERR;
return SCPE_OK;
}
/* Symbol tables */
#define I_V_L 16 /* long mode */
#define I_V_D 17 /* double mode */
#define I_L (1 << I_V_L)
#define I_D (1 << I_V_D)
/* Warning: for literals, the class number MUST equal the field width!! */
#define I_V_CL 18 /* class bits */
#define I_M_CL 017 /* class mask */
#define I_V_NPN 0 /* no operands */
#define I_V_REG 1 /* reg */
#define I_V_SOP 2 /* operand */
#define I_V_3B 3 /* 3b literal */
#define I_V_FOP 4 /* flt operand */
#define I_V_AFOP 5 /* fac, flt operand */
#define I_V_6B 6 /* 6b literal */
#define I_V_BR 7 /* cond branch */
#define I_V_8B 8 /* 8b literal */
#define I_V_SOB 9 /* reg, disp */
#define I_V_RSOP 10 /* reg, operand */
#define I_V_ASOP 11 /* fac, operand */
#define I_V_ASMD 12 /* fac, moded int op */
#define I_V_DOP 13 /* double operand */
#define I_V_CCC 14 /* CC clear */
#define I_V_CCS 15 /* CC set */
#define I_NPN (I_V_NPN << I_V_CL)
#define I_REG (I_V_REG << I_V_CL)
#define I_3B (I_V_3B << I_V_CL)
#define I_SOP (I_V_SOP << I_V_CL)
#define I_FOP (I_V_FOP << I_V_CL)
#define I_6B (I_V_6B << I_V_CL)
#define I_BR (I_V_BR << I_V_CL)
#define I_8B (I_V_8B << I_V_CL)
#define I_AFOP (I_V_AFOP << I_V_CL)
#define I_ASOP (I_V_ASOP << I_V_CL)
#define I_RSOP (I_V_RSOP << I_V_CL)
#define I_SOB (I_V_SOB << I_V_CL)
#define I_ASMD (I_V_ASMD << I_V_CL)
#define I_DOP (I_V_DOP << I_V_CL)
#define I_CCC (I_V_CCC << I_V_CL)
#define I_CCS (I_V_CCS << I_V_CL)
static const int32 masks[] = {
0177777, 0177770, 0177700, 0177770,
0177700+I_D, 0177400+I_D, 0177700, 0177400,
0177400, 0177000, 0177000, 0177400,
0177400+I_D+I_L, 0170000, 0177777, 0177777 };
static const char *opcode[] = {
"HALT","WAIT","RTI","BPT",
"IOT","RESET","RTT","MFPT",
"JMP","RTS","SPL",
"NOP","CLC","CLV","CLV CLC",
"CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC",
"CLN","CLN CLC","CLN CLV","CLN CLV CLC",
"CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC",
"NOP","SEC","SEV","SEV SEC",
"SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC",
"SEN","SEN SEC","SEN SEV","SEN SEV SEC",
"SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC",
"SWAB","BR","BNE","BEQ",
"BGE","BLT","BGT","BLE",
"JSR",
"CLR","COM","INC","DEC",
"NEG","ADC","SBC","TST",
"ROR","ROL","ASR","ASL",
"MARK","MFPI","MTPI","SXT",
"CSM", "TSTSET","WRTLCK",
"MOV","CMP","BIT","BIC",
"BIS","ADD",
"MUL","DIV","ASH","ASHC",
"XOR",
"FADD","FSUB","FMUL","FDIV",
"L2DR",
"MOVC","MOVRC","MOVTC",
"LOCC","SKPC","SCANC","SPANC",
"CMPC","MATC",
"ADDN","SUBN","CMPN","CVTNL",
"CVTPN","CVTNP","ASHN","CVTLN",
"L3DR",
"ADDP","SUBP","CMPP","CVTPL",
"MULP","DIVP","ASHP","CVTLP",
"MOVCI","MOVRCI","MOVTCI",
"LOCCI","SKPCI","SCANCI","SPANCI",
"CMPCI","MATCI",
"ADDNI","SUBNI","CMPNI","CVTNLI",
"CVTPNI","CVTNPI","ASHNI","CVTLNI",
"ADDPI","SUBPI","CMPPI","CVTPLI",
"MULPI","DIVPI","ASHPI","CVTLPI",
"SOB",
"BPL","BMI","BHI","BLOS",
"BVC","BVS","BCC","BCS",
"BHIS","BLO", /* encode only */
"EMT","TRAP",
"CLRB","COMB","INCB","DECB",
"NEGB","ADCB","SBCB","TSTB",
"RORB","ROLB","ASRB","ASLB",
"MTPS","MFPD","MTPD","MFPS",
"MOVB","CMPB","BITB","BICB",
"BISB","SUB",
"CFCC","SETF","SETI","SETD","SETL",
"LDFPS","STFPS","STST",
"CLRF","CLRD","TSTF","TSTD",
"ABSF","ABSD","NEGF","NEGD",
"MULF","MULD","MODF","MODD",
"ADDF","ADDD","LDF","LDD",
"SUBF","SUBD","CMPF","CMPD",
"STF","STD","DIVF","DIVD",
"STEXP",
"STCFI","STCDI","STCFL","STCDL",
"STCFD","STCDF",
"LDEXP",
"LDCIF","LDCID","LDCLF","LDCLD",
"LDCFD","LDCDF",
NULL };
static const int32 opc_val[] = {
0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN,
0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN,
0000100+I_SOP, 0000200+I_REG, 0000230+I_3B,
0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN,
0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN,
0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN,
0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC,
0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN,
0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN,
0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN,
0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS,
0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR,
0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR,
0004000+I_RSOP,
0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP,
0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP,
0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP,
0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP,
0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP,
0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP,
0050000+I_DOP, 0060000+I_DOP,
0070000+I_RSOP, 0071000+I_RSOP, 0072000+I_RSOP, 0073000+I_RSOP,
0074000+I_RSOP,
0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG,
0076020+I_REG,
0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN,
0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN,
0076044+I_NPN, 0076045+I_NPN,
0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN,
0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN,
0076060+I_REG,
0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN,
0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN,
0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN,
0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN,
0076144+I_NPN, 0076145+I_NPN,
0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN,
0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN,
0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN,
0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN,
0077000+I_SOB,
0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR,
0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR,
0103000+I_BR, 0103400+I_BR,
0104000+I_8B, 0104400+I_8B,
0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP,
0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP,
0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP,
0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP,
0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP,
0150000+I_DOP, 0160000+I_DOP,
0170000+I_NPN, 0170001+I_NPN, 0170002+I_NPN, 0170011+I_NPN, 0170012+I_NPN,
0170100+I_SOP, 0170200+I_SOP, 0170300+I_SOP,
0170400+I_FOP, 0170400+I_FOP+I_D, 0170500+I_FOP, 0170500+I_FOP+I_D,
0170600+I_FOP, 0170600+I_FOP+I_D, 0170700+I_FOP, 0170700+I_FOP+I_D,
0171000+I_AFOP, 0171000+I_AFOP+I_D, 0171400+I_AFOP, 0171400+I_AFOP+I_D,
0172000+I_AFOP, 0172000+I_AFOP+I_D, 0172400+I_AFOP, 0172400+I_AFOP+I_D,
0173000+I_AFOP, 0173000+I_AFOP+I_D, 0173400+I_AFOP, 0173400+I_AFOP+I_D,
0174000+I_AFOP, 0174000+I_AFOP+I_D, 0174400+I_AFOP, 0174400+I_AFOP+I_D,
0175000+I_ASOP,
0175400+I_ASMD, 0175400+I_ASMD+I_D, 0175400+I_ASMD+I_L, 0175400+I_ASMD+I_D+I_L,
0176000+I_AFOP, 0176000+I_AFOP+I_D,
0176400+I_ASOP,
0177000+I_ASMD, 0177000+I_ASMD+I_D, 0177000+I_ASMD+I_L, 0177000+I_ASMD+I_D+I_L,
0177400+I_AFOP, 0177400+I_AFOP+I_D,
-1 };
static const char *rname [] =
{ "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" };
static const char *fname [] =
{ "F0", "F1", "F2", "F3", "F4", "F5", "?6", "?7" };
/* Specifier decode
Inputs:
*of = output stream
addr = current PC
spec = specifier
nval = next word
flag = TRUE if decoding for CPU
iflag = TRUE if decoding integer instruction
Outputs:
count = -number of extra words retired
*/
int32 fprint_spec (FILE *of, t_addr addr, int32 spec, t_value nval,
int32 flag, int32 iflag)
{
int32 reg, mode;
static const int32 rgwd[8] = { 0, 0, 0, 0, 0, 0, -1, -1 };
static const int32 pcwd[8] = { 0, 0, -1, -1, 0, 0, -1, -1 };
reg = spec & 07;
mode = ((spec >> 3) & 07);
switch (mode) {
case 0:
if (iflag) fprintf (of, "%s", rname[reg]);
else fprintf (of, "%s", fname[reg]);
break;
case 1:
fprintf (of, "(%s)", rname[reg]);
break;
case 2:
if (reg != 7) fprintf (of, "(%s)+", rname[reg]);
else fprintf (of, "#%-o", nval);
break;
case 3:
if (reg != 7) fprintf (of, "@(%s)+", rname[reg]);
else fprintf (of, "@#%-o", nval);
break;
case 4:
fprintf (of, "-(%s)", rname[reg]);
break;
case 5:
fprintf (of, "@-(%s)", rname[reg]);
break;
case 6:
if ((reg != 7) || !flag) fprintf (of, "%-o(%s)", nval, rname[reg]);
else fprintf (of, "%-o", (nval + addr + 4) & 0177777);
break;
case 7:
if ((reg != 7) || !flag) fprintf (of, "@%-o(%s)", nval, rname[reg]);
else fprintf (of, "@%-o", (nval + addr + 4) & 0177777);
break; } /* end case */
return ((reg == 07)? pcwd[mode]: rgwd[mode]);
}
/* Symbolic decode
Inputs:
*of = output stream
addr = current PC
*val = values to decode
*uptr = pointer to unit
sw = switches
Outputs:
return = if >= 0, error code
if < 0, number of extra words retired
*/
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
UNIT *uptr, int32 sw)
{
int32 cflag, i, j, c1, c2, inst, fac, srcm, srcr, dstm, dstr;
int32 l8b, brdisp, wd1, wd2;
extern int32 FPS;
cflag = (uptr == NULL) || (uptr == &cpu_unit);
c1 = val[0] & 0177;
c2 = (val[0] >> 8) & 0177;
if (sw & SWMASK ('A')) { /* ASCII? */
fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);
return SCPE_OK; }
if (sw & SWMASK ('C')) { /* character? */
fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);
fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
return SCPE_OK; }
if (!(sw & SWMASK ('M'))) return SCPE_ARG;
inst = val[0] | ((FPS << (I_V_L - FPS_V_L)) & I_L) |
((FPS << (I_V_D - FPS_V_D)) & I_D); /* inst + fp mode */
for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */
if ((opc_val[i] & 0777777) == (inst & masks[j])) { /* match? */
srcm = (inst >> 6) & 077; /* opr fields */
srcr = srcm & 07;
fac = srcm & 03;
dstm = inst & 077;
dstr = dstm & 07;
l8b = inst & 0377;
/* Instruction decode */
switch (j) { /* case on class */
case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */
fprintf (of, "%s", opcode[i]);
return SCPE_OK;
case I_V_REG: /* reg */
fprintf (of, "%s %-s", opcode[i], rname[dstr]);
return SCPE_OK;
case I_V_SOP: /* sop */
fprintf (of, "%s ", opcode[i]);
return fprint_spec (of, addr, dstm, val[1], cflag, TRUE);
case I_V_3B: /* 3b */
fprintf (of, "%s %-o", opcode[i], dstr);
return SCPE_OK;
case I_V_FOP: /* fop */
fprintf (of, "%s ", opcode[i]);
return fprint_spec (of, addr, dstm, val[1], cflag, FALSE);
case I_V_AFOP: /* afop */
fprintf (of, "%s %s,", opcode[i], fname[fac]);
return fprint_spec (of, addr, dstm, val[1], cflag, FALSE);
case I_V_6B: /* 6b */
fprintf (of, "%s %-o", opcode[i], dstm);
return SCPE_OK;
case I_V_BR: /* cond branch */
fprintf (of, "%s ", opcode[i]);
brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777;
if (cflag) fprintf (of, "%-o", (addr + brdisp) & 0177777);
else if (brdisp < 01000) fprintf (of, ".+%-o", brdisp);
else fprintf (of, ".-%-o", 0200000 - brdisp);
return SCPE_OK;
case I_V_8B: /* 8b */
fprintf (of, "%s %-o", opcode[i], l8b);
return SCPE_OK;
case I_V_SOB: /* sob */
fprintf (of, "%s %s,", opcode[i], rname[srcr]);
brdisp = (dstm * 2) - 2;
if (cflag) fprintf (of, "%-o", (addr - brdisp) & 0177777);
else if (brdisp <= 0) fprintf (of, ".+%-o", -brdisp);
else fprintf (of, ".-%-o", brdisp);
return SCPE_OK;
case I_V_RSOP: /* rsop */
fprintf (of, "%s %s,", opcode[i], rname[srcr]);
return fprint_spec (of, addr, dstm, val[1], cflag, TRUE);
case I_V_ASOP: case I_V_ASMD: /* asop, asmd */
fprintf (of, "%s %s,", opcode[i], fname[fac]);
return fprint_spec (of, addr, dstm, val[1], cflag, TRUE);
case I_V_DOP: /* dop */
fprintf (of, "%s ", opcode[i]);
wd1 = fprint_spec (of, addr, srcm, val[1], cflag, TRUE);
fprintf (of, ",");
wd2 = fprint_spec (of, addr - wd1 - wd1, dstm,
val[1 - wd1], cflag, TRUE);
return wd1 + wd2; } /* end case */
} /* end if */
} /* end for */
return SCPE_ARG; /* no match */
}
#define A_PND 100 /* # seen */
#define A_MIN 040 /* -( seen */
#define A_PAR 020 /* (Rn) seen */
#define A_REG 010 /* Rn seen */
#define A_PLS 004 /* + seen */
#define A_NUM 002 /* number seen */
#define A_REL 001 /* relative addr seen */
/* Register number
Inputs:
*cptr = pointer to input string
*strings = pointer to register names
mchar = character to match after register name
Outputs:
rnum = 0..7 if a legitimate register
< 0 if error
*/
int32 get_reg (char *cptr, const char *strings[], char mchar)
{
int32 i;
if (*(cptr + 2) != mchar) return -1;
for (i = 0; i < 8; i++) {
if (strncmp (cptr, strings[i], 2) == 0) return i; }
return -1;
}
/* Number or memory address
Inputs:
*cptr = pointer to input string
*dptr = pointer to output displacement
*pflag = pointer to accumulating flags
Outputs:
cptr = pointer to next character in input string
NULL if parsing error
Flags: 0 (no result), A_NUM (number), A_REL (relative)
*/
char *get_addr (char *cptr, int32 *dptr, int32 *pflag)
{
int32 val, minus;
char *tptr;
minus = 0;
if (*cptr == '.') { /* relative? */
*pflag = *pflag | A_REL;
cptr++; }
if (*cptr == '+') { /* +? */
*pflag = *pflag | A_NUM;
cptr++; }
if (*cptr == '-') { /* -? */
*pflag = *pflag | A_NUM;
minus = 1;
cptr++; }
errno = 0;
val = strtoul (cptr, &tptr, 8);
if (cptr == tptr) { /* no number? */
if (*pflag != (A_REL + A_NUM)) return cptr;
else return NULL; }
if (errno || (*pflag == A_REL)) return NULL; /* .n? */
*dptr = (minus? -val: val) & 0177777;
*pflag = *pflag | A_NUM;
return tptr;
}
/* Specifier decode
Inputs:
*cptr = pointer to input string
addr = current PC
n1 = 0 if no extra word used
-1 if extra word used in prior decode
*sptr = pointer to output specifier
*dptr = pointer to output displacement
cflag = true if parsing for the CPU
iflag = true if integer specifier
Outputs:
status = = -1 extra word decoded
= 0 ok
= +1 error
*/
t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr,
int32 cflag, int32 iflag)
{
int32 reg, indir, pflag, disp;
indir = 0; /* no indirect */
pflag = 0;
if (*cptr == '@') { /* indirect? */
indir = 010;
cptr++; }
if (*cptr == '#') { /* literal? */
pflag = pflag | A_PND;
cptr++; }
if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */
pflag = pflag | A_MIN;
cptr++; }
else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) return 1;
if (*cptr == '(') { /* register index? */
pflag = pflag | A_PAR;
if ((reg = get_reg (cptr + 1, rname, ')')) < 0) return 1;
cptr = cptr + 4;
if (*cptr == '+') { /* autoincrement? */
pflag = pflag | A_PLS;
cptr++; } }
else if ((reg = get_reg (cptr, iflag? rname: fname, 0)) >= 0) {
pflag = pflag | A_REG;
cptr = cptr + 2; }
if (*cptr != 0) return 1; /* all done? */
/* Specifier decode, continued */
switch (pflag) { /* case on syntax */
case A_REG: /* Rn, @Rn */
*sptr = indir + reg;
return 0;
case A_PAR: /* (Rn), @(Rn) */
if (indir) { /* @(Rn) = @0(Rn) */
*sptr = 070 + reg;
*dptr = 0;
return -1; }
else *sptr = 010 + reg;
return 0;
case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */
*sptr = 020 + indir + reg;
return 0;
case A_MIN+A_PAR: /* -(Rn), @-(Rn) */
*sptr = 040 + indir + reg;
return 0;
case A_NUM+A_PAR: /* d(Rn), @d(Rn) */
*sptr = 060 + indir + reg;
*dptr = disp;
return -1;
case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */
if (!cflag) return 1;
disp = (disp + addr) & 0177777; /* fall through */
case A_PND+A_NUM: /* #n, @#n */
*sptr = 027 + indir;
*dptr = disp;
return -1;
case A_REL: case A_REL+A_NUM: /* .+n, @.+n */
*sptr = 067 + indir;
*dptr = (disp - 4 + (2 * n1)) & 0177777;
return -1;
case A_NUM: /* n, @n */
if (cflag) { /* CPU - use rel */
*sptr = 067 + indir;
*dptr = (disp - addr - 4 + (2 * n1)) & 0177777; }
else { if (indir) return 1; /* other - use abs */
*sptr = 037;
*dptr = disp; }
return -1;
default:
return 1; } /* end case */
}
/* Symbolic input
Inputs:
*cptr = pointer to input string
addr = current PC
*uptr = pointer to unit
*val = pointer to output values
sw = switches
Outputs:
status = > 0 error code
<= 0 -number of extra words
*/
t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
int32 cflag, d, i, j, reg, spec, n1, n2, disp, pflag;
t_stat r;
char gbuf[CBUFSIZE];
cflag = (uptr == NULL) || (uptr == &cpu_unit);
while (isspace (*cptr)) cptr++; /* absorb spaces */
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
val[0] = (t_value) cptr[0];
return SCPE_OK; }
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
val[0] = ((t_value) cptr[1] << 8) + (t_value) cptr[0];
return SCPE_OK; }
cptr = get_glyph (cptr, gbuf, 0); /* get opcode */
n1 = n2 = pflag = 0;
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
if (opcode[i] == NULL) return SCPE_ARG;
val[0] = opc_val[i] & 0177777; /* get value */
j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */
switch (j) { /* case on class */
case I_V_NPN: /* no operand */
break;
case I_V_REG: /* register */
cptr = get_glyph (cptr, gbuf, 0); /* get glyph */
if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG;
val[0] = val[0] | reg;
break;
case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */
cptr = get_glyph (cptr, gbuf, 0); /* get literal */
d = get_uint (gbuf, 8, (1 << j) - 1, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | d; /* put in place */
break;
case I_V_BR: /* cond br */
cptr = get_glyph (cptr, gbuf, 0); /* get address */
if ((cptr = get_addr (gbuf, &disp, &pflag)) == NULL) return SCPE_ARG;
if ((pflag & A_REL) == 0) {
if (cflag) disp = (disp - addr) & 0177777;
else return SCPE_ARG; }
if ((disp & 1) || (disp > 0400) && (disp < 0177402)) return SCPE_ARG;
val[0] = val[0] | (((disp - 2) >> 1) & 0377);
break;
case I_V_SOB: /* sob */
cptr = get_glyph (cptr, gbuf, ','); /* get glyph */
if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG;
val[0] = val[0] | (reg << 6);
cptr = get_glyph (cptr, gbuf, 0); /* get address */
if ((cptr = get_addr (gbuf, &disp, &pflag)) == NULL) return SCPE_ARG;
if ((pflag & A_REL) == 0) {
if (cflag) disp = (disp - addr) & 0177777;
else return SCPE_ARG; }
if ((disp & 1) || ((disp > 2) && (disp < 0177604))) return SCPE_ARG;
val[0] = val[0] | (((2 - disp) >> 1) & 077);
break;
case I_V_RSOP: /* reg, sop */
cptr = get_glyph (cptr, gbuf, ','); /* get glyph */
if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG;
val[0] = val[0] | (reg << 6); /* fall through */
case I_V_SOP: /* sop */
cptr = get_glyph (cptr, gbuf, 0); /* get glyph */
if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0)
return SCPE_ARG;
val[0] = val[0] | spec;
break;
case I_V_AFOP: case I_V_ASOP: case I_V_ASMD: /* fac, (s)fop */
cptr = get_glyph (cptr, gbuf, ','); /* get glyph */
if ((reg = get_reg (gbuf, fname, 0)) < 0) return SCPE_ARG;
if (reg > 3) return SCPE_ARG;
val[0] = val[0] | (reg << 6); /* fall through */
case I_V_FOP: /* fop */
cptr = get_glyph (cptr, gbuf, 0); /* get glyph */
if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag,
(j == I_V_ASOP) || (j == I_V_ASMD))) > 0) return SCPE_ARG;
val[0] = val[0] | spec;
break;
case I_V_DOP: /* double op */
cptr = get_glyph (cptr, gbuf, ','); /* get glyph */
if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0)
return SCPE_ARG;
val[0] = val[0] | (spec << 6);
cptr = get_glyph (cptr, gbuf, 0); /* get glyph */
if ((n2 = get_spec (gbuf, addr, n1, &spec, &val[1 - n1],
cflag, TRUE)) > 0) return SCPE_ARG;
val[0] = val[0] | spec;
break;
case I_V_CCC: case I_V_CCS: /* cond code oper */
for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0;
cptr = get_glyph (cptr, gbuf, 0)) {
for (i = 0; (opcode[i] != NULL) &&
(strcmp (opcode[i], gbuf) != 0) ; i++) ;
if ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) ||
(opcode[i] == NULL)) return SCPE_ARG;
val[0] = val[0] | (opc_val[i] & 0177777); }
break;
default:
return SCPE_ARG; }
if (*cptr != 0) return SCPE_ARG; /* junk at end? */
return n1 + n2;
}

1118
PDP11/pdp11_tc.c Normal file

File diff suppressed because it is too large Load Diff

632
PDP11/pdp11_tm.c Normal file
View File

@@ -0,0 +1,632 @@
/* pdp11_tm.c: PDP-11 magnetic tape simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
tm TM11/TU10 magtape
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
24-Nov-01 RMS Converted UST, POS, FLG to arrays
09-Nov-01 RMS Added bus map support
18-Oct-01 RMS Added stub diagnostic register (found by Thord Nilson)
07-Sep-01 RMS Revised device disable and interrupt mechanisms
26-Apr-01 RMS Added device enable/disable support
18-Apr-01 RMS Changed to rewind tape before boot
14-Apr-99 RMS Changed t_addr to unsigned
04-Oct-98 RMS V2.4 magtape format
10-May-98 RMS Fixed bug with non-zero unit operation (from Steven Schultz)
09-May-98 RMS Fixed problems in bootstrap (from Steven Schultz)
10-Apr-98 RMS Added 2nd block bootstrap (from John Holden,
University of Sydney)
31-Jul-97 RMS Added bootstrap (from Ethan Dicks, Ohio State)
22-Jan-97 RMS V2.3 magtape format
18-Jan-97 RMS Fixed double interrupt, error flag bugs
29-Jun-96 RMS Added unit disable support
Magnetic tapes are represented as a series of variable 8b records
of the form:
32b record length in bytes - exact number
byte 0
byte 1
:
byte n-2
byte n-1
32b record length in bytes - exact number
If the byte count is odd, the record is padded with an extra byte
of junk. File marks are represented by a single record length of 0.
End of tape is two consecutive end of file marks.
*/
#include "pdp11_defs.h"
#define TM_NUMDR 8 /* #drives */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_WLK 1 << UNIT_V_WLK
#define UNIT_W_UF 2 /* saved user flags */
#define USTAT u3 /* unit status */
#define UNUM u4 /* unit number */
#define TM_MAXFR (1 << 16) /* max transfer */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* Command - tm_cmd */
#define MTC_ERR (1 << CSR_V_ERR) /* error */
#define MTC_V_DEN 13 /* density */
#define MTC_M_DEN 03
#define MTC_DEN (MTC_M_DEN << MTC_V_DEN)
#define MTC_INIT 0010000 /* init */
#define MTC_LPAR 0004000 /* parity select */
#define MTC_V_UNIT 8 /* unit */
#define MTC_M_UNIT 07
#define MTC_UNIT (MTC_M_UNIT << MTC_V_UNIT)
#define MTC_DONE (1 << CSR_V_DONE) /* done */
#define MTC_IE (1 << CSR_V_IE) /* interrupt enable */
#define MTC_V_EMA 4 /* ext mem address */
#define MTC_M_EMA 03
#define MTC_EMA (MTC_M_EMA << MTC_V_EMA)
#define MTC_V_FNC 1 /* function */
#define MTC_M_FNC 07
#define MTC_UNLOAD 00
#define MTC_READ 01
#define MTC_WRITE 02
#define MTC_WREOF 03
#define MTC_SPACEF 04
#define MTC_SPACER 05
#define MTC_WREXT 06
#define MTC_REWIND 07
#define MTC_FNC (MTC_M_FNC << MTC_V_FNC)
#define MTC_GO (1 << CSR_V_GO) /* go */
#define MTC_RW (MTC_DEN | MTC_LPAR | MTC_UNIT | MTC_IE | \
MTC_EMA | MTC_FNC)
#define GET_EMA(x) (((x) & MTC_EMA) << (16 - MTC_V_EMA))
#define GET_UNIT(x) (((x) >> MTC_V_UNIT) & MTC_M_UNIT)
#define GET_FNC(x) (((x) >> MTC_V_FNC) & MTC_M_FNC)
/* Status - stored in tm_sta or (*) uptr -> USTAT or (+) calculated */
#define STA_ILL 0100000 /* illegal */
#define STA_EOF 0040000 /* *end of file */
#define STA_CRC 0020000 /* CRC error */
#define STA_PAR 0010000 /* parity error */
#define STA_DLT 0004000 /* data late */
#define STA_EOT 0002000 /* *end of tape */
#define STA_RLE 0001000 /* rec lnt error */
#define STA_BAD 0000400 /* bad tape error */
#define STA_NXM 0000200 /* non-existent mem */
#define STA_ONL 0000100 /* *online */
#define STA_BOT 0000040 /* *start of tape */
#define STA_7TK 0000020 /* 7 track */
#define STA_SDN 0000010 /* settle down */
#define STA_WLK 0000004 /* *write locked */
#define STA_REW 0000002 /* *rewinding */
#define STA_TUR 0000001 /* +unit ready */
#define STA_CLR (STA_7TK | STA_SDN) /* always clear */
#define STA_DYN (STA_EOF | STA_EOT | STA_ONL | STA_BOT | \
STA_WLK | STA_REW | STA_TUR) /* kept in USTAT */
#define STA_EFLGS (STA_ILL | STA_EOF | STA_CRC | STA_PAR | \
STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM)
/* set error */
/* Read lines - tm_rdl */
#define RDL_CLK 0100000 /* 10 Khz clock */
extern uint16 *M; /* memory */
extern int32 int_req[IPL_HLVL];
uint8 *tmxb = NULL; /* xfer buffer */
int32 tm_sta = 0; /* status register */
int32 tm_cmd = 0; /* command register */
int32 tm_ca = 0; /* current address */
int32 tm_bc = 0; /* byte count */
int32 tm_db = 0; /* data buffer */
int32 tm_rdl = 0; /* read lines */
int32 tm_time = 10; /* record latency */
int32 tm_stopioe = 1; /* stop on error */
int32 tm_enb = 1; /* device enable */
t_stat tm_svc (UNIT *uptr);
t_stat tm_reset (DEVICE *dptr);
t_stat tm_attach (UNIT *uptr, char *cptr);
t_stat tm_detach (UNIT *uptr);
t_stat tm_boot (int32 unitno);
void tm_go (UNIT *uptr);
int32 tm_updcsta (UNIT *uptr);
void tm_set_done (void);
t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc);
/* MT data structures
tm_dev MT device descriptor
tm_unit MT unit list
tm_reg MT register list
tm_mod MT modifier list
*/
UNIT tm_unit[] = {
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) },
{ UDATA (&tm_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } };
REG tm_reg[] = {
{ ORDATA (MTS, tm_sta, 16) },
{ ORDATA (MTC, tm_cmd, 16) },
{ ORDATA (MTBRC, tm_bc, 16) },
{ ORDATA (MTCMA, tm_ca, 16) },
{ ORDATA (MTD, tm_db, 8) },
{ ORDATA (MTRD, tm_rdl, 16) },
{ FLDATA (INT, IREQ (TM), INT_V_TM) },
{ FLDATA (ERR, tm_cmd, CSR_V_ERR) },
{ FLDATA (DONE, tm_cmd, CSR_V_DONE) },
{ FLDATA (IE, tm_cmd, CSR_V_IE) },
{ FLDATA (STOP_IOE, tm_stopioe, 0) },
{ DRDATA (TIME, tm_time, 24), PV_LEFT },
{ URDATA (UST, tm_unit[0].USTAT, 8, 16, 0, TM_NUMDR, 0) },
{ URDATA (POS, tm_unit[0].pos, 10, 31, 0,
TM_NUMDR, PV_LEFT | REG_RO) },
{ URDATA (FLG, tm_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
TM_NUMDR, REG_HRO) },
{ FLDATA (*DEVENB, tm_enb, 0), REG_HRO },
{ NULL } };
MTAB tm_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", &tm_vlock },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &tm_vlock },
{ 0 } };
DEVICE tm_dev = {
"TM", tm_unit, tm_reg, tm_mod,
TM_NUMDR, 10, 31, 1, 8, 8,
NULL, NULL, &tm_reset,
&tm_boot, &tm_attach, &tm_detach };
/* I/O dispatch routine, I/O addresses 17772520 - 17772532
17772520 MTS read only, constructed from tm_sta
plus current drive status flags
17772522 MTC read/write
17772524 MTBRC read/write
17772526 MTCMA read/write
17772530 MTD read/write
17772532 MTRD read only
*/
t_stat tm_rd (int32 *data, int32 PA, int32 access)
{
UNIT *uptr;
uptr = tm_dev.units + GET_UNIT (tm_cmd); /* get unit */
switch ((PA >> 1) & 07) { /* decode PA<3:1> */
case 0: /* MTS */
*data = tm_updcsta (uptr); /* update status */
break;
case 1: /* MTC */
tm_updcsta (uptr); /* update status */
*data = tm_cmd; /* return command */
break;
case 2: /* MTBRC */
*data = tm_bc; /* return byte count */
break;
case 3: /* MTCMA */
*data = tm_ca; /* return mem addr */
break;
case 4: /* MTD */
*data = tm_db; /* return data buffer */
break;
case 5: /* MTRD */
tm_rdl = tm_rdl ^ RDL_CLK; /* "clock" ticks */
*data = tm_rdl;
break;
default: /* unimplemented */
*data = 0;
break; }
return SCPE_OK;
}
t_stat tm_wr (int32 data, int32 PA, int32 access)
{
UNIT *uptr;
switch ((PA >> 1) & 07) { /* decode PA<3:1> */
case 0: /* MTS: read only */
break;
case 1: /* MTC */
uptr = tm_dev.units + GET_UNIT (tm_cmd); /* select unit */
if ((tm_cmd & MTC_DONE) == 0) tm_sta = tm_sta | STA_ILL;
else { if (access == WRITEB) data = (PA & 1)?
(tm_cmd & 0377) | (data << 8):
(tm_cmd & ~0377) | data;
if (data & MTC_INIT) { /* init? */
tm_reset (&tm_dev); /* reset device */
return SCPE_OK; }
if ((data & MTC_IE) == 0) /* int disable? */
CLR_INT (TM); /* clr int request */
else if ((tm_cmd & (MTC_ERR + MTC_DONE)) && !(tm_cmd & MTC_IE))
SET_INT (TM); /* set int request */
tm_cmd = (tm_cmd & ~MTC_RW) | (data & MTC_RW);
uptr = tm_dev.units + GET_UNIT (tm_cmd); /* new unit */
if (data & MTC_GO) tm_go (uptr); } /* new function? */
tm_updcsta (uptr); /* update status */
break;
case 2: /* MTBRC */
if (access == WRITEB) data = (PA & 1)?
(tm_bc & 0377) | (data << 8): (tm_bc & ~0377) | data;
tm_bc = data;
break;
case 3: /* MTCMA */
if (access == WRITEB) data = (PA & 1)?
(tm_ca & 0377) | (data << 8): (tm_ca & ~0377) | data;
tm_ca = data;
break;
case 4: /* MTD */
if ((access == WRITEB) && (PA & 1)) return SCPE_OK;
tm_db = data & 0377;
break; } /* end switch */
return SCPE_OK;
}
/* New magtape command */
void tm_go (UNIT *uptr)
{
int32 f;
f = GET_FNC (tm_cmd); /* get function */
if (((uptr -> flags & UNIT_ATT) == 0) || /* not attached? */
sim_is_active (uptr) || /* busy? */
(((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) &&
(uptr -> flags & UNIT_WPRT))) { /* write locked? */
tm_sta = tm_sta | STA_ILL; /* illegal */
tm_set_done (); /* set done */
return; }
uptr -> USTAT = uptr -> USTAT & (STA_WLK | STA_ONL); /* clear status */
tm_sta = 0; /* clear errors */
if (f == MTC_UNLOAD) { /* unload? */
uptr -> USTAT = (uptr -> USTAT | STA_REW) & ~STA_ONL;
detach_unit (uptr); } /* set offline */
else if (f == MTC_REWIND) /* rewind */
uptr -> USTAT = uptr -> USTAT | STA_REW; /* rewinding */
/* else /* uncomment this else if rewind/unload don't set done */
tm_cmd = tm_cmd & ~MTC_DONE; /* clear done */
CLR_INT (TM); /* clear int */
sim_activate (uptr, tm_time); /* start io */
return;
}
/* Unit service
If rewind done, reposition to start of tape, set status
else, do operation, set done, interrupt
*/
t_stat tm_svc (UNIT *uptr)
{
int32 f, i, t, err;
t_addr xma;
t_stat rval;
t_mtrlnt tbc, cbc;
static t_mtrlnt bceof = { 0 };
if (uptr -> USTAT & STA_REW) { /* rewind? */
uptr -> pos = 0; /* update position */
if (uptr -> flags & UNIT_ATT) /* still on line? */
uptr -> USTAT = STA_ONL | STA_BOT |
((uptr -> flags & UNIT_WPRT)? STA_WLK: 0);
else uptr -> USTAT = 0;
if (uptr -> UNUM == GET_UNIT (tm_cmd)) { /* selected? */
tm_set_done (); /* set done */
tm_updcsta (uptr); } /* update status */
return SCPE_OK; }
if ((uptr -> flags & UNIT_ATT) == 0) { /* if not attached */
uptr -> USTAT = 0; /* unit off line */
tm_sta = tm_sta | STA_ILL; /* illegal operation */
tm_set_done (); /* set done */
tm_updcsta (uptr); /* update status */
return IORETURN (tm_stopioe, SCPE_UNATT); }
f = GET_FNC (tm_cmd); /* get command */
if (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) &&
(uptr -> flags & UNIT_WPRT)) { /* write and locked? */
tm_sta = tm_sta | STA_ILL; /* illegal operation */
tm_set_done (); /* set done */
tm_updcsta (uptr); /* update status */
return SCPE_OK; }
err = 0;
rval = SCPE_OK;
xma = GET_EMA (tm_cmd) | tm_ca; /* get mem addr */
cbc = 0200000 - tm_bc; /* get bc */
switch (f) { /* case on function */
/* Unit service, continued */
case MTC_READ: /* read */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
(feof (uptr -> fileref))) {
uptr -> USTAT = uptr -> USTAT | STA_EOT;
break; }
if (tbc == 0) { /* tape mark? */
uptr -> USTAT = uptr -> USTAT | STA_EOF;
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
break; }
tbc = MTRL (tbc); /* ignore error flag */
if (tbc > cbc) tm_sta = tm_sta | STA_RLE; /* wrong size? */
if (tbc < cbc) cbc = tbc; /* use smaller */
i = fxread (tmxb, sizeof (int8), cbc, uptr -> fileref);
err = ferror (uptr -> fileref);
for ( ; i < cbc; i++) tmxb[i] = 0; /* fill with 0's */
if (t = Map_WriteB (xma, cbc, tmxb, UB)) { /* copy buf to mem */
tm_sta = tm_sta | STA_NXM; /* NXM, set err */
cbc = cbc - t; } /* adj byte cnt */
xma = (xma + cbc) & 0777777; /* inc bus addr */
tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + /* upd position */
(2 * sizeof (t_mtrlnt));
break;
case MTC_WRITE: /* write */
case MTC_WREXT: /* write ext gap */
if (t = Map_ReadB (xma, cbc, tmxb, UB)) { /* copy mem to buf */
tm_sta = tm_sta | STA_NXM; /* NXM, set err */
cbc = cbc - t; /* adj byte cnt */
if (cbc == 0) break; } /* no xfr? done */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxwrite (&cbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
fxwrite (tmxb, sizeof (int8), cbc, uptr -> fileref);
fxwrite (&cbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
err = ferror (uptr -> fileref);
xma = (xma + cbc) & 0777777; /* inc bus addr */
tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */
uptr -> pos = uptr -> pos + ((cbc + 1) & ~1) + /* upd position */
(2 * sizeof (t_mtrlnt));
break;
case MTC_WREOF:
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref);
err = ferror (uptr -> fileref);
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update position */
break;
/* Unit service, continued */
case MTC_SPACEF: /* space forward */
do { tm_bc = (tm_bc + 1) & 0177777; /* incr wc */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
feof (uptr -> fileref)) {
uptr -> USTAT = uptr -> USTAT | STA_EOT;
break; }
if (tbc == 0) { /* zero bc? */
uptr -> USTAT = uptr -> USTAT | STA_EOF;
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
break; }
uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) +
(2 * sizeof (t_mtrlnt)); }
while (tm_bc != 0);
break;
case MTC_SPACER: /* space reverse */
if (uptr -> pos == 0) { /* at BOT? */
uptr -> USTAT = uptr -> USTAT | STA_BOT;
break; }
do { tm_bc = (tm_bc + 1) & 0177777; /* incr wc */
fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt),
SEEK_SET);
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
feof (uptr -> fileref)) {
uptr -> USTAT = uptr -> USTAT | STA_BOT;
uptr -> pos = 0;
break; }
if (tbc == 0) { /* start of prv file? */
uptr -> USTAT = uptr -> USTAT | STA_EOF;
uptr -> pos = uptr -> pos - sizeof (t_mtrlnt);
break; }
uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) -
(2 * sizeof (t_mtrlnt));
if (uptr -> pos == 0) { /* start of tape? */
uptr -> USTAT = uptr -> USTAT | STA_BOT;
break; } }
while (tm_bc != 0);
break; } /* end case */
/* Unit service, continued */
if (err != 0) { /* I/O error */
tm_sta = tm_sta | STA_PAR | STA_CRC; /* flag error */
perror ("MT I/O error");
rval = SCPE_IOERR;
clearerr (uptr -> fileref); }
tm_cmd = (tm_cmd & ~MTC_EMA) | ((xma >> (16 - MTC_V_EMA)) & MTC_EMA);
tm_ca = xma & 0177777; /* update mem addr */
tm_set_done (); /* set done */
tm_updcsta (uptr); /* update status */
return IORETURN (tm_stopioe, rval);
}
/* Update controller status */
int32 tm_updcsta (UNIT *uptr)
{
tm_sta = (tm_sta & ~(STA_DYN | STA_CLR)) | (uptr -> USTAT & STA_DYN);
if (sim_is_active (uptr)) tm_sta = tm_sta & ~STA_TUR;
else tm_sta = tm_sta | STA_TUR;
if (tm_sta & STA_EFLGS) tm_cmd = tm_cmd | MTC_ERR;
else tm_cmd = tm_cmd & ~MTC_ERR;
if ((tm_cmd & MTC_IE) == 0) CLR_INT (TM);
return tm_sta;
}
/* Set done */
void tm_set_done (void)
{
tm_cmd = tm_cmd | MTC_DONE;
if (tm_cmd & MTC_IE) SET_INT (TM);
return;
}
/* Reset routine */
t_stat tm_reset (DEVICE *dptr)
{
int32 u;
UNIT *uptr;
extern int32 ts_enb;
if (tm_enb) ts_enb = 0; /* TM or TS */
tm_cmd = MTC_DONE; /* set done */
tm_bc = tm_ca = tm_db = tm_sta = tm_rdl = 0;
CLR_INT (TM); /* clear interrupt */
for (u = 0; u < TM_NUMDR; u++) { /* loop thru units */
uptr = tm_dev.units + u;
uptr -> UNUM = u; /* init drive number */
sim_cancel (uptr); /* cancel activity */
if (uptr -> flags & UNIT_ATT) uptr -> USTAT = STA_ONL |
((uptr -> pos)? 0: STA_BOT) |
((uptr -> flags & UNIT_WPRT)? STA_WLK: 0);
else uptr -> USTAT = 0; }
if (tmxb == NULL) tmxb = calloc (TM_MAXFR, sizeof (unsigned int8));
if (tmxb == NULL) return SCPE_MEM;
return SCPE_OK;
}
/* Attach routine */
t_stat tm_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK) return r;
uptr -> USTAT = STA_ONL | STA_BOT | ((uptr -> flags & UNIT_WPRT)? STA_WLK: 0);
if (uptr -> UNUM == GET_UNIT (tm_cmd)) tm_updcsta (uptr);
return r;
}
/* Detach routine */
t_stat tm_detach (UNIT* uptr)
{
if (!sim_is_active (uptr)) uptr -> USTAT = 0;
if (uptr -> UNUM == GET_UNIT (tm_cmd)) tm_updcsta (uptr);
return detach_unit (uptr);
}
/* Write lock/enable routine */
t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if ((uptr -> flags & UNIT_ATT) &&
(val || (uptr -> flags & UNIT_RO)))
uptr -> USTAT = uptr -> USTAT | STA_WLK;
else uptr -> USTAT = uptr -> USTAT & ~STA_WLK;
if (uptr -> UNUM == GET_UNIT (tm_cmd)) tm_updcsta (uptr);
return SCPE_OK;
}
/* Device bootstrap
Magtape boot format changed over time. Originally, a boot tape
contained a boot loader in the first block. Eventually, the first
block was reserved for a tape label, and the second block was
expected to contain a boot loader. BSD and DEC operating systems
use the second block scheme, so it is the default.
To boot from the first block, use boot -o (old).
*/
#define BOOT_START 040000
#define BOOT_UNIT (BOOT_START + 6)
#define BOOT1_LEN (sizeof (boot1_rom) / sizeof (int32))
#define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int32))
static const int32 boot1_rom[] = {
0012706, 0040000, /* mov #boot_start, sp */
0012700, 0000000, /* mov #unit_num, r0 */
0012701, 0172526, /* mov #172526, r1 ; mtcma */
0005011, /* clr (r1) */
0011041, /* mov r1, -(r1) ; mtbrc */
0010002, /* mov r0,r2 */
0000302, /* swab r2 */
0062702, 0060003, /* add #60003, r2 */
0010241, /* mov r2, -(r1) ; read + go */
0105711, /* tstb (r1) ; mtc */
0100376, /* bpl .-2 */
0005002, /* clr r2 */
0005003, /* clr r3 */
0005004, /* clr r4 */
0012705, 0052115, /* mov #MT, r5 */
0005007 /* clr r7 */
};
static const int32 boot2_rom[] = {
0012706, 0040000, /* mov #boot_start, sp */
0012700, 0000000, /* mov #unit_num, r0 */
0012701, 0172526, /* mov #172526, r1 ; mtcma */
0005011, /* clr (r1) */
0012741, 0177777, /* mov #-1, -(r1) ; mtbrc */
0010002, /* mov r0,r2 */
0000302, /* swab r2 */
0062702, 0060011, /* add #60011, r2 */
0010241, /* mov r2, -(r1) ; space + go */
0105711, /* tstb (r1) ; mtc */
0100376, /* bpl .-2 */
0010002, /* mov r0,r2 */
0000302, /* swab r2 */
0062702, 0060003, /* add #60003, r2 */
0010211, /* mov r2, (r1) ; read + go */
0105711, /* tstb (r1) ; mtc */
0100376, /* bpl .-2 */
0005002, /* clr r2 */
0005003, /* clr r3 */
0005004, /* clr r4 */
0012705, 0052115, /* mov #MT, r5 */
0005007 /* clr r7 */
};
t_stat tm_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
extern int32 sim_switches;
tm_unit[unitno].pos = 0;
if (sim_switches & SWMASK ('O')) {
for (i = 0; i < BOOT1_LEN; i++)
M[(BOOT_START >> 1) + i] = boot1_rom[i]; }
else { for (i = 0; i < BOOT2_LEN; i++)
M[(BOOT_START >> 1) + i] = boot2_rom[i]; }
M[BOOT_UNIT >> 1] = unitno;
saved_PC = BOOT_START;
return SCPE_OK;
}

978
PDP11/pdp11_ts.c Normal file
View File

@@ -0,0 +1,978 @@
/* pdp11_ts.c: TS11/TSV05 magnetic tape simulator
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
ts TS11/TSV05 magtape
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
09-Nov-01 RMS Added bus map, VAX support
15-Oct-01 RMS Integrated debug logging across simulator
27-Sep-01 RMS Implemented extended characteristics and status
Fixed bug in write characteristics status return
19-Sep-01 RMS Fixed bug in bootstrap
15-Sep-01 RMS Fixed bug in NXM test
07-Sep-01 RMS Revised device disable and interrupt mechanism
13-Jul-01 RMS Fixed bug in space reverse (found by Peter Schorn)
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.
The TS11 functions in three environments:
- PDP-11 Q22 systems - the I/O map is one for one, so it's safe to
go through the I/O map
- PDP-11 Unibus 22b systems - the TS11 behaves as an 18b Unibus
peripheral and must go through the I/O map
- VAX Q22 systems - the TS11 must go through the I/O map
*/
#if defined (USE_INT64) /* VAX version */
#include "vax_defs.h"
#define VM_VAX 1
#define TS_RDX 16
#define TS_DF_ENB 1 /* on by default */
#define TS_18B FALSE /* always 22b */
#define ADDRTEST 0177700
#define DMASK 0xFFFF
extern int32 ReadB (t_addr pa);
extern void WriteB (t_addr pa, int32 val);
extern int32 ReadW (t_addr pa);
extern void WriteW (t_addr pa, int32 val);
#else /* PDP11 version */
#include "pdp11_defs.h"
#define VM_PDP11 1
#define TS_RDX 8
#define TS_DF_ENB 0 /* off by default */
#define TS_18B (cpu_18b || cpu_ubm)
#define ADDRTEST (TS_18B? 0177774: 0177700)
extern uint16 *M;
extern int32 cpu_18b, cpu_ubm;
#define ReadB(p) ((M[(p) >> 1] >> (((p) & 1)? 8: 0)) & 0377)
#define WriteB(p,v) M[(p) >> 1] = ((p) & 1)? \
((M[(p) >> 1] & 0377) | ((v) << 8)): \
((M[(p) >> 1] & ~0377) | (v))
#define ReadW(p) M[(p) >> 1]
#define WriteW(p,v) M[(p) >> 1] = (v)
#endif
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define TS_MAXFR (1 << 16) /* max xfer */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* TSBA/TSDB - 17772520: base address/data buffer register
read: most recent memory address
write word: initiate command
write byte: diagnostic use
*/
/* TSSR - 17772522: subsystem status register
TSDBX - 17772523: extended address register
read: return status
write word: initialize
write byte: if odd, set extended packet address register
*/
#define TSSR_SC 0100000 /* special condition */
#define TSSR_RMR 0010000 /* reg mod refused */
#define TSSR_NXM 0004000 /* nxm */
#define TSSR_NBA 0002000 /* need buf addr */
#define TSSR_V_EMA 8 /* mem addr<17:16> */
#define TSSR_EMA 0001400
#define TSSR_SSR 0000200 /* subsystem ready */
#define TSSR_OFL 0000100 /* offline */
#define TSSR_V_TC 1 /* term class */
#define TSSR_TC (07 << TSSR_V_TC)
#define TC0 (0 << TSSR_V_TC) /* ok */
#define TC1 (1 << TSSR_V_TC) /* attention */
#define TC2 (2 << TSSR_V_TC) /* status alert */
#define TC3 (3 << TSSR_V_TC) /* func reject */
#define TC4 (4 << TSSR_V_TC) /* retry, moved */
#define TC5 (5 << TSSR_V_TC) /* retry */
#define TC6 (6 << TSSR_V_TC) /* pos lost */
#define TC7 (7 << TSSR_V_TC) /* fatal err */
#define TSSR_MBZ 0060060
#define TSDBX_M_XA 017 /* ext addr */
#define TSDBX_BOOT 0000200 /* boot */
/* Command packet offsets */
#define CMD_PLNT 4 /* cmd pkt length */
#define cmdhdr tscmdp[0] /* header */
#define cmdadl tscmdp[1] /* address low */
#define cmdadh tscmdp[2] /* address high */
#define cmdlnt tscmdp[3] /* length */
/* Command packet header */
#define CMD_ACK 0100000 /* acknowledge */
#define CMD_CVC 0040000 /* clear vol chk */
#define CMD_OPP 0020000 /* opposite */
#define CMD_SWP 0010000 /* swap bytes */
#define CMD_V_MODE 8 /* mode */
#define CMD_M_MODE 017
#define CMD_IE 0000200 /* int enable */
#define CMD_V_FNC 0 /* function */
#define CMD_M_FNC 037 /* function */
#define CMD_N_FNC (CMD_M_FNC + 1)
#define FNC_READ 001 /* read */
#define FNC_WCHR 004 /* write char */
#define FNC_WRIT 005 /* write */
#define FNC_WSSM 006 /* write mem */
#define FNC_POS 010 /* position */
#define FNC_FMT 011 /* format */
#define FNC_CTL 012 /* control */
#define FNC_INIT 013 /* init */
#define FNC_GSTA 017 /* get status */
#define CMD_MBZ 0000140
#define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC)
#define GET_MOD(x) (((x) >> CMD_V_MODE) & CMD_M_MODE)
/* Function test flags */
#define FLG_MO 001 /* motion */
#define FLG_WR 002 /* write */
#define FLG_AD 004 /* addr mem */
/* Message packet offsets */
#define MSG_PLNT 8 /* packet length */
#define msghdr tsmsgp[0] /* header */
#define msglnt tsmsgp[1] /* length */
#define msgrfc tsmsgp[2] /* residual frame */
#define msgxs0 tsmsgp[3] /* ext status 0 */
#define msgxs1 tsmsgp[4] /* ext status 1 */
#define msgxs2 tsmsgp[5] /* ext status 2 */
#define msgxs3 tsmsgp[6] /* ext status 3 */
#define msgxs4 tsmsgp[7] /* ext status 4 */
/* Message packet header */
#define MSG_ACK 0100000 /* acknowledge */
#define MSG_MATN 0000000 /* attention */
#define MSG_MILL 0000400 /* illegal */
#define MSG_MNEF 0001000 /* non exec fnc */
#define MSG_CEND 0000020 /* end */
#define MSG_CFAIL 0000021 /* fail */
#define MSG_CERR 0000022 /* error */
#define MSG_CATN 0000023 /* attention */
/* Extended status register 0 */
#define XS0_TMK 0100000 /* tape mark */
#define XS0_RLS 0040000 /* rec lnt short */
#define XS0_LET 0020000 /* log end tape */
#define XS0_RLL 0010000 /* rec lnt long */
#define XS0_WLE 0004000 /* write lock err */
#define XS0_NEF 0002000 /* non exec fnc */
#define XS0_ILC 0001000 /* illegal cmd */
#define XS0_ILA 0000400 /* illegal addr */
#define XS0_MOT 0000200 /* motion */
#define XS0_ONL 0000100 /* online */
#define XS0_IE 0000040 /* int enb */
#define XS0_VCK 0000020 /* volume check */
#define XS0_PET 0000010 /* 1600 bpi */
#define XS0_WLK 0000004 /* write lock */
#define XS0_BOT 0000002 /* BOT */
#define XS0_EOT 0000001 /* EOT */
#define XS0_ALLERR 0177600 /* all errors */
/* Extended status register 1 - none of these errors are ever set */
/* Extended status register 2 */
#define XS2_XTF 0000200 /* ext features */
/* Extended status register 3 */
#define XS3_OPI 0000100 /* op incomplete */
#define XS3_REV 0000040 /* reverse */
#define XS3_RIB 0000001 /* reverse to BOT */
/* Extended status register 4 */
#define XS4_HDS 0100000 /* high density */
/* Write characteristics packet offsets */
#define WCH_PLNT 5 /* packet length */
#define wchadl tswchp[0] /* address low */
#define wchadh tswchp[1] /* address high */
#define wchlnt tswchp[2] /* length */
#define wchopt tswchp[3] /* options */
#define wchxopt tswchp[4] /* ext options */
/* Write characteristics options */
#define WCH_ESS 0000200 /* stop dbl tmk */
#define WCH_ENB 0000100 /* BOT = tmk */
#define WCH_EAI 0000040 /* enb attn int */
#define WCH_ERI 0000020 /* enb mrls int */
/* Write characteristics extended options */
#define WCHX_HDS 0000040 /* high density */
#define MAX(a,b) (((a) >= (b))? (a): (b))
extern int32 int_req[IPL_HLVL];
extern UNIT cpu_unit;
extern FILE *sim_log;
uint8 *tsxb = NULL; /* xfer buffer */
int32 tssr = 0; /* status register */
int32 tsba = 0; /* mem addr */
int32 tsdbx = 0; /* data buf ext */
int32 tscmdp[CMD_PLNT] = { 0 }; /* command packet */
int32 tsmsgp[MSG_PLNT] = { 0 }; /* message packet */
int32 tswchp[WCH_PLNT] = { 0 }; /* wr char packet */
int32 ts_ownc = 0; /* tape owns cmd */
int32 ts_ownm = 0; /* tape owns msg */
int32 ts_qatn = 0; /* queued attn */
int32 ts_bcmd = 0; /* boot cmd */
int32 ts_time = 10; /* record latency */
int32 ts_enb = TS_DF_ENB; /* device enable */
t_stat ts_svc (UNIT *uptr);
t_stat ts_reset (DEVICE *dptr);
t_stat ts_attach (UNIT *uptr, char *cptr);
t_stat ts_detach (UNIT *uptr);
t_stat ts_boot (int32 unitno);
int32 ts_updtssr (int32 t);
int32 ts_updxs0 (int32 t);
void ts_cmpendcmd (int32 s0, int32 s1);
void ts_endcmd (int32 ssf, int32 xs0f, int32 msg);
/* TS data structures
ts_dev TS device descriptor
ts_unit TS unit list
ts_reg TS register list
ts_mod TS modifier list
*/
UNIT ts_unit = { UDATA (&ts_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) };
REG ts_reg[] = {
{ GRDATA (TSSR, tssr, TS_RDX, 16, 0) },
{ GRDATA (TSBA, tsba, TS_RDX, 22, 0) },
{ GRDATA (TSDBX, tsdbx, TS_RDX, 8, 0) },
{ GRDATA (CHDR, cmdhdr, TS_RDX, 16, 0) },
{ GRDATA (CADL, cmdadl, TS_RDX, 16, 0) },
{ GRDATA (CADH, cmdadh, TS_RDX, 16, 0) },
{ GRDATA (CLNT, cmdlnt, TS_RDX, 16, 0) },
{ GRDATA (MHDR, msghdr, TS_RDX, 16, 0) },
{ GRDATA (MRFC, msgrfc, TS_RDX, 16, 0) },
{ GRDATA (MXS0, msgxs0, TS_RDX, 16, 0) },
{ GRDATA (MXS1, msgxs1, TS_RDX, 16, 0) },
{ GRDATA (MXS2, msgxs2, TS_RDX, 16, 0) },
{ GRDATA (MXS3, msgxs3, TS_RDX, 16, 0) },
{ GRDATA (MSX4, msgxs4, TS_RDX, 16, 0) },
{ GRDATA (WADL, wchadl, TS_RDX, 16, 0) },
{ GRDATA (WADH, wchadh, TS_RDX, 16, 0) },
{ GRDATA (WLNT, wchlnt, TS_RDX, 16, 0) },
{ GRDATA (WOPT, wchopt, TS_RDX, 16, 0) },
{ GRDATA (WXOPT, wchxopt, TS_RDX, 16, 0) },
{ FLDATA (INT, IREQ (TS), INT_V_TS) },
{ FLDATA (ATTN, ts_qatn, 0) },
{ FLDATA (BOOT, ts_bcmd, 0) },
{ FLDATA (OWNC, ts_ownc, 0) },
{ FLDATA (OWNM, ts_ownm, 0) },
{ DRDATA (TIME, ts_time, 24), PV_LEFT + REG_NZ },
{ DRDATA (POS, ts_unit.pos, 31), PV_LEFT + REG_RO },
{ FLDATA (WLK, ts_unit.flags, UNIT_V_WLK), REG_HRO },
{ FLDATA (*DEVENB, ts_enb, 0), REG_HRO },
{ NULL } };
MTAB ts_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ 0 } };
DEVICE ts_dev = {
"TS", &ts_unit, ts_reg, ts_mod,
1, 10, 31, 1, TS_RDX, 8,
NULL, NULL, &ts_reset,
&ts_boot, &ts_attach, &ts_detach };
/* I/O dispatch routine, I/O addresses 17772520 - 17772522
17772520 TSBA read/write
17772522 TSSR read/write
*/
t_stat ts_rd (int32 *data, int32 PA, int32 access)
{
switch ((PA >> 1) & 01) { /* decode PA<1> */
case 0: /* TSBA */
*data = tsba & DMASK; /* low 16b of ba */
break;
case 1: /* TSSR */
*data = tssr = ts_updtssr (tssr); /* update tssr */
break; }
return SCPE_OK;
}
t_stat ts_wr (int32 data, int32 PA, int32 access)
{
int32 i;
t_addr pa;
switch ((PA >> 1) & 01) { /* decode PA<1> */
case 0: /* TSDB */
if ((tssr & TSSR_SSR) == 0) { /* ready? */
tssr = tssr | TSSR_RMR; /* no, refuse */
break; }
tsba = ((tsdbx & TSDBX_M_XA) << 18) | /* form pkt addr */
((data & 03) << 16) | (data & 0177774);
tsdbx = 0; /* clr tsdbx */
tssr = ts_updtssr (tssr & TSSR_NBA); /* clr ssr, err */
msgxs0 = ts_updxs0 (msgxs0 & ~XS0_ALLERR); /* clr err, upd xs0 */
msgrfc = msgxs1 = msgxs2 = msgxs3 = msgxs4 = 0; /* clr status */
CLR_INT (TS); /* clr int req */
for (i = 0; i < CMD_PLNT; i++) { /* get cmd pkt */
if (Map_Addr (tsba, &pa) && ADDR_IS_MEM (pa))
tscmdp[i] = ReadW (pa);
else { ts_endcmd (TSSR_NXM + TC5, 0, MSG_ACK|MSG_MNEF|MSG_CFAIL);
return SCPE_OK; }
tsba = tsba + 2; } /* incr tsba */
ts_ownc = ts_ownm = 1; /* tape owns all */
sim_activate (&ts_unit, ts_time); /* activate */
break;
case 1: /* TSSR */
if (PA & 1) { /* TSDBX */
if (TS_18B) return SCPE_OK; /* not in TS11 */
if (tssr & TSSR_SSR) { /* ready? */
tsdbx = data; /* save */
if (data & TSDBX_BOOT) {
ts_bcmd = 1;
sim_activate (&ts_unit, ts_time); } }
else tssr = tssr | TSSR_RMR; } /* no, err */
else if (access == WRITE) ts_reset (&ts_dev); /* reset */
break; }
return SCPE_OK;
}
/* Tape motion routines */
#define XTC(x,t) (((unsigned) (x) << 16) | (t))
#define GET_X(x) (((x) >> 16) & 0177777)
#define GET_T(x) ((x) & 0177777)
int32 ts_rdlntf (UNIT *uptr, t_mtrlnt *tbc)
{
fseek (uptr -> fileref, uptr -> pos, SEEK_SET); /* set pos */
fxread (tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); /* read rec lnt */
if (ferror (uptr -> fileref)) return (XTC (XS0_EOT | XS0_RLS, TC2));
if (feof (uptr -> fileref)) return (XTC (XS0_TMK | XS0_RLS, TC2));
return 0;
}
int32 ts_spacef (UNIT *uptr, int32 fc, t_bool upd)
{
int32 st;
t_mtrlnt tbc;
do { if (st = ts_rdlntf (uptr, &tbc)) return st; /* read rec lnt */
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update pos */
if (tbc == 0) return (XTC (XS0_TMK | XS0_RLS, TC2));
uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) + sizeof (t_mtrlnt);
fc = (fc - 1) & DMASK; /* decr wc */
if (upd) msgrfc = fc; }
while (fc != 0);
return 0;
}
int32 ts_skipf (UNIT *uptr, int32 fc)
{
int32 tc;
t_mtrlnt prvp;
t_bool tmkprv = FALSE;
msgrfc = fc;
if ((uptr -> pos == 0) && (wchopt & WCH_ENB)) tmkprv = TRUE;
do { prvp = uptr -> pos; /* save cur pos */
tc = ts_spacef (uptr, 0, FALSE); /* space fwd */
if (GET_X (tc) & XS0_TMK) { /* tape mark? */
msgrfc = (msgrfc - 1) & DMASK; /* decr count */
if (tmkprv && (wchopt & WCH_ESS) &&
(uptr -> pos - prvp == sizeof (t_mtrlnt)))
return (XTC ((msgrfc? XS0_RLS: 0) |
XS0_TMK | XS0_LET, TC2));
tmkprv = TRUE; }
else { if (tc) return tc; /* other err? */
tmkprv = FALSE; } }
while (msgrfc != 0);
return 0;
}
int32 ts_rdlntr (UNIT *uptr, t_mtrlnt *tbc)
{
msgxs3 = msgxs3 | XS3_REV; /* set rev op */
fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt), SEEK_SET);
fxread (tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
if (ferror (uptr -> fileref) || /* error or eof? */
feof (uptr -> fileref)) {
msgxs3 = msgxs3 | XS3_OPI; /* fatal err */
return (XTC (XS0_RLS, TC6)); }
return 0;
}
int32 ts_spacer (UNIT *uptr, int32 fc, t_bool upd)
{
int32 st;
t_mtrlnt tbc;
do { if (uptr -> pos == 0) break; /* BOT? */
if (st = ts_rdlntr (uptr, &tbc)) return st; /* read rec lnt */
uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); /* update pos */
if (tbc == 0) return (XTC (XS0_TMK | XS0_RLS, TC2));
uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) - sizeof (t_mtrlnt);
fc = (fc - 1) & DMASK; /* decr wc */
if (upd) msgrfc = fc; }
while (fc != 0);
if (uptr -> pos == 0) {
msgxs3 = msgxs3 | XS3_RIB;
return (XTC (XS0_BOT | (fc? XS0_RLS: 0), TC2)); }
return 0;
}
int32 ts_skipr (UNIT *uptr, int32 fc)
{
int32 tc;
t_mtrlnt prvp;
t_bool tmkprv = FALSE;
msgrfc = fc;
do { prvp = uptr -> pos; /* save cur pos */
tc = ts_spacer (uptr, 0, FALSE); /* space rev */
if (GET_X (tc) & XS0_TMK) { /* tape mark? */
msgrfc = (msgrfc - 1) & DMASK; /* decr wc */
if (tmkprv && (wchopt & WCH_ESS) &&
(prvp - uptr -> pos == sizeof (t_mtrlnt)))
return (XTC ((msgrfc? XS0_RLS: 0) |
XS0_TMK | XS0_LET, TC2));
tmkprv = TRUE; }
else { if (tc) return tc; /* other err? */
tmkprv = FALSE; } }
while (msgrfc != 0);
return 0;
}
int32 ts_readf (UNIT *uptr, int32 fc)
{
int32 i, st;
t_mtrlnt tbc, wbc;
t_addr wa, pa;
msgrfc = fc;
if (st = ts_rdlntf (uptr, &tbc)) return st; /* read rec lnt */
if (tbc == 0) { /* tape mark? */
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update pos */
return (XTC (XS0_TMK | XS0_RLS, TC2)); }
if (fc == 0) fc = 0200000; /* byte count */
tsba = (cmdadh << 16) | cmdadl; /* buf addr */
tbc = MTRL (tbc); /* ignore err flag */
wbc = (tbc > TS_MAXFR)? TS_MAXFR: tbc; /* cap rec size */
wbc = (wbc > fc)? fc: wbc; /* cap buf size */
i = fxread (tsxb, sizeof (uint8), wbc, uptr -> fileref); /* read record */
if (ferror (uptr -> fileref)) return XTC (XS0_EOT | XS0_RLS, TC2);
for ( ; i < wbc; i++) tsxb[i] = 0; /* fill with 0's */
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + (2 * sizeof (t_mtrlnt));
for (i = 0; i < wbc; i++) { /* copy buffer */
wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */
if (Map_Addr (wa, &pa) && ADDR_IS_MEM (pa)) /* map addr, nxm? */
WriteB (pa, tsxb[i]); /* no, store */
else { tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */
return (XTC (XS0_RLS, TC4)); }
tsba = tsba + 1;
msgrfc = (msgrfc - 1) & DMASK; }
if (msgrfc) return (XTC (XS0_RLS, TC2)); /* buf too big? */
if (tbc > wbc) return (XTC (XS0_RLL, TC2)); /* rec too big? */
return 0;
}
int32 ts_readr (UNIT *uptr, int32 fc)
{
int32 i, st;
t_mtrlnt tbc, wbc;
t_addr wa, pa;
msgrfc = fc;
if (uptr -> pos == 0) { /* BOT? */
msgxs3 = msgxs3 | XS3_RIB; /* nothing to do */
return (XTC (XS0_BOT | XS0_RLS, TC2)); }
if (st = ts_rdlntr (uptr, &tbc)) return st; /* read rec lnt */
if (tbc == 0) { /* tape mark? */
uptr -> pos = uptr -> pos - sizeof (t_mtrlnt); /* update pos */
return XTC (XS0_TMK | XS0_RLS, TC2); }
if (fc == 0) fc = 0200000; /* byte count */
tsba = (cmdadh << 16) | cmdadl + fc; /* buf addr */
tbc = MTRL (tbc); /* ignore err flag */
wbc = (tbc > TS_MAXFR)? TS_MAXFR: tbc; /* cap rec size */
wbc = (wbc > fc)? fc: wbc; /* cap buf size */
fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt) - wbc, SEEK_SET);
i = fxread (tsxb, sizeof (uint8), wbc, uptr -> fileref);
for ( ; i < wbc; i++) tsxb[i] = 0; /* fill with 0's */
if (ferror (uptr -> fileref)) { /* error? */
msgxs3 = msgxs3 | XS3_OPI;
return XTC (XS0_RLS, TC6); }
uptr -> pos = uptr -> pos - ((tbc + 1) & ~1) - (2 * sizeof (t_mtrlnt));
for (i = wbc; i > 0; i--) { /* copy buffer */
tsba = tsba - 1;
wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */
if (Map_Addr (wa, &pa) && ADDR_IS_MEM (pa)) /* map addr, nxm? */
WriteB (pa, tsxb[i - 1]); /* no, store */
else { tssr = ts_updtssr (tssr | TSSR_NXM);
return (XTC (XS0_RLS, TC4)); }
msgrfc = (msgrfc - 1) & DMASK; }
if (msgrfc) return (XTC (XS0_RLS, TC2)); /* buf too big? */
if (tbc > wbc) return (XTC (XS0_RLL, TC2)); /* rec too big? */
return 0;
}
int32 ts_write (UNIT *uptr, int32 fc)
{
int32 i;
t_addr wa, pa;
msgrfc = fc;
if (fc == 0) fc = 0200000; /* byte count */
tsba = (cmdadh << 16) | cmdadl; /* buf addr */
for (i = 0; i < fc; i++) { /* copy mem to buf */
wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */
if (Map_Addr (wa, &pa) && ADDR_IS_MEM (pa)) /* map addr, nxm? */
tsxb[i] = ReadB (pa); /* no, store */
else { tssr = ts_updtssr (tssr | TSSR_NXM);
return TC5; }
tsba = tsba + 1; }
fseek (uptr -> fileref, uptr -> pos, SEEK_SET); /* position */
fxwrite (&fc, sizeof (t_mtrlnt), 1, uptr -> fileref);
fxwrite (tsxb, sizeof (uint8), fc, uptr -> fileref);
fxwrite (&fc, sizeof (t_mtrlnt), 1, uptr -> fileref);
uptr -> pos = uptr -> pos + ((fc + 1) & ~1) + (2 * sizeof (t_mtrlnt));
msgrfc = 0;
if (ferror (uptr -> fileref)) { /* error? */
msgxs3 = msgxs3 | XS3_OPI;
return TC6; }
return 0;
}
int32 ts_wtmk (UNIT *uptr)
{
t_mtrlnt bceof = 0;
fseek (uptr -> fileref, uptr -> pos, SEEK_SET); /* set pos */
fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref);
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update position */
if (ferror (uptr -> fileref)) return TC6;
return XTC (XS0_TMK, TC0);
}
/* Unit service */
t_stat ts_svc (UNIT *uptr)
{
int32 i, fnc, mod, st0, st1;
t_addr pa;
static const int32 fnc_mod[CMD_N_FNC] = { /* max mod+1 0 ill */
0, 4, 0, 0, 1, 2, 1, 0, /* 00 - 07 */
5, 3, 5, 1, 0, 0, 0, 1, /* 10 - 17 */
0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */
0, 0, 0, 0, 0, 0, 0, 0 }; /* 30 - 37 */
static const int32 fnc_flg[CMD_N_FNC] = {
0, FLG_MO+FLG_AD, 0, 0, 0, FLG_MO+FLG_WR+FLG_AD, FLG_AD, 0,
FLG_MO, FLG_MO+FLG_WR, FLG_MO, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */
0, 0, 0, 0, 0, 0, 0, 0 }; /* 30 - 37 */
if (ts_bcmd) { /* boot? */
ts_bcmd = 0; /* clear flag */
uptr -> pos = 0; /* rewind */
if (uptr -> flags & UNIT_ATT) { /* attached? */
cmdlnt = cmdadh = cmdadl = 0; /* defang rd */
ts_spacef (uptr, 1, FALSE); /* space fwd */
ts_readf (uptr, 512); /* read blk */
tssr = ts_updtssr (tssr | TSSR_SSR); }
else tssr = ts_updtssr (tssr | TSSR_SSR | TC3);
if (cmdhdr & CMD_IE) SET_INT (TS);
return SCPE_OK; }
if (!(cmdhdr & CMD_ACK)) { /* no acknowledge? */
tssr = ts_updtssr (tssr | TSSR_SSR); /* set rdy, int */
if (cmdhdr & CMD_IE) SET_INT (TS);
ts_ownc = ts_ownm = 0; /* CPU owns all */
return SCPE_OK; }
fnc = GET_FNC (cmdhdr); /* get fnc+mode */
mod = GET_MOD (cmdhdr);
if ((fnc != FNC_WCHR) && (tssr & TSSR_NBA)) { /* ~wr chr & nba? */
ts_endcmd (TC3, 0, 0); /* error */
return SCPE_OK; }
if (ts_qatn && (wchopt & WCH_EAI)) { /* attn pending? */
ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn msg */
SET_INT (TS); /* set interrupt */
ts_qatn = 0; /* not pending */
return SCPE_OK; }
if (cmdhdr & CMD_CVC) /* cvc? clr vck */
msgxs0 = msgxs0 & ~XS0_VCK;
if ((cmdhdr & CMD_MBZ) || (mod >= fnc_mod[fnc])) { /* test mbz */
ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL);
return SCPE_OK; }
if ((fnc_flg[fnc] & FLG_MO) && /* mot+(vck|!att)? */
((msgxs0 & XS0_VCK) || !(uptr -> flags & UNIT_ATT))) {
ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL);
return SCPE_OK; }
if ((fnc_flg[fnc] & FLG_WR) && /* write? */
(uptr -> flags & UNIT_WPRT)) { /* write lck? */
ts_endcmd (TC3, XS0_WLE | XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL);
return SCPE_OK; }
if ((((fnc == FNC_READ) && (mod == 1)) || /* read rev */
((fnc == FNC_POS) && (mod & 1))) && /* space rev */
(uptr -> pos == 0)) { /* BOT? */
ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL);
return SCPE_OK; }
if ((fnc_flg[fnc] & FLG_AD) && (cmdadh & ADDRTEST)) { /* buf addr > 22b? */
ts_endcmd (TC3, XS0_ILA, MSG_ACK | MSG_MILL | MSG_CFAIL);
return SCPE_OK; }
st0 = st1 = 0;
switch (fnc) { /* case on func */
case FNC_INIT: /* init */
uptr -> pos = 0; /* rewind */
case FNC_WSSM: /* write mem */
case FNC_GSTA: /* get status */
ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* send end packet */
return SCPE_OK;
case FNC_WCHR: /* write char */
if ((cmdadh & ADDRTEST) || (cmdadl & 1) || (cmdlnt < 6)) {
ts_endcmd (TSSR_NBA | TC3, XS0_ILA, 0);
break; }
tsba = (cmdadh << 16) | cmdadl;
for (i = 0; (i < WCH_PLNT) && (i < (cmdlnt / 2)); i++) {
if (Map_Addr (tsba, &pa) && ADDR_IS_MEM (pa))
tswchp[i] = ReadW (pa);
else { ts_endcmd (TSSR_NBA | TSSR_NXM | TC5, 0, 0);
return SCPE_OK; }
tsba = tsba + 2; }
if ((wchlnt < ((MSG_PLNT - 1) * 2)) || (wchadh & 0177700) ||
(wchadl & 1)) ts_endcmd (TSSR_NBA | TC3, 0, 0);
else { msgxs2 = msgxs2 | XS2_XTF | 1;
tssr = ts_updtssr (tssr & ~TSSR_NBA);
ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); }
return SCPE_OK;
case FNC_CTL: /* control */
switch (mod) { /* case mode */
case 00: /* msg buf rls */
tssr = ts_updtssr (tssr | TSSR_SSR); /* set SSR */
if (wchopt & WCH_ERI) SET_INT (TS);
ts_ownc = 0; ts_ownm = 1; /* keep msg */
break;
case 01: /* clean */
ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* nop */
break;
case 02: /* rewind and unload */
detach_unit (uptr); /* unload */
ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND);
break;
case 03: /* undefined */
ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL);
return SCPE_OK;
case 04: /* rewind */
ts_unit.pos = 0;
ts_endcmd (TC0, XS0_BOT, MSG_ACK | MSG_CEND);
break; }
break;
case FNC_READ: /* read */
switch (mod) { /* case mode */
case 00: /* fwd */
st0 = ts_readf (uptr, cmdlnt); /* read */
break;
case 01: /* back */
st0 = ts_readr (uptr, cmdlnt); /* read */
break;
case 02: /* reread fwd */
if (cmdhdr & CMD_OPP) { /* opposite? */
st0 = ts_readr (uptr, cmdlnt);
st1 = ts_spacef (uptr, 1, FALSE); }
else { st0 = ts_spacer (uptr, 1, FALSE);
st1 = ts_readf (uptr, cmdlnt); }
break;
case 03: /* reread back */
if (cmdhdr & CMD_OPP) { /* opposite */
st0 = ts_readf (uptr, cmdlnt);
st1 = ts_spacer (uptr, 1, FALSE); }
else { st0 = ts_spacef (uptr, 1, FALSE);
st1 = ts_readr (uptr, cmdlnt); }
break; }
ts_cmpendcmd (st0, st1);
break;
case FNC_WRIT: /* write */
switch (mod) { /* case mode */
case 00: /* write */
st0 = ts_write (uptr, cmdlnt);
break;
case 01: /* rewrite */
st0 = ts_spacer (uptr, 1, FALSE);
st1 = ts_write (uptr, cmdlnt);
break; }
ts_cmpendcmd (st0, st1);
break;
case FNC_FMT: /* format */
switch (mod) { /* case mode */
case 00: /* write tmk */
st0 = ts_wtmk (uptr);
break;
case 01: /* erase */
break;
case 02: /* retry tmk */
st0 = ts_spacer (uptr, 1, FALSE);
st1 = ts_wtmk (uptr);
break; }
ts_cmpendcmd (st0, st1);
break;
case FNC_POS:
switch (mod) { /* case mode */
case 00: /* space fwd */
st0 = ts_spacef (uptr, cmdadl, TRUE);
break;
case 01: /* space rev */
st0 = ts_spacer (uptr, cmdadl, TRUE);
break;
case 02: /* space ffwd */
st0 = ts_skipf (uptr, cmdadl);
break;
case 03: /* space frev */
st0 = ts_skipr (uptr, cmdadl);
break;
case 04: /* rewind */
ts_unit.pos = 0;
break; }
ts_cmpendcmd (st0, 0);
break; }
return SCPE_OK;
}
/* Utility routines */
int32 ts_updtssr (int32 t)
{
t = (t & ~TSSR_EMA) | ((tsba >> (16 - TSSR_V_EMA)) & TSSR_EMA);
if (ts_unit.flags & UNIT_ATT) t = t & ~TSSR_OFL;
else t = t | TSSR_OFL;
return (t & ~TSSR_MBZ);
}
int32 ts_updxs0 (int32 t)
{
t = (t & ~(XS0_ONL | XS0_WLK | XS0_BOT | XS0_IE)) | XS0_PET;
if (ts_unit.flags & UNIT_ATT) {
t = t | XS0_ONL;
if (ts_unit.flags & UNIT_WPRT) t = t | XS0_WLK;
if (ts_unit.pos == 0) t = (t | XS0_BOT) & ~XS0_EOT; }
else t = t & ~XS0_EOT;
if (cmdhdr & CMD_IE) t = t | XS0_IE;
return t;
}
void ts_cmpendcmd (int32 s0, int32 s1)
{
int32 xs0, ssr, tc;
static const int32 msg[8] = {
MSG_ACK | MSG_CEND, MSG_ACK | MSG_MATN | MSG_CATN,
MSG_ACK | MSG_CEND, MSG_ACK | MSG_CFAIL,
MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR,
MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR };
xs0 = GET_X (s0 | s1); /* or XS0 errs */
ssr = GET_T (s0 | s1) & ~TSSR_TC; /* or SSR errs */
tc = MAX (s0 & TSSR_TC, s1 & TSSR_TC); /* max term code */
ts_endcmd (ssr | tc, xs0, msg[tc]); /* end cmd */
return;
}
void ts_endcmd (int32 tc, int32 xs0, int32 msg)
{
int32 i;
t_addr pa;
msgxs0 = ts_updxs0 (msgxs0 | xs0); /* update XS0 */
if (wchxopt & WCHX_HDS) msgxs4 = msgxs4 | XS4_HDS; /* update XS4 */
if (msg && !(tssr & TSSR_NBA)) { /* send end pkt */
msghdr = msg;
msglnt = wchlnt - 4; /* exclude hdr, bc */
tsba = (wchadh << 16) | wchadl;
for (i = 0; (i < MSG_PLNT) && (i < (wchlnt / 2)); i++) {
if (Map_Addr (tsba, &pa) && ADDR_IS_MEM (pa))
WriteW (pa, tsmsgp[i]);
else { tssr = tssr | TSSR_NXM;
tc = (tc & ~TSSR_TC) | TC4;
break; }
tsba = tsba + 2; } }
tssr = ts_updtssr (tssr | tc | TSSR_SSR | (tc? TSSR_SC: 0));
if (cmdhdr & CMD_IE) SET_INT (TS);
ts_ownm = 0; ts_ownc = 0;
return;
}
/* Device reset */
t_stat ts_reset (DEVICE *dptr)
{
int32 i;
#if defined (VM_PDP11)
extern int32 tm_enb;
if (ts_enb) tm_enb = 0; /* TM or TS */
#endif
ts_unit.pos = 0;
tsba = tsdbx = 0;
ts_ownc = ts_ownm = 0;
ts_bcmd = 0;
ts_qatn = 0;
tssr = ts_updtssr (TSSR_NBA | TSSR_SSR);
for (i = 0; i < CMD_PLNT; i++) tscmdp[i] = 0;
for (i = 0; i < WCH_PLNT; i++) tswchp[i] = 0;
for (i = 0; i < MSG_PLNT; i++) tsmsgp[i] = 0;
msgxs0 = ts_updxs0 (XS0_VCK);
CLR_INT (TS);
if (tsxb == NULL) tsxb = calloc (TS_MAXFR, sizeof (unsigned int8));
if (tsxb == NULL) return SCPE_MEM;
return SCPE_OK;
}
/* Attach */
t_stat ts_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = attach_unit (uptr, cptr); /* attach unit */
if (r != SCPE_OK) return r; /* error? */
tssr = tssr & ~TSSR_OFL; /* clr offline */
if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) return r; /* attn msg? */
if (ts_ownm) { /* own msg buf? */
ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */
SET_INT (TS); /* set interrupt */
ts_qatn = 0; } /* don't queue */
else ts_qatn = 1; /* else queue */
return r;
}
/* Detach routine */
t_stat ts_detach (UNIT* uptr)
{
t_stat r;
r = detach_unit (uptr); /* detach unit */
if (r != SCPE_OK) return r; /* error? */
tssr = tssr | TSSR_OFL; /* set offline */
if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) return r; /* attn msg? */
if (ts_ownm) { /* own msg buf? */
ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */
SET_INT (TS); /* set interrupt */
ts_qatn = 0; } /* don't queue */
else ts_qatn = 1; /* else queue */
return r;
}
/* Boot */
#if defined (VM_PDP11)
#define BOOT_START 01000
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32))
static const int32 boot_rom[] = {
0012706, 0001000, /* mov #boot_start, sp */
0012700, 0172520, /* mov #tsba, r0 */
0012701, 0172522, /* mov #tssr, r1 */
0005011, /* clr (r1) ; init, rew */
0105711, /* tstb (r1) ; wait */
0100376, /* bpl .-2 */
0012710, 0001070, /* mov #pkt1, (r0) ; set char */
0105711, /* tstb (r1) ; wait */
0100376, /* bpl .-2 */
0012710, 0001110, /* mov #pkt2, (r0) ; read, skip */
0105711, /* tstb (r1) ; wait */
0100376, /* bpl .-2 */
0012710, 0001110, /* mov #pkt2, (r0) ; read */
0105711, /* tstb (r1) ; wait */
0100376, /* bpl .-2 */
0005711, /* tst (r1) ; err? */
0100421, /* bmi hlt */
0005000, /* clr r0 */
0012705, 0051515, /* mov #MS, r5 */
0005007, /* clr r7 */
0046523, /* pad */
0140004, /* pkt1: 140004, wcpk, 0, 8. */
0001100,
0000000,
0000010,
0001122, /* wcpk: msg, 0, 14., 0 */
0000000,
0000016,
0000000,
0140001, /* pkt2: 140001, 0, 0, 512. */
0000000,
0000000,
0001000,
0000000 /* hlt: halt */
/* msg: .blk 4 */
};
t_stat ts_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
ts_unit.pos = 0;
for (i = 0; i < BOOT_LEN; i++)
M[(BOOT_START >> 1) + i] = boot_rom[i];
saved_PC = BOOT_START;
return SCPE_OK;
}
#else
t_stat ts_boot (int32 unitno)
{
return SCPE_NOFNC;
}
#endif