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

Notes For V2.8

1. New Features

1.1 Directory and documentation

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

1.2 SCP

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

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

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

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

1.3 PDP-11 simulator

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

1.4 PDP-10 simulator

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

1.5 18b PDP simulator

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

1.5 IBM System 3 Simulator

- Written by Charles (Dutch) Owen.

1.6 VAX Simulator (in debug)

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

1.7 SDS 940 Simulator (not tested)

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

1.8 Altair Z80

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

1.9 Interdata

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

2. Release Notes

2.1 SCP

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

2.2 PDP-11

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

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

2.3 VAX

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

2.4 SDS 940

The SDS 940 is untested, awaiting real code.

2.5 GCC Optimization

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

3. Work list

3.1 SCP

- Better ENABLE/DISABLE.

3.2 PDP-11 RQDX3

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

24
NOVA/eclipse.txt Normal file
View File

@@ -0,0 +1,24 @@
Charles Owen's Eclipse Modules
1. Eclipse CPU simulator
The Eclipse CPU simulator can be used with the Nova definitions and peripheral
modules to produce an Eclipse simulator that will run Eclipse mapped RDOS V7.5.
The compilation procedure is the same as for the Nova simulator, except:
- the symbol ECLIPSE must be defined
- the module eclipse_cpu.c must be substituted for nova_cpu.c
- the output should be named eclipse rather than nova
For example, to compile under UNIX, move nova_cpu.c out of the source directory
and then give this command:
% cc -DECLIPSE eclipse_cpu.c nova_*.c -o eclipse
2. Alternate terminal emulator
The module eclipse_tt.c can be used with either an Eclipse or Nova CPU simulator
in place of nova_tt.c. It provides a full emulation of the cursor controls on
the Dasher video terminal but requires that the underlying operating system
interpret VT100 cursor controls. Thus, it works under VMS or UNIX but not under
Windows or OS/2.

3411
NOVA/eclipse_cpu.c Normal file

File diff suppressed because it is too large Load Diff

382
NOVA/eclipse_tt.c Normal file
View File

@@ -0,0 +1,382 @@
/* eclipse_tt.c: Eclipse console terminal simulator
Copyright (c) 1993-1997,
Robert M Supnik, Digital Equipment Corporation
Commercial use prohibited
tti terminal input
tto terminal output
*/
#include "nova_defs.h"
#define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */
#define UNIT_DASHER (1 << UNIT_V_DASHER)
extern int32 int_req, dev_busy, dev_done, dev_disable;
t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr);
t_stat ttx_setmod (UNIT *uptr, int32 value);
extern t_stat sim_activate (UNIT *uptr, int32 delay);
extern t_stat sim_cancel (UNIT *uptr);
extern t_stat sim_poll_kbd (void);
extern t_stat sim_putchar (int32 out);
/* TTI data structures
tti_dev TTI device descriptor
tti_unit TTI unit descriptor
tti_reg TTI register list
ttx_mod TTI/TTO modifiers list
*/
UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT };
REG tti_reg[] = {
{ ORDATA (BUF, tti_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_TTI) },
{ FLDATA (DONE, dev_done, INT_V_TTI) },
{ FLDATA (DISABLE, dev_disable, INT_V_TTI) },
{ FLDATA (INT, int_req, INT_V_TTI) },
{ DRDATA (POS, tti_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT },
{ FLDATA (MODE, tti_unit.flags, UNIT_V_DASHER), REG_HRO },
{ NULL } };
MTAB ttx_mod[] = {
{ UNIT_DASHER, 0, "ANSI", "ANSI", &ttx_setmod },
{ UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx_setmod },
{ 0 } };
DEVICE tti_dev = {
"TTI", &tti_unit, tti_reg, ttx_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tti_reset,
NULL, NULL, NULL };
/* TTO data structures
tto_dev TTO device descriptor
tto_unit TTO unit descriptor
tto_reg TTO register list
*/
UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT };
REG tto_reg[] = {
{ ORDATA (BUF, tto_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_TTO) },
{ FLDATA (DONE, dev_done, INT_V_TTO) },
{ FLDATA (DISABLE, dev_disable, INT_V_TTO) },
{ FLDATA (INT, int_req, INT_V_TTO) },
{ DRDATA (POS, tto_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, tto_unit.wait, 24), PV_LEFT },
{ FLDATA (MODE, tto_unit.flags, UNIT_V_DASHER), REG_HRO },
{ NULL } };
DEVICE tto_dev = {
"TTO", &tto_unit, tto_reg, ttx_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tto_reset,
NULL, NULL, NULL };
/* Terminal input: IOT routine */
int32 tti (int32 pulse, int32 code, int32 AC)
{
int32 iodata;
iodata = (code == ioDIA)? tti_unit.buf & 0377: 0;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_TTI; /* set busy */
dev_done = dev_done & ~INT_TTI; /* clear done, int */
int_req = int_req & ~INT_TTI;
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_TTI; /* clear busy */
dev_done = dev_done & ~INT_TTI; /* clear done, int */
int_req = int_req & ~INT_TTI;
break; } /* end switch */
return iodata;
}
/* Unit service */
t_stat tti_svc (UNIT *uptr)
{
int32 temp;
sim_activate (&tti_unit, tti_unit.wait); /* continue poll */
if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */
tti_unit.buf = temp & 0177;
/* --- BEGIN MODIFIED CODE --- */
if (tti_unit.flags & UNIT_DASHER) /* translate input */
translate_in();
/* --- END MODIFIED CODE --- */
dev_busy = dev_busy & ~INT_TTI; /* clear busy */
dev_done = dev_done | INT_TTI; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
tti_unit.pos = tti_unit.pos + 1;
return SCPE_OK;
}
/* -------------------- BEGIN INSERTION -----------------------*/
int curpos = 0; /* used by translate_out() */
int row = 0, col = 0; /* ditto - for cursor positioning */
int spec200 = 0; /* signals next char is 'special' */
/* Translation: Vt100 input to D200 keycodes. */
int32 translate_in()
{
char rev = 0;
if (tti_unit.buf == '\r')
rev = '\n';
if (tti_unit.buf == '\n')
rev = '\r';
if (rev)
tti_unit.buf = rev;
}
/* -------------------- END INSERTION -----------------------*/
/* Reset routine */
t_stat tti_reset (DEVICE *dptr)
{
tti_unit.buf = 0;
dev_busy = dev_busy & ~INT_TTI; /* clear busy */
dev_done = dev_done & ~INT_TTI; /* clear done, int */
int_req = int_req & ~INT_TTI;
sim_activate (&tti_unit, tti_unit.wait); /* activate unit */
return SCPE_OK;
}
/* Terminal output: IOT routine */
int32 tto (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) tto_unit.buf = AC & 0377;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_TTO; /* set busy */
dev_done = dev_done & ~INT_TTO; /* clear done, int */
int_req = int_req & ~INT_TTO;
sim_activate (&tto_unit, tto_unit.wait); /* activate unit */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_TTO; /* clear busy */
dev_done = dev_done & ~INT_TTO; /* clear done, int */
int_req = int_req & ~INT_TTO;
sim_cancel (&tto_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat tto_svc (UNIT *uptr)
{
int32 c, temp;
dev_busy = dev_busy & ~INT_TTO; /* clear busy */
dev_done = dev_done | INT_TTO; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
c = tto_unit.buf & 0177;
/* --- BEGIN MODIFIED CODE --- */
if (tto_unit.flags & UNIT_DASHER) {
if ((temp = translate_out(c)) != SCPE_OK) return temp;
} else {
if ((temp = sim_putchar (c)) != SCPE_OK) return temp;
tto_unit.pos = tto_unit.pos + 1;
}
/* --- END MODIFIED CODE --- */
return SCPE_OK;
}
/* -------------------- BEGIN INSERTION -----------------------*/
/* Translation routine - D200 screen controls to VT-100 controls. */
int32 translate_out(int32 c)
{
int32 temp;
char outstr[32];
if (spec200 == 1) { /* Special terminal control seq */
spec200 = 0;
switch (c) {
case 'C': /* read model ID */
return SCPE_OK;
case 'E': /* Reverse video off */
return SCPE_OK;
case 'D': /* Reverse video on */
return SCPE_OK;
default:
return SCPE_OK;
}
}
if (curpos == 1) { /* 2nd char of cursor position */
col = c & 0x7f;
curpos++;
return (SCPE_OK);
}
if (curpos == 2) { /* 3rd char of cursor position */
row = c & 0x7f;
curpos = 0;
sprintf(outstr, "\033[%d;%dH", row+1, col+1);
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
}
switch (c) { /* Single-char command or data */
case 003: /* Blink enable */
break;
case 004: /* Blink disable */
break;
case 005: /* Read cursor address */
break;
case 010: /* Cursor home */
sprintf(outstr, "\033[1;1H");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
row = col = 0;
return (SCPE_OK);
case 012: /* Newline */
if ((temp = sim_putchar('\r')) != SCPE_OK) return temp;
tto_unit.pos += 1;
if ((temp = sim_putchar(c)) != SCPE_OK) return temp;
tto_unit.pos += 1;
col = 1;
row++;
if (row > 24) row = 1;
return (SCPE_OK);
case 013: /* Erase EOL */
sprintf(outstr, "\033[K");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
case 014: /* Erase screen */
sprintf(outstr, "\033[1;1H\033[2J");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
row = col = 0;
return (SCPE_OK);
case 015: /* CR */
if ((temp = sim_putchar(c)) != SCPE_OK) return temp;
tto_unit.pos += 1;
col = 1;
return (SCPE_OK);
case 016: /* Blink On */
sprintf(outstr, "\033[5m");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
case 017: /* Blink off */
sprintf(outstr, "\033[25m");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
case 020: /* Write cursor address */
curpos = 1;
return SCPE_OK;
case 024: /* underscore on */
sprintf(outstr, "\033[4m");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
case 025: /* underscore off */
sprintf(outstr, "\033[24m");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
break;
case 027: /* cursor up */
sprintf(outstr, "\033[A");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
row--;
if (row < 1) row = 24;
return (SCPE_OK);
case 030: /* cursor right */
sprintf(outstr, "\033[C");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
col++;
if (col > 80) {
col = 1;
row++;
if (row > 24) row = 1;
}
return (SCPE_OK);
case 031: /* Cursor left */
sprintf(outstr, "\033[D");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
tto_unit.pos += 1;
col--;
if (col < 1) {
col = 80;
row--;
if (row < 1) row = 24;
}
return (SCPE_OK);
case 032: /* Cursor down */
sprintf(outstr, "\033[B");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
row++;
if (row > 24) row = 1;
return (SCPE_OK);
case 034: /* Dim on */
sprintf(outstr, "\033[22m");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
case 035: /* Dim off */
sprintf(outstr, "\033[1m");
if ((temp = putseq(outstr)) != SCPE_OK) return temp;
return (SCPE_OK);
case 036: /* Special sequence */
spec200 = 1;
return SCPE_OK;
default: /* ..A character of data */
if ((temp = sim_putchar(c)) != SCPE_OK) return temp;
tto_unit.pos += 1;
col++;
if (col > 80) {
col = 1;
row++;
if (row > 24) row = 24;
}
return (SCPE_OK);
}
return SCPE_OK;
}
int32 putseq(char *seq)
{
int i, len, temp;
len = strlen(seq);
for (i = 0; i < len; i++) {
if ((temp = sim_putchar(seq[i])) != SCPE_OK)
return temp;
tto_unit.pos += 1;
}
return SCPE_OK;
}
/* -------------------- END INSERTION -----------------------*/
/* Reset routine */
t_stat tto_reset (DEVICE *dptr)
{
tto_unit.buf = 0;
dev_busy = dev_busy & ~INT_TTO; /* clear busy */
dev_done = dev_done & ~INT_TTO; /* clear done, int */
int_req = int_req & ~INT_TTO;
sim_cancel (&tto_unit); /* deactivate unit */
return SCPE_OK;
}
t_stat ttx_setmod (UNIT *uptr, int32 value)
{
tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | value;
tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | value;
return SCPE_OK;
}

125
NOVA/nova_clk.c Normal file
View File

@@ -0,0 +1,125 @@
/* nova_clk.c: NOVA real-time clock simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
clk real-time clock
17-Sep-01 RMS Added terminal multiplexor support
17-Mar-01 RMS Moved function prototype
05-Mar-01 RMS Added clock calibration
24-Sep-97 RMS Fixed bug in unit service (found by Charles Owen)
*/
#include "nova_defs.h"
extern int32 int_req, dev_busy, dev_done, dev_disable;
int32 clk_sel = 0; /* selected freq */
int32 clk_time[4] = { 16000, 100000, 10000, 1000 }; /* freq table */
int32 clk_tps[4] = { 60, 10, 100, 1000 }; /* ticks per sec */
int32 clk_adj[4] = { 1, -5, 2, 20 }; /* tmxr adjust */
int32 tmxr_poll = 16000; /* tmxr poll */
t_stat clk_svc (UNIT *uptr);
t_stat clk_reset (DEVICE *dptr);
/* CLK data structures
clk_dev CLK device descriptor
clk_unit CLK unit descriptor
clk_reg CLK register list
*/
UNIT clk_unit = { UDATA (&clk_svc, 0, 0) };
REG clk_reg[] = {
{ ORDATA (SELECT, clk_sel, 2) },
{ FLDATA (BUSY, dev_busy, INT_V_CLK) },
{ FLDATA (DONE, dev_done, INT_V_CLK) },
{ FLDATA (DISABLE, dev_disable, INT_V_CLK) },
{ FLDATA (INT, int_req, INT_V_CLK) },
{ DRDATA (TIME0, clk_time[0], 24), REG_NZ + PV_LEFT },
{ DRDATA (TIME1, clk_time[1], 24), REG_NZ + PV_LEFT },
{ DRDATA (TIME2, clk_time[2], 24), REG_NZ + PV_LEFT },
{ DRDATA (TIME3, clk_time[3], 24), REG_NZ + PV_LEFT },
{ NULL } };
DEVICE clk_dev = {
"CLK", &clk_unit, clk_reg, NULL,
1, 0, 0, 0, 0, 0,
NULL, NULL, &clk_reset,
NULL, NULL, NULL };
/* IOT routine */
int32 clk (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) { /* DOA */
clk_sel = AC & 3; /* save select */
sim_rtc_init (clk_time[clk_sel]); } /* init calibr */
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_CLK; /* set busy */
dev_done = dev_done & ~INT_CLK; /* clear done, int */
int_req = int_req & ~INT_CLK;
if (!sim_is_active (&clk_unit)) /* not running? */
sim_activate (&clk_unit, /* activate */
sim_rtc_init (clk_time[clk_sel])); /* init calibr */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_CLK; /* clear busy */
dev_done = dev_done & ~INT_CLK; /* clear done, int */
int_req = int_req & ~INT_CLK;
sim_cancel (&clk_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat clk_svc (UNIT *uptr)
{
int32 t;
dev_done = dev_done | INT_CLK; /* set done */
dev_busy = dev_busy & ~INT_CLK; /* clear busy */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
t = sim_rtc_calb (clk_tps[clk_sel]); /* calibrate delay */
sim_activate (&clk_unit, t); /* reactivate unit */
if (clk_adj[clk_sel] > 0) /* clk >= 60Hz? */
tmxr_poll = t * clk_adj[clk_sel]; /* poll is longer */
else tmxr_poll = t / (-clk_adj[clk_sel]); /* poll is shorter */
return SCPE_OK;
}
/* Reset routine */
t_stat clk_reset (DEVICE *dptr)
{
clk_sel = 0;
dev_busy = dev_busy & ~INT_CLK; /* clear busy */
dev_done = dev_done & ~INT_CLK; /* clear done, int */
int_req = int_req & ~INT_CLK;
sim_cancel (&clk_unit); /* deactivate unit */
tmxr_poll = clk_time[0]; /* poll is default */
return SCPE_OK;
}

914
NOVA/nova_cpu.c Normal file
View File

@@ -0,0 +1,914 @@
/* nova_cpu.c: NOVA CPU simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
cpu Nova central processor
07-Dec-01 RMS Revised to use breakpoint package
30-Nov-01 RMS Added extended SET/SHOW support
10-Aug-01 RMS Removed register in declarations
17-Jul-01 RMS Moved function prototype
26-Apr-01 RMS Added device enable/disable support
05-Mar-01 RMS Added clock calibration
22-Dec-00 RMS Added Bruce Ray's second terminal
15-Dec-00 RMS Added Charles Owen's CPU bootstrap
08-Dec-00 RMS Changes from Bruce Ray
-- fixed trap test to include Nova 3
-- fixed DIV and DIVS divide by 0
-- fixed RETN to set SP from FP
-- fixed IORST to preserve carry
-- added "secret" Nova 4 PSHN/SAVEN instructions
-- added plotter support
15-Oct-00 RMS Fixed bug in MDV test, added stack, byte, trap instructions
14-Apr-98 RMS Changed t_addr to unsigned
15-Sep-97 RMS Added read and write breakpoints
The register state for the NOVA CPU is:
AC[0:3]<0:15> general registers
C carry flag
PC<0:14> program counter
The NOVA has three instruction formats: memory reference, I/O transfer,
and operate. The memory reference format is:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 0| op | AC |in| mode| displacement | memory reference
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
<0:4> mnemonic action
00000 JMP PC = MA
00001 JMS AC3 = PC, PC = MA
00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0
00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0
001'n LDA ACn = M[MA]
010'n STA M[MA] = ACn
<5:7> mode action
000 page zero direct MA = zext (IR<8:15>)
001 PC relative direct MA = PC + sext (IR<8:15>)
010 AC2 relative direct MA = AC2 + sext (IR<8:15>)
011 AC3 relative direct MA = AC3 + sext (IR<8:15>)
100 page zero indirect MA = M[zext (IR<8:15>)]
101 PC relative indirect MA = M[PC + sext (IR<8:15>)]
110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)]
111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)]
Memory reference instructions can access an address space of 32K words.
An instruction can directly reference the first 256 words of memory
(called page zero), as well as 256 words relative to the PC, AC2, or
AC3; it can indirectly access all 32K words. If an indirect address
is in locations 00020-00027, the indirect address is incremented and
rewritten to memory before use; if in 00030-00037, decremented and
rewritten.
*/
/* The I/O transfer format is:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 0 1 1| AC | opcode |pulse| device | I/O transfer
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The IOT instruction sends the opcode, pulse, and specified AC to the
specified I/O device. The device may accept data, provide data,
initiate or cancel operations, or skip on status.
The operate format is:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
\______/ \___/ \___/ | | | |
| | | | | | +--- reverse skip sense
| | | | | +--- skip if C == 0
| | | | +--- skip if result == 0
| | | +--- don't load result
| | +--- carry in (load as is,
| | set to Zero,
| | set to One,
| | load Complement)
| +--- shift (none,
| left one,
| right one,
| byte swap)
+--- operation (complement,
negate,
move,
increment,
add complement,
subtract,
add,
and)
The operate instruction can be microprogrammed to perform operations
on the source and destination AC's and the Carry flag.
Some notes from Bruce Ray:
1. DG uses the value of the autoindex location -before- the
modification to determine if additional indirect address
levels are to be performed. Most DG emulators conform to
this standard, but some vendor machines (i.e. Point 4 Mark 8)
do not.
2. Infinite indirect references may occur on unmapped systems
and can "hang" the hardware. Some DG diagnostics perform
10,000s of references during a single instruction.
3. Nova 3 adds the following instructions to the standard Nova
instruction set:
trap instructions
stack push/pop instructions
save/return instructions
stack register manipulation instructions
unsigned MUL/DIV
4. Nova 4 adds the following instructions to the Nova 3 instruction
set:
signed MUL/DIV
load/store byte
secret (undocumented) stack instructions [PSHN, SAVN]
5. Nova, Nova 3 and Nova 4 unsigned mul/div instructions are the
same instruction code values on all machines.
*/
/* This routine is the instruction decode routine for the NOVA.
It is called from the simulator control program to execute
instructions in simulated memory, starting at the simulated PC.
It runs until 'reason' is set non-zero.
General notes:
1. Reasons to stop. The simulator can be stopped by:
HALT instruction
breakpoint encountered
infinite indirection loop
unknown I/O device and STOP_DEV flag set
I/O error in I/O simulator
2. Interrupts. Interrupts are maintained by four parallel variables:
dev_done device done flags
dev_disable device interrupt disable flags
dev_busy device busy flags
int_req interrupt requests
In addition, int_req contains the interrupt enable and ION pending
flags. If ION and ION pending are set, and at least one interrupt
request is pending, then an interrupt occurs. Note that the 16b PIO
mask must be mapped to the simulator's device bit mapping.
3. Non-existent memory. On the NOVA, reads to non-existent memory
return zero, and writes are ignored. In the simulator, the
largest possible memory is instantiated and initialized to zero.
Thus, only writes need be checked against actual memory size.
4. Adding I/O devices. These modules must be modified:
nova_defs.h add interrupt request definition
nova_cpu.c add IOT mask, PI mask, and routine to dev_table
nova_sys.c add pointer to data structures to sim_devices
*/
#include "nova_defs.h"
#define INCA(x) (((x) + 1) & AMASK)
#define DECA(x) (((x) - 1) & AMASK)
#define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): (x))
#define STK_CHECK(x,y) if (((x) & 0377) < (y)) int_req = int_req | INT_STK
#define IND_STEP(x) M[x] & A_IND; \
if (((x) & 077770) == AUTO_INC) \
M[x] = (M[x] + 1) & 0177777; \
else if (((x) & 077770) == AUTO_DEC) \
M[x] = (M[x] - 1) & 0177777; \
x = M[x] & AMASK
#define UNIT_V_MDV (UNIT_V_UF) /* MDV present */
#define UNIT_MDV (1 << UNIT_V_MDV)
#define UNIT_V_STK (UNIT_V_UF+1) /* stack instr */
#define UNIT_STK (1 << UNIT_V_STK)
#define UNIT_V_BYT (UNIT_V_UF+2) /* byte instr */
#define UNIT_BYT (1 << UNIT_V_BYT)
#define UNIT_IOPT (UNIT_MDV | UNIT_STK | UNIT_BYT)
#define UNIT_NOVA3 (UNIT_MDV | UNIT_STK)
#define UNIT_NOVA4 (UNIT_MDV | UNIT_STK | UNIT_BYT)
#define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */
#define UNIT_MSIZE (1 << UNIT_V_MSIZE)
uint16 M[MAXMEMSIZE] = { 0 }; /* memory */
int32 AC[4] = { 0 }; /* accumulators */
int32 C = 0; /* carry flag */
int32 saved_PC = 0; /* program counter */
int32 SP = 0; /* stack pointer */
int32 FP = 0; /* frame pointer */
int32 SR = 0; /* switch register */
int32 dev_done = 0; /* device done flags */
int32 dev_busy = 0; /* device busy flags */
int32 dev_disable = 0; /* int disable flags */
int32 iot_enb = -1; /* IOT enables */
int32 int_req = 0; /* interrupt requests */
int32 pimask = 0; /* priority int mask */
int32 pwr_low = 0; /* power fail flag */
int32 ind_max = 16; /* iadr nest limit */
int32 stop_dev = 0; /* stop on ill dev */
int32 old_PC = 0; /* previous PC */
extern int32 sim_int_char;
extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
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_boot (int32 unitno);
extern int32 ptr (int32 pulse, int32 code, int32 AC);
extern int32 ptp (int32 pulse, int32 code, int32 AC);
extern int32 tti (int32 pulse, int32 code, int32 AC);
extern int32 tto (int32 pulse, int32 code, int32 AC);
extern int32 tti1 (int32 pulse, int32 code, int32 AC);
extern int32 tto1 (int32 pulse, int32 code, int32 AC);
extern int32 clk (int32 pulse, int32 code, int32 AC);
extern int32 plt (int32 pulse, int32 code, int32 AC);
extern int32 lpt (int32 pulse, int32 code, int32 AC);
extern int32 dsk (int32 pulse, int32 code, int32 AC);
extern int32 dkp (int32 pulse, int32 code, int32 AC);
extern int32 mta (int32 pulse, int32 code, int32 AC);
int32 nulldev (int32 pulse, int32 code, int32 AC);
extern t_stat sim_activate (UNIT *uptr, int32 delay);
/* IOT dispatch table */
struct ndev dev_table[64] = {
{ 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 0 - 7 */
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ INT_TTI, PI_TTI, &tti }, { INT_TTO, PI_TTO, &tto }, /* 10 - 17 */
{ INT_PTR, PI_PTR, &ptr }, { INT_PTP, PI_PTP, &ptp },
{ INT_CLK, PI_CLK, &clk }, { INT_PLT, PI_PLT, &plt },
{ 0, 0, &nulldev }, { INT_LPT, PI_LPT, &lpt },
{ INT_DSK, PI_DSK, &dsk }, { 0, 0, &nulldev }, /* 20 - 27 */
{ INT_MTA, PI_MTA, &mta }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 30 - 37 */
{ 0, 0, &nulldev }, {INT_DKP, PI_DKP, &dkp },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 40 - 47 */
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ INT_TTI1, PI_TTI1, &tti1 }, { INT_TTO1, PI_TTO1, &tto1 }, /* 50 - 57 */
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 60 - 67 */
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev }, /* 70 - 77 */
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev },
{ 0, 0, &nulldev }, { 0, 0, &nulldev } };
/* CPU data structures
cpu_dev CPU device descriptor
cpu_unit CPU unit descriptor
cpu_reg CPU register list
cpu_mod CPU modifiers list
*/
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK + UNIT_MDV,
MAXMEMSIZE) };
REG cpu_reg[] = {
{ ORDATA (PC, saved_PC, 15) },
{ ORDATA (AC0, AC[0], 16) },
{ ORDATA (AC1, AC[1], 16) },
{ ORDATA (AC2, AC[2], 16) },
{ ORDATA (AC3, AC[3], 16) },
{ FLDATA (C, C, 16) },
{ ORDATA (SP, SP, 16) },
{ ORDATA (FP, FP, 16) },
{ ORDATA (SR, SR, 16) },
{ ORDATA (PI, pimask, 16) },
{ FLDATA (ION, int_req, INT_V_ION) },
{ FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) },
{ FLDATA (STKOVF, int_req, INT_V_STK) },
{ FLDATA (PWR, pwr_low, 0) },
{ ORDATA (INT, int_req, INT_V_ION+1), REG_RO },
{ ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO },
{ ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO },
{ ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO },
{ FLDATA (STOP_DEV, stop_dev, 0) },
{ FLDATA (MDV, cpu_unit.flags, UNIT_V_MDV), REG_HRO },
{ FLDATA (ISTK, cpu_unit.flags, UNIT_V_STK), REG_HRO },
{ FLDATA (IBYT, cpu_unit.flags, UNIT_V_BYT), REG_HRO },
{ DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT },
{ ORDATA (OLDPC, old_PC, 15), REG_RO },
{ ORDATA (WRU, sim_int_char, 8) },
{ ORDATA (IOTENB, iot_enb, 32), REG_HRO },
{ NULL } };
MTAB cpu_mod[] = {
{ UNIT_IOPT, UNIT_NOVA4, "NOVA4", "NOVA4", NULL },
{ UNIT_IOPT, UNIT_NOVA3, "NOVA3", "NOVA3", NULL },
{ UNIT_IOPT, UNIT_MDV, "MDV", "MDV", NULL },
{ UNIT_IOPT, 0, "none", "NONE", NULL },
{ UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size },
{ UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size },
{ UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size },
{ UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size },
{ UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size },
{ UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size },
{ UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size },
{ UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size },
{ 0 } };
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
1, 8, 15, 1, 8, 16,
&cpu_ex, &cpu_dep, &cpu_reset,
NULL, NULL, NULL };
t_stat sim_instr (void)
{
extern int32 sim_interval;
int32 PC, IR, i;
t_stat reason;
void mask_out (int32 mask);
extern int32 clk_sel, clk_time[4];
/* Restore register state */
PC = saved_PC & AMASK; /* load local PC */
C = C & CBIT;
mask_out (pimask); /* reset int system */
reason = 0;
sim_rtc_init (clk_time[clk_sel]); /* init calibration */
/* Main instruction fetch/decode loop */
while (reason == 0) { /* loop until halted */
if (sim_interval <= 0) { /* check clock queue */
if (reason = sim_process_event ()) break; }
if (int_req > INT_PENDING) { /* interrupt? */
int32 MA, indf;
int_req = int_req & ~INT_ION;
old_PC = M[INT_SAV] = PC;
if (int_req & INT_STK) { /* stack overflow? */
int_req = int_req & ~INT_STK; /* clear */
MA = STK_JMP; } /* jmp @3 */
else MA = INT_JMP; /* intr: jmp @1 */
for (i = 0, indf = 1; indf && (i < ind_max); i++) {
indf = IND_STEP (MA); } /* indirect loop */
if (i >= ind_max) {
reason = STOP_IND_INT;
break; }
PC = MA; } /* end interrupt */
if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
reason = STOP_IBKPT; /* stop simulation */
break; }
IR = M[PC]; /* fetch instr */
PC = (PC + 1) & AMASK;
int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */
sim_interval = sim_interval - 1;
/* Operate instruction */
if (IR & I_OPR) { /* operate? */
int32 src, srcAC, dstAC;
srcAC = I_GETSRC (IR); /* get reg decodes */
dstAC = I_GETDST (IR);
switch (I_GETCRY (IR)) { /* decode carry */
case 0: /* load */
src = AC[srcAC] | C;
break;
case 1: /* clear */
src = AC[srcAC];
break;
case 2: /* set */
src = AC[srcAC] | CBIT;
break;
case 3: /* complement */
src = AC[srcAC] | (C ^ CBIT);
break; } /* end switch carry */
switch (I_GETALU (IR)) { /* decode ALU */
case 0: /* COM */
src = src ^ DMASK;
break;
case 1: /* NEG */
src = ((src ^ DMASK) + 1) & CMASK;
break;
case 2: /* MOV */
break;
case 3: /* INC */
src = (src + 1) & CMASK;
break;
case 4: /* ADC */
src = ((src ^ DMASK) + AC[dstAC]) & CMASK;
break;
case 5: /* SUB */
src = ((src ^ DMASK) + AC[dstAC] + 1) & CMASK;
break;
case 6: /* ADD */
src = (src + AC[dstAC]) & CMASK;
break;
case 7: /* AND */
src = src & (AC[dstAC] | CBIT);
break; } /* end switch oper */
/* Operate, continued */
switch (I_GETSHF (IR)) { /* decode shift */
case 0: /* nop */
break;
case 1: /* L */
src = ((src << 1) | (src >> 16)) & CMASK;
break;
case 2: /* R */
src = ((src >> 1) | (src << 16)) & CMASK;
break;
case 3: /* S */
src = ((src & 0377) << 8) | ((src >> 8) & 0377) |
(src & CBIT);
break; } /* end switch shift */
switch (I_GETSKP (IR)) { /* decode skip */
case 0: /* nop */
if ((IR & I_NLD) && (cpu_unit.flags & UNIT_STK)) {
int32 indf, MA; /* Nova 3 or 4 trap */
old_PC = M[TRP_SAV] = (PC - 1) & AMASK;
MA = TRP_JMP; /* jmp @47 */
for (i = 0, indf = 1; indf && (i < ind_max); i++) {
indf = IND_STEP (MA); } /* resolve ind */
if (i >= ind_max) { /* indirect loop? */
reason = STOP_IND_TRP;
break; }
PC = MA; /* new PC */
break; }
break;
case 1: /* SKP */
PC = (PC + 1) & AMASK;
break;
case 2: /* SZC */
if (src < CBIT) PC = (PC + 1) & AMASK;
break;
case 3: /* SNC */
if (src >= CBIT) PC = (PC + 1) & AMASK;
break;
case 4: /* SZR */
if ((src & DMASK) == 0) PC = (PC + 1) & AMASK;
break;
case 5: /* SNR */
if ((src & DMASK) != 0) PC = (PC + 1) & AMASK;
break;
case 6: /* SEZ */
if (src <= CBIT) PC = (PC + 1) & AMASK;
break;
case 7: /* SBN */
if (src > CBIT) PC = (PC + 1) & AMASK;
break; } /* end switch skip */
if ((IR & I_NLD) == 0) { /* load? */
AC[dstAC] = src & DMASK;
C = src & CBIT; } /* end if load */
} /* end if operate */
/* Memory reference instructions */
else if (IR < 060000) { /* mem ref? */
int32 src, MA, indf;
MA = I_GETDISP (IR); /* get disp */
switch (I_GETMODE (IR)) { /* decode mode */
case 0: /* page zero */
break;
case 1: /* PC relative */
if (MA & DISPSIGN) MA = 077400 | MA;
MA = (MA + PC - 1) & AMASK;
break;
case 2: /* AC2 relative */
if (MA & DISPSIGN) MA = 077400 | MA;
MA = (MA + AC[2]) & AMASK;
break;
case 3: /* AC3 relative */
if (MA & DISPSIGN) MA = 077400 | MA;
MA = (MA + AC[3]) & AMASK;
break; } /* end switch mode */
if (indf = IR & I_IND) { /* indirect? */
for (i = 0; indf && (i < ind_max); i++) { /* count */
indf = IND_STEP (MA); } /* resolve indirect */
if (i >= ind_max) { /* too many? */
reason = STOP_IND;
break; } }
/* Memory reference, continued */
switch (I_GETOPAC (IR)) { /* decode op + AC */
case 001: /* JSR */
AC[3] = PC;
case 000: /* JMP */
old_PC = PC;
PC = MA;
break;
case 002: /* ISZ */
src = (M[MA] + 1) & DMASK;
if (MEM_ADDR_OK (MA)) M[MA] = src;
if (src == 0) PC = (PC + 1) & AMASK;
break;
case 003: /* DSZ */
src = (M[MA] - 1) & DMASK;
if (MEM_ADDR_OK (MA)) M[MA] = src;
if (src == 0) PC = (PC + 1) & AMASK;
break;
case 004: /* LDA 0 */
AC[0] = M[MA];
break;
case 005: /* LDA 1 */
AC[1] = M[MA];
break;
case 006: /* LDA 2 */
AC[2] = M[MA];
break;
case 007: /* LDA 3 */
AC[3] = M[MA];
break;
case 010: /* STA 0 */
if (MEM_ADDR_OK (MA)) M[MA] = AC[0];
break;
case 011: /* STA 1 */
if (MEM_ADDR_OK (MA)) M[MA] = AC[1];
break;
case 012: /* STA 2 */
if (MEM_ADDR_OK (MA)) M[MA] = AC[2];
break;
case 013: /* STA 3 */
if (MEM_ADDR_OK (MA)) M[MA] = AC[3];
break; } /* end switch */
} /* end mem ref */
/* IOT instruction */
else { /* IOT */
int32 dstAC, pulse, code, device, iodata;
dstAC = I_GETDST (IR); /* decode fields */
code = I_GETIOT (IR);
pulse = I_GETPULSE (IR);
device = I_GETDEV (IR);
if (code == ioSKP) { /* IO skip? */
switch (pulse) { /* decode IR<8:9> */
case 0: /* skip if busy */
if ((device == DEV_CPU)? (int_req & INT_ION) != 0:
(dev_busy & dev_table[device].mask) != 0)
PC = (PC + 1) & AMASK;
break;
case 1: /* skip if not busy */
if ((device == DEV_CPU)? (int_req & INT_ION) == 0:
(dev_busy & dev_table[device].mask) == 0)
PC = (PC + 1) & AMASK;
break;
case 2: /* skip if done */
if ((device == DEV_CPU)? pwr_low != 0:
(dev_done & dev_table[device].mask) != 0)
PC = (PC + 1) & AMASK;
break;
case 3: /* skip if not done */
if ((device == DEV_CPU)? pwr_low == 0:
(dev_done & dev_table[device].mask) == 0)
PC = (PC + 1) & AMASK;
break; } /* end switch */
} /* end IO skip */
/* IOT, continued */
else if (device == DEV_MDV) {
switch (code) { /* case on opcode */
case ioNIO: /* frame ptr */
if (cpu_unit.flags & UNIT_STK) {
if (pulse == iopN) FP = AC[dstAC] & AMASK;
if (pulse == iopC) AC[dstAC] = FP; }
break;
case ioDIA: /* load byte */
if (cpu_unit.flags & UNIT_BYT)
AC[dstAC] = (M[AC[pulse] >> 1] >>
((AC[pulse] & 1)? 0: 8)) & 0377;
else AC[dstAC] = 0;
break;
case ioDOA: /* stack ptr */
if (cpu_unit.flags & UNIT_STK) {
if (pulse == iopN) SP = AC[dstAC] & AMASK;
if (pulse == iopC) AC[dstAC] = SP; }
break;
case ioDIB: /* push, pop */
if (cpu_unit.flags & UNIT_STK) {
if (pulse == iopN) { /* push */
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];
STK_CHECK (SP, 1); }
if (pulse == iopC) { /* pop */
AC[dstAC] = M[SP];
SP = DECA (SP); }
if ((pulse == iopP) && /* Nova 4 pshn */
(cpu_unit.flags & UNIT_BYT)) {
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC];
if (SP > M[042]) int_req = int_req | INT_STK ;
}
}
break;
case ioDOB: /* store byte */
if (cpu_unit.flags & UNIT_BYT) {
int32 MA, val;
MA = AC[pulse] >> 1;
val = AC[dstAC] & 0377;
if (MEM_ADDR_OK (MA)) M[MA] = (AC[pulse] & 1)?
((M[MA] & ~0377) | val):
((M[MA] & 0377) | (val << 8)); }
break;
case ioDIC: /* save, return */
if (cpu_unit.flags & UNIT_STK) {
if (pulse == iopN) { /* save */
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[0];
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[1];
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[2];
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = FP;
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) |
(AC[3] & AMASK);
AC[3] = FP = SP & AMASK;
STK_CHECK (SP, 5); }
if (pulse == iopC) { /* retn */
old_PC = PC;
SP = FP & AMASK;
C = (M[SP] << 1) & CBIT;
PC = M[SP] & AMASK;
SP = DECA (SP);
AC[3] = M[SP];
SP = DECA (SP);
AC[2] = M[SP];
SP = DECA (SP);
AC[1] = M[SP];
SP = DECA (SP);
AC[0] = M[SP];
SP = DECA (SP);
FP = AC[3] & AMASK; }
if ((pulse == iopP) && /* Nova 4 saven */
(cpu_unit.flags & UNIT_BYT)) {
int32 frameSz = M[PC] ;
PC = INCA (PC) ;
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[0];
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[1];
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = AC[2];
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = FP;
SP = INCA (SP);
if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) |
(AC[3] & AMASK);
AC[3] = FP = SP & AMASK ;
SP = (SP + frameSz) & AMASK ;
if (SP > M[042]) int_req = int_req | INT_STK;
}
}
break;
case ioDOC:
if ((dstAC == 2) && (cpu_unit.flags & UNIT_MDV)) {
uint32 mddata, uAC0, uAC1, uAC2;
uAC0 = (unsigned int32) AC[0];
uAC1 = (unsigned int32) AC[1];
uAC2 = (unsigned int32) AC[2];
if (pulse == iopP) { /* mul */
mddata = (uAC1 * uAC2) + uAC0;
AC[0] = (mddata >> 16) & DMASK;
AC[1] = mddata & DMASK; }
if (pulse == iopS) { /* div */
if ((uAC0 >= uAC2) || (uAC2 == 0))
C = CBIT;
else { C = 0;
mddata = (uAC0 << 16) | uAC1;
AC[1] = mddata / uAC2;
AC[0] = mddata % uAC2; } } }
if ((dstAC == 3) && (cpu_unit.flags & UNIT_BYT)) {
int32 mddata;
if (pulse == iopC) { /* muls */
mddata = (SEXT (AC[1]) * SEXT (AC[2])) +
SEXT (AC[0]);
AC[0] = (mddata >> 16) & DMASK;
AC[1] = mddata & DMASK; }
if (pulse == iopN) { /* divs */
if (AC[2] == 0) C = CBIT;
else { mddata = (SEXT (AC[0]) << 16) | AC[1];
AC[1] = mddata / SEXT (AC[2]);
AC[0] = mddata % SEXT (AC[2]);
if ((AC[1] > 077777) || (AC[1] < -0100000))
C = CBIT;
else C = 0;
AC[0] = AC[0] & DMASK; } } }
break; } /* end case code */
} /* end if mul/div */
/* IOT, continued */
else if (device == DEV_CPU) { /* CPU control */
switch (code) { /* decode IR<5:7> */
case ioDIA: /* read switches */
AC[dstAC] = SR;
break;
case ioDIB: /* int ack */
AC[dstAC] = 0;
int_req = (int_req & ~INT_DEV) |
(dev_done & ~dev_disable);
iodata = int_req & (-int_req);
for (i = DEV_LOW; i <= DEV_HIGH; i++) {
if (iodata & dev_table[i].mask) {
AC[dstAC] = i; break; } }
break;
case ioDOB: /* mask out */
mask_out (pimask = AC[dstAC]);
break;
case ioDIC: /* io reset */
reset_all (0); /* reset devices */
break;
case ioDOC: /* halt */
reason = STOP_HALT;
break; } /* end switch code */
switch (pulse) { /* decode IR<8:9> */
case iopS: /* ion */
int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING;
break;
case iopC: /* iof */
int_req = int_req & ~INT_ION;
break; } /* end switch pulse */
} /* end CPU control */
else if ((dev_table[device].mask == 0) ||
(dev_table[device].mask & iot_enb)) { /* normal device */
iodata = dev_table[device].routine (pulse, code, AC[dstAC]);
reason = iodata >> IOT_V_REASON;
if (code & 1) AC[dstAC] = iodata & 0177777; }
else reason = stop_dev;
} /* end if IOT */
} /* end while */
/* Simulation halted */
saved_PC = PC;
return reason;
}
/* Null device */
int32 nulldev (int32 pulse, int32 code, int32 AC)
{
return stop_dev << IOT_V_REASON;
}
/* New priority mask out */
void mask_out (int32 newmask)
{
int32 i;
dev_disable = 0;
for (i = DEV_LOW; i <= DEV_HIGH; i++) {
if (newmask & dev_table[i].pi)
dev_disable = dev_disable | dev_table[i].mask; }
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
return;
}
/* Reset routine */
t_stat cpu_reset (DEVICE *dptr)
{
int_req = int_req & ~(INT_ION | INT_STK);
pimask = 0;
dev_disable = 0;
pwr_low = 0;
sim_brk_types = sim_brk_dflt = SWMASK ('E');
return SCPE_OK;
}
/* Memory examine */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE) return SCPE_NXM;
if (vptr != NULL) *vptr = M[addr] & DMASK;
return SCPE_OK;
}
/* Memory deposit */
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE) return SCPE_NXM;
M[addr] = val & DMASK;
return SCPE_OK;
}
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
{
int32 mc = 0;
t_addr i;
if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))
return SCPE_ARG;
for (i = val; i < MEMSIZE; i++) mc = mc | M[i];
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
return SCPE_OK;
MEMSIZE = val;
for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
return SCPE_OK;
}
/* Bootstrap routine for CPU */
#define BOOT_START 00000
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
static const int32 boot_rom[] = {
0062677, /* IORST ;reset all I/O */
0060477, /* READS 0 ;read SR into AC0 */
0024026, /* LDA 1,C77 ;get dev mask */
0107400, /* AND 0,1 ;isolate dev code */
0124000, /* COM 1,1 ;- device code - 1 */
0010014, /* LOOP: ISZ OP1 ;device code to all */
0010030, /* ISZ OP2 ;I/O instructions */
0010032, /* ISZ OP3 */
0125404, /* INC 1,1,SZR ;done? */
0000005, /* JMP LOOP ;no, increment again */
0030016, /* LDA 2,C377 ;place JMP 377 into */
0050377, /* STA 2,377 ;location 377 */
0060077, /* OP1: 060077 ;start device (NIOS 0) */
00101102, /* MOVL 0,0,SZC ;test switch 0, low speed? */
0000377, /* C377: JMP 377 ;no - jmp 377 & wait */
0004030, /* LOOP2: JSR GET+1 ;get a frame */
0101065, /* MOVC 0,0,SNR ;is it non-zero? */
0000017, /* JMP LOOP2 ;no, ignore */
0004027, /* LOOP4: JSR GET ;yes, get full word */
0046026, /* STA 1,@C77 ;store starting at 100 */
/* ;2's complement of word ct */
0010100, /* ISZ 100 ;done? */
0000022, /* JMP LOOP4 ;no, get another */
0000077, /* C77: JMP 77 ;yes location ctr and */
/* ;jmp to last word */
0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */
/* OP2: */
0063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */
0000030, /* JMP LOOP3 ;no -- wait */
0060477, /* OP3: 060477 ;y -- read in ac0 (DIAS 0,0) */
0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */
0000030, /* JMP LOOP3 ;no go back after it */
0125300, /* MOVS 1,1 ;yes swap them */
0001400, /* JMP 0,3 ;rtn with full word */
0000000 /* 0 ;padding */
};
t_stat cpu_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
saved_PC = BOOT_START;
return SCPE_OK;
}

235
NOVA/nova_defs.h Normal file
View File

@@ -0,0 +1,235 @@
/* nova_defs.h: NOVA/Eclipse simulator definitions
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
22-Dec-00 RMS Added Bruce Ray's second terminal support
10-Dec-00 RMS Added Charles Owen's Eclipse support
08-Dec-00 RMS Added Bruce Ray's plotter support
15-Oct-00 RMS Added stack, byte, trap instructions
14-Apr-99 RMS Changed t_addr to unsigned
16-Mar-95 RMS Added dynamic memory size
06-Dec-95 RMS Added magnetic tape
The author gratefully acknowledges the help of Tom West, Diana Englebart,
Carl Friend, Bruce Ray, and Charles Owen in resolving questions about
the NOVA.
*/
#include "sim_defs.h" /* simulator defns */
/* Simulator stop codes */
#define STOP_RSRV 1 /* must be 1 */
#define STOP_HALT 2 /* HALT */
#define STOP_IBKPT 3 /* breakpoint */
#define STOP_IND 4 /* indirect loop */
#define STOP_IND_INT 5 /* ind loop, intr */
#define STOP_IND_TRP 6 /* ind loop, trap */
/* Memory */
#if defined (ECLIPSE)
#define MAXMEMSIZE 1048576 /* max memory size */
#else
#define MAXMEMSIZE 32768 /* max memory size */
#endif
#define AMASK 077777 /* logical addr mask */
#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */
#define MEMSIZE (cpu_unit.capac) /* actual memory size */
#define A_V_IND 15 /* ind: indirect */
#define A_IND (1 << A_V_IND)
#define MEM_ADDR_OK(x) (((t_addr) (x)) < MEMSIZE)
/* Architectural constants */
#define SIGN 0100000 /* sign */
#define DMASK 0177777 /* data mask */
#define CBIT (DMASK + 1) /* carry bit */
#define CMASK (CBIT | DMASK) /* carry + data */
/* Reserved memory locations */
#define INT_SAV 0 /* intr saved PC */
#define INT_JMP 1 /* intr jmp @ */
#define STK_JMP 3 /* stack jmp @ */
#define TRP_SAV 046 /* trap saved PC */
#define TRP_JMP 047 /* trap jmp @ */
#define AUTO_INC 020 /* start autoinc */
#define AUTO_DEC 030 /* start autodec */
/* Instruction format */
#define I_OPR 0100000 /* operate */
#define I_M_SRC 03 /* OPR: src AC */
#define I_V_SRC 13
#define I_GETSRC(x) (((x) >> I_V_SRC) & I_M_SRC)
#define I_M_DST 03 /* dst AC */
#define I_V_DST 11
#define I_GETDST(x) (((x) >> I_V_DST) & I_M_DST)
#define I_M_ALU 07 /* OPR: ALU op */
#define I_V_ALU 8
#define I_GETALU(x) (((x) >> I_V_ALU) & I_M_ALU)
#define I_M_SHF 03 /* OPR: shift */
#define I_V_SHF 6
#define I_GETSHF(x) (((x) >> I_V_SHF) & I_M_SHF)
#define I_M_CRY 03 /* OPR: carry */
#define I_V_CRY 4
#define I_GETCRY(x) (((x) >> I_V_CRY) & I_M_CRY)
#define I_V_NLD 3 /* OPR: no load */
#define I_NLD (1 << I_V_NLD)
#define I_M_SKP 07 /* OPR: skip */
#define I_V_SKP 0
#define I_GETSKP(x) (((x) >> I_V_SKP) & I_M_SKP)
#define I_M_OPAC 017 /* MRF: opcode + AC */
#define I_V_OPAC 11
#define I_GETOPAC(x) (((x) >> I_V_OPAC) & I_M_OPAC)
#define I_V_IND 10 /* MRF: indirect */
#define I_IND (1 << I_V_IND)
#define I_M_MODE 03 /* MRF: mode */
#define I_V_MODE 8
#define I_GETMODE(x) (((x) >> I_V_MODE) & I_M_MODE)
#define I_M_DISP 0377 /* MRF: disp */
#define I_V_DISP 0
#define I_GETDISP(x) (((x) >> I_V_DISP) & I_M_DISP)
#define DISPSIZE (I_M_DISP + 1) /* page size */
#define DISPSIGN (DISPSIZE >> 1) /* page sign */
#define I_M_IOT 07 /* IOT: code */
#define I_V_IOT 8
#define I_GETIOT(x) (((x) >> I_V_IOT) & I_M_IOT)
#define I_M_PULSE 03 /* IOT pulse */
#define I_V_PULSE 6
#define I_GETPULSE(x) (((x) >> I_V_PULSE) & I_M_PULSE)
#define I_M_DEV 077 /* IOT: device */
#define I_V_DEV 0
#define I_GETDEV(x) (((x) >> I_V_DEV) & I_M_DEV)
#define I_M_XOP 037 /* XOP: code */
#define I_V_XOP 6
#define I_GETXOP(x) (((x) >> I_V_XOP) & I_M_XOP)
/* IOT return codes */
#define IOT_V_REASON 16 /* set reason */
#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */
/* IOT fields */
#define ioNIO 0 /* opcode field */
#define ioDIA 1
#define ioDOA 2
#define ioDIB 3
#define ioDOB 4
#define ioDIC 5
#define ioDOC 6
#define ioSKP 7
#define iopN 0 /* pulse field */
#define iopS 1
#define iopC 2
#define iopP 3
/* Device numbers */
#define DEV_LOW 010 /* lowest intr dev */
#define DEV_HIGH 051 /* highest intr dev */
#define DEV_MDV 001 /* multiply/divide */
#define DEV_MAP 003 /* MMPU control */
#define DEV_ECC 002 /* ECC memory control */
#define DEV_CPU 077 /* CPU control */
/* I/O structure
The NOVA I/O structure is tied together by dev_table, indexed by
the device number. Each entry in dev_table consists of
mask device mask for busy, done (simulator representation)
pi pi disable bit (hardware representation)
routine IOT action routine
*/
struct ndev {
int32 mask; /* done/busy mask */
int32 pi; /* assigned pi bit */
int32 (*routine)(); /* dispatch routine */
};
/* Device flags (simulator representation)
Priority (for INTA) runs from low numbers to high
*/
#define INT_V_DKP 3 /* moving head disk */
#define INT_V_DSK 4 /* fixed head disk */
#define INT_V_MTA 5 /* magnetic tape */
#define INT_V_LPT 6 /* line printer */
#define INT_V_CLK 7 /* clock */
#define INT_V_PTR 8 /* paper tape reader */
#define INT_V_PTP 9 /* paper tape punch */
#define INT_V_PLT 10 /* plotter */
#define INT_V_TTI 11 /* keyboard */
#define INT_V_TTO 12 /* terminal */
#define INT_V_TTI1 13 /* second keyboard */
#define INT_V_TTO1 14 /* second terminal */
#define INT_V_STK 15 /* stack overflow */
#define INT_V_NO_ION_PENDING 16 /* ion delay */
#define INT_V_ION 17 /* interrupts on */
#define INT_DKP (1 << INT_V_DKP)
#define INT_DSK (1 << INT_V_DSK)
#define INT_MTA (1 << INT_V_MTA)
#define INT_LPT (1 << INT_V_LPT)
#define INT_CLK (1 << INT_V_CLK)
#define INT_PTR (1 << INT_V_PTR)
#define INT_PTP (1 << INT_V_PTP)
#define INT_PLT (1 << INT_V_PLT)
#define INT_TTI (1 << INT_V_TTI)
#define INT_TTO (1 << INT_V_TTO)
#define INT_TTI1 (1 << INT_V_TTI1)
#define INT_TTO1 (1 << INT_V_TTO1)
#define INT_STK (1 << INT_V_STK)
#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING)
#define INT_ION (1 << INT_V_ION)
#define INT_DEV ((1 << INT_V_STK) - 1) /* device ints */
#define INT_PENDING INT_ION+INT_NO_ION_PENDING
/* PI disable bits */
#define PI_DKP 0000400
#define PI_DSK 0000100
#define PI_MTA 0000040
#define PI_LPT 0000010
#define PI_CLK 0000004
#define PI_PTR 0000020
#define PI_PTP 0000004
#define PI_PLT 0000010
#define PI_TTI 0000002
#define PI_TTO 0000001
#define PI_TTI1 PI_TTI
#define PI_TTO1 PI_TTO
/* #define PI_CDR 0000040 */
/* #define PI_DCM 0100000 */
/* #define PI_CAS 0000040 */
/* #define PI_ADCV 0000002 */

721
NOVA/nova_dkp.c Normal file
View File

@@ -0,0 +1,721 @@
/* nova_dkp.c: NOVA moving head disk simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
dkp moving head disk
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
24-Nov-01 RMS Changed FLG, CAPAC to arrays
26-Apr-01 RMS Added device enable/disable support
12-Dec-00 RMS Added Eclipse support from Charles Owen
15-Oct-00 RMS Editorial changes
14-Apr-99 RMS Changed t_addr to unsigned
15-Sep-97 RMS Fixed bug in DIB/DOB for new disks
15-Sep-97 RMS Fixed bug in cylinder extraction (found by Charles Owen)
10-Sep-97 RMS Fixed bug in error reporting (found by Charles Owen)
25-Nov-96 RMS Defaulted to autosize
29-Jun-96 RMS Added unit disable support
*/
#include "nova_defs.h"
#define DKP_NUMDR 4 /* #drives */
#define DKP_NUMWD 256 /* words/sector */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
#define UNIT_M_DTYPE 017
#define UNIT_V_AUTO (UNIT_V_UF + 5) /* autosize */
#define UNIT_W_UF 7 /* saved flag width */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define UNIT_AUTO (1 << UNIT_V_AUTO)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define FUNC u3 /* function */
#define CYL u4 /* on cylinder */
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* Unit, surface, sector, count register
Original format: 2b, 6b, 4b, 4b
Revised format: 2b, 5b, 5b, 4b
*/
#define USSC_V_COUNT 0 /* count */
#define USSC_M_COUNT 017
#define USSC_V_OSECTOR 4 /* old: sector */
#define USSC_M_OSECTOR 017
#define USSC_V_OSURFACE 8 /* old: surface */
#define USSC_M_OSURFACE 077
#define USSC_V_NSECTOR 4 /* new: sector */
#define USSC_M_NSECTOR 037
#define USSC_V_NSURFACE 9 /* new: surface */
#define USSC_M_NSURFACE 037
#define USSC_V_UNIT 14 /* unit */
#define USSC_M_UNIT 03
#define USSC_UNIT (USSC_M_UNIT << USSC_V_UNIT)
#define GET_COUNT(x) (((x) >> USSC_V_COUNT) & USSC_M_COUNT)
#define GET_SECT(x,dt) ((drv_tab[dt].new)? \
(((x) >> USSC_V_NSECTOR) & USSC_M_NSECTOR): \
(((x) >> USSC_V_OSECTOR) & USSC_M_OSECTOR) )
#define GET_SURF(x,dt) ((drv_tab[dt].new)? \
(((x) >> USSC_V_NSURFACE) & USSC_M_NSURFACE): \
(((x) >> USSC_V_OSURFACE) & USSC_M_OSURFACE) )
#define GET_UNIT(x) (((x) >> USSC_V_UNIT) & USSC_M_UNIT)
/* Flags, command, cylinder register
Original format: 5b, 2b, 1b + 8b (surrounding command)
Revised format: 5b, 2b, 9b
*/
#define FCCY_V_OCYL 0 /* old: cylinder */
#define FCCY_M_OCYL 0377
#define FCCY_V_OCMD 8 /* old: command */
#define FCCY_M_OCMD 3
#define FCCY_V_OCEX 10 /* old: cyl extend */
#define FCCY_OCEX (1 << FCCY_V_OCEX)
#define FCCY_V_NCYL 0 /* new: cylinder */
#define FCCY_M_NCYL 0777
#define FCCY_V_NCMD 9 /* new: command */
#define FCCY_M_NCMD 3
#define FCCY_READ 0
#define FCCY_WRITE 1
#define FCCY_SEEK 2
#define FCCY_RECAL 3
#define FCCY_FLAGS 0174000 /* flags */
#define GET_CMD(x,dt) ((drv_tab[dt].new)? \
(((x) >> FCCY_V_NCMD) & FCCY_M_NCMD): \
(((x) >> FCCY_V_OCMD) & FCCY_M_OCMD) )
#define GET_CYL(x,dt) ((drv_tab[dt].new)? \
(((x) >> FCCY_V_NCYL) & FCCY_M_NCYL): \
((((x) >> FCCY_V_OCYL) & FCCY_M_OCYL) | \
((dt != TYPE_D44)? 0: \
(((x) & FCCY_OCEX) >> (FCCY_V_OCEX - FCCY_V_OCMD)))) )
/* Status */
#define STA_ERR 0000001 /* error */
#define STA_DLT 0000002 /* data late */
#define STA_CRC 0000004 /* crc error */
#define STA_UNS 0000010 /* unsafe */
#define STA_XCY 0000020 /* cross cylinder */
#define STA_CYL 0000040 /* nx cylinder */
#define STA_DRDY 0000100 /* drive ready */
#define STA_SEEK3 0000200 /* seeking unit 3 */
#define STA_SEEK2 0000400 /* seeking unit 2 */
#define STA_SEEK1 0001000 /* seeking unit 1 */
#define STA_SEEK0 0002000 /* seeking unit 0 */
#define STA_SKDN3 0004000 /* seek done unit 3 */
#define STA_SKDN2 0010000 /* seek done unit 2 */
#define STA_SKDN1 0020000 /* seek done unit 1 */
#define STA_SKDN0 0040000 /* seek done unit 0 */
#define STA_DONE 0100000 /* operation done */
#define STA_DYN (STA_DRDY | STA_CYL) /* set from unit */
#define STA_EFLGS (STA_ERR | STA_DLT | STA_CRC | STA_UNS | \
STA_XCY | STA_CYL) /* error flags */
#define STA_DFLGS (STA_DONE | STA_SKDN0 | STA_SKDN1 | \
STA_SKDN2 | STA_SKDN3) /* done flags */
#define GET_SA(cy,sf,sc,t) (((((cy)*drv_tab[t].surf)+(sf))* \
drv_tab[t].sect)+(sc))
/* This controller supports many different disk drive types:
type #sectors/ #surfaces/ #cylinders/ new format?
surface cylinder drive
floppy 8 1 77 no
DS/DD floppy 16 2 77 yes
Diablo 31 12 2 203 no
6225 20 2 245 yes
Century 111 6 10 203 no
Diablo 44 12 4 408 no
6099 32 4 192 yes
6227 20 6 245 yes
6070 24 4 408 yes
Century 114 12 20 203 no
6103 32 8 192 yes
4231 23 19 411 yes
In theory, each drive can be a different type. The size field in
each unit selects the drive capacity for each drive and thus the
drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE.
*/
#define TYPE_FLP 0
#define SECT_FLP 8
#define SURF_FLP 1
#define CYL_FLP 77
#define SIZE_FLP (SECT_FLP * SURF_FLP * CYL_FLP * DKP_NUMWD)
#define NFMT_FLP FALSE
#define TYPE_DSDD 1
#define SECT_DSDD 16
#define SURF_DSDD 2
#define CYL_DSDD 77
#define SIZE_DSDD (SECT_DSDD * SURF_DSDD * CYL_DSDD * DKP_NUMWD)
#define NFMT_DSDD TRUE
#define TYPE_D31 2
#define SECT_D31 12
#define SURF_D31 2
#define CYL_D31 203
#define SIZE_D31 (SECT_D31 * SURF_D31 * CYL_D31 * DKP_NUMWD)
#define NFMT_D31 FALSE
#define TYPE_6225 3
#define SECT_6225 20
#define SURF_6225 2
#define CYL_6225 245
#define SIZE_6225 (SECT_6225 * SURF_6225 * CYL_6225 * DKP_NUMWD)
#define NFMT_6225 TRUE
#define TYPE_C111 4
#define SECT_C111 6
#define SURF_C111 10
#define CYL_C111 203
#define SIZE_C111 (SECT_C111 * SURF_C111 * CYL_C111 * DKP_NUMWD)
#define NFMT_C111 FALSE
#define TYPE_D44 5
#define SECT_D44 12
#define SURF_D44 4
#define CYL_D44 408
#define SIZE_D44 (SECT_D44 * SURF_D44 * CYL_D44 * DKP_NUMWD)
#define NFMT_D44 FALSE
#define TYPE_6099 6
#define SECT_6099 32
#define SURF_6099 4
#define CYL_6099 192
#define SIZE_6099 (SECT_6099 * SURF_6099 * CYL_6099 * DKP_NUMWD)
#define NFMT_6099 TRUE
#define TYPE_6227 7
#define SECT_6227 20
#define SURF_6227 6
#define CYL_6227 245
#define SIZE_6227 (SECT_6227 * SURF_6227 * CYL_6227 * DKP_NUMWD)
#define NFMT_6227 TRUE
#define TYPE_6070 8
#define SECT_6070 24
#define SURF_6070 4
#define CYL_6070 408
#define SIZE_6070 (SECT_6070 * SURF_6070 * CYL_6070 * DKP_NUMWD)
#define NFMT_6070 TRUE
#define TYPE_C114 9
#define SECT_C114 12
#define SURF_C114 20
#define CYL_C114 203
#define SIZE_C114 (SECT_C114 * SURF_C114 * CYL_C114 * DKP_NUMWD)
#define NFMT_C114 FALSE
#define TYPE_6103 10
#define SECT_6103 32
#define SURF_6103 8
#define CYL_6103 192
#define SIZE_6103 (SECT_6103 * SURF_6103 * CYL_6103 * DKP_NUMWD)
#define NFMT_6103 TRUE
#define TYPE_4231 11
#define SECT_4231 23
#define SURF_4231 19
#define CYL_4231 411
#define SIZE_4231 (SECT_4231 * SURF_4231 * CYL_4231 * DKP_NUMWD)
#define NFMT_4231 TRUE
struct drvtyp {
int32 sect; /* sectors */
int32 surf; /* surfaces */
int32 cyl; /* cylinders */
int32 size; /* #blocks */
int32 new; /* new format flag */
};
struct drvtyp drv_tab[] = {
{ SECT_FLP, SURF_FLP, CYL_FLP, SIZE_FLP, NFMT_FLP },
{ SECT_DSDD, SURF_DSDD, CYL_DSDD, SIZE_DSDD, NFMT_DSDD },
{ SECT_D31, SURF_D31, CYL_D31, SIZE_D31, NFMT_D31 },
{ SECT_6225, SURF_6225, CYL_6225, SIZE_6225, NFMT_6225 },
{ SECT_C111, SURF_C111, CYL_C111, SIZE_C111, NFMT_C111 },
{ SECT_D44, SURF_D44, CYL_D44, SIZE_D44, NFMT_D44 },
{ SECT_6099, SURF_6099, CYL_6099, SIZE_6099, NFMT_6099 },
{ SECT_6227, SURF_6227, CYL_6227, SIZE_6227, NFMT_6227 },
{ SECT_6070, SURF_6070, CYL_6070, SIZE_6070, NFMT_6070 },
{ SECT_C114, SURF_C114, CYL_C114, SIZE_C114, NFMT_C114 },
{ SECT_6103, SURF_6103, CYL_6103, SIZE_6103, NFMT_6103 },
{ SECT_4231, SURF_4231, CYL_4231, SIZE_4231, NFMT_4231 },
{ 0 } };
extern uint16 M[];
extern UNIT cpu_unit;
extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb;
int32 dkp_ma = 0; /* memory address */
int32 dkp_ussc = 0; /* unit/sf/sc/cnt */
int32 dkp_fccy = 0; /* flags/cylinder */
int32 dkp_sta = 0; /* status register */
int32 dkp_swait = 100; /* seek latency */
int32 dkp_rwait = 100; /* rotate latency */
t_stat dkp_svc (UNIT *uptr);
t_stat dkp_reset (DEVICE *dptr);
t_stat dkp_boot (int32 unitno);
t_stat dkp_attach (UNIT *uptr, char *cptr);
t_stat dkp_go (void);
t_stat dkp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
#if defined (ECLIPSE)
extern int32 MapAddr (int32 map, int32 addr);
#else
#define MapAddr(m,a) (a)
#endif
/* DKP data structures
dkp_dev DKP device descriptor
dkp_unit DKP unit list
dkp_reg DKP register list
dkp_mod DKP modifier list
*/
UNIT dkp_unit[] = {
{ UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) },
{ UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) },
{ UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) },
{ UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+
UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) } };
REG dkp_reg[] = {
{ ORDATA (FCCY, dkp_fccy, 16) },
{ ORDATA (USSC, dkp_ussc, 16) },
{ ORDATA (STA, dkp_sta, 16) },
{ ORDATA (MA, dkp_ma, 16) },
{ FLDATA (INT, int_req, INT_V_DKP) },
{ FLDATA (BUSY, dev_busy, INT_V_DKP) },
{ FLDATA (DONE, dev_done, INT_V_DKP) },
{ FLDATA (DISABLE, dev_disable, INT_V_DKP) },
{ DRDATA (STIME, dkp_swait, 24), PV_LEFT },
{ DRDATA (RTIME, dkp_rwait, 24), PV_LEFT },
{ URDATA (FLG, dkp_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
DKP_NUMDR, REG_HRO) },
{ URDATA (CAPAC, dkp_unit[0].capac, 10, 31, 0,
DKP_NUMDR, PV_LEFT | REG_HRO) },
{ FLDATA (*DEVENB, iot_enb, INT_V_DKP), REG_HRO },
{ NULL } };
MTAB dkp_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", NULL },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE) + UNIT_ATT,
"6030 (floppy)", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE) + UNIT_ATT,
"6097 (DS/DD floppy)", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE) + UNIT_ATT,
"4047 (Diablo 31)", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE) + UNIT_ATT,
"4234/6045 (Diablo 44)", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE) + UNIT_ATT,
"4048 (Century 111)", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE) + UNIT_ATT,
"2314/4057 (Century 114)", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE) + UNIT_ATT,
"6225", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE) + UNIT_ATT,
"6227", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE) + UNIT_ATT,
"6099", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE) + UNIT_ATT,
"6103", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE) + UNIT_ATT,
"6070", NULL, NULL },
{ (UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE) + UNIT_ATT,
"4231/3330", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE),
"6030 (floppy)", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE),
"6097 (DS/DD floppy)", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE),
"4047 (Diablo 31)", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE),
"4234/6045 (Diablo 44)", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE),
"4048 (Century 111)", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE),
"2314/4057 (Century 114)", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE),
"6225", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE),
"6227", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE),
"6099", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE),
"6103", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE),
"6070", NULL, NULL },
{ (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE),
"4231/3330", NULL, NULL },
{ (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL },
{ UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE),
NULL, "FLOPPY", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE),
NULL, "6030", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE),
NULL, "DSDDFLOPPY", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE),
NULL, "6097", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE),
NULL, "D31", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE),
NULL, "4047", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE),
NULL, "D44", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE),
NULL, "4234", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE),
NULL, "6045", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE),
NULL, "C111", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE),
NULL, "4048", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE),
NULL, "C114", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE),
NULL, "2314", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE),
NULL, "4057", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_6225 << UNIT_V_DTYPE),
NULL, "6225", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_6227 << UNIT_V_DTYPE),
NULL, "6227", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_6099 << UNIT_V_DTYPE),
NULL, "6099", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_6103 << UNIT_V_DTYPE),
NULL, "6103", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_6070 << UNIT_V_DTYPE),
NULL, "6070", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE),
NULL, "4231", &dkp_set_size },
{ (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE),
NULL, "3330", &dkp_set_size },
{ 0 } };
DEVICE dkp_dev = {
"DP", dkp_unit, dkp_reg, dkp_mod,
DKP_NUMDR, 8, 30, 1, 8, 16,
NULL, NULL, &dkp_reset,
&dkp_boot, &dkp_attach, NULL };
/* IOT routine */
int32 dkp (int32 pulse, int32 code, int32 AC)
{
UNIT *uptr;
int32 u, rval, dtype;
rval = 0;
uptr = dkp_dev.units + GET_UNIT (dkp_ussc); /* select unit */
dtype = GET_DTYPE (uptr -> flags); /* get drive type */
switch (code) { /* decode IR<5:7> */
case ioDIA: /* DIA */
dkp_sta = dkp_sta & ~STA_DYN; /* clear dynamic */
if (uptr -> flags & UNIT_ATT) dkp_sta = dkp_sta | STA_DRDY;
if (uptr -> CYL >= drv_tab[dtype].cyl)
dkp_sta = dkp_sta | STA_CYL; /* bad cylinder? */
if (dkp_sta & STA_EFLGS) dkp_sta = dkp_sta | STA_ERR;
rval = dkp_sta;
break;
case ioDOA: /* DOA */
if ((dev_busy & INT_DKP) == 0) {
dkp_fccy = AC; /* save cmd, cyl */
dkp_sta = dkp_sta & ~(AC & FCCY_FLAGS); }
break;
case ioDIB: /* DIB */
rval = dkp_ma; /* return buf addr */
break;
case ioDOB: /* DOB */
if ((dev_busy & INT_DKP) == 0) dkp_ma =
AC & (drv_tab[dtype].new? DMASK: AMASK);
break;
case ioDIC: /* DIC */
rval = dkp_ussc; /* return unit, sect */
break;
case ioDOC: /* DOC */
if ((dev_busy & INT_DKP) == 0) dkp_ussc = AC; /* save unit, sect */
break; } /* end switch code */
/* IOT, continued */
u = GET_UNIT(dkp_ussc); /* select unit */
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_DKP; /* set busy */
dev_done = dev_done & ~INT_DKP; /* clear done */
int_req = int_req & ~INT_DKP; /* clear int */
if (dkp_go ()) break; /* new cmd, error? */
dev_busy = dev_busy & ~INT_DKP; /* clear busy */
dev_done = dev_done | INT_DKP; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
dkp_sta = dkp_sta | STA_DONE;
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_DKP; /* clear busy */
dev_done = dev_done & ~INT_DKP; /* clear done */
int_req = int_req & ~INT_DKP; /* clear int */
dkp_sta = dkp_sta & ~(STA_DFLGS + STA_EFLGS);
if (dkp_unit[u].FUNC != FCCY_SEEK) sim_cancel (&dkp_unit[u]);
break;
case iopP: /* pulse */
dev_done = dev_done & ~INT_DKP; /* clear done */
if (dkp_go ()) break; /* new seek command */
dev_done = dev_done | INT_DKP; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
dkp_sta = dkp_sta | (STA_SKDN0 >> u); /* set seek done */
break; } /* end case pulse */
return rval;
}
/* New command, start vs pulse handled externally
Returns true if command ok, false if error
*/
t_stat dkp_go (void)
{
UNIT *uptr;
int32 newcyl, func, u, dtype;
dkp_sta = dkp_sta & ~STA_EFLGS; /* clear errors */
u = GET_UNIT (dkp_ussc); /* get unit number */
uptr = dkp_dev.units + u; /* get unit */
if (((uptr -> flags & UNIT_ATT) == 0) || sim_is_active (uptr)) {
dkp_sta = dkp_sta | STA_ERR; /* attached or busy? */
return FALSE; }
dtype = GET_DTYPE (uptr -> flags); /* get drive type */
func = GET_CMD (dkp_fccy, dtype); /* get function */
newcyl = GET_CYL (dkp_fccy, dtype); /* get cylinder */
switch (func) { /* decode command */
case FCCY_READ: case FCCY_WRITE:
sim_activate (uptr, dkp_rwait); /* schedule */
break;
case FCCY_RECAL: /* recalibrate */
newcyl = 0;
func = FCCY_SEEK;
case FCCY_SEEK: /* seek */
sim_activate (uptr, dkp_swait * abs (newcyl - uptr -> CYL));
dkp_sta = dkp_sta | (STA_SEEK0 >> u); /* set seeking */
uptr -> CYL = newcyl; /* on cylinder */
break; } /* end case command */
uptr -> FUNC = func; /* save command */
return TRUE; /* no error */
}
/* Unit service
If seek done, put on cylinder;
else, do read or write
If controller was busy, clear busy, set done, interrupt
Memory access: sectors are read into/written from an intermediate
buffer to allow word-by-word mapping of memory addresses on the
Eclipse. This allows each word written to memory to be tested
for out of range.
*/
t_stat dkp_svc (UNIT *uptr)
{
int32 sc, sa, xcsa, awc, bda;
int32 sx, dx, pa;
int32 dtype, u, err, newsect, newsurf;
t_stat rval;
static uint16 tbuf[DKP_NUMWD]; /* transfer buffer */
rval = SCPE_OK;
dtype = GET_DTYPE (uptr -> flags); /* get drive type */
if (uptr -> FUNC == FCCY_SEEK) { /* seek? */
if (uptr -> CYL >= drv_tab[dtype].cyl) /* bad cylinder? */
dkp_sta = dkp_sta | STA_ERR | STA_CYL;
dev_done = dev_done | INT_DKP; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
u = uptr - dkp_dev.units; /* get unit number */
dkp_sta = (dkp_sta | (STA_SKDN0 >> u)) /* set seek done */
& ~(STA_SEEK0 >> u); /* clear seeking */
return SCPE_OK; }
if (((uptr -> flags & UNIT_ATT) == 0) || /* not attached? */
((uptr -> flags & UNIT_WPRT) && (uptr -> FUNC == FCCY_WRITE)))
dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */
else if ((uptr -> CYL >= drv_tab[dtype].cyl) || /* bad cylinder */
(GET_SURF (dkp_ussc, dtype) >= drv_tab[dtype].surf) || /* bad surface */
(GET_SECT (dkp_ussc, dtype) >= drv_tab[dtype].sect)) /* or bad sector? */
dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS;
else if (GET_CYL (dkp_fccy, dtype) != uptr -> CYL) /* address error? */
dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS;
else { sc = 16 - GET_COUNT (dkp_ussc); /* get sector count */
sa = GET_SA (uptr -> CYL, GET_SURF (dkp_ussc, dtype),
GET_SECT (dkp_ussc, dtype), dtype); /* get disk block */
xcsa = GET_SA (uptr -> CYL + 1, 0, 0, dtype); /* get next cyl addr */
if ((sa + sc) > xcsa ) { /* across cylinder? */
sc = xcsa - sa; /* limit transfer */
dkp_sta = dkp_sta | STA_XCY; } /* xcyl error */
bda = sa * DKP_NUMWD * sizeof (short); /* to words, bytes */
err = fseek (uptr -> fileref, bda, SEEK_SET); /* position drive */
if (uptr -> FUNC == FCCY_READ) { /* read? */
for (sx = 0; sx < sc; sx++) { /* loop thru sectors */
awc = fxread (tbuf, sizeof(uint16), DKP_NUMWD, uptr -> fileref);
for ( ; awc < DKP_NUMWD; awc++) tbuf[awc] = 0;
if (err = ferror (uptr -> fileref)) break;
for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop thru buffer */
pa = MapAddr (0, dkp_ma);
if (MEM_ADDR_OK (pa)) M[pa] = tbuf[dx];
dkp_ma = (dkp_ma + 1) & AMASK; } } }
if (uptr -> FUNC == FCCY_WRITE) { /* write? */
for (sx = 0; sx < sc; sx++) { /* loop thru sectors */
for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop into buffer */
pa = MapAddr (0, dkp_ma);
tbuf[dx] = M[pa];
dkp_ma = (dkp_ma + 1) & AMASK; }
fxwrite (tbuf, sizeof(int16), DKP_NUMWD, uptr -> fileref);
if (err = ferror (uptr -> fileref)) break; } }
if (err != 0) {
perror ("DKP I/O error");
rval = SCPE_IOERR; }
clearerr (uptr -> fileref);
sa = sa + sc; /* update sector addr */
newsect = sa % drv_tab[dtype].sect;
newsurf = (sa / drv_tab[dtype].sect) % drv_tab[dtype].surf;
dkp_ussc = (dkp_ussc & USSC_UNIT) | ((dkp_ussc + sc) & USSC_M_COUNT) |
((drv_tab[dtype].new)?
((newsurf << USSC_V_NSURFACE) | (newsect << USSC_V_NSECTOR)):
((newsurf << USSC_V_OSURFACE) | (newsect << USSC_V_OSECTOR)) );
dkp_sta = dkp_sta | STA_DONE; } /* set status */
dev_busy = dev_busy & ~INT_DKP; /* clear busy */
dev_done = dev_done | INT_DKP; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
return rval;
}
/* Reset routine */
t_stat dkp_reset (DEVICE *dptr)
{
int32 u;
UNIT *uptr;
dev_busy = dev_busy & ~INT_DKP; /* clear busy */
dev_done = dev_done & ~INT_DKP; /* clear done, int */
int_req = int_req & ~INT_DKP;
dkp_fccy = dkp_ussc = dkp_ma = dkp_sta = 0; /* clear registers */
for (u = 0; u < DKP_NUMDR; u++) { /* loop thru units */
uptr = dkp_dev.units + u;
sim_cancel (uptr); /* cancel activity */
uptr -> CYL = uptr -> FUNC = 0; }
return SCPE_OK;
}
/* Attach routine (with optional autosizing) */
t_stat dkp_attach (UNIT *uptr, char *cptr)
{
int32 i, p;
t_stat r;
uptr -> capac = drv_tab[GET_DTYPE (uptr -> flags)].size;
r = attach_unit (uptr, cptr);
if ((r != SCPE_OK) || ((uptr -> flags & UNIT_AUTO) == 0)) return r;
if (fseek (uptr -> fileref, 0, SEEK_END)) return SCPE_OK;
if ((p = ftell (uptr -> fileref)) == 0) return SCPE_OK;
for (i = 0; drv_tab[i].sect != 0; i++) {
if (p <= (drv_tab[i].size * (int) sizeof (short))) {
uptr -> flags = (uptr -> flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE);
uptr -> capac = drv_tab[i].size;
return SCPE_OK; } }
return SCPE_OK;
}
/* Set size command validation routine */
t_stat dkp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if (uptr -> flags & UNIT_ATT) return SCPE_ALATT;
uptr -> capac = drv_tab[GET_DTYPE (val)].size;
return SCPE_OK;
}
/* Bootstrap routine */
#define BOOT_START 02000
#define BOOT_UNIT 02021
#define BOOT_SEEK 02022
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
static const int32 boot_rom[] = {
060233, /* NIOC 0,DKP ; clear disk */
020420, /* LDA 0,USSC ; unit, sfc, sec, cnt */
063033, /* DOC 0,DKP ; select disk */
020417, /* LDA 0,SEKCMD ; command, cylinder */
061333, /* DOAP 0,DKP ; start seek */
024415, /* LDA 1,SEKDN */
060433, /* DIA 0,DKP ; get status */
0123415, /* AND# 1,0,SZR ; skip if done */
000776, /* JMP .-2 */
0102400, /* SUB 0,0 ; mem addr = 0 */
062033, /* DOB 0,DKP */
020411, /* LDA 0,REDCMD ; command, cylinder */
061133, /* DOAS 0,DKP ; start read */
060433, /* DIA 0, DKP ; get status */
0101113, /* MOVL# 0,0,SNC ; skip if done */
000776, /* JMP .-2 */
000377, /* JMP 377 */
000016, /* USSC: 0.B1+0.B7+0.B11+16 */
0175000, /* SEKCMD: 175000 */
074000, /* SEKDN: 074000 */
0174000 /* REDCMD: 174000 */
};
t_stat dkp_boot (int32 unitno)
{
int32 i, dtype;
extern int32 saved_PC;
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
unitno = unitno & USSC_M_UNIT;
dtype = GET_DTYPE (dkp_unit[unitno].flags);
M[BOOT_UNIT] = M[BOOT_UNIT] | (unitno << USSC_V_UNIT);
if (drv_tab[dtype].new) M[BOOT_SEEK] = 0176000;
saved_PC = BOOT_START;
return SCPE_OK;
}

565
NOVA/nova_doc.txt Normal file
View File

@@ -0,0 +1,565 @@
To: Users
From: Bob Supnik
Subj: Nova Simulator Usage
Date: 1-Dec-01
COPYRIGHT NOTICE
The following copyright notice applies to both the SIMH source and binary:
Original code published in 1993-2001, written by Robert M Supnik
Copyright (c) 1993-2001, Robert M Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
This memorandum documents the Nova simulator.
1. Simulator Files
sim/ sim_defs.h
sim_sock.h
sim_tmxr.h
scp.c
scp_tty.c
sim_rev.c
sim_sock.c
sim_tmxr.c
sim/nova/ nova_defs.h
nova_cpu.c
nova_clk.c
nova_dkp.c
nova_dsk.c
nova_lp.c
nova_mta.c
nova_plt.c
nova_sys.c
nova_tt.c
nova_tt1.c
2. Nova Features
The Nova simulator is configured as follows:
device simulates
name(s)
CPU Nova CPU with 32KW of memory
- hardware multiply/divide
PTR,PTP paper tape reader/punch
TTI,TTO console terminal
TTI1,TTO1 second terminal
LPT line printer
PLT plotter
CLK real-time clock
DK head-per-track disk controller
DP moving head disk controller with four drives
MT magnetic tape controller with eight drives
The TTI1/TTO1, PLT, DK, DP, and MT devices can be DISABLEd.
The Nova simulator implements these unique stop conditions:
- reference to undefined I/O device, and STOP_DEV is set
- more than INDMAX indirect addresses are detected during
an interrupt
- more than INDMAX indirect addresses are detected during
memory reference address decoding
The Nova loader supports standard binary format tapes. The DUMP command
is not implemented.
2.1 CPU
The only CPU options are the presence of the optional instructions
and the size of main memory.
SET CPU NOVA4 enable Nova4 instructions
SET CPU NOVA3 enable Nova3 instructions
SET CPU MDV enable multiply/divide
SET CPU NONE disable all optional instructions
SET CPU 4K set memory size = 4K
SET CPU 8K set memory size = 8K
SET CPU 12K set memory size = 12K
SET CPU 16K set memory size = 16K
SET CPU 20K set memory size = 20K
SET CPU 24K set memory size = 24K
SET CPU 28K set memory size = 28K
SET CPU 32K set memory size = 32K
(MDV = unsigned multiply/divide instructions)
(Nova 3 = unsigned multiply/divide, stack, trap instructions)
(Nova 4 = unsigned and signed multiply/divide, stack, byte, trap instructions)
If memory size is being reduced, and the memory being truncated contains
non-zero data, the simulator asks for confirmation. Data in the truncated
portion of memory is lost. Initial memory size is 32K.
CPU registers include the visible state of the processor as well as the
control registers for the interrupt system.
name size comments
PC 15 program counter
AC0..AC3 16 accumulators 0..3
C 1 carry
SR 16 front panel switches
PI 16 priority interrupt mask
ION 1 interrupt enable
ION_DELAY 1 interrupt enable delay for ION
PWR 1 power fail interrupt
INT 15 interrupt pending flags
BUSY 15 device busy flags
DONE 15 device done flags
DISABLE 15 device interrupt disable flags
STOP_DEV 1 stop on undefined IOT
INDMAX 15 maximum number of nested indirects
OLDPC 15 PC prior to last JMP, JMS, or interrupt
WRU 8 interrupt character
2.2 Programmed I/O Devices
2.2.1 Paper Tape Reader (PTR)
The paper tape reader (PTR) reads data from a disk file. The POS
register specifies the number of the next data item to be read. Thus,
by changing POS, the user can backspace or advance the reader.
The paper tape reader implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 position in the input file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of tape or paper
end of file 1 report error and stop
0 out of tape or paper
OS I/O error x report error and stop
2.2.2 Paper Tape Punch (PTP)
The paper tape punch (PTP) writes data to a disk file. The POS
register specifies the number of the next data item to be written.
Thus, by changing POS, the user can backspace or advance the punch.
The paper tape punch implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 position in the output file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of tape or paper
OS I/O error x report error and stop
2.2.3 Terminal Input (TTI)
The terminal input polls the console keyboard for input. Terminal
input options include the ability to set ANSI mode or limited Dasher
compatibility mode:
SET TTI ANSI normal mode
SET TTI DASHER Dasher mode
Setting either TTI or TTO changes both devices. In Dasher mode, carriage
return is changed to newline on input, and ^X is changed to backspace.
The terminal input implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 number of characters input
TIME 24 keyboard polling interval
2.2.4 Terminal Output (TTO)
The terminal output writes to the simulator console window. Terminal
output options include the the ability to set ANSI mode or limited
Dasher compatibility mode:
SET TTO ANSI normal mode
SET TTO DASHER Dasher mode
Setting either TTI or TTO changes both devices. In Dasher mode, carriage
return is changed to newline on input, and ^X is changed to backspace.
The terminal output implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 number of characters output
TIME 24 time from I/O initiation to interrupt
2.2.5 Line Printer (LPT)
The line printer (LPT) writes data to a disk file. The POS register
specifies the number of the next data item to be written. Thus,
by changing POS, the user can backspace or advance the printer.
The line printer implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 position in the output file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of paper
OS I/O error x report error and stop
2.2.6 Real-Time Clock (CLK)
The real-time clock (CLK) implements these registers:
name size comments
SELECT 2 selected clock interval
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
TIME0 24 clock frequency, select = 0
TIME1 24 clock frequency, select = 1
TIME2 24 clock frequency, select = 2
TIME3 24 clock frequency, select = 3
The real-time clock autocalibrates; the clock interval is adjusted up or
down so that the clock tracks actual elapsed time.
2.2.7 Plotter (PTP)
The plotter (PLT) writes data to a disk file. The POS register
specifies the number of the next data item to be written. Thus,
by changing POS, the user can backspace or advance the plotter.
The plotter implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 position in the output file
TIME 24 time from I/O initiation to interrupt
STOP_IOE 1 stop on I/O error
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 out of tape or paper
OS I/O error x report error and stop
2.2.8 Second Terminal (TTI1, TTO1)
The second terminal consists of two independent devices, TTI1 and TTO1.
The additional terminal performs input and output through a Telnet session
connecting into a user-specified port. The ATTACH command specifies the
port to be used:
ATTACH TTI1 <port>(cr) -- set up listening port
where port is a decimal number between 1 and 65535 that is not being used
for other TCP/IP activities.
Once TTI1 is attached and the simulator is running, the terminal listens
for a connection on the specified port. It assumes that the incoming
connection is a Telnet connection. The connection remain opens until
disconnected by the Telnet client, or by a DETACH TTI1 command.
The second terminal has two options, recognized on both devices, for
setting limited Dasher-compatibility mode or ANSI mode:
SET TTI1 ANSI normal mode
SET TTI1 DASHER Dasher mode
SET TTO1 ANSI normal mode
SET TTO1 DASHER Dasher mode
Setting either TTI1 or TTO1 changes both devices. In Dasher mode, carriage
return is changed to newline on input, and ^X is changed to backspace.
The SHOW TTI1 LINESTATUS command displays the current connection to the
second terminal.
The second terminal input implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 number of characters input
TIME 24 keyboard polling interval
The second terminal output implements these registers:
name size comments
BUF 8 last data item processed
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
POS 31 number of characters output
TIME 24 time from I/O initiation to interrupt
2.3 Fixed Head Disk (DK)
The fixed head disk controller implements these registers:
name size comments
STAT 16 status
DA 16 disk address
MA 16 memory address
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 device disable flag
INT 1 interrupt pending flag
WLK 8 write lock switches
TIME 24 rotational delay, per sector
STOP_IOE 1 stop on I/O error
The fixed head disk controller supports the BOOT command.
Error handling is as follows:
error STOP_IOE processed as
not attached 1 report error and stop
0 disk not ready
Fixed head disk data files are buffered in memory; therefore, end of file
and OS I/O errors cannot occur.
2.4 Moving Head Disk (DP)
Moving head disk options include the ability to make units write enabled or
write locked, and to select the type of drive:
SET DPn LOCKED set unit n write locked
SET DPn ENABLED set unit n write enabled
SET DPn FLOPPY set unit n to floppy disk
SET DPn D31 set unit n to Diablo 31
SET DPn D44 set unit n to Diablo 44
SET DPn C111 set unit n to Century 111
SET DPn C114 set unit n to Century 114
SET DPn 6225 set unit n to 6225
SET DPn 6099 set unit n to 6099
SET DPn 6227 set unit n to 6227
SET DPn 6070 set unit n to 6070
SET DPn 6103 set unit n to 6103
SET DPn 4231 set unit n to 4231
Units can also be REMOVEd or ADDed to the configuration. The moving head
disk controller supports the BOOT command.
All drives have 256 16b words per sector. The other disk parameters are:
drive cylinders surfaces sectors size (MW) DG models
floppy 77 1 8 .158 6038
D31 203 2 12 1.247 4047, 4237, 4238
D44 408 4 12 5.014 4234, 6045
C111 203 10 6 3.118 4048
C114 203 20 12 12.472 4057, 2314
6225 20 2 245 2.508
6099 32 4 192 6.291
6227 20 6 245 7.526
6070 24 4 408 10.027
6103 32 8 192 12.583
4231 23 19 411 45.979
The moving head disk controller implements these registers:
name size comments
FCCY 16 flags, command, cylinder
USSC 16 unit, surface, sector, count
STAT 16 status
MA 16 memory address
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
STIME 24 seek time, per cylinder
RTIME 24 rotational delay
Error handling is as follows:
error processed as
not attached disk not ready
end of file assume rest of disk is zero
OS I/O error report error and stop
2.5 Magnetic Tape (MT)
Magnetic tape options include the ability to make units write enabled or
or write locked.
SET MTn LOCKED set unit n write locked
SET MTn ENABLED set unit n write enabled
Units can also be REMOVEd or ADDed to the configuration. The magnetic
tape controller supports the BOOT command.
The magnetic tape controller implements these registers:
name size comments
CU 16 command, unit
MA 16 memory address
WC 16 word count
STA1 16 status word 1
STA2 16 status word 2
EP 1 extended polling mode (not supported)
BUSY 1 device busy flag
DONE 1 device done flag
DISABLE 1 interrupt disable flag
INT 1 interrupt pending flag
STOP_IOE 1 stop on I/O error
CTIME 24 controller delay
RTIME 24 record delay
UST[0:7] 32 unit status, units 0-7
POS[0:7] 31 position, units 0-7
Error handling is as follows:
error processed as
not attached tape not ready
end of file (read or space) end of physical tape
(write) ignored
OS I/O error report error and stop
2.6 Symbolic Display and Input
The Nova simulator implements symbolic display and input. Display is
controlled by command line switches:
-a display as ASCII character
-c display as two character ASCII string
-m display instruction mnemonics
Input parsing is controlled by the first character typed in or by command
line switches:
' or -a ASCII character
" or -c two character ASCII string
alphabetic instruction mnemonic
numeric octal number
Instruction input uses standard Nova assembler syntax. There are three
instruction classes: memory reference, IOT, and operate.
Memory reference instructions have the format
memref {ac,}{@}address{,index}
LDA and STA require an initial register; ISZ, DSZ, JSR, and JMP do not.
The syntax for addresses and indices is as follows:
syntax mode displacement comments
0 <= n < 0400 0 n
{+/-}n >= 0400 1 {+/-}n - PC must be in range [-200, 177]
invalid on disk
.+/-n 1 {+/-}n must be in range [-200, 177]
{+/-}n,2 2 {+/-}n must be in range [-200, 177]
{+/-}n,3 3 {+/-}n must be in range [-200, 177]
IOT instructions have one of four formats
syntax example
iot HALT
iot reg INTA
iot device SKPDN
iot reg,device DOAS
Devices may be specified as mnemonics or as numbers in the range 0 - 077.
Operate instructions have the format
opcode{#} reg,reg{,skip}
In all Nova instructions, blanks may be substituted for commas as field
delimiters.

244
NOVA/nova_dsk.c Normal file
View File

@@ -0,0 +1,244 @@
/* nova_dsk.c: 4019 fixed head disk simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
dsk fixed head disk
23-Aug-01 RMS Fixed bug in write watermarking
26-Apr-01 RMS Added device enable/disable support
10-Dec-00 RMS Added Eclipse support
15-Oct-00 RMS Editorial changes
14-Apr-99 RMS Changed t_addr to unsigned
The 4019 is a head-per-track disk. To minimize overhead, the entire disk
is buffered in memory.
*/
#include "nova_defs.h"
#include <math.h>
/* Constants */
#define DSK_NUMWD 256 /* words/sector */
#define DSK_NUMSC 8 /* sectors/track */
#define DSK_NUMTR 128 /* tracks/disk */
#define DSK_NUMDK 8 /* disks/controller */
#define DSK_SCSIZE (DSK_NUMDK*DSK_NUMTR*DSK_NUMSC) /* sectors/drive */
#define DSK_AMASK (DSK_SCSIZE - 1) /* address mask */
#define DSK_SIZE (DSK_SCSIZE * DSK_NUMWD) /* words/drive */
#define GET_DISK(x) (((x) / (DSK_NUMSC * DSK_NUMTR)) & (DSK_NUMDK - 1))
/* Parameters in the unit descriptor */
#define FUNC u4 /* function */
/* Status register */
#define DSKS_WLS 020 /* write lock status */
#define DSKS_DLT 010 /* data late error */
#define DSKS_NSD 004 /* non-existent disk */
#define DSKS_CRC 002 /* parity error */
#define DSKS_ERR 001 /* error summary */
#define DSKS_ALLERR (DSKS_WLS | DSKS_DLT | DSKS_NSD | DSKS_CRC | DSKS_ERR)
/* Map logical sector numbers to physical sector numbers
(indexed by track<2:0>'sector)
*/
static const int32 sector_map[] = {
0, 2, 4, 6, 1, 3, 5, 7, 1, 3, 5, 7, 2, 4, 6, 0,
2, 4, 6, 0, 3, 5, 7, 1, 3, 5, 7, 1, 4, 6, 0, 2,
4, 6, 0, 2, 5, 7, 1, 3, 5, 7, 1, 3, 6, 0, 2, 4,
6, 0, 2, 4, 7, 1, 3, 5, 7, 1, 3, 5, 0, 2, 4, 6 };
#define DSK_MMASK 077
#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \
((double) DSK_NUMSC)))
extern uint16 M[];
extern UNIT cpu_unit;
extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb;
int32 dsk_stat = 0; /* status register */
int32 dsk_da = 0; /* disk address */
int32 dsk_ma = 0; /* memory address */
int32 dsk_wlk = 0; /* wrt lock switches */
int32 dsk_stopioe = 1; /* stop on error */
int32 dsk_time = 100; /* time per sector */
t_stat dsk_svc (UNIT *uptr);
t_stat dsk_reset (DEVICE *dptr);
t_stat dsk_boot (int32 unitno);
#if defined (ECLIPSE)
extern int32 MapAddr (int32 map, int32 addr);
#else
#define MapAddr(m,a) (a)
#endif
/* DSK data structures
dsk_dev device descriptor
dsk_unit unit descriptor
dsk_reg register list
*/
UNIT dsk_unit =
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF,
DSK_SIZE) };
REG dsk_reg[] = {
{ ORDATA (STAT, dsk_stat, 16) },
{ ORDATA (DA, dsk_da, 16) },
{ ORDATA (MA, dsk_ma, 16) },
{ FLDATA (BUSY, dev_busy, INT_V_DSK) },
{ FLDATA (DONE, dev_done, INT_V_DSK) },
{ FLDATA (DISABLE, dev_disable, INT_V_DSK) },
{ FLDATA (INT, int_req, INT_V_DSK) },
{ ORDATA (WLK, dsk_wlk, 8) },
{ DRDATA (TIME, dsk_time, 24), REG_NZ + PV_LEFT },
{ FLDATA (STOP_IOE, dsk_stopioe, 0) },
{ FLDATA (*DEVENB, iot_enb, INT_V_DSK), REG_HRO },
{ NULL } };
DEVICE dsk_dev = {
"DK", &dsk_unit, dsk_reg, NULL,
1, 8, 21, 1, 8, 16,
NULL, NULL, &dsk_reset,
&dsk_boot, NULL, NULL };
/* IOT routine */
int32 dsk (int32 pulse, int32 code, int32 AC)
{
int32 t, rval;
rval = 0;
switch (code) { /* decode IR<5:7> */
case ioDIA: /* DIA */
rval = dsk_stat & DSKS_ALLERR; /* read status */
break;
case ioDOA: /* DOA */
dsk_da = AC & DSK_AMASK; /* save disk addr */
break;
case ioDIB: /* DIB */
rval = dsk_ma & AMASK; /* read mem addr */
break;
case ioDOB: /* DOB */
dsk_ma = AC & AMASK; /* save mem addr */
break; } /* end switch code */
if (pulse) { /* any pulse? */
dev_busy = dev_busy & ~INT_DSK; /* clear busy */
dev_done = dev_done & ~INT_DSK; /* clear done */
int_req = int_req & ~INT_DSK; /* clear int */
dsk_stat = 0; /* clear status */
sim_cancel (&dsk_unit); } /* stop I/O */
if ((pulse == iopP) && ((dsk_wlk >> GET_DISK (dsk_da)) & 1)) { /* wrt lock? */
dev_done = dev_done | INT_DSK; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
dsk_stat = DSKS_ERR + DSKS_WLS; /* set status */
return rval; }
if (pulse & 1) { /* read or write? */
if (((t_addr) (dsk_da * DSK_NUMWD)) >= dsk_unit.capac) { /* inv sev? */
dev_done = dev_done | INT_DSK; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */
return rval; } /* done */
dsk_unit.FUNC = pulse; /* save command */
dev_busy = dev_busy | INT_DSK; /* set busy */
t = sector_map[dsk_da & DSK_MMASK] - GET_SECTOR (dsk_time);
if (t < 0) t = t + DSK_NUMSC;
sim_activate (&dsk_unit, t * dsk_time); } /* activate */
return rval;
}
/* Unit service */
t_stat dsk_svc (UNIT *uptr)
{
int32 i, da, pa;
dev_busy = dev_busy & ~INT_DSK; /* clear busy */
dev_done = dev_done | INT_DSK; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
if ((uptr -> flags & UNIT_BUF) == 0) { /* not buf? abort */
dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */
return IORETURN (dsk_stopioe, SCPE_UNATT); }
da = dsk_da * DSK_NUMWD; /* calc disk addr */
if (uptr -> FUNC == iopS) { /* read? */
for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */
pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */
if (MEM_ADDR_OK (pa)) M[pa] =
*(((int16 *) uptr -> filebuf) + da + i); }
dsk_ma = (dsk_ma + DSK_NUMWD) & AMASK; }
if (uptr -> FUNC == iopP) { /* write? */
for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */
pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */
*(((int16 *) uptr -> filebuf) + da + i) = M[pa]; }
if (((t_addr) (da + i)) >= uptr -> hwmark) /* past end? */
uptr -> hwmark = da + i + 1; /* upd hwmark */
dsk_ma = (dsk_ma + DSK_NUMWD + 3) & AMASK; }
dsk_stat = 0; /* set status */
return SCPE_OK;
}
/* Reset routine */
t_stat dsk_reset (DEVICE *dptr)
{
dsk_stat = dsk_da = dsk_ma = 0;
dev_busy = dev_busy & ~INT_DSK; /* clear busy */
dev_done = dev_done & ~INT_DSK; /* clear done */
int_req = int_req & ~INT_DSK; /* clear int */
sim_cancel (&dsk_unit);
return SCPE_OK;
}
/* Bootstrap routine */
#define BOOT_START 2000
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
static const int32 boot_rom[] = {
060220, /* NIOC DSK ; clear disk */
0102400, /* SUB 0,0 ; addr = 0 */
061020, /* DOA 0,DSK ; set disk addr */
062120, /* DOBS 0,DSK ; set mem addr, rd */
063620, /* SKPDN DSK ; done? */
000776, /* JMP .-2 */
000377, /* JMP 377 */
};
t_stat dsk_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
saved_PC = BOOT_START;
return SCPE_OK;
}

115
NOVA/nova_lp.c Normal file
View File

@@ -0,0 +1,115 @@
/* nova_lp.c: NOVA line printer simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
lpt line printer
*/
#include "nova_defs.h"
extern int32 int_req, dev_busy, dev_done, dev_disable;
int32 lpt_stopioe = 0; /* stop on error */
t_stat lpt_svc (UNIT *uptr);
t_stat lpt_reset (DEVICE *dptr);
/* LPT data structures
lpt_dev LPT device descriptor
lpt_unit LPT unit descriptor
lpt_reg LPT register list
*/
UNIT lpt_unit = {
UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
REG lpt_reg[] = {
{ ORDATA (BUF, lpt_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_LPT) },
{ FLDATA (DONE, dev_done, INT_V_LPT) },
{ FLDATA (DISABLE, dev_disable, INT_V_LPT) },
{ FLDATA (INT, int_req, INT_V_LPT) },
{ DRDATA (POS, lpt_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, lpt_stopioe, 0) },
{ NULL } };
DEVICE lpt_dev = {
"LPT", &lpt_unit, lpt_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &lpt_reset,
NULL, NULL, NULL };
/* IOT routine */
int32 lpt (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) lpt_unit.buf = AC & 0177;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_LPT; /* set busy */
dev_done = dev_done & ~INT_LPT; /* clear done, int */
int_req = int_req & ~INT_LPT;
if ((lpt_unit.buf != 015) && (lpt_unit.buf != 014) &&
(lpt_unit.buf != 012))
return (lpt_svc (&lpt_unit) << IOT_V_REASON);
sim_activate (&lpt_unit, lpt_unit.wait);
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_LPT; /* clear busy */
dev_done = dev_done & ~INT_LPT; /* clear done, int */
int_req = int_req & ~INT_LPT;
sim_cancel (&lpt_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat lpt_svc (UNIT *uptr)
{
dev_busy = dev_busy & ~INT_LPT; /* clear busy */
dev_done = dev_done | INT_LPT; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */
return IORETURN (lpt_stopioe, SCPE_UNATT);
if (putc (lpt_unit.buf, lpt_unit.fileref) == EOF) {
perror ("LPT I/O error");
clearerr (lpt_unit.fileref);
return SCPE_IOERR; }
lpt_unit.pos = ftell (lpt_unit.fileref);
return SCPE_OK;
}
/* Reset routine */
t_stat lpt_reset (DEVICE *dptr)
{
lpt_unit.buf = 0;
dev_busy = dev_busy & ~INT_LPT; /* clear busy */
dev_done = dev_done & ~INT_LPT; /* clear done, int */
int_req = int_req & ~INT_LPT;
sim_cancel (&lpt_unit); /* deactivate unit */
return SCPE_OK;
}

580
NOVA/nova_mta.c Normal file
View File

@@ -0,0 +1,580 @@
/* nova_mta.c: NOVA magnetic tape simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
mta magnetic tape
30-Nov-01 RMS Added read only unit, extended SET/SHOW support
24-Nov-01 RMS Changed POS, USTAT, FLG to an array
26-Apr-01 RMS Added device enable/disable support
18-Apr-01 RMS Changed to rewind tape before boot
10-Dec-00 RMS Added Eclipse support from Charles Owen
15-Oct-00 RMS Editorial changes
11-Nov-98 CEO Removed clear of mta_ma on iopC
04-Oct-98 RMS V2.4 magtape format
18-Jan-97 RMS V2.3 magtape format
29-Jun-96 RMS Added unit enable/disable support
Magnetic tapes are represented as a series of variable records
of the form:
32b byte count byte count is little endian
byte 0
byte 1
:
byte n-2
byte n-1
32b byte count
If the byte count is odd, the record is padded with an extra byte
of junk. File marks are represented by a byte count of 0 and are
not duplicated; end of tape by end of file.
*/
#include "nova_defs.h"
#define MTA_NUMDR 8 /* #drives */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_W_UF 2 /* saved flags width */
#define USTAT u3 /* unit status */
#define UNUM u4 /* unit number */
#define DTSIZE (1 << 14) /* max data xfer */
#define DTMASK (DTSIZE - 1)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* Command/unit */
#define CU_CI 0100000 /* clear interrupt */
#define CU_EP 0002000 /* poll enable */
#define CU_DE 0001000 /* disable erase */
#define CU_DA 0000400 /* disable autoretry */
#define CU_PE 0000400 /* PE mode */
#define CU_V_CMD 3 /* command */
#define CU_M_CMD 027
#define CU_READ 000
#define CU_REWIND 001
#define CU_CMODE 002
#define CU_SPACEF 003
#define CU_SPACER 004
#define CU_WRITE 005
#define CU_WREOF 006
#define CU_ERASE 007
#define CU_READNS 020
#define CU_UNLOAD 021
#define CU_DMODE 022
#define CU_V_UNIT 0 /* unit */
#define CU_M_UNIT 07
#define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD)
#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT)
/* Status 1 - stored in mta_sta<31:16> or (*) uptr -> USTAT<31:16> */
#define STA_ERR1 (0100000u << 16) /* error */
#define STA_DLT (0040000 << 16) /* data late */
#define STA_REW (0020000 << 16) /* *rewinding */
#define STA_ILL (0010000 << 16) /* illegal */
#define STA_HDN (0004000 << 16) /* high density */
#define STA_DAE (0002000 << 16) /* data error */
#define STA_EOT (0001000 << 16) /* *end of tape */
#define STA_EOF (0000400 << 16) /* *end of file */
#define STA_BOT (0000200 << 16) /* *start of tape */
#define STA_9TK (0000100 << 16) /* nine track */
#define STA_BAT (0000040 << 16) /* bad tape */
#define STA_CHG (0000010 << 16) /* status change */
#define STA_WLK (0000004 << 16) /* *write lock */
#define STA_ODD (0000002 << 16) /* odd character */
#define STA_RDY (0000001 << 16) /* *drive ready */
/* Status 2 - stored in mta_sta<15:0> or (*) uptr -> USTAT<15:0> */
#define STA_ERR2 0100000 /* error */
#define STA_RWY 0040000 /* runaway tape */
#define STA_FGP 0020000 /* false gap */
#define STA_CDL 0004000 /* corrected dlt */
#define STA_V_UNIT 8
#define STA_M_UNIT 07 /* unit */
#define STA_WCO 0000200 /* word count ovflo */
#define STA_BDS 0000100 /* bad signal */
#define STA_OVS 0000040 /* overskew */
#define STA_CRC 0000020 /* check error */
#define STA_STE 0000010 /* single trk error */
#define STA_FPR 0000004 /* false preamble */
#define STA_FMT 0000002 /* format error */
#define STA_PEM 0000001 /* *PE mode */
#define STA_EFLGS1 (STA_DLT | STA_ILL | STA_DAE | STA_EOT | \
STA_EOF | STA_BOT | STA_BAT | STA_ODD)
#define STA_EFLGS2 (STA_FGP | STA_CDL | STA_BDS | STA_OVS | \
STA_CRC | STA_FPR | STA_FPR) /* set error 2 */
#define STA_CLR ((020 << 16) | 0010000) /* always clear */
#define STA_SET (STA_HDN | STA_9TK) /* always set */
#define STA_DYN (STA_REW | STA_EOT | STA_EOF | STA_BOT | \
STA_WLK | STA_RDY | STA_PEM) /* kept in USTAT */
#define STA_MON (STA_REW | STA_BOT | STA_WLK | STA_RDY | \
STA_PEM) /* set status chg */
extern uint16 M[];
extern UNIT cpu_unit;
extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb;
int32 mta_ma = 0; /* memory address */
int32 mta_wc = 0; /* word count */
int32 mta_cu = 0; /* command/unit */
int32 mta_sta = 0; /* status register */
int32 mta_ep = 0; /* enable polling */
int32 mta_cwait = 100; /* command latency */
int32 mta_rwait = 100; /* record latency */
t_stat mta_svc (UNIT *uptr);
t_stat mta_reset (DEVICE *dptr);
t_stat mta_boot (int32 unitno);
t_stat mta_attach (UNIT *uptr, char *cptr);
t_stat mta_detach (UNIT *uptr);
int32 mta_updcsta (UNIT *uptr);
void mta_upddsta (UNIT *uptr, int32 newsta);
t_stat mta_vlock (UNIT *uptr, int32 val, char *cptr, void *desc);
#if defined (ECLIPSE)
extern int32 MapAddr (int32 map, int32 addr);
#else
#define MapAddr(m,a) (a)
#endif
static const int ctype[32] = { /* c vs r timing */
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 };
/* MTA data structures
mta_dev MTA device descriptor
mta_unit MTA unit list
mta_reg MTA register list
mta_mod MTA modifier list
*/
UNIT mta_unit[] = {
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) },
{ UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } };
REG mta_reg[] = {
{ ORDATA (CU, mta_cu, 16) },
{ ORDATA (MA, mta_ma, 16) },
{ ORDATA (WC, mta_wc, 16) },
{ GRDATA (STA1, mta_sta, 8, 16, 16) },
{ ORDATA (STA2, mta_sta, 16) },
{ FLDATA (EP, mta_ep, 0) },
{ FLDATA (BUSY, dev_busy, INT_V_MTA) },
{ FLDATA (DONE, dev_done, INT_V_MTA) },
{ FLDATA (DISABLE, dev_disable, INT_V_MTA) },
{ FLDATA (INT, int_req, INT_V_MTA) },
{ DRDATA (CTIME, mta_cwait, 24), PV_LEFT },
{ DRDATA (RTIME, mta_rwait, 24), PV_LEFT },
{ URDATA (UST, mta_unit[0].USTAT, 8, 32, 0, MTA_NUMDR, 0) },
{ URDATA (POS, mta_unit[0].pos, 8, 31, 0,
MTA_NUMDR, REG_RO | PV_LEFT) },
{ URDATA (FLG, mta_unit[0].flags, 8, UNIT_W_UF, UNIT_V_UF - 1,
MTA_NUMDR, REG_HRO) },
{ FLDATA (*DEVENB, iot_enb, INT_V_MTA), REG_HRO },
{ NULL } };
MTAB mta_mod[] = {
{ UNIT_WLK, 0, "write enabled", "ENABLED", &mta_vlock },
{ UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", &mta_vlock },
{ 0 } };
DEVICE mta_dev = {
"MT", mta_unit, mta_reg, mta_mod,
MTA_NUMDR, 10, 31, 1, 8, 8,
NULL, NULL, &mta_reset,
&mta_boot, &mta_attach, &mta_detach };
/* IOT routine */
int32 mta (int32 pulse, int32 code, int32 AC)
{
UNIT *uptr;
int32 u, c, rval;
rval = 0;
uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */
switch (code) { /* decode IR<5:7> */
case ioDIA: /* DIA */
rval = (mta_updcsta (uptr) >> 16) & DMASK; /* return status 1 */
break;
case ioDOA: /* DOA */
/* if (AC & CU_CI) ... clear ep int */
mta_cu = AC; /* save cmd/unit */
uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */
mta_updcsta (uptr); /* update status */
break;
case ioDIB: /* DIB */
rval = mta_ma & AMASK; /* return ma */
break;
case ioDOB: /* DOB */
mta_ma = AC & AMASK; /* save ma */
break;
case ioDIC: /* DIC */
rval = mta_updcsta (uptr) & DMASK; /* return status 2 */
break;
case ioDOC: /* DOC */
mta_wc = ((AC & 040000) << 1) | (AC & 077777); /* save wc */
break; } /* end switch code */
/* IOT, continued */
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
c = GET_CMD (mta_cu); /* get command */
if (dev_busy & INT_MTA) break; /* ignore if busy */
if ((uptr -> USTAT & STA_RDY) == 0) { /* drive not ready? */
mta_sta = mta_sta | STA_ILL; /* illegal op */
dev_busy = dev_busy & ~INT_MTA; /* clear busy */
dev_done = dev_done | INT_MTA; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); }
else if ((c == CU_REWIND) || (c == CU_UNLOAD)) { /* rewind, unload? */
mta_upddsta (uptr, (uptr -> USTAT & /* update status */
~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)) | STA_REW);
sim_activate (uptr, mta_rwait); /* start IO */
if (c == CU_UNLOAD) detach_unit (uptr); }
else { mta_sta = 0; /* clear errors */
dev_busy = dev_busy | INT_MTA; /* set busy */
dev_done = dev_done & ~INT_MTA; /* clear done */
int_req = int_req & ~INT_MTA; /* clear int */
if (ctype[c]) sim_activate (uptr, mta_cwait);
else { mta_upddsta (uptr, uptr -> USTAT &
~(STA_BOT | STA_EOF | STA_EOT | STA_RDY));
sim_activate (uptr, mta_rwait); } }
mta_updcsta (uptr); /* update status */
break;
case iopC: /* clear */
for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */
uptr = mta_dev.units + u; /* cancel IO */
if (sim_is_active (uptr) && !(uptr -> USTAT & STA_REW)) {
mta_upddsta (uptr, uptr -> USTAT | STA_RDY);
sim_cancel (uptr); } }
dev_busy = dev_busy & ~INT_MTA; /* clear busy */
dev_done = dev_done & ~INT_MTA; /* clear done */
int_req = int_req & ~INT_MTA; /* clear int */
mta_sta = mta_cu = 0; /* clear registers */
mta_updcsta (&mta_unit[0]); /* update status */
break; } /* end case pulse */
return rval;
}
/* Unit service
If rewind done, reposition to start of tape, set status
else, do operation, clear busy, set done, interrupt
*/
t_stat mta_svc (UNIT *uptr)
{
int32 c, i, p, u, pa, err;
t_stat rval;
t_mtrlnt cbc, tbc, wc;
uint16 c1, c2;
static uint8 dbuf[2 * DTSIZE];
static t_mtrlnt bceof = { 0 };
rval = SCPE_OK;
u = uptr -> UNUM; /* get unit number */
if (uptr -> USTAT & STA_REW) { /* rewind? */
uptr -> pos = 0; /* update position */
mta_upddsta (uptr, (uptr -> USTAT & ~STA_REW) | STA_BOT | STA_RDY);
if (u == GET_UNIT (mta_cu)) mta_updcsta (uptr);
return rval; }
err = 0;
c = GET_CMD (mta_cu); /* command */
wc = DTSIZE - (mta_wc & DTMASK); /* io wc */
if ((uptr -> flags & UNIT_ATT) == 0) { /* not attached? */
mta_upddsta (uptr, 0); /* unit off line */
mta_sta = mta_sta | STA_ILL; } /* illegal operation */
else if ((uptr -> flags & UNIT_WPRT) && /* write locked? */
((c == CU_WRITE) || (c == CU_WREOF) || (c == CU_ERASE))) {
mta_upddsta (uptr, uptr -> USTAT | STA_WLK | STA_RDY);
mta_sta = mta_sta | STA_ILL; } /* illegal operation */
else switch (c) { /* case on command */
case CU_CMODE: /* controller mode */
mta_ep = mta_cu & CU_EP;
break;
case CU_DMODE: /* drive mode */
if (uptr -> pos) mta_sta = mta_sta | STA_ILL; /* must be BOT */
else mta_upddsta (uptr, (mta_cu & CU_PE)? /* update drv status */
uptr -> USTAT | STA_PEM: uptr -> USTAT & ~ STA_PEM);
break;
/* Unit service, continued */
case CU_READ: /* read */
case CU_READNS: /* read non-stop */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref); /* read byte count */
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
(feof (uptr -> fileref))) {
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOT);
break; }
if (tbc == 0) { /* tape mark? */
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOF);
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
break; }
tbc = MTRL (tbc); /* ignore error flag */
cbc = wc * 2; /* expected bc */
if (tbc & 1) mta_sta = mta_sta | STA_ODD; /* odd byte count? */
if (tbc > cbc) mta_sta = mta_sta | STA_WCO; /* too big? */
else { cbc = tbc; /* no, use it */
wc = (cbc + 1) / 2; } /* adjust wc */
i = fxread (dbuf, sizeof (int8), cbc, uptr -> fileref);
for ( ; i < cbc; i++) dbuf[i] = 0;
err = ferror (uptr -> fileref);
for (i = p = 0; i < wc; i++) { /* copy buf to mem */
c1 = dbuf[p++];
c2 = dbuf[p++];
pa = MapAddr (0, mta_ma); /* map address */
if (MEM_ADDR_OK (pa)) M[pa] = (c1 << 8) | c2;
mta_ma = (mta_ma + 1) & AMASK; }
mta_wc = (mta_wc + wc) & DMASK;
uptr -> pos = uptr -> pos + ((tbc + 1) & ~1) +
(2 * sizeof (t_mtrlnt));
mta_upddsta (uptr, uptr -> USTAT | STA_RDY);
break;
case CU_WRITE: /* write */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
tbc = wc * 2; /* io byte count */
fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
for (i = p = 0; i < wc; i++) { /* copy to buffer */
pa = MapAddr (0, mta_ma); /* map address */
dbuf[p++] = (M[pa] >> 8) & 0377;
dbuf[p++] = M[pa] & 0377;
mta_ma = (mta_ma + 1) & AMASK; }
fxwrite (dbuf, sizeof (int8), tbc, uptr -> fileref);
fxwrite (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
err = ferror (uptr -> fileref);
mta_wc = 0;
uptr -> pos = uptr -> pos + tbc + (2 * sizeof (t_mtrlnt));
mta_upddsta (uptr, uptr -> USTAT | STA_RDY);
break;
case CU_WREOF: /* write eof */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxwrite (&bceof, sizeof (t_mtrlnt), 1, uptr -> fileref);
err = ferror (uptr -> fileref);
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
mta_upddsta (uptr, uptr -> USTAT | STA_EOF | STA_RDY);
break;
case CU_ERASE: /* erase */
mta_upddsta (uptr, uptr -> USTAT | STA_RDY);
break;
/* Unit service, continued */
case CU_SPACEF: /* space forward */
do { mta_wc = (mta_wc + 1) & DMASK; /* incr wc */
fseek (uptr -> fileref, uptr -> pos, SEEK_SET);
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
(feof (uptr -> fileref))) {
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOT);
break; }
if (tbc == 0) { /* zero bc? */
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOF);
uptr -> pos = uptr -> pos + sizeof (t_mtrlnt);
break; }
uptr -> pos = uptr -> pos + ((MTRL (tbc) + 1) & ~1) +
(2 * sizeof (t_mtrlnt)); }
while (mta_wc != 0);
mta_upddsta (uptr, uptr -> USTAT | STA_RDY);
break;
case CU_SPACER: /* space reverse */
if (uptr -> pos == 0) { /* at BOT? */
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_BOT);
break; }
do { mta_wc = (mta_wc + 1) & DMASK; /* incr wc */
fseek (uptr -> fileref, uptr -> pos - sizeof (t_mtrlnt),
SEEK_SET); /* bs to reclnt */
fxread (&tbc, sizeof (t_mtrlnt), 1, uptr -> fileref);
if ((err = ferror (uptr -> fileref)) || /* error or eof? */
(feof (uptr -> fileref))) {
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_BOT);
uptr -> pos = 0;
break; }
if (tbc == 0) { /* zero bc? */
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_EOF);
uptr -> pos = uptr -> pos - sizeof (t_mtrlnt);
break; }
uptr -> pos = uptr -> pos - ((MTRL (tbc) + 1) & ~1) -
(2 * sizeof (t_mtrlnt));
if (uptr -> pos == 0) { /* start of tape? */
mta_upddsta (uptr, uptr -> USTAT | STA_RDY | STA_BOT);
break; } }
while (mta_wc != 0);
mta_upddsta (uptr, uptr -> USTAT | STA_RDY);
break;
default: /* reserved */
mta_sta = mta_sta | STA_ILL;
break; } /* end case */
/* Unit service, continued */
if (err != 0) { /* I/O error */
mta_sta = mta_sta | STA_DAE; /* flag error */
perror ("MTA I/O error");
rval = SCPE_IOERR;
clearerr (uptr -> fileref); }
mta_updcsta (uptr); /* update status */
dev_busy = dev_busy & ~INT_MTA; /* clear busy */
dev_done = dev_done | INT_MTA; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
return rval;
}
/* Update controller status */
int32 mta_updcsta (UNIT *uptr) /* update ctrl */
{
mta_sta = (mta_sta & ~(STA_DYN | STA_CLR | STA_ERR1 | STA_ERR2)) |
(uptr -> USTAT & STA_DYN) | STA_SET;
if (mta_sta & STA_EFLGS1) mta_sta = mta_sta | STA_ERR1;
if (mta_sta & STA_EFLGS2) mta_sta = mta_sta | STA_ERR2;
return mta_sta;
}
/* Update drive status */
void mta_upddsta (UNIT *uptr, int32 newsta) /* drive status */
{
int32 change;
if ((uptr -> flags & UNIT_ATT) == 0) newsta = 0; /* offline? */
change = (uptr -> USTAT ^ newsta) & STA_MON; /* changes? */
uptr -> USTAT = newsta & STA_DYN; /* update status */
if (change) {
/* if (mta_ep) { /* if polling */
/* u = uptr -> UNUM; /* unit num */
/* mta_sta = (mta_sta & ~STA_UNIT) | (u << STA_V_UNIT);
/* set polling interupt... } */
mta_sta = mta_sta | STA_CHG; } /* flag change */
return;
}
/* Reset routine */
t_stat mta_reset (DEVICE *dptr)
{
int32 u;
UNIT *uptr;
dev_busy = dev_busy & ~INT_MTA; /* clear busy */
dev_done = dev_done & ~INT_MTA; /* clear done, int */
int_req = int_req & ~INT_MTA;
mta_cu = mta_wc = mta_ma = mta_sta = 0; /* clear registers */
mta_ep = 0;
for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */
uptr = mta_dev.units + u;
sim_cancel (uptr); /* cancel activity */
uptr -> UNUM = u;
if (uptr -> flags & UNIT_ATT) uptr -> USTAT = STA_RDY |
(uptr -> USTAT & STA_PEM) |
((uptr -> flags & UNIT_WPRT)? STA_WLK: 0) |
((uptr -> pos)? 0: STA_BOT);
else uptr -> USTAT = 0; }
mta_updcsta (&mta_unit[0]); /* update status */
return SCPE_OK;
}
/* Attach routine */
t_stat mta_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK) return r;
if (!sim_is_active (uptr)) mta_upddsta (uptr, STA_RDY | STA_BOT | STA_PEM |
((uptr -> flags & UNIT_WPRT)? STA_WLK: 0));
return r;
}
/* Detach routine */
t_stat mta_detach (UNIT* uptr)
{
if (!sim_is_active (uptr)) mta_upddsta (uptr, 0);
return detach_unit (uptr);
}
/* Write lock/unlock validate routine */
t_stat mta_vlock (UNIT *uptr, int32 val, char *cptr, void *desc)
{
if ((uptr -> flags & UNIT_ATT) && val)
mta_upddsta (uptr, uptr -> USTAT | STA_WLK);
else mta_upddsta (uptr, uptr -> USTAT & ~STA_WLK);
return SCPE_OK;
}
/* Bootstrap routine */
#define BOOT_START 02000
#define BOOT_UNIT 02020
#define BOOT_LEN (sizeof (boot_rom) / sizeof (int))
static const int32 boot_rom[] = {
060222, /* NIOC 0,MTA ; clear disk */
020417, /* LDA 0,UNIT ; unit */
024417, /* LDA 1,REWIND ; cmd */
0107000, /* ADD 0,1 ; cmd + unit */
065122, /* DOAS 1,MTA ; start rewind */
070422, /* DIA 2,MTA ; get status */
0151213, /* MOVR# 2,2,SNC ; skip if done */
000776, /* JMP .-2 */
0126400, /* SUB 1,1 ; ma, wc = 0 */
066022, /* DOB 1,MTA */
067022, /* DOC 1,MTA */
061122, /* DOAS 0,MTA ; start read */
070422, /* DIA 2,MTA ; get status */
0151213, /* MOVR# 2,2,SNC ; skip if done */
000776, /* JMP .-2 */
000377, /* JMP 377 */
000000, /* UNIT: */
000010 /* REWIND: 10 */
};
t_stat mta_boot (int32 unitno)
{
int32 i;
extern int32 saved_PC;
mta_unit[unitno].pos = 0;
for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i];
M[BOOT_UNIT] = (unitno & CU_M_UNIT) << CU_V_UNIT;
saved_PC = BOOT_START;
return SCPE_OK;
}

115
NOVA/nova_plt.c Normal file
View File

@@ -0,0 +1,115 @@
/* nova_plt.c: NOVA plotter simulator
Copyright (c) 2000-2001, Robert M. Supnik
Written by Bruce Ray and used with his gracious permission.
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.
plt plotter
26-Apr-01 RMS Added device enable/disable support
*/
#include "nova_defs.h"
extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb;
int32 plt_stopioe = 0; /* stop on error */
t_stat plt_svc (UNIT *uptr);
t_stat plt_reset (DEVICE *dptr);
/* PLT data structures
plt_dev PLT device descriptor
plt_unit PLT unit descriptor
plt_reg PLT register list
*/
UNIT plt_unit = {
UDATA (&plt_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
REG plt_reg[] = {
{ ORDATA (BUF, plt_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_PLT) },
{ FLDATA (DONE, dev_done, INT_V_PLT) },
{ FLDATA (DISABLE, dev_disable, INT_V_PLT) },
{ FLDATA (INT, int_req, INT_V_PLT) },
{ DRDATA (POS, plt_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, plt_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, plt_stopioe, 0) },
{ FLDATA (*DEVENB, iot_enb, INT_V_PLT), REG_HRO },
{ NULL } };
DEVICE plt_dev = {
"PLT", &plt_unit, plt_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &plt_reset,
NULL, NULL, NULL };
/* plotter: IOT routine */
int32 plt (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) plt_unit.buf = AC & 0377;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_PLT; /* set busy */
dev_done = dev_done & ~INT_PLT; /* clear done, int */
int_req = int_req & ~INT_PLT;
sim_activate (&plt_unit, plt_unit.wait); /* activate unit */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_PLT; /* clear busy */
dev_done = dev_done & ~INT_PLT; /* clear done, int */
int_req = int_req & ~INT_PLT;
sim_cancel (&plt_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat plt_svc (UNIT *uptr)
{
dev_busy = dev_busy & ~INT_PLT; /* clear busy */
dev_done = dev_done | INT_PLT; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
if ((plt_unit.flags & UNIT_ATT) == 0) /* attached? */
return IORETURN (plt_stopioe, SCPE_UNATT);
if (putc (plt_unit.buf, plt_unit.fileref) == EOF) {
perror ("PLT I/O error");
clearerr (plt_unit.fileref);
return SCPE_IOERR; }
plt_unit.pos = plt_unit.pos + 1;
return SCPE_OK;
}
/* Reset routine */
t_stat plt_reset (DEVICE *dptr)
{
plt_unit.buf = 0;
dev_busy = dev_busy & ~INT_PLT; /* clear busy */
dev_done = dev_done & ~INT_PLT; /* clear done, int */
int_req = int_req & ~INT_PLT;
sim_cancel (&plt_unit); /* deactivate unit */
return SCPE_OK;
}

202
NOVA/nova_pt.c Normal file
View File

@@ -0,0 +1,202 @@
/* nova_pt.c: NOVA paper tape read/punch simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
ptr paper tape reader
ptp paper tape punch
29-Nov-01 RMS Added read only unit support
*/
#include "nova_defs.h"
extern int32 int_req, dev_busy, dev_done, dev_disable;
int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */
t_stat ptr_svc (UNIT *uptr);
t_stat ptp_svc (UNIT *uptr);
t_stat ptr_reset (DEVICE *dptr);
t_stat ptp_reset (DEVICE *dptr);
/* PTR data structures
ptr_dev PTR device descriptor
ptr_unit PTR unit descriptor
ptr_reg PTR register list
*/
UNIT ptr_unit = {
UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0),
SERIAL_IN_WAIT };
REG ptr_reg[] = {
{ ORDATA (BUF, ptr_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_PTR) },
{ FLDATA (DONE, dev_done, INT_V_PTR) },
{ FLDATA (DISABLE, dev_disable, INT_V_PTR) },
{ FLDATA (INT, int_req, INT_V_PTR) },
{ DRDATA (POS, ptr_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, ptr_stopioe, 0) },
{ NULL } };
DEVICE ptr_dev = {
"PTR", &ptr_unit, ptr_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptr_reset,
NULL, NULL, NULL };
/* PTP data structures
ptp_dev PTP device descriptor
ptp_unit PTP unit descriptor
ptp_reg PTP register list
*/
UNIT ptp_unit = {
UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT };
REG ptp_reg[] = {
{ ORDATA (BUF, ptp_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_PTP) },
{ FLDATA (DONE, dev_done, INT_V_PTP) },
{ FLDATA (DISABLE, dev_disable, INT_V_PTP) },
{ FLDATA (INT, int_req, INT_V_PTP) },
{ DRDATA (POS, ptp_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT },
{ FLDATA (STOP_IOE, ptp_stopioe, 0) },
{ NULL } };
DEVICE ptp_dev = {
"PTP", &ptp_unit, ptp_reg, NULL,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptp_reset,
NULL, NULL, NULL };
/* Paper tape reader: IOT routine */
int32 ptr (int32 pulse, int32 code, int32 AC)
{
int32 iodata;
iodata = (code == ioDIA)? ptr_unit.buf & 0377: 0;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_PTR; /* set busy */
dev_done = dev_done & ~INT_PTR; /* clear done, int */
int_req = int_req & ~INT_PTR;
sim_activate (&ptr_unit, ptr_unit.wait); /* activate unit */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_PTR; /* clear busy */
dev_done = dev_done & ~INT_PTR; /* clear done, int */
int_req = int_req & ~INT_PTR;
sim_cancel (&ptr_unit); /* deactivate unit */
break; } /* end switch */
return iodata;
}
/* Unit service */
t_stat ptr_svc (UNIT *uptr)
{
int32 temp;
if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */
return IORETURN (ptr_stopioe, SCPE_UNATT);
if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */
if (feof (ptr_unit.fileref)) {
if (ptr_stopioe) printf ("PTR end of file\n");
else return SCPE_OK; }
else perror ("PTR I/O error");
clearerr (ptr_unit.fileref);
return SCPE_IOERR; }
dev_busy = dev_busy & ~INT_PTR; /* clear busy */
dev_done = dev_done | INT_PTR; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
ptr_unit.buf = temp & 0377;
ptr_unit.pos = ptr_unit.pos + 1;
return SCPE_OK;
}
/* Reset routine */
t_stat ptr_reset (DEVICE *dptr)
{
ptr_unit.buf = 0;
dev_busy = dev_busy & ~INT_PTR; /* clear busy */
dev_done = dev_done & ~INT_PTR; /* clear done, int */
int_req = int_req & ~INT_PTR;
sim_cancel (&ptr_unit); /* deactivate unit */
return SCPE_OK;
}
/* Paper tape punch: IOT routine */
int32 ptp (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) ptp_unit.buf = AC & 0377;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_PTP; /* set busy */
dev_done = dev_done & ~INT_PTP; /* clear done, int */
int_req = int_req & ~INT_PTP;
sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_PTP; /* clear busy */
dev_done = dev_done & ~INT_PTP; /* clear done, int */
int_req = int_req & ~INT_PTP;
sim_cancel (&ptp_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat ptp_svc (UNIT *uptr)
{
dev_busy = dev_busy & ~INT_PTP; /* clear busy */
dev_done = dev_done | INT_PTP; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */
return IORETURN (ptp_stopioe, SCPE_UNATT);
if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) {
perror ("PTP I/O error");
clearerr (ptp_unit.fileref);
return SCPE_IOERR; }
ptp_unit.pos = ptp_unit.pos + 1;
return SCPE_OK;
}
/* Reset routine */
t_stat ptp_reset (DEVICE *dptr)
{
ptp_unit.buf = 0;
dev_busy = dev_busy & ~INT_PTP; /* clear busy */
dev_done = dev_done & ~INT_PTP; /* clear done, int */
int_req = int_req & ~INT_PTP;
sim_cancel (&ptp_unit); /* deactivate unit */
return SCPE_OK;
}

955
NOVA/nova_sys.c Normal file
View File

@@ -0,0 +1,955 @@
/* nova_sys.c: NOVA simulator interface
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
17-Sep-01 RMS Removed multiconsole support
31-May-01 RMS Added multiconsole support
14-Mar-01 RMS Revised load/dump interface (again)
22-Dec-00 RMS Added second terminal support
10-Dec-00 RMS Added Eclipse support
08-Dec-00 BKR Added plotter support
30-Oct-00 RMS Added support for examine to file
15-Oct-00 RMS Added stack, byte, trap instructions
14-Apr-99 RMS Changed t_addr to unsigned
27-Oct-98 RMS V2.4 load interface
24-Sep-97 RMS Fixed bug in device name table (found by Charles Owen)
*/
#include "nova_defs.h"
#include <ctype.h>
extern DEVICE cpu_dev;
extern UNIT cpu_unit;
#if defined (ECLIPSE)
extern DEVICE map_dev;
#endif
extern DEVICE ptr_dev, ptp_dev;
extern DEVICE plt_dev;
extern DEVICE tti_dev, tto_dev;
extern DEVICE tti1_dev, tto1_dev;
extern DEVICE clk_dev, lpt_dev;
extern DEVICE dkp_dev, dsk_dev;
extern DEVICE mta_dev;
extern UNIT tti_unit, tto_unit;
extern UNIT tti1_unit, tto1_unit;
extern REG cpu_reg[];
extern uint16 M[];
extern int32 saved_PC;
/* SCP data structures
sim_name simulator name string
sim_PC pointer to saved PC register descriptor
sim_emax number of words needed for examine
sim_devices array of pointers to simulated devices
sim_stop_messages array of pointers to stop messages
sim_load binary loader
*/
#if defined (ECLIPSE)
char sim_name[] = "ECLIPSE";
#else
char sim_name[] = "NOVA";
#endif
REG *sim_PC = &cpu_reg[0];
int32 sim_emax = 4;
DEVICE *sim_devices[] = {
&cpu_dev,
#if defined (ECLIPSE)
&map_dev,
#endif
&ptr_dev, &ptp_dev,
&tti_dev, &tto_dev,
&tti1_dev, &tto1_dev,
&clk_dev, &plt_dev,
&lpt_dev, &dsk_dev,
&dkp_dev, &mta_dev,
NULL };
const char *sim_stop_messages[] = {
"Unknown error",
"Unknown I/O instruction",
"HALT instruction",
"Breakpoint",
"Nested indirect address limit exceeded",
"Nested indirect interrupt address limit exceeded",
"Nested indirect trap address limit exceeded",
"Read breakpoint",
"Write breakpoint" };
/* Binary loader
Loader format consists of blocks, optionally preceded, separated, and
followed by zeroes. Each block consists of:
lo_count
hi_count
lo_origin
hi_origin
lo_checksum
hi_checksum
lo_data byte ---
hi_data byte |
: > -count words
lo_data byte |
hi_data byte ---
If the word count is [0,-20], then the block is normal data.
If the word count is [-21,-n], then the block is repeated data.
If the word count is 1, the block is the start address.
If the word count is >1, the block is an error block.
*/
t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
{
int32 data, csum, count, state, i;
t_addr origin;
if ((*cptr != 0) || (flag != 0)) return SCPE_ARG;
state = 0;
while ((i = getc (fileref)) != EOF) {
switch (state) {
case 0: /* leader */
count = i;
state = (count != 0);
break;
case 1: /* high count */
csum = count = (i << 8) | count;
state = 2;
break;
case 2: /* low origin */
origin = i;
state = 3;
break;
case 3: /* high origin */
origin = (i << 8) | origin;
csum = csum + origin;
state = 4;
break;
case 4: /* low checksum */
csum = csum + i;
state = 5;
break;
case 5: /* high checksum */
csum = csum + (i << 8);
if (count == 1) saved_PC = origin; /* count = 1? */
if (count <= 1) { /* count = 0/1? */
if (csum & 0177777) return SCPE_CSUM;
state = 0;
break; }
if (count < 0100000) { /* count > 1 */
state = 8;
break; }
count = 0200000 - count;
state = 6;
break;
case 6: /* low data */
data = i;
state = 7;
break;
case 7: /* high data */
data = (i << 8) | data;
csum = csum + data;
if (count > 20) { /* large block */
for (count = count - 1; count == 1; count--) {
if (origin >= MEMSIZE) return SCPE_NXM;
M[origin] = data;
origin = origin + 1; } }
if (origin >= MEMSIZE) return SCPE_NXM;
M[origin] = data;
origin = origin + 1;
count = count - 1;
if (count == 0) {
if (csum & 0177777) return SCPE_CSUM;
state = 0;
break; }
state = 6;
break;
case 8: /* error block */
if (i == 0377) state = 0;
break; } /* end switch */
} /* end while */
/* Ok to find end of tape between blocks or in error state */
return ((state == 0) || (state == 8))? SCPE_OK: SCPE_FMT;
}
/* Symbol tables */
#define I_V_FL 18 /* flag bits */
#define I_M_FL 037 /* flag width */
#define I_V_NPN 000 /* no operands */
#define I_V_R 001 /* reg */
#define I_V_D 002 /* device */
#define I_V_RD 003 /* reg,device */
#define I_V_M 004 /* mem addr */
#define I_V_RM 005 /* reg,mem addr */
#define I_V_RR 006 /* operate */
#define I_V_BY 007 /* Nova byte pointer */
#define I_V_2AC 010 /* reg,reg */
#define I_V_RSI 011 /* reg,short imm */
#define I_V_LI 012 /* long imm */
#define I_V_RLI 013 /* reg,long imm */
#define I_V_LM 014 /* long mem addr */
#define I_V_RLM 015 /* reg,long mem addr */
#define I_V_FRM 016 /* flt reg,long mem addr */
#define I_V_FST 017 /* flt long mem, status */
#define I_V_XP 020 /* XOP */
#define I_NPN (I_V_NPN << I_V_FL)
#define I_R (I_V_R << I_V_FL)
#define I_D (I_V_D << I_V_FL)
#define I_RD (I_V_RD << I_V_FL)
#define I_M (I_V_M << I_V_FL)
#define I_RM (I_V_RM << I_V_FL)
#define I_RR (I_V_RR << I_V_FL)
#define I_BY (I_V_BY << I_V_FL)
#define I_2AC (I_V_2AC << I_V_FL)
#define I_RSI (I_V_RSI << I_V_FL)
#define I_LI (I_V_LI << I_V_FL)
#define I_RLI (I_V_RLI << I_V_FL)
#define I_LM (I_V_LM << I_V_FL)
#define I_RLM (I_V_RLM << I_V_FL)
#define I_FRM (I_V_FRM << I_V_FL)
#define I_FST (I_V_FST << I_V_FL)
#define I_XP (I_V_XP << I_V_FL)
static const int32 masks[] = {
0177777, 0163777, 0177700, 0163700,
0174000, 0160000, 0103770, 0163477,
0103777, 0103777, 0177777, 0163777,
0176377, 0162377, 0103777, 0163777,
0100077 };
static const char *opcode[] = {
"JMP", "JSR", "ISZ", "DSZ",
"LDA", "STA",
#if defined (ECLIPSE)
"ADI", "SBI", "DAD", "DSB",
"IOR", "XOR", "ANC", "XCH",
"SGT", "SGE", "LSH", "DLSH",
"HXL", "HXR", "DHXL", "DHXR",
"BTO", "BTZ", "SBZ", "SZBO",
"LOB", "LRB", "COB", "LDB",
"STB", "PSH", "POP",
"LMP", "SYSC",
"PSHR", "POPB", "BAM", "POPJ",
"RTN", "BLM", "DIVX",
"MUL", "MULS", "DIV", "DIVS",
"SAVE", "RSTR",
"XOP",
"FAS", "FAD", "FSS", "FSD",
"FMS", "FMD", "FDS", "FDD",
"FAMS", "FAMD", "FSMS", "FSMD",
"FMMS", "FMMD", "FDMS", "FDMD",
"FLDS", "FLDD", "FSTS", "FSTD",
"FLAS", "FLMD", "FFAS", "FFMD",
"FNOM", "FRH", "FAB", "FNEG",
"FSCAL", "FEXP", "FINT", "FHLV",
"FNS", "FSA", "FSEQ", "FSNE",
"FSLT", "FSGE", "FSLE", "FSGT",
"FSNM", "FSND", "FSNU", "FSNUD",
"FSNO", "FSNOD", "FSNUO", "FSNER",
"FSST", "FLST",
"FTE", "FTD", "FCLE",
"FPSH", "FPOP",
"FCMP", "FMOV",
"CMV", "CMP", "CTR", "CMT",
"EJMP", "EJSR", "EISZ", "EDSZ",
"ELDA", "ESTA", "ELEF",
"ELDB", "ESTB", "DSPA",
"PSHJ", "CLM", "SNB",
"MSP", "XCT", "HLV",
"IORI", "XORI", "ANDI", "ADDI",
#endif
"COM", "COMZ", "COMO", "COMC",
"COML", "COMZL", "COMOL", "COMCL",
"COMR", "COMZR", "COMOR", "COMCR",
"COMS", "COMZS", "COMOS", "COMCS",
"COM#", "COMZ#", "COMO#", "COMC#",
"COML#", "COMZL#", "COMOL#", "COMCL#",
"COMR#", "COMZR#", "COMOR#", "COMCR#",
"COMS#", "COMZS#", "COMOS#", "COMCS#",
"NEG", "NEGZ", "NEGO", "NEGC",
"NEGL", "NEGZL", "NEGOL", "NEGCL",
"NEGR", "NEGZR", "NEGOR", "NEGCR",
"NEGS", "NEGZS", "NEGOS", "NEGCS",
"NEG#", "NEGZ#", "NEGO#", "NEGC#",
"NEGL#", "NEGZL#", "NEGOL#", "NEGCL#",
"NEGR#", "NEGZR#", "NEGOR#", "NEGCR#",
"NEGS#", "NEGZS#", "NEGOS#", "NEGCS#",
"MOV", "MOVZ", "MOVO", "MOVC",
"MOVL", "MOVZL", "MOVOL", "MOVCL",
"MOVR", "MOVZR", "MOVOR", "MOVCR",
"MOVS", "MOVZS", "MOVOS", "MOVCS",
"MOV#", "MOVZ#", "MOVO#", "MOVC#",
"MOVL#", "MOVZL#", "MOVOL#", "MOVCL#",
"MOVR#", "MOVZR#", "MOVOR#", "MOVCR#",
"MOVS#", "MOVZS#", "MOVOS#", "MOVCS#",
"INC", "INCZ", "INCO", "INCC",
"INCL", "INCZL", "INCOL", "INCCL",
"INCR", "INCZR", "INCOR", "INCCR",
"INCS", "INCZS", "INCOS", "INCCS",
"INC#", "INCZ#", "INCO#", "INCC#",
"INCL#", "INCZL#", "INCOL#", "INCCL#",
"INCR#", "INCZR#", "INCOR#", "INCCR#",
"INCS#", "INCZS#", "INCOS#", "INCCS#",
"ADC", "ADCZ", "ADCO", "ADCC",
"ADCL", "ADCZL", "ADCOL", "ADCCL",
"ADCR", "ADCZR", "ADCOR", "ADCCR",
"ADCS", "ADCZS", "ADCOS", "ADCCS",
"ADC#", "ADCZ#", "ADCO#", "ADCC#",
"ADCL#", "ADCZL#", "ADCOL#", "ADCCL#",
"ADCR#", "ADCZR#", "ADCOR#", "ADCCR#",
"ADCS#", "ADCZS#", "ADCOS#", "ADCCS#",
"SUB", "SUBZ", "SUBO", "SUBC",
"SUBL", "SUBZL", "SUBOL", "SUBCL",
"SUBR", "SUBZR", "SUBOR", "SUBCR",
"SUBS", "SUBZS", "SUBOS", "SUBCS",
"SUB#", "SUBZ#", "SUBO#", "SUBC#",
"SUBL#", "SUBZL#", "SUBOL#", "SUBCL#",
"SUBR#", "SUBZR#", "SUBOR#", "SUBCR#",
"SUBS#", "SUBZS#", "SUBOS#", "SUBCS#",
"ADD", "ADDZ", "ADDO", "ADDC",
"ADDL", "ADDZL", "ADDOL", "ADDCL",
"ADDR", "ADDZR", "ADDOR", "ADDCR",
"ADDS", "ADDZS", "ADDOS", "ADDCS",
"ADD#", "ADDZ#", "ADDO#", "ADDC#",
"ADDL#", "ADDZL#", "ADDOL#", "ADDCL#",
"ADDR#", "ADDZR#", "ADDOR#", "ADDCR#",
"ADDS#", "ADDZS#", "ADDOS#", "ADDCS#",
"AND", "ANDZ", "ANDO", "ANDC",
"ANDL", "ANDZL", "ANDOL", "ANDCL",
"ANDR", "ANDZR", "ANDOR", "ANDCR",
"ANDS", "ANDZS", "ANDOS", "ANDCS",
"AND#", "ANDZ#", "ANDO#", "ANDC#",
"ANDL#", "ANDZL#", "ANDOL#", "ANDCL#",
"ANDR#", "ANDZR#", "ANDOR#", "ANDCR#",
"ANDS#", "ANDZS#", "ANDOS#", "ANDCS#",
"ION", "IOF",
"RDSW", "INTA", "MSKO", "IORST", "HALT",
#if !defined (ECLIPSE)
"MUL", "DIV", "MULS", "DIVS",
"PSHA", "POPA", "SAV", "RET",
"MTSP", "MTFP", "MFSP", "MFFP",
"LDB", "STB",
#endif
"NIO", "NIOS", "NIOC", "NIOP",
"DIA", "DIAS", "DIAC", "DIAP",
"DOA", "DOAS", "DOAC", "DOAP",
"DIB", "DIBS", "DIBC", "DIBP",
"DOB", "DOBS", "DOBC", "DOBP",
"DIC", "DICS", "DICC", "DICP",
"DOC", "DOCS", "DOCC", "DOCP",
"SKPBN", "SKPBZ", "SKPDN", "SKPDZ",
NULL };
static const opc_val[] = {
0000000+I_M, 0004000+I_M, 0010000+I_M, 0014000+I_M,
0020000+I_RM, 0040000+I_RM,
#if defined (ECLIPSE)
0100010+I_RSI, 0100110+I_RSI, 0100210+I_2AC, 0100310+I_2AC,
0100410+I_2AC, 0100510+I_2AC, 0100610+I_2AC, 0100710+I_2AC,
0101010+I_2AC, 0101110+I_2AC, 0101210+I_RSI, 0101310+I_RSI,
0101410+I_RSI, 0101510+I_RSI, 0101610+I_RSI, 0101710+I_RSI,
0102010+I_2AC, 0102110+I_2AC, 0102210+I_2AC, 0102310+I_2AC,
0102410+I_2AC, 0102510+I_2AC, 0102610+I_2AC, 0102710+I_2AC,
0103010+I_2AC, 0103110+I_2AC, 0103210+I_2AC,
0113410+I_NPN, 0103510+I_RSI,
0103710+I_NPN, 0107710+I_NPN, 0113710+I_NPN, 0117710+I_NPN,
0127710+I_NPN, 0133710+I_NPN, 0137710+I_NPN,
0143710+I_NPN, 0147710+I_NPN, 0153710+I_NPN, 0157710+I_NPN,
0163710+I_LI, 0167710+I_NPN,
0100030+I_XP,
0100050+I_2AC, 0100150+I_2AC, 0100250+I_2AC, 0100350+I_2AC,
0100450+I_2AC, 0100550+I_2AC, 0100650+I_2AC, 0100750+I_2AC,
0101050+I_FRM, 0101150+I_FRM, 0101250+I_FRM, 0101350+I_FRM,
0101450+I_FRM, 0101550+I_FRM, 0101650+I_FRM, 0101750+I_FRM,
0102050+I_FRM, 0102150+I_FRM, 0102250+I_FRM, 0102350+I_FRM,
0102450+I_2AC, 0102550+I_FRM, 0102650+I_2AC, 0102750+I_FRM,
0103050+I_R, 0123050+I_R, 0143050+I_R, 0163050+I_R,
0103150+I_R, 0123150+I_R, 0143150+I_R, 0163150+I_R,
0103250+I_NPN, 0107250+I_NPN, 0113250+I_NPN, 0117250+I_NPN,
0123250+I_NPN, 0127250+I_NPN, 0133250+I_NPN, 0137250+I_NPN,
0143250+I_NPN, 0147250+I_NPN, 0153250+I_NPN, 0157250+I_NPN,
0163250+I_NPN, 0167250+I_NPN, 0173250+I_NPN, 0177250+I_NPN,
0103350+I_FST, 0123350+I_FST,
0143350+I_NPN, 0147350+I_NPN, 0153350+I_NPN,
0163350+I_NPN, 0167350+I_NPN,
0103450+I_2AC, 0103550+I_2AC,
0153650+I_NPN, 0157650+I_NPN, 0163650+I_NPN, 0167650+I_NPN,
0102070+I_LM, 0106070+I_LM, 0112070+I_LM, 0116070+I_LM,
0122070+I_RLM, 0142070+I_RLM, 0162070+I_RLM,
0102170+I_RLM, 0122170+I_RLM, 0142170+I_RLM,
0102270+I_LM, 0102370+I_2AC, 0102770+I_2AC,
0103370+I_R, 0123370+I_R, 0143370+I_R,
0103770+I_RLI, 0123770+I_RLI, 0143770+I_RLI, 0163770+I_RLI,
#endif
0100000+I_RR, 0100020+I_RR, 0100040+I_RR, 0100060+I_RR,
0100100+I_RR, 0100120+I_RR, 0100140+I_RR, 0100160+I_RR,
0100200+I_RR, 0100220+I_RR, 0100240+I_RR, 0100260+I_RR,
0100300+I_RR, 0100320+I_RR, 0100340+I_RR, 0100360+I_RR,
0100010+I_RR, 0100030+I_RR, 0100050+I_RR, 0100070+I_RR,
0100110+I_RR, 0100130+I_RR, 0100150+I_RR, 0100170+I_RR,
0100210+I_RR, 0100230+I_RR, 0100250+I_RR, 0100270+I_RR,
0100310+I_RR, 0100330+I_RR, 0100350+I_RR, 0100370+I_RR,
0100400+I_RR, 0100420+I_RR, 0100440+I_RR, 0100460+I_RR,
0100500+I_RR, 0100520+I_RR, 0100540+I_RR, 0100560+I_RR,
0100600+I_RR, 0100620+I_RR, 0100640+I_RR, 0100660+I_RR,
0100700+I_RR, 0100720+I_RR, 0100740+I_RR, 0100760+I_RR,
0100410+I_RR, 0100430+I_RR, 0100450+I_RR, 0100470+I_RR,
0100510+I_RR, 0100530+I_RR, 0100550+I_RR, 0100570+I_RR,
0100610+I_RR, 0100630+I_RR, 0100650+I_RR, 0100670+I_RR,
0100710+I_RR, 0100730+I_RR, 0100750+I_RR, 0100770+I_RR,
0101000+I_RR, 0101020+I_RR, 0101040+I_RR, 0101060+I_RR,
0101100+I_RR, 0101120+I_RR, 0101140+I_RR, 0101160+I_RR,
0101200+I_RR, 0101220+I_RR, 0101240+I_RR, 0101260+I_RR,
0101300+I_RR, 0101320+I_RR, 0101340+I_RR, 0101360+I_RR,
0101010+I_RR, 0101030+I_RR, 0101050+I_RR, 0101070+I_RR,
0101110+I_RR, 0101130+I_RR, 0101150+I_RR, 0101170+I_RR,
0101210+I_RR, 0101230+I_RR, 0101250+I_RR, 0101270+I_RR,
0101310+I_RR, 0101330+I_RR, 0101350+I_RR, 0101370+I_RR,
0101400+I_RR, 0101420+I_RR, 0101440+I_RR, 0101460+I_RR,
0101500+I_RR, 0101520+I_RR, 0101540+I_RR, 0101560+I_RR,
0101600+I_RR, 0101620+I_RR, 0101640+I_RR, 0101660+I_RR,
0101700+I_RR, 0101720+I_RR, 0101740+I_RR, 0101760+I_RR,
0101410+I_RR, 0101430+I_RR, 0101450+I_RR, 0101470+I_RR,
0101510+I_RR, 0101530+I_RR, 0101550+I_RR, 0101570+I_RR,
0101610+I_RR, 0101630+I_RR, 0101650+I_RR, 0101670+I_RR,
0101710+I_RR, 0101730+I_RR, 0101750+I_RR, 0101770+I_RR,
0102000+I_RR, 0102020+I_RR, 0102040+I_RR, 0102060+I_RR,
0102100+I_RR, 0102120+I_RR, 0102140+I_RR, 0102160+I_RR,
0102200+I_RR, 0102220+I_RR, 0102240+I_RR, 0102260+I_RR,
0102300+I_RR, 0102320+I_RR, 0102340+I_RR, 0102360+I_RR,
0102010+I_RR, 0102030+I_RR, 0102050+I_RR, 0102070+I_RR,
0102110+I_RR, 0102130+I_RR, 0102150+I_RR, 0102170+I_RR,
0102210+I_RR, 0102230+I_RR, 0102250+I_RR, 0102270+I_RR,
0102310+I_RR, 0102330+I_RR, 0102350+I_RR, 0102370+I_RR,
0102400+I_RR, 0102420+I_RR, 0102440+I_RR, 0102460+I_RR,
0102500+I_RR, 0102520+I_RR, 0102540+I_RR, 0102560+I_RR,
0102600+I_RR, 0102620+I_RR, 0102640+I_RR, 0102660+I_RR,
0102700+I_RR, 0102720+I_RR, 0102740+I_RR, 0102760+I_RR,
0102410+I_RR, 0102430+I_RR, 0102450+I_RR, 0102470+I_RR,
0102510+I_RR, 0102530+I_RR, 0102550+I_RR, 0102570+I_RR,
0102610+I_RR, 0102630+I_RR, 0102650+I_RR, 0102670+I_RR,
0102710+I_RR, 0102730+I_RR, 0102750+I_RR, 0102770+I_RR,
0103000+I_RR, 0103020+I_RR, 0103040+I_RR, 0103060+I_RR,
0103100+I_RR, 0103120+I_RR, 0103140+I_RR, 0103160+I_RR,
0103200+I_RR, 0103220+I_RR, 0103240+I_RR, 0103260+I_RR,
0103300+I_RR, 0103320+I_RR, 0103340+I_RR, 0103360+I_RR,
0103010+I_RR, 0103030+I_RR, 0103050+I_RR, 0103070+I_RR,
0103110+I_RR, 0103130+I_RR, 0103150+I_RR, 0103170+I_RR,
0103210+I_RR, 0103230+I_RR, 0103250+I_RR, 0103270+I_RR,
0103310+I_RR, 0103330+I_RR, 0103350+I_RR, 0103370+I_RR,
0103400+I_RR, 0103420+I_RR, 0103440+I_RR, 0103460+I_RR,
0103500+I_RR, 0103520+I_RR, 0103540+I_RR, 0103560+I_RR,
0103600+I_RR, 0103620+I_RR, 0103640+I_RR, 0103660+I_RR,
0103700+I_RR, 0103720+I_RR, 0103740+I_RR, 0103760+I_RR,
0103410+I_RR, 0103430+I_RR, 0103450+I_RR, 0103470+I_RR,
0103510+I_RR, 0103530+I_RR, 0103550+I_RR, 0103570+I_RR,
0103610+I_RR, 0103630+I_RR, 0103650+I_RR, 0103670+I_RR,
0103710+I_RR, 0103730+I_RR, 0103750+I_RR, 0103770+I_RR,
0060177+I_NPN, 0060277+I_NPN,
0060477+I_R, 0061477+I_R, 0062077+I_R, 0062677+I_NPN, 0063077+I_NPN,
#if !defined (ECLIPSE)
0073301+I_NPN, 0073101+I_NPN, 0077201+I_NPN, 0077001+I_NPN,
0061401+I_R, 0061601+I_R, 0062401+I_NPN, 0062601+I_NPN,
0061001+I_R, 0060001+I_R, 0061201+I_R, 0060201+I_R,
0060401+I_BY, 0062001+I_BY,
#endif
0060000+I_D, 0060100+I_D, 0060200+I_D, 0060300+I_D,
0060400+I_RD, 0060500+I_RD, 0060600+I_RD, 0060700+I_RD,
0061000+I_RD, 0061100+I_RD, 0061200+I_RD, 0061300+I_RD,
0061400+I_RD, 0061500+I_RD, 0061600+I_RD, 0061700+I_RD,
0062000+I_RD, 0062100+I_RD, 0062200+I_RD, 0062300+I_RD,
0062400+I_RD, 0062500+I_RD, 0062600+I_RD, 0062700+I_RD,
0063000+I_RD, 0063100+I_RD, 0063200+I_RD, 0063300+I_RD,
0063400+I_D, 0063500+I_D, 0063600+I_D, 0063700+I_D,
-1 };
static const char *skip[] = {
"SKP", "SZC", "SNC", "SZR", "SNR", "SEZ", "SBN",
NULL };
static const char *device[] = {
#if defined (ECLIPSE)
"ERCC", "MAP",
#endif
"TTI", "TTO", "PTR", "PTP", "RTC", "PLT", "CDR", "LPT",
"DSK", "MTA", "DCM", "ADCV", "DKP", "CAS",
"TTI1", "TTO1", "CPU",
NULL };
static const int32 dev_val[] = {
#if defined (ECLIPSE)
002, 003,
#endif
010, 011, 012, 013, 014, 015, 016, 017,
020, 022, 024, 030, 033, 034,
050, 051, 077,
-1 };
/* Address decode
Inputs:
*of = output stream
addr = current PC
ind = indirect flag
mode = addressing mode
disp = displacement
ext = true if extended address
cflag = true if decoding for CPU
Outputs:
return = error code
*/
t_stat fprint_addr (FILE *of, t_addr addr, int32 ind, int32 mode,
int32 disp, t_bool ext, int32 cflag)
{
int32 dsign, dmax;
if (ext) dmax = AMASK + 1; /* get max disp */
else dmax = I_M_DISP + 1;
dsign = dmax >> 1; /* get disp sign */
if (ind) fprintf (of, "@"); /* indirect? */
switch (mode & 03) { /* mode */
case 0: /* absolute */
fprintf (of, "%-o", disp);
break;
case 1: /* PC rel */
if (disp & dsign) {
if (cflag) fprintf (of, "%-o", (addr - (dmax - disp)) & AMASK);
else fprintf (of, ".-%-o", dmax - disp); }
else { if (cflag) fprintf (of, "%-o", (addr + disp) & AMASK);
else fprintf (of, ".+%-o", disp); }
break;
case 2: /* AC2 rel */
if (disp & dsign) fprintf (of, "-%-o,2", dmax - disp);
else fprintf (of, "%-o,2", disp);
break;
case 3: /* AC3 rel */
if (disp & dsign) fprintf (of, "-%-o,3", dmax - disp);
else fprintf (of, "%-o,3", disp);
break; } /* end switch */
return SCPE_OK;
}
/* Symbolic output
Inputs:
*of = output stream
addr = current PC
*val = pointer to values
*uptr = pointer to unit
sw = switches
Outputs:
status = error code
*/
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
UNIT *uptr, int32 sw)
{
int32 cflag, i, j, c1, c2, inst, dv, src, dst, skp;
int32 ind, mode, disp, dev;
int32 byac, extind, extdisp, xop;
cflag = (uptr == NULL) || (uptr == &cpu_unit);
c1 = (val[0] >> 8) & 0177;
c2 = val[0] & 0177;
if (sw & SWMASK ('A')) { /* ASCII? */
fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
return SCPE_OK; }
if (sw & SWMASK ('C')) { /* character? */
fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);
fprintf (of, (c2 < 040)? "<%03o>": "%c", c2);
return SCPE_OK; }
if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* mnemonic? */
/* Instruction decode */
inst = val[0];
for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */
src = I_GETSRC (inst); /* opr fields */
dst = I_GETDST (inst);
skp = I_GETSKP (inst);
ind = inst & I_IND; /* mem ref fields */
mode = I_GETMODE (inst);
disp = I_GETDISP (inst);
dev = I_GETDEV (inst); /* IOT fields */
byac = I_GETPULSE (inst); /* byte fields */
xop = I_GETXOP (inst); /* XOP fields */
extind = val[1] & A_IND; /* extended fields */
extdisp = val[1] & AMASK;
for (dv = 0; (dev_val[dv] >= 0) && (dev_val[dv] != dev); dv++) ;
switch (j) { /* switch on class */
case I_V_NPN: /* no operands */
fprintf (of, "%s", opcode[i]); /* opcode */
break;
case I_V_R: /* reg only */
fprintf (of, "%s %-o", opcode[i], dst);
break;
case I_V_D: /* dev only */
if (dev_val[dv] >= 0)
fprintf (of, "%s %s", opcode[i], device[dv]);
else fprintf (of, "%s %-o", opcode[i], dev);
break;
case I_V_RD: /* reg, dev */
if (dev_val[dv] >= 0)
fprintf (of, "%s %-o,%s", opcode[i], dst, device[dv]);
else fprintf (of, "%s %-o,%-o", opcode[i], dst, dev);
break;
case I_V_M: /* addr only */
fprintf (of, "%s ", opcode[i]);
fprint_addr (of, addr, ind, mode, disp, FALSE, cflag);
break;
case I_V_RM: /* reg, addr */
fprintf (of, "%s %-o,", opcode[i], dst);
fprint_addr (of, addr, ind, mode, disp, FALSE, cflag);
break;
case I_V_RR: /* operate */
fprintf (of, "%s %-o,%-o", opcode[i], src, dst);
if (skp) fprintf (of, ",%s", skip[skp-1]);
break;
case I_V_BY: /* byte */
fprintf (of, "%s %-o,%-o", opcode[i], byac, dst);
break;
case I_V_2AC: /* reg, reg */
fprintf (of, "%s %-o,%-o", opcode[i], src, dst);
break;
case I_V_RSI: /* reg, short imm */
fprintf (of, "%s %-o,%-o", opcode[i], src + 1, dst);
break;
case I_V_LI: /* long imm */
fprintf (of, "%s %-o", opcode[i], val[1]);
return -1;
case I_V_RLI: /* reg, long imm */
fprintf (of, "%s %-o,%-o", opcode[i], val[1], dst);
return -1;
case I_V_LM: /* long addr */
fprintf (of, "%s ", opcode[i]);
fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag);
return -1;
case I_V_RLM: /* reg, long addr */
fprintf (of, "%s %-o,", opcode[i], dst);
fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag);
return -1;
case I_V_FRM: /* flt reg, long addr */
fprintf (of, "%s %-o,", opcode[i], dst);
fprint_addr (of, addr, extind, src, extdisp, TRUE, cflag);
return -1;
case I_V_FST: /* flt status */
fprintf (of, "%s ", opcode[i]);
fprint_addr (of, addr, extind, dst, extdisp, AMASK + 1, cflag);
return -1;
case I_V_XP: /* XOP */
fprintf (of, "%s %-o,%-o,%-o", opcode[i], src, dst, xop);
break; } /* end case */
return SCPE_OK; } /* end if */
} /* end for */
return SCPE_ARG;
}
/* Address parse
Inputs:
*cptr = pointer to input string
addr = current PC
ext = extended address
cflag = true if parsing for CPU
val[3] = output array
Outputs:
optr = pointer to next char in input string
NULL if error
*/
#define A_FL 001 /* CPU flag */
#define A_NX 002 /* index seen */
#define A_PER 004 /* period seen */
#define A_NUM 010 /* number seen */
#define A_SI 020 /* sign seen */
#define A_MI 040 /* - seen */
char *get_addr (char *cptr, t_addr addr, t_bool ext, int32 cflag, int32 *val)
{
int32 d, r, x, pflag;
char gbuf[CBUFSIZE];
int32 dmax, dsign;
if (ext) dmax = AMASK + 1; /* get max disp */
else dmax = I_M_DISP + 1;
dsign = dmax >> 1; /* get disp sign */
val[0] = 0; /* no indirect */
val[1] = 0; /* PC rel */
val[2] = 0; /* no addr */
pflag = cflag & A_FL; /* isolate flag */
if (*cptr == '@') { /* indirect? */
val[0] = 1;
cptr++; }
if (*cptr == '.') { /* relative? */
pflag = pflag | A_PER;
x = 1; /* "index" is PC */
cptr++; }
if (*cptr == '+') { /* + sign? */
pflag = pflag | A_SI;
cptr++; }
else if (*cptr == '-') { /* - sign? */
pflag = pflag | A_MI | A_SI;
cptr++; }
if (*cptr != 0) { /* number? */
cptr = get_glyph (cptr, gbuf, ','); /* get glyph */
d = get_uint (gbuf, 8, AMASK, &r);
if (r != SCPE_OK) return NULL;
pflag = pflag | A_NUM; }
if (*cptr != 0) { /* index? */
cptr = get_glyph (cptr, gbuf, 0); /* get glyph */
x = get_uint (gbuf, 8, I_M_DST, &r);
if ((r != SCPE_OK) || (x < 2)) return NULL;
pflag = pflag | A_NX; }
/* Address parse, continued */
switch (pflag) { /* case on flags */
case A_NUM: case A_NUM+A_SI: /* ~CPU, (+)num */
if (d < dmax) val[2] = d;
else return NULL;
break;
case A_NUM+A_FL: case A_NUM+A_SI+A_FL: /* CPU, (+)num */
if (d < dmax) val[2] = d;
else if (((d >= (((int32) addr - dsign) & AMASK)) &&
(d < (((int32) addr + dsign) & AMASK))) ||
(d >= ((int32) addr + (-dsign & AMASK)))) {
val[1] = 1; /* PC rel */
val[2] = (d - addr) & (dmax - 1); }
else return NULL;
break;
case A_PER: case A_PER+A_FL: /* . */
case A_PER+A_SI+A_NUM: case A_PER+A_SI+A_NUM+A_FL: /* . + num */
case A_PER+A_SI+A_MI+A_NUM: /* . - num */
case A_PER+A_SI+A_MI+A_NUM+A_FL:
case A_NX+A_NUM: case A_NX+A_NUM+A_FL: /* num, ndx */
case A_NX+A_SI+A_NUM: case A_NX+A_SI+A_NUM+A_FL: /* +num, ndx */
case A_NX+A_SI+A_MI+A_NUM: /* -num, ndx */
case A_NX+A_SI+A_MI+A_NUM+A_FL:
val[1] = x; /* set mode */
if (((pflag & A_MI) == 0) && (d < dsign)) val[2] = d;
else if ((pflag & A_MI) && (d <= dsign)) val[2] = (dmax - d);
else return NULL;
break;
default:
return NULL; } /* end case */
return cptr;
}
/* Parse two registers
Inputs:
*cptr = input string
term = second terminating character
val = output array
Outputs:
optr = pointer to next char in input string
NULL if error
*/
char *get_2reg (char *cptr, char term, int32 *val)
{
char gbuf[CBUFSIZE];
t_stat r;
cptr = get_glyph (cptr, gbuf, ','); /* get register */
val[0] = get_uint (gbuf, 8, I_M_SRC, &r);
if (r != SCPE_OK) return NULL;
cptr = get_glyph (cptr, gbuf, term); /* get register */
val[1] = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return NULL;
return cptr;
}
/* Symbolic input
Inputs:
*cptr = pointer to input string
addr = current PC
*uptr = pointer to unit
*val = pointer to output values
sw = switches
Outputs:
status = error status
*/
t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
int32 cflag, d, i, j, amd[3];
t_stat r, rtn;
char gbuf[CBUFSIZE];
cflag = (uptr == NULL) || (uptr == &cpu_unit);
while (isspace (*cptr)) cptr++; /* absorb spaces */
if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
val[0] = (t_value) cptr[0];
return SCPE_OK; }
if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */
if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */
val[0] = ((t_value) cptr[0] << 8) + (t_value) cptr[1];
return SCPE_OK; }
/* Instruction parse */
rtn = SCPE_OK; /* assume 1 word */
cptr = get_glyph (cptr, gbuf, 0); /* get opcode */
for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ;
if (opcode[i] == NULL) return SCPE_ARG;
val[0] = opc_val[i] & 0177777; /* get value */
j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */
switch (j) { /* case on class */
case I_V_NPN: /* no operand */
break;
case I_V_R: /* IOT reg */
cptr = get_glyph (cptr, gbuf, 0); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
break;
case I_V_RD: /* IOT reg,dev */
cptr = get_glyph (cptr, gbuf, ','); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
case I_V_D: /* IOT dev */
cptr = get_glyph (cptr, gbuf, 0); /* get device */
for (i = 0; (device[i] != NULL) && (strcmp (device[i], gbuf) != 0);
i++);
if (device[i] != NULL) val[0] = val[0] | dev_val[i];
else { d = get_uint (gbuf, 8, I_M_DEV, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DEV); }
break;
case I_V_RM: /* reg, addr */
cptr = get_glyph (cptr, gbuf, ','); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
case I_V_M: /* addr */
cptr = get_addr (cptr, addr, FALSE, cflag, amd);
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[0] << I_V_IND) | (amd[1] << I_V_MODE) | amd[2];
break;
case I_V_RR: /* operate */
cptr = get_2reg (cptr, ',', amd); /* get 2 reg */
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST);
if (*cptr != 0) { /* skip? */
cptr = get_glyph (cptr, gbuf, 0); /* get skip */
for (i = 0; (skip[i] != NULL) &&
(strcmp (skip[i], gbuf) != 0); i++) ;
if (skip[i] == NULL) return SCPE_ARG;
val[0] = val[0] | (i + 1); } /* end for */
break;
case I_V_BY: /* byte */
cptr = get_2reg (cptr, 0, amd); /* get 2 reg */
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[0] << I_V_PULSE) | (amd[1] << I_V_DST);
break;
case I_V_2AC: /* reg, reg */
cptr = get_2reg (cptr, 0, amd); /* get 2 reg */
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST);
break;
case I_V_RSI: /* reg, short imm */
cptr = get_glyph (cptr, gbuf, ','); /* get immediate */
d = get_uint (gbuf, 8, I_M_SRC + 1, &r);
if ((d == 0) || (r != SCPE_OK)) return SCPE_ARG;
val[0] = val[0] | ((d - 1) << I_V_SRC); /* put in place */
cptr = get_glyph (cptr, gbuf, 0); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
break;
case I_V_RLI: /* reg, long imm */
cptr = get_glyph (cptr, gbuf, ','); /* get immediate */
val[1] = get_uint (gbuf, 8, DMASK, &r);
if (r != SCPE_OK) return SCPE_ARG;
cptr = get_glyph (cptr, gbuf, 0); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
rtn = -1;
break;
case I_V_LI: /* long imm */
cptr = get_glyph (cptr, gbuf, 0); /* get immediate */
val[1] = get_uint (gbuf, 8, DMASK, &r);
if (r != SCPE_OK) return SCPE_ARG;
rtn = -1;
break;
case I_V_RLM: /* reg, long mem */
cptr = get_glyph (cptr, gbuf, ','); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
case I_V_LM: /* long mem */
cptr = get_addr (cptr, addr, TRUE, cflag, amd);
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[1] << I_V_MODE);
val[1] = (amd[0] << A_V_IND) | amd[2];
rtn = -1;
break;
case I_V_FRM: /* flt reg, long mem */
cptr = get_glyph (cptr, gbuf, ','); /* get register */
d = get_uint (gbuf, 8, I_M_DST, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_DST); /* put in place */
cptr = get_addr (cptr, addr, TRUE, cflag, amd);
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[1] << I_V_SRC);
val[1] = (amd[0] << A_V_IND) | amd[2];
rtn = -1;
break;
case I_V_FST: /* flt status */
cptr = get_addr (cptr, addr, TRUE, cflag, amd);
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[1] << I_V_DST);
val[1] = (amd[0] << A_V_IND) | amd[2];
rtn = -1;
break;
case I_V_XP: /* XOP */
cptr = get_2reg (cptr, ',', amd); /* get 2 reg */
if (cptr == NULL) return SCPE_ARG;
val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST);
cptr = get_glyph (cptr, gbuf, 0); /* get argument */
d = get_uint (gbuf, 8, I_M_XOP, &r);
if (r != SCPE_OK) return SCPE_ARG;
val[0] = val[0] | (d << I_V_XOP);
break; } /* end case */
if (*cptr != 0) return SCPE_ARG; /* any leftovers? */
return rtn;
}

209
NOVA/nova_tt.c Normal file
View File

@@ -0,0 +1,209 @@
/* nova_tt.c: NOVA console terminal simulator
Copyright (c) 1993-2001, Robert M. Supnik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Robert M Supnik shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
tti terminal input
tto terminal output
30-Nov-01 RMS Added extended SET/SHOW support
17-Sep-01 RMS Removed multiconsole support
07-Sep-01 RMS Moved function prototypes
31-May-01 RMS Added multiconsole support
*/
#include "nova_defs.h"
#define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */
#define UNIT_DASHER (1 << UNIT_V_DASHER)
extern int32 int_req, dev_busy, dev_done, dev_disable;
t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr);
t_stat ttx_setmod (UNIT *uptr, int32 val, char *cptr);
/* TTI data structures
tti_dev TTI device descriptor
tti_unit TTI unit descriptor
tti_reg TTI register list
ttx_mod TTI/TTO modifiers list
*/
UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT };
REG tti_reg[] = {
{ ORDATA (BUF, tti_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_TTI) },
{ FLDATA (DONE, dev_done, INT_V_TTI) },
{ FLDATA (DISABLE, dev_disable, INT_V_TTI) },
{ FLDATA (INT, int_req, INT_V_TTI) },
{ DRDATA (POS, tti_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT },
{ FLDATA (MODE, tti_unit.flags, UNIT_V_DASHER), REG_HRO },
{ NULL } };
MTAB ttx_mod[] = {
{ UNIT_DASHER, 0, "ANSI", "ANSI", &ttx_setmod },
{ UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx_setmod },
{ 0 } };
DEVICE tti_dev = {
"TTI", &tti_unit, tti_reg, ttx_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tti_reset,
NULL, NULL, NULL };
/* TTO data structures
tto_dev TTO device descriptor
tto_unit TTO unit descriptor
tto_reg TTO register list
*/
UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT };
REG tto_reg[] = {
{ ORDATA (BUF, tto_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_TTO) },
{ FLDATA (DONE, dev_done, INT_V_TTO) },
{ FLDATA (DISABLE, dev_disable, INT_V_TTO) },
{ FLDATA (INT, int_req, INT_V_TTO) },
{ DRDATA (POS, tto_unit.pos, 31), PV_LEFT },
{ DRDATA (TIME, tto_unit.wait, 24), PV_LEFT },
{ FLDATA (MODE, tto_unit.flags, UNIT_V_DASHER), REG_HRO },
{ NULL } };
DEVICE tto_dev = {
"TTO", &tto_unit, tto_reg, ttx_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tto_reset,
NULL, NULL, NULL };
/* Terminal input: IOT routine */
int32 tti (int32 pulse, int32 code, int32 AC)
{
int32 iodata;
iodata = (code == ioDIA)? tti_unit.buf & 0377: 0;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_TTI; /* set busy */
dev_done = dev_done & ~INT_TTI; /* clear done, int */
int_req = int_req & ~INT_TTI;
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_TTI; /* clear busy */
dev_done = dev_done & ~INT_TTI; /* clear done, int */
int_req = int_req & ~INT_TTI;
break; } /* end switch */
return iodata;
}
/* Unit service */
t_stat tti_svc (UNIT *uptr)
{
int32 temp;
sim_activate (&tti_unit, tti_unit.wait); /* continue poll */
if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */
tti_unit.buf = temp & 0177;
if ((tti_unit.flags & UNIT_DASHER) && (tti_unit.buf == '\r'))
tti_unit.buf = '\n'; /* Dasher: cr -> nl */
dev_busy = dev_busy & ~INT_TTI; /* clear busy */
dev_done = dev_done | INT_TTI; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
tti_unit.pos = tti_unit.pos + 1;
return SCPE_OK;
}
/* Reset routine */
t_stat tti_reset (DEVICE *dptr)
{
tti_unit.buf = 0;
dev_busy = dev_busy & ~INT_TTI; /* clear busy */
dev_done = dev_done & ~INT_TTI; /* clear done, int */
int_req = int_req & ~INT_TTI;
sim_activate (&tti_unit, tti_unit.wait); /* activate unit */
return SCPE_OK;
}
/* Terminal output: IOT routine */
int32 tto (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) tto_unit.buf = AC & 0377;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_TTO; /* set busy */
dev_done = dev_done & ~INT_TTO; /* clear done, int */
int_req = int_req & ~INT_TTO;
sim_activate (&tto_unit, tto_unit.wait); /* activate unit */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_TTO; /* clear busy */
dev_done = dev_done & ~INT_TTO; /* clear done, int */
int_req = int_req & ~INT_TTO;
sim_cancel (&tto_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat tto_svc (UNIT *uptr)
{
int32 c, temp;
dev_busy = dev_busy & ~INT_TTO; /* clear busy */
dev_done = dev_done | INT_TTO; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
c = tto_unit.buf & 0177;
if ((tto_unit.flags & UNIT_DASHER) && (c == 031)) c = '\b';
if ((temp = sim_putchar (c)) != SCPE_OK) return temp;
tto_unit.pos = tto_unit.pos + 1;
return SCPE_OK;
}
/* Reset routine */
t_stat tto_reset (DEVICE *dptr)
{
tto_unit.buf = 0;
dev_busy = dev_busy & ~INT_TTO; /* clear busy */
dev_done = dev_done & ~INT_TTO; /* clear done, int */
int_req = int_req & ~INT_TTO;
sim_cancel (&tto_unit); /* deactivate unit */
return SCPE_OK;
}
t_stat ttx_setmod (UNIT *uptr, int32 val, char *cptr)
{
tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | val;
tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | val;
return SCPE_OK;
}

276
NOVA/nova_tt1.c Normal file
View File

@@ -0,0 +1,276 @@
/* nova_tt1.c: NOVA second terminal simulator
Copyright (c) 1993-2001, Robert M. Supnik
Written by Bruce Ray and used with his gracious permission.
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.
tti1 second terminal input
tto1 second terminal output
30-Nov-01 RMS Added extended SET/SHOW support
17-Sep-01 RMS Changed to use terminal multiplexor library
07-Sep-01 RMS Moved function prototypes
31-May-01 RMS Added multiconsole support
26-Apr-01 RMS Added device enable/disable support
*/
#include "nova_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */
#define UNIT_DASHER (1 << UNIT_V_DASHER)
extern int32 int_req, dev_busy, dev_done, dev_disable, iot_enb;
extern int32 tmxr_poll; /* calibrated poll */
TMLN tt1_ldsc = { 0 }; /* line descriptors */
TMXR tt_desc = { 1, 0, &tt1_ldsc }; /* mux descriptor */
t_stat tti1_svc (UNIT *uptr);
t_stat tto1_svc (UNIT *uptr);
t_stat tti1_reset (DEVICE *dptr);
t_stat tto1_reset (DEVICE *dptr);
t_stat ttx1_setmod (UNIT *uptr, int32 val, char *cptr);
t_stat tti1_attach (UNIT *uptr, char *cptr);
t_stat tti1_detach (UNIT *uptr);
t_stat tti1_status (FILE *st, UNIT *uptr, int32 val, void *desc);
/* TTI1 data structures
tti1_dev TTI1 device descriptor
tti1_unit TTI1 unit descriptor
tti1_reg TTI1 register list
ttx1_mod TTI1/TTO1 modifiers list
*/
UNIT tti1_unit = { UDATA (&tti1_svc, UNIT_ATTABLE, 0), KBD_POLL_WAIT };
REG tti1_reg[] = {
{ ORDATA (BUF, tti1_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_TTI1) },
{ FLDATA (DONE, dev_done, INT_V_TTI1) },
{ FLDATA (DISABLE, dev_disable, INT_V_TTI1) },
{ FLDATA (INT, int_req, INT_V_TTI1) },
{ DRDATA (POS, tt1_ldsc.rxcnt, 31), PV_LEFT },
{ DRDATA (TIME, tti1_unit.wait, 24), REG_NZ + PV_LEFT },
{ FLDATA (MODE, tti1_unit.flags, UNIT_V_DASHER), REG_HRO },
{ FLDATA (*DEVENB, iot_enb, INT_V_TTI1), REG_HRO },
{ NULL } };
MTAB ttx1_mod[] = {
{ UNIT_DASHER, 0, "ANSI", "ANSI", &ttx1_setmod },
{ UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx1_setmod },
{ UNIT_ATT, UNIT_ATT, "line status", NULL, NULL, &tti1_status },
{ MTAB_XTD | MTAB_VDV | MTAB_VUN | MTAB_NMO, 0, "LINE", NULL,
NULL, &tti1_status, NULL },
{ 0 } };
DEVICE tti1_dev = {
"TTI1", &tti1_unit, tti1_reg, ttx1_mod,
1, 10, 31, 1, 8, 8,
&tmxr_ex, &tmxr_dep, &tti1_reset,
NULL, &tti1_attach, &tti1_detach };
/* TTO1 data structures
tto1_dev TTO1 device descriptor
tto1_unit TTO1 unit descriptor
tto1_reg TTO1 register list
*/
UNIT tto1_unit = { UDATA (&tto1_svc, 0, 0), SERIAL_OUT_WAIT };
REG tto1_reg[] = {
{ ORDATA (BUF, tto1_unit.buf, 8) },
{ FLDATA (BUSY, dev_busy, INT_V_TTO1) },
{ FLDATA (DONE, dev_done, INT_V_TTO1) },
{ FLDATA (DISABLE, dev_disable, INT_V_TTO1) },
{ FLDATA (INT, int_req, INT_V_TTO1) },
{ DRDATA (POS, tt1_ldsc.txcnt, 31), PV_LEFT },
{ DRDATA (TIME, tto1_unit.wait, 24), PV_LEFT },
{ FLDATA (MODE, tto1_unit.flags, UNIT_V_DASHER), REG_HRO },
{ FLDATA (*DEVENB, iot_enb, INT_V_TTI1), REG_HRO },
{ NULL } };
DEVICE tto1_dev = {
"TTO1", &tto1_unit, tto1_reg, ttx1_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &tto1_reset,
NULL, NULL, NULL };
/* Terminal input: IOT routine */
int32 tti1 (int32 pulse, int32 code, int32 AC)
{
int32 iodata;
iodata = (code == ioDIA)? tti1_unit.buf & 0377: 0;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_TTI1; /* set busy */
dev_done = dev_done & ~INT_TTI1; /* clear done, int */
int_req = int_req & ~INT_TTI1;
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_TTI1; /* clear busy */
dev_done = dev_done & ~INT_TTI1; /* clear done, int */
int_req = int_req & ~INT_TTI1;
break; } /* end switch */
return iodata;
}
/* Unit service */
t_stat tti1_svc (UNIT *uptr)
{
int32 temp, newln;
if (tt1_ldsc.conn) { /* connected? */
tmxr_poll_rx (&tt_desc); /* poll for input */
if (temp = tmxr_getc_ln (&tt1_ldsc)) { /* get char */
uptr -> buf = temp & 0177;
if ((uptr -> flags & UNIT_DASHER) &&
(uptr -> buf == '\r'))
uptr -> buf = '\n'; /* Dasher: cr -> nl */
dev_busy = dev_busy & ~INT_TTI1; /* clear busy */
dev_done = dev_done | INT_TTI1; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); }
sim_activate (uptr, uptr -> wait); } /* continue poll */
if (uptr -> flags & UNIT_ATT) { /* attached? */
newln = tmxr_poll_conn (&tt_desc, uptr); /* poll connect */
if (newln >= 0) { /* got one? */
sim_activate (&tti1_unit, tti1_unit.wait);
tt1_ldsc.rcve = 1; } /* rcv enabled */
sim_activate (uptr, tmxr_poll); } /* sched poll */
return SCPE_OK;
}
/* Reset routine */
t_stat tti1_reset (DEVICE *dptr)
{
tti1_unit.buf = 0;
dev_busy = dev_busy & ~INT_TTI1; /* clear busy */
dev_done = dev_done & ~INT_TTI1; /* clear done, int */
int_req = int_req & ~INT_TTI1;
if (tt1_ldsc.conn) { /* if conn, */
sim_activate (&tti1_unit, tti1_unit.wait); /* activate, */
tt1_ldsc.rcve = 1; } /* enable */
else if (tti1_unit.flags & UNIT_ATT) /* if attached, */
sim_activate (&tti1_unit, tmxr_poll); /* activate */
else sim_cancel (&tti1_unit); /* else stop */
return SCPE_OK;
}
/* Terminal output: IOT routine */
int32 tto1 (int32 pulse, int32 code, int32 AC)
{
if (code == ioDOA) tto1_unit.buf = AC & 0377;
switch (pulse) { /* decode IR<8:9> */
case iopS: /* start */
dev_busy = dev_busy | INT_TTO1; /* set busy */
dev_done = dev_done & ~INT_TTO1; /* clear done, int */
int_req = int_req & ~INT_TTO1;
sim_activate (&tto1_unit, tto1_unit.wait); /* activate unit */
break;
case iopC: /* clear */
dev_busy = dev_busy & ~INT_TTO1; /* clear busy */
dev_done = dev_done & ~INT_TTO1; /* clear done, int */
int_req = int_req & ~INT_TTO1;
sim_cancel (&tto1_unit); /* deactivate unit */
break; } /* end switch */
return 0;
}
/* Unit service */
t_stat tto1_svc (UNIT *uptr)
{
int32 c;
dev_busy = dev_busy & ~INT_TTO1; /* clear busy */
dev_done = dev_done | INT_TTO1; /* set done */
int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable);
c = tto1_unit.buf & 0177;
if ((tto1_unit.flags & UNIT_DASHER) && (c == 031)) c = '\b';
if (tt1_ldsc.conn) { /* connected? */
if (tt1_ldsc.xmte) { /* tx enabled? */
tmxr_putc_ln (&tt1_ldsc, c); /* output char */
tmxr_poll_tx (&tt_desc); } /* poll xmt */
else { tmxr_poll_tx (&tt_desc); /* poll xmt */
sim_activate (&tto1_unit, tmxr_poll); /* wait */
return SCPE_OK; } }
return SCPE_OK;
}
/* Reset routine */
t_stat tto1_reset (DEVICE *dptr)
{
tto1_unit.buf = 0;
dev_busy = dev_busy & ~INT_TTO1; /* clear busy */
dev_done = dev_done & ~INT_TTO1; /* clear done, int */
int_req = int_req & ~INT_TTO1;
sim_cancel (&tto1_unit); /* deactivate unit */
return SCPE_OK;
}
t_stat ttx1_setmod (UNIT *uptr, int32 val, char *cptr)
{
tti1_unit.flags = (tti1_unit.flags & ~UNIT_DASHER) | val;
tto1_unit.flags = (tto1_unit.flags & ~UNIT_DASHER) | val;
return SCPE_OK;
}
/* Attach routine */
t_stat tti1_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = tmxr_attach (&tt_desc, uptr, cptr); /* attach */
if (r != SCPE_OK) return r; /* error */
sim_activate (uptr, tmxr_poll); /* start poll */
return SCPE_OK;
}
/* Detach routine */
t_stat tti1_detach (UNIT *uptr)
{
t_stat r;
r = tmxr_detach (&tt_desc, uptr); /* detach */
tt1_ldsc.rcve = 0; /* disable rcv */
sim_cancel (uptr); /* stop poll */
return r;
}
/* Status routine */
t_stat tti1_status (FILE *st, UNIT *uptr, int32 val, void *desc)
{
tmxr_fstatus (st, &tt1_ldsc, -1);
return SCPE_OK;
}