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:
committed by
Mark Pizzolato
parent
654937fc88
commit
701f0fe028
1259
PDP11/pdp11_cis.c
Normal file
1259
PDP11/pdp11_cis.c
Normal file
File diff suppressed because it is too large
Load Diff
2322
PDP11/pdp11_cpu.c
Normal file
2322
PDP11/pdp11_cpu.c
Normal file
File diff suppressed because it is too large
Load Diff
454
PDP11/pdp11_defs.h
Normal file
454
PDP11/pdp11_defs.h
Normal 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
868
PDP11/pdp11_doc.txt
Normal 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
42
PDP11/pdp11_dz.c
Normal 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
998
PDP11/pdp11_fp.c
Normal 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
361
PDP11/pdp11_io.c
Normal 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
156
PDP11/pdp11_lp.c
Normal 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
592
PDP11/pdp11_rk.c
Normal 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
627
PDP11/pdp11_rl.c
Normal 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
1155
PDP11/pdp11_rp.c
Normal file
File diff suppressed because it is too large
Load Diff
1849
PDP11/pdp11_rq.c
Normal file
1849
PDP11/pdp11_rq.c
Normal file
File diff suppressed because it is too large
Load Diff
435
PDP11/pdp11_rx.c
Normal file
435
PDP11/pdp11_rx.c
Normal 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
520
PDP11/pdp11_stddev.c
Normal 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
859
PDP11/pdp11_sys.c
Normal 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
1118
PDP11/pdp11_tc.c
Normal file
File diff suppressed because it is too large
Load Diff
632
PDP11/pdp11_tm.c
Normal file
632
PDP11/pdp11_tm.c
Normal 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
978
PDP11/pdp11_ts.c
Normal 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
|
||||
Reference in New Issue
Block a user