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
128
PDP8/pdp8_clk.c
Normal file
128
PDP8/pdp8_clk.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/* pdp8_clk.c: PDP-8 real-time clock 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.
|
||||
|
||||
clk real time clock
|
||||
|
||||
05-Sep-01 RMS Added terminal multiplexor support
|
||||
17-Jul-01 RMS Moved function prototype
|
||||
05-Mar-01 RMS Added clock calibration support
|
||||
|
||||
Note: includes the IOT's for both the PDP-8/E and PDP-8/A clocks
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
extern int32 int_req, int_enable, dev_done, stop_inst;
|
||||
t_stat clk_svc (UNIT *uptr);
|
||||
t_stat clk_reset (DEVICE *dptr);
|
||||
int32 clk_tps = 60; /* ticks/second */
|
||||
int32 tmxr_poll = 16000; /* term mux poll */
|
||||
|
||||
/* CLK data structures
|
||||
|
||||
clk_dev CLK device descriptor
|
||||
clk_unit CLK unit descriptor
|
||||
clk_reg CLK register list
|
||||
*/
|
||||
|
||||
UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 };
|
||||
|
||||
REG clk_reg[] = {
|
||||
{ FLDATA (DONE, dev_done, INT_V_CLK) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_CLK) },
|
||||
{ FLDATA (INT, int_req, INT_V_CLK) },
|
||||
{ DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||
{ DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE clk_dev = {
|
||||
"CLK", &clk_unit, clk_reg, NULL,
|
||||
1, 0, 0, 0, 0, 0,
|
||||
NULL, NULL, &clk_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
/* IOT routine
|
||||
|
||||
IOT's 6131-6133 are the PDP-8/E clock
|
||||
IOT's 6135-6137 the PDP-8/A clock
|
||||
*/
|
||||
|
||||
int32 clk (int32 pulse, int32 AC)
|
||||
{
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* CLEI */
|
||||
int_enable = int_enable | INT_CLK; /* enable clk ints */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 2: /* CLDI */
|
||||
int_enable = int_enable & ~INT_CLK; /* disable clk ints */
|
||||
int_req = int_req & ~INT_CLK; /* update interrupts */
|
||||
return AC;
|
||||
case 3: /* CLSC */
|
||||
if (dev_done & INT_CLK) { /* flag set? */
|
||||
dev_done = dev_done & ~INT_CLK; /* clear flag */
|
||||
int_req = int_req & ~INT_CLK; /* clear int req */
|
||||
return IOT_SKP + AC; }
|
||||
return AC;
|
||||
case 5: /* CLLE */
|
||||
if (AC & 1) int_enable = int_enable | INT_CLK; /* test AC<11> */
|
||||
else int_enable = int_enable & ~INT_CLK;
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 6: /* CLCL */
|
||||
dev_done = dev_done & ~INT_CLK; /* clear flag */
|
||||
int_req = int_req & ~INT_CLK; /* clear int req */
|
||||
return AC;
|
||||
case 7: /* CLSK */
|
||||
return (dev_done & INT_CLK)? IOT_SKP + AC: AC;
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat clk_svc (UNIT *uptr)
|
||||
{
|
||||
int32 t;
|
||||
|
||||
dev_done = dev_done | INT_CLK; /* set done */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
t = sim_rtc_calb (clk_tps); /* calibrate clock */
|
||||
sim_activate (&clk_unit, t); /* reactivate unit */
|
||||
tmxr_poll = t; /* set mux poll */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat clk_reset (DEVICE *dptr)
|
||||
{
|
||||
dev_done = dev_done & ~INT_CLK; /* clear done, int */
|
||||
int_req = int_req & ~INT_CLK;
|
||||
int_enable = int_enable & ~INT_CLK; /* clear enable */
|
||||
sim_activate (&clk_unit, clk_unit.wait); /* activate unit */
|
||||
tmxr_poll = clk_unit.wait; /* set mux poll */
|
||||
return SCPE_OK;
|
||||
}
|
||||
1141
PDP8/pdp8_cpu.c
Normal file
1141
PDP8/pdp8_cpu.c
Normal file
File diff suppressed because it is too large
Load Diff
143
PDP8/pdp8_defs.h
Normal file
143
PDP8/pdp8_defs.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/* pdp8_defs.h: PDP-8 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.
|
||||
|
||||
25-Nov-01 RMS Added RL8A support
|
||||
16-Sep-01 RMS Added multiple KL support
|
||||
18-Mar-01 RMS Added DF32 support
|
||||
15-Feb-01 RMS Added DECtape support
|
||||
14-Apr-99 RMS Changed t_addr to unsigned
|
||||
19-Mar-95 RMS Added dynamic memory size
|
||||
02-May-94 RMS Added non-existent memory handling
|
||||
|
||||
The author gratefully acknowledges the help of Max Burnet, Richie Lary,
|
||||
and Bill Haygood in resolving questions about the PDP-8
|
||||
*/
|
||||
|
||||
#include "sim_defs.h" /* simulator defns */
|
||||
|
||||
/* Simulator stop codes */
|
||||
|
||||
#define STOP_RSRV 1 /* must be 1 */
|
||||
#define STOP_HALT 2 /* HALT */
|
||||
#define STOP_IBKPT 3 /* breakpoint */
|
||||
|
||||
/* Memory */
|
||||
|
||||
#define MAXMEMSIZE 32768 /* max memory size */
|
||||
#define MEMSIZE (cpu_unit.capac) /* actual memory size */
|
||||
#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */
|
||||
#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE)
|
||||
|
||||
/* IOT subroutine return codes */
|
||||
|
||||
#define IOT_V_SKP 12 /* skip */
|
||||
#define IOT_V_REASON 13 /* reason */
|
||||
#define IOT_SKP (1 << IOT_V_SKP)
|
||||
#define IOT_REASON (1 << IOT_V_REASON)
|
||||
#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */
|
||||
|
||||
/* Interrupt flags
|
||||
|
||||
The interrupt flags consist of three groups:
|
||||
|
||||
1. Devices with individual interrupt enables. These record
|
||||
their interrupt requests in device_done and their enables
|
||||
in device_enable, and must occupy the low bit positions.
|
||||
|
||||
2. Devices without interrupt enables. These record their
|
||||
interrupt requests directly in int_req, and must occupy
|
||||
the middle bit positions.
|
||||
|
||||
3. Overhead. These exist only in int_req and must occupy the
|
||||
high bit positions.
|
||||
|
||||
Because the PDP-8 does not have priority interrupts, the order
|
||||
of devices within groups does not matter.
|
||||
|
||||
Note: all extra KL input and output interrupts must be assigned
|
||||
to contiguous bits.
|
||||
*/
|
||||
|
||||
#define INT_V_START 0 /* enable start */
|
||||
#define INT_V_LPT (INT_V_START+0) /* line printer */
|
||||
#define INT_V_PTP (INT_V_START+1) /* tape punch */
|
||||
#define INT_V_PTR (INT_V_START+2) /* tape reader */
|
||||
#define INT_V_TTO (INT_V_START+3) /* terminal */
|
||||
#define INT_V_TTI (INT_V_START+4) /* keyboard */
|
||||
#define INT_V_CLK (INT_V_START+5) /* clock */
|
||||
#define INT_V_TTO1 (INT_V_START+6) /* tto1 */
|
||||
#define INT_V_TTO2 (INT_V_START+7) /* tto2 */
|
||||
#define INT_V_TTO3 (INT_V_START+8) /* tto3 */
|
||||
#define INT_V_TTO4 (INT_V_START+9) /* tto4 */
|
||||
#define INT_V_TTI1 (INT_V_START+10) /* tti1 */
|
||||
#define INT_V_TTI2 (INT_V_START+11) /* tti2 */
|
||||
#define INT_V_TTI3 (INT_V_START+12) /* tti3 */
|
||||
#define INT_V_TTI4 (INT_V_START+13) /* tti4 */
|
||||
#define INT_V_DIRECT (INT_V_START+14) /* direct start */
|
||||
#define INT_V_RX (INT_V_DIRECT+0) /* RX8E */
|
||||
#define INT_V_RK (INT_V_DIRECT+1) /* RK8E */
|
||||
#define INT_V_RF (INT_V_DIRECT+2) /* RF08 */
|
||||
#define INT_V_DF (INT_V_DIRECT+3) /* DF32 */
|
||||
#define INT_V_MT (INT_V_DIRECT+4) /* TM8E */
|
||||
#define INT_V_DTA (INT_V_DIRECT+5) /* TC08 */
|
||||
#define INT_V_RL (INT_V_DIRECT+6) /* RL8A */
|
||||
#define INT_V_PWR (INT_V_DIRECT+7) /* power int */
|
||||
#define INT_V_UF (INT_V_DIRECT+8) /* user int */
|
||||
#define INT_V_OVHD (INT_V_DIRECT+9) /* overhead start */
|
||||
#define INT_V_NO_ION_PENDING (INT_V_OVHD+0) /* ion pending */
|
||||
#define INT_V_NO_CIF_PENDING (INT_V_OVHD+1) /* cif pending */
|
||||
#define INT_V_ION (INT_V_OVHD+2) /* interrupts on */
|
||||
|
||||
#define INT_LPT (1 << INT_V_LPT)
|
||||
#define INT_PTP (1 << INT_V_PTP)
|
||||
#define INT_PTR (1 << INT_V_PTR)
|
||||
#define INT_TTO (1 << INT_V_TTO)
|
||||
#define INT_TTI (1 << INT_V_TTI)
|
||||
#define INT_CLK (1 << INT_V_CLK)
|
||||
#define INT_TTO1 (1 << INT_V_TTO1)
|
||||
#define INT_TTO2 (1 << INT_V_TTO2)
|
||||
#define INT_TTO3 (1 << INT_V_TTO3)
|
||||
#define INT_TTO4 (1 << INT_V_TTO4)
|
||||
#define INT_TTI1 (1 << INT_V_TTI1)
|
||||
#define INT_TTI2 (1 << INT_V_TTI2)
|
||||
#define INT_TTI3 (1 << INT_V_TTI3)
|
||||
#define INT_TTI4 (1 << INT_V_TTI4)
|
||||
#define INT_RX (1 << INT_V_RX)
|
||||
#define INT_RK (1 << INT_V_RK)
|
||||
#define INT_RF (1 << INT_V_RF)
|
||||
#define INT_DF (1 << INT_V_DF)
|
||||
#define INT_MT (1 << INT_V_MT)
|
||||
#define INT_DTA (1 << INT_V_DTA)
|
||||
#define INT_RL (1 << INT_V_RL)
|
||||
#define INT_PWR (1 << INT_V_PWR)
|
||||
#define INT_UF (1 << INT_V_UF)
|
||||
#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING)
|
||||
#define INT_NO_CIF_PENDING (1 << INT_V_NO_CIF_PENDING)
|
||||
#define INT_ION (1 << INT_V_ION)
|
||||
#define INT_DEV_ENABLE ((1 << INT_V_DIRECT) - 1) /* devices w/enables */
|
||||
#define INT_ALL ((1 << INT_V_OVHD) - 1) /* all interrupts */
|
||||
#define INT_INIT_ENABLE (INT_TTI+INT_TTO+INT_PTR+INT_PTP+INT_LPT)
|
||||
#define INT_PENDING (INT_ION+INT_NO_CIF_PENDING+INT_NO_ION_PENDING)
|
||||
#define INT_UPDATE ((int_req & ~INT_DEV_ENABLE) | (dev_done & int_enable))
|
||||
280
PDP8/pdp8_df.c
Normal file
280
PDP8/pdp8_df.c
Normal file
@@ -0,0 +1,280 @@
|
||||
/* pdp8_df.c: DF32 fixed head disk simulator
|
||||
|
||||
Copyright (c) 1993-2001, Robert M Supnik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Robert M Supnik.
|
||||
|
||||
df DF32 fixed head disk
|
||||
|
||||
28-Nov-01 RMS Added RL8A support
|
||||
25-Apr-01 RMS Added device enable/disable support
|
||||
|
||||
The DF32 is a head-per-track disk. It uses the three cycle data break
|
||||
facility. To minimize overhead, the entire DF32 is buffered in memory.
|
||||
|
||||
Two timing parameters are provided:
|
||||
|
||||
df_time Interword timing, must be non-zero
|
||||
df_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise,
|
||||
DMA occurs in a burst
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
#include <math.h>
|
||||
|
||||
/* Constants */
|
||||
|
||||
#define DF_NUMWD 2048 /* words/track */
|
||||
#define DF_NUMTR 16 /* tracks/disk */
|
||||
#define DF_NUMDK 4 /* disks/controller */
|
||||
#define DF_SIZE (DF_NUMDK * DF_NUMTR * DF_NUMWD) /* words/drive */
|
||||
#define DF_WC 07750 /* word count */
|
||||
#define DF_MA 07751 /* mem address */
|
||||
#define DF_WMASK (DF_NUMWD - 1) /* word mask */
|
||||
|
||||
/* Parameters in the unit descriptor */
|
||||
|
||||
#define FUNC u4 /* function */
|
||||
#define DF_READ 2 /* read */
|
||||
#define DF_WRITE 4 /* write */
|
||||
|
||||
/* Status register */
|
||||
|
||||
#define DFS_PCA 04000 /* photocell status */
|
||||
#define DFS_DEX 03700 /* disk addr extension */
|
||||
#define DFS_MEX 00070 /* mem addr extension */
|
||||
#define DFS_DRL 00004 /* data late error */
|
||||
#define DFS_WLS 00002 /* write lock error */
|
||||
#define DFS_PER 00001 /* parity error */
|
||||
#define DFS_ERR (DFS_DRL + DFS_WLS + DFS_PER)
|
||||
#define DFS_V_DEX 6
|
||||
#define DFS_V_MEX 3
|
||||
|
||||
#define GET_MEX(x) (((x) & DFS_MEX) << (12 - DFS_V_MEX))
|
||||
#define GET_DEX(x) (((x) & DFS_DEX) << (12 - DFS_V_DEX))
|
||||
#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
|
||||
((double) DF_NUMWD)))
|
||||
#define UPDATE_PCELL if (GET_POS (df_time) < 6) df_sta = df_sta | DFS_PCA; \
|
||||
else df_sta = df_sta & ~DFS_PCA
|
||||
|
||||
extern uint16 M[];
|
||||
extern int32 int_req, dev_enb, stop_inst;
|
||||
extern UNIT cpu_unit;
|
||||
extern int32 rf_devenb;
|
||||
int32 df_sta = 0; /* status register */
|
||||
int32 df_da = 0; /* disk address */
|
||||
int32 df_done = 0; /* done flag */
|
||||
int32 df_wlk = 0; /* write lock */
|
||||
int32 df_time = 10; /* inter-word time */
|
||||
int32 df_burst = 1; /* burst mode flag */
|
||||
int32 df_stopioe = 1; /* stop on error */
|
||||
t_stat df_svc (UNIT *uptr);
|
||||
t_stat pcell_svc (UNIT *uptr);
|
||||
t_stat df_reset (DEVICE *dptr);
|
||||
t_stat df_boot (int32 unitno);
|
||||
|
||||
/* DF32 data structures
|
||||
|
||||
df_dev RF device descriptor
|
||||
df_unit RF unit descriptor
|
||||
pcell_unit photocell timing unit (orphan)
|
||||
df_reg RF register list
|
||||
*/
|
||||
|
||||
UNIT df_unit =
|
||||
{ UDATA (&df_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
|
||||
DF_SIZE) };
|
||||
|
||||
REG df_reg[] = {
|
||||
{ ORDATA (STA, df_sta, 12) },
|
||||
{ ORDATA (DA, df_da, 12) },
|
||||
{ ORDATA (WC, M[DF_WC], 12) },
|
||||
{ ORDATA (MA, M[DF_MA], 12) },
|
||||
{ FLDATA (DONE, df_done, 0) },
|
||||
{ FLDATA (INT, int_req, INT_V_DF) },
|
||||
{ ORDATA (WLS, df_wlk, 8) },
|
||||
{ DRDATA (TIME, df_time, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (BURST, df_burst, 0) },
|
||||
{ FLDATA (STOP_IOE, df_stopioe, 0) },
|
||||
{ FLDATA (*DEVENB, dev_enb, INT_V_DF), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE df_dev = {
|
||||
"DF", &df_unit, df_reg, NULL,
|
||||
1, 8, 17, 1, 8, 12,
|
||||
NULL, NULL, &df_reset,
|
||||
&df_boot, NULL, NULL };
|
||||
|
||||
/* IOT routines */
|
||||
|
||||
int32 df60 (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 t;
|
||||
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if (pulse & 1) { /* DCMA */
|
||||
df_da = 0; /* clear disk addr */
|
||||
df_done = 0; /* clear done */
|
||||
df_sta = df_sta & ~DFS_ERR; /* clear errors */
|
||||
int_req = int_req & ~INT_DF; } /* clear int req */
|
||||
if (pulse & 6) { /* DMAR, DMAW */
|
||||
df_da = df_da | AC; /* disk addr |= AC */
|
||||
df_unit.FUNC = pulse & ~1; /* save function */
|
||||
t = (df_da & DF_WMASK) - GET_POS (df_time); /* delta to new loc */
|
||||
if (t < 0) t = t + DF_NUMWD; /* wrap around? */
|
||||
sim_activate (&df_unit, t * df_time); /* schedule op */
|
||||
AC = 0; } /* clear AC */
|
||||
return AC;
|
||||
}
|
||||
|
||||
/* Based on the hardware implementation. DEAL and DEAC work as follows:
|
||||
|
||||
6615 pulse 1 = clear df_sta<dex,mex>
|
||||
pulse 4 = df_sta = df_sta | AC<dex,mex>
|
||||
AC = AC | old_df_sta
|
||||
6616 pulse 2 = clear AC, skip if address confirmed
|
||||
pulse 4 = df_sta = df_sta | AC<dex,mex> = 0 (nop)
|
||||
AC = AC | old_df_sta
|
||||
*/
|
||||
|
||||
int32 df61 (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 old_df_sta = df_sta;
|
||||
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if (pulse & 1) /* DCEA */
|
||||
df_sta = df_sta & ~(DFS_DEX | DFS_MEX); /* clear dex, mex */
|
||||
if (pulse & 2) /* DSAC */
|
||||
AC = ((df_da & DF_WMASK) == GET_POS (df_time))? IOT_SKP: 0;
|
||||
if (pulse & 4) {
|
||||
df_sta = df_sta | (AC & (DFS_DEX | DFS_MEX)); /* DEAL */
|
||||
AC = AC | old_df_sta; } /* DEAC */
|
||||
return AC;
|
||||
}
|
||||
|
||||
int32 df62 (int32 pulse, int32 AC)
|
||||
{
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if (pulse & 1) { /* DFSE */
|
||||
if ((df_sta & DFS_ERR) == 0) AC = AC | IOT_SKP; }
|
||||
if (pulse & 2) { /* DFSC */
|
||||
if (pulse & 4) AC = AC & ~07777; /* for DMAC */
|
||||
else if (df_done) AC = AC | IOT_SKP; }
|
||||
if (pulse & 4) AC = AC | df_da; /* DMAC */
|
||||
return AC;
|
||||
}
|
||||
|
||||
/* Unit service
|
||||
|
||||
Note that for reads and writes, memory addresses wrap around in the
|
||||
current field. This code assumes the entire disk is buffered.
|
||||
*/
|
||||
|
||||
t_stat df_svc (UNIT *uptr)
|
||||
{
|
||||
int32 pa, t, mex;
|
||||
t_addr da;
|
||||
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */
|
||||
df_done = 1;
|
||||
int_req = int_req | INT_DF; /* update int req */
|
||||
return IORETURN (df_stopioe, SCPE_UNATT); }
|
||||
|
||||
mex = GET_MEX (df_sta);
|
||||
da = GET_DEX (df_sta) | df_da; /* form disk addr */
|
||||
do { M[DF_WC] = (M[DF_WC] + 1) & 07777; /* incr word count */
|
||||
M[DF_MA] = (M[DF_MA] + 1) & 07777; /* incr mem addr */
|
||||
pa = mex | M[DF_MA]; /* add extension */
|
||||
if (uptr -> FUNC == DF_READ) {
|
||||
if (MEM_ADDR_OK (pa)) /* read, check nxm */
|
||||
M[pa] = *(((int16 *) uptr -> filebuf) + da); }
|
||||
else { t = (da >> 14) & 07;
|
||||
if ((df_wlk >> t) & 1) df_sta = df_sta | DFS_WLS;
|
||||
else { *(((int16 *) uptr -> filebuf) + da) = M[pa];
|
||||
if (da >= uptr -> hwmark)
|
||||
uptr -> hwmark = da + 1; } }
|
||||
da = (da + 1) & 0377777; } /* incr disk addr */
|
||||
while ((M[DF_WC] != 0) && (df_burst != 0)); /* brk if wc, no brst */
|
||||
|
||||
if (M[DF_WC] != 0) /* more to do? */
|
||||
sim_activate (&df_unit, df_time); /* sched next */
|
||||
else { if (uptr -> FUNC != DF_READ) da = (da - 1) & 0377777;
|
||||
df_done = 1; /* done */
|
||||
int_req = int_req | INT_DF; } /* update int req */
|
||||
df_sta = (df_sta & ~DFS_DEX) | ((da >> (12 - DFS_V_DEX)) & DFS_DEX);
|
||||
df_da = da & 07777; /* separate disk addr */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat df_reset (DEVICE *dptr)
|
||||
{
|
||||
if (dev_enb & INT_DF) /* DF? no RF or RL */
|
||||
dev_enb = dev_enb & ~(INT_RF | INT_RL);
|
||||
df_sta = df_da = 0;
|
||||
df_done = 1;
|
||||
int_req = int_req & ~INT_DF; /* clear interrupt */
|
||||
sim_cancel (&df_unit);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Bootstrap routine */
|
||||
|
||||
#define OS8_START 07750
|
||||
#define OS8_LEN (sizeof (os8_rom) / sizeof (int32))
|
||||
#define DM4_START 00200
|
||||
#define DM4_LEN (sizeof (dm4_rom) / sizeof (int32))
|
||||
|
||||
static const int32 os8_rom[] = {
|
||||
07600, /* 7750, CLA CLL ; also word count */
|
||||
06603, /* 7751, DMAR ; also address */
|
||||
06622, /* 7752, DFSC ; done? */
|
||||
05352, /* 7753, JMP .-1 ; no */
|
||||
05752 /* 7754, JMP @.-2 ; enter boot */
|
||||
};
|
||||
|
||||
static const int32 dm4_rom[] = {
|
||||
00200, 07600, /* 0200, CLA CLL */
|
||||
00201, 06603, /* 0201, DMAR ; read */
|
||||
00202, 06622, /* 0202, DFSC ; done? */
|
||||
00203, 05202, /* 0203, JMP .-1 ; no */
|
||||
00204, 05600, /* 0204, JMP @.-4 ; enter boot */
|
||||
07750, 07576, /* 7750, 7576 ; word count */
|
||||
07751, 07576 /* 7751, 7576 ; address */
|
||||
};
|
||||
|
||||
t_stat df_boot (int32 unitno)
|
||||
{
|
||||
int32 i;
|
||||
extern int32 sim_switches, saved_PC;
|
||||
|
||||
if (sim_switches & SWMASK ('D')) {
|
||||
for (i = 0; i < DM4_LEN; i = i + 2)
|
||||
M[dm4_rom[i]] = dm4_rom[i + 1];
|
||||
saved_PC = DM4_START; }
|
||||
else { for (i = 0; i < OS8_LEN; i++)
|
||||
M[OS8_START + i] = os8_rom[i];
|
||||
saved_PC = OS8_START; }
|
||||
return SCPE_OK;
|
||||
}
|
||||
676
PDP8/pdp8_doc.txt
Normal file
676
PDP8/pdp8_doc.txt
Normal file
@@ -0,0 +1,676 @@
|
||||
To: Users
|
||||
From: Bob Supnik
|
||||
Subj: PDP-8 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-8 simulator.
|
||||
|
||||
|
||||
1. Simulator Files
|
||||
|
||||
sim/ sim_defs.h
|
||||
sim_sock.h
|
||||
sim_tmxr.h
|
||||
scp.c
|
||||
scp_tty.c
|
||||
sim_rev.c
|
||||
sim_sock.c
|
||||
sim_tmxr.c
|
||||
|
||||
sim/pdp8/ pdp8_defs.h
|
||||
pdp8_cpu.c
|
||||
pdp8_df.c
|
||||
pdp8_dt.c
|
||||
pdp8_lp.c
|
||||
pdp8_mt.c
|
||||
pdp8_pt.c
|
||||
pdp8_rf.c
|
||||
pdp8_rk.c
|
||||
pdp8_rl.c
|
||||
pdp8_rx.c
|
||||
pdp8_sys.c
|
||||
pdp8_tt.c
|
||||
pdp8_ttx.c
|
||||
|
||||
2. PDP-8 Features
|
||||
|
||||
The PDP-8 simulator is configured as follows:
|
||||
|
||||
device simulates
|
||||
name(s)
|
||||
|
||||
CPU PDP-8/E CPU with 32KW of memory
|
||||
- KE8E extended arithmetic element (EAE)
|
||||
- KM8E memory management and timeshare control
|
||||
PTR,PTP PC8E paper tape reader/punch
|
||||
TTI,TTO KL8E console terminal
|
||||
TTI1-4,TTO1-4 KL8JA additional terminals
|
||||
LPT LE8E line printer
|
||||
CLK DK8E line frequency clock (also PDP-8/A compatible)
|
||||
RK RK8E/RK05 cartridge disk controller with four drives
|
||||
RF RF08/RS08 fixed head disk controller with four platters, or
|
||||
DF DF32/DS32 fixed head disk controller with four platters
|
||||
RL RL8A/RL01 cartridge disk controller with four drives
|
||||
RX RX8E/RX01 floppy disk controller with two drives
|
||||
DT TC08/TU56 DECtape controller with eight drives
|
||||
MT TM8E/TU10 magnetic tape controller with eight drives
|
||||
|
||||
The RK, RF, DF, RL, RX, DT, and MT devices can be DISABLEd. The PDP-8
|
||||
can support only one of the set {DF32, RF08, RL8A},since they use the same
|
||||
IOT's. The simulator defaults to the RF08. To change the disk at device
|
||||
addresses 60-61:
|
||||
|
||||
ENABLE DF enable DF32, disable RF08 and RL8A
|
||||
ENABLE RF enable RF08, disable DF32 and RL8A
|
||||
ENABLE RL enable RL8A, disable DF32 and RF08
|
||||
|
||||
The PDP-8 simulator implements one unique stop condition: if an undefined
|
||||
instruction (unimplemented IOT or OPR) is decoded, and register STOP_INST
|
||||
is set, the simulator halts.
|
||||
|
||||
The PDP-8 loader supports both RIM format and BIN format tapes. If the file
|
||||
extension is .RIM, or the -r switch is specified with LOAD, the file is
|
||||
assumed to be RIM format; if the file extension is not .RIM, or if the -b
|
||||
switch is specified, the file is assumed to be BIN format.
|
||||
|
||||
2.1 CPU
|
||||
|
||||
The only CPU options are the presence of the EAE and the size of main
|
||||
memory; the memory extension and time-share control is always included,
|
||||
even if memory size is 4K.
|
||||
|
||||
SET CPU EAE enable EAE
|
||||
SET CPU NOEAE disable EAE
|
||||
SET CPU 4K set memory size = 4K
|
||||
SET CPU 8K set memory size = 8K
|
||||
SET CPU 12K set memory size = 12K
|
||||
SET CPU 16K set memory size = 16K
|
||||
SET CPU 20K set memory size = 20K
|
||||
SET CPU 24K set memory size = 24K
|
||||
SET CPU 28K set memory size = 28K
|
||||
SET CPU 32K set memory size = 32K
|
||||
|
||||
If memory size is being reduced, and the memory being truncated contains
|
||||
non-zero data, the simulator asks for confirmation. Data in the truncated
|
||||
portion of memory is lost. Initial memory size is 32K.
|
||||
|
||||
CPU registers include the visible state of the processor as well as the
|
||||
control registers for the interrupt system.
|
||||
|
||||
name size comments
|
||||
|
||||
PC 15 program counter, including IF as high 3 bits
|
||||
AC 12 accumulator
|
||||
MQ 12 multiplier-quotient
|
||||
L 1 link
|
||||
SR 12 front panel switches
|
||||
IF 3 instruction field
|
||||
DF 3 data field
|
||||
IB 3 instruction field buffer
|
||||
SF 7 save field
|
||||
UF 1 user mode flag
|
||||
UB 1 user mode buffer
|
||||
SC 5 EAE shift counter
|
||||
GTF 1 EAE greater than flag
|
||||
EMODE 1 EAE mode (0 = A, 1 = B)
|
||||
ION 1 interrupt enable
|
||||
ION_DELAY 1 interrupt enable delay for ION
|
||||
CIF_DELAY 1 interrupt enable delay for CIF
|
||||
PWR_INT 1 power fail interrupt
|
||||
UF_INT 1 user mode violation interrupt
|
||||
INT 15 interrupt pending flags
|
||||
DONE 15 device done flags
|
||||
ENABLE 15 device interrupt enable flags
|
||||
OLDPC 15 PC prior to last JMP, JMS, or interrupt
|
||||
STOP_INST 1 stop on undefined instruction
|
||||
WRU 8 interrupt character
|
||||
|
||||
2.2 Programmed I/O Devices
|
||||
|
||||
2.2.1 PC8E Paper Tape Reader (PTR)
|
||||
|
||||
The paper tape reader (PTR) reads data from a disk file. The POS
|
||||
register specifies the number of the next data item to be read. Thus,
|
||||
by changing POS, the user can backspace or advance the reader.
|
||||
|
||||
The paper tape reader supports the BOOT command. BOOT PTR copies the
|
||||
RIM loader into memory and starts it running.
|
||||
|
||||
The paper tape reader implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
BUF 8 last data item processed
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 position in the input file
|
||||
TIME 24 time from I/O initiation to interrupt
|
||||
STOP_IOE 1 stop on I/O error
|
||||
|
||||
Error handling is as follows:
|
||||
|
||||
error STOP_IOE processed as
|
||||
|
||||
not attached 1 report error and stop
|
||||
0 out of tape
|
||||
|
||||
end of file 1 report error and stop
|
||||
0 out of tape
|
||||
|
||||
OS I/O error x report error and stop
|
||||
|
||||
2.2.2 PC8E Paper Tape Punch (PTP)
|
||||
|
||||
The paper tape punch (PTP) writes data to a disk file. The POS register
|
||||
specifies the number of the next data item to bewritten. Thus, by
|
||||
changing POS, the user can backspace or advance the punch.
|
||||
|
||||
The paper tape punch implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
BUF 8 last data item processed
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 position in the output file
|
||||
TIME 24 time from I/O initiation to interrupt
|
||||
STOP_IOE 1 stop on I/O error
|
||||
|
||||
Error handling is as follows:
|
||||
|
||||
error STOP_IOE processed as
|
||||
|
||||
not attached 1 report error and stop
|
||||
0 out of tape
|
||||
|
||||
OS I/O error x report error and stop
|
||||
|
||||
2.2.3 KL8E Terminal Input (TTI)
|
||||
|
||||
The terminal input (TTI) polls the console keyboard for input. The
|
||||
input side has one option, UC; when set, it automatically converts lower
|
||||
case input to upper case. This is required by OS/8 and is on by default.
|
||||
|
||||
The terminal input implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
BUF 8 last data item processed
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 number of characters input
|
||||
TIME 24 keyboard polling interval
|
||||
|
||||
2.2.4 KL8E 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
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 number of characters output
|
||||
TIME 24 time from I/O initiation to interrupt
|
||||
|
||||
2.2.5 LE8E Line Printer (LPT)
|
||||
|
||||
The line printer (LPT) writes data to a disk file. The POS register
|
||||
specifies the number of the next data item to be read or written. Thus,
|
||||
by changing POS, the user can backspace or advance the printer.
|
||||
|
||||
The line printer implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
BUF 8 last data item processed
|
||||
ERR 1 error status flag
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 position in the output file
|
||||
TIME 24 time from I/O initiation to interrupt
|
||||
|
||||
Error handling is as follows:
|
||||
|
||||
error STOP_IOE processed as
|
||||
|
||||
not attached 1 report error and stop
|
||||
0 set error flag
|
||||
|
||||
OS I/O error x report error and stop
|
||||
|
||||
2.2.6 DK8E Line-Frequency Clock (CLK)
|
||||
|
||||
The real-time clock (CLK) implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
TIME 24 clock interval
|
||||
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 KL8JA Additional Terminals (TTI1-4, TTO1-4)
|
||||
|
||||
Each additional terminal consists of two independent devices, TTIn and
|
||||
TTOn. The entire set is modelled as a terminal multiplexor, with TTI1
|
||||
as the master unit. The additional terminals perform input and output
|
||||
through Telnet sessions connected to a user-specified port. The ATTACH
|
||||
command specifies the port to be used:
|
||||
|
||||
ATTACH TTI1 <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.
|
||||
|
||||
Once TTI1 is attached and the simulator is running, the terminals listen
|
||||
for connections on the specified port. They assume that the incoming
|
||||
connections are Telnet connections. The connections remain open until
|
||||
disconnected either by the Telnet client, or by a DETACH TTI1 command.
|
||||
|
||||
The SHOW TTI1 LINESTATUS command displays the current connections to the
|
||||
additional terminals.
|
||||
|
||||
The input devices (TTI1-4) implement these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
BUF 8 last data item processed
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 number of characters input
|
||||
TIME 24 keyboard polling interval
|
||||
|
||||
The output devices (TTO1-4) implement these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
BUF 8 last data item processed
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
INT 1 interrupt pending flag
|
||||
POS 31 number of characters output
|
||||
TIME 24 time from I/O initiation to interrupt
|
||||
|
||||
The additional terminals do not support save and restore. All open
|
||||
connections are lost when the simulator shuts down or TTI1 is detached.
|
||||
|
||||
2.3 Moving Head Disks
|
||||
|
||||
2.3.1 RK8E Cartridge Disk (RK)
|
||||
|
||||
RK8E options include the ability to make units write enabled or write locked:
|
||||
|
||||
SET RKn LOCKED set unit n write locked
|
||||
SET RKn ENABLED set unit n write enabled
|
||||
|
||||
Units can also be REMOVEd or ADDed to the configuration. The RK8E supports
|
||||
the BOOT command.
|
||||
|
||||
The RK8E implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
RKSTA 12 status
|
||||
RKCMD 12 disk command
|
||||
RKDA 12 disk address
|
||||
RKMA 12 current memory address
|
||||
BUSY 1 control busy flag
|
||||
INT 1 interrupt pending flag
|
||||
STIME 24 seek time, per cylinder
|
||||
RTIME 24 rotational delay
|
||||
STOP_IOE 1 stop on I/O error
|
||||
|
||||
Error handling is as follows:
|
||||
|
||||
error STOP_IOE processed as
|
||||
|
||||
not attached 1 report error and stop
|
||||
0 disk not ready
|
||||
|
||||
end of file x assume rest of disk is zero
|
||||
|
||||
OS I/O error x report error and stop
|
||||
|
||||
2.3.2 RL8A Cartridge Disk (RL)
|
||||
|
||||
RL8A options include the ability to make units write enabled or write locked:
|
||||
|
||||
SET RKn LOCKED set unit n write locked
|
||||
SET RKn ENABLED set unit n write enabled
|
||||
|
||||
Units can also be REMOVEd or ADDed to the configuration. The RK8E supports
|
||||
the BOOT command.
|
||||
|
||||
The RL8A implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
RLCSA 12 control/status A
|
||||
RLCSB 12 control/status B
|
||||
RLMA 12 memory address
|
||||
RLWC 12 word count
|
||||
RLSA 6 sector address
|
||||
RLER 12 error flags
|
||||
RLSI 16 silo top word
|
||||
RLSI1 16 silo second word
|
||||
RLSI2 16 silo third word
|
||||
RLSIL 1 silo read left/right flag
|
||||
INT 1 interrupt request
|
||||
DONE 1 done flag
|
||||
ERR 1 composite error flag
|
||||
STIME 1 seek time, per cylinder
|
||||
RTIME 1 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 RX8E/RX01 Floppy Disk (RX)
|
||||
|
||||
RX8E options include the ability to set units write enabled or write locked:
|
||||
|
||||
SET RXn LOCKED set unit n write locked
|
||||
SET RXn ENABLED set unit n write enabled
|
||||
|
||||
The RX8E supports the BOOT command.
|
||||
|
||||
The RX8E implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
RXCS 12 status
|
||||
RXDB 12 data buffer
|
||||
RXES 8 error status
|
||||
RXTA 8 current track
|
||||
RXSA 8 current sector
|
||||
STAPTR 3 controller state
|
||||
BUFPTR 3 buffer pointer
|
||||
INT 1 interrupt pending flag
|
||||
DONE 1 device done flag
|
||||
ENABLE 1 interrupt enable flag
|
||||
TR 1 transfer ready flag
|
||||
ERR 1 error flag
|
||||
CTIME 24 command completion time
|
||||
STIME 24 seek time, per track
|
||||
XTIME 24 transfer ready delay
|
||||
STOP_IOE 1 stop on I/O error
|
||||
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 Fixed Head Disks
|
||||
|
||||
Either the RF08 or the DF32 can be present in a configuration, but
|
||||
not both.
|
||||
|
||||
2.5.1 RF08/RS08 Fixed Head Disk (RF)
|
||||
|
||||
The RF08 implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
STA 12 status
|
||||
DA 20 current disk address
|
||||
MA 12 memory address (in memory)
|
||||
WC 12 word count (in memory)
|
||||
WLK 32 write lock switches
|
||||
INT 1 interrupt pending flag
|
||||
DONE 1 device done flag
|
||||
TIME 24 rotational delay, per word
|
||||
BURST 1 burst flag
|
||||
STOP_IOE 1 stop on I/O error
|
||||
|
||||
The RF08 supports the BOOT command. The default bootstrap is for OS/8. To
|
||||
bootstrap the 4K Disk Monitor, use the BOOT -D command.
|
||||
|
||||
The RF08 is a three-cycle data break device. If BURST = 0, word transfers
|
||||
are scheduled individually; if BURST = 1, the entire transfer occurs in
|
||||
a single data break.
|
||||
|
||||
Error handling is as follows:
|
||||
|
||||
error STOP_IOE processed as
|
||||
|
||||
not attached 1 report error and stop
|
||||
0 disk not ready
|
||||
|
||||
RF08 data files are buffered in memory; therefore, end of file and OS
|
||||
I/O errors cannot occur.
|
||||
|
||||
2.5.2 DF32/DS32 Fixed Head Disk (RF)
|
||||
|
||||
The DF32 implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
STA 12 status, disk and memory address extension
|
||||
DA 12 low order disk address
|
||||
MA 12 memory address (in memory)
|
||||
WC 12 word count (in memory)
|
||||
WLK 16 write lock switches
|
||||
INT 1 interrupt pending flag
|
||||
DONE 1 device done flag
|
||||
TIME 24 rotational delay, per word
|
||||
BURST 1 burst flag
|
||||
STOP_IOE 1 stop on I/O error
|
||||
|
||||
The DF32 supports the BOOT command. The default bootstrap is for OS/8. To
|
||||
bootstrap the 4K Disk Monitor, use the BOOT -D command.
|
||||
|
||||
The DF32 is a three-cycle data break device. If BURST = 0, word transfers
|
||||
are scheduled individually; if BURST = 1, the entire transfer occurs in
|
||||
a single data break.
|
||||
|
||||
Error handling is as follows:
|
||||
|
||||
error STOP_IOE processed as
|
||||
|
||||
not attached 1 report error and stop
|
||||
0 disk not ready
|
||||
|
||||
DF32 data files are buffered in memory; therefore, end of file and OS
|
||||
I/O errors cannot occur.
|
||||
|
||||
2.6 TC08/TU56 DECtape (DT)
|
||||
|
||||
DECtapes drives are numbered 1-8; in the simulator, drive 8 is unit 0.
|
||||
DECtape options include the ability to make units write enabled or write
|
||||
locked.
|
||||
|
||||
SET DTn LOCKED set unit n write locked
|
||||
SET DTn ENABLED set unit n write enabled
|
||||
|
||||
Units can also be REMOVEd or ADDed to the configuration. The TC08 supports
|
||||
the BOOT command.
|
||||
|
||||
The TC08 supports both PDP-8 format and PDP-9/11/15 format DECtape images.
|
||||
ATTACH tries to determine the tape format from the DECtape image; the user
|
||||
can force a particular format with switches:
|
||||
|
||||
-f foreign (PDP-9/11/15) format
|
||||
-n native (PDP-8) format
|
||||
|
||||
The DECtape controller is a data-only simulator; the timing and mark
|
||||
track, and block header and trailer, are not stored. Thus, the WRITE
|
||||
TIMING AND MARK TRACK function is not supported; the READ ALL function
|
||||
always returns the hardware standard block header and trailer; and the
|
||||
WRITE ALL function dumps non-data words into the bit bucket.
|
||||
|
||||
The DECtape controller implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
DTSA 12 status register A
|
||||
DTSB 12 status register B
|
||||
INT 1 interrupt pending flag
|
||||
ENB 1 interrupt enable flag
|
||||
DTF 1 DECtape flag
|
||||
ERF 1 error flag
|
||||
CA 12 current address (memory location 7754)
|
||||
WC 12 word count (memory location 7755)
|
||||
LTIME 31 time between lines
|
||||
ACTIME 31 time to accelerate to full speed
|
||||
DCTIME 31 time to decelerate to a full stop
|
||||
SUBSTATE 2 read/write command substate
|
||||
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.7 TM8E Magnetic Tape (MT)
|
||||
|
||||
Magnetic tape options include the ability to make units write enabled or
|
||||
or write locked.
|
||||
|
||||
SET MTn LOCKED set unit n write locked
|
||||
SET MTn ENABLED set unit n write enabled
|
||||
|
||||
Units can also be REMOVEd or ADDed to the configuration.
|
||||
|
||||
The magnetic tape controller implements these registers:
|
||||
|
||||
name size comments
|
||||
|
||||
CMD 12 command
|
||||
FNC 12 function
|
||||
CA 12 memory address
|
||||
WC 12 word count
|
||||
DB 12 data buffer
|
||||
STA 12 main status
|
||||
STA2 6 secondary status
|
||||
DONE 1 device done flag
|
||||
INT 1 interrupt pending flag
|
||||
STOP_IOE 1 stop on I/O error
|
||||
TIME 24 record delay
|
||||
UST[0:7] 24 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.8 Symbolic Display and Input
|
||||
|
||||
The PDP-8 simulator implements symbolic display and input. Display is
|
||||
controlled by command line switches:
|
||||
|
||||
-a display as ASCII character
|
||||
-c display as (sixbit) character string
|
||||
-t display as (TSS/8 sixbit) character string
|
||||
-m display instruction mnemonics
|
||||
|
||||
Input parsing is controlled by the first character typed in or by command
|
||||
line switches:
|
||||
|
||||
' or -a ASCII character
|
||||
" or -c two character sixbit string
|
||||
# or -t two character TSS/8 sixbit string
|
||||
alphabetic instruction mnemonic
|
||||
numeric octal number
|
||||
|
||||
Instruction input uses standard PDP-8 assembler syntax. There are four
|
||||
instruction classes: memory reference, IOT, field change, and operate.
|
||||
|
||||
Memory reference instructions have the format
|
||||
|
||||
memref {I} {C/Z} address
|
||||
|
||||
where I signifies indirect, C a current page reference, and Z a zero page
|
||||
reference. The address is an octal number in the range 0 - 07777; if C or
|
||||
Z is specified, the address is a page offset in the range 0 - 177. Normally,
|
||||
C is not needed; the simulator figures out from the address what mode to use.
|
||||
However, when referencing memory outside the CPU (eg, disks), there is no
|
||||
valid PC, and C must be used to specify current page addressing.
|
||||
|
||||
IOT instructions consist of single mnemonics, eg, KRB, TLS. IOT instructions
|
||||
may be or'd together
|
||||
|
||||
iot iot iot...
|
||||
|
||||
The simulator does not check the legality of the proposed combination. IOT's
|
||||
for which there is no opcode may be specified as IOT n, where n is an octal
|
||||
number in the range 0 - 0777.
|
||||
|
||||
Field change instructions (CIF, CDF) have the format
|
||||
|
||||
fldchg field
|
||||
|
||||
where field is an octal number in the range 0 - 7. Field change instructions
|
||||
may be or'd together.
|
||||
|
||||
Operate instructions have the format
|
||||
|
||||
opr opr opr...
|
||||
|
||||
The simulator does not check the legality of the proposed combination. EAE
|
||||
mode A and B mnemonics may be specified regardless of the EAE mode. The
|
||||
operands for MUY and DVI must be deposited explicitly.
|
||||
1130
PDP8/pdp8_dt.c
Normal file
1130
PDP8/pdp8_dt.c
Normal file
File diff suppressed because it is too large
Load Diff
148
PDP8/pdp8_lp.c
Normal file
148
PDP8/pdp8_lp.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/* pdp8_lp.c: PDP-8 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 LP8E line printer
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
extern int32 int_req, int_enable, dev_done, stop_inst;
|
||||
int32 lpt_err = 0; /* error flag */
|
||||
int32 lpt_stopioe = 0; /* stop on error */
|
||||
t_stat lpt_svc (UNIT *uptr);
|
||||
t_stat lpt_reset (DEVICE *dptr);
|
||||
t_stat lpt_attach (UNIT *uptr, char *cptr);
|
||||
t_stat lpt_detach (UNIT *uptr);
|
||||
|
||||
/* LPT data structures
|
||||
|
||||
lpt_dev LPT device descriptor
|
||||
lpt_unit LPT unit descriptor
|
||||
lpt_reg LPT register list
|
||||
*/
|
||||
|
||||
UNIT lpt_unit = {
|
||||
UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
|
||||
|
||||
REG lpt_reg[] = {
|
||||
{ ORDATA (BUF, lpt_unit.buf, 8) },
|
||||
{ FLDATA (ERR, lpt_err, 0) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_LPT) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_LPT) },
|
||||
{ FLDATA (INT, int_req, INT_V_LPT) },
|
||||
{ DRDATA (POS, lpt_unit.pos, 31), PV_LEFT },
|
||||
{ DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT },
|
||||
{ FLDATA (STOP_IOE, lpt_stopioe, 0) },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE lpt_dev = {
|
||||
"LPT", &lpt_unit, lpt_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &lpt_reset,
|
||||
NULL, &lpt_attach, &lpt_detach };
|
||||
|
||||
/* IOT routine */
|
||||
|
||||
int32 lpt (int32 pulse, int32 AC)
|
||||
{
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* PSKF */
|
||||
return (dev_done & INT_LPT)? IOT_SKP + AC: AC;
|
||||
case 2: /* PCLF */
|
||||
dev_done = dev_done & ~INT_LPT; /* clear flag */
|
||||
int_req = int_req & ~INT_LPT; /* clear int req */
|
||||
return AC;
|
||||
case 3: /* PSKE */
|
||||
return (lpt_err)? IOT_SKP + AC: AC;
|
||||
case 6: /* PCLF!PSTB */
|
||||
dev_done = dev_done & ~INT_LPT; /* clear flag */
|
||||
int_req = int_req & ~INT_LPT; /* clear int req */
|
||||
case 4: /* PSTB */
|
||||
lpt_unit.buf = AC & 0177; /* load buffer */
|
||||
if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) ||
|
||||
(lpt_unit.buf == 012)) {
|
||||
sim_activate (&lpt_unit, lpt_unit.wait);
|
||||
return AC; }
|
||||
return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC;
|
||||
case 5: /* PSIE */
|
||||
int_enable = int_enable | INT_LPT; /* set enable */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 7: /* PCIE */
|
||||
int_enable = int_enable & ~INT_LPT; /* clear enable */
|
||||
int_req = int_req & ~INT_LPT; /* clear int req */
|
||||
return AC;
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat lpt_svc (UNIT *uptr)
|
||||
{
|
||||
dev_done = dev_done | INT_LPT; /* set done */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
if ((lpt_unit.flags & UNIT_ATT) == 0) {
|
||||
lpt_err = 1;
|
||||
return IORETURN (lpt_stopioe, SCPE_UNATT); }
|
||||
if (putc (lpt_unit.buf, lpt_unit.fileref) == EOF) {
|
||||
perror ("LPT I/O error");
|
||||
clearerr (lpt_unit.fileref);
|
||||
return SCPE_IOERR; }
|
||||
lpt_unit.pos = ftell (lpt_unit.fileref);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat lpt_reset (DEVICE *dptr)
|
||||
{
|
||||
lpt_unit.buf = 0;
|
||||
dev_done = dev_done & ~INT_LPT; /* clear done, int */
|
||||
int_req = int_req & ~INT_LPT;
|
||||
int_enable = int_enable | INT_LPT; /* set enable */
|
||||
lpt_err = (lpt_unit.flags & UNIT_ATT) == 0;
|
||||
sim_cancel (&lpt_unit); /* deactivate unit */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Attach routine */
|
||||
|
||||
t_stat lpt_attach (UNIT *uptr, char *cptr)
|
||||
{
|
||||
t_stat reason;
|
||||
|
||||
reason = attach_unit (uptr, cptr);
|
||||
lpt_err = (lpt_unit.flags & UNIT_ATT) == 0;
|
||||
return reason;
|
||||
}
|
||||
|
||||
/* Detach routine */
|
||||
|
||||
t_stat lpt_detach (UNIT *uptr)
|
||||
{
|
||||
lpt_err = 1;
|
||||
return detach_unit (uptr);
|
||||
}
|
||||
569
PDP8/pdp8_mt.c
Normal file
569
PDP8/pdp8_mt.c
Normal file
@@ -0,0 +1,569 @@
|
||||
/* pdp8_mt.c: PDP-8 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.
|
||||
|
||||
mt TM8E/TU10 magtape
|
||||
|
||||
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
|
||||
24-Nov-01 RMS Changed UST, POS, FLG to arrays
|
||||
25-Apr-01 RMS Added device enable/disable support
|
||||
04-Oct-98 RMS V2.4 magtape format
|
||||
22-Jan-97 RMS V2.3 magtape format
|
||||
01-Jan-96 RMS Rewritten from TM8-E Maintenance Manual
|
||||
|
||||
Magnetic tapes are represented as a series of variable records
|
||||
of the form:
|
||||
|
||||
32b byte count
|
||||
byte 0
|
||||
byte 1
|
||||
:
|
||||
byte n-2
|
||||
byte n-1
|
||||
32b byte count
|
||||
|
||||
If the byte count is odd, the record is padded with an extra byte
|
||||
of junk. File marks are represented by a byte count of 0.
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
#define MT_NUMDR 8 /* #drives */
|
||||
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
|
||||
#define UNIT_WLK (1 << UNIT_V_WLK)
|
||||
#define UNIT_W_UF 2 /* saved user flags */
|
||||
#define USTAT u3 /* unit status */
|
||||
#define UNUM u4 /* unit number */
|
||||
#define DBSIZE (1 << 12) /* max data cmd */
|
||||
#define DBMASK (SBSIZE - 1)
|
||||
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
|
||||
|
||||
/* Command/unit - mt_cu */
|
||||
|
||||
#define CU_V_UNIT 9 /* unit */
|
||||
#define CU_M_UNIT 07
|
||||
#define CU_PARITY 00400 /* parity select */
|
||||
#define CU_IEE 00200 /* error int enable */
|
||||
#define CU_IED 00100 /* done int enable */
|
||||
#define CU_V_EMA 3 /* ext mem address */
|
||||
#define CU_M_EMA 07
|
||||
#define CU_EMA (CU_M_EMA << CU_V_EMA)
|
||||
#define CU_DTY 00002 /* drive type */
|
||||
#define CU_UNPAK 00001 /* 6b vs 8b mode */
|
||||
#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT)
|
||||
#define GET_EMA(x) (((x) & CU_EMA) << (12 - CU_V_EMA))
|
||||
|
||||
/* Function - mt_fn */
|
||||
|
||||
#define FN_V_FNC 9 /* function */
|
||||
#define FN_M_FNC 07
|
||||
#define FN_UNLOAD 00
|
||||
#define FN_REWIND 01
|
||||
#define FN_READ 02
|
||||
#define FN_CMPARE 03
|
||||
#define FN_WRITE 04
|
||||
#define FN_WREOF 05
|
||||
#define FN_SPACEF 06
|
||||
#define FN_SPACER 07
|
||||
#define FN_ERASE 00400 /* erase */
|
||||
#define FN_CRC 00200 /* read CRC */
|
||||
#define FN_GO 00100 /* go */
|
||||
#define FN_INC 00040 /* incr mode */
|
||||
#define FN_RMASK 07740 /* readable bits */
|
||||
#define GET_FNC(x) (((x) >> FN_V_FNC) & FN_M_FNC)
|
||||
|
||||
/* Status - stored in mt_sta or (*) uptr -> USTAT */
|
||||
|
||||
#define STA_ERR (04000 << 12) /* error */
|
||||
#define STA_REW (02000 << 12) /* *rewinding */
|
||||
#define STA_BOT (01000 << 12) /* *start of tape */
|
||||
#define STA_REM (00400 << 12) /* *offline */
|
||||
#define STA_PAR (00200 << 12) /* parity error */
|
||||
#define STA_EOF (00100 << 12) /* *end of file */
|
||||
#define STA_RLE (00040 << 12) /* rec lnt error */
|
||||
#define STA_DLT (00020 << 12) /* data late */
|
||||
#define STA_EOT (00010 << 12) /* *end of tape */
|
||||
#define STA_WLK (00004 << 12) /* *write locked */
|
||||
#define STA_CPE (00002 << 12) /* compare error */
|
||||
#define STA_ILL (00001 << 12) /* illegal */
|
||||
#define STA_INC 00010 /* increment error */
|
||||
#define STA_LAT 00004 /* lateral par error */
|
||||
#define STA_CRC 00002 /* CRC error */
|
||||
#define STA_LON 00001 /* long par error */
|
||||
|
||||
#define STA_CLR (FN_RMASK | 00020) /* always clear */
|
||||
#define STA_DYN (STA_REW | STA_BOT | STA_REM | STA_EOF | \
|
||||
STA_EOT | STA_WLK) /* kept in USTAT */
|
||||
#define STA_EFLGS (STA_BOT | STA_PAR | STA_RLE | STA_DLT | \
|
||||
STA_EOT | STA_CPE | STA_ILL | STA_EOF | STA_INC)
|
||||
/* set error */
|
||||
#define TUR(u) (!sim_is_active (u)) /* tape unit ready */
|
||||
|
||||
extern uint16 M[];
|
||||
extern int32 int_req, dev_enb, stop_inst;
|
||||
extern UNIT cpu_unit;
|
||||
int32 mt_cu = 0; /* command/unit */
|
||||
int32 mt_fn = 0; /* function */
|
||||
int32 mt_ca = 0; /* current address */
|
||||
int32 mt_wc = 0; /* word count */
|
||||
int32 mt_sta = 0; /* status register */
|
||||
int32 mt_db = 0; /* data buffer */
|
||||
int32 mt_done = 0; /* mag tape flag */
|
||||
int32 mt_time = 10; /* record latency */
|
||||
int32 mt_stopioe = 1; /* stop on error */
|
||||
t_stat mt_svc (UNIT *uptr);
|
||||
t_stat mt_reset (DEVICE *dptr);
|
||||
t_stat mt_attach (UNIT *uptr, char *cptr);
|
||||
t_stat mt_detach (UNIT *uptr);
|
||||
int32 mt_updcsta (UNIT *uptr);
|
||||
int32 mt_ixma (int32 xma);
|
||||
t_stat mt_vlock (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||
UNIT *mt_busy (void);
|
||||
void mt_set_done (void);
|
||||
|
||||
/* MT data structures
|
||||
|
||||
mt_dev MT device descriptor
|
||||
mt_unit MT unit list
|
||||
mt_reg MT register list
|
||||
mt_mod MT modifier list
|
||||
*/
|
||||
|
||||
UNIT mt_unit[] = {
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
|
||||
{ UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } };
|
||||
|
||||
REG mt_reg[] = {
|
||||
{ ORDATA (CMD, mt_cu, 12) },
|
||||
{ ORDATA (FNC, mt_fn, 12) },
|
||||
{ ORDATA (CA, mt_ca, 12) },
|
||||
{ ORDATA (WC, mt_wc, 12) },
|
||||
{ ORDATA (DB, mt_db, 12) },
|
||||
{ GRDATA (STA, mt_sta, 8, 12, 12) },
|
||||
{ ORDATA (STA2, mt_sta, 6) },
|
||||
{ FLDATA (DONE, mt_done, 0) },
|
||||
{ FLDATA (INT, int_req, INT_V_MT) },
|
||||
{ FLDATA (STOP_IOE, mt_stopioe, 0) },
|
||||
{ DRDATA (TIME, mt_time, 24), PV_LEFT },
|
||||
{ URDATA (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0) },
|
||||
{ URDATA (POS, mt_unit[0].pos, 10, 31, 0,
|
||||
MT_NUMDR, PV_LEFT | REG_RO) },
|
||||
{ URDATA (FLG, mt_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
|
||||
MT_NUMDR, REG_HRO) },
|
||||
{ FLDATA (*DEVENB, dev_enb, INT_V_MT), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
MTAB mt_mod[] = {
|
||||
{ UNIT_WLK, 0, "write enabled", "ENABLED", &mt_vlock },
|
||||
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &mt_vlock },
|
||||
{ 0 } };
|
||||
|
||||
DEVICE mt_dev = {
|
||||
"MT", mt_unit, mt_reg, mt_mod,
|
||||
MT_NUMDR, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &mt_reset,
|
||||
NULL, &mt_attach, &mt_detach };
|
||||
|
||||
/* IOT routines */
|
||||
|
||||
int32 mt70 (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 f;
|
||||
UNIT *uptr;
|
||||
|
||||
uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* LWCR */
|
||||
mt_wc = AC; /* load word count */
|
||||
return 0;
|
||||
case 2: /* CWCR */
|
||||
mt_wc = 0; /* clear word count */
|
||||
return AC;
|
||||
case 3: /* LCAR */
|
||||
mt_ca = AC; /* load mem address */
|
||||
return 0;
|
||||
case 4: /* CCAR */
|
||||
mt_ca = 0; /* clear mem address */
|
||||
return AC;
|
||||
case 5: /* LCMR */
|
||||
if (mt_busy ()) mt_sta = mt_sta | STA_ILL; /* busy? illegal op */
|
||||
mt_cu = AC; /* load command reg */
|
||||
mt_updcsta (mt_dev.units + GET_UNIT (mt_cu));
|
||||
return 0;
|
||||
|
||||
/* MT70, continued */
|
||||
|
||||
case 6: /* LFGR */
|
||||
if (mt_busy ()) mt_sta = mt_sta | STA_ILL; /* busy? illegal op */
|
||||
mt_fn = AC; /* load function */
|
||||
if ((mt_fn & FN_GO) == 0) { /* go set? */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
return 0; }
|
||||
f = GET_FNC (mt_fn); /* get function */
|
||||
if (((uptr -> flags & UNIT_ATT) == 0) || !TUR (uptr) ||
|
||||
(((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WPRT))
|
||||
|| (((f == FN_SPACER) || (f == FN_REWIND)) && (uptr -> pos == 0))) {
|
||||
mt_sta = mt_sta | STA_ILL; /* illegal op error */
|
||||
mt_set_done (); /* set done */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
return 0; }
|
||||
uptr -> USTAT = uptr -> USTAT & STA_WLK; /* clear status */
|
||||
if (f == FN_UNLOAD) { /* unload? */
|
||||
detach_unit (uptr); /* set offline */
|
||||
uptr -> USTAT = STA_REW | STA_REM; /* rewinding, off */
|
||||
mt_set_done (); } /* set done */
|
||||
else if (f == FN_REWIND) { /* rewind */
|
||||
uptr -> USTAT = uptr -> USTAT | STA_REW; /* rewinding */
|
||||
mt_set_done (); } /* set done */
|
||||
else mt_done = 0; /* clear done */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
sim_activate (uptr, mt_time); /* start io */
|
||||
return 0;
|
||||
case 7: /* LDBR */
|
||||
if (mt_busy ()) mt_sta = mt_sta | STA_ILL; /* busy? illegal op */
|
||||
mt_db = AC; /* load buffer */
|
||||
mt_set_done (); /* set done */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
return 0; } /* end switch */
|
||||
return (stop_inst << IOT_V_REASON) + AC; /* ill inst */
|
||||
}
|
||||
|
||||
/* IOTs, continued */
|
||||
|
||||
int32 mt71 (int32 pulse, int32 AC)
|
||||
{
|
||||
UNIT *uptr;
|
||||
|
||||
uptr = mt_dev.units + GET_UNIT (mt_cu);
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* RWCR */
|
||||
return mt_wc; /* read word count */
|
||||
case 2: /* CLT */
|
||||
mt_reset (&mt_dev); /* reset everything */
|
||||
return AC;
|
||||
case 3: /* RCAR */
|
||||
return mt_ca; /* read mem address */
|
||||
case 4: /* RMSR */
|
||||
return ((mt_updcsta (uptr) >> 12) & 07777); /* read status */
|
||||
case 5: /* RCMR */
|
||||
return mt_cu; /* read command */
|
||||
case 6: /* RFSR */
|
||||
return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK))
|
||||
& 07777); /* read function */
|
||||
case 7: /* RDBR */
|
||||
return mt_db; } /* read data buffer */
|
||||
return (stop_inst << IOT_V_REASON) + AC; /* ill inst */
|
||||
}
|
||||
|
||||
int32 mt72 (int32 pulse, int32 AC)
|
||||
{
|
||||
UNIT *uptr;
|
||||
|
||||
uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* SKEF */
|
||||
return (mt_sta & STA_ERR)? IOT_SKP + AC: AC;
|
||||
case 2: /* SKCB */
|
||||
return (!mt_busy ())? IOT_SKP + AC: AC;
|
||||
case 3: /* SKJD */
|
||||
return mt_done? IOT_SKP + AC: AC;
|
||||
case 4: /* SKTR */
|
||||
return (TUR (uptr))? IOT_SKP + AC: AC;
|
||||
case 5: /* CLF */
|
||||
if (TUR (uptr)) mt_reset (&mt_dev); /* if TUR, zap */
|
||||
else { mt_sta = 0; /* clear status */
|
||||
mt_done = 0; /* clear done */
|
||||
mt_updcsta (uptr); } /* update status */
|
||||
return AC; } /* end switch */
|
||||
return (stop_inst << IOT_V_REASON) + AC; /* ill inst */
|
||||
}
|
||||
|
||||
/* Unit service
|
||||
|
||||
If rewind done, reposition to start of tape, set status
|
||||
else, do operation, set done, interrupt
|
||||
*/
|
||||
|
||||
t_stat mt_svc (UNIT *uptr)
|
||||
{
|
||||
int32 f, i, p, u, err, wc, xma;
|
||||
t_stat rval;
|
||||
t_mtrlnt tbc, cbc;
|
||||
uint16 c, c1, c2;
|
||||
uint8 dbuf[(2 * DBSIZE)];
|
||||
static t_mtrlnt bceof = { 0 };
|
||||
|
||||
u = uptr -> UNUM; /* get unit number */
|
||||
if (uptr -> USTAT & STA_REW) { /* rewind? */
|
||||
uptr -> pos = 0; /* update position */
|
||||
if (uptr -> flags & UNIT_ATT) /* still on line? */
|
||||
uptr -> USTAT = (uptr -> USTAT & STA_WLK) | STA_BOT;
|
||||
else uptr -> USTAT = STA_REM;
|
||||
if (u == GET_UNIT (mt_cu)) { /* selected? */
|
||||
mt_set_done (); /* set done */
|
||||
mt_updcsta (uptr); } /* update status */
|
||||
return SCPE_OK; }
|
||||
|
||||
if ((uptr -> flags & UNIT_ATT) == 0) { /* if not attached */
|
||||
uptr -> USTAT = STA_REM; /* unit off line */
|
||||
mt_sta = mt_sta | STA_ILL; /* illegal operation */
|
||||
mt_set_done (); /* set done */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
return IORETURN (mt_stopioe, SCPE_UNATT); }
|
||||
|
||||
f = GET_FNC (mt_fn); /* get command */
|
||||
if (((f == FN_WRITE) || (f == FN_WREOF)) && (uptr -> flags & UNIT_WPRT)) {
|
||||
mt_sta = mt_sta | STA_ILL; /* illegal operation */
|
||||
mt_set_done (); /* set done */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
return SCPE_OK; }
|
||||
|
||||
err = 0;
|
||||
rval = SCPE_OK;
|
||||
xma = GET_EMA (mt_cu) + mt_ca; /* get mem addr */
|
||||
wc = 010000 - mt_wc; /* get wc */
|
||||
switch (f) { /* case on function */
|
||||
|
||||
/* Unit service, continued */
|
||||
|
||||
case FN_READ: /* read */
|
||||
case FN_CMPARE: /* read/compare */
|
||||
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
|
||||
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
|
||||
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
|
||||
(feof (uptr -> fileref))) {
|
||||
uptr -> USTAT = uptr -> USTAT | STA_EOT | STA_RLE;
|
||||
break; }
|
||||
if (tbc == 0) { /* tape mark? */
|
||||
uptr -> USTAT = uptr -> USTAT | STA_EOF | STA_RLE;
|
||||
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
|
||||
break; }
|
||||
cbc = (mt_cu & CU_UNPAK)? wc: wc * 2; /* expected bc */
|
||||
tbc = MTRL (tbc); /* ignore error flag */
|
||||
if (tbc != cbc) mt_sta = mt_sta | STA_RLE; /* wrong size? */
|
||||
if (tbc < cbc) { /* record small? */
|
||||
cbc = tbc; /* use smaller */
|
||||
wc = (mt_cu & CU_UNPAK)? cbc: (cbc + 1) / 2; }
|
||||
i = fxread (dbuf, sizeof (int8), cbc, uptr -> fileref);
|
||||
for ( ; i < cbc; i++) dbuf[i] = 0; /* fill with 0's */
|
||||
err = ferror (uptr -> fileref);
|
||||
for (i = p = 0; i < wc; i++) { /* copy buffer */
|
||||
xma = mt_ixma (xma); /* increment xma */
|
||||
if (mt_cu & CU_UNPAK) c = dbuf[p++];
|
||||
else { c1 = dbuf[p++] & 077;
|
||||
c2 = dbuf[p++] & 077;
|
||||
c = (c1 << 6) | c2; }
|
||||
if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c;
|
||||
else if ((f == FN_CMPARE) && (M[xma] != c)) {
|
||||
mt_sta = mt_sta | STA_CPE;
|
||||
break; } }
|
||||
mt_wc = (mt_wc + wc) & 07777; /* update wc */
|
||||
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + /* update tape pos */
|
||||
(2 * sizeof (t_mtrlnt));
|
||||
break;
|
||||
case FN_WRITE: /* write */
|
||||
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
|
||||
tbc = (mt_cu & CU_UNPAK)? wc: wc * 2;
|
||||
fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
|
||||
for (i = p = 0; i < wc; i++) { /* copy buf to tape */
|
||||
xma = mt_ixma (xma); /* incr mem addr */
|
||||
if (mt_cu & CU_UNPAK) dbuf[p++] = M[xma] & 0377;
|
||||
else { dbuf[p++] = (M[xma] >> 6) & 077;
|
||||
dbuf[p++] = M[xma] & 077; } }
|
||||
fxwrite (dbuf, sizeof (int8), (tbc + 1) & ~1, uptr -> fileref);
|
||||
fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
|
||||
err = ferror (uptr -> fileref);
|
||||
mt_wc = 0;
|
||||
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) + /* update tape pos */
|
||||
(2 * sizeof (t_mtrlnt));
|
||||
break;
|
||||
case FN_WREOF:
|
||||
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
|
||||
fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref); /* write eof */
|
||||
err = ferror (uptr -> fileref);
|
||||
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt); /* update tape pos */
|
||||
break;
|
||||
|
||||
/* Unit service, continued */
|
||||
|
||||
case FN_SPACEF: /* space forward */
|
||||
do { mt_wc = (mt_wc + 1) & 07777; /* incr wc */
|
||||
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
|
||||
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
|
||||
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
|
||||
feof (uptr -> fileref)) {
|
||||
uptr -> USTAT = uptr -> USTAT | STA_EOT;
|
||||
break; }
|
||||
if (tbc == 0) { /* zero bc? */
|
||||
uptr -> USTAT = uptr -> USTAT | STA_EOF;
|
||||
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
|
||||
break; }
|
||||
uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) +
|
||||
(2 * sizeof (t_mtrlnt)); }
|
||||
while (mt_wc != 0);
|
||||
break;
|
||||
case FN_SPACER: /* space reverse */
|
||||
if (uptr -> pos == 0) { /* at BOT? */
|
||||
uptr -> USTAT = uptr -> USTAT | STA_BOT;
|
||||
break; }
|
||||
do { mt_wc = (mt_wc + 1) & 07777; /* incr wc */
|
||||
fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt),
|
||||
SEEK_SET);
|
||||
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
|
||||
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
|
||||
feof (uptr -> fileref)) {
|
||||
uptr -> USTAT = uptr -> USTAT | STA_BOT;
|
||||
uptr -> pos = 0;
|
||||
break; }
|
||||
if (tbc == 0) { /* start of prv file? */
|
||||
uptr -> USTAT = uptr -> USTAT | STA_EOF;
|
||||
uptr -> pos = uptr -> pos - sizeof (t_mtrlnt);
|
||||
break; }
|
||||
uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) -
|
||||
(2 * sizeof (t_mtrlnt));
|
||||
if (uptr -> pos == 0) { /* at BOT? */
|
||||
uptr -> USTAT = uptr -> USTAT | STA_BOT;
|
||||
break; } }
|
||||
while (mt_wc != 0);
|
||||
break; } /* end case */
|
||||
|
||||
/* Unit service, continued */
|
||||
|
||||
if (err != 0) { /* I/O error */
|
||||
mt_sta = mt_sta | STA_PAR | STA_CRC; /* flag error */
|
||||
perror ("MT I/O error");
|
||||
rval = SCPE_IOERR;
|
||||
clearerr (uptr -> fileref); }
|
||||
mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA);
|
||||
mt_ca = xma & 07777; /* update mem addr */
|
||||
mt_set_done (); /* set done */
|
||||
mt_updcsta (uptr); /* update status */
|
||||
return IORETURN (mt_stopioe, rval);
|
||||
}
|
||||
|
||||
/* Update controller status */
|
||||
|
||||
int32 mt_updcsta (UNIT *uptr)
|
||||
{
|
||||
mt_sta = (mt_sta & ~(STA_DYN | STA_ERR | STA_CLR)) | (uptr -> USTAT & STA_DYN);
|
||||
if (mt_sta & STA_EFLGS) mt_sta = mt_sta | STA_ERR;
|
||||
if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) ||
|
||||
(mt_done && (mt_cu & CU_IED))) int_req = int_req | INT_MT;
|
||||
else int_req = int_req & ~INT_MT;
|
||||
return mt_sta;
|
||||
}
|
||||
|
||||
/* Test if controller busy */
|
||||
|
||||
UNIT *mt_busy (void)
|
||||
{
|
||||
int32 u;
|
||||
UNIT *uptr;
|
||||
|
||||
for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */
|
||||
uptr = mt_dev.units + u;
|
||||
if (sim_is_active (uptr) && ((uptr -> USTAT & STA_REW) == 0))
|
||||
return uptr; }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Increment extended memory address */
|
||||
|
||||
int32 mt_ixma (int32 xma) /* incr extended ma */
|
||||
{
|
||||
int32 v;
|
||||
|
||||
v = ((xma + 1) & 07777) | (xma & 070000); /* wrapped incr */
|
||||
if (mt_fn & FN_INC) { /* increment mode? */
|
||||
if (xma == 077777) mt_sta = mt_sta | STA_INC; /* at limit? error */
|
||||
else v = xma + 1; } /* else 15b incr */
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Set done */
|
||||
|
||||
void mt_set_done (void)
|
||||
{
|
||||
mt_done = 1; /* set done */
|
||||
mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC); /* clear func<4:6> */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat mt_reset (DEVICE *dptr)
|
||||
{
|
||||
int32 u;
|
||||
UNIT *uptr;
|
||||
|
||||
mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0;
|
||||
int_req = int_req & ~INT_MT; /* clear interrupt */
|
||||
for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */
|
||||
uptr = mt_dev.units + u;
|
||||
uptr -> UNUM = u; /* init drive number */
|
||||
sim_cancel (uptr); /* cancel activity */
|
||||
if (uptr -> flags & UNIT_ATT) uptr -> USTAT =
|
||||
((uptr -> pos)? 0: STA_BOT) |
|
||||
((uptr -> flags & UNIT_WPRT)? STA_WLK: 0);
|
||||
else uptr -> USTAT = STA_REM; }
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Attach routine */
|
||||
|
||||
t_stat mt_attach (UNIT *uptr, char *cptr)
|
||||
{
|
||||
t_stat r;
|
||||
|
||||
r = attach_unit (uptr, cptr);
|
||||
if (r != SCPE_OK) return r;
|
||||
uptr -> USTAT = STA_BOT | ((uptr -> flags & UNIT_WPRT)? STA_WLK: 0);
|
||||
if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Detach routine */
|
||||
|
||||
t_stat mt_detach (UNIT* uptr)
|
||||
{
|
||||
if (!sim_is_active (uptr)) uptr -> USTAT = STA_REM;
|
||||
if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr);
|
||||
return detach_unit (uptr);
|
||||
}
|
||||
|
||||
/* Write lock/enable routine */
|
||||
|
||||
t_stat mt_vlock (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||||
{
|
||||
if ((uptr -> flags & UNIT_ATT) && val) uptr -> USTAT = uptr -> USTAT | STA_WLK;
|
||||
else uptr -> USTAT = uptr -> USTAT & ~STA_WLK;
|
||||
if (uptr -> UNUM == GET_UNIT (mt_cu)) mt_updcsta (uptr);
|
||||
return SCPE_OK;
|
||||
}
|
||||
244
PDP8/pdp8_pt.c
Normal file
244
PDP8/pdp8_pt.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator
|
||||
|
||||
Copyright (c) 1993-2001, Robert M Supnik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Robert M Supnik.
|
||||
|
||||
ptr,ptp PC8E paper tape reader/punch
|
||||
|
||||
30-Nov-01 RMS Added read only unit support
|
||||
30-Mar-98 RMS Added RIM loader as PTR bootstrap
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
extern int32 int_req, int_enable, dev_done, stop_inst;
|
||||
int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */
|
||||
t_stat ptr_svc (UNIT *uptr);
|
||||
t_stat ptp_svc (UNIT *uptr);
|
||||
t_stat ptr_reset (DEVICE *dptr);
|
||||
t_stat ptp_reset (DEVICE *dptr);
|
||||
t_stat ptr_boot (int32 unitno);
|
||||
|
||||
/* 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) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_PTR) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_PTR) },
|
||||
{ FLDATA (INT, int_req, INT_V_PTR) },
|
||||
{ DRDATA (POS, ptr_unit.pos, 31), PV_LEFT },
|
||||
{ DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT },
|
||||
{ FLDATA (STOP_IOE, ptr_stopioe, 0) },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE ptr_dev = {
|
||||
"PTR", &ptr_unit, ptr_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ptr_reset,
|
||||
&ptr_boot, NULL, NULL };
|
||||
|
||||
/* PTP data structures
|
||||
|
||||
ptp_dev PTP device descriptor
|
||||
ptp_unit PTP unit descriptor
|
||||
ptp_reg PTP register list
|
||||
*/
|
||||
|
||||
UNIT ptp_unit = {
|
||||
UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
|
||||
|
||||
REG ptp_reg[] = {
|
||||
{ ORDATA (BUF, ptp_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_PTP) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_PTP) },
|
||||
{ FLDATA (INT, int_req, INT_V_PTP) },
|
||||
{ DRDATA (POS, ptp_unit.pos, 31), PV_LEFT },
|
||||
{ DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT },
|
||||
{ FLDATA (STOP_IOE, ptp_stopioe, 0) },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE ptp_dev = {
|
||||
"PTP", &ptp_unit, ptp_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ptp_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
/* Paper tape reader: IOT routine */
|
||||
|
||||
int32 ptr (int32 pulse, int32 AC)
|
||||
{
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 0: /* RPE */
|
||||
int_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 1: /* RSF */
|
||||
return (dev_done & INT_PTR)? IOT_SKP + AC: AC;
|
||||
case 6: /* RFC!RRB */
|
||||
sim_activate (&ptr_unit, ptr_unit.wait);
|
||||
case 2: /* RRB */
|
||||
dev_done = dev_done & ~INT_PTR; /* clear flag */
|
||||
int_req = int_req & ~INT_PTR; /* clear int req */
|
||||
return (AC | ptr_unit.buf); /* or data to AC */
|
||||
case 4: /* RFC */
|
||||
sim_activate (&ptr_unit, ptr_unit.wait);
|
||||
dev_done = dev_done & ~INT_PTR; /* clear flag */
|
||||
int_req = int_req & ~INT_PTR; /* clear int req */
|
||||
return AC;
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat ptr_svc (UNIT *uptr)
|
||||
{
|
||||
int32 temp;
|
||||
|
||||
if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */
|
||||
return IORETURN (ptr_stopioe, SCPE_UNATT);
|
||||
if ((temp = getc (ptr_unit.fileref)) == EOF) {
|
||||
if (feof (ptr_unit.fileref)) {
|
||||
if (ptr_stopioe) printf ("PTR end of file\n");
|
||||
else return SCPE_OK; }
|
||||
else perror ("PTR I/O error");
|
||||
clearerr (ptr_unit.fileref);
|
||||
return SCPE_IOERR; }
|
||||
dev_done = dev_done | INT_PTR; /* set done */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
ptr_unit.buf = temp & 0377;
|
||||
ptr_unit.pos = ptr_unit.pos + 1;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat ptr_reset (DEVICE *dptr)
|
||||
{
|
||||
ptr_unit.buf = 0;
|
||||
dev_done = dev_done & ~INT_PTR; /* clear done, int */
|
||||
int_req = int_req & ~INT_PTR;
|
||||
int_enable = int_enable | INT_PTR; /* set enable */
|
||||
sim_cancel (&ptr_unit); /* deactivate unit */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Paper tape punch: IOT routine */
|
||||
|
||||
int32 ptp (int32 pulse, int32 AC)
|
||||
{
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 0: /* PCE */
|
||||
int_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 1: /* PSF */
|
||||
return (dev_done & INT_PTP)? IOT_SKP + AC: AC;
|
||||
case 2: /* PCF */
|
||||
dev_done = dev_done & ~INT_PTP; /* clear flag */
|
||||
int_req = int_req & ~INT_PTP; /* clear int req */
|
||||
return AC;
|
||||
case 6: /* PLS */
|
||||
dev_done = dev_done & ~INT_PTP; /* clear flag */
|
||||
int_req = int_req & ~INT_PTP; /* clear int req */
|
||||
case 4: /* PPC */
|
||||
ptp_unit.buf = AC & 0377; /* load punch buf */
|
||||
sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */
|
||||
return AC;
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat ptp_svc (UNIT *uptr)
|
||||
{
|
||||
dev_done = dev_done | INT_PTP; /* set done */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */
|
||||
return IORETURN (ptp_stopioe, SCPE_UNATT);
|
||||
if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) {
|
||||
perror ("PTP I/O error");
|
||||
clearerr (ptp_unit.fileref);
|
||||
return SCPE_IOERR; }
|
||||
ptp_unit.pos = ptp_unit.pos + 1;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat ptp_reset (DEVICE *dptr)
|
||||
{
|
||||
ptp_unit.buf = 0;
|
||||
dev_done = dev_done & ~INT_PTP; /* clear done, int */
|
||||
int_req = int_req & ~INT_PTP;
|
||||
int_enable = int_enable | INT_PTP; /* set enable */
|
||||
sim_cancel (&ptp_unit); /* deactivate unit */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Bootstrap routine */
|
||||
|
||||
#define BOOT_START 07756
|
||||
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
|
||||
|
||||
static const int32 boot_rom[] = {
|
||||
06014, /* 7756, RFC */
|
||||
06011, /* 7757, LOOP, RSF */
|
||||
05357, /* JMP .-1 */
|
||||
06016, /* RFC RRB */
|
||||
07106, /* CLL RTL*/
|
||||
07006, /* RTL */
|
||||
07510, /* SPA*/
|
||||
05374, /* JMP 7774 */
|
||||
07006, /* RTL */
|
||||
06011, /* RSF */
|
||||
05367, /* JMP .-1 */
|
||||
06016, /* RFC RRB */
|
||||
07420, /* SNL */
|
||||
03776, /* DCA I 7776 */
|
||||
03376, /* 7774, DCA 7776 */
|
||||
05357, /* JMP 7757 */
|
||||
00000, /* 7776, 0 */
|
||||
05301 /* 7777, JMP 7701 */
|
||||
};
|
||||
|
||||
t_stat ptr_boot (int32 unitno)
|
||||
{
|
||||
int32 i;
|
||||
extern int32 saved_PC;
|
||||
extern uint16 M[];
|
||||
|
||||
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
|
||||
saved_PC = BOOT_START;
|
||||
return SCPE_OK;
|
||||
}
|
||||
318
PDP8/pdp8_rf.c
Normal file
318
PDP8/pdp8_rf.c
Normal file
@@ -0,0 +1,318 @@
|
||||
/* pdp8_rf.c: RF08 fixed head disk simulator
|
||||
|
||||
Copyright (c) 1993-2001, Robert M Supnik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Robert M Supnik.
|
||||
|
||||
rf RF08 fixed head disk
|
||||
|
||||
28-Nov-01 RMS Added RL8A support
|
||||
25-Apr-01 RMS Added device enable/disable support
|
||||
19-Mar-01 RMS Added disk monitor bootstrap, fixed IOT decoding
|
||||
15-Feb-01 RMS Fixed 3 cycle data break sequence
|
||||
14-Apr-99 RMS Changed t_addr to unsigned
|
||||
30-Mar-98 RMS Fixed bug in RF bootstrap
|
||||
|
||||
The RF08 is a head-per-track disk. It uses the three cycle data break
|
||||
facility. To minimize overhead, the entire RF08 is buffered in memory.
|
||||
|
||||
Two timing parameters are provided:
|
||||
|
||||
rf_time Interword timing, must be non-zero
|
||||
rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise,
|
||||
DMA occurs in a burst
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
#include <math.h>
|
||||
|
||||
/* Constants */
|
||||
|
||||
#define RF_NUMWD 2048 /* words/track */
|
||||
#define RF_NUMTR 128 /* tracks/disk */
|
||||
#define RF_NUMDK 4 /* disks/controller */
|
||||
#define RF_SIZE (RF_NUMDK * RF_NUMTR * RF_NUMWD) /* words/drive */
|
||||
#define RF_WC 07750 /* word count */
|
||||
#define RF_MA 07751 /* mem address */
|
||||
#define RF_WMASK (RF_NUMWD - 1) /* word mask */
|
||||
|
||||
/* Parameters in the unit descriptor */
|
||||
|
||||
#define FUNC u4 /* function */
|
||||
#define RF_READ 2 /* read */
|
||||
#define RF_WRITE 4 /* write */
|
||||
|
||||
/* Status register */
|
||||
|
||||
#define RFS_PCA 04000 /* photocell status */
|
||||
#define RFS_DRE 02000 /* data req enable */
|
||||
#define RFS_WLS 01000 /* write lock status */
|
||||
#define RFS_EIE 00400 /* error int enable */
|
||||
#define RFS_PIE 00200 /* photocell int enb */
|
||||
#define RFS_CIE 00100 /* done int enable */
|
||||
#define RFS_MEX 00070 /* memory extension */
|
||||
#define RFS_DRL 00004 /* data late error */
|
||||
#define RFS_NXD 00002 /* non-existent disk */
|
||||
#define RFS_PER 00001 /* parity error */
|
||||
#define RFS_ERR (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER)
|
||||
#define RFS_V_MEX 3
|
||||
|
||||
#define GET_MEX(x) (((x) & RFS_MEX) << (12 - RFS_V_MEX))
|
||||
#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \
|
||||
((double) RF_NUMWD)))
|
||||
#define UPDATE_PCELL if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \
|
||||
else rf_sta = rf_sta & ~RFS_PCA
|
||||
#define RF_INT_UPDATE if ((rf_done && (rf_sta & RFS_CIE)) || \
|
||||
((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \
|
||||
((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \
|
||||
int_req = int_req | INT_RF; \
|
||||
else int_req = int_req & ~INT_RF
|
||||
|
||||
extern uint16 M[];
|
||||
extern int32 int_req, dev_enb, stop_inst;
|
||||
extern UNIT cpu_unit;
|
||||
extern int32 df_devenb;
|
||||
int32 rf_sta = 0; /* status register */
|
||||
int32 rf_da = 0; /* disk address */
|
||||
int32 rf_done = 0; /* done flag */
|
||||
int32 rf_wlk = 0; /* write lock */
|
||||
int32 rf_time = 10; /* inter-word time */
|
||||
int32 rf_burst = 1; /* burst mode flag */
|
||||
int32 rf_stopioe = 1; /* stop on error */
|
||||
t_stat rf_svc (UNIT *uptr);
|
||||
t_stat pcell_svc (UNIT *uptr);
|
||||
t_stat rf_reset (DEVICE *dptr);
|
||||
t_stat rf_boot (int32 unitno);
|
||||
|
||||
/* RF08 data structures
|
||||
|
||||
rf_dev RF device descriptor
|
||||
rf_unit RF unit descriptor
|
||||
pcell_unit photocell timing unit (orphan)
|
||||
rf_reg RF register list
|
||||
*/
|
||||
|
||||
UNIT rf_unit =
|
||||
{ UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
|
||||
RF_SIZE) };
|
||||
|
||||
UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) };
|
||||
|
||||
REG rf_reg[] = {
|
||||
{ ORDATA (STA, rf_sta, 12) },
|
||||
{ ORDATA (DA, rf_da, 20) },
|
||||
{ ORDATA (WC, M[RF_WC], 12) },
|
||||
{ ORDATA (MA, M[RF_MA], 12) },
|
||||
{ FLDATA (DONE, rf_done, 0) },
|
||||
{ FLDATA (INT, int_req, INT_V_RF) },
|
||||
{ ORDATA (WLK, rf_wlk, 32) },
|
||||
{ DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (BURST, rf_burst, 0) },
|
||||
{ FLDATA (STOP_IOE, rf_stopioe, 0) },
|
||||
{ FLDATA (*DEVENB, dev_enb, INT_V_RF), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE rf_dev = {
|
||||
"RF", &rf_unit, rf_reg, NULL,
|
||||
1, 8, 20, 1, 8, 12,
|
||||
NULL, NULL, &rf_reset,
|
||||
&rf_boot, NULL, NULL };
|
||||
|
||||
/* IOT routines */
|
||||
|
||||
int32 rf60 (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 t;
|
||||
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if (pulse & 1) { /* DCMA */
|
||||
rf_da = rf_da & ~07777; /* clear DAR<8:19> */
|
||||
rf_done = 0; /* clear done */
|
||||
rf_sta = rf_sta & ~RFS_ERR; /* clear errors */
|
||||
RF_INT_UPDATE; } /* update int req */
|
||||
if (pulse & 6) { /* DMAR, DMAW */
|
||||
rf_da = rf_da | AC; /* DAR<8:19> |= AC */
|
||||
rf_unit.FUNC = pulse & ~1; /* save function */
|
||||
t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */
|
||||
if (t < 0) t = t + RF_NUMWD; /* wrap around? */
|
||||
sim_activate (&rf_unit, t * rf_time); /* schedule op */
|
||||
AC = 0; } /* clear AC */
|
||||
return AC;
|
||||
}
|
||||
|
||||
int32 rf61 (int32 pulse, int32 AC)
|
||||
{
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* DCIM */
|
||||
rf_sta = rf_sta & 07007; /* clear STA<3:8> */
|
||||
int_req = int_req & ~INT_RF; /* clear int req */
|
||||
sim_cancel (&pcell_unit); /* cancel photocell */
|
||||
return AC;
|
||||
case 2: /* DSAC */
|
||||
return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP + AC: AC;
|
||||
case 5: /* DIML */
|
||||
rf_sta = (rf_sta & 07007) | (AC & 0770); /* STA<3:8> <- AC */
|
||||
if (rf_sta & RFS_PIE) /* photocell int? */
|
||||
sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) *
|
||||
rf_time);
|
||||
else sim_cancel (&pcell_unit);
|
||||
RF_INT_UPDATE; /* update int req */
|
||||
return 0; /* clear AC */
|
||||
case 6: /* DIMA */
|
||||
return rf_sta; } /* AC <- STA<0:11> */
|
||||
return AC;
|
||||
}
|
||||
|
||||
/* IOT's, continued */
|
||||
|
||||
int32 rf62 (int32 pulse, int32 AC)
|
||||
{
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if (pulse & 1) { /* DFSE */
|
||||
if (rf_sta & RFS_ERR) AC = AC | IOT_SKP; }
|
||||
if (pulse & 2) { /* DFSC */
|
||||
if (pulse & 4) AC = AC & ~07777; /* for DMAC */
|
||||
else if (rf_done) AC = AC | IOT_SKP; }
|
||||
if (pulse & 4) AC = AC | (rf_da & 07777); /* DMAC */
|
||||
return AC;
|
||||
}
|
||||
|
||||
int32 rf64 (int32 pulse, int32 AC)
|
||||
{
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 1: /* DCXA */
|
||||
rf_da = rf_da & 07777; /* clear DAR<0:7> */
|
||||
return AC;
|
||||
case 3: /* DXAL */
|
||||
rf_da = (rf_da & 07777) | ((AC & 0377) << 12); /* DAR<0:7> <- AC */
|
||||
return 0; /* clear AC */
|
||||
case 5: /* DXAC */
|
||||
return ((rf_da >> 12) & 0377); /* AC <- DAR<0:7> */
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service
|
||||
|
||||
Note that for reads and writes, memory addresses wrap around in the
|
||||
current field. This code assumes the entire disk is buffered.
|
||||
*/
|
||||
|
||||
t_stat rf_svc (UNIT *uptr)
|
||||
{
|
||||
int32 pa, t, mex;
|
||||
|
||||
UPDATE_PCELL; /* update photocell */
|
||||
if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */
|
||||
rf_sta = rf_sta | RFS_NXD;
|
||||
rf_done = 1;
|
||||
RF_INT_UPDATE; /* update int req */
|
||||
return IORETURN (rf_stopioe, SCPE_UNATT); }
|
||||
|
||||
mex = GET_MEX (rf_sta);
|
||||
do { M[RF_WC] = (M[RF_WC] + 1) & 07777; /* incr word count */
|
||||
M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */
|
||||
pa = mex | M[RF_MA]; /* add extension */
|
||||
if (uptr -> FUNC == RF_READ) {
|
||||
if (MEM_ADDR_OK (pa)) /* read, check nxm */
|
||||
M[pa] = *(((int16 *) uptr -> filebuf) + rf_da); }
|
||||
else { t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07);
|
||||
if ((rf_wlk >> t) & 1) rf_sta = rf_sta | RFS_WLS;
|
||||
else { *(((int16 *) uptr -> filebuf) + rf_da) = M[pa];
|
||||
if (((t_addr) rf_da) >= uptr -> hwmark)
|
||||
uptr -> hwmark = rf_da + 1; } }
|
||||
rf_da = (rf_da + 1) & 03777777; } /* incr disk addr */
|
||||
while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */
|
||||
|
||||
if (M[RF_WC] != 0) /* more to do? */
|
||||
sim_activate (&rf_unit, rf_time); /* sched next */
|
||||
else { rf_done = 1; /* done */
|
||||
RF_INT_UPDATE; } /* update int req */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Photocell unit service */
|
||||
|
||||
t_stat pcell_svc (UNIT *uptr)
|
||||
{
|
||||
rf_sta = rf_sta | RFS_PCA; /* set photocell */
|
||||
if (rf_sta & RFS_PIE) { /* int enable? */
|
||||
sim_activate (&pcell_unit, RF_NUMWD * rf_time);
|
||||
int_req = int_req | INT_RF; }
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat rf_reset (DEVICE *dptr)
|
||||
{
|
||||
if (dev_enb & INT_RF) /* RF? no DF or RL */
|
||||
dev_enb = dev_enb & ~(INT_DF | INT_RL);
|
||||
rf_sta = rf_da = 0;
|
||||
rf_done = 1;
|
||||
int_req = int_req & ~INT_RF; /* clear interrupt */
|
||||
sim_cancel (&rf_unit);
|
||||
sim_cancel (&pcell_unit);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Bootstrap routine */
|
||||
|
||||
#define OS8_START 07750
|
||||
#define OS8_LEN (sizeof (os8_rom) / sizeof (int32))
|
||||
#define DM4_START 00200
|
||||
#define DM4_LEN (sizeof (dm4_rom) / sizeof (int32))
|
||||
|
||||
static const int32 os8_rom[] = {
|
||||
07600, /* 7750, CLA CLL ; also word count */
|
||||
06603, /* 7751, DMAR ; also address */
|
||||
06622, /* 7752, DFSC ; done? */
|
||||
05352, /* 7753, JMP .-1 ; no */
|
||||
05752 /* 7754, JMP @.-2 ; enter boot */
|
||||
};
|
||||
|
||||
static const int32 dm4_rom[] = {
|
||||
00200, 07600, /* 0200, CLA CLL */
|
||||
00201, 06603, /* 0201, DMAR ; read */
|
||||
00202, 06622, /* 0202, DFSC ; done? */
|
||||
00203, 05202, /* 0203, JMP .-1 ; no */
|
||||
00204, 05600, /* 0204, JMP @.-4 ; enter boot */
|
||||
07750, 07576, /* 7750, 7576 ; word count */
|
||||
07751, 07576 /* 7751, 7576 ; address */
|
||||
};
|
||||
|
||||
t_stat rf_boot (int32 unitno)
|
||||
{
|
||||
int32 i;
|
||||
extern int32 sim_switches, saved_PC;
|
||||
|
||||
if (sim_switches & SWMASK ('D')) {
|
||||
for (i = 0; i < DM4_LEN; i = i + 2)
|
||||
M[dm4_rom[i]] = dm4_rom[i + 1];
|
||||
saved_PC = DM4_START; }
|
||||
else { for (i = 0; i < OS8_LEN; i++)
|
||||
M[OS8_START + i] = os8_rom[i];
|
||||
saved_PC = OS8_START; }
|
||||
return SCPE_OK;
|
||||
}
|
||||
398
PDP8/pdp8_rk.c
Normal file
398
PDP8/pdp8_rk.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/* pdp8_rk.c: RK8E 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 RK8E/RK05 cartridge disk
|
||||
|
||||
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
|
||||
24-Nov-01 RMS Converted FLG to array, made register names consistent
|
||||
25-Apr-01 RMS Added device enable/disable support
|
||||
29-Jun-96 RMS Added unit enable/disable support
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
/* Constants */
|
||||
|
||||
#define RK_NUMSC 16 /* sectors/surface */
|
||||
#define RK_NUMSF 2 /* surfaces/cylinder */
|
||||
#define RK_NUMCY 203 /* cylinders/drive */
|
||||
#define RK_NUMWD 256 /* words/sector */
|
||||
#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) /* words/drive */
|
||||
#define RK_NUMDR 4 /* drives/controller */
|
||||
#define RK_M_NUMDR 03
|
||||
|
||||
/* Flags in the unit flags word */
|
||||
|
||||
#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */
|
||||
#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */
|
||||
#define UNIT_W_UF 3 /* user flags width */
|
||||
#define UNIT_HWLK (1 << UNIT_V_HWLK)
|
||||
#define UNIT_SWLK (1 << UNIT_V_SWLK)
|
||||
#define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write protect */
|
||||
|
||||
/* Parameters in the unit descriptor */
|
||||
|
||||
#define CYL u3 /* current cylinder */
|
||||
#define FUNC u4 /* function */
|
||||
|
||||
/* Status register */
|
||||
|
||||
#define RKS_DONE 04000 /* transfer done */
|
||||
#define RKS_HMOV 02000 /* heads moving */
|
||||
#define RKS_SKFL 00400 /* drive seek fail */
|
||||
#define RKS_NRDY 00200 /* drive not ready */
|
||||
#define RKS_BUSY 00100 /* control busy error */
|
||||
#define RKS_TMO 00040 /* timeout error */
|
||||
#define RKS_WLK 00020 /* write lock error */
|
||||
#define RKS_CRC 00010 /* CRC error */
|
||||
#define RKS_DLT 00004 /* data late error */
|
||||
#define RKS_STAT 00002 /* drive status error */
|
||||
#define RKS_CYL 00001 /* cyl address error */
|
||||
#define RKS_ERR (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL)
|
||||
|
||||
/* Command register */
|
||||
|
||||
#define RKC_M_FUNC 07 /* function */
|
||||
#define RKC_READ 0
|
||||
#define RKC_RALL 1
|
||||
#define RKC_WLK 2
|
||||
#define RKC_SEEK 3
|
||||
#define RKC_WRITE 4
|
||||
#define RKC_WALL 5
|
||||
#define RKC_V_FUNC 9
|
||||
#define RKC_IE 00400 /* interrupt enable */
|
||||
#define RKC_SKDN 00200 /* int on seek done */
|
||||
#define RKC_HALF 00100 /* 128W sector */
|
||||
#define RKC_MEX 00070 /* memory extension */
|
||||
#define RKC_V_MEX 3
|
||||
#define RKC_M_DRV 03 /* drive select */
|
||||
#define RKC_V_DRV 1
|
||||
#define RKC_CYHI 00001 /* high cylinder addr */
|
||||
|
||||
#define GET_FUNC(x) (((x) >> RKC_V_FUNC) & RKC_M_FUNC)
|
||||
#define GET_DRIVE(x) (((x) >> RKC_V_DRV) & RKC_M_DRV)
|
||||
#define GET_MEX(x) (((x) & RKC_MEX) << (12 - RKC_V_MEX))
|
||||
|
||||
/* Disk address */
|
||||
|
||||
#define RKD_V_SECT 0 /* sector */
|
||||
#define RKD_M_SECT 017
|
||||
#define RKD_V_SUR 4 /* surface */
|
||||
#define RKD_M_SUR 01
|
||||
#define RKD_V_CYL 5 /* cylinder */
|
||||
#define RKD_M_CYL 0177
|
||||
#define GET_CYL(x,y) ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \
|
||||
(((y) >> RKD_V_CYL) & RKD_M_CYL))
|
||||
#define GET_DA(x,y) ((((x) & RKC_CYHI) << 12) | y)
|
||||
|
||||
/* Reset commands */
|
||||
|
||||
#define RKX_CLS 0 /* clear status */
|
||||
#define RKX_CLC 1 /* clear control */
|
||||
#define RKX_CLD 2 /* clear drive */
|
||||
#define RKX_CLSA 3 /* clear status alt */
|
||||
|
||||
#define RK_INT_UPDATE \
|
||||
if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \
|
||||
((rk_cmd & RKC_IE) != 0)) int_req = int_req | INT_RK; \
|
||||
else int_req = int_req & ~INT_RK
|
||||
#define RK_MIN 10
|
||||
#define MAX(x,y) (((x) > (y))? (x): (y))
|
||||
|
||||
extern uint16 M[];
|
||||
extern int32 int_req, dev_enb, stop_inst;
|
||||
extern UNIT cpu_unit;
|
||||
int32 rk_busy = 0; /* controller busy */
|
||||
int32 rk_sta = 0; /* status register */
|
||||
int32 rk_cmd = 0; /* command register */
|
||||
int32 rk_da = 0; /* disk address */
|
||||
int32 rk_ma = 0; /* memory address */
|
||||
int32 rk_swait = 10, rk_rwait = 10; /* seek, rotate wait */
|
||||
int32 rk_stopioe = 1; /* stop on error */
|
||||
t_stat rk_svc (UNIT *uptr);
|
||||
t_stat rk_reset (DEVICE *dptr);
|
||||
t_stat rk_boot (int32 unitno);
|
||||
void rk_go (int32 function, int32 cylinder);
|
||||
|
||||
/* RK-8E data structures
|
||||
|
||||
rk_dev RK device descriptor
|
||||
rk_unit RK unit list
|
||||
rk_reg RK register list
|
||||
rk_mod RK modifiers list
|
||||
*/
|
||||
|
||||
UNIT rk_unit[] = {
|
||||
{ UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
|
||||
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 (RKSTA, rk_sta, 12) },
|
||||
{ ORDATA (RKCMD, rk_cmd, 12) },
|
||||
{ ORDATA (RKDA, rk_da, 12) },
|
||||
{ ORDATA (RKMA, rk_ma, 12) },
|
||||
{ FLDATA (BUSY, rk_busy, 0) },
|
||||
{ FLDATA (INT, int_req, INT_V_RK) },
|
||||
{ DRDATA (STIME, rk_swait, 24), PV_LEFT },
|
||||
{ DRDATA (RTIME, rk_rwait, 24), PV_LEFT },
|
||||
{ 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, dev_enb, INT_V_RK), 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, 12,
|
||||
NULL, NULL, &rk_reset,
|
||||
&rk_boot, NULL, NULL };
|
||||
|
||||
/* IOT routine */
|
||||
|
||||
int32 rk (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 i;
|
||||
UNIT *uptr;
|
||||
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 0: /* unused */
|
||||
return (stop_inst << IOT_V_REASON) + AC;
|
||||
case 1: /* DSKP */
|
||||
return (rk_sta & (RKS_DONE + RKS_ERR))? /* skip on done, err */
|
||||
IOT_SKP + AC: AC;
|
||||
case 2: /* DCLR */
|
||||
rk_sta = 0; /* clear status */
|
||||
switch (AC & 03) { /* decode AC<10:11> */
|
||||
case RKX_CLS: /* clear status */
|
||||
if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
|
||||
case RKX_CLSA: /* clear status alt */
|
||||
break;
|
||||
case RKX_CLC: /* clear control */
|
||||
rk_cmd = rk_busy = 0; /* clear registers */
|
||||
rk_ma = rk_da = 0;
|
||||
for (i = 0; i < RK_NUMDR; i++) sim_cancel (&rk_unit[i]);
|
||||
break;
|
||||
case RKX_CLD: /* reset drive */
|
||||
if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
|
||||
else rk_go (RKC_SEEK, 0); /* seek to 0 */
|
||||
break; } /* end switch AC */
|
||||
break;
|
||||
case 3: /* DLAG */
|
||||
if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
|
||||
else { rk_da = AC; /* load disk addr */
|
||||
rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da)); }
|
||||
break;
|
||||
case 4: /* DLCA */
|
||||
if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
|
||||
else rk_ma = AC; /* load curr addr */
|
||||
break;
|
||||
case 5: /* DRST */
|
||||
uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */
|
||||
rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY); /* clear dynamic */
|
||||
if ((uptr -> flags & UNIT_ATT) == 0) rk_sta = rk_sta | RKS_NRDY;
|
||||
if (sim_is_active (uptr)) rk_sta = rk_sta | RKS_HMOV;
|
||||
return rk_sta;
|
||||
case 6: /* DLDC */
|
||||
if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY;
|
||||
else { rk_cmd = AC; /* load command */
|
||||
rk_sta = 0; } /* clear status */
|
||||
break;
|
||||
case 7: /* DMAN */
|
||||
break; } /* end case pulse */
|
||||
RK_INT_UPDATE; /* update int req */
|
||||
return 0; /* clear AC */
|
||||
}
|
||||
|
||||
/* Initiate new function
|
||||
|
||||
Called with function, cylinder, to allow recalibrate as well as
|
||||
load and go to be processed by this routine.
|
||||
|
||||
Assumes that the controller is idle, and that updating of interrupt
|
||||
request will be done by the caller.
|
||||
*/
|
||||
|
||||
void rk_go (int32 func, int32 cyl)
|
||||
{
|
||||
int32 t;
|
||||
UNIT *uptr;
|
||||
|
||||
if (func == RKC_RALL) func = RKC_READ; /* all? use standard */
|
||||
if (func == RKC_WALL) func = RKC_WRITE;
|
||||
uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */
|
||||
if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */
|
||||
rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT;
|
||||
return; }
|
||||
if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) { /* busy or bad cyl? */
|
||||
rk_sta = rk_sta | RKS_DONE | RKS_STAT;
|
||||
return; }
|
||||
if ((func == RKC_WRITE) && (uptr -> flags & UNIT_WPRT)) {
|
||||
rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */
|
||||
return; }
|
||||
if (func == RKC_WLK) { /* write lock? */
|
||||
uptr -> flags = uptr -> flags | UNIT_SWLK;
|
||||
rk_sta = rk_sta | RKS_DONE;
|
||||
return; }
|
||||
t = abs (cyl - uptr -> CYL) * rk_swait; /* seek time */
|
||||
if (func == RKC_SEEK) { /* seek? */
|
||||
sim_activate (uptr, MAX (RK_MIN, t)); /* schedule */
|
||||
rk_sta = rk_sta | RKS_DONE; } /* set done */
|
||||
else { sim_activate (uptr, t + rk_rwait); /* schedule */
|
||||
rk_busy = 1; } /* set busy */
|
||||
uptr -> FUNC = func; /* save func */
|
||||
uptr -> CYL = cyl; /* put on cylinder */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unit service
|
||||
|
||||
If seek, complete seek command
|
||||
Else complete data transfer command
|
||||
|
||||
The unit control block contains the function and cylinder address for
|
||||
the current command.
|
||||
|
||||
Note that memory addresses wrap around in the current field.
|
||||
*/
|
||||
|
||||
static uint16 fill[RK_NUMWD/2] = { 0 };
|
||||
t_stat rk_svc (UNIT *uptr)
|
||||
{
|
||||
int32 err, wc, wc1, awc, swc, pa, da;
|
||||
UNIT *seluptr;
|
||||
|
||||
if (uptr -> FUNC == RKC_SEEK) { /* seek? */
|
||||
seluptr = rk_dev.units + GET_DRIVE (rk_cmd); /* see if selected */
|
||||
if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) {
|
||||
rk_sta = rk_sta | RKS_DONE;
|
||||
RK_INT_UPDATE; }
|
||||
return SCPE_OK; }
|
||||
|
||||
if ((uptr -> flags & UNIT_ATT) == 0) { /* not att? abort */
|
||||
rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT;
|
||||
rk_busy = 0;
|
||||
RK_INT_UPDATE;
|
||||
return IORETURN (rk_stopioe, SCPE_UNATT); }
|
||||
|
||||
if ((uptr -> FUNC == RKC_WRITE) && (uptr -> flags & UNIT_WPRT)) {
|
||||
rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */
|
||||
rk_busy = 0;
|
||||
RK_INT_UPDATE;
|
||||
return SCPE_OK; }
|
||||
|
||||
pa = GET_MEX (rk_cmd) | rk_ma; /* phys address */
|
||||
da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */
|
||||
swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */
|
||||
if ((wc1 = ((rk_ma + wc) - 010000)) > 0) wc = wc - wc1; /* if wrap, limit */
|
||||
err = fseek (uptr -> fileref, da, SEEK_SET); /* locate sector */
|
||||
|
||||
if ((uptr -> FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */
|
||||
awc = fxread (&M[pa], sizeof (int16), wc, uptr -> fileref);
|
||||
for ( ; awc < wc; awc++) M[pa + awc] = 0; /* fill if eof */
|
||||
err = ferror (uptr -> fileref);
|
||||
if ((wc1 > 0) && (err == 0)) { /* field wraparound? */
|
||||
pa = pa & 070000; /* wrap phys addr */
|
||||
awc = fxread (&M[pa], sizeof (int16), wc1, uptr -> fileref);
|
||||
for ( ; awc < wc1; awc++) M[pa + awc] = 0; /* fill if eof */
|
||||
err = ferror (uptr -> fileref); } }
|
||||
|
||||
if ((uptr -> FUNC == RKC_WRITE) && (err == 0)) { /* write? */
|
||||
fxwrite (&M[pa], sizeof (int16), wc, uptr -> fileref);
|
||||
err = ferror (uptr -> fileref);
|
||||
if ((wc1 > 0) && (err == 0)) { /* field wraparound? */
|
||||
pa = pa & 070000; /* wrap phys addr */
|
||||
fxwrite (&M[pa], sizeof (int16), wc1, uptr -> fileref);
|
||||
err = ferror (uptr -> fileref); }
|
||||
if ((rk_cmd & RKC_HALF) && (err == 0)) { /* fill half sector */
|
||||
fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr -> fileref);
|
||||
err = ferror (uptr -> fileref); } }
|
||||
|
||||
rk_ma = (rk_ma + swc) & 07777; /* incr mem addr reg */
|
||||
rk_sta = rk_sta | RKS_DONE; /* set done */
|
||||
rk_busy = 0;
|
||||
RK_INT_UPDATE;
|
||||
|
||||
if (err != 0) {
|
||||
perror ("RK I/O error");
|
||||
clearerr (uptr -> fileref);
|
||||
return SCPE_IOERR; }
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat rk_reset (DEVICE *dptr)
|
||||
{
|
||||
int32 i;
|
||||
UNIT *uptr;
|
||||
|
||||
rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0;
|
||||
int_req = int_req & ~INT_RK; /* clear interrupt */
|
||||
for (i = 0; i < RK_NUMDR; i++) { /* stop all units */
|
||||
uptr = rk_dev.units + i;
|
||||
sim_cancel (uptr);
|
||||
uptr -> flags = uptr -> flags & ~UNIT_SWLK;
|
||||
uptr -> CYL = uptr -> FUNC = 0; }
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Bootstrap routine */
|
||||
|
||||
#define BOOT_START 023
|
||||
#define BOOT_UNIT 032
|
||||
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
|
||||
|
||||
static const int32 boot_rom[] = {
|
||||
06007, /* 23, CAF */
|
||||
06744, /* 24, DLCA ; addr = 0 */
|
||||
01032, /* 25, TAD UNIT ; unit no */
|
||||
06746, /* 26, DLDC ; command, unit */
|
||||
06743, /* 27, DLAG ; disk addr, go */
|
||||
01032, /* 30, TAD UNIT ; unit no, for OS */
|
||||
05031, /* 31, JMP . */
|
||||
00000 /* UNIT, 0 ; in bits <9:10> */
|
||||
};
|
||||
|
||||
t_stat rk_boot (int32 unitno)
|
||||
{
|
||||
int32 i;
|
||||
extern int32 saved_PC;
|
||||
|
||||
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
|
||||
M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1;
|
||||
saved_PC = BOOT_START;
|
||||
return SCPE_OK;
|
||||
}
|
||||
619
PDP8/pdp8_rl.c
Normal file
619
PDP8/pdp8_rl.c
Normal file
@@ -0,0 +1,619 @@
|
||||
/* pdp8_rl.c: RL8A 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 RL8A cartridge disk
|
||||
|
||||
30-Nov-01 RMS Cloned from RL11
|
||||
|
||||
The RL8A 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 RL8A controller has several serious complications.
|
||||
- Seeking is relative to the current disk address; this requires
|
||||
keeping accurate track of the current cylinder.
|
||||
- The RL8A will not switch heads or cross cylinders during transfers.
|
||||
- The RL8A operates in 8b and 12b mode, like the RX8E; in 12b mode, it
|
||||
packs 2 12b words into 3 bytes, creating a 170 "word" sector with
|
||||
one wasted byte. Multi-sector transfers in 12b mode don't work.
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
/* Constants */
|
||||
|
||||
#define RL_NUMBY 256 /* 8b bytes/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 << 12) /* max transfer */
|
||||
#define RL01_SIZE (RL_NUMCY*RL_NUMSF*RL_NUMSC*RL_NUMBY) /* words/drive */
|
||||
#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */
|
||||
#define RL_BBMAP 014 /* sector for bblk map */
|
||||
#define RL_BBID 0123 /* ID for bblk map */
|
||||
|
||||
/* Flags in the unit flags word */
|
||||
|
||||
#define UNIT_V_WLK (UNIT_V_UF) /* 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 (1u << 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 protect */
|
||||
|
||||
/* Parameters in the unit descriptor */
|
||||
|
||||
#define TRK u3 /* current cylinder */
|
||||
#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 */
|
||||
|
||||
/* RLCSA, seek = offset/rw = address (also uptr -> TRK) */
|
||||
|
||||
#define RLCSA_DIR 04000 /* direction */
|
||||
#define RLCSA_HD 02000 /* head select */
|
||||
#define RLCSA_CYL 00777 /* cyl offset */
|
||||
#define GET_CYL(x) ((x) & RLCSA_CYL)
|
||||
#define GET_TRK(x) ((((x) & RLCSA_CYL) * RL_NUMSF) + \
|
||||
(((x) & RLCSA_HD)? 1: 0))
|
||||
#define GET_DA(x) ((GET_TRK(x) * RL_NUMSC) + rlsa)
|
||||
|
||||
/* RLCSB, function/unit select */
|
||||
|
||||
#define RLCSB_V_FUNC 0 /* function */
|
||||
#define RLCSB_M_FUNC 07
|
||||
#define RLCSB_MNT 0
|
||||
#define RLCSB_CLRD 1
|
||||
#define RLCSB_GSTA 2
|
||||
#define RLCSB_SEEK 3
|
||||
#define RLCSB_RHDR 4
|
||||
#define RLCSB_WRITE 5
|
||||
#define RLCSB_READ 6
|
||||
#define RLCSB_RNOHDR 7
|
||||
#define RLCSB_V_MEX 3 /* memory extension */
|
||||
#define RLCSB_M_MEX 07
|
||||
#define RLCSB_V_DRIVE 6 /* drive */
|
||||
#define RLCSB_M_DRIVE 03
|
||||
#define RLCSB_V_IE 8 /* int enable */
|
||||
#define RLCSB_IE (1u << RLCSB_V_IE)
|
||||
#define RLCSB_8B 01000 /* 12b/8b */
|
||||
#define RCLS_MNT 02000 /* maint NI */
|
||||
#define RLCSB_RW 0001777 /* read/write */
|
||||
#define GET_FUNC(x) (((x) >> RLCSB_V_FUNC) & RLCSB_M_FUNC)
|
||||
#define GET_MEX(x) (((x) >> RLCSB_V_MEX) & RLCSB_M_MEX)
|
||||
#define GET_DRIVE(x) (((x) >> RLCSB_V_DRIVE) & RLCSB_M_DRIVE)
|
||||
|
||||
/* RLSA, disk sector */
|
||||
|
||||
#define RLSA_V_SECT 6 /* sector */
|
||||
#define RLSA_M_SECT 077
|
||||
#define GET_SECT(x) (((x) >> RLSA_V_SECT) & RLSA_M_SECT)
|
||||
|
||||
/* RLER, error register */
|
||||
|
||||
#define RLER_DRDY 00001 /* drive ready */
|
||||
#define RLER_DRE 00002 /* drive error */
|
||||
#define RLER_HDE 01000 /* header error */
|
||||
#define RLER_INCMP 02000 /* incomplete */
|
||||
#define RLER_ICRC 04000 /* CRC error */
|
||||
#define RLER_MASK 07003
|
||||
|
||||
/* RLSI, silo register, used only in read header */
|
||||
|
||||
#define RLSI_V_TRK 6 /* track */
|
||||
|
||||
extern uint16 M[];
|
||||
extern int32 int_req, dev_enb;
|
||||
extern UNIT cpu_unit;
|
||||
uint8 *rlxb = NULL; /* xfer buffer */
|
||||
int32 rlcsa = 0; /* control/status A */
|
||||
int32 rlcsb = 0; /* control/status B */
|
||||
int32 rlma = 0; /* memory address */
|
||||
int32 rlwc = 0; /* word count */
|
||||
int32 rlsa = 0; /* sector address */
|
||||
int32 rler = 0; /* error register */
|
||||
int32 rlsi = 0, rlsi1 = 0, rlsi2 = 0; /* silo queue */
|
||||
int32 rl_lft = 0; /* silo left/right */
|
||||
int32 rl_done = 0; /* done flag */
|
||||
int32 rl_erf = 0; /* error flag */
|
||||
int32 rl_swait = 10; /* seek wait */
|
||||
int32 rl_rwait = 10; /* rotate wait */
|
||||
int32 rl_stopioe = 1; /* stop on error */
|
||||
t_stat rl_svc (UNIT *uptr);
|
||||
t_stat rl_reset (DEVICE *dptr);
|
||||
void rl_set_done (int32 error);
|
||||
t_stat rl_boot (int32 unitno);
|
||||
t_stat rl_attach (UNIT *uptr, char *cptr);
|
||||
t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||
t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||
|
||||
/* RL8A data structures
|
||||
|
||||
rl_dev RL device descriptor
|
||||
rl_unit RL unit list
|
||||
rl_reg RL register list
|
||||
rl_mod RL modifier list
|
||||
*/
|
||||
|
||||
UNIT rl_unit[] = {
|
||||
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
|
||||
UNIT_ROABLE, RL01_SIZE) },
|
||||
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
|
||||
UNIT_ROABLE, RL01_SIZE) },
|
||||
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
|
||||
UNIT_ROABLE, RL01_SIZE) },
|
||||
{ UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
|
||||
UNIT_ROABLE, RL01_SIZE) } };
|
||||
|
||||
REG rl_reg[] = {
|
||||
{ ORDATA (RLCSA, rlcsa, 12) },
|
||||
{ ORDATA (RLCSB, rlcsb, 12) },
|
||||
{ ORDATA (RLMA, rlma, 12) },
|
||||
{ ORDATA (RLWC, rlwc, 12) },
|
||||
{ ORDATA (RLSA, rlsa, 6) },
|
||||
{ ORDATA (RLER, rler, 12) },
|
||||
{ ORDATA (RLSI, rlsi, 16) },
|
||||
{ ORDATA (RLSI1, rlsi1, 16) },
|
||||
{ ORDATA (RLSI2, rlsi2, 16) },
|
||||
{ FLDATA (RLSIL, rl_lft, 0) },
|
||||
{ FLDATA (INT, int_req, INT_V_RL) },
|
||||
{ FLDATA (DONE, rl_done, INT_V_RL) },
|
||||
{ FLDATA (IE, rlcsb, RLCSB_V_IE) },
|
||||
{ FLDATA (ERR, rl_erf, 0) },
|
||||
{ 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, dev_enb, INT_V_RL), 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, 8, 24, 1, 8, 8,
|
||||
NULL, NULL, &rl_reset,
|
||||
&rl_boot, &rl_attach, NULL };
|
||||
|
||||
/* IOT 60 routine */
|
||||
|
||||
int32 rl60 (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 curr, offs, newc, maxc;
|
||||
UNIT *uptr;
|
||||
|
||||
switch (pulse) { /* case IR<9:11> */
|
||||
case 0: /* RLDC */
|
||||
rl_reset (&rl_dev); /* reset device */
|
||||
break;
|
||||
case 1: /* RLSD */
|
||||
if (rl_done) AC = IOT_SKP; /* skip if done */
|
||||
else AC = 0;
|
||||
rl_done = 0; /* clear done */
|
||||
int_req = int_req & ~INT_RL; /* clear intr */
|
||||
return AC;
|
||||
case 2: /* RLMA */
|
||||
rlma = AC;
|
||||
break;
|
||||
case 3: /* RLCA */
|
||||
rlcsa = AC;
|
||||
break;
|
||||
case 4: /* RLCB */
|
||||
rlcsb = AC;
|
||||
rl_done = 0; /* clear done */
|
||||
rler = rl_erf = 0; /* clear errors */
|
||||
int_req = int_req & ~INT_RL; /* clear intr */
|
||||
rl_lft = 0; /* clear silo ptr */
|
||||
uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */
|
||||
switch (GET_FUNC (rlcsb)) { /* case on func */
|
||||
case RLCSB_CLRD: /* clear drive */
|
||||
uptr -> STAT = uptr -> STAT & ~RLDS_ERR; /* clear errors */
|
||||
case RLCSB_MNT: /* mnt */
|
||||
rl_set_done (0);
|
||||
break;
|
||||
case RLCSB_SEEK: /* seek */
|
||||
curr = GET_CYL (uptr -> TRK); /* current cylinder */
|
||||
offs = GET_CYL (rlcsa); /* offset */
|
||||
if (rlcsa & RLCSA_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 | (rlcsa & RLCSA_HD);
|
||||
sim_activate (uptr, rl_swait * abs (newc - curr));
|
||||
break;
|
||||
default: /* data transfer */
|
||||
sim_activate (uptr, rl_swait); /* activate unit */
|
||||
break; } /* end switch func */
|
||||
break;
|
||||
case 5: /* RLSA */
|
||||
rlsa = GET_SECT (AC);
|
||||
break;
|
||||
case 6: /* spare */
|
||||
return 0;
|
||||
case 7: /* RLWC */
|
||||
rlwc = AC;
|
||||
break; } /* end switch pulse */
|
||||
return 0; /* clear AC */
|
||||
}
|
||||
|
||||
/* IOT 61 routine */
|
||||
|
||||
int32 rl61 (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 dat;
|
||||
UNIT *uptr;
|
||||
|
||||
switch (pulse) { /* case IR<9:11> */
|
||||
case 0: /* RRER */
|
||||
uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */
|
||||
if (!sim_is_active (uptr) && /* update drdy */
|
||||
(uptr -> flags & UNIT_ATT))
|
||||
rler = rler | RLER_DRDY;
|
||||
else rler = rler & ~RLER_DRDY;
|
||||
dat = rler & RLER_MASK;
|
||||
break;
|
||||
case 1: /* RRWC */
|
||||
dat = rlwc;
|
||||
break;
|
||||
case 2: /* RRCA */
|
||||
dat = rlcsa;
|
||||
break;
|
||||
case 3: /* RRCB */
|
||||
dat = rlcsb;
|
||||
break;
|
||||
case 4: /* RRSA */
|
||||
dat = (rlsa << RLSA_V_SECT) & 07777;
|
||||
break;
|
||||
case 5: /* RRSI */
|
||||
if (rl_lft) { /* silo left? */
|
||||
dat = (rlsi >> 8) & 0377; /* get left 8b */
|
||||
rlsi = rlsi1; /* ripple */
|
||||
rlsi1 = rlsi2; }
|
||||
else dat = rlsi & 0377; /* get right 8b */
|
||||
rl_lft = rl_lft ^ 1; /* change side */
|
||||
break;
|
||||
case 6: /* spare */
|
||||
return AC;
|
||||
case 7: /* RLSE */
|
||||
if (rl_erf) dat = IOT_SKP | AC; /* skip if err */
|
||||
else dat = AC;
|
||||
rl_erf = 0;
|
||||
break; } /* end switch pulse */
|
||||
return dat;
|
||||
}
|
||||
|
||||
/* 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, maxc;
|
||||
int32 i, j, func, da, bc, wbc;
|
||||
t_addr ma;
|
||||
|
||||
func = GET_FUNC (rlcsb); /* get function */
|
||||
if (func == RLCSB_GSTA) { /* get status? */
|
||||
rlsi = uptr -> STAT |
|
||||
((uptr -> TRK & RLCSA_HD)? RLDS_HD: 0) |
|
||||
((uptr -> flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT);
|
||||
if (uptr -> flags & UNIT_RL02) rlsi = rlsi | RLDS_RL02;
|
||||
if (uptr -> flags & UNIT_WPRT) rlsi = rlsi | RLDS_WLK;
|
||||
rlsi2 = rlsi1 = rlsi;
|
||||
rl_set_done (0); /* done */
|
||||
return SCPE_OK; }
|
||||
|
||||
if ((uptr -> flags & UNIT_ATT) == 0) { /* attached? */
|
||||
uptr -> STAT = uptr -> STAT | RLDS_SPE; /* spin error */
|
||||
rl_set_done (RLER_INCMP); /* flag error */
|
||||
return IORETURN (rl_stopioe, SCPE_UNATT); }
|
||||
|
||||
if ((func == RLCSB_WRITE) && (uptr -> flags & UNIT_WPRT)) {
|
||||
uptr -> STAT = uptr -> STAT | RLDS_WGE; /* write and locked */
|
||||
rl_set_done (RLER_DRE); /* flag error */
|
||||
return SCPE_OK; }
|
||||
|
||||
if (func == RLCSB_SEEK) { /* seek? */
|
||||
rl_set_done (0); /* done */
|
||||
return SCPE_OK; }
|
||||
|
||||
if (func == RLCSB_RHDR) { /* read header? */
|
||||
rlsi = (GET_TRK (uptr -> TRK) << RLSI_V_TRK) | rlsa;
|
||||
rlsi1 = rlsi2 = 0;
|
||||
rl_set_done (0); /* done */
|
||||
return SCPE_OK; }
|
||||
|
||||
if (((func != RLCSB_RNOHDR) && (GET_CYL (uptr -> TRK) != GET_CYL (rlcsa)))
|
||||
|| (rlsa >= RL_NUMSC)) { /* bad cyl or sector? */
|
||||
rl_set_done (RLER_HDE | RLER_INCMP); /* flag error */
|
||||
return SCPE_OK; }
|
||||
|
||||
ma = (GET_MEX (rlcsb) << 12) | rlma; /* get mem addr */
|
||||
da = GET_DA (rlcsa) * RL_NUMBY; /* get disk addr */
|
||||
wc = 010000 - rlwc; /* get true wc */
|
||||
if (rlcsb & RLCSB_8B) { /* 8b mode? */
|
||||
bc = wc; /* bytes to xfr */
|
||||
maxc = (RL_NUMSC - rlsa) * RL_NUMBY; /* max transfer */
|
||||
if (bc > maxc) wc = bc = maxc; } /* trk ovrun? limit */
|
||||
else { bc = ((wc * 3) + 1) / 2; /* 12b mode */
|
||||
if (bc > RL_NUMBY) { /* > 1 sector */
|
||||
bc = RL_NUMBY; /* cap xfer */
|
||||
wc = (RL_NUMBY * 2) / 3; } }
|
||||
|
||||
err = fseek (uptr -> fileref, da, SEEK_SET);
|
||||
|
||||
if ((func >= RLCSB_READ) && (err == 0) && /* read (no hdr)? */
|
||||
MEM_ADDR_OK (ma)) { /* valid bank? */
|
||||
i = fxread (rlxb, sizeof (int8), bc, uptr -> fileref);
|
||||
err = ferror (uptr -> fileref);
|
||||
for ( ; i < bc; i++) rlxb[i] = 0; /* fill buffer */
|
||||
for (i = j = 0; i < wc; i++) { /* store buffer */
|
||||
if (rlcsb & RLCSB_8B) /* 8b mode? */
|
||||
M[ma] = rlxb[i] & 0377; /* store */
|
||||
else if (i & 1) { /* odd wd 12b? */
|
||||
M[ma] = ((rlxb[j + 1] >> 4) & 017) |
|
||||
(((uint16) rlxb[j + 2]) << 4);
|
||||
j = j + 3; }
|
||||
else M[ma] = rlxb[j] | /* even wd 12b */
|
||||
((((uint16) rlxb[j + 1]) & 017) << 8);
|
||||
ma = (ma & 070000) + ((ma + 1) & 07777);
|
||||
} /* end for */
|
||||
} /* end if wr */
|
||||
|
||||
if ((func == RLCSB_WRITE) && (err == 0)) { /* write? */
|
||||
for (i = j = 0; i < wc; i++) { /* fetch buffer */
|
||||
if (rlcsb & RLCSB_8B) /* 8b mode? */
|
||||
rlxb[i] = M[ma] & 0377; /* fetch */
|
||||
else if (i & 1) { /* odd wd 12b? */
|
||||
rlxb[j + 1] = rlxb[j + 1] | ((M[ma] & 017) << 4);
|
||||
rlxb[j + 2] = ((M[ma] >> 4) & 0377);
|
||||
j = j + 3; }
|
||||
else { /* even wd 12b */
|
||||
rlxb[j] = M[ma] & 0377;
|
||||
rlxb[j + 1] = (M[ma] >> 8) & 017; }
|
||||
ma = (ma & 070000) + ((ma + 1) & 07777);
|
||||
} /* end for */
|
||||
wbc = (bc + (RL_NUMBY - 1)) & ~(RL_NUMBY - 1); /* clr to */
|
||||
for (i = bc; i < wbc; i++) rlxb[i] = 0; /* end of blk */
|
||||
fxwrite (rlxb, sizeof (int8), wbc, uptr -> fileref);
|
||||
err = ferror (uptr -> fileref);
|
||||
} /* end write */
|
||||
|
||||
rlwc = (rlwc + wc) & 07777; /* final word count */
|
||||
if (rlwc != 0) rler = rler | RLER_INCMP; /* completed? */
|
||||
rlma = (rlma + wc) & 07777; /* final word addr */
|
||||
rlsa = rlsa + ((bc + (RL_NUMBY - 1)) / RL_NUMBY);
|
||||
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)
|
||||
{
|
||||
rl_done = 1;
|
||||
rler = rler | status;
|
||||
if (rler) rl_erf = 1;
|
||||
if (rlcsb & RLCSB_IE) int_req = int_req | INT_RL;
|
||||
else int_req = int_req & ~INT_RL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Device reset
|
||||
|
||||
Note that the RL8A does NOT recalibrate its drives on RESET
|
||||
*/
|
||||
|
||||
t_stat rl_reset (DEVICE *dptr)
|
||||
{
|
||||
int32 i;
|
||||
UNIT *uptr;
|
||||
|
||||
if (dev_enb & INT_RL) /* RL? no DF or RF */
|
||||
dev_enb = dev_enb & ~(INT_DF | INT_RF);
|
||||
rlcsa = rlcsb = rlsa = rler = 0;
|
||||
rlma = rlwc = 0;
|
||||
rlsi = rlsi1 = rlsi2 = 0;
|
||||
rl_lft = 0;
|
||||
rl_done = 0;
|
||||
rl_erf = 0;
|
||||
int_req = int_req & ~INT_RL;
|
||||
for (i = 0; i < RL_NUMDR; i++) {
|
||||
uptr = rl_dev.units + i;
|
||||
sim_cancel (uptr);
|
||||
uptr -> STAT = 0; }
|
||||
if (rlxb == NULL) rlxb = calloc (RL_MAXFR, sizeof (unsigned int8));
|
||||
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; /* cyl 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 rl_set_bad (uptr, 0, NULL, NULL); }
|
||||
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;
|
||||
}
|
||||
|
||||
/* Factory bad block table creation routine
|
||||
|
||||
This routine writes the OS/8 specific bad block map in track 0, sector 014 (RL_BBMAP):
|
||||
|
||||
words 0 magic number = 0123 (RL_BBID)
|
||||
words 1-n block numbers
|
||||
:
|
||||
words n+1 end of table = 0
|
||||
|
||||
Inputs:
|
||||
uptr = pointer to unit
|
||||
val = ignored
|
||||
Outputs:
|
||||
sta = status code
|
||||
*/
|
||||
|
||||
t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||||
{
|
||||
int32 i, da = RL_BBMAP * RL_NUMBY;
|
||||
|
||||
if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_UNATT;
|
||||
if (uptr -> flags & UNIT_RO) return SCPE_RO;
|
||||
if (!get_yn ("Create bad block table? [N]", FALSE)) return SCPE_OK;
|
||||
if (fseek (uptr -> fileref, da, SEEK_SET)) return SCPE_IOERR;
|
||||
rlxb[0] = RL_BBID;
|
||||
for (i = 1; i < RL_NUMBY; i++) rlxb[i] = 0;
|
||||
fxwrite (rlxb, sizeof (int8), RL_NUMBY, uptr -> fileref);
|
||||
if (ferror (uptr -> fileref)) return SCPE_IOERR;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Bootstrap */
|
||||
|
||||
#define BOOT_START 1 /* start */
|
||||
#define BOOT_UNIT 02006 /* unit number */
|
||||
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
|
||||
|
||||
static const int16 boot_rom[] = {
|
||||
06600, /* BT, RLDC ; reset */
|
||||
07201, /* 02, CLA IAC ; clr drv = 1 */
|
||||
04027, /* 03, JMS GO ; do io */
|
||||
01004, /* 04, TAD 4 ; rd hdr fnc */
|
||||
04027, /* 05, JMS GO ; do io */
|
||||
06615, /* 06, RRSI ; rd hdr lo */
|
||||
07002, /* 07, BSW ; swap */
|
||||
07012, /* 10, RTR ; lo cyl to L */
|
||||
06615, /* 11, RRSI ; rd hdr hi */
|
||||
00025, /* 12, AND 25 ; mask = 377 */
|
||||
07004, /* 13, RTL ; get cyl */
|
||||
06603, /* 14, RLCA ; set addr */
|
||||
07325, /* 15, CLA STL IAC RAL ; seek = 3 */
|
||||
04027, /* 16, JMS GO ; do io */
|
||||
07332, /* 17, CLA STL RTR ; dir in = 2000 */
|
||||
06605, /* 20, RLSA ; sector */
|
||||
01026, /* 21, TAD (-200) ; one sector */
|
||||
06607, /* 22, RLWC ; word cnt */
|
||||
07327, /* 23, CLA STL IAC RTL ; read = 6*/
|
||||
04027, /* 24, JMS GO ; do io */
|
||||
00377, /* 25, JMP 377 ; start */
|
||||
07600, /* 26, -200 ; word cnt */
|
||||
00000, /* GO, 0 ; subr */
|
||||
06604, /* 30, RLCB ; load fnc */
|
||||
06601, /* 31, RLSD ; wait */
|
||||
05031, /* 32, JMP .-1 ; */
|
||||
06617, /* 33, RLSE ; error? */
|
||||
05427, /* 34, JMP I GO ; no, ok */
|
||||
05001 /* 35, JMP BT ; restart */
|
||||
};
|
||||
|
||||
|
||||
t_stat rl_boot (int32 unitno)
|
||||
{
|
||||
int32 i;
|
||||
extern int32 saved_PC;
|
||||
|
||||
if (unitno) return SCPE_ARG;
|
||||
rl_unit[unitno].TRK = 0;
|
||||
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
|
||||
saved_PC = BOOT_START;
|
||||
return SCPE_OK;
|
||||
}
|
||||
443
PDP8/pdp8_rx.c
Normal file
443
PDP8/pdp8_rx.c
Normal file
@@ -0,0 +1,443 @@
|
||||
/* pdp8_rx.c: RX8E/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 RX8E/RX01 floppy disk
|
||||
|
||||
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
|
||||
24-Nov-01 RMS Converted FLG to array
|
||||
17-Jul-01 RMS Fixed warning from VC++ 6
|
||||
26-Apr-01 RMS Added device enable/disable support
|
||||
13-Apr-01 RMS Revised for register arrays
|
||||
14-Apr-99 RMS Changed t_addr to unsigned
|
||||
15-Aug-96 RMS Fixed bug in LCD
|
||||
|
||||
An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B.
|
||||
Tracks are numbered 0-76, sectors 1-26. The RX8E can store data in
|
||||
8b mode or 12b mode. In 8b mode, the controller reads or writes
|
||||
128 bytes per sector. In 12b mode, the reads or writes 64 12b words
|
||||
per sector. The 12b words are bit packed into the first 96 bytes
|
||||
of the sector; the last 32 bytes are zeroed on writes.
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
|
||||
#define RX_NUMTR 77 /* tracks/disk */
|
||||
#define RX_M_TRACK 0377
|
||||
#define RX_NUMSC 26 /* sectors/track */
|
||||
#define RX_M_SECTOR 0177 /* cf Jones!! */
|
||||
#define RX_NUMBY 128 /* bytes/sector */
|
||||
#define RX_NUMWD (RX_NUMBY / 2) /* words/sector */
|
||||
#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */
|
||||
#define RX_NUMDR 2 /* drives/controller */
|
||||
#define RX_M_NUMDR 01
|
||||
#define UNIT_V_WLK (UNIT_V_UF) /* write locked */
|
||||
#define UNIT_WLK (1 << UNIT_V_UF)
|
||||
#define 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_DRV 0020 /* drive */
|
||||
#define RXCS_MODE 0100 /* mode */
|
||||
#define RXCS_MAINT 0200 /* maintenance */
|
||||
|
||||
#define RXES_CRC 0001 /* CRC error */
|
||||
#define RXES_PAR 0002 /* parity error */
|
||||
#define RXES_ID 0004 /* init done */
|
||||
#define RXES_WLK 0010 /* write protect */
|
||||
#define RXES_DD 0100 /* deleted data */
|
||||
#define RXES_DRDY 0200 /* drive ready */
|
||||
|
||||
#define TRACK u3 /* current track */
|
||||
#define READ_RXDBR ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr)
|
||||
#define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY
|
||||
|
||||
extern int32 int_req, int_enable, dev_done, dev_enb;
|
||||
int32 rx_tr = 0; /* xfer ready flag */
|
||||
int32 rx_err = 0; /* error flag */
|
||||
int32 rx_csr = 0; /* control/status */
|
||||
int32 rx_dbr = 0; /* data buffer */
|
||||
int32 rx_esr = 0; /* error status */
|
||||
int32 rx_ecode = 0; /* error code */
|
||||
int32 rx_track = 0; /* desired track */
|
||||
int32 rx_sector = 0; /* desired sector */
|
||||
int32 rx_state = IDLE; /* controller state */
|
||||
int32 rx_cwait = 100; /* command time */
|
||||
int32 rx_swait = 10; /* seek, per track */
|
||||
int32 rx_xwait = 1; /* tr set time */
|
||||
int32 rx_stopioe = 1; /* stop on error */
|
||||
uint8 rx_buf[RX_NUMBY] = { 0 }; /* sector buffer */
|
||||
int32 bufptr = 0; /* buffer pointer */
|
||||
t_stat rx_svc (UNIT *uptr);
|
||||
t_stat rx_reset (DEVICE *dptr);
|
||||
t_stat rx_boot (int32 unitno);
|
||||
|
||||
/* RX8E data structures
|
||||
|
||||
rx_dev RX device descriptor
|
||||
rx_unit RX unit list
|
||||
rx_reg RX register list
|
||||
rx_mod RX modifier list
|
||||
*/
|
||||
|
||||
UNIT rx_unit[] = {
|
||||
{ UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+
|
||||
UNIT_ROABLE, RX_SIZE) },
|
||||
{ UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+
|
||||
UNIT_ROABLE, RX_SIZE) } };
|
||||
|
||||
REG rx_reg[] = {
|
||||
{ ORDATA (RXCS, rx_csr, 12) },
|
||||
{ ORDATA (RXDB, rx_dbr, 12) },
|
||||
{ ORDATA (RXES, rx_esr, 8) },
|
||||
{ ORDATA (RXERR, rx_ecode, 8) },
|
||||
{ ORDATA (RXTA, rx_track, 8) },
|
||||
{ ORDATA (RXSA, rx_sector, 8) },
|
||||
{ ORDATA (STAPTR, rx_state, 3), REG_RO },
|
||||
{ ORDATA (BUFPTR, bufptr, 7) },
|
||||
{ FLDATA (TR, rx_tr, 0) },
|
||||
{ FLDATA (ERR, rx_err, 0) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_RX) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_RX) },
|
||||
{ FLDATA (INT, int_req, INT_V_RX) },
|
||||
{ DRDATA (CTIME, rx_cwait, 24), PV_LEFT },
|
||||
{ DRDATA (STIME, rx_swait, 24), PV_LEFT },
|
||||
{ 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, dev_enb, INT_V_RX), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
MTAB rx_mod[] = {
|
||||
{ UNIT_WLK, 0, "write enabled", "ENABLED", NULL },
|
||||
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
|
||||
{ 0 } };
|
||||
|
||||
DEVICE rx_dev = {
|
||||
"RX", rx_unit, rx_reg, rx_mod,
|
||||
RX_NUMDR, 8, 20, 1, 8, 8,
|
||||
NULL, NULL, &rx_reset,
|
||||
&rx_boot, NULL, NULL };
|
||||
|
||||
/* IOT routine */
|
||||
|
||||
int32 rx (int32 pulse, int32 AC)
|
||||
{
|
||||
int32 drv;
|
||||
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 0: /* unused */
|
||||
break;
|
||||
case 1: /* LCD */
|
||||
if (rx_state != IDLE) return AC; /* ignore if busy */
|
||||
rx_dbr = rx_csr = AC; /* save new command */
|
||||
dev_done = dev_done & ~INT_RX; /* clear done, int */
|
||||
int_req = int_req & ~INT_RX;
|
||||
rx_tr = rx_err = 0; /* clear flags */
|
||||
bufptr = 0; /* clear buf pointer */
|
||||
switch ((AC >> RXCS_V_FUNC) & RXCS_M_FUNC) { /* decode command */
|
||||
case RXCS_FILL:
|
||||
rx_state = FILL; /* state = fill */
|
||||
rx_tr = 1; /* xfer is ready */
|
||||
break;
|
||||
case RXCS_EMPTY:
|
||||
rx_state = EMPTY; /* state = empty */
|
||||
sim_activate (&rx_unit[0], rx_xwait); /* sched xfer */
|
||||
break;
|
||||
case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
|
||||
rx_state = RWDS; /* state = get sector */
|
||||
rx_tr = 1; /* xfer is ready */
|
||||
rx_esr = rx_esr & RXES_ID; /* clear errors */
|
||||
break;
|
||||
default:
|
||||
rx_state = CMD_COMPLETE; /* state = cmd compl */
|
||||
drv = (rx_csr & RXCS_DRV) > 0; /* get drive number */
|
||||
sim_activate (&rx_unit[drv], rx_cwait); /* sched done */
|
||||
break; } /* end switch func */
|
||||
return 0; /* clear AC */
|
||||
case 2: /* XDR */
|
||||
switch (rx_state & 07) { /* case on state */
|
||||
default: /* default */
|
||||
return READ_RXDBR; /* return data reg */
|
||||
case EMPTY: /* emptying buffer */
|
||||
sim_activate (&rx_unit[0], rx_xwait); /* sched xfer */
|
||||
return READ_RXDBR; /* return data reg */
|
||||
case RWDS: /* sector */
|
||||
rx_sector = AC & RX_M_SECTOR; /* save sector */
|
||||
case FILL: /* fill */
|
||||
rx_dbr = AC; /* save data */
|
||||
sim_activate (&rx_unit[0], rx_xwait); /* sched xfer */
|
||||
break;
|
||||
case RWDT: /* track */
|
||||
rx_track = AC & RX_M_TRACK; /* save track */
|
||||
rx_dbr = AC; /* save data */
|
||||
drv = (rx_csr & RXCS_DRV) > 0; /* get drive number */
|
||||
sim_activate (&rx_unit[drv], /* sched done */
|
||||
rx_swait * abs (rx_track - rx_unit[drv].TRACK));
|
||||
break; } /* end switch state */
|
||||
break;
|
||||
case 3: /* STR */
|
||||
if (rx_tr != 0) {
|
||||
rx_tr = 0;
|
||||
return IOT_SKP + AC; }
|
||||
break;
|
||||
case 4: /* SER */
|
||||
if (rx_err != 0) {
|
||||
rx_err = 0;
|
||||
return IOT_SKP + AC; }
|
||||
break;
|
||||
case 5: /* SDN */
|
||||
if ((dev_done & INT_RX) != 0) {
|
||||
dev_done = dev_done & ~INT_RX;
|
||||
int_req = int_req & ~INT_RX;
|
||||
return IOT_SKP + AC; }
|
||||
break;
|
||||
case 6: /* INTR */
|
||||
if (AC & 1) int_enable = int_enable | INT_RX;
|
||||
else int_enable = int_enable & ~INT_RX;
|
||||
int_req = INT_UPDATE;
|
||||
break;
|
||||
case 7: /* INIT */
|
||||
rx_reset (&rx_dev); /* reset device */
|
||||
break; } /* end case pulse */
|
||||
return AC;
|
||||
}
|
||||
|
||||
/* Unit service; the action to be taken depends on the transfer state:
|
||||
|
||||
IDLE Should never get here, treat as unknown command
|
||||
RWDS Just transferred sector, wait for track, set tr
|
||||
RWDT Just transferred track, do read or write, finish command
|
||||
FILL copy dbr to rx_buf[bufptr], advance ptr
|
||||
if bufptr > max, finish command, else set tr
|
||||
EMPTY if bufptr > max, finish command, else
|
||||
copy rx_buf[bufptr] to dbr, advance ptr, set tr
|
||||
CMD_COMPLETE copy requested data to dbr, finish command
|
||||
INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command
|
||||
|
||||
For RWDT and CMD_COMPLETE, the input argument is the selected drive;
|
||||
otherwise, it is drive 0.
|
||||
*/
|
||||
|
||||
t_stat rx_svc (UNIT *uptr)
|
||||
{
|
||||
int32 i, func, byptr;
|
||||
t_addr da;
|
||||
t_stat rval;
|
||||
void rx_done (int32 new_dbr, int32 new_ecode);
|
||||
#define PTR12(x) (((x) + (x) + (x)) >> 1)
|
||||
|
||||
rval = SCPE_OK; /* assume ok */
|
||||
func = (rx_csr >> RXCS_V_FUNC) & RXCS_M_FUNC; /* get function */
|
||||
switch (rx_state) { /* case on state */
|
||||
case IDLE: /* idle */
|
||||
rx_done (rx_esr, 0); /* done */
|
||||
break;
|
||||
case EMPTY: /* empty buffer */
|
||||
if (rx_csr & RXCS_MODE) { /* 8b xfer? */
|
||||
if (bufptr >= RX_NUMBY) { /* done? */
|
||||
rx_done (rx_esr, 0); /* set done */
|
||||
break; } /* and exit */
|
||||
rx_dbr = rx_buf[bufptr]; } /* else get data */
|
||||
else { byptr = PTR12 (bufptr); /* 12b xfer */
|
||||
if (bufptr >= RX_NUMWD) { /* done? */
|
||||
rx_done (rx_esr, 0); /* set done */
|
||||
break; } /* and exit */
|
||||
rx_dbr = (bufptr & 1)? /* get data */
|
||||
((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]:
|
||||
(rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017); }
|
||||
bufptr = bufptr + 1;
|
||||
rx_tr = 1;
|
||||
break;
|
||||
case FILL: /* fill buffer */
|
||||
if (rx_csr & RXCS_MODE) { /* 8b xfer? */
|
||||
rx_buf[bufptr] = rx_dbr; /* fill buffer */
|
||||
bufptr = bufptr + 1;
|
||||
if (bufptr < RX_NUMBY) rx_tr = 1; /* if more, set xfer */
|
||||
else rx_done (rx_esr, 0); } /* else done */
|
||||
else { byptr = PTR12 (bufptr); /* 12b xfer */
|
||||
if (bufptr & 1) { /* odd or even? */
|
||||
rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017);
|
||||
rx_buf[byptr + 1] = rx_dbr & 0377; }
|
||||
else {
|
||||
rx_buf[byptr] = (rx_dbr >> 4) & 0377;
|
||||
rx_buf[byptr + 1] = (rx_dbr & 017) << 4; }
|
||||
bufptr = bufptr + 1;
|
||||
if (bufptr < RX_NUMWD) rx_tr = 1; /* if more, set xfer */
|
||||
else { for (i = PTR12 (RX_NUMWD); i < RX_NUMBY; i++)
|
||||
rx_buf[i] = 0; /* else fill sector */
|
||||
rx_done (rx_esr, 0); } } /* set done */
|
||||
break;
|
||||
case RWDS: /* wait for sector */
|
||||
rx_tr = 1; /* set xfer ready */
|
||||
rx_state = RWDT; /* advance state */
|
||||
break;
|
||||
case RWDT: /* wait for track */
|
||||
if (rx_track >= RX_NUMTR) { /* bad track? */
|
||||
rx_done (rx_esr, 0040); /* done, error */
|
||||
break; }
|
||||
uptr -> TRACK = rx_track; /* now on track */
|
||||
if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */
|
||||
rx_done (rx_esr, 0070); /* done, error */
|
||||
break; }
|
||||
if ((uptr -> flags & UNIT_BUF) == 0) { /* not buffered? */
|
||||
rx_done (rx_esr, 0110); /* done, error */
|
||||
rval = SCPE_UNATT; /* return error */
|
||||
break; }
|
||||
da = CALC_DA (rx_track, rx_sector); /* get disk address */
|
||||
if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */
|
||||
if (func == RXCS_READ) { /* read? */
|
||||
for (i = 0; i < RX_NUMBY; i++)
|
||||
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,
|
||||
return to IDLE state.
|
||||
*/
|
||||
|
||||
void rx_done (int32 new_dbr, int32 new_ecode)
|
||||
{
|
||||
dev_done = dev_done | INT_RX; /* set done */
|
||||
int_req = INT_UPDATE; /* update ints */
|
||||
rx_dbr = new_dbr; /* update buffer */
|
||||
if (new_ecode != 0) { /* test for error */
|
||||
rx_ecode = new_ecode;
|
||||
rx_err = 1; }
|
||||
rx_state = IDLE; /* now idle */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset routine. The RX is one of the few devices that schedules
|
||||
an I/O transfer as part of its initialization.
|
||||
*/
|
||||
|
||||
t_stat rx_reset (DEVICE *dptr)
|
||||
{
|
||||
rx_esr = rx_ecode = 0; /* clear error */
|
||||
rx_tr = rx_err = 0; /* clear flags */
|
||||
dev_done = dev_done & ~INT_RX; /* clear done, int */
|
||||
int_req = int_req & ~INT_RX;
|
||||
int_enable = int_enable & ~INT_RX;
|
||||
rx_dbr = rx_csr = 0; /* 12b mode, drive 0 */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Bootstrap routine */
|
||||
|
||||
#define BOOT_START 022
|
||||
#define BOOT_INST 060
|
||||
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
|
||||
|
||||
static const int32 boot_rom[] = {
|
||||
06755, /* 22, SDN */
|
||||
05022, /* 23, JMP .-1 */
|
||||
07126, /* 24, CLL CML RTL ; read command + */
|
||||
01060, /* 25, TAD UNIT ; unit no */
|
||||
06751, /* 26, LCD ; load read+unit */
|
||||
07201, /* 27, CLL IAC ; AC = 1 */
|
||||
04053, /* 30, JMS 053 ; load sector */
|
||||
04053, /* 31, JMS 053 ; load track */
|
||||
07104, /* 32, CLL RAL ; AC = 2 */
|
||||
06755, /* 33, SDN */
|
||||
05054, /* 34, JMP 54 */
|
||||
06754, /* 35, SER */
|
||||
07450, /* 36, SNA ; more to do? */
|
||||
07610, /* 37, CLA SKP ; error */
|
||||
05046, /* 40, JMP 46 ; go empty */
|
||||
07402, 07402, /* 41-45, HALT ; error */
|
||||
07402, 07402, 07402,
|
||||
06751, /* 46, LCD ; load empty */
|
||||
04053, /* 47, JMS 53 ; get data */
|
||||
03002, /* 50, DCA 2 ; store */
|
||||
02050, /* 51, ISZ 50 ; incr store */
|
||||
05047, /* 52, JMP 47 ; loop until done */
|
||||
00000, /* 53, 0 */
|
||||
06753, /* 54, STR */
|
||||
05033, /* 55, JMP 33 */
|
||||
06752, /* 56, XDR */
|
||||
05453, /* 57, JMP I 53 */
|
||||
07024, /* UNIT, CML RAL ; for unit 1 */
|
||||
06030 /* 61, KCC */
|
||||
};
|
||||
|
||||
t_stat rx_boot (int32 unitno)
|
||||
{
|
||||
int32 i;
|
||||
extern int32 saved_PC;
|
||||
extern uint16 M[];
|
||||
|
||||
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
|
||||
M[BOOT_INST] = unitno? 07024: 07004;
|
||||
saved_PC = BOOT_START;
|
||||
return SCPE_OK;
|
||||
}
|
||||
516
PDP8/pdp8_sys.c
Normal file
516
PDP8/pdp8_sys.c
Normal file
@@ -0,0 +1,516 @@
|
||||
/* pdp8_sys.c: PDP-8 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.
|
||||
|
||||
26-Nov-01 RMS Added RL8A support
|
||||
17-Sep-01 RMS Removed multiconsole support
|
||||
16-Sep-01 RMS Added TSS/8 packed char support, added KL8A support
|
||||
27-May-01 RMS Added multiconsole support
|
||||
18-Mar-01 RMS Added DF32 support
|
||||
14-Mar-01 RMS Added extension detection of RIM binary tapes
|
||||
15-Feb-01 RMS Added DECtape support
|
||||
30-Oct-00 RMS Added support for examine to file
|
||||
27-Oct-98 RMS V2.4 load interface
|
||||
10-Apr-98 RMS Added RIM loader support
|
||||
17-Feb-97 RMS Fixed bug in handling of bin loader fields
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
#include <ctype.h>
|
||||
|
||||
extern DEVICE cpu_dev;
|
||||
extern UNIT cpu_unit;
|
||||
extern DEVICE ptr_dev, ptp_dev;
|
||||
extern DEVICE tti_dev, tto_dev;
|
||||
extern DEVICE clk_dev, lpt_dev;
|
||||
extern DEVICE rk_dev, rl_dev;
|
||||
extern DEVICE rx_dev;
|
||||
extern DEVICE df_dev, rf_dev;
|
||||
extern DEVICE dt_dev, mt_dev;
|
||||
extern DEVICE tti1_dev, tto1_dev;
|
||||
extern DEVICE tti2_dev, tto2_dev;
|
||||
extern DEVICE tti3_dev, tto3_dev;
|
||||
extern DEVICE tti4_dev, tto4_dev;
|
||||
extern REG cpu_reg[];
|
||||
extern uint16 M[];
|
||||
extern int32 sim_switches;
|
||||
|
||||
/* SCP data structures and interface routines
|
||||
|
||||
sim_name simulator name string
|
||||
sim_PC pointer to saved PC register descriptor
|
||||
sim_emax maximum number of words for examine/deposit
|
||||
sim_devices array of pointers to simulated devices
|
||||
sim_consoles array of pointers to consoles (if more than one)
|
||||
sim_stop_messages array of pointers to stop messages
|
||||
sim_load binary loader
|
||||
*/
|
||||
|
||||
char sim_name[] = "PDP-8";
|
||||
|
||||
REG *sim_PC = &cpu_reg[0];
|
||||
|
||||
int32 sim_emax = 4;
|
||||
|
||||
DEVICE *sim_devices[] = {
|
||||
&cpu_dev,
|
||||
&ptr_dev, &ptp_dev,
|
||||
&tti_dev, &tto_dev,
|
||||
&tti1_dev, &tto1_dev,
|
||||
&tti2_dev, &tto2_dev,
|
||||
&tti3_dev, &tto3_dev,
|
||||
&tti4_dev, &tto4_dev,
|
||||
&clk_dev, &lpt_dev,
|
||||
&rk_dev, &rl_dev,
|
||||
&rx_dev,
|
||||
&df_dev, &rf_dev,
|
||||
&dt_dev, &mt_dev,
|
||||
NULL };
|
||||
|
||||
const char *sim_stop_messages[] = {
|
||||
"Unknown error",
|
||||
"Unimplemented instruction",
|
||||
"HALT instruction",
|
||||
"Breakpoint" };
|
||||
|
||||
/* Binary loader
|
||||
|
||||
Two loader formats are supported: RIM loader (-r) and BIN (-b) loader.
|
||||
|
||||
RIM loader format consists of alternating pairs of addresses and 12-bit
|
||||
words. It can only operate in field 0 and is not checksummed.
|
||||
|
||||
BIN loader format consists of a string of 12-bit words (made up from
|
||||
7-bit characters) between leader and trailer (200). The last word on
|
||||
tape is the checksum. A word with the "link" bit set is a new origin;
|
||||
a character > 0200 indicates a change of field.
|
||||
*/
|
||||
|
||||
t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
|
||||
{
|
||||
int32 rubout, word, low, high, csum, newf, state, i;
|
||||
t_addr origin, field;
|
||||
extern t_bool match_ext (char *fnam, char *ext);
|
||||
|
||||
if ((*cptr != 0) || (flag != 0)) return SCPE_ARG;
|
||||
rubout = state = field = newf = origin = csum = 0;
|
||||
if ((sim_switches & SWMASK ('R')) || /* RIM format? */
|
||||
(match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B')))) {
|
||||
while ((i = getc (fileref)) != EOF) {
|
||||
switch (state) {
|
||||
case 0: /* leader */
|
||||
if ((i != 0) && (i < 0200)) state = 1;
|
||||
high = i;
|
||||
break;
|
||||
case 1: /* low byte */
|
||||
word = (high << 6) | i; /* form word */
|
||||
if (word > 07777) origin = word & 07777;
|
||||
else M[origin] = word;
|
||||
state = 2;
|
||||
break;
|
||||
case 2: /* high byte */
|
||||
if (i >= 0200) return SCPE_OK; /* end of tape? */
|
||||
high = i; /* save high */
|
||||
state = 1;
|
||||
break; } /* end switch */
|
||||
} /* end while */
|
||||
} /* end if */
|
||||
else { while ((i = getc (fileref)) != EOF) { /* BIN format */
|
||||
if (rubout) {
|
||||
rubout = 0;
|
||||
continue; }
|
||||
if (i == 0377) {
|
||||
rubout = 1;
|
||||
continue; }
|
||||
if (i > 0200) {
|
||||
newf = (i & 070) << 9;
|
||||
continue; }
|
||||
switch (state) {
|
||||
case 0: /* leader */
|
||||
if ((i != 0) && (i != 0200)) state = 1;
|
||||
high = i; /* save as high */
|
||||
break;
|
||||
case 1: /* low byte */
|
||||
low = i;
|
||||
state = 2;
|
||||
break;
|
||||
case 2: /* high with test */
|
||||
word = (high << 6) | low;
|
||||
if (i == 0200) { /* end of tape? */
|
||||
if ((csum - word) & 07777) return SCPE_CSUM;
|
||||
return SCPE_OK; }
|
||||
csum = csum + low + high;
|
||||
if (word >= 010000) origin = word & 07777;
|
||||
else { if ((field | origin) >= MEMSIZE)
|
||||
return SCPE_NXM;
|
||||
M[field | origin] = word & 07777;
|
||||
origin = (origin + 1) & 07777; }
|
||||
field = newf;
|
||||
high = i;
|
||||
state = 1;
|
||||
break; } /* end switch */
|
||||
} /* end while */
|
||||
} /* end else */
|
||||
return SCPE_FMT; /* eof? error */
|
||||
}
|
||||
|
||||
/* Symbol tables */
|
||||
|
||||
#define I_V_FL 18 /* flag start */
|
||||
#define I_M_FL 07 /* flag mask */
|
||||
#define I_V_NPN 0 /* no operand */
|
||||
#define I_V_FLD 1 /* field change */
|
||||
#define I_V_MRF 2 /* mem ref */
|
||||
#define I_V_IOT 3 /* general IOT */
|
||||
#define I_V_OP1 4 /* operate 1 */
|
||||
#define I_V_OP2 5 /* operate 2 */
|
||||
#define I_V_OP3 6 /* operate 3 */
|
||||
#define I_NPN (I_V_NPN << I_V_FL)
|
||||
#define I_FLD (I_V_FLD << I_V_FL)
|
||||
#define I_MRF (I_V_MRF << I_V_FL)
|
||||
#define I_IOT (I_V_IOT << I_V_FL)
|
||||
#define I_OP1 (I_V_OP1 << I_V_FL)
|
||||
#define I_OP2 (I_V_OP2 << I_V_FL)
|
||||
#define I_OP3 (I_V_OP3 << I_V_FL)
|
||||
|
||||
static const int32 masks[] = {
|
||||
07777, 07707, 07000, 07000,
|
||||
07416, 07571, 017457 };
|
||||
|
||||
static const char *opcode[] = {
|
||||
"SKON", "ION", "IOF", "SRQ",
|
||||
"GTF", "RTF", "SGT", "CAF",
|
||||
"RPE", "RSF", "RRB", "RFC", "RFC RRB",
|
||||
"PCE", "PSF", "PCF", "PPC", "PLS",
|
||||
"KCF", "KSF", "KCC", "KRS", "KIE", "KRB",
|
||||
"TLF", "TSF", "TCF", "TPC", "SPI", "TLS",
|
||||
"SBE", "SPL", "CAL",
|
||||
"CLEI", "CLDI", "CLSC", "CLLE", "CLCL", "CLSK",
|
||||
"CINT", "RDF", "RIF", "RIB",
|
||||
"RMF", "SINT", "CUF", "SUF",
|
||||
"ADCL", "ADLM", "ADST", "ADRB",
|
||||
"ADSK", "ADSE", "ADLE", "ADRS",
|
||||
"DCMA", "DMAR", "DMAW",
|
||||
"DCIM", "DSAC", "DIML", "DIMA",
|
||||
"DCEA", "DEAL", "DEAC",
|
||||
"DFSE", "DFSC", "DISK", "DMAC",
|
||||
"DCXA", "DXAL", "DXAC",
|
||||
"PSKF", "PCLF", "PSKE",
|
||||
"PSTB", "PSIE", "PCLF PSTB", "PCIE",
|
||||
"LWCR", "CWCR", "LCAR",
|
||||
"CCAR", "LCMR", "LFGR", "LDBR",
|
||||
"RWCR", "CLT", "RCAR",
|
||||
"RMSR", "RCMR", "RFSR", "RDBR",
|
||||
"SKEF", "SKCB", "SKJD", "SKTR", "CLF",
|
||||
"DSKP", "DCLR", "DLAG",
|
||||
"DLCA", "DRST", "DLDC", "DMAN",
|
||||
"LCD", "XDR", "STR",
|
||||
"SER", "SDN", "INTR", "INIT",
|
||||
"DTRA", "DTCA", "DTXA", "DTLA",
|
||||
"DTSF", "DTRB", "DTLB",
|
||||
"RLDC", "RLSD", "RLMA", "RLCA",
|
||||
"RLCB", "RLSA", "RLWC",
|
||||
"RRER", "RRWC", "RRCA", "RRCB",
|
||||
"RRSA", "RRSI", "RLSE",
|
||||
|
||||
"CDF", "CIF", "CIF CDF",
|
||||
"AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT",
|
||||
"NOP", "NOP2", "NOP3", "SWAB", "SWBA",
|
||||
"STL", "GLK", "STA", "LAS", "CIA",
|
||||
"BSW", "RAL", "RTL", "RAR", "RTR", "RAL RAR", "RTL RTR",
|
||||
"SKP", "SNL", "SZL",
|
||||
"SZA", "SNA", "SZA SNL", "SNA SZL",
|
||||
"SMA", "SPA", "SMA SNL", "SPA SZL",
|
||||
"SMA SZA", "SPA SNA", "SMA SZA SNL", "SPA SNA SZL",
|
||||
"SCL", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR",
|
||||
"SCA", "SCA SCL", "SCA MUY", "SCA DVI",
|
||||
"SCA NMI", "SCA SHL", "SCA ASR", "SCA LSR",
|
||||
"ACS", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR",
|
||||
"SCA", "DAD", "DST", "SWBA",
|
||||
"DPSZ", "DPIC", "DCIM", "SAM",
|
||||
"CLA", "CLL", "CMA", "CML", "IAC", /* encode only */
|
||||
"CLA", "OAS", "HLT",
|
||||
"CLA", "MQA", "MQL",
|
||||
NULL, NULL, NULL, NULL, /* decode only */
|
||||
NULL };
|
||||
|
||||
static const int32 opc_val[] = {
|
||||
06000+I_NPN, 06001+I_NPN, 06002+I_NPN, 06003+I_NPN,
|
||||
06004+I_NPN, 06005+I_NPN, 06006+I_NPN, 06007+I_NPN,
|
||||
06010+I_NPN, 06011+I_NPN, 06012+I_NPN, 06014+I_NPN, 06016+I_NPN,
|
||||
06020+I_NPN, 06021+I_NPN, 06022+I_NPN, 06024+I_NPN, 06026+I_NPN,
|
||||
06030+I_NPN, 06031+I_NPN, 06032+I_NPN, 06034+I_NPN, 06035+I_NPN, 06036+I_NPN,
|
||||
06040+I_NPN, 06041+I_NPN, 06042+I_NPN, 06044+I_NPN, 06045+I_NPN, 06046+I_NPN,
|
||||
06101+I_NPN, 06102+I_NPN, 06103+I_NPN,
|
||||
06131+I_NPN, 06132+I_NPN, 06133+I_NPN, 06135+I_NPN, 06136+I_NPN, 06137+I_NPN,
|
||||
06204+I_NPN, 06214+I_NPN, 06224+I_NPN, 06234+I_NPN,
|
||||
06244+I_NPN, 06254+I_NPN, 06264+I_NPN, 06274+I_NPN,
|
||||
06530+I_NPN, 06531+I_NPN, 06532+I_NPN, 06533+I_NPN,
|
||||
06534+I_NPN, 06535+I_NPN, 06536+I_NPN, 06537+I_NPN,
|
||||
06601+I_NPN, 06603+I_NPN, 06605+I_NPN,
|
||||
06611+I_NPN, 06612+I_NPN, 06615+I_NPN, 06616+I_NPN,
|
||||
06611+I_NPN, 06615+I_NPN, 06616+I_NPN,
|
||||
06621+I_NPN, 06622+I_NPN, 06623+I_NPN, 06626+I_NPN,
|
||||
06641+I_NPN, 06643+I_NPN, 06645+I_NPN,
|
||||
06661+I_NPN, 06662+I_NPN, 06663+I_NPN,
|
||||
06664+I_NPN, 06665+I_NPN, 06666+I_NPN, 06667+I_NPN,
|
||||
06701+I_NPN, 06702+I_NPN, 06703+I_NPN,
|
||||
06704+I_NPN, 06705+I_NPN, 06706+I_NPN, 06707+I_NPN,
|
||||
06711+I_NPN, 06712+I_NPN, 06713+I_NPN,
|
||||
06714+I_NPN, 06715+I_NPN, 06716+I_NPN, 06717+I_NPN,
|
||||
06721+I_NPN, 06722+I_NPN, 06723+I_NPN, 06724+I_NPN, 06725+I_NPN,
|
||||
06741+I_NPN, 06742+I_NPN, 06743+I_NPN,
|
||||
06744+I_NPN, 06745+I_NPN, 06746+I_NPN, 06747+I_NPN,
|
||||
06751+I_NPN, 06752+I_NPN, 06753+I_NPN,
|
||||
06754+I_NPN, 06755+I_NPN, 06756+I_NPN, 06757+I_NPN,
|
||||
06761+I_NPN, 06762+I_NPN, 06764+I_NPN, 06766+I_NPN,
|
||||
06771+I_NPN, 06772+I_NPN, 06774+I_NPN,
|
||||
06600+I_NPN, 06601+I_NPN, 06602+I_NPN, 06603+I_NPN,
|
||||
06604+I_NPN, 06605+I_NPN, 06607+I_NPN,
|
||||
06610+I_NPN, 06611+I_NPN, 06612+I_NPN, 06613+I_NPN,
|
||||
06614+I_NPN, 06615+I_NPN, 06617+I_NPN,
|
||||
|
||||
06201+I_FLD, 06202+I_FLD, 06203+I_FLD,
|
||||
00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF,
|
||||
04000+I_MRF, 05000+I_MRF, 06000+I_IOT,
|
||||
07000+I_NPN, 07400+I_NPN, 07401+I_NPN, 07431+I_NPN, 07447+I_NPN,
|
||||
07120+I_NPN, 07204+I_NPN, 07240+I_NPN, 07604+I_NPN, 07041+I_NPN,
|
||||
07002+I_OP1, 07004+I_OP1, 07006+I_OP1,
|
||||
07010+I_OP1, 07012+I_OP1, 07014+I_OP1, 07016+I_OP1,
|
||||
07410+I_OP2, 07420+I_OP2, 07430+I_OP2,
|
||||
07440+I_OP2, 07450+I_OP2, 07460+I_OP2, 07470+I_OP2,
|
||||
07500+I_OP2, 07510+I_OP2, 07520+I_OP2, 07530+I_OP2,
|
||||
07540+I_OP2, 07550+I_OP2, 07560+I_OP2, 07570+I_OP2,
|
||||
07403+I_OP3, 07405+I_OP3, 07407+I_OP3,
|
||||
07411+I_OP3, 07413+I_OP3, 07415+I_OP3, 07417+I_OP3,
|
||||
07441+I_OP3, 07443+I_OP3, 07445+I_OP3, 07447+I_OP3,
|
||||
07451+I_OP3, 07453+I_OP3, 07455+I_OP3, 07457+I_OP3,
|
||||
017403+I_OP3, 017405+I_OP3, 0174017+I_OP3,
|
||||
017411+I_OP3, 017413+I_OP3, 017415+I_OP3, 017417+I_OP3,
|
||||
017441+I_OP3, 017443+I_OP3, 017445+I_OP3, 017447+I_OP3,
|
||||
017451+I_OP3, 017453+I_OP3, 017455+I_OP3, 017457+I_OP3,
|
||||
07200+I_OP1, 07100+I_OP1, 07040+I_OP1, 07020+I_OP1, 07001+I_OP1,
|
||||
07600+I_OP2, 07404+I_OP2, 07402+I_OP2,
|
||||
07601+I_OP3, 07501+I_OP3, 07421+I_OP3,
|
||||
07000+I_OP1, 07400+I_OP2, 07401+I_OP3, 017401+I_OP3,
|
||||
-1 };
|
||||
|
||||
/* Operate decode
|
||||
|
||||
Inputs:
|
||||
*of = output stream
|
||||
inst = mask bits
|
||||
class = instruction class code
|
||||
sp = space needed?
|
||||
Outputs:
|
||||
status = space needed
|
||||
*/
|
||||
|
||||
int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp)
|
||||
{
|
||||
int32 i, j;
|
||||
|
||||
for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
|
||||
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
|
||||
if ((j == class) && (opc_val[i] & inst)) { /* same class? */
|
||||
inst = inst & ~opc_val[i]; /* mask bit set? */
|
||||
fprintf (of, (sp? " %s": "%s"), opcode[i]);
|
||||
sp = 1; } }
|
||||
return sp;
|
||||
}
|
||||
|
||||
/* Symbolic decode
|
||||
|
||||
Inputs:
|
||||
*of = output stream
|
||||
addr = current PC
|
||||
*val = pointer to data
|
||||
*uptr = pointer to unit
|
||||
sw = switches
|
||||
Outputs:
|
||||
return = status code
|
||||
*/
|
||||
|
||||
#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x)
|
||||
#define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100)
|
||||
#define TSSTOASC(x) ((x) + 040)
|
||||
|
||||
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
|
||||
UNIT *uptr, int32 sw)
|
||||
{
|
||||
int32 cflag, i, j, sp, inst, disp;
|
||||
extern int32 emode;
|
||||
|
||||
cflag = (uptr == NULL) || (uptr == &cpu_unit);
|
||||
inst = val[0];
|
||||
if (sw & SWMASK ('A')) { /* ASCII? */
|
||||
if (inst > 0377) return SCPE_ARG;
|
||||
fprintf (of, FMTASC (inst & 0177));
|
||||
return SCPE_OK; }
|
||||
if (sw & SWMASK ('C')) { /* characters? */
|
||||
fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077));
|
||||
fprintf (of, "%c", SIXTOASC (inst & 077));
|
||||
return SCPE_OK; }
|
||||
if (sw & SWMASK ('T')) { /* TSS8 packed? */
|
||||
fprintf (of, "%c", TSSTOASC ((inst >> 6) & 077));
|
||||
fprintf (of, "%c", TSSTOASC (inst & 077));
|
||||
return SCPE_OK; }
|
||||
if (!(sw & SWMASK ('M'))) return SCPE_ARG;
|
||||
|
||||
/* Instruction decode */
|
||||
|
||||
inst = val[0] | ((emode & 1) << 12); /* include EAE mode */
|
||||
for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
|
||||
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
|
||||
if ((opc_val[i] & 017777) == (inst & masks[j])) { /* match? */
|
||||
|
||||
switch (j) { /* case on class */
|
||||
case I_V_NPN: /* no operands */
|
||||
fprintf (of, "%s", opcode[i]); /* opcode */
|
||||
break;
|
||||
case I_V_FLD: /* field change */
|
||||
fprintf (of, "%s %-o", opcode[i], (inst >> 3) & 07);
|
||||
break;
|
||||
case I_V_MRF: /* mem ref */
|
||||
disp = inst & 0177; /* displacement */
|
||||
fprintf (of, "%s%s", opcode[i], ((inst & 00400)? " I ": " "));
|
||||
if (inst & 0200) { /* current page? */
|
||||
if (cflag) fprintf (of, "%-o", (addr & 07600) | disp);
|
||||
else fprintf (of, "C %-o", disp); }
|
||||
else fprintf (of, "%-o", disp); /* page zero */
|
||||
break;
|
||||
case I_V_IOT: /* IOT */
|
||||
fprintf (of, "%s %-o", opcode[i], inst & 0777);
|
||||
break;
|
||||
case I_V_OP1: /* operate group 1 */
|
||||
sp = fprint_opr (of, inst & 0361, j, 0);
|
||||
if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]);
|
||||
break;
|
||||
case I_V_OP2: /* operate group 2 */
|
||||
if (opcode[i]) fprintf (of, "%s", opcode[i]); /* skips */
|
||||
fprint_opr (of, inst & 0206, j, opcode[i] != NULL);
|
||||
break;
|
||||
case I_V_OP3: /* operate group 3 */
|
||||
sp = fprint_opr (of, inst & 0320, j, 0);
|
||||
if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]);
|
||||
break; } /* end case */
|
||||
return SCPE_OK; } /* end if */
|
||||
} /* end for */
|
||||
return SCPE_ARG;
|
||||
}
|
||||
|
||||
/* Symbolic input
|
||||
|
||||
Inputs:
|
||||
*cptr = pointer to input string
|
||||
addr = current PC
|
||||
*uptr = pointer to unit
|
||||
*val = pointer to output values
|
||||
sw = switches
|
||||
Outputs:
|
||||
status = error status
|
||||
*/
|
||||
|
||||
t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
|
||||
{
|
||||
int32 cflag, d, i, j, k;
|
||||
t_stat r;
|
||||
char gbuf[CBUFSIZE];
|
||||
|
||||
cflag = (uptr == NULL) || (uptr == &cpu_unit);
|
||||
while (isspace (*cptr)) cptr++; /* absorb spaces */
|
||||
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
|
||||
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
|
||||
val[0] = (t_value) cptr[0] | 0200;
|
||||
return SCPE_OK; }
|
||||
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */
|
||||
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
|
||||
val[0] = (((t_value) cptr[0] & 077) << 6) |
|
||||
((t_value) cptr[1] & 077);
|
||||
return SCPE_OK; }
|
||||
if ((sw & SWMASK ('T')) || ((*cptr == '"') && cptr++)) { /* TSS8 string? */
|
||||
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
|
||||
val[0] = (((t_value) (cptr[0] - 040) & 077) << 6) |
|
||||
((t_value) (cptr[1] - 040) & 077);
|
||||
return SCPE_OK; }
|
||||
|
||||
/* Instruction parse */
|
||||
|
||||
cptr = get_glyph (cptr, gbuf, 0); /* get opcode */
|
||||
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
|
||||
if (opcode[i] == NULL) return SCPE_ARG;
|
||||
val[0] = opc_val[i] & 07777; /* get value */
|
||||
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
|
||||
|
||||
switch (j) { /* case on class */
|
||||
case I_V_IOT: /* IOT */
|
||||
cptr = get_glyph (cptr, gbuf, 0); /* get dev+pulse */
|
||||
d = get_uint (gbuf, 8, 0777, &r);
|
||||
if (r != SCPE_OK) return SCPE_ARG;
|
||||
val[0] = val[0] | d;
|
||||
break;
|
||||
case I_V_FLD: /* field */
|
||||
for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0;
|
||||
cptr = get_glyph (cptr, gbuf, 0)) {
|
||||
for (i = 0; (opcode[i] != NULL) &&
|
||||
(strcmp (opcode[i], gbuf) != 0) ; i++) ;
|
||||
if (opcode[i] != NULL) {
|
||||
k = (opc_val[i] >> I_V_FL) & I_M_FL;
|
||||
if (k != j) return SCPE_ARG;
|
||||
val[0] = val[0] | (opc_val[i] & 07777); }
|
||||
else { d = get_uint (gbuf, 8, 07, &r);
|
||||
if (r != SCPE_OK) return SCPE_ARG;
|
||||
val[0] = val[0] | (d << 3);
|
||||
break; } }
|
||||
break;
|
||||
case I_V_MRF: /* mem ref */
|
||||
cptr = get_glyph (cptr, gbuf, 0); /* get next field */
|
||||
if (strcmp (gbuf, "I") == 0) { /* indirect? */
|
||||
val[0] = val[0] | 0400;
|
||||
cptr = get_glyph (cptr, gbuf, 0); }
|
||||
if ((k = (strcmp (gbuf, "C") == 0)) || (strcmp (gbuf, "Z") == 0)) {
|
||||
cptr = get_glyph (cptr, gbuf, 0);
|
||||
d = get_uint (gbuf, 8, 0177, &r);
|
||||
if (r != SCPE_OK) return SCPE_ARG;
|
||||
val[0] = val[0] | d | (k? 0200: 0); }
|
||||
else { d = get_uint (gbuf, 8, 07777, &r);
|
||||
if (r != SCPE_OK) return SCPE_ARG;
|
||||
if (d <= 0177) val[0] = val[0] | d;
|
||||
else if (cflag && (((addr ^ d) & 07600) == 0))
|
||||
val[0] = val[0] | (d & 0177) | 0200;
|
||||
else return SCPE_ARG; }
|
||||
break;
|
||||
case I_V_NPN: case I_V_OP1: case I_V_OP2: case I_V_OP3: /* operates */
|
||||
for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0;
|
||||
cptr = get_glyph (cptr, gbuf, 0)) {
|
||||
for (i = 0; (opcode[i] != NULL) &&
|
||||
(strcmp (opcode[i], gbuf) != 0) ; i++) ;
|
||||
k = opc_val[i] & 07777;
|
||||
if ((opcode[i] == NULL) || (((k ^ val[0]) & 07000) != 0))
|
||||
return SCPE_ARG;
|
||||
val[0] = val[0] | k; }
|
||||
break; } /* end case */
|
||||
if (*cptr != 0) return SCPE_ARG; /* junk at end? */
|
||||
return SCPE_OK;
|
||||
}
|
||||
208
PDP8/pdp8_tt.c
Normal file
208
PDP8/pdp8_tt.c
Normal file
@@ -0,0 +1,208 @@
|
||||
/* pdp8_tt.c: PDP-8 console terminal simulator
|
||||
|
||||
Copyright (c) 1993-2001, Robert M Supnik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Robert M Supnik.
|
||||
|
||||
tti,tto KL8E terminal input/output
|
||||
|
||||
07-Sep-01 RMS Moved function prototypes
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */
|
||||
#define UNIT_UC (1 << UNIT_V_UC)
|
||||
extern int32 int_req, int_enable, dev_done, stop_inst;
|
||||
t_stat tti_svc (UNIT *uptr);
|
||||
t_stat tto_svc (UNIT *uptr);
|
||||
t_stat tti_reset (DEVICE *dptr);
|
||||
t_stat tto_reset (DEVICE *dptr);
|
||||
|
||||
/* TTI data structures
|
||||
|
||||
tti_dev TTI device descriptor
|
||||
tti_unit TTI unit descriptor
|
||||
tti_reg TTI register list
|
||||
tti_mod TTI modifiers list
|
||||
*/
|
||||
|
||||
UNIT tti_unit = { UDATA (&tti_svc, UNIT_UC, 0), KBD_POLL_WAIT };
|
||||
|
||||
REG tti_reg[] = {
|
||||
{ ORDATA (BUF, tti_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTI) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTI) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTI) },
|
||||
{ DRDATA (POS, tti_unit.pos, 31), PV_LEFT },
|
||||
{ DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (UC, tti_unit.flags, UNIT_V_UC), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
MTAB tti_mod[] = {
|
||||
{ UNIT_UC, 0, "lower case", "LC", NULL },
|
||||
{ UNIT_UC, UNIT_UC, "upper case", "UC", NULL },
|
||||
{ 0 } };
|
||||
|
||||
DEVICE tti_dev = {
|
||||
"TTI", &tti_unit, tti_reg, tti_mod,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &tti_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
/* TTO data structures
|
||||
|
||||
tto_dev TTO device descriptor
|
||||
tto_unit TTO unit descriptor
|
||||
tto_reg TTO register list
|
||||
*/
|
||||
|
||||
UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT };
|
||||
|
||||
REG tto_reg[] = {
|
||||
{ ORDATA (BUF, tto_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTO) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTO) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTO) },
|
||||
{ DRDATA (POS, tto_unit.pos, 31), PV_LEFT },
|
||||
{ DRDATA (TIME, tto_unit.wait, 24), PV_LEFT },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tto_dev = {
|
||||
"TTO", &tto_unit, tto_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &tto_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
/* Terminal input: IOT routine */
|
||||
|
||||
int32 tti (int32 pulse, int32 AC)
|
||||
{
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 0: /* KCF */
|
||||
dev_done = dev_done & ~INT_TTI; /* clear flag */
|
||||
int_req = int_req & ~INT_TTI;
|
||||
return AC;
|
||||
case 1: /* KSF */
|
||||
return (dev_done & INT_TTI)? IOT_SKP + AC: AC;
|
||||
case 2: /* KCC */
|
||||
dev_done = dev_done & ~INT_TTI; /* clear flag */
|
||||
int_req = int_req & ~INT_TTI;
|
||||
return 0; /* clear AC */
|
||||
case 4: /* KRS */
|
||||
return (AC | tti_unit.buf); /* return buffer */
|
||||
case 5: /* KIE */
|
||||
if (AC & 1) int_enable = int_enable | (INT_TTI+INT_TTO);
|
||||
else int_enable = int_enable & ~(INT_TTI+INT_TTO);
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 6: /* KRB */
|
||||
dev_done = dev_done & ~INT_TTI; /* clear flag */
|
||||
int_req = int_req & ~INT_TTI;
|
||||
return (tti_unit.buf); /* return buffer */
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat tti_svc (UNIT *uptr)
|
||||
{
|
||||
int32 temp;
|
||||
|
||||
sim_activate (&tti_unit, tti_unit.wait); /* continue poll */
|
||||
if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */
|
||||
temp = temp & 0177;
|
||||
if ((tti_unit.flags & UNIT_UC) && islower (temp))
|
||||
temp = toupper (temp);
|
||||
tti_unit.buf = temp | 0200; /* got char */
|
||||
dev_done = dev_done | INT_TTI; /* set done */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
tti_unit.pos = tti_unit.pos + 1;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat tti_reset (DEVICE *dptr)
|
||||
{
|
||||
tti_unit.buf = 0;
|
||||
dev_done = dev_done & ~INT_TTI; /* clear done, int */
|
||||
int_req = int_req & ~INT_TTI;
|
||||
int_enable = int_enable | INT_TTI; /* set enable */
|
||||
sim_activate (&tti_unit, tti_unit.wait); /* activate unit */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Terminal output: IOT routine */
|
||||
|
||||
int32 tto (int32 pulse, int32 AC)
|
||||
{
|
||||
switch (pulse) { /* decode IR<9:11> */
|
||||
case 0: /* TLF */
|
||||
dev_done = dev_done | INT_TTO; /* set flag */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
return AC;
|
||||
case 1: /* TSF */
|
||||
return (dev_done & INT_TTO)? IOT_SKP + AC: AC;
|
||||
case 2: /* TCF */
|
||||
dev_done = dev_done & ~INT_TTO; /* clear flag */
|
||||
int_req = int_req & ~INT_TTO; /* clear int req */
|
||||
return AC;
|
||||
case 5: /* SPI */
|
||||
return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC;
|
||||
case 6: /* TLS */
|
||||
dev_done = dev_done & ~INT_TTO; /* clear flag */
|
||||
int_req = int_req & ~INT_TTO; /* clear int req */
|
||||
case 4: /* TPC */
|
||||
sim_activate (&tto_unit, tto_unit.wait); /* activate unit */
|
||||
tto_unit.buf = AC; /* load buffer */
|
||||
return AC;
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat tto_svc (UNIT *uptr)
|
||||
{
|
||||
int32 temp;
|
||||
|
||||
dev_done = dev_done | INT_TTO; /* set done */
|
||||
int_req = INT_UPDATE; /* update interrupts */
|
||||
if ((temp = sim_putchar (tto_unit.buf & 0177)) != SCPE_OK) return temp;
|
||||
tto_unit.pos = tto_unit.pos + 1;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat tto_reset (DEVICE *dptr)
|
||||
{
|
||||
tto_unit.buf = 0;
|
||||
dev_done = dev_done & ~INT_TTO; /* clear done, int */
|
||||
int_req = int_req & ~INT_TTO;
|
||||
int_enable = int_enable | INT_TTO; /* set enable */
|
||||
sim_cancel (&tto_unit); /* deactivate unit */
|
||||
return SCPE_OK;
|
||||
}
|
||||
428
PDP8/pdp8_ttx.c
Normal file
428
PDP8/pdp8_ttx.c
Normal file
@@ -0,0 +1,428 @@
|
||||
/* pdp8_ttx.c: PDP-8 additional terminals 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.
|
||||
|
||||
ttix,ttox PT08/KL8JA terminal input/output
|
||||
|
||||
30-Nov-01 RMS Added extended SET/SHOW support
|
||||
|
||||
This module implements four individual serial interfaces similar in function
|
||||
to the console. These interfaces are mapped to Telnet based connections as
|
||||
though they were the four lines of a terminal multiplexor. The connection
|
||||
polling mechanism is superimposed onto the keyboard of the first interface.
|
||||
*/
|
||||
|
||||
#include "pdp8_defs.h"
|
||||
#include "sim_sock.h"
|
||||
#include "sim_tmxr.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#define TTX_LINES 4
|
||||
#define TTX_MASK (TTX_LINES - 1)
|
||||
#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */
|
||||
#define UNIT_UC (1 << UNIT_V_UC)
|
||||
#define TTX_GETLN(x) (((x) >> 4) & TTX_MASK)
|
||||
|
||||
extern int32 int_req, int_enable, dev_done, stop_inst;
|
||||
extern int32 tmxr_poll; /* calibrated poll */
|
||||
TMLN tt1_ldsc = { 0 }; /* line descriptors */
|
||||
TMLN tt2_ldsc = { 0 }; /* line descriptors */
|
||||
TMLN tt3_ldsc = { 0 }; /* line descriptors */
|
||||
TMLN tt4_ldsc = { 0 }; /* line descriptors */
|
||||
|
||||
TMXR ttx_desc = { /* mux descriptor */
|
||||
TTX_LINES, 0, &tt1_ldsc, &tt2_ldsc, &tt3_ldsc, &tt4_ldsc };
|
||||
|
||||
t_stat ttix_svc (UNIT *uptr);
|
||||
t_stat ttix_reset (DEVICE *dptr);
|
||||
t_stat ttox_svc (UNIT *uptr);
|
||||
t_stat ttox_reset (DEVICE *dptr);
|
||||
t_stat ttx_attach (UNIT *uptr, char *cptr);
|
||||
t_stat ttx_detach (UNIT *uptr);
|
||||
t_stat ttx_status (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||||
|
||||
/* TTIx data structures
|
||||
|
||||
ttix_dev TTIx device descriptor
|
||||
ttix_unit TTIx unit descriptor
|
||||
ttix_reg TTIx register list
|
||||
ttix_mod TTIx modifiers list
|
||||
*/
|
||||
|
||||
MTAB ttix_mod[] = {
|
||||
{ UNIT_UC, 0, "lower case", "LC", NULL },
|
||||
{ UNIT_UC, UNIT_UC, "upper case", "UC", NULL },
|
||||
{ UNIT_ATT, UNIT_ATT, "line status", NULL, NULL, &ttx_status },
|
||||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LINESTATUS", NULL,
|
||||
NULL, &ttx_status, NULL },
|
||||
{ 0 } };
|
||||
|
||||
UNIT ttix_unit[] = {
|
||||
{ UDATA (&ttix_svc, UNIT_ATTABLE+UNIT_UC, 0), KBD_POLL_WAIT },
|
||||
{ UDATA (&ttix_svc, UNIT_UC, 0), KBD_POLL_WAIT },
|
||||
{ UDATA (&ttix_svc, UNIT_UC, 0), KBD_POLL_WAIT },
|
||||
{ UDATA (&ttix_svc, UNIT_UC, 0), KBD_POLL_WAIT } };
|
||||
|
||||
#define tti1_unit ttix_unit[0]
|
||||
#define tti2_unit ttix_unit[1]
|
||||
#define tti3_unit ttix_unit[2]
|
||||
#define tti4_unit ttix_unit[3]
|
||||
|
||||
REG tti1_reg[] = {
|
||||
{ ORDATA (BUF, tti1_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTI1) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTI1) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTI1) },
|
||||
{ DRDATA (POS, tt1_ldsc.rxcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tti1_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (UC, tti1_unit.flags, UNIT_V_UC), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tti1_dev = {
|
||||
"TTI1", &tti1_unit, tti1_reg, ttix_mod,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
&tmxr_ex, &tmxr_dep, &ttix_reset,
|
||||
NULL, &ttx_attach, &ttx_detach };
|
||||
|
||||
REG tti2_reg[] = {
|
||||
{ ORDATA (BUF, tti2_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTI2) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTI2) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTI2) },
|
||||
{ DRDATA (POS, tt2_ldsc.rxcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tti2_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (UC, tti2_unit.flags, UNIT_V_UC), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tti2_dev = {
|
||||
"TTI2", &tti2_unit, tti2_reg, ttix_mod,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttix_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
REG tti3_reg[] = {
|
||||
{ ORDATA (BUF, tti3_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTI3) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTI3) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTI3) },
|
||||
{ DRDATA (POS, tt3_ldsc.rxcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tti3_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (UC, tti3_unit.flags, UNIT_V_UC), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tti3_dev = {
|
||||
"TTI3", &tti3_unit, tti3_reg, ttix_mod,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttix_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
REG tti4_reg[] = {
|
||||
{ ORDATA (BUF, tti4_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTI4) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTI4) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTI4) },
|
||||
{ DRDATA (POS, tt4_ldsc.rxcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tti4_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||
{ FLDATA (UC, tti4_unit.flags, UNIT_V_UC), REG_HRO },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tti4_dev = {
|
||||
"TTI4", &tti4_unit, tti4_reg, ttix_mod,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttix_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
/* TTOx data structures
|
||||
|
||||
ttox_dev TTOx device descriptor
|
||||
ttox_unit TTOx unit descriptor
|
||||
ttox_reg TTOx register list
|
||||
*/
|
||||
|
||||
UNIT ttox_unit[] = {
|
||||
{ UDATA (&ttox_svc, 0, 0), SERIAL_OUT_WAIT },
|
||||
{ UDATA (&ttox_svc, 0, 0), SERIAL_OUT_WAIT },
|
||||
{ UDATA (&ttox_svc, 0, 0), SERIAL_OUT_WAIT },
|
||||
{ UDATA (&ttox_svc, 0, 0), SERIAL_OUT_WAIT } };
|
||||
|
||||
#define tto1_unit ttox_unit[0]
|
||||
#define tto2_unit ttox_unit[0]
|
||||
#define tto3_unit ttox_unit[0]
|
||||
#define tto4_unit ttox_unit[0]
|
||||
|
||||
REG tto1_reg[] = {
|
||||
{ ORDATA (BUF, tto1_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTO1) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTO1) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTO1) },
|
||||
{ DRDATA (POS, tt1_ldsc.txcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tto1_unit.wait, 24), PV_LEFT },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tto1_dev = {
|
||||
"TTO1", &tto1_unit, tto1_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttox_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
REG tto2_reg[] = {
|
||||
{ ORDATA (BUF, tto2_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTO2) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTO2) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTO2) },
|
||||
{ DRDATA (POS, tt2_ldsc.txcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tto2_unit.wait, 24), PV_LEFT },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tto2_dev = {
|
||||
"TTO2", &tto2_unit, tto2_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttox_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
REG tto3_reg[] = {
|
||||
{ ORDATA (BUF, tto3_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTO3) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTO3) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTO3) },
|
||||
{ DRDATA (POS, tt3_ldsc.txcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tto3_unit.wait, 24), PV_LEFT },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tto3_dev = {
|
||||
"TTO3", &tto3_unit, tto3_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttox_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
REG tto4_reg[] = {
|
||||
{ ORDATA (BUF, tto4_unit.buf, 8) },
|
||||
{ FLDATA (DONE, dev_done, INT_V_TTO4) },
|
||||
{ FLDATA (ENABLE, int_enable, INT_V_TTO4) },
|
||||
{ FLDATA (INT, int_req, INT_V_TTO4) },
|
||||
{ DRDATA (POS, tt4_ldsc.txcnt, 32), PV_LEFT },
|
||||
{ DRDATA (TIME, tto4_unit.wait, 24), PV_LEFT },
|
||||
{ NULL } };
|
||||
|
||||
DEVICE tto4_dev = {
|
||||
"TTO4", &tto4_unit, tto4_reg, NULL,
|
||||
1, 10, 31, 1, 8, 8,
|
||||
NULL, NULL, &ttox_reset,
|
||||
NULL, NULL, NULL };
|
||||
|
||||
/* Terminal input: IOT routine */
|
||||
|
||||
int32 ttix (int32 inst, int32 AC)
|
||||
{
|
||||
int32 pulse = inst & 07; /* IOT pulse */
|
||||
int32 ln = TTX_GETLN (inst); /* line # */
|
||||
int32 itti = (INT_TTI1 << ln); /* rx intr */
|
||||
int32 itto = (INT_TTO1 << ln); /* tx intr */
|
||||
|
||||
switch (pulse) { /* case IR<9:11> */
|
||||
case 0: /* KCF */
|
||||
dev_done = dev_done & ~itti; /* clear flag */
|
||||
int_req = int_req & ~itti;
|
||||
break;
|
||||
case 1: /* KSF */
|
||||
return (dev_done & itti)? IOT_SKP + AC: AC;
|
||||
case 2: /* KCC */
|
||||
dev_done = dev_done & ~itti; /* clear flag */
|
||||
int_req = int_req & ~itti;
|
||||
return 0; /* clear AC */
|
||||
case 4: /* KRS */
|
||||
return (AC | ttix_unit[ln].buf); /* return buf */
|
||||
case 5: /* KIE */
|
||||
if (AC & 1) int_enable = int_enable | (itti + itto);
|
||||
else int_enable = int_enable & ~(itti + itto);
|
||||
int_req = INT_UPDATE; /* update intr */
|
||||
break;
|
||||
case 6: /* KRB */
|
||||
dev_done = dev_done & ~itti; /* clear flag */
|
||||
int_req = int_req & ~itti;
|
||||
return ttix_unit[ln].buf; /* return buf */
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
return AC;
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat ttix_svc (UNIT *uptr)
|
||||
{
|
||||
int32 temp, newln;
|
||||
int32 ln = uptr - ttix_unit; /* line # */
|
||||
int32 itti = (INT_TTI1 << ln); /* rx intr */
|
||||
|
||||
if (ttx_desc.ldsc[ln] -> conn) { /* connected? */
|
||||
tmxr_poll_rx (&ttx_desc); /* poll for input */
|
||||
if (temp = tmxr_getc_ln (ttx_desc.ldsc[ln])) { /* get char */
|
||||
temp = temp & 0177; /* mask to 7b */
|
||||
if ((uptr -> flags & UNIT_UC) && islower (temp))
|
||||
temp = toupper (temp);
|
||||
uptr -> buf = temp | 0200; /* Teletype code */
|
||||
dev_done = dev_done | itti; /* set done */
|
||||
int_req = INT_UPDATE; } /* update intr */
|
||||
sim_activate (uptr, uptr -> wait); } /* continue poll */
|
||||
if (uptr -> flags & UNIT_ATT) { /* attached? */
|
||||
newln = tmxr_poll_conn (&ttx_desc, uptr); /* poll connect */
|
||||
if (newln >= 0) { /* got one? */
|
||||
sim_activate (&ttix_unit[newln], ttix_unit[newln].wait);
|
||||
ttx_desc.ldsc[newln] -> rcve = 1; } /* rcv enabled */
|
||||
sim_activate (uptr, tmxr_poll); } /* sched poll */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat ttix_reset (DEVICE *dptr)
|
||||
{
|
||||
UNIT *uptr = dptr -> units; /* unit */
|
||||
int32 ln = uptr - ttix_unit; /* line # */
|
||||
int32 itti = (INT_TTI1 << ln); /* rx intr */
|
||||
|
||||
uptr -> buf = 0; /* clr buf */
|
||||
dev_done = dev_done & ~itti; /* clr done, int */
|
||||
int_req = int_req & ~itti;
|
||||
int_enable = int_enable | itti; /* set enable */
|
||||
if (ttx_desc.ldsc[ln] -> conn) { /* if conn, */
|
||||
sim_activate (uptr, uptr -> wait); /* activate, */
|
||||
ttx_desc.ldsc[ln] -> rcve = 1; } /* enable */
|
||||
else if (uptr -> flags & UNIT_ATT) /* if attached, */
|
||||
sim_activate (uptr, tmxr_poll); /* activate */
|
||||
else sim_cancel (uptr); /* else stop */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Terminal output: IOT routine */
|
||||
|
||||
int32 ttox (int32 inst, int32 AC)
|
||||
{
|
||||
int32 pulse = inst & 07; /* pulse */
|
||||
int32 ln = TTX_GETLN (inst); /* line # */
|
||||
int32 itti = (INT_TTI1 << ln); /* rx intr */
|
||||
int32 itto = (INT_TTO1 << ln); /* tx intr */
|
||||
|
||||
switch (pulse) { /* case IR<9:11> */
|
||||
case 0: /* TLF */
|
||||
dev_done = dev_done | itto; /* set flag */
|
||||
int_req = INT_UPDATE; /* update intr */
|
||||
break;
|
||||
case 1: /* TSF */
|
||||
return (dev_done & itto)? IOT_SKP + AC: AC;
|
||||
case 2: /* TCF */
|
||||
dev_done = dev_done & ~itto; /* clear flag */
|
||||
int_req = int_req & ~itto; /* clear intr */
|
||||
break;
|
||||
case 5: /* SPI */
|
||||
return (int_req & (itti | itto))? IOT_SKP + AC: AC;
|
||||
case 6: /* TLS */
|
||||
dev_done = dev_done & ~itto; /* clear flag */
|
||||
int_req = int_req & ~itto; /* clear int req */
|
||||
case 4: /* TPC */
|
||||
sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate */
|
||||
ttox_unit[ln].buf = AC & 0377; /* load buffer */
|
||||
break;
|
||||
default:
|
||||
return (stop_inst << IOT_V_REASON) + AC; } /* end switch */
|
||||
return AC;
|
||||
}
|
||||
|
||||
/* Unit service */
|
||||
|
||||
t_stat ttox_svc (UNIT *uptr)
|
||||
{
|
||||
int32 ln = uptr - ttox_unit; /* line # */
|
||||
int32 itto = (INT_TTO1 << ln); /* tx intr */
|
||||
|
||||
if (ttx_desc.ldsc[ln] -> conn) { /* connected? */
|
||||
if (ttx_desc.ldsc[ln] -> xmte) { /* tx enabled? */
|
||||
TMLN *lp = ttx_desc.ldsc[ln]; /* get line */
|
||||
tmxr_putc_ln (lp, uptr -> buf & 0177); /* output char */
|
||||
tmxr_poll_tx (&ttx_desc); } /* poll xmt */
|
||||
else { tmxr_poll_tx (&ttx_desc); /* poll xmt */
|
||||
sim_activate (uptr, tmxr_poll); /* wait */
|
||||
return SCPE_OK; } }
|
||||
dev_done = dev_done | itto; /* set done */
|
||||
int_req = INT_UPDATE; /* update intr */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Reset routine */
|
||||
|
||||
t_stat ttox_reset (DEVICE *dptr)
|
||||
{
|
||||
UNIT *uptr = dptr -> units; /* unit */
|
||||
int32 ln = uptr - ttox_unit; /* line # */
|
||||
int32 itto = (INT_TTO1 << ln); /* tx intr */
|
||||
|
||||
uptr -> buf = 0; /* clr buf */
|
||||
dev_done = dev_done & ~itto; /* clr done, int */
|
||||
int_req = int_req & ~itto;
|
||||
int_enable = int_enable | itto; /* set enable */
|
||||
sim_cancel (uptr); /* deactivate */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Attach master unit */
|
||||
|
||||
t_stat ttx_attach (UNIT *uptr, char *cptr)
|
||||
{
|
||||
t_stat r;
|
||||
|
||||
r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */
|
||||
if (r != SCPE_OK) return r; /* error */
|
||||
sim_activate (uptr, tmxr_poll); /* start poll */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Detach master unit */
|
||||
|
||||
t_stat ttx_detach (UNIT *uptr)
|
||||
{
|
||||
int32 i;
|
||||
t_stat r;
|
||||
|
||||
r = tmxr_detach (&ttx_desc, uptr); /* detach */
|
||||
for (i = 0; i < TTX_LINES; i++) { /* all lines, */
|
||||
ttx_desc.ldsc[i] -> rcve = 0; /* disable rcv */
|
||||
sim_cancel (&ttix_unit[i]); } /* stop poll */
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* Status */
|
||||
|
||||
t_stat ttx_status (FILE *st, UNIT *uptr, int32 val, void *desc)
|
||||
{
|
||||
int32 i;
|
||||
|
||||
fprintf (st, "line status:");
|
||||
for (i = 0; (i < TTX_LINES) && (ttx_desc.ldsc[i] -> conn == 0); i++) ;
|
||||
if (i < TTX_LINES) {
|
||||
for (i = 0; i < TTX_LINES; i++) {
|
||||
if (ttx_desc.ldsc[i] -> conn)
|
||||
tmxr_fstatus (st, ttx_desc.ldsc[i], i); } }
|
||||
else fprintf (st, " all disconnected");
|
||||
return SCPE_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user