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

Notes For V3.1-0

RESTRICTION: The FP15 and XVM features of the PDP-15 are only partially
debugged.  Do NOT enable these features for normal operations.

1. New Features in 3.1-0

1.1 SCP and libraries

- Added simulated Ethernet support for VMS, FreeBSD, Mac OS/X.
- Added status return to tmxr_putc_ln.
- Added sim_putchar_s to handle possible output stalls.

1.2 All DECtapes

- Added "DECtape off reel" error stop.

1.3 All Asynchronous Consoles

- Added support for output congestion stall if using a Telnet connection.

1.4 PDP-1

- Added Type 23 parallel drum support.

1.5 PDP-8

- Added instruction history.
- Added TSC8-75 option support for ETOS.
- Added TD8E DECtape support.

1.6 PDP-18b

- Added instruction history.
- Changed PDP-9, PDP-15 API default to enabled.

1.7 PDP-11

- Added support for 18b only Qbus devices.
- Formalized bus and addressing definitions.
- Added control to enable/disable autoconfiguration.
- Added stub support for second Unibus Ethernet controller.

1.7 Interdata 32b

- Added instruction history.

1.8 Eclipse

- Added floating point support.
- Added programmable interval timer support.

1.9 H316

- Added DMA/DMC support.
- Added fixed head disk support.
- Added moving head disk support.
- Added magtape support.

1.10 IBM 1130 (Brian Knittel)

- Added support for physical card reader, using the Cardread
interface (www.ibm1130.org/sim/downloads).
- Added support for physical printer (flushes output buffer after
each line).

2. Bugs Fixed in 3.1-0

2.1 SCP and libraries

- Fixed numerous bugs in Ethernet library.

2.2 All DECtapes

- Fixed reverse checksum value in 'read all' mode.
- Simplified (and sped up) timing.

2.3 PDP-8

- Fixed bug in RX28 read status (found by Charles Dickman).
- Fixed RX28 double density write.

2.4 PDP-18b

- Fixed autoincrement bug in PDP-4, PDP-7, PDP-9.

2.5 PDP-11/VAX

- Revised RQ MB->LBN conversion for greater accuracy.
- Fixed bug in IO configuration (found by David Hittner).
- Fixed bug with multiple RQ RAUSER drives.
- Fixed bug in second Qbus Ethernet controller interrupts.

2.6 Nova/Eclipse

- Fixed bugs in DKP flag clear, map setup, map usage (Charles Owen).
- Fixed bug in MT, reset completes despite I/O reset (Charles Owen).
- Fixed bug in MT, space operations return word count (Charles Owen).

2.7 IBM 1130 (Brian Knittel)

- Fixed bug in setting carry bit in subtract and subtract double.
- Fixed timing problem in console printer simulation.

2.8 1620

- Fixed bug in branch digit (found by Dave Babcock).

3. New Features in 3.0 vs prior releases

3.1 SCP and Libraries

- Added ASSIGN/DEASSIGN (logical name) commands.
- Changed RESTORE to unconditionally detach files.
- Added E11 and TPC format support to magtape library.
- Fixed bug in SHOW CONNECTIONS.
- Added USE_ADDR64 support.

3.2 All magtapes

- Magtapes support SIMH format, E11 format, and TPC format (read only).
- SET <tape_unit> FORMAT=format sets the specified tape unit's format.
- SHOW <tape_unit> FORMAT displays the specified tape unit's format.
- Tape format can also be set as part of the ATTACH command, using
  the -F switch.

3.3 VAX

- VAX can be compiled without USE_INT64.
- If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support
  files > 2GB.
- VAX ROM has speed control (SET ROM DELAY/NODELAY).

3.4 PDP-1

- Added block loader format support to LOAD.
- Changed BOOT PTR to allow loading of all of the first bank of memory.
- The LOAD command takes an optional argument specifying the memory field
  to be loaded.
- The PTR BOOT command takes its starting memory field from the TA (address
  switch) register.

3.5 PDP-18b Family

- Added PDP-4 EAE support.
- Added PDP-15 FP15 support.
- Added PDP-15 XVM support.
- Added PDP-15 "re-entrancy ECO".
- Added PDP-7, PDP-9, PDP-15 hardware RIM loader support in BOOT PTR.

4. Bugs Fixed in 3.0 vs prior releases

4.1 SCP and Libraries

- Fixed end of file problem in dep, idep.
- Fixed handling of trailing spaces in dep, idep.

4.2 VAX

- Fixed CVTfi bug: integer overflow not set if exponent out of range
- Fixed EMODx bugs:
  o First and second operands reversed
  o Separated fraction received wrong exponent
  o Overflow calculation on separated integer incorrect
  o Fraction not set to zero if exponent out of range
- Fixed interval timer and ROM access to pass power-up self-test even on very
  fast host processors (fixes from Mark Pizzolato).
- Fixed bug in user disk size (found by Chaskiel M Grundman).

4.3 1401

- Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS.
- Fixed MCE bug, BS off by 1 if zero suppress.
- Fixed chaining bug, D lost if return to SCP.
- Fixed H branch, branch occurs after continue.
- Added check for invalid 8 character MCW, LCA.
- Fixed magtape load-mode end of record response.
- Revised fetch to model hardware more closely.
- Fixed tape read end-of-record handling based on real 1401.
- Added diagnostic read (space forward).

4.4 Nova

- Fixed DSK variable size interaction with restore.
- Fixed bug in DSK set size routine.

4.5 PDP-1

- Fixed DT variable size interaction with restore.
- Updated CPU, line printer, standard devices to detect indefinite I/O wait.
- Fixed incorrect logical, missing activate, break in drum simulator.
- Fixed bugs in instruction decoding, overprinting for line printer.
- Fixed system hang if continue after PTR error.
- Fixed PTR to start/stop on successive rpa instructions.

4.6 PDP-11

- Fixed DT variable size interaction with restore.
- Fixed bug in MMR1 update (found by Tim Stark).
- Added XQ features and fixed bugs:
  o Corrected XQ interrupts on IE state transition (code by Tom Evans).
  o Added XQ interrupt clear on soft reset.
  o Removed XQ interrupt when setting XL or RL (multiple people).
  o Added SET/SHOW XQ STATS.
  o Added SHOW XQ FILTERS.
  o Added ability to split received packet into multiple buffers.
  o Added explicit runt and giant packet processing.
- Fixed bug in user disk size (found by Chaskiel M Grundman).

4.7 PDP-18B

- Fixed DT, RF variable size interaction with restore.
- Fixed MT bug in MTTR.
- Fixed bug in PDP-4 line printer overprinting.
- Fixed bug in PDP-15 memory protect/skip interaction.
- Fixed bug in RF set size routine.
- Increased PTP TIME for PDP-15 operating systems.
- Fixed priorities in PDP-15 API (differs from PDP-9).
- Fixed sign handling in PDP-15 EAE unsigned mul/div (differs from PDP-9).
- Fixed bug in CAF, clears API subsystem.

4.8 PDP-8

- Fixed DT, DF, RF, RX variable size interaction with restore.
- Fixed MT bug in SKTR.
- Fixed bug in DF, RF set size routine.

4.9 HP2100

- Fixed bug in DP (13210A controller only), DQ read status.
- Fixed bug in DP, DQ seek complete.
- Fixed DR drum sizes.
- Fixed DR variable capacity interaction with SAVE/RESTORE.

4.10 GRI

- Fixed bug in SC queue pointer management.

4.11 PDP-10

- Fixed bug in RP read header.

4.12 Ibm1130

- Fixed bugs found by APL 1130.

4.13 Altairz80

- Fixed bug in real-time clock on Windows host.

4.14 1620

- Fixed bug in immediate index add (found by Michael Short).
This commit is contained in:
Bob Supnik
2003-12-31 11:49:00 -08:00
committed by Mark Pizzolato
parent b2101ecdd4
commit 1da2d9452d
140 changed files with 17663 additions and 16338 deletions

View File

@@ -1,6 +1,6 @@
/* pdp8_cpu.c: PDP-8 CPU simulator
Copyright (c) 1993-2003, Robert M Supnik
Copyright (c) 1993-2004, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -25,6 +25,9 @@
cpu central processor
31-Dec-03 RMS Fixed bug in set_cpu_hist
13-Oct-03 RMS Added instruction history
Added TSC8-75 support (from Bernhard Baehr)
12-Mar-03 RMS Added logical name support
04-Oct-02 RMS Revamped device dispatching, added device number support
06-Jan-02 RMS Added device enable/disable routines
@@ -184,12 +187,23 @@
#define PCQ_SIZE 64 /* must be 2**n */
#define PCQ_MASK (PCQ_SIZE - 1)
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC
#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = MA
#define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */
#define UNIT_NOEAE (1 << UNIT_V_NOEAE)
#define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
#define HIST_PC 0x40000000
#define HIST_MIN 64
#define HIST_MAX 65536
struct InstHistory {
int32 pc;
int32 ea;
int16 ir;
int16 opnd;
int16 lac;
int16 mq; };
uint16 M[MAXMEMSIZE] = { 0 }; /* main memory */
int32 saved_LAC = 0; /* saved L'AC */
int32 saved_MQ = 0; /* saved MQ */
@@ -203,6 +217,10 @@ int32 SC = 0; /* EAE shift count */
int32 UB = 0; /* User mode Buffer */
int32 UF = 0; /* User mode Flag */
int32 OSR = 0; /* Switch Register */
int32 tsc_ir = 0; /* TSC8-75 IR */
int32 tsc_pc = 0; /* TSC8-75 PC */
int32 tsc_cdf = 0; /* TSC8-75 CDF flag */
int32 tsc_enb = 0; /* TSC8-75 enabled */
int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
int32 pcq_p = 0; /* PC queue ptr */
REG *pcq_r = NULL; /* PC queue reg ptr */
@@ -211,6 +229,9 @@ int32 int_enable = INT_INIT_ENABLE; /* intr enables */
int32 int_req = 0; /* intr requests */
int32 stop_inst = 0; /* trap on ill inst */
int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat); /* device dispatch */
int32 hst_p = 0; /* history pointer */
int32 hst_lnt = 0; /* history length */
struct InstHistory *hst = NULL; /* instruction history */
extern int32 sim_interval;
extern int32 sim_int_char;
@@ -223,6 +244,8 @@ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);
t_bool build_dev_tab (void);
/* CPU data structures
@@ -275,6 +298,8 @@ MTAB cpu_mod[] = {
{ UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size },
{ UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size },
{ UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "HISTORY", "HISTORY",
&cpu_set_hist, &cpu_show_hist },
{ 0 } };
DEVICE cpu_dev = {
@@ -335,9 +360,8 @@ sim_interval = sim_interval - 1;
major opcode. For IOT, the extra decode points are not useful;
for OPR, only the group flag (IR<3>) is used.
The following macros define the address calculations for data and
jump calculations. Data calculations return a full 15b extended
address, jump calculations a 12b field-relative address.
AND, TAD, ISZ, DCA calculate a full 15b effective address.
JMS, JMP calculate a 12b field-relative effective address.
Autoindex calculations always occur within the same field as the
instruction fetch. The field must exist; otherwise, the instruction
@@ -346,84 +370,103 @@ sim_interval = sim_interval - 1;
Note that MA contains IF'PC.
*/
#define ZERO_PAGE MA = IF | (IR & 0177)
#define CURR_PAGE MA = (MA & 077600) | (IR & 0177)
#define INDIRECT if ((MA & 07770) != 00010) MA = DF | M[MA]; \
else MA = DF | (M[MA] = (M[MA] + 1) & 07777)
if (hst_lnt) { /* history enabled? */
int32 ea;
#define ZERO_PAGE_J MA = IR & 0177
#define CURR_PAGE_J MA = (MA & 007600) | (IR & 0177)
#define INDIRECT_J if ((MA & 07770) != 00010) MA = M[MA]; \
else MA = (M[MA] = (M[MA] + 1) & 07777)
#define CHANGE_FIELD IF = IB; UF = UB; \
int_req = int_req | INT_NO_CIF_PENDING
hst_p = (hst_p + 1); /* next entry */
if (hst_p >= hst_lnt) hst_p = 0;
hst[hst_p].pc = MA | HIST_PC; /* save PC, IR, LAC, MQ */
hst[hst_p].ir = IR;
hst[hst_p].lac = LAC;
hst[hst_p].mq = MQ;
if (IR < 06000) { /* mem ref? */
if (IR & 0200) ea = (MA & 077600) | (IR & 0177);
else ea = IF | (IR & 0177); /* direct addr */
if (IR & 0400) { /* indirect? */
if (IR < 04000) { /* mem operand? */
if ((ea & 07770) != 00010) ea = DF | M[ea];
else ea = DF | ((M[ea] + 1) & 07777); }
else { /* no, jms/jmp */
if ((ea & 07770) != 00010) ea = IB | M[ea];
else ea = IB | ((M[ea] + 1) & 07777); }
}
hst[hst_p].ea = ea; /* save eff addr */
hst[hst_p].opnd = M[ea]; /* save operand */
}
}
switch ((IR >> 7) & 037) { /* decode IR<0:4> */
/* Opcode 0, AND */
case 000: /* AND, dir, zero */
ZERO_PAGE;
MA = IF | (IR & 0177); /* dir addr, page zero */
LAC = LAC & (M[MA] | 010000);
break;
case 001: /* AND, dir, curr */
CURR_PAGE;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
LAC = LAC & (M[MA] | 010000);
break;
case 002: /* AND, indir, zero */
ZERO_PAGE;
INDIRECT;
MA = IF | (IR & 0177); /* dir addr, page zero */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
LAC = LAC & (M[MA] | 010000);
break;
case 003: /* AND, indir, curr */
CURR_PAGE;
INDIRECT;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
LAC = LAC & (M[MA] | 010000);
break;
/* Opcode 1, TAD */
case 004: /* TAD, dir, zero */
ZERO_PAGE;
MA = IF | (IR & 0177); /* dir addr, page zero */
LAC = (LAC + M[MA]) & 017777;
break;
case 005: /* TAD, dir, curr */
CURR_PAGE;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
LAC = (LAC + M[MA]) & 017777;
break;
case 006: /* TAD, indir, zero */
ZERO_PAGE;
INDIRECT;
MA = IF | (IR & 0177); /* dir addr, page zero */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
LAC = (LAC + M[MA]) & 017777;
break;
case 007: /* TAD, indir, curr */
CURR_PAGE;
INDIRECT;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
LAC = (LAC + M[MA]) & 017777;
break;
/* Opcode 2, ISZ */
case 010: /* ISZ, dir, zero */
ZERO_PAGE;
MA = IF | (IR & 0177); /* dir addr, page zero */
M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */
if (MB == 0) PC = (PC + 1) & 07777;
break;
case 011: /* ISZ, dir, curr */
CURR_PAGE;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */
if (MB == 0) PC = (PC + 1) & 07777;
break;
case 012: /* ISZ, indir, zero */
ZERO_PAGE;
INDIRECT;
MA = IF | (IR & 0177); /* dir addr, page zero */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
MB = (M[MA] + 1) & 07777;
if (MEM_ADDR_OK (MA)) M[MA] = MB;
if (MB == 0) PC = (PC + 1) & 07777;
break;
case 013: /* ISZ, indir, curr */
CURR_PAGE;
INDIRECT;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
MB = (M[MA] + 1) & 07777;
if (MEM_ADDR_OK (MA)) M[MA] = MB;
if (MB == 0) PC = (PC + 1) & 07777;
@@ -432,91 +475,178 @@ case 013: /* ISZ, indir, curr */
/* Opcode 3, DCA */
case 014: /* DCA, dir, zero */
ZERO_PAGE;
MA = IF | (IR & 0177); /* dir addr, page zero */
M[MA] = LAC & 07777;
LAC = LAC & 010000;
break;
case 015: /* DCA, dir, curr */
CURR_PAGE;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
M[MA] = LAC & 07777;
LAC = LAC & 010000;
break;
case 016: /* DCA, indir, zero */
ZERO_PAGE;
INDIRECT;
MA = IF | (IR & 0177); /* dir addr, page zero */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777;
LAC = LAC & 010000;
break;
case 017: /* DCA, indir, curr */
CURR_PAGE;
INDIRECT;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777;
LAC = LAC & 010000;
break;
/* Opcode 4, JMS */
/* Opcode 4, JMS. From Bernhard Baehr's description of the TSC8-75:
(In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF
flag is cleared. The address of the JMS instruction is loaded into the ERTB
register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the
target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF,
clearing the interrupt inhibit flag, storing of the return address in the first
word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed
as usual. */
case 020: /* JMS, dir, zero */
ZERO_PAGE_J;
CHANGE_FIELD;
MA = IF | MA;
PCQ_ENTRY;
if (MEM_ADDR_OK (MA)) M[MA] = PC;
MA = IR & 0177; /* dir addr, page zero */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; } /* clear flag */
if (UF && tsc_enb) { /* user mode, TSC enab? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } /* request intr */
else { /* normal */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
MA = IF | MA;
if (MEM_ADDR_OK (MA)) M[MA] = PC; }
PC = (MA + 1) & 07777;
break;
case 021: /* JMS, dir, curr */
CURR_PAGE_J;
CHANGE_FIELD;
MA = IF | MA;
PCQ_ENTRY;
if (MEM_ADDR_OK (MA)) M[MA] = PC;
MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; } /* clear flag */
if (UF && tsc_enb) { /* user mode, TSC enab? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } /* request intr */
else { /* normal */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
MA = IF | MA;
if (MEM_ADDR_OK (MA)) M[MA] = PC; }
PC = (MA + 1) & 07777;
break;
case 022: /* JMS, indir, zero */
ZERO_PAGE;
INDIRECT_J;
CHANGE_FIELD;
MA = IF | MA;
PCQ_ENTRY;
if (MEM_ADDR_OK (MA)) M[MA] = PC;
MA = IF | (IR & 0177); /* dir addr, page zero */
if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */
else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; } /* clear flag */
if (UF && tsc_enb) { /* user mode, TSC enab? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } /* request intr */
else { /* normal */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
MA = IF | MA;
if (MEM_ADDR_OK (MA)) M[MA] = PC; }
PC = (MA + 1) & 07777;
break;
case 023: /* JMS, indir, curr */
CURR_PAGE;
INDIRECT_J;
CHANGE_FIELD;
MA = IF | MA;
PCQ_ENTRY;
if (MEM_ADDR_OK (MA)) M[MA] = PC;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */
else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; } /* clear flag */
if (UF && tsc_enb) { /* user mode, TSC enab? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } /* request intr */
else { /* normal */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
MA = IF | MA;
if (MEM_ADDR_OK (MA)) M[MA] = PC; }
PC = (MA + 1) & 07777;
break;
/* Opcode 5, JMP. From Bernhard Baehr's description of the TSC8-75:
/* Opcode 5, JMP */
(In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF
flag is cleared. The address of the JMP instruction is loaded into the ERTB
register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual
(including the setting of IF, UF and clearing the interrupt inhibit flag). */
case 024: /* JMP, dir, zero */
ZERO_PAGE_J;
CHANGE_FIELD;
PCQ_ENTRY;
MA = IR & 0177; /* dir addr, page zero */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; /* clear flag */
if (tsc_enb) { /* TSC8 enabled? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } } /* request intr */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
PC = MA;
break;
case 025: /* JMP, dir, curr */
CURR_PAGE_J;
CHANGE_FIELD;
PCQ_ENTRY;
MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; /* clear flag */
if (tsc_enb) { /* TSC8 enabled? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } } /* request intr */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
PC = MA;
break;
case 026: /* JMP, indir, zero */
ZERO_PAGE;
INDIRECT_J;
CHANGE_FIELD;
PCQ_ENTRY;
MA = IF | (IR & 0177); /* dir addr, page zero */
if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */
else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; /* clear flag */
if (tsc_enb) { /* TSC8 enabled? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } } /* request intr */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
PC = MA;
break;
case 027: /* JMP, indir, curr */
CURR_PAGE;
INDIRECT_J;
CHANGE_FIELD;
PCQ_ENTRY;
MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */
if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */
else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (UF) { /* user mode? */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; /* clear flag */
if (tsc_enb) { /* TSC8 enabled? */
tsc_pc = (PC - 1) & 07777; /* save PC */
int_req = int_req | INT_TSC; } } /* request intr */
IF = IB; /* change IF */
UF = UB; /* change UF */
int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */
PC = MA;
break;
@@ -601,7 +731,11 @@ case 034:case 035: /* OPR, group 1 */
break; } /* uses address path */
break; /* end group 1 */
/* OPR group 2 */
/* OPR group 2. From Bernhard Baehr's description of the TSC8-75:
(In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with
HLT and OSR: Additional to raising a user mode interrupt, the current OPR
opcode is moved to the ERIOT register and the ECDF flag is cleared. */
case 036:case 037: /* OPR, groups 2, 3 */
if ((IR & 01) == 0) { /* group 2 */
@@ -657,7 +791,10 @@ case 036:case 037: /* OPR, groups 2, 3 */
if ((LAC < 04000) && (LAC != 0)) PC = (PC + 1) & 07777;
break; } /* end switch skips */
if (IR & 0200) LAC = LAC & 010000; /* CLA */
if ((IR & 06) && UF) int_req = int_req | INT_UF;
if ((IR & 06) && UF) { /* user mode? */
int_req = int_req | INT_UF; /* request intr */
tsc_ir = IR; /* save instruction */
tsc_cdf = 0; } /* clear flag */
else {
if (IR & 04) LAC = LAC | OSR; /* OSR */
if (IR & 02) reason = STOP_HALT; } /* HLT */
@@ -745,7 +882,8 @@ case 036:case 037: /* OPR, groups 2, 3 */
case 021: /* mode B: DAD */
if (emode) {
MA = IF | PC;
INDIRECT; /* defer state */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
MQ = MQ + M[MA];
MA = DF | ((MA + 1) & 07777);
LAC = (LAC & 07777) + M[MA] + (MQ >> 12);
@@ -764,7 +902,8 @@ case 036:case 037: /* OPR, groups 2, 3 */
case 022: /* mode B: DST */
if (emode) {
MA = IF | PC;
INDIRECT; /* defer state */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
if (MEM_ADDR_OK (MA)) M[MA] = MQ & 07777;
MA = DF | ((MA + 1) & 07777);
if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777;
@@ -773,7 +912,10 @@ case 036:case 037: /* OPR, groups 2, 3 */
LAC = LAC | SC; /* mode A: SCA then */
case 002: /* MUY */
MA = IF | PC;
if (emode) { INDIRECT; } /* mode B: defer */
if (emode) { /* mode B: defer */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
}
temp = (MQ * M[MA]) + (LAC & 07777);
LAC = (temp >> 12) & 07777;
MQ = temp & 07777;
@@ -788,7 +930,10 @@ case 036:case 037: /* OPR, groups 2, 3 */
LAC = LAC | SC; /* mode A: SCA then */
case 003: /* DVI */
MA = IF | PC;
if (emode) { INDIRECT; } /* mode B: defer */
if (emode) { /* mode B: defer */
if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */
else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */
}
if ((LAC & 07777) >= M[MA]) { /* overflow? */
LAC = LAC | 010000; /* set link */
MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */
@@ -873,11 +1018,18 @@ case 036:case 037: /* OPR, groups 2, 3 */
break; } /* end switch */
break; /* end case 7 */
/* Opcode 6, IOT */
/* Opcode 6, IOT. From Bernhard Baehr's description of the TSC8-75:
(In user mode) Additional to raising a user mode interrupt, the current IOT
opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1),
the ECDF flag is set, otherwise it is cleared. */
case 030:case 031:case 032:case 033: /* IOT */
if (UF) { /* privileged? */
int_req = int_req | INT_UF;
int_req = int_req | INT_UF; /* request intr */
tsc_ir = IR; /* save instruction */
if ((IR & 07707) == 06201) tsc_cdf = 1; /* set/clear flag */
else tsc_cdf = 0;
break; }
device = (IR >> 3) & 077; /* device = IR<3:8> */
pulse = IR & 07; /* pulse = IR<9:11> */
@@ -922,6 +1074,7 @@ case 030:case 031:case 032:case 033: /* IOT */
dev_done = 0;
int_enable = INT_INIT_ENABLE;
LAC = 0;
reset_all (1); /* reset all dev */
break; } /* end switch pulse */
break; /* end case 0 */
@@ -1143,3 +1296,58 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */
} /* end for i */
return FALSE;
}
/* Set history */
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
{
int32 i, lnt;
t_stat r;
if (cptr == NULL) {
for (i = 0; i < hst_lnt; i++) hst[i].pc = 0;
hst_p = 0;
return SCPE_OK; }
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;
hst_p = 0;
if (hst_lnt) {
free (hst);
hst_lnt = 0;
hst = NULL; }
if (lnt) {
hst = calloc (sizeof (struct InstHistory), lnt);
if (hst == NULL) return SCPE_MEM;
hst_lnt = lnt; }
return SCPE_OK;
}
/* Show history */
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
{
int32 l, k, di;
t_value sim_eval;
struct InstHistory *h;
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
UNIT *uptr, int32 sw);
if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */
fprintf (st, "PC L AC MQ ea IR\n\n");
di = hst_p; /* work forward */
for (k = 0; k < hst_lnt; k++) { /* print specified */
h = &hst[(++di) % hst_lnt]; /* entry pointer */
if (h->pc & HIST_PC) { /* instruction? */
l = (h->lac >> 12) & 1; /* link */
fprintf (st, "%05o %o %04o %04o ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq);
if (h->ir < 06000) fprintf (st, "%05o ", h->ea);
else fprintf (st, " ");
sim_eval = h->ir;
if ((fprint_sym (st, h->pc & ADDRMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0)
fprintf (st, "(undefined) %04o", h->ir);
if (h->ir < 04000) fprintf (st, " [%04o]", h->opnd);
fputc ('\n', st); /* end line */
} /* end else instruction */
} /* end for */
return SCPE_OK;
}

View File

@@ -23,6 +23,7 @@
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
13-Oct-03 RMS Added TSC8-75 support
04-Oct-02 RMS Added variable device number support
20-Jan-02 RMS Fixed bug in TTx interrupt enable initialization
25-Nov-01 RMS Added RL8A support
@@ -45,6 +46,7 @@
#define STOP_HALT 2 /* HALT */
#define STOP_IBKPT 3 /* breakpoint */
#define STOP_NOTSTD 4 /* non-std devno */
#define STOP_DTOFF 5 /* DECtape off reel */
/* Memory */
@@ -86,6 +88,7 @@ typedef struct pdp8_dib DIB;
#define DEV_TTI 003 /* console input */
#define DEV_TTO 004 /* console output */
#define DEV_CLK 013 /* clock */
#define DEV_TSC 036
#define DEV_KJ8 040 /* extra terminals */
#define DEV_DF 060 /* DF32 */
#define DEV_RF 060 /* RF08 */
@@ -95,6 +98,7 @@ typedef struct pdp8_dib DIB;
#define DEV_RK 074 /* RK8E */
#define DEV_RX 075 /* RX8E/RX28 */
#define DEV_DTA 076 /* TC08 */
#define DEV_TD8E 077 /* TD8E */
/* Interrupt flags
@@ -143,7 +147,8 @@ typedef struct pdp8_dib DIB;
#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_TSC (INT_V_DIRECT+9) /* TSC8-75 int */
#define INT_V_OVHD (INT_V_DIRECT+10) /* 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 */
@@ -171,6 +176,7 @@ typedef struct pdp8_dib DIB;
#define INT_RL (1 << INT_V_RL)
#define INT_PWR (1 << INT_V_PWR)
#define INT_UF (1 << INT_V_UF)
#define INT_TSC (1 << INT_V_TSC)
#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)

View File

@@ -25,6 +25,7 @@
df DF32 fixed head disk
26-Oct-03 RMS Cleaned up buffer copy code
26-Jul-03 RMS Fixed bug in set size routine
14-Mar-03 RMS Fixed variable platter interaction with save/restore
03-Mar-03 RMS Fixed autosizing
@@ -231,6 +232,7 @@ t_stat df_svc (UNIT *uptr)
{
int32 pa, t, mex;
uint32 da;
int16 *fbuf = uptr->filebuf;
UPDATE_PCELL; /* update photocell */
if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */
@@ -247,13 +249,13 @@ do { if (da >= uptr->capac) { /* nx disk addr? */
M[DF_MA] = (M[DF_MA] + 1) & 07777; /* incr mem addr */
pa = mex | M[DF_MA]; /* add extension */
if (uptr->FUNC == DF_READ) { /* read? */
if (MEM_ADDR_OK (pa)) /* check nxm */
M[pa] = *(((int16 *) uptr->filebuf) + da); }
if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da]; } /* if !nxm, read wd */
else { /* write */
t = (da >> 14) & 07; /* check wr lock */
if ((df_wlk >> t) & 1) df_sta = df_sta | DFS_WLS;
if ((df_wlk >> t) & 1) /* locked? set err */
df_sta = df_sta | DFS_WLS;
else { /* not locked */
*(((int16 *) uptr->filebuf) + da) = M[pa];
fbuf[da] = M[pa]; /* write word */
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 */

View File

@@ -59,6 +59,8 @@ sim/pdp8/ pdp8_defs.h
pdp8_rl.c
pdp8_rx.c
pdp8_sys.c
pdp8_td.c
pdp8_tsc.c
pdp8_tt.c
pdp8_ttx.c
@@ -72,6 +74,7 @@ name(s)
CPU PDP-8/E CPU with 4KW-32KW of memory
- KE8E extended arithmetic element (EAE)
- KM8E memory management and timeshare control
TSC TSC8-75 ETOS operating system timeshare control
PTR,PTP PC8E paper tape reader/punch
TTI,TTO KL8E console terminal
TTIX,TTOX KL8JA additional terminals
@@ -83,6 +86,7 @@ DF DF32/DS32 fixed head disk controller with 1-4 platters
RL RL8A/RL01 cartridge disk controller with four drives
RX RX8E/RX01, RX28/RX02 floppy disk controller with two drives
DT TC08/TU56 DECtape controller with eight drives
TD TD8E/TU56 DECtape controller with two drives
MT TM8E/TU10 magnetic tape controller with eight drives
Most devices can be disabled or enabled, by the commands:
@@ -102,17 +106,28 @@ default is the RF08. To change the disk at device numbers 60-61:
sim> SET DF ENABLED, or enable DF32
sim> SET RL ENABLED enable RL8A
The PDP-8 can only support one of the set {TC08, TD8E} using the default
device numbers, since both use device number 77. The default is the
TC08. To change the DECtape controller to the TD8E:
sim> SET DT DISABLED disable TC08
sim> SET TD ENABLED enable TD8E
Alternately, the device conflict can be eliminated by changing device
numbers:
sim> SET RL DEV=50
sim> SET RL ENA
sim> SET TD DEV=74
sim> SET TD ENA
However, devices can only be BOOTed with their default device numbers.
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 simulator implements several unique stop conditions:
- if an undefined instruction (unimplemented IOT or OPR) is
decoded, and register STOP_INST
- if a simulated DECtape runs off the end of its reel
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
@@ -172,9 +187,38 @@ control registers for the interrupt system.
STOP_INST 1 stop on undefined instruction
WRU 8 interrupt character
2.2 Programmed I/O Devices
The CPU can maintain a history of the most recently executed instructions.
This is controlled by the SET CPU HISTORY and SHOW CPU HISTORY commands:
2.2.1 PC8E Paper Tape Reader (PTR)
SET CPU HISTORY clear history buffer
SET CPU HISTORY=0 disable history
SET CPU HISTORY=n enable history, display length = n
SHOW CPU HISTORY print CPU history
The maximum length for the history is 65536 entries.
2.2 TSC8-75 ETOS Timeshare Control (TSC)
ETOS is a timeshared operating system for the PDP-8, providing multiple
virtual OS/8 environments for up to 32 users. It requires a special
timeshare control option, the TSC8-75. The TSC8-75 is normally disabled;
to run ETOS, it must be enabled with the command:
SET TSC ENABLED
The TSC8-75 implements these registers:
IR most recently trapped instruction
PC PC of most recently trapped instruction
CDF 1 if trapped instruction is CDF, 0 otherwise
ENB interrupt enable flag
INT interrupt pending flag
Except for operation of ETOS, the TSC8-75 should be left disabled.
2.3 Programmed I/O Devices
2.3.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,
@@ -207,7 +251,7 @@ Error handling is as follows:
OS I/O error x report error and stop
2.2.2 PC8E Paper Tape Punch (PTP)
2.3.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
@@ -234,7 +278,7 @@ Error handling is as follows:
OS I/O error x report error and stop
2.2.3 KL8E Terminal Input (TTI)
2.3.3 KL8E Terminal Input (TTI)
The terminal interfaces (TTI, TTO) can be set to one of three modes:
KSR, 7B, or 8B. In KSR mode, lower case input and output characters
@@ -261,7 +305,7 @@ to simulate typing ^C:
SET TTI CTRL-C
2.2.4 KL8E Terminal Output (TTO)
2.3.4 KL8E Terminal Output (TTO)
The terminal output (TTO) writes to the simulator console window. It
implements these registers:
@@ -275,7 +319,7 @@ implements these registers:
POS 32 number of characters output
TIME 24 time from I/O initiation to interrupt
2.2.5 LE8E Line Printer (LPT)
2.3.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,
@@ -302,7 +346,7 @@ Error handling is as follows:
OS I/O error x report error and stop
2.2.6 DK8E Line-Frequency Clock (CLK)
2.3.6 DK8E Line-Frequency Clock (CLK)
The real-time clock (CLK) frequency can be adjusted as follows:
@@ -323,7 +367,7 @@ The clock implements these registers:
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 (TTIX, TTOX)
2.3.7 KL8JA Additional Terminals (TTIX, TTOX)
The additional terminals consist of two independent devices, TTIX and
TTOX. The entire set is modelled as a terminal multiplexor, with TTIX
@@ -378,9 +422,58 @@ The output device (TTOX) implements these registers:
The additional terminals do not support save and restore. All open
connections are lost when the simulator shuts down or TTIX is detached.
2.3 Moving Head Disks
2.3.8 TD8E/TU56 DECtape (TD)
2.3.1 RK8E Cartridge Disk (RK)
The TD8E is a programmed I/O, non-interrupt controller, supporting two
DECtape drives (0 and 1). The TD8E simulator puts a high burden on the
host processor, because tape activity is simulated a line (3b) at a time.
Unless the PDP-8 software requires the TD8E, the TC08 should be used
to simulate DECtapes. The TD8E is disabled by default.
TD8E options include the ability to make units write enabled or write
locked.
SET DTn LOCKED set unit n write locked
SET DTn WRITEENABLED set unit n write enabled
Units can also be set ONLINE or OFFLINE. The TD8E supports the BOOT command.
The TD8E supports supports PDP-8 format, PDP-11 format, and 18b format
DECtape images. ATTACH tries to determine the tape format from the DECtape
image; the user can force a particular format with switches:
-r PDP-8 format
-s PDP-11 format
-t 18b format
The TD8E controller is a data-only simulator; the timing and mark
track, and block header and trailer, are not stored. Thus, read always
produces standard values for header and trailer words, and write throws
header and trailer words into the bit bucket.
The TD8E controller implements these registers:
name size comments
TDCMD 4 command register
TDDAT 12 data register
TDMTK 6 mark track register
TDSLF 1 single line flag
TDQLF 1 quad line flag
TDTME 1 timing error flag
TDQL 2 quad line counter
LTIME 31 time between lines
DCTIME 31 time to decelerate to a full stop
POS[0:7] 32 position, in lines, units 0-7
STATT[0:7] 18 unit state, units 0-7
The LTIME parameter should not be changed, or OS/8 may fail to run
correctly. The DCTIME parameter should always be at least 100 times
greater than LTIME. Acceleration time is 75% of deceleration time.
2.4 Moving Head Disks
2.4.1 RK8E Cartridge Disk (RK)
RK8E options include the ability to make units write enabled or write locked:
@@ -414,7 +507,7 @@ Error handling is as follows:
OS I/O error x report error and stop
2.3.2 RL8A Cartridge Disk (RL)
2.4.2 RL8A Cartridge Disk (RL)
RL8A options include the ability to make units write enabled or write locked:
@@ -455,7 +548,7 @@ Error handling is as follows:
OS I/O error x report error and stop
2.4 RX8E/RX01, RX28/RX02 Floppy Disk (RX)
2.5 RX8E/RX01, RX28/RX02 Floppy Disk (RX)
The RX can be configured as an RX8E with two RX01 drives, or an RX28 with
two RX02 drives:
@@ -513,12 +606,12 @@ Error handling is as follows:
RX01 and RX02 data files are buffered in memory; therefore, end of file
and OS I/O errors cannot occur.
2.5 Fixed Head Disks
2.6 Fixed Head Disks
Either the RF08 or the DF32 can be present in a configuration, but
not both, with default device addressing.
2.5.1 RF08/RS08 Fixed Head Disk (RF)
2.6.1 RF08/RS08 Fixed Head Disk (RF)
RF08 options include the ability to set the number of platters to a
fixed value between 1 and 4, or to autosize the number of platters
@@ -564,7 +657,7 @@ Error handling is as follows:
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)
2.6.2 DF32/DS32 Fixed Head Disk (RF)
DF32 options include the ability to set the number of platters to a
fixed value between 1 and 4, or to autosize the number of platters
@@ -610,10 +703,10 @@ Error handling is as follows:
DF32 data files are buffered in memory; therefore, end of file and OS
I/O errors cannot occur.
2.6 TC08/TU56 DECtape (DT)
2.7 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
TC08 options include the ability to make units write enabled or write
locked.
SET DTn LOCKED set unit n write locked
@@ -629,7 +722,7 @@ image; the user can force a particular format with switches:
-s PDP-11 format
-t 18b format
The DECtape controller is a data-only simulator; the timing and mark
The TC08 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
@@ -648,7 +741,6 @@ The DECtape controller implements these registers:
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] 32 position, in lines, units 0-7
@@ -659,10 +751,11 @@ 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
- DCTIME needs to be at least 100 times LTIME
2.7 TM8E Magnetic Tape (MT)
Acceleration time is set to 75% of deceleration time.
2.8 TM8E Magnetic Tape (MT)
Magnetic tape options include the ability to make units write enabled or
or write locked.
@@ -700,7 +793,7 @@ Error handling is as follows:
OS I/O error parity error; if STOP_IOE, stop
2.8 Symbolic Display and Input
2.9 Symbolic Display and Input
The PDP-8 simulator implements symbolic display and input. Display is
controlled by command line switches:

View File

@@ -25,6 +25,7 @@
dt TC08/TU56 DECtape
18-Oct-03 RMS Fixed bugs in read all, tightened timing
25-Apr-03 RMS Revised for extended file support
14-Mar-03 RMS Fixed sizing interaction with save/restore
17-Oct-02 RMS Fixed bug in end of reel logic
@@ -52,13 +53,16 @@
When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format.
DECtape motion is measured in 3b lines. Time between lines is 33.33us.
Tape density is nominally 300 lines per inch. The format of a DECtape is
Tape density is nominally 300 lines per inch. The format of a DECtape (as
taken from the TD8E formatter) is:
reverse end zone 36000 lines ~ 10 feet
reverse end zone 8192 reverse end zone codes ~ 10 feet
reverse buffer 200 interblock codes
block 0
:
block n
forward end zone 36000 lines ~ 10 feet
forward buffer 200 interblock codes
forward end zone 8192 forward end zone codes ~ 10 feet
A block consists of five 18b header words, a tape-specific number of data
words, and five 18b trailer words. All systems except the PDP-8 use a
@@ -73,14 +77,14 @@
header word 0 0
header word 1 block number (for forward reads)
header words 2,3 0
header word 4 0
header word 4 checksum (for reverse reads)
:
trailer word 4 checksum
trailer word 4 checksum (for forward reads)
trailer words 3,2 0
trailer word 1 block number (for reverse reads)
trailer word 0 0
Write all writes only the data words and dumps the interblock words in the
Write all writes only the data words and dumps the non-data words in the
bit bucket.
*/
@@ -101,10 +105,15 @@
/* System independent DECtape constants */
#define DT_EZLIN 36000 /* end zone length */
#define DT_HTLIN 30 /* header/trailer lines */
#define DT_BLKLN 6 /* blk no line in h/t */
#define DT_CSMLN 24 /* checksum line in h/t */
#define DT_LPERMC 6 /* lines per mark track */
#define DT_BLKWD 1 /* blk no word in h/t */
#define DT_CSMWD 4 /* checksum word in h/t */
#define DT_HTWRD 5 /* header/trailer words */
#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */
#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */
#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */
#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */
#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */
/* 16b, 18b, 36b DECtape constants */
@@ -257,8 +266,7 @@ extern int32 sim_switches;
int32 dtsa = 0; /* status A */
int32 dtsb = 0; /* status B */
int32 dt_ltime = 12; /* interline time */
int32 dt_actime = 54000; /* accel time */
int32 dt_dctime = 72000; /* decel time */
int32 dt_dctime = 40000; /* decel time */
int32 dt_substate = 0;
int32 dt_log = 0; /* debug */
int32 dt_logblk = 0;
@@ -319,9 +327,8 @@ REG dt_reg[] = {
{ FLDATA (ERF, dtsb, DTB_V_ERF) },
{ ORDATA (WC, M[DT_WC], 18) },
{ ORDATA (CA, M[DT_CA], 18) },
{ DRDATA (LTIME, dt_ltime, 31), REG_NZ },
{ DRDATA (ACTIME, dt_actime, 31), REG_NZ },
{ DRDATA (DCTIME, dt_dctime, 31), REG_NZ },
{ DRDATA (LTIME, dt_ltime, 31), REG_NZ | PV_LEFT },
{ DRDATA (DCTIME, dt_dctime, 31), REG_NZ | PV_LEFT },
{ ORDATA (SUBSTATE, dt_substate, 2) },
{ ORDATA (LOG, dt_log, 4), REG_HIDDEN },
{ DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN },
@@ -451,7 +458,7 @@ if ((prev_mving | new_mving) == 0) return; /* stop to stop */
if (new_mving & ~prev_mving) { /* start? */
if (dt_setpos (uptr)) return; /* update pos */
sim_cancel (uptr); /* stop current */
sim_activate (uptr, dt_actime); /* schedule accel */
sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */
DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */
DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */
return; }
@@ -477,7 +484,7 @@ if (prev_dir ^ new_dir) { /* dir chg? */
if (prev_mot < DTS_ACCF) { /* not accel/at speed? */
if (dt_setpos (uptr)) return; /* update pos */
sim_cancel (uptr); /* cancel cur */
sim_activate (uptr, dt_actime); /* schedule accel */
sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */
DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */
DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */
return; }
@@ -598,11 +605,13 @@ case DTS_STOP: /* stop */
delta = 0;
break;
case DTS_DECF: /* slowing */
ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime;
ulin = ut / (uint32) dt_ltime;
udelt = dt_dctime / dt_ltime;
delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
break;
case DTS_ACCF: /* accelerating */
ulin = ut / (uint32) dt_ltime; udelt = dt_actime / dt_ltime;
ulin = ut / (uint32) dt_ltime;
udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime;
delta = (ulin * ulin) / (2 * udelt);
break;
case DTS_ATSF: /* at speed */
@@ -631,7 +640,7 @@ t_stat dt_svc (UNIT *uptr)
int32 mot = DTS_GETMOT (uptr->STATE);
int32 dir = mot & DTS_DIR;
int32 fnc = DTS_GETFNC (uptr->STATE);
int16 *bptr = uptr->filebuf;
int16 *fbuf = uptr->filebuf;
int32 unum = uptr - dt_dev.units;
int32 blk, wrd, ma, relpos, dat;
uint32 ba;
@@ -645,10 +654,10 @@ uint32 ba;
switch (mot) {
case DTS_DECF: case DTS_DECR: /* decelerating */
if (dt_setpos (uptr)) return SCPE_OK; /* update pos */
if (dt_setpos (uptr)) return STOP_DTOFF; /* update pos */
uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */
if (uptr->STATE) /* not stopped? */
sim_activate (uptr, dt_actime); /* must be reversing */
sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */
return SCPE_OK;
case DTS_ACCF: case DTS_ACCR: /* accelerating */
dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */
@@ -666,7 +675,7 @@ default: /* other */
Off reel - detach unit (it must be deselected)
*/
if (dt_setpos (uptr)) return SCPE_OK; /* update pos */
if (dt_setpos (uptr)) return STOP_DTOFF; /* update pos */
if (DT_QEZ (uptr)) { /* in end zone? */
dt_seterr (uptr, DTB_END); /* end zone error */
return SCPE_OK; }
@@ -721,7 +730,7 @@ case FNC_READ: /* read */
M[DT_CA] = (M[DT_CA] + 1) & 07777;
ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */
ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */
dat = bptr[ba]; /* get tape word */
dat = fbuf[ba]; /* get tape word */
if (dir) dat = dt_comobv (dat); /* rev? comp obv */
if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */
if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */
@@ -773,7 +782,7 @@ case FNC_WRIT: /* write */
ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */
dat = dt_substate? 0: M[ma]; /* get word */
if (dir) dat = dt_comobv (dat); /* rev? comp obv */
bptr[ba] = dat; /* write word */
fbuf[ba] = dat; /* write word */
if (ba >= uptr->hwmark) uptr->hwmark = ba + 1;
if (M[DT_WC] == 0) dt_substate = DTO_WCO;
if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */
@@ -809,7 +818,7 @@ case FNC_RALL:
(relpos < (DTU_LPERB (uptr) - DT_HTLIN))) {
wrd = DT_LIN2WD (uptr->pos, uptr);
ba = (blk * DTU_BSIZE (uptr)) + wrd;
dat = bptr[ba]; /* get tape word */
dat = fbuf[ba]; /* get tape word */
if (dir) dat = dt_comobv (dat); } /* rev? comp obv */
else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */
sim_activate (uptr, DT_WSIZE * dt_ltime);
@@ -845,7 +854,7 @@ case FNC_WALL:
if (dir) dat = dt_comobv (dat);
wrd = DT_LIN2WD (uptr->pos, uptr);
ba = (blk * DTU_BSIZE (uptr)) + wrd;
bptr[ba] = dat; /* write word */
fbuf[ba] = dat; /* write word */
if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; }
/* /* ignore hdr */
sim_activate (uptr, DT_WSIZE * dt_ltime);
@@ -872,7 +881,7 @@ return SCPE_OK;
Word Word Content Word Word Content
(abs) (rel) (abs) (rel)
137 8 rev csm'00 6 6 fwd csm'00
137 8 fwd csm'00 6 6 rev csm'00
138 9 0000 5 5 0000
139 10 0000 4 4 0000
140 11 0000 3 3 0000
@@ -886,7 +895,7 @@ return SCPE_OK;
4 4 0000 139 10 0000
5 5 0000 138 9 0000
6 6 0000 137 8 0000
7 7 00'fwd csm 136 7 00'rev csm
7 7 rev csm 136 7 00'fwd csm
*/
int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir)
@@ -894,8 +903,8 @@ int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir)
if (relpos >= DT_HTLIN) relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr));
if (dir) { /* reverse */
switch (relpos / DT_WSIZE) {
case 6: /* fwd csum */
return (dt_comobv (dt_csum (uptr, blk)));
case 6: /* rev csm */
return 077;
case 2: /* lo fwd blk */
return dt_comobv ((blk & 077) << 6);
case 1: /* hi fwd blk */
@@ -904,11 +913,13 @@ if (dir) { /* reverse */
return (blk >> 6) & 07777;
case 11: /* lo rev blk */
return ((blk & 077) << 6);
case 7: /* fwd csum */
return (dt_comobv (dt_csum (uptr, blk)) << 6);
default: /* others */
return 077; } }
return 07777; } }
else { /* forward */
switch (relpos / DT_WSIZE) {
case 8: /* rev csum */
case 8: /* fwd csum */
return (dt_csum (uptr, blk) << 6);
case 12: /* lo rev blk */
return dt_comobv ((blk & 077) << 6);
@@ -918,6 +929,8 @@ else { /* forward */
return ((blk >> 6) & 07777);
case 3: /* lo fwd blk */
return ((blk & 077) << 6);
case 7: /* rev csum */
return 077;
default: /* others */
break; } }
return 0;
@@ -968,13 +981,13 @@ return dat;
int32 dt_csum (UNIT *uptr, int32 blk)
{
int16 *bptr = uptr->filebuf;
int16 *fbuf = uptr->filebuf;
int32 ba = blk * DTU_BSIZE (uptr);
int32 i, csum, wrd;
csum = 077; /* init csum */
for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */
wrd = bptr[ba + i] ^ 07777; /* get ~word */
wrd = fbuf[ba + i] ^ 07777; /* get ~word */
csum = csum ^ (wrd >> 6) ^ wrd; }
return (csum & 077);
}
@@ -1061,7 +1074,7 @@ return SCPE_OK;
t_stat dt_attach (UNIT *uptr, char *cptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *bptr;
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k;
int32 u = uptr - dt_dev.units;
t_stat r;
@@ -1086,7 +1099,7 @@ uptr->filebuf = calloc (uptr->capac, sizeof (int16));
if (uptr->filebuf == NULL) { /* can't alloc? */
detach_unit (uptr);
return SCPE_MEM; }
bptr = uptr->filebuf; /* file buffer */
fbuf = uptr->filebuf; /* file buffer */
printf ("%s%d: ", sim_dname (&dt_dev), u);
if (uptr->flags & UNIT_8FMT) printf ("12b format");
else if (uptr->flags & UNIT_11FMT) printf ("16b format");
@@ -1104,10 +1117,10 @@ else { /* 16b/18b */
if (k == 0) break;
for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0;
for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */
bptr[ba] = (pdp18b[k] >> 6) & 07777;
bptr[ba + 1] = ((pdp18b[k] & 077) << 6) |
fbuf[ba] = (pdp18b[k] >> 6) & 07777;
fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |
((pdp18b[k + 1] >> 12) & 077);
bptr[ba + 2] = pdp18b[k + 1] & 07777;
fbuf[ba + 2] = pdp18b[k + 1] & 07777;
ba = ba + 3; } /* end blk loop */
} /* end file loop */
uptr->hwmark = ba; } /* end else */
@@ -1128,19 +1141,19 @@ return SCPE_OK;
t_stat dt_detach (UNIT* uptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *bptr;
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k;
int32 u = uptr - dt_dev.units;
uint32 ba;
if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;
if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */
if (sim_is_active (uptr)) {
sim_cancel (uptr);
if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) {
dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF;
DT_UPDINT; }
uptr->STATE = uptr->pos = 0; }
bptr = uptr->filebuf; /* file buffer */
fbuf = uptr->filebuf; /* file buffer */
if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */
printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u);
rewind (uptr->fileref); /* start of file */
@@ -1150,10 +1163,10 @@ if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */
else { /* 16b/18b */
for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */
for (k = 0; k < D18_NBSIZE; k = k + 2) {
pdp18b[k] = ((uint32) (bptr[ba] & 07777) << 6) |
((uint32) (bptr[ba + 1] >> 6) & 077);
pdp18b[k + 1] = ((uint32) (bptr[ba + 1] & 077) << 12) |
((uint32) (bptr[ba + 2] & 07777));
pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |
((uint32) (fbuf[ba + 1] >> 6) & 077);
pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |
((uint32) (fbuf[ba + 2] & 07777));
ba = ba + 3; } /* end loop blk */
if (uptr->flags & UNIT_11FMT) { /* 16b? */
for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i];

View File

@@ -25,6 +25,7 @@
rf RF08 fixed head disk
26-Oct-03 RMS Cleaned up buffer copy code
26-Jul-03 RMS Fixed bug in set size routine
14-Mar-03 RMS Fixed variable platter interaction with save/restore
03-Mar-03 RMS Fixed autosizing
@@ -274,6 +275,7 @@ return AC;
t_stat rf_svc (UNIT *uptr)
{
int32 pa, t, mex;
int16 *fbuf = uptr->filebuf;
UPDATE_PCELL; /* update photocell */
if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */
@@ -290,13 +292,14 @@ do { if ((uint32) rf_da >= rf_unit.capac) { /* disk overflow? */
M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */
pa = mex | M[RF_MA]; /* add extension */
if (uptr->FUNC == RF_READ) { /* read? */
if (MEM_ADDR_OK (pa)) /* check nxm */
M[pa] = *(((int16 *) uptr->filebuf) + rf_da); }
if (MEM_ADDR_OK (pa)) /* if !nxm */
M[pa] = fbuf[rf_da]; } /* read word */
else { /* write */
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 ((rf_wlk >> t) & 1) /* write locked? */
rf_sta = rf_sta | RFS_WLS;
else { /* not locked */
fbuf[rf_da] = M[pa]; /* write word */
if (((uint32) 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 */

View File

@@ -25,6 +25,8 @@
rx RX8E/RX01, RX28/RX02 floppy disk
05-Nov-03 RMS Fixed bug in RX28 read status (found by Charles Dickman)
26-Oct-03 RMS Cleaned up buffer copy code, fixed double density write
25-Apr-03 RMS Revised for extended file support
14-Mar-03 RMS Fixed variable size interaction with save/restore
03-Mar-03 RMS Fixed autosizing
@@ -129,7 +131,7 @@ int32 rx_swait = 10; /* seek, per track */
int32 rx_xwait = 1; /* tr set time */
int32 rx_stopioe = 0; /* stop on error */
uint8 rx_buf[RX2_NUMBY] = { 0 }; /* sector buffer */
static int32 bptr = 0; /* buffer pointer */
int32 rx_bptr = 0; /* buffer pointer */
DEVICE rx_dev;
int32 rx (int32 IR, int32 AC);
@@ -167,7 +169,7 @@ REG rx_reg[] = {
{ ORDATA (RXTA, rx_track, 8) },
{ ORDATA (RXSA, rx_sector, 8) },
{ DRDATA (STAPTR, rx_state, 4), REG_RO },
{ DRDATA (BUFPTR, bptr, 8) },
{ DRDATA (BUFPTR, rx_bptr, 8) },
{ FLDATA (TR, rx_tr, 0) },
{ FLDATA (ERR, rx_err, 0) },
{ FLDATA (DONE, dev_done, INT_V_RX) },
@@ -226,7 +228,7 @@ case 1: /* LCD */
dev_done = dev_done & ~INT_RX; /* clear done, int */
int_req = int_req & ~INT_RX;
rx_tr = rx_err = 0; /* clear flags */
bptr = 0; /* clear buf pointer */
rx_bptr = 0; /* clear buf pointer */
if (rx_28 && (AC & RXCS_MODE)) { /* RX28 8b mode? */
rx_dbr = rx_csr = AC & 0377; /* save 8b */
rx_tr = 1; /* xfer is ready */
@@ -287,9 +289,11 @@ switch (RXCS_GETFNC (rx_csr)) { /* decode command */
case RXCS_FILL:
rx_state = FILL; /* state = fill */
rx_tr = 1; /* xfer is ready */
rx_esr = rx_esr & RXES_ID; /* clear errors */
break;
case RXCS_EMPTY:
rx_state = EMPTY; /* state = empty */
rx_esr = rx_esr & RXES_ID; /* clear errors */
sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */
break;
case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL:
@@ -301,6 +305,7 @@ case RXCS_SDEN:
if (rx_28) { /* RX28? */
rx_state = SDCNF; /* state = get conf */
rx_tr = 1; /* xfer is ready */
rx_esr = rx_esr & RXES_ID; /* clear errors */
break; } /* else fall thru */
default:
rx_state = CMD_COMPLETE; /* state = cmd compl */
@@ -315,10 +320,10 @@ return;
RWDS Save sector, set TR, set RWDT
RWDT Save track, set RWXFR
RWXFR Read/write buffer
FILL copy dbr to rx_buf[bptr], advance ptr
if bptr > max, finish command, else set tr
EMPTY if bptr > max, finish command, else
copy rx_buf[bptr] to dbr, advance ptr, set tr
FILL copy dbr to rx_buf[rx_bptr], advance ptr
if rx_bptr > max, finish command, else set tr
EMPTY if rx_bptr > max, finish command, else
copy rx_buf[rx_bptr] 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
@@ -329,12 +334,13 @@ return;
t_stat rx_svc (UNIT *uptr)
{
int32 i, func, byptr, bps, wps;
int8 *fbuf = uptr->filebuf;
uint32 da;
#define PTR12(x) (((x) + (x) + (x)) >> 1)
if (rx_28 && (uptr->flags & UNIT_DEN))
bps = RX2_NUMBY;
else bps = RX_NUMBY;
if (rx_28 && (uptr->flags & UNIT_DEN)) /* RX28 and double density? */
bps = RX2_NUMBY; /* double bytes/sector */
else bps = RX_NUMBY; /* RX8E, normal count */
wps = bps / 2;
func = RXCS_GETFNC (rx_csr); /* get function */
switch (rx_state) { /* case on state */
@@ -344,40 +350,40 @@ case IDLE: /* idle */
case EMPTY: /* empty buffer */
if (rx_csr & RXCS_MODE) { /* 8b xfer? */
if (bptr >= bps) { /* done? */
if (rx_bptr >= bps) { /* done? */
rx_done (0, 0); /* set done */
break; } /* and exit */
rx_dbr = rx_buf[bptr]; } /* else get data */
rx_dbr = rx_buf[rx_bptr]; } /* else get data */
else {
byptr = PTR12 (bptr); /* 12b xfer */
if (bptr >= wps) { /* done? */
byptr = PTR12 (rx_bptr); /* 12b xfer */
if (rx_bptr >= wps) { /* done? */
rx_done (0, 0); /* set done */
break; } /* and exit */
rx_dbr = (bptr & 1)? /* get data */
rx_dbr = (rx_bptr & 1)? /* get data */
((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]:
(rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017); }
bptr = bptr + 1;
rx_bptr = rx_bptr + 1;
rx_tr = 1;
break;
case FILL: /* fill buffer */
if (rx_csr & RXCS_MODE) { /* 8b xfer? */
rx_buf[bptr] = rx_dbr; /* fill buffer */
bptr = bptr + 1;
if (bptr < bps) rx_tr = 1; /* if more, set xfer */
rx_buf[rx_bptr] = rx_dbr; /* fill buffer */
rx_bptr = rx_bptr + 1;
if (rx_bptr < bps) rx_tr = 1; /* if more, set xfer */
else rx_done (0, 0); } /* else done */
else {
byptr = PTR12 (bptr); /* 12b xfer */
if (bptr & 1) { /* odd or even? */
byptr = PTR12 (rx_bptr); /* 12b xfer */
if (rx_bptr & 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; }
bptr = bptr + 1;
if (bptr < wps) rx_tr = 1; /* if more, set xfer */
rx_bptr = rx_bptr + 1;
if (rx_bptr < wps) rx_tr = 1; /* if more, set xfer */
else {
for (i = PTR12 (RX_NUMWD); i < RX_NUMBY; i++)
for (i = PTR12 (wps); i < bps; i++)
rx_buf[i] = 0; /* else fill sector */
rx_done (0, 0); } } /* set done */
break;
@@ -412,15 +418,13 @@ case RWXFR: /* transfer */
da = CALC_DA (rx_track, rx_sector, bps); /* get disk address */
if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */
if (func == RXCS_READ) { /* read? */
for (i = 0; i < bps; i++)
rx_buf[i] = *(((int8 *) uptr->filebuf) + da + i); }
for (i = 0; i < bps; i++) rx_buf[i] = fbuf[da + i]; }
else { /* write */
if (uptr->flags & UNIT_WPRT) { /* locked? */
rx_done (0, 0100); /* done, error */
break; }
for (i = 0; i < RX_NUMBY; i++) /* write */
*(((int8 *) uptr->filebuf) + da + i) = rx_buf[i];
da = da + RX_NUMBY;
for (i = 0; i < bps; i++) fbuf[da + i] = rx_buf[i];
da = da + bps;
if (da > uptr->hwmark) uptr->hwmark = da; }
rx_done (0, 0); /* done */
break;
@@ -433,8 +437,7 @@ case SDCNF: /* confirm set density */
sim_activate (uptr, rx_cwait * 100); /* schedule operation */
break;
case SDXFR: /* erase disk */
for (i = 0; i < (int32) uptr->capac; i++)
*(((int8 *) uptr->filebuf) + i) = 0;
for (i = 0; i < (int32) uptr->capac; i++) fbuf[i] = 0;
uptr->hwmark = uptr->capac;
if (rx_csr & RXCS_DEN) uptr->flags = uptr->flags | UNIT_DEN;
else uptr->flags = uptr->flags & ~UNIT_DEN;
@@ -445,7 +448,13 @@ case CMD_COMPLETE: /* command complete */
if (func == RXCS_ECODE) { /* read ecode? */
rx_dbr = rx_ecode; /* set dbr */
rx_done (0, -1); } /* don't update */
else rx_done (0, 0);
else if (rx_28) { /* no, read sta; RX28? */
rx_esr = rx_esr & ~RXES_DERR; /* assume dens match */
if (((uptr->flags & UNIT_DEN) != 0) ^ /* densities mismatch? */
((rx_csr & RXCS_DEN) != 0))
rx_done (RXES_DERR, 0240); /* yes, error */
else rx_done (0, 0); } /* no, ok */
else rx_done (0, 0); /* RX8E status */
break;
case INIT_COMPLETE: /* init complete */
@@ -456,7 +465,7 @@ case INIT_COMPLETE: /* init complete */
break; }
da = CALC_DA (1, 1, bps); /* track 1, sector 1 */
for (i = 0; i < bps; i++) /* read sector */
rx_buf[i] = *(((int8 *) uptr->filebuf) + da + i);
rx_buf[i] = fbuf[da + i];
rx_done (RXES_ID, 0); /* set done */
if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020;
break; } /* end case state */
@@ -475,7 +484,7 @@ rx_state = IDLE; /* now idle */
dev_done = dev_done | INT_RX; /* set done */
int_req = INT_UPDATE; /* update ints */
rx_esr = (rx_esr | esr_flags) & ~(RXES_DRDY|RXES_RX02|RXES_DEN);
if (rx_28) rx_esr = rx_esr | RXES_RX02; /* update estat */
if (rx_28) rx_esr = rx_esr | RXES_RX02; /* RX28? */
if (rx_unit[drv].flags & UNIT_ATT) { /* update drv rdy */
rx_esr = rx_esr | RXES_DRDY;
if (rx_unit[drv].flags & UNIT_DEN) /* update density */

View File

@@ -23,6 +23,7 @@
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
17-Oct-03 RMS Added TSC8-75, TD8E support, DECtape off reel message
25-Apr-03 RMS Revised for extended file support
30-Dec-01 RMS Revised for new TTX
26-Nov-01 RMS Added RL8A support
@@ -43,13 +44,15 @@
extern DEVICE cpu_dev;
extern UNIT cpu_unit;
extern DEVICE tsc_dev;
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 dt_dev, td_dev;
extern DEVICE mt_dev;
extern DEVICE ttix_dev, ttox_dev;
extern REG cpu_reg[];
extern uint16 M[];
@@ -74,14 +77,23 @@ int32 sim_emax = 4;
DEVICE *sim_devices[] = {
&cpu_dev,
&ptr_dev, &ptp_dev,
&tti_dev, &tto_dev,
&ttix_dev, &ttox_dev,
&clk_dev, &lpt_dev,
&rk_dev, &rl_dev,
&tsc_dev,
&ptr_dev,
&ptp_dev,
&tti_dev,
&tto_dev,
&ttix_dev,
&ttox_dev,
&clk_dev,
&lpt_dev,
&rk_dev,
&rl_dev,
&rx_dev,
&df_dev, &rf_dev,
&dt_dev, &mt_dev,
&df_dev,
&rf_dev,
&dt_dev,
&td_dev,
&mt_dev,
NULL };
const char *sim_stop_messages[] = {
@@ -89,7 +101,8 @@ const char *sim_stop_messages[] = {
"Unimplemented instruction",
"HALT instruction",
"Breakpoint",
"Non-standard device number" };
"Non-standard device number",
"DECtape off reel" };
/* Binary loader
@@ -230,6 +243,8 @@ static const char *opcode[] = {
"RLCB", "RLSA", "RLWC",
"RRER", "RRWC", "RRCA", "RRCB",
"RRSA", "RRSI", "RLSE",
"ETDS", "ESKP", "ECTF", "ECDF",
"ERTB", "ESME", "ERIOT", "ETEN",
"CDF", "CIF", "CIF CDF",
"AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT",
@@ -287,6 +302,8 @@ static const int32 opc_val[] = {
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,
06360+I_NPN, 06361+I_NPN, 06362+I_NPN, 06363+I_NPN,
06364+I_NPN, 06365+I_NPN, 06366+I_NPN, 06367+I_NPN,
06201+I_FLD, 06202+I_FLD, 06203+I_FLD,
00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF,
@@ -411,7 +428,7 @@ for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
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 */
break; } /* end case */
return SCPE_OK; } /* end if */
} /* end for */
return SCPE_ARG;

847
PDP8/pdp8_td.c Normal file
View File

@@ -0,0 +1,847 @@
/* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator
Copyright (c) 1993-2003, 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 module was inspired by Gerold Pauler's TD8E simulator for Doug Jones'
PDP8 simulator but tracks the hardware implementation more closely.
td TD8E/TU56 DECtape
PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words.
Three file formats are supported:
18b/36b 256 words per block [256 x 18b]
16b 256 words per block [256 x 16b]
12b 129 words per block [129 x 12b]
When a 16b or 18/36b DECtape file is read in, it is converted to 12b format.
DECtape motion is measured in 3b lines. Time between lines is 33.33us.
Tape density is nominally 300 lines per inch. The format of a DECtape (as
taken from the TD8E formatter) is:
reverse end zone 8192 reverse end zone codes ~ 10 feet
reverse buffer 200 interblock codes
block 0
:
block n
forward buffer 200 interblock codes
forward end zone 8192 forward end zone codes ~ 10 feet
A block consists of five 18b header words, a tape-specific number of data
words, and five 18b trailer words. All systems except the PDP-8 use a
standard block length of 256 words; the PDP-8 uses a standard block length
of 86 words (x 18b = 129 words x 12b).
Because a DECtape file only contains data, the simulator cannot support
write timing and mark track and can only do a limited implementation
of non-data words. Read assumes that the tape has been conventionally
written forward:
header word 0 0
header word 1 block number (for forward reads)
header words 2,3 0
header word 4 checksum (for reverse reads)
:
trailer word 4 checksum (for forward reads)
trailer words 3,2 0
trailer word 1 block number (for reverse reads)
trailer word 0 0
Write modifies only the data words and dumps the non-data words in the
bit bucket.
*/
#include "pdp8_defs.h"
#define DT_NUMDR 2 /* #drives */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */
#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_8FMT (1 << UNIT_V_8FMT)
#define UNIT_11FMT (1 << UNIT_V_11FMT)
#define STATE u3 /* unit state */
#define LASTT u4 /* last time update */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* System independent DECtape constants */
#define DT_LPERMC 6 /* lines per mark track */
#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */
#define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */
#define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */
/* 16b, 18b, 36b DECtape constants */
#define D18_WSIZE 6 /* word sizein lines */
#define D18_BSIZE 384 /* block size in 12b */
#define D18_TSIZE 578 /* tape size */
#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN)
#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE))
#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */
#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE)
#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32))
#define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16))
/* 12b DECtape constants */
#define D8_WSIZE 4 /* word size in lines */
#define D8_BSIZE 129 /* block size in 12b */
#define D8_TSIZE 1474 /* tape size */
#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN)
#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE))
#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */
#define D8_FILSIZ (D8_CAPAC * sizeof (int16))
/* This controller */
#define DT_CAPAC D8_CAPAC /* default */
#define DT_WSIZE D8_WSIZE
/* Calculated constants, per unit */
#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE)
#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE)
#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB)
#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ)
#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC)
#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u))
#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u))
/* Command register */
#define TDC_UNIT 04000 /* unit select */
#define TDC_FWDRV 02000 /* fwd/rev */
#define TDC_STPGO 01000 /* stop/go */
#define TDC_RW 00400 /* read/write */
#define TDC_MASK 07400 /* implemented */
#define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0)
/* Status register */
#define TDS_WLO 00200 /* write lock */
#define TDS_TME 00100 /* timing/sel err */
/* Mark track register and codes */
#define MTK_MASK 077
#define MTK_REV_END 055 /* rev end zone */
#define MTK_INTER 025 /* interblock */
#define MTK_FWD_BLK 026 /* fwd block */
#define MTK_REV_GRD 032 /* reverse guard */
#define MTK_FWD_PRE 010 /* lock, etc */
#define MTK_DATA 070 /* data */
#define MTK_REV_PRE 073 /* lock, etc */
#define MTK_FWD_GRD 051 /* fwd guard */
#define MTK_REV_BLK 045 /* rev block */
#define MTK_FWD_END 022 /* fwd end zone */
/* DECtape state */
#define STA_STOP 0 /* stopped */
#define STA_DEC 2 /* decelerating */
#define STA_ACC 4 /* accelerating */
#define STA_UTS 6 /* up to speed */
#define STA_DIR 1 /* fwd/rev */
#define ABS(x) (((x) < 0)? (-(x)): (x))
#define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1)
/* State and declarations */
int32 td_cmd = 0; /* command */
int32 td_dat = 0; /* data */
int32 td_mtk = 0; /* mark track */
int32 td_slf = 0; /* single line flag */
int32 td_qlf = 0; /* quad line flag */
int32 td_tme = 0; /* timing error flag */
int32 td_csum = 0; /* save check sum */
int32 td_qlctr = 0; /* quad line ctr */
int32 td_ltime = 20; /* interline time */
int32 td_dctime = 40000; /* decel time */
static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */
DEVICE td_dev;
int32 td77 (int32 IR, int32 AC);
t_stat td_svc (UNIT *uptr);
t_stat td_reset (DEVICE *dptr);
t_stat td_attach (UNIT *uptr, char *cptr);
t_stat td_detach (UNIT *uptr);
t_stat td_boot (int32 unitno, DEVICE *dptr);
t_bool td_newsa (int32 newf);
t_bool td_setpos (UNIT *uptr);
int32 td_header (UNIT *uptr, int32 blk, int32 line);
int32 td_trailer (UNIT *uptr, int32 blk, int32 line);
int32 td_read (UNIT *uptr, int32 blk, int32 line);
void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb);
int32 td_set_mtk (int32 code, int32 u, int32 k);
t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc);
extern uint16 M[];
extern int32 sim_switches;
extern int32 sim_is_running;
/* TD data structures
td_dev DT device descriptor
td_unit DT unit list
td_reg DT register list
td_mod DT modifier list
*/
DIB td_dib = { DEV_TD8E, 1, { &td77 } };
UNIT td_unit[] = {
{ UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) },
{ UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+
UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } };
REG td_reg[] = {
{ GRDATA (TDCMD, td_cmd, 8, 4, 8) },
{ ORDATA (TDDAT, td_dat, 12) },
{ ORDATA (TDMTK, td_mtk, 6) },
{ FLDATA (TDSLF, td_slf, 0) },
{ FLDATA (TDQLF, td_qlf, 0) },
{ FLDATA (TDTME, td_tme, 0) },
{ ORDATA (TDQL, td_qlctr, 2) },
{ ORDATA (TDCSUM, td_csum, 6), REG_RO },
{ DRDATA (LTIME, td_ltime, 31), REG_NZ | PV_LEFT },
{ DRDATA (DCTIME, td_dctime, 31), REG_NZ | PV_LEFT },
{ URDATA (POS, td_unit[0].pos, 10, T_ADDR_W, 0,
DT_NUMDR, PV_LEFT | REG_RO) },
{ URDATA (STATT, td_unit[0].STATE, 8, 18, 0,
DT_NUMDR, REG_RO) },
{ URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0,
DT_NUMDR, REG_HRO) },
{ ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO },
{ NULL } };
MTAB td_mod[] = {
{ UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL },
{ UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL },
{ UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL },
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
&set_dev, &show_dev, NULL },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos },
{ 0 } };
DEVICE td_dev = {
"TD", td_unit, td_reg, td_mod,
DT_NUMDR, 8, 24, 1, 8, 12,
NULL, NULL, &td_reset,
&td_boot, &td_attach, &td_detach,
&td_dib, DEV_DISABLE | DEV_DIS };
/* IOT routines */
int32 td77 (int32 IR, int32 AC)
{
int32 pulse = IR & 07;
int32 u = TDC_GETUNIT (td_cmd); /* get unit */
int32 diff, t;
switch (pulse) {
case 01: /* SDSS */
if (td_slf) return AC | IOT_SKP;
break;
case 02: /* SDST */
if (td_tme) return AC | IOT_SKP;
break;
case 03: /* SDSQ */
if (td_qlf) return AC | IOT_SKP;
break;
case 04: /* SDLC */
td_tme = 0; /* clear tim err */
diff = (td_cmd ^ AC) & TDC_MASK; /* cmd changes */
td_cmd = AC & TDC_MASK; /* update cmd */
if ((diff != 0) && (diff != TDC_RW)) { /* signif change? */
if (td_newsa (td_cmd)) /* new command */
return AC | (STOP_DTOFF << IOT_V_REASON); }
break;
case 05: /* SDLD */
td_slf = 0; /* clear flags */
td_qlf = 0;
td_qlctr = 0;
td_dat = AC; /* load data reg */
break;
case 06: /* SDRC */
td_slf = 0; /* clear flags */
td_qlf = 0;
td_qlctr = 0;
t = td_cmd | td_mtk; /* form status */
if (td_tme || !(td_unit[u].flags & UNIT_ATT)) /* tim/sel err? */
t = t | TDS_TME;
if (td_unit[u].flags & UNIT_WPRT) /* write locked? */
t = t | TDS_WLO;
return t; /* return status */
case 07: /* SDRD */
td_slf = 0; /* clear flags */
td_qlf = 0;
td_qlctr = 0;
return td_dat; } /* return data */
return AC;
}
/* Command register change (start/stop, forward/reverse, new unit)
1. If change in motion, stop to start
- schedule up to speed
- set function as next state
2. If change in motion, start to stop, or change in direction
- schedule stop
*/
t_bool td_newsa (int32 newf)
{
int32 prev_mving, new_mving, prev_dir, new_dir;
UNIT *uptr;
uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */
if ((uptr->flags & UNIT_ATT) == 0) return FALSE; /* new unit attached? */
new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */
prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */
new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */
prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */
td_mtk = 0; /* mark trk reg cleared */
if (!prev_mving && !new_mving) return FALSE; /* stop from stop? */
if (new_mving && !prev_mving) { /* start from stop? */
if (td_setpos (uptr)) return TRUE; /* update pos */
sim_cancel (uptr); /* stop current */
sim_activate (uptr, td_dctime - (td_dctime >> 2)); /* sched accel */
uptr->STATE = STA_ACC | new_dir; /* set status */
td_slf = td_qlf = td_qlctr = 0; /* clear state */
return FALSE; }
if ((prev_mving && !new_mving) || /* stop from moving? */
(prev_dir != new_dir)) { /* dir chg while moving? */
if (uptr->STATE >= STA_ACC) { /* not stopping? */
if (td_setpos (uptr)) return TRUE; /* update pos */
sim_cancel (uptr); /* stop current */
sim_activate (uptr, td_dctime); /* schedule decel */
uptr->STATE = STA_DEC | prev_dir; /* set status */
td_slf = td_qlf = td_qlctr = 0; } /* clear state */
return FALSE; }
return FALSE;
}
/* Update DECtape position
DECtape motion is modeled as a constant velocity, with linear
acceleration and deceleration. The motion equations are as follows:
t = time since operation started
tmax = time for operation (accel, decel only)
v = at speed velocity in lines (= 1/td_ltime)
Then:
at speed dist = t * v
accel dist = (t^2 * v) / (2 * tmax)
decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax)
This routine uses the relative (integer) time, rather than the absolute
(floating point) time, to allow save and restore of the start times.
*/
t_bool td_setpos (UNIT *uptr)
{
uint32 new_time, ut, ulin, udelt;
int32 delta;
new_time = sim_grtime (); /* current time */
ut = new_time - uptr->LASTT; /* elapsed time */
if (ut == 0) return FALSE; /* no time gone? exit */
uptr->LASTT = new_time; /* update last time */
switch (uptr->STATE & ~STA_DIR) { /* case on motion */
case STA_STOP: /* stop */
delta = 0;
break;
case STA_DEC: /* slowing */
ulin = ut / (uint32) td_ltime;
udelt = td_dctime / td_ltime;
delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt);
break;
case STA_ACC: /* accelerating */
ulin = ut / (uint32) td_ltime;
udelt = (td_dctime - (td_dctime >> 2)) / td_ltime;
delta = (ulin * ulin) / (2 * udelt);
break;
case STA_UTS: /* at speed */
delta = ut / (uint32) td_ltime;
break; }
if (uptr->STATE & STA_DIR) uptr->pos = uptr->pos - delta; /* update pos */
else uptr->pos = uptr->pos + delta;
if (((int32) uptr->pos < 0) ||
((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) {
detach_unit (uptr); /* off reel */
sim_cancel (uptr); /* no timing pulses */
return TRUE; }
return FALSE;
}
/* Unit service - unit is either changing speed, or it is up to speed */
t_stat td_svc (UNIT *uptr)
{
int32 mot = uptr->STATE & ~STA_DIR;
int32 dir = uptr->STATE & STA_DIR;
int32 unum = uptr - td_dev.units;
int32 su = TDC_GETUNIT (td_cmd);
int32 mtkb, datb;
/* Motion cases
Decelerating - if go, next state must be accel as specified by td_cmd
Accelerating - next state must be up to speed, fall through
Up to speed - process line */
if (mot == STA_STOP) return SCPE_OK; /* stopped? done */
if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
uptr->STATE = uptr->pos = 0; /* also done */
return SCPE_UNATT; }
switch (mot) { /* case on motion */
case STA_DEC: /* deceleration */
if (td_setpos (uptr)) return STOP_DTOFF; /* update pos */
if ((unum != su) || !(td_cmd & TDC_STPGO)) /* not sel or stop? */
uptr->STATE = 0; /* stop */
else { /* selected and go */
uptr->STATE = STA_ACC | /* accelerating */
((td_cmd & TDC_FWDRV)? STA_DIR: 0); /* in new dir */
sim_activate (uptr, td_dctime - (td_dctime >> 2)); }
return SCPE_OK;
case STA_ACC: /* accelerating */
if (td_setpos (uptr)) return STOP_DTOFF; /* update pos */
uptr->STATE = STA_UTS | dir; /* set up to speed */
break;
case STA_UTS: /* up to speed */
if (dir) uptr->pos = uptr->pos - 1; /* adjust position */
else uptr->pos = uptr->pos + 1;
uptr->LASTT = sim_grtime (); /* save time */
if (((int32) uptr->pos < 0) || /* off reel? */
(uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) {
detach_unit (uptr);
return STOP_DTOFF; }
break; } /* check function */
/* At speed - process the current line
Once the TD8E is running at speed, it operates line by line. If reading,
the current mark track bit is shifted into the mark track register, and
the current data nibble (3b) is shifted into the data register. If
writing, the current mark track bit is shifted into the mark track
register, the top nibble from the data register is written to tape, and
the data register is shifted up. The complexity here comes from
synthesizing the mark track, based on tape position, and the header data. */
sim_activate (uptr, td_ltime); /* sched next line */
if (unum != su) return SCPE_OK; /* not sel? done */
td_slf = 1; /* set single */
td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */
if (td_qlctr == 0) { /* lines mod 4? */
if (td_qlf) { /* quad line set? */
td_tme = 1; /* timing error */
td_cmd = td_cmd & ~TDC_RW; } /* clear write */
else td_qlf = 1; } /* no, set quad */
datb = 0; /* assume no data */
if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */
mtkb = MTK_BIT (MTK_REV_END, uptr->pos);
else if (uptr->pos < DT_EZLIN) /* rev buffer? */
mtkb = MTK_BIT (MTK_INTER, uptr->pos);
else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */
int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */
int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */
if (lineno < DT_HTLIN) { /* header? */
if ((td_cmd & TDC_RW) == 0) /* read? */
datb = td_header (uptr, blkno, lineno); } /* get nibble */
else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) { /* data? */
if (td_cmd & TDC_RW) /* write? */
td_write (uptr, blkno, /* write data nibble */
lineno - DT_HTLIN, /* data rel line num */
(td_dat >> 9) & 07);
else datb = td_read (uptr, blkno, /* no, read */
lineno - DT_HTLIN); }
else if ((td_cmd & TDC_RW) == 0) /* trailer; read? */
datb = td_trailer (uptr, blkno, lineno - /* get trlr nibble */
(DTU_LPERB (uptr) - DT_HTLIN));
mtkb = tdb_mtk[unum][lineno]; }
else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN))
mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */
else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */
if (dir) { /* reverse? */
mtkb = mtkb ^ 01; /* complement mark bit, */
datb = datb ^ 07; } /* data bits */
td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */
td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */
return SCPE_OK;
}
/* Header read - reads out 18b words in 3b increments
word lines contents
0 0-5 0
1 6-11 block number
2 12-17 0
3 18-23 0
4 24-29 reverse checksum (0777777)
*/
int32 td_header (UNIT *uptr, int32 blk, int32 line)
{
int32 nibp;
switch (line) {
case 8: case 9: case 10: case 11: /* block num */
nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC));
return (blk >> nibp) & 07;
case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */
return 07; /* 777777 */
default:
return 0; }
}
/* Trailer read - reads out 18b words in 3b increments
Checksum is stored to avoid double calculation
word lines contents
0 0-5 forward checksum (lines 0-1, rest 0)
1 6-11 0
2 12-17 0
3 18-23 reverse block mark
4 24-29 0
Note that the reverse block mark (when read forward) appears
as the complement obverse (3b nibbles swapped end for end and
complemented).
*/
int32 td_trailer (UNIT *uptr, int32 blk, int32 line)
{
int32 nibp, i, ba;
int16 *fbuf= uptr->filebuf;
switch (line) {
case 0:
td_csum = 07777; /* init csum */
ba = blk * DTU_BSIZE (uptr);
for (i = 0; i < DTU_BSIZE (uptr); i++) /* loop thru buf */
td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777;
td_csum = ((td_csum >> 6) ^ td_csum) & 077;
return (td_csum >> 3) & 07;
case 1:
return (td_csum & 07);
case 18: case 19: case 20: case 21:
nibp = 3 * (line % DT_LPERMC);
return ((blk >> nibp) & 07) ^ 07;
default:
return 0; }
}
/* Data read - convert block number/data line # to offset in data array */
int32 td_read (UNIT *uptr, int32 blk, int32 line)
{
int16 *fbuf = uptr->filebuf; /* buffer */
uint32 ba = blk * DTU_BSIZE (uptr); /* block base */
int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */
ba = ba + (line / DT_WSIZE); /* block addr */
return (fbuf[ba] >> nibp) & 07; /* get data nibble */
}
/* Data write - convert block number/data line # to offset in data array */
void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat)
{
int16 *fbuf = uptr->filebuf; /* buffer */
uint32 ba = blk * DTU_BSIZE (uptr); /* block base */
int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */
ba = ba + (line / DT_WSIZE); /* block addr */
fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */
if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; /* upd length */
return;
}
/* Reset routine */
t_stat td_reset (DEVICE *dptr)
{
int32 i;
UNIT *uptr;
for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */
uptr = td_dev.units + i;
if (sim_is_running) { /* CAF? */
if (uptr->STATE >= STA_ACC) { /* accel or uts? */
if (td_setpos (uptr)) continue; /* update pos */
sim_cancel (uptr);
sim_activate (uptr, td_dctime); /* sched decel */
uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR);
} }
else {
sim_cancel (uptr); /* sim reset */
uptr->STATE = 0;
uptr->LASTT = sim_grtime (); } }
td_slf = td_qlf = td_qlctr = 0; /* clear state */
td_cmd = td_dat = td_mtk = 0;
td_csum = 0;
return SCPE_OK;
}
/* Bootstrap routine - OS/8 only
1) Read reverse until reverse end zone (mark track is complement obverse
2) Read forward until mark track code 031. This is a composite code from
the last 4b of the forward block number and the first two bits of the
reverse guard (01 -0110 01- 1010). There are 16 lines before the first
data word.
3) Store data words from 7354 to end of page. This includes header and
trailer words.
4) Continue at location 7400.
*/
#define BOOT_START 07300
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16))
static const uint16 boot_rom[] = {
01312, /* ST, TAD L4MT ;=2000, reverse */
04312, /* JMS L4MT ; rev lk for 022 */
04312, /* JMS L4MT ; fwd lk for 031 */
06773, /* DAT, SDSQ ; wait for 12b */
05303, /* JMP .-1 */
06777, /* SDRD ; read word */
03726, /* DCA I BUF ; store */
02326, /* ISZ BUF ; incr ptr */
05303, /* JMP DAT ; if not 0, cont */
05732, /* JMP I SCB ; jump to boot */
02000, /* L4MT,2000 ; overwritten */
01300, /* TAD ST ; =1312, go */
06774, /* SDLC ; new command */
06771, /* MTK, SDSS ; wait for mark */
05315, /* JMP .-1 */
06776, /* SDRC ; get mark code */
00331, /* AND K77 ; mask to 6b */
01327, /* CMP, TAD MCD ; got target code? */
07640, /* SZA CLA ; skip if yes */
05315, /* JMP MTK ; wait for mark */
02321, /* ISZ CMP ; next target */
05712, /* JMP I L4MT ; exit */
07354, /* BUF, 7354 ; loading point */
07756, /* MCD, -22 ; target 1 */
07747, /* -31 ; target 2 */
00077, /* 77 ; mask */
07400 /* SCB, 7400 ; secondary boot */
};
t_stat td_boot (int32 unitno, DEVICE *dptr)
{
int32 i;
extern int32 saved_PC;
if (unitno) return SCPE_ARG; /* only unit 0 */
if (td_dib.dev != DEV_TD8E) return STOP_NOTSTD; /* only std devno */
td_unit[unitno].pos = DT_EZLIN;
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
saved_PC = BOOT_START;
return SCPE_OK;
}
/* Attach routine
Determine 12b, 16b, or 18b/36b format
Allocate buffer
If 16b or 18b, read 16b or 18b format and convert to 12b in buffer
If 12b, read data into buffer
Set up mark track bit array
*/
t_stat td_attach (UNIT *uptr, char *cptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k, mtkpb;
int32 u = uptr - td_dev.units;
t_stat r;
uint32 ba, sz;
r = attach_unit (uptr, cptr); /* attach */
if (r != SCPE_OK) return r; /* fail? */
if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */
uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT;
if (sim_switches & SWMASK ('T')) /* att 18b? */
uptr->flags = uptr->flags & ~UNIT_8FMT;
else if (sim_switches & SWMASK ('S')) /* att 16b? */
uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
else if (!(sim_switches & SWMASK ('R')) && /* autosize? */
(sz = sim_fsize (cptr))) {
if (sz == D11_FILSIZ)
uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT;
else if (sz > D8_FILSIZ)
uptr->flags = uptr->flags & ~UNIT_8FMT; } }
uptr->capac = DTU_CAPAC (uptr); /* set capacity */
uptr->filebuf = calloc (uptr->capac, sizeof (int16));
if (uptr->filebuf == NULL) { /* can't alloc? */
detach_unit (uptr);
return SCPE_MEM; }
fbuf = uptr->filebuf; /* file buffer */
printf ("%s%d: ", sim_dname (&td_dev), u);
if (uptr->flags & UNIT_8FMT) printf ("12b format");
else if (uptr->flags & UNIT_11FMT) printf ("16b format");
else printf ("18b/36b format");
printf (", buffering file in memory\n");
if (uptr->flags & UNIT_8FMT) /* 12b? */
uptr->hwmark = fxread (uptr->filebuf, sizeof (int16),
uptr->capac, uptr->fileref);
else { /* 16b/18b */
for (ba = 0; ba < uptr->capac; ) { /* loop thru file */
if (uptr->flags & UNIT_11FMT) {
k = fxread (pdp11b, sizeof (int16), D18_NBSIZE, uptr->fileref);
for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i]; }
else k = fxread (pdp18b, sizeof (int32), D18_NBSIZE, uptr->fileref);
if (k == 0) break;
for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0;
for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */
fbuf[ba] = (pdp18b[k] >> 6) & 07777;
fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) |
((pdp18b[k + 1] >> 12) & 077);
fbuf[ba + 2] = pdp18b[k + 1] & 07777;
ba = ba + 3; } /* end blk loop */
} /* end file loop */
uptr->hwmark = ba; } /* end else */
uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */
uptr->pos = DT_EZLIN; /* beyond leader */
uptr->LASTT = sim_grtime (); /* last pos update */
uptr->STATE = STA_STOP; /* stopped */
mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */
k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */
k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */
k = td_set_mtk (MTK_REV_GRD, u, k);
for (i = 0; i < 4; i++) k = td_set_mtk (MTK_FWD_PRE, u, k);
for (i = 0; i < (mtkpb - 4); i++) k = td_set_mtk (MTK_DATA, u, k);
for (i = 0; i < 4; i++) k = td_set_mtk (MTK_REV_PRE, u, k);
k = td_set_mtk (MTK_FWD_GRD, u, k);
k = td_set_mtk (MTK_REV_BLK, u, k);
k = td_set_mtk (MTK_INTER, u, k);
return SCPE_OK;
}
/* Detach routine
If 12b, write buffer to file
If 16b or 18b, convert 12b buffer to 16b or 18b and write to file
Deallocate buffer
*/
t_stat td_detach (UNIT* uptr)
{
uint32 pdp18b[D18_NBSIZE];
uint16 pdp11b[D18_NBSIZE], *fbuf;
int32 i, k;
int32 u = uptr - td_dev.units;
uint32 ba;
if (!(uptr->flags & UNIT_ATT)) return SCPE_OK;
fbuf = uptr->filebuf; /* file buffer */
if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */
printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u);
rewind (uptr->fileref); /* start of file */
if (uptr->flags & UNIT_8FMT) /* PDP8? */
fxwrite (uptr->filebuf, sizeof (int16), /* write file */
uptr->hwmark, uptr->fileref);
else { /* 16b/18b */
for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */
for (k = 0; k < D18_NBSIZE; k = k + 2) {
pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) |
((uint32) (fbuf[ba + 1] >> 6) & 077);
pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) |
((uint32) (fbuf[ba + 2] & 07777));
ba = ba + 3; } /* end loop blk */
if (uptr->flags & UNIT_11FMT) { /* 16b? */
for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i];
fxwrite (pdp11b, sizeof (int16),
D18_NBSIZE, uptr->fileref); }
else fxwrite (pdp18b, sizeof (int32),
D18_NBSIZE, uptr->fileref);
} /* end loop buf */
} /* end else */
if (ferror (uptr->fileref)) perror ("I/O error");
} /* end if hwmark */
free (uptr->filebuf); /* release buf */
uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */
uptr->filebuf = NULL; /* clear buf ptr */
uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */
uptr->capac = DT_CAPAC; /* default size */
uptr->pos = uptr->STATE = 0;
sim_cancel (uptr); /* no more pulses */
return detach_unit (uptr);
}
/* Set mark track code into bit array */
int32 td_set_mtk (int32 code, int32 u, int32 k)
{
int32 i;
for (i = 5; i >= 0; i--) tdb_mtk[u][k++] = (code >> i) & 1;
return k;
}
/* Show position */
t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc)
{
if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT;
if (uptr->pos < DT_EZLIN) /* rev end zone? */
fprintf (st, "Reverse end zone\n");
else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */
int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */
int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */
fprintf (st, "Block %d, line %d, ", blkno, lineno);
if (lineno < DT_HTLIN) /* header? */
fprintf (st, "header cell %d, nibble %d\n",
lineno / DT_LPERMC, lineno % DT_LPERMC);
else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) /* data? */
fprintf (st, "data word %d, nibble %d\n",
(lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE);
else fprintf (st, "trailer cell %d, nibble %d\n",
(lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC,
(lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC); }
else fprintf (st, "Forward end zone\n"); /* fwd end zone */
return SCPE_OK;
}

143
PDP8/pdp8_tsc.c Normal file
View File

@@ -0,0 +1,143 @@
/* pdp8_tsc.c: PDP-8 ETOS timesharing option board (TSC8-75)
Copyright (c) 2003, 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 module is based on Bernhard Baehr's PDP-8/E simulator
PDP-8/E Simulator Source Code
Copyright ) 2001-2003 Bernhard Baehr
TSC8iots.c - IOTs for the TSC8-75 Board plugin
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
tsc TSC8-75 option board
*/
#include "pdp8_defs.h"
extern int32 int_req;
extern int32 SF;
extern int32 tsc_ir; /* "ERIOT" */
extern int32 tsc_pc; /* "ERTB" */
extern int32 tsc_cdf; /* "ECDF" */
extern int32 tsc_enb; /* enable */
#define UNIT_V_SN699 (UNIT_V_UF + 0) /* SN 699 or above */
#define UNIT_SN699 (1 << UNIT_V_SN699)
DEVICE tsc_dev;
int32 tsc (int32 IR, int32 AC);
t_stat tsc_reset (DEVICE *dptr);
/* TSC data structures
tsc_dev TSC device descriptor
tsc_unit TSC unit descriptor
tsc_reg TSC register list
*/
DIB tsc_dib = { DEV_TSC, 1, { &tsc } };
UNIT tsc_unit = { UDATA (NULL, UNIT_SN699, 0) };
REG tsc_reg[] = {
{ ORDATA (IR, tsc_ir, 12) },
{ ORDATA (PC, tsc_pc, 12) },
{ FLDATA (CDF, tsc_cdf, 0) },
{ FLDATA (ENB, tsc_enb, 0) },
{ FLDATA (INT, int_req, INT_V_TSC) },
{ NULL } };
MTAB tsc_mod[] = {
{ UNIT_SN699, UNIT_SN699, "ESME", "ESME", NULL },
{ UNIT_SN699, 0, "no ESME", "NOESME", NULL },
{ 0 } };
DEVICE tsc_dev = {
"TSC", &tsc_unit, tsc_reg, tsc_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tsc_reset,
NULL, NULL, NULL,
&tsc_dib, DEV_DISABLE | DEV_DIS };
/* IOT routine */
int32 tsc (int32 IR, int32 AC)
{
switch (IR & 07) { /* decode IR<9:11> */
case 0: /* ETDS */
tsc_enb = 0; /* disable int req */
int_req = int_req & ~INT_TSC; /* clear flag */
break;
case 1: /* ESKP */
return (int_req & INT_TSC)? IOT_SKP + AC: AC; /* skip on int req */
case 2: /* ECTF */
int_req = int_req & ~INT_TSC; /* clear int req */
break;
case 3: /* ECDF */
AC = AC | ((tsc_ir >> 3) & 07); /* read "ERIOT"<6:8> */
if (tsc_cdf) AC = AC | IOT_SKP; /* if cdf, skip */
tsc_cdf = 0;
break;
case 4: /* ERTB */
return tsc_pc;
case 5: /* ESME */
if (tsc_unit.flags & UNIT_SN699) { /* enabled? */
if (tsc_cdf && ((tsc_ir & 070) >> 3) == (SF & 07)) {
AC = AC | IOT_SKP;
tsc_cdf = 0; } }
break;
case 6: /* ERIOT */
return tsc_ir;
case 7: /* ETEN */
tsc_enb = 1;
break; } /* end switch */
return AC;
}
/* Reset routine */
t_stat tsc_reset (DEVICE *dptr)
{
tsc_ir = 0;
tsc_pc = 0;
tsc_cdf = 0;
tsc_enb = 0;
int_req = int_req & ~INT_TSC;
return SCPE_OK;
}

View File

@@ -1,6 +1,6 @@
/* pdp8_tt.c: PDP-8 console terminal simulator
Copyright (c) 1993-2003, Robert M Supnik
Copyright (c) 1993-2004, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -25,6 +25,7 @@
tti,tto KL8E terminal input/output
29-Dec-03 RMS Added console output backpressure support
25-Apr-03 RMS Revised for extended file support
02-Mar-02 RMS Added SET TTI CTRL-C
22-Dec-02 RMS Added break support
@@ -233,13 +234,15 @@ t_stat tto_svc (UNIT *uptr)
int32 c;
t_stat r;
dev_done = dev_done | INT_TTO; /* set done */
int_req = INT_UPDATE; /* update interrupts */
if (tto_unit.flags & UNIT_KSR) { /* UC only? */
c = tto_unit.buf & 0177;
if (islower (c)) c = toupper (c); }
else c = tto_unit.buf & ((tto_unit.flags & UNIT_8B)? 0377: 0177);
if ((r = sim_putchar (c)) != SCPE_OK) return r;
if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output char; error? */
sim_activate (uptr, uptr->wait); /* try again */
return ((r == SCPE_STALL)? SCPE_OK: r); } /* if !stall, report */
dev_done = dev_done | INT_TTO; /* set done */
int_req = INT_UPDATE; /* update interrupts */
tto_unit.pos = tto_unit.pos + 1;
return SCPE_OK;
}