KA10: Initial release of PDP10 KA/KI and PDP6 simulators.

This commit is contained in:
Richard Cornwell 2019-07-09 20:59:01 -04:00
parent 461f2ea513
commit 90b7d2beac
37 changed files with 29808 additions and 0 deletions

392
PDP10/ka10_auxcpu.c Normal file
View file

@ -0,0 +1,392 @@
/* ka10_auxcpu.c: Auxiliary processor.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device which interfaces with an auxiliary processor
through shared memory and inter-processor interrupts.
*/
#include "kx10_defs.h"
#include "sim_tmxr.h"
#ifndef NUM_DEVS_AUXCPU
#define NUM_DEVS_AUXCPU 0
#endif
#if NUM_DEVS_AUXCPU > 0
#include <fcntl.h>
//#include <unistd.h>
#include <sys/types.h>
/* External bus interface. */
#define DATO 1
#define DATI 2
#define ACK 3
#define ERR 4
#define TIMEOUT 5
#define IRQ 6
/* Simulator time units for a Unibus memory cycle. */
#define AUXCPU_MEM_CYCLE 100
/* Interprocessor interrupt device. */
#define AUXCPU_DEVNUM 020
#define AUXCPU_POLL 1000
static int pia = 0;
static int status = 0;
static t_stat auxcpu_devio(uint32 dev, t_uint64 *data);
static t_stat auxcpu_svc (UNIT *uptr);
static t_stat auxcpu_reset (DEVICE *dptr);
static t_stat auxcpu_attach (UNIT *uptr, CONST char *ptr);
static t_stat auxcpu_detach (UNIT *uptr);
static t_stat auxcpu_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
static const char *auxcpu_description (DEVICE *dptr);
UNIT auxcpu_unit[1] = {
{ UDATA (&auxcpu_svc, UNIT_IDLE|UNIT_ATTABLE, 0), 1000 },
};
static REG auxcpu_reg[] = {
{ DRDATAD (POLL, auxcpu_unit[0].wait, 24, "poll interval"), PV_LEFT },
{ NULL }
};
static MTAB auxcpu_mod[] = {
{ 0 }
};
#define DBG_TRC 1
#define DBG_CMD 2
static DEBTAB auxcpu_debug[] = {
{"TRACE", DBG_TRC, "Routine trace"},
{"CMD", DBG_CMD, "Command Processing"},
{0},
};
DEVICE auxcpu_dev = {
"AUXCPU", auxcpu_unit, auxcpu_reg, auxcpu_mod,
1, 8, 16, 2, 8, 16,
NULL, /* examine */
NULL, /* deposit */
&auxcpu_reset, /* reset */
NULL, /* boot */
auxcpu_attach, /* attach */
auxcpu_detach, /* detach */
NULL, /* context */
DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX,
DBG_CMD, /* debug control */
auxcpu_debug, /* debug flags */
NULL, /* memory size chage */
NULL, /* logical name */
NULL, /* help */
&auxcpu_attach_help, /* attach help */
NULL, /* help context */
&auxcpu_description, /* description */
};
static TMLN auxcpu_ldsc; /* line descriptor */
static TMXR auxcpu_desc = { 1, 0, 0, &auxcpu_ldsc }; /* mux descriptor */
static t_stat auxcpu_reset (DEVICE *dptr)
{
sim_debug(DBG_TRC, dptr, "auxcpu_reset()\n");
auxcpu_unit[0].flags |= UNIT_ATTABLE | UNIT_IDLE;
auxcpu_desc.packet = TRUE;
auxcpu_desc.notelnet = TRUE;
auxcpu_desc.buffered = 2048;
if (auxcpu_unit[0].flags & UNIT_ATT)
sim_activate (&auxcpu_unit[0], 1000);
else
sim_cancel (&auxcpu_unit[0]);
return SCPE_OK;
}
static t_stat auxcpu_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
if (!cptr || !*cptr)
return SCPE_ARG;
if (!(uptr->flags & UNIT_ATTABLE))
return SCPE_NOATT;
r = tmxr_attach_ex (&auxcpu_desc, uptr, cptr, FALSE);
if (r != SCPE_OK) /* error? */
return r;
sim_debug(DBG_TRC, &auxcpu_dev, "activate connection\n");
sim_activate (uptr, 10); /* start poll */
uptr->flags |= UNIT_ATT;
return SCPE_OK;
}
static t_stat auxcpu_detach (UNIT *uptr)
{
t_stat r;
if (!(uptr->flags & UNIT_ATT))
return SCPE_OK;
sim_cancel (uptr);
r = tmxr_detach (&auxcpu_desc, uptr);
uptr->flags &= ~UNIT_ATT;
free (uptr->filename);
uptr->filename = NULL;
return r;
}
static void build (unsigned char *request, unsigned char octet)
{
request[0]++;
request[request[0]] = octet & 0377;
}
static t_stat auxcpu_svc (UNIT *uptr)
{
tmxr_poll_rx (&auxcpu_desc);
if (auxcpu_ldsc.rcve && !auxcpu_ldsc.conn) {
auxcpu_ldsc.rcve = 0;
tmxr_reset_ln (&auxcpu_ldsc);
}
/* If incoming interrput => status |= 010 */
if (status & 010)
set_interrupt(AUXCPU_DEVNUM, pia);
else
clr_interrupt(AUXCPU_DEVNUM);
if (tmxr_poll_conn(&auxcpu_desc) >= 0) {
sim_debug(DBG_CMD, &auxcpu_dev, "got connection\n");
auxcpu_ldsc.rcve = 1;
uptr->wait = AUXCPU_POLL;
}
sim_activate (uptr, uptr->wait);
return SCPE_OK;
}
static t_stat auxcpu_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
const char helpString[] =
/* The '*'s in the next line represent the standard text width of a help line */
/****************************************************************************/
" The %D device connects a secondary processor that is sharing memory with the.\n"
" primary.\n\n"
" The device must be attached to a receive port, this is done by using the\n"
" ATTACH command to specify the receive port number.\n"
"\n"
"+sim> ATTACH %U port\n"
"\n"
;
return scp_help (st, dptr, uptr, flag, helpString, cptr);
return SCPE_OK;
}
static const char *auxcpu_description (DEVICE *dptr)
{
return "Auxiliary processor";
}
static int error (const char *message)
{
sim_debug (DBG_TRC, &auxcpu_dev, "%s\r\n", message);
sim_debug (DBG_TRC, &auxcpu_dev, "CLOSE\r\n");
auxcpu_ldsc.rcve = 0;
tmxr_reset_ln (&auxcpu_ldsc);
return -1;
}
static int transaction (unsigned char *request, unsigned char *response)
{
const uint8 *auxcpu_request;
size_t size;
t_stat stat;
stat = tmxr_put_packet_ln (&auxcpu_ldsc, request + 1, (size_t)request[0]);
if (stat != SCPE_OK)
return error ("Write error in transaction");
do {
tmxr_poll_rx (&auxcpu_desc);
stat = tmxr_get_packet_ln (&auxcpu_ldsc, &auxcpu_request, &size);
} while (stat != SCPE_OK || size == 0);
if (size > 7)
return error ("Malformed transaction");
memcpy (response, auxcpu_request, size);
return 0;
}
int auxcpu_read (int addr, t_uint64 *data)
{
unsigned char request[12];
unsigned char response[12];
sim_interval -= AUXCPU_MEM_CYCLE;
if ((auxcpu_unit[0].flags & UNIT_ATT) == 0) {
*data = 0;
return 0;
}
addr &= 037777;
memset (request, 0, sizeof request);
build (request, DATI);
build (request, addr & 0377);
build (request, (addr >> 8) & 0377);
build (request, (addr >> 16) & 0377);
transaction (request, response);
switch (response[0])
{
case ACK:
*data = (t_uint64)response[1];
*data |= (t_uint64)response[2] << 8;
*data |= (t_uint64)response[3] << 16;
*data |= (t_uint64)response[4] << 24;
*data |= (t_uint64)response[5] << 32;
break;
case ERR:
fprintf (stderr, "AUXCPU: Read error %06o\r\n", addr);
*data = 0;
break;
case TIMEOUT:
fprintf (stderr, "AUXCPU: Read timeout %06o\r\n", addr);
*data = 0;
break;
default:
return error ("Protocol error");
}
return 0;
}
int auxcpu_write (int addr, t_uint64 data)
{
unsigned char request[12];
unsigned char response[12];
sim_interval -= AUXCPU_MEM_CYCLE;
if ((ten11_unit[0].flags & UNIT_ATT) == 0) {
return 0;
}
addr &= 037777;
memset (request, 0, sizeof request);
build (request, DATO);
build (request, (addr) & 0377);
build (request, (addr >> 8) & 0377);
build (request, (addr >> 16) & 0377);
build (request, (data) & 0377);
build (request, (data >> 8) & 0377);
build (request, (data >> 16) & 0377);
build (request, (data >> 24) & 0377);
build (request, (data >> 32) & 0377);
transaction (request, response);
switch (response[0])
{
case ACK:
break;
case ERR:
fprintf (stderr, "AUXCPU: Write error %06o\r\n", addr);
break;
case TIMEOUT:
fprintf (stderr, "AUXCPU: Write timeout %06o\r\n", addr);
break;
default:
return error ("Protocol error");
}
return 0;
}
static int auxcpu_interrupt (void)
{
unsigned char request[12];
unsigned char response[12];
memset (request, 0, sizeof request);
sim_debug(DEBUG_IRQ, &auxcpu_dev, "PDP-10 interrupting the PDP-6\n");
build (request, IRQ);
transaction (request, response);
switch (response[1])
{
case ACK:
break;
case ERR:
case TIMEOUT:
fprintf (stderr, "AUXCPU: Interrupt error or timeout\r\n");
break;
default:
return error ("Protocol error");
}
return 0;
}
t_stat auxcpu_devio(uint32 dev, t_uint64 *data)
{
DEVICE *dptr = &auxcpu_dev;
switch(dev & 07) {
case CONO:
sim_debug(DEBUG_CONO, &auxcpu_dev, "CONO %012llo\n", *data);
pia = *data & 7;
if (*data & 010)
{
// Clear interrupt from the PDP-6.
status &= ~010;
clr_interrupt(AUXCPU_DEVNUM);
}
if (*data & 020)
auxcpu_interrupt ();
break;
case CONI:
*data = (status & 010) | pia;
sim_debug(DEBUG_CONI, &auxcpu_dev, "CONI %012llo\n", *data);
break;
case DATAI:
*data = 0;
sim_debug(DEBUG_CONI, &auxcpu_dev, "DATAI %012llo\n", *data);
break;
case DATAO:
sim_debug(DEBUG_CONI, &auxcpu_dev, "DATAO %012llo\n", *data);
break;
}
return SCPE_OK;
}
#endif

551
PDP10/ka10_ch10.c Normal file
View file

@ -0,0 +1,551 @@
/* ka10_ch.c: CH10 Chaosnet interface.
------------------------------------------------------------------------------
Copyright (c) 2019, Richard Cornwell, original by Lars Brinkhoff.
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
THE AUTHOR 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 the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
------------------------------------------------------------------------------
*/
#include "kx10_defs.h"
#include "sim_tmxr.h"
#ifndef NUM_DEVS_CH10
#define NUM_DEVS_CH10 0
#endif
#if (NUM_DEVS_CH10 > 0)
#define CH_DEVNUM 0470
/* CSR bits */
#define PIA 00000007LL /* Interrupt channel */
#define TXIE 00000010LL /* Transmit interrupt enable */
#define RXIE 00000020LL /* Receive interrupt enable */
#define SPY 00000040LL /* Spy */
#define LOOP 00000100LL /* Loop back */
#define SWAP 00000200LL /* Swap bytes */
#define HALF 00000400LL /* Halfword output */
#define TXD 00001000LL /* Transmit done */
#define RXD 00002000LL /* Receive done */
#define TXA 00004000LL /* Transmit abort */
#define CTX 00004000LL /* Clear transmitter */
#define LOST 00170000LL /* Lost count */
#define RESET 00010000LL /* Reset */
#define CRC 00200000LL /* CRC error */
#define WLE 00400000LL /* Word length error */
#define PLE 01000000LL /* Packet length error */
#define OVER 02000000LL /* Overrun */
#define STATUS_BITS (PIA|TXIE|RXIE|SPY|LOOP|SWAP|HALF|TXA|LOST|CRC|WLE|PLE|OVER)
BITFIELD ch10_csr_bits[] = {
BIT(PIA),
BIT(TXIE),
BIT(RXIE),
BIT(SPY),
BIT(LOOP),
BIT(SWAP),
BIT(HALF),
BIT(TXD),
BIT(RXD),
BIT(TXA),
BIT(CTX),
BITF(LOST,4),
BIT(CRC),
BIT(WLE),
BIT(PLE),
BIT(OVER),
ENDBITS
};
#define CHUDP_HEADER 4
#define IOLN_CH 020
#define DBG_TRC 0x0001
#define DBG_REG 0x0002
#define DBG_PKT 0x0004
#define DBG_DAT 0x0008
#define DBG_INT 0x0010
#define DBG_ERR 0x0020
t_stat ch10_svc(UNIT *);
t_stat ch10_reset (DEVICE *);
t_stat ch10_attach (UNIT *, CONST char *);
t_stat ch10_detach (UNIT *);
t_stat ch10_devio(uint32 dev, uint64 *data);
t_stat ch10_show_peer (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
t_stat ch10_set_peer (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
t_stat ch10_show_node (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
t_stat ch10_set_node (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
t_stat ch10_help (FILE *, DEVICE *, UNIT *, int32, const char *);
t_stat ch10_help_attach (FILE *, DEVICE *, UNIT *, int32, const char *);
const char *ch10_description (DEVICE *);
static char peer[256];
int address;
static uint64 ch10_status;
static int rx_count;
static int tx_count;
static uint8 rx_buffer[512+100];
static uint8 tx_buffer[512+100];
TMLN ch10_lines[1] = { {0} };
TMXR ch10_tmxr = { 1, NULL, 0, ch10_lines};
UNIT ch10_unit[] = {
{UDATA (&ch10_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },
};
REG ch10_reg[] = {
{ GRDATADF(CSR, ch10_status, 16, 16, 0, "Control and status", ch10_csr_bits), REG_FIT },
{ GRDATAD(RXCNT, rx_count, 16, 16, 0, "Receive word count"), REG_FIT|REG_RO},
{ GRDATAD(TXCNT, tx_count, 16, 16, 0, "Transmit word count"), REG_FIT|REG_RO},
{ BRDATAD(RXBUF, rx_buffer, 16, 8, sizeof rx_buffer, "Receive packet buffer"), REG_FIT},
{ BRDATAD(TXBUF, tx_buffer, 16, 8, sizeof tx_buffer, "Transmit packet buffer"), REG_FIT},
{ BRDATAD(PEER, peer, 16, 8, sizeof peer, "Network peer"), REG_HRO},
{ GRDATAD(NODE, address, 16, 16, 0, "Node address"), REG_HRO},
{ NULL } };
MTAB ch10_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PEER", "PEER",
&ch10_set_peer, &ch10_show_peer, NULL, "Remote host name and port" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "NODE", "NODE",
&ch10_set_node, &ch10_show_node, NULL, "Chaosnet node address" },
{ 0 },
};
DIB ch10_dib = {CH_DEVNUM, 1, &ch10_devio, NULL};
DEBTAB ch10_debug[] = {
{ "TRC", DBG_TRC, "Detailed trace" },
{ "REG", DBG_REG, "Hardware registers" },
{ "PKT", DBG_PKT, "Packets" },
{ "DAT", DBG_DAT, "Packet data" },
{ "INT", DBG_INT, "Interrupts" },
{ "ERR", DBG_ERR, "Error conditions" },
{ 0 }
};
DEVICE ch10_dev = {
"CH", ch10_unit, ch10_reg, ch10_mod,
1, 8, 16, 1, 8, 16,
NULL, NULL, &ch10_reset,
NULL, &ch10_attach, &ch10_detach,
&ch10_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX,
0, ch10_debug, NULL, NULL, &ch10_help, &ch10_help_attach, NULL,
&ch10_description
};
uint16 ch10_checksum (const uint8 *p, int count)
{
int32 sum = 0;
while (count > 1) {
sum += (p[0]<<8) | p[1];
p += 2;
count -= 2;
}
if ( count > 0)
sum += p[0];
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (~sum) & 0xffff;
}
int ch10_test_int (void)
{
if ((ch10_status & (RXD|RXIE)) == (RXD|RXIE) ||
(ch10_status & (TXD|TXIE)) == (TXD|TXIE)) {
sim_debug (DBG_INT, &ch10_dev, "%s %s Interrupt\n",
ch10_status & RXD ? "RX" : "",
ch10_status & TXD ? "TX" : "");
set_interrupt(CH_DEVNUM, ch10_status & PIA);
return 1;
} else {
clr_interrupt(CH_DEVNUM);
return 0;
}
}
void ch10_validate (const uint8 *p, int count)
{
uint16 chksum;
int size;
sim_debug (DBG_TRC, &ch10_dev, "Packet opcode: %02x\n", p[0]);
sim_debug (DBG_TRC, &ch10_dev, "MBZ: %02x\n", p[1]);
sim_debug (DBG_TRC, &ch10_dev, "Forwarding count: %02x\n", p[2] >> 4);
size = ((p[2] & 0xF) << 8) + p[3];
sim_debug (DBG_TRC, &ch10_dev, "Packet size: %03x\n", size);
sim_debug (DBG_TRC, &ch10_dev, "Destination address: %o\n", (p[4] << 8) + p[5]);
sim_debug (DBG_TRC, &ch10_dev, "Destination index: %02x\n", (p[6] << 8) + p[7]);
sim_debug (DBG_TRC, &ch10_dev, "Source address: %o\n", (p[8] << 8) + p[9]);
sim_debug (DBG_TRC, &ch10_dev, "Source index: %02x\n", (p[10] << 8) + p[11]);
sim_debug (DBG_TRC, &ch10_dev, "Packet number: %02x\n", (p[12] << 8) + p[13]);
sim_debug (DBG_TRC, &ch10_dev, "Acknowledgement: %02x\n", (p[14] << 8) + p[15]);
if (p[1] != 0)
sim_debug (DBG_ERR, &ch10_dev, "Bad packet\n");
chksum = ch10_checksum (p, count);
if (chksum != 0) {
sim_debug (DBG_ERR, &ch10_dev, "Checksum error: %04x\n", chksum);
ch10_status |= CRC;
} else
sim_debug (DBG_TRC, &ch10_dev, "Checksum: %05o\n", chksum);
}
t_stat ch10_transmit ()
{
size_t len;
t_stat r;
int i = CHUDP_HEADER + tx_count;
uint16 chk;
if (tx_count > (512 - CHUDP_HEADER)) {
sim_debug (DBG_PKT, &ch10_dev, "Pack size failed, %d bytes.\n", (int)tx_count);
ch10_status |= PLE;
return SCPE_INCOMP;
}
tx_buffer[i] = tx_buffer[8+CHUDP_HEADER];
tx_buffer[i+1] = tx_buffer[9+CHUDP_HEADER];
tx_count += 2;
chk = ch10_checksum(tx_buffer + CHUDP_HEADER, tx_count);
tx_buffer[i+2] = (chk >> 8) & 0xff;
tx_buffer[i+3] = chk & 0xff;
tx_count += 2;
tmxr_poll_tx (&ch10_tmxr);
len = CHUDP_HEADER + (size_t)tx_count;
r = tmxr_put_packet_ln (&ch10_lines[0], (const uint8 *)&tx_buffer, len);
if (r == SCPE_OK) {
sim_debug (DBG_PKT, &ch10_dev, "Sent UDP packet, %d bytes.\n", (int)len);
tmxr_poll_tx (&ch10_tmxr);
} else {
sim_debug (DBG_ERR, &ch10_dev, "Sending UDP failed: %d.\n", r);
ch10_status |= OVER;
}
tx_count = 0;
ch10_test_int ();
return SCPE_OK;
}
void ch10_receive (void)
{
size_t count;
const uint8 *p;
uint16 dest;
tmxr_poll_rx (&ch10_tmxr);
if (tmxr_get_packet_ln (&ch10_lines[0], &p, &count) != SCPE_OK) {
sim_debug (DBG_ERR, &ch10_dev, "TMXR error receiving packet\n");
return;
}
if (p == NULL)
return;
dest = ((p[4+CHUDP_HEADER] & 0xff) << 8) + (p[5+CHUDP_HEADER] & 0xff);
sim_debug (DBG_PKT, &ch10_dev, "Received UDP packet, %d bytes for: %o\n", (int)count, dest);
/* Check if packet for us. */
if (dest != address && dest != 0 && (ch10_status & SPY) == 0)
return;
if ((RXD & ch10_status) == 0) {
count = (count + 1) & 0776;
memcpy (rx_buffer + (512 - count), p, count);
rx_count = count;
sim_debug (DBG_TRC, &ch10_dev, "Rx count, %d\n", rx_count);
ch10_validate (p + CHUDP_HEADER, count - CHUDP_HEADER);
ch10_status |= RXD;
ch10_lines[0].rcve = FALSE;
sim_debug (DBG_TRC, &ch10_dev, "Rx off\n");
ch10_test_int ();
} else {
sim_debug (DBG_ERR, &ch10_dev, "Lost packet\n");
if ((ch10_status & LOST) < LOST)
ch10_status += 01000;
}
}
void ch10_clear (void)
{
ch10_status = TXD;
rx_count = 0;
tx_count = 0;
tx_buffer[0] = 1; /* CHUDP header */
tx_buffer[1] = 1;
tx_buffer[2] = 0;
tx_buffer[3] = 0;
ch10_lines[0].rcve = TRUE;
ch10_test_int ();
}
void ch10_command (uint32 data)
{
if (data & RXD) {
sim_debug (DBG_REG, &ch10_dev, "Clear RX\n");
ch10_status &= ~RXD;
rx_count = 0;
ch10_lines[0].rcve = TRUE;
rx_count = 0;
}
if (data & RESET) {
/* Do this first so other bits can do their things. */
sim_debug (DBG_REG, &ch10_dev, "Reset\n");
ch10_clear ();
}
if (data & CTX) {
sim_debug (DBG_REG, &ch10_dev, "Clear TX\n");
tx_count = 0;
ch10_status |= TXD;
ch10_status &= ~TXA;
}
if (data & TXD) {
sim_debug (DBG_REG, &ch10_dev, "XMIT TX\n");
ch10_transmit();
ch10_status &= ~TXA;
}
}
t_stat ch10_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &imx_dev;
switch(dev & 07) {
case CONO:
sim_debug (DBG_REG, &ch10_dev, "CONO %012llo %012llo \n", *data, ch10_status);
ch10_command ((uint32)(*data & RMASK));
ch10_status &= ~STATUS_BITS;
ch10_status |= *data & STATUS_BITS;
ch10_test_int ();
break;
case CONI:
*data = ch10_status & (STATUS_BITS|TXD|RXD);
*data |= (uint64)address << 20;
break;
case DATAO:
ch10_status &= ~TXD;
if (tx_count < 512) {
int i = CHUDP_HEADER + tx_count;
if (ch10_status & SWAP) {
tx_buffer[i] = (*data >> 20) & 0xff;
tx_buffer[i+1] = (*data >> 28) & 0xff;
} else {
tx_buffer[i] = (*data >> 28) & 0xff;
tx_buffer[i+1] = (*data >> 20) & 0xff;
}
tx_count+=2;
if ((ch10_status & HALF) == 0) {
if (ch10_status & SWAP) {
tx_buffer[i+2] = (*data >> 4) & 0xff;
tx_buffer[i+3] = (*data >> 12) & 0xff;
} else {
tx_buffer[i+2] = (*data >> 12) & 0xff;
tx_buffer[i+3] = (*data >> 4) & 0xff;
}
tx_count+=2;
}
sim_debug (DBG_DAT, &ch10_dev, "Write buffer word %d:%02x %02x %02x %02x %012llo %012llo\n",
tx_count, tx_buffer[i], tx_buffer[i+1], tx_buffer[i+2], tx_buffer[i+3], *data, ch10_status);
return SCPE_OK;
} else {
sim_debug (DBG_ERR, &ch10_dev, "Write buffer overflow\n");
ch10_status |= PLE;
return SCPE_OK;
}
case DATAI:
if (rx_count == 0) {
*data = 0;
sim_debug (DBG_ERR, &ch10_dev, "Read empty buffer\n");
} else {
int i = 512-rx_count;
ch10_status &= ~RXD;
if (ch10_status & SWAP) {
*data = ((uint64)(rx_buffer[i]) & 0xff) << 20;
*data |= ((uint64)(rx_buffer[i+1]) & 0xff) << 28;
*data |= ((uint64)(rx_buffer[i+2]) & 0xff) << 4;
*data |= ((uint64)(rx_buffer[i+3]) & 0xff) << 12;
} else {
*data = ((uint64)(rx_buffer[i]) & 0xff) << 28;
*data |= ((uint64)(rx_buffer[i+1]) & 0xff) << 20;
*data |= ((uint64)(rx_buffer[i+2]) & 0xff) << 12;
*data |= ((uint64)(rx_buffer[i+3]) & 0xff) << 4;
}
rx_count-=4;
sim_debug (DBG_DAT, &ch10_dev, "Read buffer word %d:%02x %02x %02x %02x %012llo %012llo\n",
rx_count, rx_buffer[i], rx_buffer[i+1], rx_buffer[i+2], rx_buffer[i+3], *data, ch10_status);
}
}
return SCPE_OK;
}
t_stat ch10_svc(UNIT *uptr)
{
sim_clock_coschedule (uptr, 1000);
(void)tmxr_poll_conn (&ch10_tmxr);
if (ch10_lines[0].conn) {
ch10_receive ();
}
if (tx_count == 0)
ch10_status |= TXD;
ch10_test_int ();
return SCPE_OK;
}
t_stat ch10_attach (UNIT *uptr, CONST char *cptr)
{
char linkinfo[256];
t_stat r;
ch10_dev.dctrl |= 0xF77F0000;
if (address == -1)
return sim_messagef (SCPE_2FARG, "Must set Chaosnet NODE address first \"SET CH NODE=val\"\n");
if (peer[0] == '\0')
return sim_messagef (SCPE_2FARG, "Must set Chaosnet PEER \"SET CH PEER=host:port\"\n");
snprintf (linkinfo, sizeof(linkinfo), "Buffer=%d,UDP,%s,PACKET,Connect=%s,Line=0",
(int)sizeof tx_buffer, cptr, peer);
r = tmxr_attach (&ch10_tmxr, uptr, linkinfo);
if (r != SCPE_OK) {
sim_debug (DBG_ERR, &ch10_dev, "TMXR error opening master\n");
return sim_messagef (r, "Error Opening: %s\n", peer);
}
uptr->filename = (char *)realloc (uptr->filename, 1 + strlen (cptr));
strcpy (uptr->filename, cptr);
sim_activate (uptr, 1000);
return SCPE_OK;
}
t_stat ch10_detach (UNIT *uptr)
{
sim_cancel (uptr);
tmxr_detach (&ch10_tmxr, uptr);
return SCPE_OK;
}
t_stat ch10_reset (DEVICE *dptr)
{
ch10_clear ();
if (ch10_unit[0].flags & UNIT_ATT)
sim_activate (&ch10_unit[0], 100);
return SCPE_OK;
}
t_stat ch10_show_peer (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
{
fprintf (st, "peer=%s", peer[0] ? peer : "unspecified");
return SCPE_OK;
}
t_stat ch10_set_peer (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
char host[256], port[256];
if ((cptr == NULL) || (*cptr == 0))
return SCPE_ARG;
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
if (sim_parse_addr (cptr, host, sizeof host, NULL, port, sizeof port, NULL, NULL))
return SCPE_ARG;
if (host[0] == '\0')
return SCPE_ARG;
strncpy (peer, cptr, sizeof(peer) - 1);
return SCPE_OK;
}
t_stat ch10_show_node (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
{
if (address == -1)
fprintf (st, "node=unspecified");
else
fprintf (st, "node=%o", address);
return SCPE_OK;
}
t_stat ch10_set_node (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
t_stat r;
int x;
if ((cptr == NULL) || (*cptr == 0))
return SCPE_ARG;
if (uptr->flags & UNIT_ATT)
return SCPE_ALATT;
x = (int)get_uint (cptr, 8, 0177777, &r);
if (r != SCPE_OK)
return SCPE_ARG;
address = x;
return SCPE_OK;
}
const char *ch10_description (DEVICE *dptr)
{
return "CH11 Chaosnet interface";
}
t_stat ch10_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "CH10 Chaosnet interface\n\n");
fprintf (st, "It's a network interface for MIT's Chaosnet. Options allow\n");
fprintf (st, "control of the node address and network peer. The node address must\n");
fprintf (st, "be a 16-bit octal number.\n");
fprint_set_help (st, dptr);
fprintf (st, "\nConfigured options and controller state can be displayed with:\n");
fprint_show_help (st, dptr);
fprintf (st, "\nThe CH10 simulation will encapsulate Chaosnet packets in UDP or TCP.\n");
fprintf (st, "To access the network, the simulated Chaosnet interface must be attached\n");
fprintf (st, "to a network peer.\n\n");
ch10_help_attach (st, dptr, uptr, flag, cptr);
fprintf (st, "Software that runs on SIMH that supports this device include:\n");
fprintf (st, " - ITS, the PDP-10 Incompatible Timesharing System\n");
fprintf (st, "Outside SIMH, there's KLH10 and Lisp machine simulators. Various\n");
fprintf (st, "encapsulating transport mechanisms exist: UDP, IP, Ethernet.\n\n");
fprintf (st, "Documentation:\n");
fprintf (st, "https://lm-3.github.io/amber.html#Hardware-Programming-Documentation\n\n");
return SCPE_OK;
}
t_stat ch10_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "To configure CH10, first set the local Chaosnet node address, and\n");
fprintf (st, "the peer:\n\n");
fprintf (st, " sim> SET CH NODE=<octal address>\n");
fprintf (st, " sim> SET CH PEER=<remote host>:<remote port>\n\n");
fprintf (st, "Then, attach a local port. By default UDP is used:\n\n");
fprintf (st, " sim> ATTACH CH <local port>\n\n");
fprintf (st, "If TCP is desired, add \"TCP\":\n\n");
fprintf (st, " sim> ATTACH CH <local port>,TCP\n\n");
return SCPE_OK;
}
#endif

98
PDP10/ka10_dkb.c Normal file
View file

@ -0,0 +1,98 @@
/* ka10_dkb.c:Stanford Microswitch scanner.
Copyright (c) 2013-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_DKB
#define NUM_DEVS_DKB 0
#endif
#if NUM_DEVS_DKB > 0
t_stat dkb_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *dkb_description (DEVICE *dptr);
#define DKB_DEVNUM 0310
#define STATUS u3
#define DATA u4
#define PIA u5
t_stat dkb_devio(uint32 dev, uint64 *data);
DIB dkb_dib = { DKB_DEVNUM, 1, dkb_devio, NULL};
UNIT dkb_unit[] = {
{ 0 }
};
MTAB dkb_mod[] = {
{ 0 }
};
DEVICE dkb_dev = {
"DKB", dkb_unit, NULL, dkb_mod,
2, 10, 31, 1, 8, 8,
NULL, NULL, NULL,
NULL, NULL, NULL, &dkb_dib, DEV_DEBUG | DEV_DISABLE | DEV_DIS, 0, dev_debug,
NULL, NULL, &dkb_help, NULL, NULL, &dkb_description
};
int status;
t_stat dkb_devio(uint32 dev, uint64 *data) {
/* uint64 res; */
switch(dev & 3) {
case CONI:
*data = status;
sim_debug(DEBUG_CONI, &dkb_dev, "DKB %03o CONI %06o\n", dev, (uint32)*data);
break;
case CONO:
status = (int)(*data&7);
sim_debug(DEBUG_CONO, &dkb_dev, "DKB %03o CONO %06o\n", dev, (uint32)*data);
break;
case DATAI:
sim_debug(DEBUG_DATAIO, &dkb_dev, "DKB %03o DATAI %06o\n", dev, (uint32)*data);
break;
case DATAO:
sim_debug(DEBUG_DATAIO, &dkb_dev, "DKB %03o DATAO %06o\n", dev, (uint32)*data);
break;
}
return SCPE_OK;
}
t_stat dkb_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
return SCPE_OK;
}
const char *dkb_description (DEVICE *dptr)
{
return "Console TTY Line";
}
#endif

391
PDP10/ka10_dpk.c Normal file
View file

@ -0,0 +1,391 @@
/* ka10_dpk.c: Systems Concepts DK-10, Datapoint kludge.
Copyright (c) 2018-2019, Lars Brinkhoff
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
RICHARD CORNWELL 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.
The Systems Concepts DK-10, also known as the Datapoint kludge, is
a device with 16 terminal ports. It's specific to the MIT AI lab
PDP-10.
*/
#include <time.h>
#include "sim_defs.h"
#include "sim_tmxr.h"
#include "kx10_defs.h"
#define DPK_NAME "DPK"
#define DPK_DEVNUM 0604
#define DPK_LINES 16
#define DPK_IEN 04000000 /* Interrupt enable. */
#define DPK_PIA 000000007 /* PI channel assignment */
#define DPK_IDONE 000000010 /* Input char available. */
#define DPK_NXM 000000020 /* NXM. */
#define DPK_PAR 000000040 /* Parity error. */
#define DPK_BUSY 000000100 /* Ouput line busy. */
#define DPK_IN 000000200 /* State of input line. */
#define DPK_ODONE 000000400 /* Output buffer done. */
#define DPK_OLINE 017000000 /* Line number, output. */
#define DPK_CONI_BITS (DPK_PIA | DPK_IDONE | DPK_PAR | DPK_NXM | \
DPK_BUSY | DPK_IN | DPK_ODONE | DPK_OLINE)
#define DPK_FN 000000700 /* Function. */
#define DPK_SET_ODONE 000000000 /* Set output done. */
#define DPK_OSTART 000000100 /* Start output. */
#define DPK_ISTOP 000000200 /* Stop input. */
#define DPK_ISTART 000000300 /* Start input. */
#define DPK_OSTOP 000000400 /* Stop output, clear output done. */
#define DPK_OSPEED 000000500 /* Set output speed, start output. */
#define DPK_ISPEED_STOP 000000600 /* Set input speed, stop input. */
#define DPK_ISPEED_START 000000700 /* Set input speed, start input. */
#define DPK_SPEED 000007000 /* Speed code. */
#define DPK_ILINE 000170000 /* Line number. */
#define DPK_MANY 000200000 /* Apply to selected line through highest. */
#define DPK_RESET 000400000 /* Master clear. */
#define PORT_OUTPUT 1
#define PORT_INPUT 2
static t_stat dpk_devio(uint32 dev, uint64 *data);
static t_stat dpk_svc (UNIT *uptr);
static t_stat dpk_reset (DEVICE *dptr);
static t_stat dpk_attach (UNIT *uptr, CONST char *cptr);
static t_stat dpk_detach (UNIT *uptr);
static const char *dpk_description (DEVICE *dptr);
static t_stat dpk_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr);
extern int32 tmxr_poll;
TMLN dpk_ldsc[DPK_LINES] = { 0 };
TMXR dpk_desc = { DPK_LINES, 0, 0, dpk_ldsc };
static int dpk_ien = 0;
static int dpk_base = 0;
static int dpk_status = 0;
static int dpk_port[16];
static int dpk_ibuf[16];
static int dpk_ird = 0;
static int dpk_iwr = 0;
UNIT dpk_unit[] = {
{UDATA(dpk_svc, TT_MODE_8B|UNIT_ATTABLE|UNIT_DISABLE, 0)}, /* 0 */
};
DIB dpk_dib = {DPK_DEVNUM, 1, &dpk_devio, NULL};
MTAB dpk_mod[] = {
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" },
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &dpk_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
NULL, &tmxr_show_summ, (void *) &dpk_desc, "Display a summary of line states" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &dpk_desc, "Display current connections" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &dpk_desc, "Display multiplexer statistics" },
{ 0 }
};
DEVICE dpk_dev = {
DPK_NAME, dpk_unit, NULL, dpk_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, dpk_reset, NULL, dpk_attach, dpk_detach,
&dpk_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug,
NULL, NULL, dpk_help, NULL, NULL, dpk_description
};
static t_stat dpk_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &dpk_dev;
int port;
switch(dev & 07) {
case CONO|4:
sim_debug(DEBUG_CONO, &dpk_dev, "%012llo\n", *data);
port = (*data & DPK_ILINE) >> 12;
if (*data & DPK_RESET)
dpk_reset (&dpk_dev);
if (*data & DPK_IDONE) {
dpk_status &= ~DPK_IDONE;
dpk_iwr = dpk_ird = 0;
}
if (*data & DPK_PAR)
dpk_status &= ~DPK_PAR;
if (*data & DPK_NXM)
dpk_status &= ~DPK_NXM;
switch (*data & DPK_FN) {
case DPK_SET_ODONE:
dpk_status |= DPK_ODONE;
break;
case DPK_OSTART:
dpk_port[port] |= PORT_OUTPUT;
dpk_status &= ~DPK_ODONE;
break;
case DPK_ISTOP:
dpk_port[port] &= ~PORT_INPUT;
break;
case DPK_ISTART:
dpk_port[port] |= PORT_OUTPUT;
break;
case DPK_OSTOP:
dpk_port[port] &= ~PORT_OUTPUT;
dpk_status &= ~DPK_ODONE;
break;
case DPK_OSPEED:
sim_debug(DEBUG_CMD, &dpk_dev, "Set port %d output speed %lld\n",
port, (*data & DPK_SPEED) >> 9);
dpk_port[port] |= PORT_OUTPUT;
break;
case DPK_ISPEED_STOP:
dpk_port[port] &= ~PORT_INPUT;
goto ispeed;
case DPK_ISPEED_START:
dpk_port[port] |= PORT_INPUT;
ispeed:
sim_debug(DEBUG_CMD, &dpk_dev, "Set port %d input speed %lld\n",
port, (*data & DPK_SPEED) >> 9);
break;
default:
fprintf (stderr, "Unknown function: %llo\n", *data);
exit (1);
break;
}
dpk_status &= ~DPK_PIA;
dpk_status |= *data & DPK_PIA;
break;
case CONI|4:
*data = dpk_status & DPK_CONI_BITS;
sim_debug(DEBUG_CONI, &dpk_dev, "%07llo\n", *data);
break;
case DATAO|4:
dpk_base = *data & 03777777;
if (*data & DPK_IEN)
dpk_ien = 1;
sim_debug(DEBUG_DATAIO, &dpk_dev, "DATAO %06llo\n", *data);
break;
case DATAI|4:
if (dpk_ird == dpk_iwr) {
*data = 0;
break;
}
*data = dpk_ibuf[dpk_ird++];
dpk_ird &= 15;
sim_debug(DEBUG_DATAIO, &dpk_dev, "DATAI %06llo\n", *data);
if (dpk_ird == dpk_iwr) {
dpk_status &= ~DPK_IDONE;
}
break;
}
if (dpk_ien && (dpk_status & (DPK_IDONE | DPK_ODONE)))
set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA);
else
clr_interrupt(DPK_DEVNUM);
return SCPE_OK;
}
/* Note, the byte pointer used by the hardware has halfwords swapped. */
static int ildb (uint64 *pointer)
{
uint64 bp = *pointer;
int pos, ch, addr;
again:
pos = (bp >> 12) & 077;
pos -= 7;
addr = (bp >> 18) & 0777777;
if (pos < 0) {
pos = 36 - 7;
addr++;
addr &= 0777777;
}
if (M[addr] & 1) {
bp = M[addr];
goto again;
} else
*pointer = ((uint64)addr << 18) | (pos << 12) | 7 << 6;
ch = (M[addr] >> pos) & 0177;
return ch;
}
static int dpk_output (int port, TMLN *lp)
{
uint64 count;
int ch;
if ((dpk_port[port] & PORT_OUTPUT) == 0)
return 0;
if (M[dpk_base + 2*port] == 0777777777777LL) {
dpk_port[port] &= ~PORT_OUTPUT;
dpk_status &= ~DPK_OLINE;
dpk_status |= port << 18;
dpk_status |= DPK_ODONE;
if (dpk_ien)
set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA);
return 0;
}
ch = ildb (&M[dpk_base + 2*port + 1]);
ch = sim_tt_outcvt(ch & 0377, TT_GET_MODE (dpk_unit[0].flags));
tmxr_putc_ln (lp, ch);
count = M[dpk_base + 2*port] - 1;
M[dpk_base + 2*port] = count & 0777777777777LL;
return 1;
}
static t_stat dpk_svc (UNIT *uptr)
{
static int scan = 0;
int i;
/* 16 ports at 4800 baud, rounded up. */
sim_activate_after (uptr, 200);
i = tmxr_poll_conn (&dpk_desc);
if (i >= 0) {
dpk_ldsc[i].conn = 1;
dpk_ldsc[i].rcve = 1;
dpk_ldsc[i].xmte = 1;
sim_debug(DEBUG_CMD, &dpk_dev, "Connect %d\n", i);
}
tmxr_poll_rx (&dpk_desc);
tmxr_poll_tx (&dpk_desc);
for (i = 0; i < DPK_LINES; i++) {
/* Round robin scan 16 lines. */
scan = (scan + 1) & 017;
/* 1 means the line became ready since the last check. Ignore
-1 which means "still ready". */
if (tmxr_txdone_ln (&dpk_ldsc[scan])) {
if (dpk_output (scan, &dpk_ldsc[scan]))
break;
}
if (!dpk_ldsc[scan].conn)
continue;
if (tmxr_input_pending_ln (&dpk_ldsc[scan])) {
if ((dpk_port[scan] & PORT_INPUT) == 0)
continue;
dpk_ibuf[dpk_iwr++] = (scan << 18) | (tmxr_getc_ln (&dpk_ldsc[scan]) & 0177);
dpk_iwr &= 15;
dpk_status |= DPK_IDONE;
if (dpk_ien)
set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA);
break;
}
}
return SCPE_OK;
}
static t_stat dpk_reset (DEVICE *dptr)
{
sim_debug(DEBUG_CMD, &dpk_dev, "Reset\n");
if (dpk_unit->flags & UNIT_ATT)
sim_activate (dpk_unit, tmxr_poll);
else
sim_cancel (dpk_unit);
dpk_ien = 0;
dpk_base = 0;
dpk_status = 0;
dpk_ird = dpk_iwr = 0;
memset (dpk_port, 0, sizeof dpk_port);
clr_interrupt(DPK_DEVNUM);
return SCPE_OK;
}
static t_stat dpk_attach (UNIT *uptr, CONST char *cptr)
{
t_stat stat;
int i;
stat = tmxr_attach (&dpk_desc, uptr, cptr);
for (i = 0; i < DPK_LINES; i++) {
dpk_ldsc[i].rcve = 0;
dpk_ldsc[i].xmte = 0;
/* Clear txdone so tmxr_txdone_ln will not return return true
on the first call. */
dpk_ldsc[i].txdone = 0;
}
if (stat == SCPE_OK)
sim_activate (uptr, tmxr_poll);
return stat;
}
static t_stat dpk_detach (UNIT *uptr)
{
t_stat stat = tmxr_detach (&dpk_desc, uptr);
int i;
for (i = 0; i < DPK_LINES; i++) {
dpk_ldsc[i].rcve = 0;
dpk_ldsc[i].xmte = 0;
}
dpk_status = 0;
sim_cancel (uptr);
return stat;
}
static t_stat dpk_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr)
{
fprintf (st, "DPK Datapoint kludge terminal multiplexer\n\n");
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
tmxr_attach_help (st, dptr, uptr, flag, cptr);
fprintf (st, "Terminals can be set to one of three modes: 7P, 7B, or 8B.\n\n");
fprintf (st, " mode input characters output characters\n\n");
fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n");
fprintf (st, " non-printing characters suppressed\n");
fprintf (st, " 7B high-order bit cleared high-order bit cleared\n");
fprintf (st, " 8B no changes no changes\n\n");
fprintf (st, "The default mode is 7B.\n\n");
fprintf (st, "Once DPK is attached and the simulator is running, the terminals listen for\n");
fprintf (st, "connections on the specified port. They assume that the incoming connections\n");
fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n");
fprintf (st, "by the Telnet client, a SET DPK DISCONNECT command, or a DETACH DPK command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, " sim> SHOW DPK CONNECTIONS show current connections\n");
fprintf (st, " sim> SHOW DPK STATISTICS show statistics for active connections\n");
fprintf (st, " sim> SET DPKn DISCONNECT disconnects the specified line.\n");
fprint_reg_help (st, &dc_dev);
fprintf (st, "\nThe terminals do not support save and restore. All open connections\n");
fprintf (st, "are lost when the simulator shuts down or DPK is detached.\n");
return SCPE_OK;
}
static const char *dpk_description (DEVICE *dptr)
{
return "Systems Concepts DK-10, Datapoint kludge";
}

118
PDP10/ka10_imx.c Normal file
View file

@ -0,0 +1,118 @@
/* ka10_imx.c: Input multplexor for A/D.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device which has 128 A/D channels. It's specific to the MIT
AI lab PDP-10.
*/
#include <time.h>
#include "kx10_defs.h"
#ifndef NUM_DEVS_IMX
#define NUM_DEVS_IMX 0
#endif
#if (NUM_DEVS_IMX > 0)
#define IMX_DEVNUM 0574
#define IMX_PIA 0000007
#define IMX_DONE 0000010
#define IMX_PACK 0000040
#define IMX_SEQUENCE 0000100
#define IMX_TEST 0000200
#define IMX_RATE 0377000
#define IMX_ASSIGNED 0400000000000LL
#define IMX_CONO (IMX_PIA | IMX_PACK | IMX_SEQUENCE | IMX_RATE)
#define IMX_CONI (IMX_PIA | IMX_DONE | IMX_PACK | IMX_SEQUENCE | IMX_TEST | IMX_ASSIGNED)
#define IMX_CHANNEL 0000177
t_stat imx_devio(uint32 dev, uint64 *data);
const char *imx_description (DEVICE *dptr);
static uint64 status = IMX_ASSIGNED;
static int initial_channel = 0;
static int current_channel = 0;
UNIT imx_unit[] = {
{UDATA(NULL, UNIT_DISABLE, 0)}, /* 0 */
};
DIB imx_dib = {IMX_DEVNUM, 1, &imx_devio, NULL};
MTAB imx_mod[] = {
{ 0 }
};
DEVICE imx_dev = {
"IMX", imx_unit, NULL, imx_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, NULL, NULL, NULL, NULL,
&imx_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL,
NULL, NULL, NULL, NULL, NULL, &imx_description
};
static int imx_sample (void)
{
int sample = 2048;
if (status & IMX_SEQUENCE)
current_channel = (current_channel + 1) & IMX_CHANNEL;
else
current_channel = initial_channel;
return sample;
}
t_stat imx_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &imx_dev;
switch(dev & 07) {
case CONO|4:
status &= ~IMX_CONO;
status |= *data & IMX_CONO;
current_channel = initial_channel;
break;
case CONI|4:
status |= IMX_DONE;
*data = status & IMX_CONI;
break;
case DATAO|4:
initial_channel = *data & IMX_CHANNEL;
break;
case DATAI|4:
*data = imx_sample();
if (status & IMX_PACK) {
*data <<= 24;
*data |= imx_sample() << 12;
*data |= imx_sample();
}
break;
}
return SCPE_OK;
}
const char *imx_description (DEVICE *dptr)
{
return "A/D input multiplexor";
}
#endif

304
PDP10/ka10_mty.c Normal file
View file

@ -0,0 +1,304 @@
/* ka10_tk10.c: MTY, Morton multiplex box: Terminal multiplexor.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device with 32 high-speed terminal lines. It's specific
to the MIT Mathlab and Dynamic Modeling PDP-10s.
*/
#include <time.h>
#include "sim_defs.h"
#include "sim_tmxr.h"
#include "kx10_defs.h"
#ifndef NUM_DEVS_MTY
#define NUM_DEVS_MTY 0
#endif
#if (NUM_DEVS_MTY > 0)
#define MTY_NAME "MTY"
#define MTY_DEVNUM 0400
#define MTY_LINES 32
#define MTY_PIA 0000007 /* PI channel assignment */
#define MTY_RQINT 0000010 /* Request interrupt. */
#define MTY_ODONE 0000010 /* Output done. */
#define MTY_IDONE 0000040 /* Input done. */
#define MTY_STOP 0000200 /* Clear output done. */
#define MTY_LINE 0370000 /* Line number. */
#define MTY_DONE (MTY_IDONE | MTY_ODONE)
#define MTY_CONI_BITS (MTY_PIA | MTY_DONE | MTY_LINE)
#define MTY_CONO_BITS (MTY_PIA | MTY_LINE)
static t_stat mty_devio(uint32 dev, uint64 *data);
static t_stat mty_svc (UNIT *uptr);
static t_stat mty_reset (DEVICE *dptr);
static t_stat mty_attach (UNIT *uptr, CONST char *cptr);
static t_stat mty_detach (UNIT *uptr);
static const char *mty_description (DEVICE *dptr);
static t_stat mty_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr);
extern int32 tmxr_poll;
TMLN mty_ldsc[MTY_LINES] = { 0 };
TMXR mty_desc = { MTY_LINES, 0, 0, mty_ldsc };
static uint64 status = 0;
UNIT mty_unit[] = {
{UDATA(mty_svc, TT_MODE_7B|UNIT_ATTABLE|UNIT_DISABLE, 0)}, /* 0 */
};
DIB mty_dib = {MTY_DEVNUM, 1, &mty_devio, NULL};
MTAB mty_mod[] = {
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &mty_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
NULL, &tmxr_show_summ, (void *) &mty_desc, "Display a summary of line states" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &mty_desc, "Display current connections" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &mty_desc, "Display multiplexer statistics" },
{ 0 }
};
DEVICE mty_dev = {
MTY_NAME, mty_unit, NULL, mty_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, mty_reset, NULL, mty_attach, mty_detach,
&mty_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug,
NULL, NULL, mty_help, NULL, NULL, mty_description
};
static t_stat mty_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &mty_dev;
TMLN *lp;
int line;
uint64 word;
int ch;
switch(dev & 07) {
case CONO:
sim_debug(DEBUG_CONO, &mty_dev, "%06llo\n", *data);
status &= ~MTY_CONO_BITS;
status |= *data & MTY_CONO_BITS;
line = (status & MTY_LINE) >> 12;
if (*data & MTY_STOP) {
status &= ~MTY_ODONE;
/* Set txdone so future calls to tmxr_txdone_ln will
return -1 rather than 1. */
mty_ldsc[line].txdone = 1;
sim_debug(DEBUG_CMD, &mty_dev, "Clear output done line %d\n",
line);
}
if (*data & MTY_RQINT) {
status |= MTY_ODONE;
sim_debug(DEBUG_CMD, &mty_dev, "Request interrupt line %d\n",
line);
}
if ((*data & (MTY_STOP | MTY_RQINT)) == 0)
sim_debug(DEBUG_CMD, &mty_dev, "Select line %d\n",
line);
break;
case CONI:
*data = status & MTY_CONI_BITS;
sim_debug(DEBUG_CONI, &mty_dev, "%06llo\n", *data);
break;
case DATAO:
line = (status & MTY_LINE) >> 12;
word = *data;
sim_debug(DEBUG_DATAIO, &mty_dev, "DATAO line %d -> %012llo\n",
line, *data);
lp = &mty_ldsc[line];
if (!mty_ldsc[line].conn)
/* If the line isn't connected, clear txdone to force
tmxr_txdone_ln to return 1 rather than -1. */
mty_ldsc[line].txdone = 0;
/* Write up to five characters extracted from a word. NUL can
only be in the first character. */
ch = (word >> 29) & 0177;
tmxr_putc_ln (lp, sim_tt_outcvt(ch, TT_GET_MODE (mty_unit[0].flags)));
while ((ch = (word >> 22) & 0177) != 0) {
tmxr_putc_ln (lp, sim_tt_outcvt(ch, TT_GET_MODE (mty_unit[0].flags)));
word <<= 7;
}
status &= ~MTY_ODONE;
break;
case DATAI:
line = (status & MTY_LINE) >> 12;
lp = &mty_ldsc[line];
*data = tmxr_getc_ln (lp) & 0177;
sim_debug(DEBUG_DATAIO, &mty_dev, "DATAI line %d -> %012llo\n",
line, *data);
status &= ~MTY_IDONE;
break;
}
if (status & MTY_DONE)
set_interrupt(MTY_DEVNUM, status & MTY_PIA);
else
clr_interrupt(MTY_DEVNUM);
return SCPE_OK;
}
static t_stat mty_svc (UNIT *uptr)
{
static int scan = 0;
int i;
/* High speed device, poll every 0.1 ms. */
sim_activate_after (uptr, 100);
i = tmxr_poll_conn (&mty_desc);
if (i >= 0) {
mty_ldsc[i].conn = 1;
mty_ldsc[i].rcve = 1;
mty_ldsc[i].xmte = 1;
/* Set txdone so tmxr_txdone_ln will not return return 1 on
the first call after a new connection. */
mty_ldsc[i].txdone = 1;
sim_debug(DEBUG_CMD, &mty_dev, "Connect %d\n", i);
}
tmxr_poll_rx (&mty_desc);
tmxr_poll_tx (&mty_desc);
for (i = 0; i < MTY_LINES; i++) {
/* Round robin scan 32 lines. */
scan = (scan + 1) & 037;
/* 1 means the line became ready since the last check. Ignore
-1 which means "still ready". */
if (tmxr_txdone_ln (&mty_ldsc[scan]) == 1) {
sim_debug(DEBUG_DETAIL, &mty_dev, "Output ready line %d\n", scan);
status &= ~MTY_LINE;
status |= scan << 12;
status |= MTY_ODONE;
set_interrupt(MTY_DEVNUM, status & MTY_PIA);
break;
}
if (!mty_ldsc[scan].conn)
continue;
if (tmxr_input_pending_ln (&mty_ldsc[scan])) {
sim_debug(DEBUG_DETAIL, &mty_dev, "Input ready line %d\n", scan);
status &= ~MTY_LINE;
status |= scan << 12;
status |= MTY_IDONE;
set_interrupt(MTY_DEVNUM, status & MTY_PIA);
break;
}
}
return SCPE_OK;
}
static t_stat mty_reset (DEVICE *dptr)
{
sim_debug(DEBUG_CMD, &mty_dev, "Reset\n");
if (mty_unit->flags & UNIT_ATT)
sim_activate (mty_unit, tmxr_poll);
else
sim_cancel (mty_unit);
status = 0;
clr_interrupt(MTY_DEVNUM);
return SCPE_OK;
}
static t_stat mty_attach (UNIT *uptr, CONST char *cptr)
{
t_stat stat;
int i;
stat = tmxr_attach (&mty_desc, uptr, cptr);
for (i = 0; i < MTY_LINES; i++) {
mty_ldsc[i].rcve = 0;
mty_ldsc[i].xmte = 0;
/* Set txdone so tmxr_txdone_ln will not return return 1 on
the first call. */
mty_ldsc[i].txdone = 1;
}
if (stat == SCPE_OK) {
status = 0;
sim_activate (uptr, tmxr_poll);
}
return stat;
}
static t_stat mty_detach (UNIT *uptr)
{
t_stat stat = tmxr_detach (&mty_desc, uptr);
int i;
for (i = 0; i < MTY_LINES; i++) {
mty_ldsc[i].rcve = 0;
mty_ldsc[i].xmte = 0;
}
status = 0;
sim_cancel (uptr);
return stat;
}
static t_stat mty_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr)
{
fprintf (st, "MTY Morton box terminal multiplexor\n\n");
fprintf (st, "The MTY supported 32 high-speed lines at up to 80 bits/second.\n");
fprintf (st, "this simulation.\n\n");
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
tmxr_attach_help (st, dptr, uptr, flag, cptr);
fprintf (st, "Terminals can be set to one of three modes: 7P, 7B, or 8B.\n\n");
fprintf (st, " mode input characters output characters\n\n");
fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n");
fprintf (st, " non-printing characters suppressed\n");
fprintf (st, " 7B high-order bit cleared high-order bit cleared\n");
fprintf (st, " 8B no changes no changes\n\n");
fprintf (st, "The default mode is 7B.\n\n");
fprintf (st, "Once MTY is attached and the simulator is running, the terminals listen for\n");
fprintf (st, "connections on the specified port. They assume that the incoming connections\n");
fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n");
fprintf (st, "by the Telnet client, a SET MTY DISCONNECT command, or a DETACH MTY command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, " sim> SHOW MTY CONNECTIONS show current connections\n");
fprintf (st, " sim> SHOW MTY STATISTICS show statistics for active connections\n");
fprintf (st, " sim> SET MTYn DISCONNECT disconnects the specified line.\n");
fprint_reg_help (st, &dc_dev);
fprintf (st, "\nThe terminals do not support save and restore. All open connections\n");
fprintf (st, "are lost when the simulator shuts down or MTY is detached.\n");
return SCPE_OK;
}
static const char *mty_description (DEVICE *dptr)
{
return "Morton box: Terminal multiplexor";
}
#endif

127
PDP10/ka10_pd.c Normal file
View file

@ -0,0 +1,127 @@
/* ka10_pd.c: DeCoriolis clock.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device which keeps track of the time and date. An access
will return the number of ticks since the beginning of the year.
There are 60 ticks per second. The device was made by Paul
DeCoriolis at MIT.
When used with a KL10, the clock was part of the KL-UDGE board
which could also provide a 60 Hz interrupt and set console lights.
This is not needed on a KA10, so it's not implemented here.
*/
#include <time.h>
#include "kx10_defs.h"
#ifndef NUM_DEVS_PD
#define NUM_DEVS_PD 0
#endif
#if (NUM_DEVS_PD > 0)
#define PD_DEVNUM 0500
#define PD_OFF (1 << DEV_V_UF)
t_stat pd_devio(uint32 dev, uint64 *data);
const char *pd_description (DEVICE *dptr);
t_stat pd_set_on(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat pd_set_off(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat pd_show_on(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
UNIT pd_unit[] = {
{UDATA(NULL, UNIT_DISABLE, 0)}, /* 0 */
};
DIB pd_dib = {PD_DEVNUM, 1, &pd_devio, NULL};
MTAB pd_mod[] = {
{ MTAB_VDV, 0, "ON", "ON", pd_set_on, pd_show_on },
{ MTAB_VDV, PD_OFF, NULL, "OFF", pd_set_off },
{ 0 }
};
DEVICE pd_dev = {
"PD", pd_unit, NULL, pd_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, NULL, NULL, NULL, NULL,
&pd_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL,
NULL, NULL, NULL, NULL, NULL, &pd_description
};
static uint64 pd_ticks (void)
{
time_t t = time(NULL);
struct tm *x = localtime(&t);
uint64 seconds;
seconds = 86400ULL * x->tm_yday;
seconds += 3600ULL * x->tm_hour;
seconds += 60ULL * x->tm_min;
seconds += x->tm_sec;
// We could add individual ticks here, but there's no pressing need.
return 60ULL * seconds;
}
t_stat pd_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &pd_dev;
switch(dev & 07) {
case DATAI:
if (dptr->flags & PD_OFF)
*data = 0;
else
*data = pd_ticks();
break;
default:
break;
}
return SCPE_OK;
}
const char *pd_description (DEVICE *dptr)
{
return "Paul DeCoriolis clock";
}
t_stat pd_set_on(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr = &pd_dev;
dptr->flags &= ~PD_OFF;
return SCPE_OK;
}
t_stat pd_set_off(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr = &pd_dev;
dptr->flags |= PD_OFF;
return SCPE_OK;
}
t_stat pd_show_on(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
DEVICE *dptr = &pd_dev;
fprintf (st, "%s", (dptr->flags & PD_OFF) ? "off" : "on");
return SCPE_OK;
}
#endif

2436
PDP10/ka10_pmp.c Normal file

File diff suppressed because it is too large Load diff

397
PDP10/ka10_stk.c Normal file
View file

@ -0,0 +1,397 @@
/* ka10_stk.c: Stanford keyboard.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device which interfaces with a Stanford keyboard. It's
specific to the MIT AI lab PDP-10.
*/
#include <time.h>
#include "sim_video.h"
#include "display/display.h"
#include "kx10_defs.h"
#ifdef USE_DISPLAY
#if NUM_DEVS_STK > 0
#define STK_DEVNUM 070
/* CONI/O bits. */
#define STK_PIA 0000007
#define STK_DONE 0000010
/* Bucky bits. */
#define SHFT 00100
#define CTRL 00200
#define TOP 00400
#define META 01000
static t_stat stk_svc (UNIT *uptr);
static t_stat stk_devio(uint32 dev, uint64 *data);
static t_stat stk_reset (DEVICE *dptr);
static const char *stk_description (DEVICE *dptr);
static uint64 status = 0;
static int key_code = 0;
UNIT stk_unit[] = {
{UDATA(stk_svc, UNIT_DISABLE, 0)}, /* 0 */
};
DIB stk_dib = {STK_DEVNUM, 1, &stk_devio, NULL};
MTAB stk_mod[] = {
{ 0 }
};
DEVICE stk_dev = {
"STK", stk_unit, NULL, stk_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, &stk_reset, NULL, NULL, NULL,
&stk_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL,
NULL, NULL, NULL, NULL, NULL, &stk_description
};
/* Special key codes. */
#define CR 033
#define BKSL 034
#define LF 035
#define TAB 045
#define FF 046
#define VT 047
#define BS 074
#define ALT 077 /* Not sure if 42, 75, 76, or 77. */
/* This maps ASCII codes to Stanford key codes plus bucky bits. */
static int translate[] = {
0, CTRL|001,CTRL|002,CTRL|003,CTRL|004,CTRL|005,CTRL|006,CTRL|007,
CTRL|010,TAB, LF, VT, FF, CR, CTRL|016,CTRL|017,
CTRL|020,CTRL|021,CTRL|022,CTRL|023,CTRL|024,CTRL|025,CTRL|026,CTRL|027,
CTRL|030,CTRL|031,CTRL|032,ALT, CTRL|034,CTRL|035,0, CTRL|037,
' ', SHFT|',',TOP|031, TOP|022, SHFT|'6',SHFT|'7',TOP|024, TOP|011,
'(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', TOP|004, TOP|010, TOP|006, TOP|'.',
TOP|005, SHFT|001,SHFT|002,SHFT|003,SHFT|004,SHFT|005,SHFT|006,SHFT|007,
SHFT|010,SHFT|011,SHFT|012,SHFT|013,SHFT|014,SHFT|015,SHFT|016,SHFT|017,
SHFT|020,SHFT|021,SHFT|022,SHFT|023,SHFT|024,SHFT|025,SHFT|026,SHFT|027,
SHFT|030,SHFT|031,SHFT|032,TOP|'(', BKSL, TOP|')', 0, TOP|'9',
TOP|025, 001, 002, 003, 004, 005, 006, 007,
010, 011, 012, 013, 014, 015, 016, 017,
020, 021, 022, 023, 024, 025, 026, 027,
030, 031, 032, TOP|017, SHFT|'+',TOP|020, SHFT|'8',BS
};
static int bucky = 0;
static int stk_modifiers (SIM_KEY_EVENT *kev)
{
if (kev->state == SIM_KEYPRESS_DOWN) {
switch (kev->key) {
case SIM_KEY_SHIFT_L:
case SIM_KEY_SHIFT_R:
bucky |= SHFT;
return 1;
case SIM_KEY_CTRL_L:
case SIM_KEY_CTRL_R:
case SIM_KEY_CAPS_LOCK:
bucky |= CTRL;
return 1;
case SIM_KEY_WIN_L:
case SIM_KEY_WIN_R:
bucky |= TOP;
return 1;
case SIM_KEY_ALT_L:
case SIM_KEY_ALT_R:
bucky |= META;
return 1;
}
} else if (kev->state == SIM_KEYPRESS_UP) {
switch (kev->key) {
case SIM_KEY_SHIFT_L:
case SIM_KEY_SHIFT_R:
bucky &= ~SHFT;
return 1;
case SIM_KEY_CTRL_L:
case SIM_KEY_CTRL_R:
case SIM_KEY_CAPS_LOCK:
bucky &= ~CTRL;
return 1;
case SIM_KEY_WIN_L:
case SIM_KEY_WIN_R:
bucky &= ~TOP;
return 1;
case SIM_KEY_ALT_L:
case SIM_KEY_ALT_R:
bucky &= ~META;
return 1;
}
}
return 0;
}
static int stk_keys (SIM_KEY_EVENT *kev)
{
if (kev->state == SIM_KEYPRESS_UP)
return 0;
switch (kev->key) {
case SIM_KEY_0:
key_code = bucky | '+';
return 1;
case SIM_KEY_1:
key_code = bucky | '1';
return 1;
case SIM_KEY_2:
key_code = bucky | '2';
return 1;
case SIM_KEY_3:
key_code = bucky | '3';
return 1;
case SIM_KEY_4:
key_code = bucky | '4';
return 1;
case SIM_KEY_5:
key_code = bucky | '5';
return 1;
case SIM_KEY_6:
key_code = bucky | '6';
return 1;
case SIM_KEY_7:
key_code = bucky | '7';
return 1;
case SIM_KEY_8:
key_code = bucky | '8';
return 1;
case SIM_KEY_9:
key_code = bucky | '9';
return 1;
case SIM_KEY_A:
key_code = bucky | 001;
return 1;
case SIM_KEY_B:
key_code = bucky | 002;
return 1;
case SIM_KEY_C:
key_code = bucky | 003;
return 1;
case SIM_KEY_D:
key_code = bucky | 004;
return 1;
case SIM_KEY_E:
key_code = bucky | 005;
return 1;
case SIM_KEY_F:
key_code = bucky | 006;
return 1;
case SIM_KEY_G:
key_code = bucky | 007;
return 1;
case SIM_KEY_H:
key_code = bucky | 010;
return 1;
case SIM_KEY_I:
key_code = bucky | 011;
return 1;
case SIM_KEY_J:
key_code = bucky | 012;
return 1;
case SIM_KEY_K:
key_code = bucky | 013;
return 1;
case SIM_KEY_L:
key_code = bucky | 014;
return 1;
case SIM_KEY_M:
key_code = bucky | 015;
return 1;
case SIM_KEY_N:
key_code = bucky | 016;
return 1;
case SIM_KEY_O:
key_code = bucky | 017;
return 1;
case SIM_KEY_P:
key_code = bucky | 020;
return 1;
case SIM_KEY_Q:
key_code = bucky | 021;
return 1;
case SIM_KEY_R:
key_code = bucky | 022;
return 1;
case SIM_KEY_S:
key_code = bucky | 023;
return 1;
case SIM_KEY_T:
key_code = bucky | 024;
return 1;
case SIM_KEY_U:
key_code = bucky | 025;
return 1;
case SIM_KEY_V:
key_code = bucky | 026;
return 1;
case SIM_KEY_W:
key_code = bucky | 027;
return 1;
case SIM_KEY_X:
key_code = bucky | 030;
return 1;
case SIM_KEY_Y:
key_code = bucky | 031;
return 1;
case SIM_KEY_Z:
key_code = bucky | 032;
return 1;
case SIM_KEY_BACKQUOTE:
key_code = bucky | '0';
return 1;
case SIM_KEY_MINUS:
key_code = bucky | '-';
return 1;
case SIM_KEY_EQUALS:
key_code = bucky | '*';
return 1;
case SIM_KEY_LEFT_BRACKET:
key_code = bucky | '(';
return 1;
case SIM_KEY_RIGHT_BRACKET:
key_code = bucky | ')';
return 1;
case SIM_KEY_SEMICOLON:
key_code = bucky | ';';
return 1;
case SIM_KEY_SINGLE_QUOTE:
key_code = bucky | ':';
return 1;
case SIM_KEY_BACKSLASH:
key_code = bucky | BKSL;
return 1;
case SIM_KEY_LEFT_BACKSLASH:
key_code = bucky | BKSL;
return 1;
case SIM_KEY_COMMA:
key_code = bucky | ',';
return 1;
case SIM_KEY_PERIOD:
key_code = bucky | '.';
return 1;
case SIM_KEY_SLASH:
key_code = bucky | '/';
return 1;
case SIM_KEY_ESC:
key_code = bucky | ALT;
return 1;
case SIM_KEY_BACKSPACE:
case SIM_KEY_DELETE:
key_code = bucky | BS;
return 1;
case SIM_KEY_TAB:
key_code = bucky | TAB;
return 1;
case SIM_KEY_ENTER:
key_code = bucky | CR;
return 1;
case SIM_KEY_SPACE:
key_code = bucky | ' ';
return 1;
default:
return 0;
}
}
static int stk_keyboard (SIM_KEY_EVENT *kev)
{
if (stk_modifiers (kev))
return 0;
if (stk_keys (kev)) {
status |= STK_DONE;
set_interrupt(STK_DEVNUM, status & STK_PIA);
return 0;
}
return 1;
}
static t_stat stk_svc (UNIT *uptr)
{
int c = SCPE_OK;
#ifdef USE_DISPLAY
if (display_last_char) {
c = display_last_char | SCPE_KFLAG;
display_last_char = 0;
}
#endif
if (c & SCPE_KFLAG) {
key_code = translate[c & 0177];
status |= STK_DONE;
set_interrupt(STK_DEVNUM, status & STK_PIA);
}
sim_activate (uptr, 100000);
if (c & SCPE_KFLAG)
return SCPE_OK;
else
return c;
}
t_stat stk_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &stk_dev;
switch(dev & 07) {
case CONO:
status &= ~STK_PIA;
status |= *data & STK_PIA;
if (status & STK_PIA)
sim_activate (stk_unit, 1);
else
sim_cancel (stk_unit);
break;
case CONI:
*data = status;
break;
case DATAO:
break;
case DATAI:
status &= ~STK_DONE;
clr_interrupt(STK_DEVNUM);
*data = key_code;
break;
}
return SCPE_OK;
}
static t_stat stk_reset (DEVICE *dptr)
{
vid_display_kb_event_process = stk_keyboard;
return SCPE_OK;
}
const char *stk_description (DEVICE *dptr)
{
return "Stanford keyboard";
}
#endif
#endif

417
PDP10/ka10_ten11.c Normal file
View file

@ -0,0 +1,417 @@
/* ka10_ten11.c: Rubin 10-11 interface.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device which interfaces with eight Unibuses. It's
specific to the MIT AI lab PDP-10.
*/
#include "kx10_defs.h"
#include "sim_tmxr.h"
#ifndef NUM_DEVS_TEN11
#define NUM_DEVS_TEN11 0
#endif
#if (NUM_DEVS_TEN11 > 0)
#include <fcntl.h>
//#include <unistd.h>
#include <sys/types.h>
//#include <sys/socket.h>
//#include <netinet/in.h>
//#include <netinet/tcp.h>
//#include <arpa/inet.h>
/* Rubin 10-11 pager. */
static uint64 ten11_pager[256];
/* Physical address of 10-11 control page. */
#define T11CPA 03776000
/* Bits in a 10-11 page table entry. */
#define T11VALID (0400000000000LL)
#define T11WRITE (0200000000000LL)
#define T11PDP11 (0003400000000LL)
#define T11ADDR (0000377776000LL)
#define T11LIMIT (0000000001777LL)
/* External Unibus interface. */
#define DATO 1
#define DATI 2
#define ACK 3
#define ERR 4
#define TIMEOUT 5
#define TEN11_POLL 100
/* Simulator time units for a Unibus memory cycle. */
#define UNIBUS_MEM_CYCLE 100
static t_stat ten11_svc (UNIT *uptr);
static t_stat ten11_reset (DEVICE *dptr);
static t_stat ten11_attach (UNIT *uptr, CONST char *ptr);
static t_stat ten11_detach (UNIT *uptr);
static t_stat ten11_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
static const char *ten11_description (DEVICE *dptr);
UNIT ten11_unit[1] = {
{ UDATA (&ten11_svc, UNIT_IDLE|UNIT_ATTABLE, 0), 1000 },
};
static REG ten11_reg[] = {
{ DRDATAD (POLL, ten11_unit[0].wait, 24, "poll interval"), PV_LEFT },
{ NULL }
};
static MTAB ten11_mod[] = {
{ 0 }
};
#define DBG_TRC 1
#define DBG_CMD 2
static DEBTAB ten11_debug[] = {
{"TRACE", DBG_TRC, "Routine trace"},
{"CMD", DBG_CMD, "Command Processing"},
{0},
};
DEVICE ten11_dev = {
"TEN11", ten11_unit, ten11_reg, ten11_mod,
1, 8, 16, 2, 8, 16,
NULL, /* examine */
NULL, /* deposit */
&ten11_reset, /* reset */
NULL, /* boot */
ten11_attach, /* attach */
ten11_detach, /* detach */
NULL, /* context */
DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX,
DBG_CMD, /* debug control */
ten11_debug, /* debug flags */
NULL, /* memory size chage */
NULL, /* logical name */
NULL, /* help */
&ten11_attach_help, /* attach help */
NULL, /* help context */
&ten11_description, /* description */
};
static TMLN ten11_ldsc; /* line descriptor */
static TMXR ten11_desc = { 1, 0, 0, &ten11_ldsc }; /* mux descriptor */
static t_stat ten11_reset (DEVICE *dptr)
{
sim_debug(DBG_TRC, dptr, "ten11_reset()\n");
ten11_unit[0].flags |= UNIT_ATTABLE | UNIT_IDLE;
ten11_desc.packet = TRUE;
ten11_desc.notelnet = TRUE;
ten11_desc.buffered = 2048;
if (ten11_unit[0].flags & UNIT_ATT)
sim_activate (&ten11_unit[0], 1000);
else
sim_cancel (&ten11_unit[0]);
return SCPE_OK;
}
static t_stat ten11_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
if (!cptr || !*cptr)
return SCPE_ARG;
if (!(uptr->flags & UNIT_ATTABLE))
return SCPE_NOATT;
r = tmxr_attach_ex (&ten11_desc, uptr, cptr, FALSE);
if (r != SCPE_OK) /* error? */
return r;
sim_debug(DBG_TRC, &ten11_dev, "activate connection\n");
sim_activate (uptr, 10); /* start poll */
uptr->flags |= UNIT_ATT;
return SCPE_OK;
}
static t_stat ten11_detach (UNIT *uptr)
{
t_stat r;
if (!(uptr->flags & UNIT_ATT))
return SCPE_OK;
sim_cancel (uptr);
r = tmxr_detach (&ten11_desc, uptr);
uptr->flags &= ~UNIT_ATT;
free (uptr->filename);
uptr->filename = NULL;
return r;
}
static void build (unsigned char *request, unsigned char octet)
{
request[0]++;
request[request[0]] = octet;
}
static t_stat ten11_svc (UNIT *uptr)
{
tmxr_poll_rx (&ten11_desc);
if (ten11_ldsc.rcve && !ten11_ldsc.conn) {
ten11_ldsc.rcve = 0;
tmxr_reset_ln (&ten11_ldsc);
}
if (tmxr_poll_conn(&ten11_desc) >= 0) {
sim_debug(DBG_CMD, &ten11_dev, "got connection\n");
ten11_ldsc.rcve = 1;
uptr->wait = TEN11_POLL;
}
sim_activate (uptr, uptr->wait);
return SCPE_OK;
}
static t_stat ten11_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
const char helpString[] =
/* The '*'s in the next line represent the standard text width of a help line */
/****************************************************************************/
" The %D device is an implementation of the Rubin PDP-10 to PDP-11 interface\n"
" facility. This allows a PDP 10 system to reach into a PDP-11 simulator\n"
" and modify or access the contents of the PDP-11 memory.\n\n"
" The device must be attached to a receive port, this is done by using the\n"
" ATTACH command to specify the receive port number.\n"
"\n"
"+sim> ATTACH %U port\n"
"\n"
;
return scp_help (st, dptr, uptr, flag, helpString, cptr);
return SCPE_OK;
}
static const char *ten11_description (DEVICE *dptr)
{
return "Rubin PDP-10 to PDP-11 interface";
}
static int error (const char *message)
{
sim_debug (DBG_TRC, &ten11_dev, "%s\r\n", message);
sim_debug (DBG_TRC, &ten11_dev, "CLOSE\r\n");
ten11_ldsc.rcve = 0;
tmxr_reset_ln (&ten11_ldsc);
return -1;
}
static int transaction (unsigned char *request, unsigned char *response)
{
const uint8 *ten11_request;
size_t size;
t_stat stat;
stat = tmxr_put_packet_ln (&ten11_ldsc, request + 1, (size_t)request[0]);
if (stat != SCPE_OK)
return error ("Write error in transaction");
do {
tmxr_poll_rx (&ten11_desc);
stat = tmxr_get_packet_ln (&ten11_ldsc, &ten11_request, &size);
} while (stat != SCPE_OK || size == 0);
if (size > 7)
return error ("Malformed transaction");
memcpy (response, ten11_request, size);
return 0;
}
static int read_word (int addr, int *data)
{
unsigned char request[8];
unsigned char response[8];
sim_interval -= UNIBUS_MEM_CYCLE;
if ((ten11_unit[0].flags & UNIT_ATT) == 0) {
*data = 0;
return 0;
}
memset (request, 0, sizeof request);
build (request, DATI);
build (request, (addr >> 16) & 0377);
build (request, (addr >> 8) & 0377);
build (request, (addr) & 0377);
transaction (request, response);
switch (response[0])
{
case ACK:
*data = response[2];
*data |= response[1] << 8;
sim_debug (DBG_TRC, &ten11_dev, "Read word %06o\n", *data);
break;
case ERR:
fprintf (stderr, "TEN11: Read error %06o\r\n", addr);
*data = 0;
break;
case TIMEOUT:
fprintf (stderr, "TEN11: Read timeout %06o\r\n", addr);
*data = 0;
break;
default:
return error ("Protocol error");
}
return 0;
}
int ten11_read (int addr, uint64 *data)
{
int offset = addr & 01777;
int word1, word2;
if (addr >= T11CPA) {
/* Accessing the control page. */
if (offset >= 0400) {
sim_debug (DBG_TRC, &ten11_dev,
"Control page read NXM: %o @ %o\n",
offset, PC);
return 1;
}
*data = ten11_pager[offset];
} else {
/* Accessing a memory page. */
int page = (addr >> 10) & 0377;
uint64 mapping = ten11_pager[page];
int unibus, uaddr, limit;
limit = mapping & T11LIMIT;
if ((mapping & T11VALID) == 0 || offset > limit) {
sim_debug (DBG_TRC, &ten11_dev,
"(%o) %07o >= 4,,000000 / %llo / %o > %o\n",
page, addr, (mapping & T11VALID), offset, limit);
return 1;
}
unibus = (mapping & T11PDP11) >> 26;
uaddr = ((mapping & T11ADDR) >> 10) + offset;
uaddr <<= 2;
read_word (uaddr, &word1);
read_word (uaddr + 2, &word2);
*data = ((uint64)word1 << 20) | (word2 << 4);
sim_debug (DBG_TRC, &ten11_dev,
"Read: (%o) %06o -> %012llo\n",
unibus, uaddr, *data);
}
return 0;
}
static int write_word (int addr, uint16 data)
{
unsigned char request[8];
unsigned char response[8];
sim_interval -= UNIBUS_MEM_CYCLE;
if ((ten11_unit[0].flags & UNIT_ATT) == 0) {
return 0;
}
memset (request, 0, sizeof request);
build (request, DATO);
build (request, (addr >> 16) & 0377);
build (request, (addr >> 8) & 0377);
build (request, (addr) & 0377);
build (request, (data >> 8) & 0377);
build (request, (data) & 0377);
transaction (request, response);
switch (response[0])
{
case ACK:
break;
case ERR:
fprintf (stderr, "TEN11: Write error %06o\r\n", addr);
break;
case TIMEOUT:
fprintf (stderr, "TEN11: Write timeout %06o\r\n", addr);
break;
default:
return error ("Protocol error");
}
return 0;
}
int ten11_write (int addr, uint64 data)
{
int offset = addr & 01777;
if (addr >= T11CPA) {
/* Accessing the control page. */
if (offset >= 0400) {
sim_debug (DBG_TRC, &ten11_dev,
"Control page write NXM: %o @ %o\n",
offset, PC);
return 1;
}
ten11_pager[offset] = data;
sim_debug (DBG_TRC, &ten11_dev,
"Page %03o: %s %s (%llo) %06llo/%04llo\n",
offset,
(data & T11VALID) ? "V" : "I",
(data & T11WRITE) ? "RW" : "R",
(data & T11PDP11) >> 26,
(data & T11ADDR) >> 10,
(data & T11LIMIT));
} else {
/* Accessing a memory page. */
int page = (addr >> 10) & 0377;
uint64 mapping = ten11_pager[page];
int unibus, uaddr, limit;
limit = mapping & T11LIMIT;
if ((mapping & T11VALID) == 0 || offset > limit) {
sim_debug (DBG_TRC, &ten11_dev,
"(%o) %07o >= 4,,000000 / %llo / %o > %o\n",
page, addr, (mapping & T11VALID), offset, limit);
return 1;
}
unibus = (mapping & T11PDP11) >> 26;
uaddr = ((mapping & T11ADDR) >> 10) + offset;
uaddr <<= 2;
sim_debug (DBG_TRC, &ten11_dev,
"Write: (%o) %06o <- %012llo\n",
unibus, uaddr, data);
if ((data & 010) == 0)
write_word (uaddr, (data >> 20) & 0177777);
if ((data & 004) == 0)
write_word (uaddr + 2, (data >> 4) & 0177777);
}
return 0;
}
#endif

329
PDP10/ka10_tk10.c Normal file
View file

@ -0,0 +1,329 @@
/* ka10_tk10.c: Knight kludge, TTY scanner.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
This is a device with 16 terminal ports. It's specific to the MIT
AI lab and Dynamic Modeling PDP-10s.
*/
#include <time.h>
#include "sim_defs.h"
#include "sim_tmxr.h"
#include "kx10_defs.h"
#ifndef NUM_DEVS_TK10
#define NUM_DEVS_TK10 0
#endif
#if (NUM_DEVS_TK10 > 0)
#define TK10_NAME "TK"
#define TK10_DEVNUM 0600 /* Also known as NTY. */
#define TK10_LINES 16
#define TK10_PIA 0000007 /* PI channel assignment */
#define TK10_RQINT 0000010 /* Request interrupt. */
#define TK10_ODONE 0000020 /* Done flag on typeout. */
#define TK10_STOP 0000020 /* Stop interrupting. */
#define TK10_IDONE 0000040 /* Done flag on input. */
#define TK10_TYI 0007400 /* Input TTY. */
#define TK10_TYO 0170000 /* Output TTY. */
#define TK10_INT 0200000 /* Interrupt. */
#define TK10_CLEAR 0200000 /* Clear interrupt. */
#define TK10_SELECT 0400000 /* Select line. */
#define TK10_GO 0 /* 0400000 Scanning. */
#define TK10_CONI_BITS (TK10_PIA | TK10_INT | TK10_TYI | TK10_GO | \
TK10_ODONE | TK10_IDONE)
static t_stat tk10_devio(uint32 dev, uint64 *data);
static t_stat tk10_svc (UNIT *uptr);
static t_stat tk10_reset (DEVICE *dptr);
static t_stat tk10_attach (UNIT *uptr, CONST char *cptr);
static t_stat tk10_detach (UNIT *uptr);
static const char *tk10_description (DEVICE *dptr);
static t_stat tk10_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr);
extern int32 tmxr_poll;
TMLN tk10_ldsc[TK10_LINES] = { 0 };
TMXR tk10_desc = { TK10_LINES, 0, 0, tk10_ldsc };
static uint64 status = 0;
UNIT tk10_unit[] = {
{UDATA(tk10_svc, TT_MODE_7B|UNIT_ATTABLE|UNIT_DISABLE, 0)}, /* 0 */
};
DIB tk10_dib = {TK10_DEVNUM, 1, &tk10_devio, NULL};
MTAB tk10_mod[] = {
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" },
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &tk10_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
NULL, &tmxr_show_summ, (void *) &tk10_desc, "Display a summary of line states" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &tk10_desc, "Display current connections" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &tk10_desc, "Display multiplexer statistics" },
{ 0 }
};
DEVICE tk10_dev = {
TK10_NAME, tk10_unit, NULL, tk10_mod,
1, 8, 0, 1, 8, 36,
NULL, NULL, tk10_reset, NULL, tk10_attach, tk10_detach,
&tk10_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug,
NULL, NULL, tk10_help, NULL, NULL, tk10_description
};
static t_stat tk10_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &tk10_dev;
TMLN *lp;
int port;
int ch;
switch(dev & 07) {
case CONO:
sim_debug(DEBUG_CONO, &tk10_dev, "%06llo\n", *data);
if (*data & TK10_CLEAR) {
status &= ~TK10_INT;
status |= TK10_GO;
sim_debug(DEBUG_CMD, &tk10_dev, "Clear interrupt\n");
}
if (*data & TK10_STOP) {
status &= ~TK10_ODONE;
if (!(status & TK10_IDONE))
status &= ~TK10_INT;
/* Set txdone so future calls to tmxr_txdone_ln will
return -1 rather than 1. */
tk10_ldsc[(status & TK10_TYO) >> 12].txdone = 1;
sim_debug(DEBUG_CMD, &tk10_dev, "Clear output done port %lld\n",
(status & TK10_TYO) >> 12);
}
if (*data & TK10_RQINT) {
status &= ~TK10_TYI;
status |= ((status & TK10_TYO) >> 4) | TK10_ODONE | TK10_INT;
sim_debug(DEBUG_CMD, &tk10_dev, "Request interrupt port %lld\n",
(status & TK10_TYO) >> 12);
}
if (*data & TK10_SELECT) {
status &= ~TK10_TYO;
status |= ((*data) & TK10_TYO);
sim_debug(DEBUG_DETAIL, &tk10_dev, "Select port %lld\n",
(status & TK10_TYO) >> 12);
}
status &= ~TK10_PIA;
status |= *data & TK10_PIA;
break;
case CONI:
*data = status & TK10_CONI_BITS;
sim_debug(DEBUG_CONI, &tk10_dev, "%06llo\n", *data);
break;
case DATAO:
port = (status & TK10_TYO) >> 12;
sim_debug(DEBUG_DATAIO, &tk10_dev, "DATAO port %d -> %012llo\n",
port, *data);
lp = &tk10_ldsc[port];
ch = sim_tt_outcvt(*data & 0377, TT_GET_MODE (tk10_unit[0].flags));
if (!tk10_ldsc[port].conn)
/* If the port isn't connected, clear txdone to force
tmxr_txdone_ln to return 1 rather than -1. */
tk10_ldsc[port].txdone = 0;
tmxr_putc_ln (lp, ch);
status &= ~TK10_ODONE;
if (!(status & TK10_IDONE)) {
status &= ~TK10_INT;
status |= TK10_GO;
}
break;
case DATAI:
port = (status & TK10_TYO) >> 12;
lp = &tk10_ldsc[port];
*data = tmxr_getc_ln (lp);
sim_debug(DEBUG_DATAIO, &tk10_dev, "DATAI port %d -> %012llo\n",
port, *data);
status &= ~TK10_IDONE;
if (!(status & TK10_ODONE)) {
status &= ~TK10_INT;
status |= TK10_GO;
}
break;
}
if (status & TK10_INT)
set_interrupt(TK10_DEVNUM, status & TK10_PIA);
else
clr_interrupt(TK10_DEVNUM);
return SCPE_OK;
}
static t_stat tk10_svc (UNIT *uptr)
{
static int scan = 0;
int i;
/* Slow hardware only supported 300 baud teletypes. */
sim_activate_after (uptr, 2083);
i = tmxr_poll_conn (&tk10_desc);
if (i >= 0) {
tk10_ldsc[i].conn = 1;
tk10_ldsc[i].rcve = 1;
tk10_ldsc[i].xmte = 1;
/* Set txdone so tmxr_txdone_ln will not return return 1 on
the first call after a new connection. */
tk10_ldsc[i].txdone = 1;
sim_debug(DEBUG_CMD, &tk10_dev, "Connect %d\n", i);
}
#if 0
/* The GO bit is not yet properly modeled. */
if (!(status & TK10_GO))
return SCPE_OK;
#endif
tmxr_poll_rx (&tk10_desc);
tmxr_poll_tx (&tk10_desc);
for (i = 0; i < TK10_LINES; i++) {
/* Round robin scan 16 lines. */
scan = (scan + 1) & 017;
/* 1 means the line became ready since the last check. Ignore
-1 which means "still ready". */
if (tmxr_txdone_ln (&tk10_ldsc[scan]) == 1) {
sim_debug(DEBUG_DETAIL, &tk10_dev, "Output ready port %d\n", scan);
status &= ~TK10_TYI;
status |= scan << 8;
status |= TK10_INT;
status &= ~TK10_GO;
status |= TK10_ODONE;
set_interrupt(TK10_DEVNUM, status & TK10_PIA);
break;
}
if (!tk10_ldsc[scan].conn)
continue;
if (tmxr_input_pending_ln (&tk10_ldsc[scan])) {
sim_debug(DEBUG_DETAIL, &tk10_dev, "Input ready port %d\n", scan);
status &= ~TK10_TYI;
status |= scan << 8;
status |= TK10_INT;
status &= ~TK10_GO;
status |= TK10_IDONE;
set_interrupt(TK10_DEVNUM, status & TK10_PIA);
break;
}
}
return SCPE_OK;
}
static t_stat tk10_reset (DEVICE *dptr)
{
sim_debug(DEBUG_CMD, &tk10_dev, "Reset\n");
if (tk10_unit->flags & UNIT_ATT)
sim_activate (tk10_unit, tmxr_poll);
else
sim_cancel (tk10_unit);
status = 0;
clr_interrupt(TK10_DEVNUM);
return SCPE_OK;
}
static t_stat tk10_attach (UNIT *uptr, CONST char *cptr)
{
t_stat stat;
int i;
stat = tmxr_attach (&tk10_desc, uptr, cptr);
for (i = 0; i < TK10_LINES; i++) {
tk10_ldsc[i].rcve = 0;
tk10_ldsc[i].xmte = 0;
/* Set txdone so tmxr_txdone_ln will not return return 1 on
the first call. */
tk10_ldsc[i].txdone = 1;
}
if (stat == SCPE_OK) {
status = TK10_GO;
sim_activate (uptr, tmxr_poll);
}
return stat;
}
static t_stat tk10_detach (UNIT *uptr)
{
t_stat stat = tmxr_detach (&tk10_desc, uptr);
int i;
for (i = 0; i < TK10_LINES; i++) {
tk10_ldsc[i].rcve = 0;
tk10_ldsc[i].xmte = 0;
}
status = 0;
sim_cancel (uptr);
return stat;
}
static t_stat tk10_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr)
{
fprintf (st, "TK10 Knight kludge TTY scanner\n\n");
fprintf (st, "The TK10 supported 8 or 16 lines, but only the latter is supported by\n");
fprintf (st, "this simulation.\n\n");
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
tmxr_attach_help (st, dptr, uptr, flag, cptr);
fprintf (st, "Terminals can be set to one of three modes: 7P, 7B, or 8B.\n\n");
fprintf (st, " mode input characters output characters\n\n");
fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n");
fprintf (st, " non-printing characters suppressed\n");
fprintf (st, " 7B high-order bit cleared high-order bit cleared\n");
fprintf (st, " 8B no changes no changes\n\n");
fprintf (st, "The default mode is 7B.\n\n");
fprintf (st, "Once TK10 is attached and the simulator is running, the terminals listen for\n");
fprintf (st, "connections on the specified port. They assume that the incoming connections\n");
fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n");
fprintf (st, "by the Telnet client, a SET TK10 DISCONNECT command, or a DETACH TK10 command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, " sim> SHOW TK10 CONNECTIONS show current connections\n");
fprintf (st, " sim> SHOW TK10 STATISTICS show statistics for active connections\n");
fprintf (st, " sim> SET TK10n DISCONNECT disconnects the specified line.\n");
fprint_reg_help (st, &dc_dev);
fprintf (st, "\nThe terminals do not support save and restore. All open connections\n");
fprintf (st, "are lost when the simulator shuts down or TK10 is detached.\n");
return SCPE_OK;
}
static const char *tk10_description (DEVICE *dptr)
{
return "Knight kludge: TTY scanner";
}
#endif

260
PDP10/kx10_cp.c Normal file
View file

@ -0,0 +1,260 @@
/* ka10_cp.c: PDP10 Card Punch
Copyright (c) 2016-2017, Richard Cornwell
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
RICHARD CORNWELL 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.
This is the standard card punch.
These units each buffer one record in local memory and signal
ready when the buffer is full or empty. The channel must be
ready to recieve/transmit data when they are activated since
they will transfer their block during chan_cmd. All data is
transmitted as BCD characters.
*/
#include "kx10_defs.h"
#include "sim_card.h"
#include "sim_defs.h"
#if (NUM_DEVS_CP > 0)
#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_029
#define CP_DEVNUM 0110
/* std devices. data structures
cp_dev Card Punch device descriptor
cp_unit Card Punch unit descriptor
cp_reg Card Punch register list
cp_mod Card Punch modifiers list
*/
/* CONO Bits */
#define SET_DATA_REQ 0000010
#define CLR_DATA_REQ 0000020
#define SET_PUNCH_ON 0000040
#define CLR_END_CARD 0000100
#define EN_END_CARD 0000200
#define DIS_END_CARD 0000400
#define CLR_ERROR 0001000
#define EN_TROUBLE 0002000
#define DIS_TROUBLE 0004000
#define EJECT 0010000 /* Finish punch and eject */
#define OFFSET_CARD 0040000 /* Offset card stack */
#define CLR_PUNCH 0100000 /* Clear Trouble, Error, End */
/* CONI Bits */
#define PIA 0000007
#define DATA_REQ 0000010
#define PUNCH_ON 0000040
#define END_CARD 0000100 /* Eject or column 80 */
#define END_CARD_EN 0000200
#define CARD_IN_PUNCH 0000400 /* Card ready to punch */
#define ERROR 0001000 /* Punch error */
#define TROUBLE_EN 0002000
#define TROUBLE 0004000 /* Bit 18,22,23, or 21 */
#define EJECT_FAIL 0010000 /* Could not eject card 23 */
#define PICK_FAIL 0020000 /* Could not pick up card 22 */
#define NEED_OPR 0040000 /* Hopper empty, chip full 21 */
#define HOPPER_LOW 0100000 /* less 200 cards 20 */
#define TEST 0400000 /* In test mode 18 */
#define STATUS u3
#define COL u4
t_stat cp_devio(uint32 dev, uint64 *data);
t_stat cp_srv(UNIT *);
t_stat cp_reset(DEVICE *);
t_stat cp_attach(UNIT *, CONST char *);
t_stat cp_detach(UNIT *);
t_stat cp_help(FILE *, DEVICE *, UNIT *, int32, const char *);
const char *cp_description(DEVICE *dptr);
uint16 cp_buffer[80];
DIB cp_dib = { CP_DEVNUM, 1, cp_devio, NULL};
UNIT cp_unit = {UDATA(cp_srv, UNIT_CDP, 0), 600 };
MTAB cp_mod[] = {
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT",
&sim_card_set_fmt, &sim_card_show_fmt, NULL},
{0}
};
REG cp_reg[] = {
{BRDATA(BUFF, cp_buffer, 16, 16, sizeof(cp_buffer)), REG_HRO},
{0}
};
DEVICE cp_dev = {
"CP", &cp_unit, cp_reg, cp_mod,
NUM_DEVS_CP, 8, 15, 1, 8, 8,
NULL, NULL, NULL, NULL, &cp_attach, &cp_detach,
&cp_dib, DEV_DISABLE | DEV_DEBUG | DEV_CARD, 0, crd_debug,
NULL, NULL, &cp_help, NULL, NULL, &cp_description
};
/* Card punch routine
*/
t_stat cp_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &cp_unit;
switch(dev & 3) {
case CONI:
*data = uptr->STATUS;
sim_debug(DEBUG_CONI, &cp_dev, "CP: CONI %012llo\n", *data);
break;
case CONO:
clr_interrupt(dev);
sim_debug(DEBUG_CONO, &cp_dev, "CP: CONO %012llo\n", *data);
uptr->STATUS &= ~PIA;
uptr->STATUS |= *data & PIA;
if (*data & CLR_PUNCH) {
uptr->STATUS &= ~(TROUBLE|ERROR|END_CARD|END_CARD_EN|TROUBLE_EN);
break;
}
if (*data & SET_DATA_REQ) {
uptr->STATUS |= DATA_REQ;
set_interrupt(dev, uptr->STATUS);
}
if (*data & CLR_DATA_REQ)
uptr->STATUS &= ~DATA_REQ;
if (*data & CLR_END_CARD)
uptr->STATUS &= ~END_CARD;
if (*data & EN_END_CARD)
uptr->STATUS |= END_CARD_EN;
if (*data & DIS_END_CARD)
uptr->STATUS &= ~END_CARD_EN;
if (*data & EN_TROUBLE)
uptr->STATUS |= TROUBLE_EN;
if (*data & DIS_TROUBLE)
uptr->STATUS &= ~TROUBLE_EN;
if (*data & EJECT && uptr->STATUS & CARD_IN_PUNCH) {
uptr->COL = 80;
uptr->STATUS &= ~DATA_REQ;
sim_activate(uptr, uptr->wait);
}
if ((uptr->STATUS & (TROUBLE|TROUBLE_EN)) == (TROUBLE|TROUBLE_EN))
set_interrupt(CP_DEVNUM, uptr->STATUS);
if ((uptr->STATUS & (END_CARD|END_CARD_EN)) == (END_CARD|END_CARD_EN))
set_interrupt(CP_DEVNUM, uptr->STATUS);
if (*data & PUNCH_ON) {
uptr->STATUS |= PUNCH_ON;
sim_activate(uptr, uptr->wait);
}
break;
case DATAI:
*data = 0;
break;
case DATAO:
cp_buffer[uptr->COL++] = *data & 0xfff;
uptr->STATUS &= ~DATA_REQ;
clr_interrupt(dev);
sim_debug(DEBUG_DATAIO, &cp_dev, "CP: DATAO %012llo %d\n", *data,
uptr->COL);
sim_activate(uptr, uptr->wait);
break;
}
return SCPE_OK;
}
/* Handle transfer of data for card punch */
t_stat
cp_srv(UNIT *uptr) {
if (uptr->STATUS & PUNCH_ON) {
uptr->STATUS |= CARD_IN_PUNCH;
if (uptr->STATUS & DATA_REQ) {
sim_activate(uptr, uptr->wait);
return SCPE_OK;
}
if (uptr->COL < 80) {
if ((uptr->STATUS & DATA_REQ) == 0) {
uptr->STATUS |= DATA_REQ;
set_interrupt(CP_DEVNUM, uptr->STATUS);
}
sim_activate(uptr, uptr->wait);
return SCPE_OK;
}
uptr->COL = 0;
uptr->STATUS &= ~(PUNCH_ON|CARD_IN_PUNCH);
uptr->STATUS |= END_CARD;
switch(sim_punch_card(uptr, cp_buffer)) {
case CDSE_EOF:
case CDSE_EMPTY:
uptr->STATUS |= PICK_FAIL|TROUBLE;
break;
/* If we get here, something is wrong */
case CDSE_ERROR:
uptr->STATUS |= EJECT_FAIL|TROUBLE;
break;
case CDSE_OK:
break;
}
if ((uptr->STATUS & (TROUBLE|TROUBLE_EN)) == (TROUBLE|TROUBLE_EN))
set_interrupt(CP_DEVNUM, uptr->STATUS);
if (uptr->STATUS & END_CARD_EN)
set_interrupt(CP_DEVNUM, uptr->STATUS);
}
return SCPE_OK;
}
t_stat
cp_attach(UNIT * uptr, CONST char *file)
{
return sim_card_attach(uptr, file);
}
t_stat
cp_detach(UNIT * uptr)
{
if (uptr->STATUS & CARD_IN_PUNCH)
sim_punch_card(uptr, cp_buffer);
return sim_card_detach(uptr);
}
t_stat
cp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Card Punch\n\n");
sim_card_attach_help(st, dptr, uptr, flag, cptr);
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
return SCPE_OK;
}
const char *
cp_description(DEVICE *dptr)
{
return "Card Punch";
}
#endif

6353
PDP10/kx10_cpu.c Normal file

File diff suppressed because it is too large Load diff

305
PDP10/kx10_cr.c Normal file
View file

@ -0,0 +1,305 @@
/* ka10_cr.c: PDP10 Card reader.
Copyright (c) 2016-2017, Richard Cornwell
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
RICHARD CORNWELL 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.
This is the standard card reader.
These units each buffer one record in local memory and signal
ready when the buffer is full or empty. The channel must be
ready to recieve/transmit data when they are activated since
they will transfer their block during chan_cmd. All data is
transmitted as BCD characters.
*/
#include "kx10_defs.h"
#include "sim_card.h"
#include "sim_defs.h"
#if (NUM_DEVS_CR > 0)
#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | MODE_029
#define CR_DEVNUM 0150
/* std devices. data structures
cr_dev Card Reader device descriptor
cr_unit Card Reader unit descriptor
cr_reg Card Reader register list
cr_mod Card Reader modifiers list
*/
/* CONO Bits */
#define PIA 0000007
#define CLR_DRDY 0000010 /* Clear data ready */
#define CLR_END_CARD 0000020 /* Clear end of card */
#define CLR_EOF 0000040 /* Clear end of File Flag */
#define EN_READY 0000100 /* Enable ready irq */
#define CLR_DATA_MISS 0000200 /* Clear data miss */
#define EN_TROUBLE 0000400 /* Enable trouble IRQ */
#define READ_CARD 0001000 /* Read card */
#define OFFSET_CARD 0004000
#define CLR_READER 0010000 /* Clear reader */
/* CONI Bits */
#define DATA_RDY 00000010 /* Data ready */
#define END_CARD 00000020 /* End of card */
#define END_FILE 00000040 /* End of file */
#define RDY_READ 00000100 /* Ready to read */
#define DATA_MISS 00000200 /* Data missed */
#define TROUBLE 00000400 /* Trouble */
#define READING 00001000 /* Reading card */
#define HOPPER_EMPTY 00002000
#define CARD_IN_READ 00004000 /* Card in reader */
#define STOP 00010000
#define MOTION_ERROR 00020000
#define CELL_ERROR 00040000
#define PICK_ERROR 00100000
#define RDY_READ_EN 00200000
#define TROUBLE_EN 00400000
#define STATUS u3
#define COL u4
#define DATA u5
#define CARD_RDY(u) (sim_card_input_hopper_count(u) > 0 || \
sim_card_eof(u) == 1)
t_stat cr_devio(uint32 dev, uint64 *data);
t_stat cr_srv(UNIT *);
t_stat cr_reset(DEVICE *);
t_stat cr_attach(UNIT *, CONST char *);
t_stat cr_detach(UNIT *);
t_stat cr_help(FILE *, DEVICE *, UNIT *, int32, const char *);
const char *cr_description(DEVICE *dptr);
uint16 cr_buffer[80];
DIB cr_dib = { CR_DEVNUM, 1, cr_devio, NULL};
UNIT cr_unit = {
UDATA(cr_srv, UNIT_CDR, 0), 300,
};
MTAB cr_mod[] = {
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT",
&sim_card_set_fmt, &sim_card_show_fmt, NULL},
{0}
};
REG cr_reg[] = {
{BRDATA(BUFF, cr_buffer, 16, 16, sizeof(cr_buffer)), REG_HRO},
{0}
};
DEVICE cr_dev = {
"CR", &cr_unit, cr_reg, cr_mod,
NUM_DEVS_CR, 8, 15, 1, 8, 8,
NULL, NULL, NULL, NULL, &cr_attach, &cr_detach,
&cr_dib, DEV_DISABLE | DEV_DEBUG | DEV_CARD, 0, crd_debug,
NULL, NULL, &cr_help, NULL, NULL, &cr_description
};
/*
* Device entry points for card reader.
*/
t_stat cr_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &cr_unit;
switch(dev & 3) {
case CONI:
if (uptr->flags & UNIT_ATT &&
(uptr->STATUS & (TROUBLE|READING|CARD_IN_READ|END_CARD)) == 0 &&
CARD_RDY(uptr)) {
uptr->STATUS |= RDY_READ;
}
*data = uptr->STATUS;
sim_debug(DEBUG_CONI, &cr_dev, "CR: CONI %012llo\n", *data);
break;
case CONO:
clr_interrupt(dev);
sim_debug(DEBUG_CONO, &cr_dev, "CR: CONO %012llo\n", *data);
if (*data & CLR_READER) {
uptr->STATUS = 0;
if (!CARD_RDY(uptr))
uptr->STATUS |= END_FILE;
sim_cancel(uptr);
break;
}
uptr->STATUS &= ~(PIA);
uptr->STATUS |= *data & PIA;
uptr->STATUS &= ~(*data & (CLR_DRDY|CLR_END_CARD|CLR_EOF|CLR_DATA_MISS));
if (*data & EN_TROUBLE)
uptr->STATUS |= TROUBLE_EN;
if (*data & EN_READY)
uptr->STATUS |= RDY_READ_EN;
if (uptr->flags & UNIT_ATT && *data & READ_CARD) {
uptr->STATUS |= READING;
uptr->STATUS &= ~(CARD_IN_READ|RDY_READ|DATA_RDY);
uptr->COL = 0;
sim_activate(uptr, uptr->wait);
break;
}
if (CARD_RDY(uptr))
uptr->STATUS |= RDY_READ;
else
uptr->STATUS |= STOP;
if (uptr->STATUS & RDY_READ_EN && uptr->STATUS & RDY_READ)
set_interrupt(dev, uptr->STATUS);
if (uptr->STATUS & TROUBLE_EN && uptr->STATUS & TROUBLE)
set_interrupt(dev, uptr->STATUS);
break;
case DATAI:
clr_interrupt(dev);
if (uptr->STATUS & DATA_RDY) {
*data = uptr->DATA;
sim_debug(DEBUG_DATAIO, &cr_dev, "CR: DATAI %012llo\n", *data);
uptr->STATUS &= ~DATA_RDY;
} else
*data = 0;
break;
case DATAO:
break;
}
return SCPE_OK;
}
/* Handle transfer of data for card reader */
t_stat
cr_srv(UNIT *uptr) {
/* Read in card, ready to read next one set IRQ */
if (uptr->flags & UNIT_ATT /*&& uptr->STATUS & END_CARD*/) {
if ((uptr->STATUS & (READING|CARD_IN_READ|RDY_READ)) == 0 &&
(CARD_RDY(uptr))) {
sim_debug(DEBUG_EXP, &cr_dev, "CR: card ready %d\n",
sim_card_input_hopper_count(uptr));
uptr->STATUS |= RDY_READ; /* INdicate ready to start reading */
if (uptr->STATUS & RDY_READ_EN)
set_interrupt(CR_DEVNUM, uptr->STATUS);
return SCPE_OK;
}
}
/* Check if new card requested. */
if ((uptr->STATUS & (READING|CARD_IN_READ)) == READING) {
uptr->STATUS &= ~(END_CARD|RDY_READ);
switch(sim_read_card(uptr, cr_buffer)) {
case CDSE_EOF:
sim_debug(DEBUG_EXP, &cr_dev, "CR: card eof\n");
uptr->STATUS &= ~(CARD_IN_READ|READING);
uptr->STATUS |= END_FILE;
if (sim_card_input_hopper_count(uptr) != 0)
sim_activate(uptr, uptr->wait);
set_interrupt(CR_DEVNUM, uptr->STATUS);
return SCPE_OK;
case CDSE_EMPTY:
sim_debug(DEBUG_EXP, &cr_dev, "CR: card empty\n");
uptr->STATUS &= ~(CARD_IN_READ|READING);
uptr->STATUS |= HOPPER_EMPTY|TROUBLE|STOP;
if (uptr->STATUS & TROUBLE_EN)
set_interrupt(CR_DEVNUM, uptr->STATUS);
return SCPE_OK;
case CDSE_ERROR:
sim_debug(DEBUG_EXP, &cr_dev, "CR: card error\n");
uptr->STATUS &= ~(CARD_IN_READ|READING);
uptr->STATUS |= TROUBLE|PICK_ERROR|STOP;
if (uptr->STATUS & TROUBLE_EN)
set_interrupt(CR_DEVNUM, uptr->STATUS);
return SCPE_OK;
case CDSE_OK:
sim_debug(DEBUG_EXP, &cr_dev, "CR: card ok\n");
uptr->STATUS |= CARD_IN_READ;
break;
}
uptr->COL = 0;
sim_activate(uptr, uptr->wait);
return SCPE_OK;
}
/* Copy next column over */
if (uptr->STATUS & CARD_IN_READ) {
if (uptr->COL >= 80) {
uptr->STATUS &= ~(CARD_IN_READ|READING);
uptr->STATUS |= END_CARD;
set_interrupt(CR_DEVNUM, uptr->STATUS);
sim_activate(uptr, uptr->wait);
return SCPE_OK;
}
uptr->DATA = cr_buffer[uptr->COL++];
if (uptr->STATUS & DATA_RDY) {
uptr->STATUS |= DATA_MISS;
}
uptr->STATUS |= DATA_RDY;
sim_debug(DEBUG_DATA, &cr_dev, "CR Char > %d %03x\n", uptr->COL, uptr->DATA);
set_interrupt(CR_DEVNUM, uptr->STATUS);
sim_activate(uptr, uptr->wait);
}
return SCPE_OK;
}
t_stat
cr_attach(UNIT * uptr, CONST char *file)
{
t_stat r;
if ((r = sim_card_attach(uptr, file)) != SCPE_OK)
return r;
if ((uptr->STATUS & (READING|CARD_IN_READ)) == 0) {
uptr->STATUS |= RDY_READ;
uptr->STATUS &= ~(HOPPER_EMPTY|STOP|TROUBLE|CELL_ERROR|PICK_ERROR);
if (uptr->STATUS & RDY_READ_EN)
set_interrupt(CR_DEVNUM, uptr->STATUS);
}
return SCPE_OK;
}
t_stat
cr_detach(UNIT * uptr)
{
if (uptr->flags & UNIT_ATT) {
uptr->STATUS |= TROUBLE|HOPPER_EMPTY;
if (uptr->STATUS & TROUBLE_EN)
set_interrupt(CR_DEVNUM, uptr->STATUS);
}
uptr->STATUS &= ~ RDY_READ;
return sim_card_detach(uptr);
}
t_stat
cr_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Card Reader\n\n");
sim_card_attach_help(st, dptr, uptr, flag, cptr);
fprint_set_help(st, dptr);
fprint_show_help(st, dptr);
return SCPE_OK;
}
const char *
cr_description(DEVICE *dptr)
{
return "Card Reader";
}
#endif

219
PDP10/kx10_cty.c Normal file
View file

@ -0,0 +1,219 @@
/* ka10_cty.c: KA-10 front end (console terminal) simulator
Copyright (c) 2013-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell
*/
#include "kx10_defs.h"
#if PDP6 | KA | KI
#define UNIT_DUMMY (1 << UNIT_V_UF)
extern int32 tmxr_poll;
t_stat ctyi_svc (UNIT *uptr);
t_stat ctyo_svc (UNIT *uptr);
t_stat cty_reset (DEVICE *dptr);
t_stat cty_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *cty_description (DEVICE *dptr);
/* CTY data structures
cty_dev CTY device descriptor
cty_unit CTY unit descriptor
cty_reg CTY register list
*/
#define TEL_RDY 0010
#define TEL_BSY 0020
#define KEY_RDY 0040
#define KEY_BSY 0100
#define KEY_TST 04000
#define CTY_DEVNUM 0120
#define STATUS u3
#define DATA u4
#define PIA u5
t_stat cty_devio(uint32 dev, uint64 *data);
DIB cty_dib = { CTY_DEVNUM, 1, cty_devio, NULL};
UNIT cty_unit[] = {
{ UDATA (&ctyo_svc, TT_MODE_7B, 0), 10000 },
{ UDATA (&ctyi_svc, TT_MODE_7B|UNIT_IDLE, 0), 0 },
};
MTAB cty_mod[] = {
{ UNIT_DUMMY, 0, NULL, "STOP", &cty_stop_os },
{ TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode },
{ TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode },
{ TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode },
{ TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode },
{ 0 }
};
DEVICE cty_dev = {
"CTY", cty_unit, NULL, cty_mod,
2, 10, 31, 1, 8, 8,
NULL, NULL, &cty_reset,
NULL, NULL, NULL, &cty_dib, DEV_DEBUG, 0, dev_debug,
NULL, NULL, &cty_help, NULL, NULL, &cty_description
};
t_stat cty_devio(uint32 dev, uint64 *data) {
uint64 res;
switch(dev & 3) {
case CONI:
res = cty_unit[0].PIA | (cty_unit[0].STATUS & (TEL_RDY | TEL_BSY));
res |= cty_unit[1].STATUS & (KEY_RDY | KEY_BSY);
res |= cty_unit[0].STATUS & KEY_TST;
*data = res;
sim_debug(DEBUG_CONI, &cty_dev, "CTY %03o CONI %06o\n", dev, (uint32)*data);
break;
case CONO:
res = *data;
cty_unit[0].PIA = res & 07;
cty_unit[1].PIA = res & 07;
cty_unit[0].PIA &= ~(KEY_TST);
cty_unit[0].STATUS &= ~((res >> 4) & (TEL_RDY | TEL_BSY));
cty_unit[0].STATUS |= (res & (TEL_RDY | TEL_BSY | KEY_TST));
cty_unit[1].STATUS &= ~((res >> 4) & (KEY_RDY | KEY_BSY));
cty_unit[1].STATUS |= (res & (KEY_RDY | KEY_BSY));
if ((cty_unit[0].STATUS & TEL_RDY) || (cty_unit[1].STATUS & KEY_RDY))
set_interrupt(dev, cty_unit[0].PIA);
else
clr_interrupt(dev);
sim_debug(DEBUG_CONO, &cty_dev, "CTY %03o CONO %06o\n", dev, (uint32)*data);
break;
case DATAI:
res = cty_unit[1].DATA & 0xff;
cty_unit[1].STATUS &= ~KEY_RDY;
if ((cty_unit[0].STATUS & TEL_RDY) == 0)
clr_interrupt(dev);
*data = res;
sim_debug(DEBUG_DATAIO, &cty_dev, "CTY %03o DATAI %06o\n", dev, (uint32)*data);
break;
case DATAO:
cty_unit[0].DATA = *data & 0x7f;
cty_unit[0].STATUS &= ~TEL_RDY;
cty_unit[0].STATUS |= TEL_BSY;
if ((cty_unit[1].STATUS & KEY_RDY) == 0)
clr_interrupt(dev);
sim_activate(&cty_unit[0], cty_unit[0].wait);
sim_debug(DEBUG_DATAIO, &cty_dev, "CTY %03o DATAO %06o\n", dev, (uint32)*data);
break;
}
return SCPE_OK;
}
t_stat ctyo_svc (UNIT *uptr)
{
t_stat r;
int32 ch;
if (uptr->DATA != 0) {
ch = sim_tt_outcvt ( uptr->DATA, TT_GET_MODE (uptr->flags)) ;
if ((r = sim_putchar_s (ch)) != SCPE_OK) { /* output; error? */
sim_activate (uptr, uptr->wait); /* try again */
return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */
}
}
uptr->STATUS &= ~TEL_BSY;
uptr->STATUS |= TEL_RDY;
set_interrupt(CTY_DEVNUM, uptr->PIA);
return SCPE_OK;
}
t_stat ctyi_svc (UNIT *uptr)
{
int32 ch;
sim_clock_coschedule (uptr, tmxr_poll);
/* continue poll */
if ((ch = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */
return ch;
if (ch & SCPE_BREAK) /* ignore break */
return SCPE_OK;
uptr->DATA = 0177 & sim_tt_inpcvt(ch, TT_GET_MODE (uptr->flags));
uptr->DATA = ch & 0177;
uptr->STATUS |= KEY_RDY;
set_interrupt(CTY_DEVNUM, uptr->PIA);
return SCPE_OK;
}
/* Reset */
t_stat cty_reset (DEVICE *dptr)
{
cty_unit[0].STATUS &= ~(TEL_RDY | TEL_BSY);
cty_unit[1].STATUS &= ~(KEY_RDY | KEY_BSY);
clr_interrupt(CTY_DEVNUM);
sim_clock_coschedule (&cty_unit[1], tmxr_poll);
return SCPE_OK;
}
/* Stop operating system */
t_stat cty_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
M[CTY_SWITCH] = 1; /* tell OS to stop */
return SCPE_OK;
}
t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
cty_unit[0].flags = (cty_unit[0].flags & ~TT_MODE) | val;
cty_unit[1].flags = (cty_unit[1].flags & ~TT_MODE) | val;
return SCPE_OK;
}
t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "To stop the cpu use the command:\n\n");
fprintf (st, " sim> SET CTY STOP\n\n");
fprintf (st, "This will write a 1 to location %03o, causing TOPS10 to stop\n\n", CTY_SWITCH);
fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n");
fprintf (st, " mode input characters output characters\n\n");
fprintf (st, " UC lower case converted lower case converted to upper case,\n");
fprintf (st, " to upper case, high-order bit cleared,\n");
fprintf (st, " high-order bit cleared non-printing characters suppressed\n");
fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n");
fprintf (st, " non-printing characters suppressed\n");
fprintf (st, " 7B high-order bit cleared high-order bit cleared\n");
fprintf (st, " 8B no changes no changes\n\n");
fprintf (st, "The default mode is 7P. In addition, each line can be configured to\n");
fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n");
fprint_reg_help (st, &cty_dev);
return SCPE_OK;
}
const char *cty_description (DEVICE *dptr)
{
return "Console TTY Line";
}
#endif

579
PDP10/kx10_dc.c Normal file
View file

@ -0,0 +1,579 @@
/* ka10_dc.c: PDP-10 DC10 communication server simulator
Copyright (c) 2011-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
#include "kx10_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#ifndef NUM_DEVS_DC
#define NUM_DEVS_DC 0
#endif
#if (NUM_DEVS_DC > 0)
#define DC_DEVNUM 0240
#define DC10_LINES 8
#define DC10_MLINES 32
#define STATUS u3
#define DTS_LINE 007700 /* Scanner line number in STATUS */
#define PI_CHN 000007 /* IN STATUS. */
#define RCV_PI 000010 /* IN STATUS. */
#define XMT_PI 000020 /* IN STATUS. */
#define DTR_DIS 000040 /* DTR FLAG */
#define RST_SCN 000010 /* CONO */
#define DTR_SET 000020 /* CONO */
#define CLR_SCN 000040 /* CONO */
#define DATA 0000377
#define FLAG 0000400 /* Recieve data/ transmit disable */
#define LINE 0000077 /* Line number in Left */
#define LFLAG 0000100 /* Direct line number flag */
/* DC10E flags */
#define CTS 0000004 /* Clear to send */
#define RES_DET 0000002 /* Ring detect */
#define DLO 0000040 /* (ACU) Data line occupied */
#define PND 0000020 /* (ACU) Present next digit */
#define ACR 0000010 /* (ACU) Abandon Call and retry */
#define CRQ 0000040 /* (ACU) Call Request */
#define DPR 0000020 /* (ACU) Digit Presented */
#define NB 0000017 /* (ACU) Number */
#define OFF_HOOK 0000100 /* Off Hook (CD) */
#define CAUSE_PI 0000200 /* Cause PI */
uint64 dc_l_status; /* Line status */
int dc_l_count = 0; /* Scan counter */
int dc_modem = DC10_MLINES; /* Modem base address */
uint8 dcix_buf[DC10_MLINES] = { 0 }; /* Input buffers */
uint8 dcox_buf[DC10_MLINES] = { 0 }; /* Output buffers */
TMLN dc_ldsc[DC10_MLINES] = { 0 }; /* Line descriptors */
TMXR dc_desc = { DC10_LINES, 0, 0, dc_ldsc };
uint32 tx_enable, rx_rdy; /* Flags */
uint32 dc_enable; /* Enable line */
uint32 dc_ring; /* Connection pending */
uint32 rx_conn; /* Connection flags */
extern int32 tmxr_poll;
t_stat dc_devio(uint32 dev, uint64 *data);
t_stat dc_svc (UNIT *uptr);
t_stat dc_doscan (UNIT *uptr);
t_stat dc_reset (DEVICE *dptr);
t_stat dc_set_modem (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dc_show_modem (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat dc_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dc_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dc_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dc_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat dc_attach (UNIT *uptr, CONST char *cptr);
t_stat dc_detach (UNIT *uptr);
t_stat dc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *dc_description (DEVICE *dptr);
/* DC10 data structures
dc_dev DC10 device descriptor
dc_unit DC10 unit descriptor
dc_reg DC10 register list
*/
DIB dc_dib = { DC_DEVNUM, 1, &dc_devio, NULL };
UNIT dc_unit = {
UDATA (&dc_svc, TT_MODE_7B+UNIT_IDLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT
};
REG dc_reg[] = {
{ DRDATA (TIME, dc_unit.wait, 24), REG_NZ + PV_LEFT },
{ DRDATA (STATUS, dc_unit.STATUS, 18), PV_LEFT },
{ NULL }
};
MTAB dc_mod[] = {
{ TT_MODE, TT_MODE_KSR, "KSR", "KSR", NULL },
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &dc_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
NULL, &tmxr_show_summ, (void *) &dc_desc, "Display a summary of line states" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &dc_desc, "Display current connections" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &dc_desc, "Display multiplexer statistics" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
&dc_setnl, &tmxr_show_lines, (void *) &dc_desc, "Set number of lines" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MODEM", "MODEM=n",
&dc_set_modem, &dc_show_modem, (void *)&dc_modem, "Set modem offset" },
{ MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file",
&dc_set_log, NULL, (void *)&dc_desc },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG",
&dc_set_nolog, NULL, (void *)&dc_desc, "Disable logging on designated line" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL,
NULL, &dc_show_log, (void *)&dc_desc, "Display logging for all lines" },
{ 0 }
};
DEVICE dc_dev = {
"DC", &dc_unit, dc_reg, dc_mod,
1, 10, 31, 1, 8, 8,
&tmxr_ex, &tmxr_dep, &dc_reset,
NULL, &dc_attach, &dc_detach,
&dc_dib, DEV_NET | DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &dc_help, NULL, NULL, &dc_description
};
/* IOT routine */
t_stat dc_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &dc_unit;
TMLN *lp;
int ln;
switch(dev & 3) {
case CONI:
/* Check if we might have any interrupts pending */
if ((uptr->STATUS & (RCV_PI|XMT_PI)) == 0)
dc_doscan(uptr);
*data = uptr->STATUS & (PI_CHN|RCV_PI|XMT_PI);
sim_debug(DEBUG_CONI, &dc_dev, "DC %03o CONI %06o PC=%o\n",
dev, (uint32)*data, PC);
break;
case CONO:
/* Set PI */
uptr->STATUS &= ~PI_CHN;
uptr->STATUS |= PI_CHN & *data;
if (*data & RST_SCN)
dc_l_count = 0;
if (*data & DTR_SET)
uptr->STATUS |= DTR_SET;
if (*data & CLR_SCN) {
uptr->STATUS &= PI_CHN;
for (ln = 0; ln < dc_desc.lines; ln++) {
lp = &dc_ldsc[ln];
if (lp->conn) {
tmxr_linemsg (lp, "\r\nLine Hangup\r\n");
tmxr_reset_ln(lp);
}
}
tx_enable = 0;
dc_enable = 0;
rx_rdy = 0; /* Flags */
rx_conn = 0;
dc_ring = 0;
dc_l_status = 0;
}
sim_debug(DEBUG_CONO, &dc_dev, "DC %03o CONO %06o PC=%06o\n",
dev, (uint32)*data, PC);
dc_doscan(uptr);
break;
case DATAO:
if (*data & (LFLAG << 18))
ln = (*data >> 18) & 077;
else
ln = dc_l_count;
if (ln >= dc_modem) {
if (*data & CAUSE_PI)
dc_l_status |= (1LL << ln);
else
dc_l_status &= ~(1LL << ln);
ln -= dc_modem;
sim_debug(DEBUG_DETAIL, &dc_dev, "DC line modem %d %03o\n",
ln, (uint32)(*data & 0777));
if ((*data & OFF_HOOK) == 0) {
uint32 mask = ~(1 << ln);
rx_rdy &= mask;
tx_enable &= mask;
dc_enable &= mask;
lp = &dc_ldsc[ln];
if (rx_conn & (1 << ln) && lp->conn) {
sim_debug(DEBUG_DETAIL, &dc_dev, "DC line hangup %d\n", ln);
tmxr_linemsg (lp, "\r\nLine Hangup\r\n");
tmxr_reset_ln(lp);
rx_conn &= mask;
}
} else {
sim_debug(DEBUG_DETAIL, &dc_dev, "DC line off-hook %d\n", ln);
dc_enable |= 1<<ln;
if (dc_ring & (1 << ln)) {
dc_l_status |= (1LL << (ln + dc_modem));
dc_ring &= ~(1 << ln);
rx_conn |= (1 << ln);
}
}
} else if (ln < dc_desc.lines) {
lp = &dc_ldsc[ln];
if (*data & FLAG) {
tx_enable &= ~(1 << ln);
dc_l_status &= ~(1LL << ln);
} else if (lp->conn) {
int32 ch = *data & DATA;
ch = sim_tt_outcvt(ch, TT_GET_MODE (dc_unit.flags) | TTUF_KSR);
tmxr_putc_ln (lp, ch);
if (lp->xmte)
tx_enable |= (1 << ln);
else
tx_enable &= ~(1 << ln);
dc_l_status |= (1LL << ln);
}
}
dc_doscan(uptr);
sim_debug(DEBUG_DATAIO, &dc_dev, "DC %03o DATO %012llo PC=%06o\n",
dev, *data, PC);
break;
case DATAI:
ln = dc_l_count;
*data = (uint64)(ln) << 18;
if (ln >= dc_modem) {
dc_l_status &= ~(1LL << ln);
ln = ln - dc_modem;
lp = &dc_ldsc[ln];
if (dc_enable & (1 << ln))
*data |= FLAG|OFF_HOOK;
if (rx_conn & (1 << ln) && lp->conn)
*data |= FLAG|CTS;
if (dc_ring & (1 << ln))
*data |= FLAG|RES_DET;
} else if (ln < dc_desc.lines) {
/* Nothing happens if no recieve data, which is transmit ready */
lp = &dc_ldsc[ln];
if (tmxr_rqln (lp) > 0) {
int32 ch = tmxr_getc_ln (lp);
if (ch & SCPE_BREAK) /* break? */
ch = 0;
else
ch = sim_tt_inpcvt (ch, TT_GET_MODE(dc_unit.flags) | TTUF_KSR);
*data |= FLAG | (uint64)(ch & DATA);
}
if (tmxr_rqln (lp) > 0) {
rx_rdy |= 1 << ln;
dc_l_status |= (1LL << ln);
} else {
rx_rdy &= ~(1 << ln);
dc_l_status &= ~(1LL << ln);
}
}
dc_doscan(uptr);
sim_debug(DEBUG_DATAIO, &dc_dev, "DC %03o DATI %012llo PC=%06o\n",
dev, *data, PC);
break;
}
return SCPE_OK;
}
/* Unit service */
t_stat dc_svc (UNIT *uptr)
{
int32 ln;
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_OK;
ln = tmxr_poll_conn (&dc_desc); /* look for connect */
if (ln >= 0) { /* got one? rcv enb*/
dc_ldsc[ln].rcve = 1;
dc_ring |= (1 << ln);
dc_l_status |= (1LL << (ln + dc_modem)); /* Flag modem line */
sim_debug(DEBUG_DETAIL, &dc_dev, "DC line connect %d\n", ln);
}
tmxr_poll_tx(&dc_desc);
tmxr_poll_rx(&dc_desc);
for (ln = 0; ln < dc_desc.lines; ln++) {
/* Check if buffer empty */
if (dc_ldsc[ln].xmte && ((dc_l_status & (1ll << ln)) != 0)) {
tx_enable |= 1 << ln;
}
/* Check to see if any pending data for this line */
if (tmxr_rqln(&dc_ldsc[ln]) > 0) {
rx_rdy |= (1 << ln);
dc_l_status |= (1LL << ln); /* Flag line */
sim_debug(DEBUG_DETAIL, &dc_dev, "DC recieve %d\n", ln);
}
/* Check if disconnect */
if ((rx_conn & (1 << ln)) != 0 && dc_ldsc[ln].conn == 0) {
rx_conn &= ~(1 << ln);
dc_l_status |= (1LL << (ln + dc_modem)); /* Flag modem line */
sim_debug(DEBUG_DETAIL, &dc_dev, "DC line disconnect %d\n", ln);
}
}
/* If any pending status request, raise the PI signal */
if (dc_l_status)
set_interrupt(DC_DEVNUM, uptr->STATUS);
sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */
return SCPE_OK;
}
/* Scan to see if something to do */
t_stat dc_doscan (UNIT *uptr) {
int32 lmask;
uptr->STATUS &= ~(RCV_PI|XMT_PI);
clr_interrupt(DC_DEVNUM);
for (;dc_l_status != 0; dc_l_count++) {
dc_l_count &= 077;
/* Check if we found it */
if (dc_l_status & (1LL << dc_l_count)) {
/* Check if modem control or data line */
if (dc_l_count >= dc_modem) {
uptr->STATUS |= RCV_PI;
} else {
/* Must be data line */
lmask = 1 << dc_l_count;
if (rx_rdy & lmask)
uptr->STATUS |= RCV_PI;
if (tx_enable & lmask)
uptr->STATUS |= XMT_PI;
}
/* Stop scanner */
set_interrupt(DC_DEVNUM, uptr->STATUS);
return SCPE_OK;
}
}
return SCPE_OK;
}
/* Reset routine */
t_stat dc_reset (DEVICE *dptr)
{
if (dc_unit.flags & UNIT_ATT) /* if attached, */
sim_activate (&dc_unit, tmxr_poll); /* activate */
else
sim_cancel (&dc_unit); /* else stop */
tx_enable = 0;
rx_rdy = 0; /* Flags */
rx_conn = 0;
dc_l_status = 0;
dc_l_count = 0;
dc_unit.STATUS = 0;
clr_interrupt(DC_DEVNUM);
return SCPE_OK;
}
/* SET BUFFER processor */
t_stat dc_set_modem (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
int32 modem;
if (cptr == NULL)
return SCPE_ARG;
modem = (int32) get_uint (cptr, 10, 32, &r);
if (r != SCPE_OK)
return SCPE_ARG;
if (modem < 0 || modem >= (DC10_MLINES * 2))
return SCPE_ARG;
if (modem < dc_desc.lines)
return SCPE_ARG;
if ((modem % 8) == 0) {
dc_modem = modem;
return SCPE_OK;
}
return SCPE_ARG;
}
/* SHOW BUFFER processor */
t_stat dc_show_modem (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
fprintf (st, "modem=%d ", dc_modem);
return SCPE_OK;
}
/* SET LINES processor */
t_stat dc_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 newln, i, t;
t_stat r;
if (cptr == NULL)
return SCPE_ARG;
newln = (int32) get_uint (cptr, 10, DC10_MLINES, &r);
if ((r != SCPE_OK) || (newln == dc_desc.lines))
return r;
if (newln > dc_modem)
return SCPE_ARG;
if ((newln == 0) || (newln >= DC10_MLINES) || (newln % 8) != 0)
return SCPE_ARG;
if (newln < dc_desc.lines) {
for (i = newln, t = 0; i < dc_desc.lines; i++)
t = t | dc_ldsc[i].conn;
if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
return SCPE_OK;
for (i = newln; i < dc_desc.lines; i++) {
if (dc_ldsc[i].conn) {
tmxr_linemsg (&dc_ldsc[i], "\r\nOperator disconnected line\r\n");
tmxr_send_buffered_data (&dc_ldsc[i]);
}
tmxr_detach_ln (&dc_ldsc[i]); /* completely reset line */
}
}
if (dc_desc.lines < newln)
memset (dc_ldsc + dc_desc.lines, 0, sizeof(*dc_ldsc)*(newln-dc_desc.lines));
dc_desc.lines = newln;
return dc_reset (&dc_dev); /* setup lines and auto config */
}
/* SET LOG processor */
t_stat dc_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
char gbuf[CBUFSIZE];
int32 ln;
if (cptr == NULL)
return SCPE_ARG;
cptr = get_glyph (cptr, gbuf, '=');
if ((cptr == NULL) || (*cptr == 0) || (gbuf[0] == 0))
return SCPE_ARG;
ln = (int32) get_uint (gbuf, 10, dc_desc.lines, &r);
if ((r != SCPE_OK) || (ln >= dc_desc.lines))
return SCPE_ARG;
return tmxr_set_log (NULL, ln, cptr, desc);
}
/* SET NOLOG processor */
t_stat dc_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
int32 ln;
if (cptr == NULL)
return SCPE_ARG;
ln = (int32) get_uint (cptr, 10, dc_desc.lines, &r);
if ((r != SCPE_OK) || (ln >= dc_desc.lines))
return SCPE_ARG;
return tmxr_set_nolog (NULL, ln, NULL, desc);
}
/* SHOW LOG processor */
t_stat dc_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 i;
for (i = 0; i < dc_desc.lines; i++) {
fprintf (st, "line %d: ", i);
tmxr_show_log (st, NULL, i, desc);
fprintf (st, "\n");
}
return SCPE_OK;
}
/* Attach routine */
t_stat dc_attach (UNIT *uptr, CONST char *cptr)
{
t_stat reason;
reason = tmxr_attach (&dc_desc, uptr, cptr);
if (reason != SCPE_OK)
return reason;
sim_activate (uptr, tmxr_poll);
return SCPE_OK;
}
/* Detach routine */
t_stat dc_detach (UNIT *uptr)
{
int32 i;
t_stat reason;
reason = tmxr_detach (&dc_desc, uptr);
for (i = 0; i < dc_desc.lines; i++)
dc_ldsc[i].rcve = 0;
sim_cancel (uptr);
return reason;
}
t_stat dc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "DC10E Terminal Interfaces\n\n");
fprintf (st, "The DC10 supported up to 8 blocks of 8 lines. Modem control was on a seperate\n");
fprintf (st, "line. The simulator supports this by setting modem control to a fixed offset\n");
fprintf (st, "from the given line. The number of lines is specified with a SET command:\n\n");
fprintf (st, " sim> SET DC LINES=n set number of additional lines to n [8-32]\n\n");
fprintf (st, "Lines must be set in multiples of 8.\n");
fprintf (st, "The default offset for modem lines is 32. This can be changed with\n\n");
fprintf (st, " sim> SET DC MODEM=n set offset for modem control to n [8-32]\n\n");
fprintf (st, "Modem control must be set larger then the number of lines\n");
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
tmxr_attach_help (st, dptr, uptr, flag, cptr);
fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n");
fprintf (st, " mode input characters output characters\n\n");
fprintf (st, " UC lower case converted lower case converted to upper case,\n");
fprintf (st, " to upper case, high-order bit cleared,\n");
fprintf (st, " high-order bit cleared non-printing characters suppressed\n");
fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n");
fprintf (st, " non-printing characters suppressed\n");
fprintf (st, " 7B high-order bit cleared high-order bit cleared\n");
fprintf (st, " 8B no changes no changes\n\n");
fprintf (st, "The default mode is 7P.\n");
fprintf (st, "Finally, each line supports output logging. The SET DCn LOG command enables\n");
fprintf (st, "logging on a line:\n\n");
fprintf (st, " sim> SET DCn LOG=filename log output of line n to filename\n\n");
fprintf (st, "The SET DCn NOLOG command disables logging and closes the open log file,\n");
fprintf (st, "if any.\n\n");
fprintf (st, "Once DC is attached and the simulator is running, the terminals listen for\n");
fprintf (st, "connections on the specified port. They assume that the incoming connections\n");
fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n");
fprintf (st, "by the Telnet client, a SET DC DISCONNECT command, or a DETACH DC command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, " sim> SHOW DC CONNECTIONS show current connections\n");
fprintf (st, " sim> SHOW DC STATISTICS show statistics for active connections\n");
fprintf (st, " sim> SET DCn DISCONNECT disconnects the specified line.\n");
fprint_reg_help (st, &dc_dev);
fprintf (st, "\nThe additional terminals do not support save and restore. All open connections\n");
fprintf (st, "are lost when the simulator shuts down or DC is detached.\n");
return SCPE_OK;
}
const char *dc_description (DEVICE *dptr)
{
return "DC10E asynchronous line interface";
}
#endif

492
PDP10/kx10_defs.h Normal file
View file

@ -0,0 +1,492 @@
/* ka10_defs.h: PDP-10 simulator definitions
Copyright (c) 2011-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
#ifndef _KA10_DEFS_H_
#define _KA10_DEFS_H_ 0
#include "sim_defs.h" /* simulator defns */
#if defined(USE_ADDR64)
#error "PDP-10 does not support 64b addresses!"
#endif
#ifndef PDP6
#define PDP6 0
#endif
#ifndef KA
#define KA 0
#endif
#ifndef KI
#define KI 0
#endif
#ifndef KLA
#define KLA 0
#endif
#ifndef KLB
#define KLB 0
#endif
#ifndef KL /* Either KL10A or KL10B */
#define KL (KLA+KLB)
#endif
#if (PDP6 + KA + KI + KL) != 1
#error "Please define only one type of CPU"
#endif
#ifndef KI_22BIT
#define KI_22BIT KI|KL
#endif
/* Support for ITS Pager */
#ifndef ITS
#define ITS KA
#endif
/* Support for TENEX Pager */
#ifndef BBN
#define BBN KA
#endif
/* Support for WAITS mods */
#ifndef WAITS
#define WAITS KA
#endif
#ifndef PDP6_DEV /* Include PDP6 devices */
#define PDP6_DEV PDP6|WAITS
#endif
/* MPX interrupt multiplexer for ITS systems */
#define MPX_DEV ITS
/* Digital Equipment Corporation's 36b family had six implementations:
name mips comments
PDP-6 0.25 Original 36b implementation, 1964
KA10 0.38 First PDP-10, flip chips, 1967
KI10 0.72 First paging system, flip chip + MSI, 1972
KL10 1.8 First ECL system, ECL 10K, 1975
KL10B 1.8 Expanded addressing, ECL 10K, 1978
KS10 0.3 Last 36b system, 2901 based, 1979
In addition, it ran four major (incompatible) operating systems:
name company comments
TOPS-10 DEC Original timesharing system
ITS MIT "Incompatible Timesharing System"
TENEX BBN ARPA-sponsored, became
TOPS-20 DEC Commercial version of TENEX
All of the implementations differ from one another, in instruction set,
I/O structure, and memory management. Further, each of the operating
systems customized the microcode of the paging systems (KI10, KL10, KS10)
for additional instructions and specialized memory management. As a
result, there is no "reference implementation" for the 36b family that
will run all programs and all operating systems. The conditionalization
and generality needed to support the full matrix of models and operating
systems, and to support 36b hardware on 32b data types, is beyond the
scope of this project.
*/
/* Abort codes, used to sort out longjmp's back to the main loop
Codes > 0 are simulator stop codes
Codes < 0 are internal aborts
Code = 0 stops execution for an interrupt check
*/
typedef t_uint64 uint64;
#define STOP_HALT 1 /* halted */
#define STOP_IBKPT 2 /* breakpoint */
/* Debuging controls */
#define DEBUG_CMD 0x0000001 /* Show device commands */
#define DEBUG_DATA 0x0000002 /* Show data transfers */
#define DEBUG_DETAIL 0x0000004 /* Show details */
#define DEBUG_EXP 0x0000008 /* Show error conditions */
#define DEBUG_CONI 0x0000020 /* Show CONI instructions */
#define DEBUG_CONO 0x0000040 /* Show CONO instructions */
#define DEBUG_DATAIO 0x0000100 /* Show DATAI/O instructions */
#define DEBUG_IRQ 0x0000200 /* Show IRQ requests */
extern DEBTAB dev_debug[];
extern DEBTAB crd_debug[];
/* Operating system flags, kept in cpu_unit.flags */
#define Q_IDLE (sim_idle_enab)
/* Device information block */
#define LMASK 00777777000000LL
#define RMASK 00000000777777LL
#define FMASK 00777777777777LL
#define CMASK 00377777777777LL
#define SMASK 00400000000000LL
#define C1 01000000000000LL
#define RSIGN 00000000400000LL
#define PMASK 00007777777777LL
#define XMASK 03777777777777LL
#define EMASK 00777000000000LL
#define MMASK 00000777777777LL
#define BIT1 00200000000000LL
#define BIT7 00002000000000LL
#define BIT8 00001000000000LL
#define BIT9 00000400000000LL
#define BIT10 00000200000000LL
#define BIT10_35 00000377777777LL
#define MANT 00000777777777LL
#define EXPO 00377000000000LL
#define FPHBIT 01000000000000000000000LL
#define FPSBIT 00400000000000000000000LL
#define FPNBIT 00200000000000000000000LL
#define FP1BIT 00100000000000000000000LL
#define FPFMASK 01777777777777777777777LL
#define FPRMASK 00000000000177777777777LL
#define FPMMASK 00000000000077777777777LL
#define FPRBIT2 00000000000100000000000LL
#define FPRBIT1 00000000000200000000000LL
#define CM(x) (FMASK ^ (x))
#define INST_V_OP 27 /* opcode */
#define INST_M_OP 0777
#define INST_V_DEV 26
#define INST_M_DEV 0177 /* device */
#define INST_V_AC 23 /* AC */
#define INST_M_AC 017
#define INST_V_IND 22 /* indirect */
#define INST_IND (1 << INST_V_IND)
#define INST_V_XR 18 /* index */
#define INST_M_XR 017
#define OP_JRST 0254 /* JRST */
#define OP_JUMPA 0324 /* JUMPA */
#define AC_XPCW 07 /* XPCW */
#define OP_JSR 0264 /* JSR */
#define GET_OP(x) ((int32) (((x) >> INST_V_OP) & INST_M_OP))
#define GET_DEV(x) ((int32) (((x) >> INST_V_DEV) & INST_M_DEV))
#define GET_AC(x) ((int32) (((x) >> INST_V_AC) & INST_M_AC))
#define TST_IND(x) ((x) & INST_IND)
#define GET_XR(x) ((int32) (((x) >> INST_V_XR) & INST_M_XR))
#define GET_ADDR(x) ((uint32) ((x) & RMASK))
#define LRZ(x) (((x) >> 18) & RMASK)
#define JRST1 (((uint64)OP_JRST << 27) + 1)
#if PDP6
#define NODIV 000000
#define FLTUND 000000
#else
#define NODIV 000001 /* 000040 */
#define FLTUND 000002 /* 000100 */
#endif
#if KI|KL
#define TRP1 000004 /* 000200 */
#define TRP2 000010 /* 000400 */
#define ADRFLT 000020 /* 001000 */
#define PUBLIC 000040 /* 002000 */
#else
#define TRP1 000000
#define TRP2 000000
#define ADRFLT 000000
#define PUBLIC 000000
#endif
#ifdef BBN
#define EXJSYS 000040 /* 002000 */
#endif
#define USERIO 000100 /* 004000 */
#define USER 000200 /* 010000 */
#define BYTI 000400 /* 020000 */
#if PDP6
#define FLTOVR 010000
#define PCHNG 001000 /* 040000 */
#else
#define FLTOVR 001000 /* 040000 */
#define PCHNG 000000
#endif
#define CRY1 002000 /* 100000 */
#define CRY0 004000 /* 200000 */
#define OVR 010000 /* 400000 */
#if KI|KL
#define PRV_PUB 020000 /* Overflow in excutive mode */
#else
#define PRV_PUB 000000 /* Not on KA or PDP6 */
#endif
#ifdef ITS
#ifdef PURE
#undef PURE
#endif
#define ONEP 000010 /* 000400 */
#define PURE 000040 /* 002000 */
#endif
#define DATAI 00
#define DATAO 01
#define CONI 02
#define CONO 03
#define CTY_SWITCH 030
#if KI_22BIT|KI
#define MAXMEMSIZE 4096 * 1024
#else
#if PDP6
#define MAXMEMSIZE 256 * 1024
#else
#define MAXMEMSIZE 1024 * 1024
#endif
#endif
#define MEMSIZE (cpu_unit[0].capac)
#define ICWA 0000000000776
#if KI_22BIT
#define AMASK 00000017777777LL
#define WMASK 0037777LL
#define CSHIFT 22
#else
#define AMASK RMASK
#define WMASK RMASK
#define CSHIFT 18
#endif
#define API_MASK 0000000007
#define PI_ENABLE 0000000010 /* Clear DONE */
#define BUSY 0000000020 /* STOP */
#define CCW_COMP 0000000040 /* Write Final CCW */
#if KI
#define DEF_SERIAL 514 /* Default DEC test machine */
#endif
#if BBN
#define BBN_PAGE 0000017777777LL
#define BBN_TRPPG 0000017000000LL
#define BBN_SPT 0000017777000LL
#define BBN_PN 0000000000777LL
#define BBN_ACC 0000040000000LL
#define BBN_TRP1 0000100000000LL
#define BBN_TRP 0000200000000LL
#define BBN_TRPMOD 0000400000000LL
#define BBN_TRPUSR 0001000000000LL
#define BBN_EXEC 0020000000000LL
#define BBN_WRITE 0040000000000LL
#define BBN_READ 0100000000000LL
#define BBN_MERGE 0161740000000LL
#endif
/* Flags for CPU unit */
#define UNIT_V_MSIZE (UNIT_V_UF + 0)
#define UNIT_MSIZE (0177 << UNIT_V_MSIZE)
#define UNIT_V_MAOFF (UNIT_V_MSIZE + 8)
#define UNIT_V_PAGE (UNIT_V_MAOFF + 1)
#define UNIT_MAOFF (1 << UNIT_V_MAOFF)
#define UNIT_TWOSEG (1 << UNIT_V_PAGE)
#define UNIT_ITSPAGE (2 << UNIT_V_PAGE)
#define UNIT_BBNPAGE (4 << UNIT_V_PAGE)
#define UNIT_M_PAGE (007 << UNIT_V_PAGE)
#define UNIT_V_WAITS (UNIT_V_PAGE + 3)
#define UNIT_M_WAITS (1 << UNIT_V_WAITS)
#define UNIT_WAITS (UNIT_M_WAITS) /* Support for WAITS xct and fix */
#define UNIT_V_MPX (UNIT_V_WAITS + 1)
#define UNIT_M_MPX (1 << UNIT_V_MPX)
#define UNIT_MPX (UNIT_M_MPX) /* MPX Device for ITS */
#if MPX_DEV
extern void set_interrupt_mpx(int dev, int lvl, int mpx);
#else
#define set_interrupt_mpx(d,l,m) set_interrupt(d,l)
#endif
extern void set_interrupt(int dev, int lvl);
extern void clr_interrupt(int dev);
extern void check_apr_irq();
extern int check_irq_level();
extern void restore_pi_hold();
extern void set_pi_hold();
extern UNIT cpu_unit[];
extern UNIT ten11_unit[];
extern UNIT auxcpu_unit[];
extern DEVICE cpu_dev;
extern DEVICE cty_dev;
extern DEVICE mt_dev;
extern DEVICE dpa_dev;
extern DEVICE dpb_dev;
extern DEVICE dpc_dev;
extern DEVICE dpd_dev;
extern DEVICE imp_dev;
extern DEVICE rpa_dev;
extern DEVICE rpb_dev;
extern DEVICE rpc_dev;
extern DEVICE rpd_dev;
extern DEVICE rsa_dev;
extern DEVICE tua_dev;
extern DEVICE lpt_dev;
extern DEVICE ptp_dev;
extern DEVICE ptr_dev;
extern DEVICE cr_dev;
extern DEVICE cp_dev;
extern DEVICE rca_dev;
extern DEVICE rcb_dev;
extern DEVICE dc_dev;
extern DEVICE dt_dev;
extern DEVICE pmp_dev;
extern DEVICE dk_dev;
extern DEVICE pd_dev;
extern DEVICE dpy_dev;
extern DEVICE imx_dev;
extern DEVICE imp_dev;
extern DEVICE ch10_dev;
extern DEVICE stk_dev;
extern DEVICE tk10_dev;
extern DEVICE mty_dev;
extern DEVICE ten11_dev;
extern DEVICE dkb_dev;
extern DEVICE auxcpu_dev;
extern DEVICE dpk_dev;
extern DEVICE wcnsls_dev; /* MIT Spacewar Consoles */
extern DEVICE dct_dev; /* PDP6 devices. */
extern DEVICE dtc_dev;
extern DEVICE mtc_dev;
extern DEVICE dsk_dev;
extern DEVICE dcs_dev;
extern t_stat (*dev_tab[128])(uint32 dev, t_uint64 *data);
#define VEC_DEVMAX 8 /* max device vec */
/* Device context block */
struct pdp_dib {
uint32 dev_num; /* device address */
uint32 num_devs; /* length */
t_stat (*io)(uint32 dev, t_uint64 *data);
int (*irq)(uint32 dev, int addr);
};
#define RH10_DEV 01000
struct rh_dev {
uint32 dev_num;
DEVICE *dev;
};
typedef struct pdp_dib DIB;
/* DF10 Interface */
struct df10 {
uint32 status;
uint32 cia;
uint32 ccw;
uint32 wcr;
uint32 cda;
uint32 devnum;
t_uint64 buf;
uint8 nxmerr;
uint8 ccw_comp;
} ;
void df10_setirq(struct df10 *df) ;
void df10_writecw(struct df10 *df) ;
void df10_finish_op(struct df10 *df, int flags) ;
void df10_setup(struct df10 *df, uint32 addr);
int df10_fetch(struct df10 *df);
int df10_read(struct df10 *df);
int df10_write(struct df10 *df);
#if PDP6_DEV
int dct_read(int u, t_uint64 *data, int c);
int dct_write(int u, t_uint64 *data, int c);
int dct_is_connect(int u);
#endif
int ten11_read (int addr, t_uint64 *data);
int ten11_write (int addr, t_uint64 data);
/* Console lights. */
extern void ka10_lights_init (void);
extern void ka10_lights_main (t_uint64);
extern void ka10_lights_set_aux (int);
extern void ka10_lights_clear_aux (int);
int auxcpu_read (int addr, t_uint64 *);
int auxcpu_write (int addr, t_uint64);
/* I/O system parameters */
#define NUM_DEVS_LP 1
#define NUM_DEVS_PT 1
#define NUM_DEVS_CR 1
#define NUM_DEVS_CP 1
#define NUM_DEVS_DPY USE_DISPLAY
#define NUM_DEVS_WCNSLS USE_DISPLAY
#if PDP6_DEV
#define NUM_DEVS_DTC 1
#define NUM_DEVS_DCT 2
#define NUM_DEVS_MTC 1
#define NUM_DEVS_DSK 1
#define NUM_DEVS_DCS 1
#endif
#if !PDP6
#define NUM_DEVS_DC 1
#define NUM_DEVS_MT 1
#define NUM_DEVS_RC 1
#define NUM_DEVS_DT 1
#define NUM_DEVS_DK 1
#define NUM_DEVS_DP 2
#define NUM_DEVS_RP 4
#define NUM_DEVS_RS 1
#define NUM_DEVS_TU 1
#define NUM_DEVS_PMP WAITS
#define NUM_DEVS_DKB WAITS
#define NUM_DEVS_PD ITS
#define NUM_DEVS_IMX ITS
#define NUM_DEVS_STK ITS
#define NUM_DEVS_TK10 ITS
#define NUM_DEVS_MTY ITS
#define NUM_DEVS_TEN11 ITS
#define NUM_DEVS_AUXCPU ITS
#define NUM_DEVS_IMP 1
#define NUM_DEVS_CH10 ITS
#define NUM_DEVS_DPK ITS
#endif
/* Global data */
extern t_bool sim_idle_enab;
extern struct rh_dev rh[];
extern t_uint64 M[MAXMEMSIZE];
extern t_uint64 FM[];
extern uint32 PC;
extern uint32 FLAGS;
#endif

139
PDP10/kx10_df.c Normal file
View file

@ -0,0 +1,139 @@
/* ka10_df.c: DF10 common routines.
Copyright (c) 2015-2017, Richard Cornwell
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
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
void df10_setirq(struct df10 *df) {
df->status |= PI_ENABLE;
set_interrupt(df->devnum, df->status);
}
void df10_writecw(struct df10 *df) {
df->status |= 1 << df->ccw_comp;
if (df->wcr != 0)
df->cda++;
M[df->cia|1] = ((uint64)(df->ccw & WMASK) << CSHIFT) | ((uint64)(df->cda) & AMASK);
}
void df10_finish_op(struct df10 *df, int flags) {
df->status &= ~BUSY;
df->status |= flags;
df10_writecw(df);
df10_setirq(df);
}
void df10_setup(struct df10 *df, uint32 addr) {
df->cia = addr & ICWA;
df->ccw = df->cia;
df->wcr = 0;
df->status |= BUSY;
df->status &= ~(1 << df->ccw_comp);
}
int df10_fetch(struct df10 *df) {
uint64 data;
if (df->ccw > MEMSIZE) {
df10_finish_op(df, df->nxmerr);
return 0;
}
data = M[df->ccw];
while((data & (WMASK << CSHIFT)) == 0) {
if ((data & AMASK) == 0 || (uint32)(data & AMASK) == df->ccw) {
df10_finish_op(df,0);
return 0;
}
df->ccw = (uint32)(data & AMASK);
if (df->ccw > MEMSIZE) {
df10_finish_op(df, 1<<df->nxmerr);
return 0;
}
data = M[df->ccw];
}
#if KA & ITS
if (cpu_unit[0].flags & UNIT_ITSPAGE) {
df->wcr = (uint32)((data >> CSHIFT) & 0077777) | 0700000;
df->cda = (uint32)(data & RMASK);
df->cda |= (uint32)((data >> 15) & 00000007000000LL) ^ 07000000;
df->ccw = (uint32)((df->ccw + 1) & AMASK);
return 1;
}
#endif
df->wcr = (uint32)((data >> CSHIFT) & WMASK);
df->cda = (uint32)(data & AMASK);
df->ccw = (uint32)((df->ccw + 1) & AMASK);
return 1;
}
int df10_read(struct df10 *df) {
uint64 data;
if (df->wcr == 0) {
if (!df10_fetch(df))
return 0;
}
df->wcr = (uint32)((df->wcr + 1) & WMASK);
if (df->cda != 0) {
if (df->cda > MEMSIZE) {
df10_finish_op(df, 1<<df->nxmerr);
return 0;
}
#if KA & ITS
if (cpu_unit[0].flags & UNIT_ITSPAGE)
df->cda = (uint32)((df->cda + 1) & RMASK) | (df->cda & 07000000);
else
#endif
df->cda = (uint32)((df->cda + 1) & AMASK);
data = M[df->cda];
} else {
data = 0;
}
df->buf = data;
if (df->wcr == 0) {
return df10_fetch(df);
}
return 1;
}
int df10_write(struct df10 *df) {
if (df->wcr == 0) {
if (!df10_fetch(df))
return 0;
}
df->wcr = (uint32)((df->wcr + 1) & WMASK);
if (df->cda != 0) {
if (df->cda > MEMSIZE) {
df10_finish_op(df, 1<<df->nxmerr);
return 0;
}
#if KA & ITS
if (cpu_unit[0].flags & UNIT_ITSPAGE)
df->cda = (uint32)((df->cda + 1) & RMASK) | (df->cda & 07000000);
else
#endif
df->cda = (uint32)((df->cda + 1) & AMASK);
M[df->cda] = df->buf;
}
if (df->wcr == 0) {
return df10_fetch(df);
}
return 1;
}

220
PDP10/kx10_dk.c Normal file
View file

@ -0,0 +1,220 @@
/* ka10_dk.c: PDP-10 DK subsystem simulator
Copyright (c) 2013-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
#include "kx10_defs.h"
#include <time.h>
#ifndef NUM_DEVS_DK
#define NUM_DEVS_DK 0
#endif
#if (NUM_DEVS_DK > 0)
#define DK_DEVNUM 070
#define STAT_REG u3
#define CLK_REG u4
#define INT_REG u5
#define CLK_TIM u6
/* CONO */
#define PIA 000007
#define CLK_CLR_FLG 000010 /* Clear Clock flag */
#define CLK_CLR_OVF 000020 /* Clear OVFL flag */
#define CLK_SET_EN 000040 /* Enable Clock */
#define CLK_CLR_EN 000100 /* Disable Clock */
#define CLK_SET_PI 000200 /* Set PI Control Flip-Flop */
#define CLK_CLR_PI 000400 /* Clear PI Control Flip-Flop */
#define CLK_GEN_CLR 001000 /* Clear control */
#define CLK_ADD_ONE 002000 /* Bump clock */
#define CLK_SET_FLG 004000 /* Set Clock Flag */
#define CLK_SET_OVF 010000 /* Set OVFL Flag */
/* CONI */
#define CLK_FLG 000010
#define CLK_OVF 000020
#define CLK_EN 000040
#define CLK_PI 000200
#define CLK_EXT 001000
t_stat dk_devio(uint32 dev, uint64 *data);
void dk_test (UNIT *uptr);
t_stat dk_svc (UNIT *uptr);
const char *dk_description (DEVICE *dptr);
DIB dk_dib[] = {
{ DK_DEVNUM, 1, &dk_devio, NULL },
{ DK_DEVNUM + 4, 1, &dk_devio, NULL}};
UNIT dk_unit[] = {
{UDATA (&dk_svc, UNIT_IDLE, 0) },
#if (NUM_DEVS_DK > 1)
{UDATA (&dk_svc, UNIT_IDLE, 0) },
#endif
};
DEVICE dk_dev = {
"DK", dk_unit, NULL, NULL,
NUM_DEVS_DK, 0, 0, 0, 0, 0,
NULL, NULL, NULL,
NULL, NULL, NULL,
&dk_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, NULL, NULL, NULL, &dk_description
};
t_stat dk_devio(uint32 dev, uint64 *data) {
int unit = (dev - DK_DEVNUM) >> 2;
UNIT *uptr;
double us;
int32 t;
if (unit < 0 || unit >= NUM_DEVS_DK)
return SCPE_OK;
uptr = &dk_unit[unit];
switch (dev & 3) {
case CONI:
*data = (uint64)(uptr->STAT_REG);
*data |= ((uint64)uptr->INT_REG) << 18;
sim_debug(DEBUG_CONI, &dk_dev, "DK %03o CONI %06o PC=%o %06o\n",
dev, (uint32)*data, PC, uptr->CLK_REG);
break;
case CONO:
/* Adjust U3 */
clr_interrupt(dev);
uptr->STAT_REG &= ~07;
if (*data & CLK_GEN_CLR) {
uptr->CLK_REG = 0;
uptr->STAT_REG = 0;
sim_cancel(uptr);
}
uptr->STAT_REG |= (uint32)(*data & 07);
if (*data & CLK_ADD_ONE) {
if ((uptr->STAT_REG & CLK_EN) == 0) {
uptr->CLK_REG++;
dk_test(uptr);
}
}
if (*data & CLK_SET_EN)
uptr->STAT_REG |= CLK_EN;
if (*data & CLK_CLR_EN)
uptr->STAT_REG &= ~CLK_EN;
if (*data & CLK_SET_OVF)
uptr->STAT_REG |= CLK_OVF;
if (*data & CLK_CLR_OVF)
uptr->STAT_REG &= ~CLK_OVF;
if (*data & CLK_SET_FLG)
uptr->STAT_REG |= CLK_FLG;
if (*data & CLK_CLR_FLG)
uptr->STAT_REG &= ~CLK_FLG;
if (*data & CLK_SET_PI)
uptr->STAT_REG |= CLK_PI;
if (*data & CLK_CLR_PI)
uptr->STAT_REG &= ~CLK_PI;
if ((uptr->STAT_REG & CLK_EN) != 0 &&
(uptr->STAT_REG & (CLK_FLG|CLK_OVF))) {
set_interrupt(dev, uptr->STAT_REG);
}
set_clock:
if (sim_is_active(uptr)) { /* Save current clock time */
us = sim_activate_time_usecs(uptr);
uptr->CLK_REG += uptr->CLK_TIM - (uint32)(us / 10.0);
sim_cancel(uptr);
}
if (uptr->INT_REG == uptr->CLK_REG) {
uptr->STAT_REG |= CLK_FLG;
set_interrupt(dev, uptr->STAT_REG);
}
if (uptr->STAT_REG & CLK_EN) {
if (uptr->INT_REG < uptr->CLK_REG) /* Count until overflow */
uptr->CLK_TIM = 01000000;
else
uptr->CLK_TIM = uptr->INT_REG;
t = uptr->CLK_TIM - uptr->CLK_REG;
us = (double)(t) * 10.0;
sim_activate_after_d(uptr, us);
} else {
sim_cancel(uptr);
}
sim_debug(DEBUG_CONO, &dk_dev, "DK %03o CONO %06o PC=%06o %06o\n",
dev, (uint32)*data, PC, uptr->STAT_REG);
break;
case DATAO:
uptr->INT_REG = (uint32)(*data & RMASK);
sim_debug(DEBUG_DATAIO, &dk_dev, "DK %03o DATO %012llo PC=%06o\n",
dev, *data, PC);
goto set_clock;
case DATAI:
if (sim_is_active(uptr)) { /* Save current clock time */
double us = sim_activate_time_usecs(uptr);
uptr->CLK_REG += uptr->CLK_TIM - (uint32)(us / 10.0);
sim_cancel(uptr);
}
*data = (uint64)(uptr->CLK_REG);
sim_debug(DEBUG_DATAIO, &dk_dev, "DK %03o DATI %012llo PC=%06o\n",
dev, *data, PC);
goto set_clock;
}
return SCPE_OK;
}
/* Bump counter by 1 */
void dk_test (UNIT *uptr)
{
int dev;
if (uptr->CLK_REG & (~RMASK))
uptr->STAT_REG |= CLK_OVF;
uptr->CLK_REG &= RMASK;
if (uptr->INT_REG == uptr->CLK_REG)
uptr->STAT_REG |= CLK_FLG;
if (uptr->STAT_REG & (CLK_FLG|CLK_OVF)) {
dev = ((uptr - dk_unit) << 2) + DK_DEVNUM;
set_interrupt(dev, uptr->STAT_REG);
}
}
/* Timer service - */
t_stat dk_svc (UNIT *uptr)
{
uptr->CLK_REG = uptr->CLK_TIM;
dk_test (uptr);
return SCPE_OK;
}
const char *dk_description (DEVICE *dptr)
{
return "DK10 Timer module";
}
#endif

999
PDP10/kx10_dp.c Normal file
View file

@ -0,0 +1,999 @@
/* ka10_dp.c: Dec Data Products Disk Drive.
Copyright (c) 2013-2017, Richard Cornwell
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
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_DP
#define NUM_DEVS_DP 0
#endif
#if (NUM_DEVS_DP > 0)
#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF)
#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF
#define RP_NUMWD 128 /* 36bit words/sec */
#define DP_DEVNUM 0250 /* First device number */
#define NUM_UNITS_DP 8
/* Flags in the unit flags word */
#define DEV_WHDR (1 << DEV_V_UF) /* Enable write headers */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
#define UNIT_M_DTYPE 3
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* Parameters in the unit descriptor */
#define CUR_CYL u3 /* current cylinder */
#define DATAPTR u4 /* data pointer */
#define UFLAGS u5 /* Function */
#define STATUS u6 /* Drive status */
#define SECT_CNT up7 /* Sector counter */
#define CONTROL 007
#define CMD_MASK 070
#define SEEK_DONE 0100 /* Seek finished */
#define SEEK_STATE 0200 /* Seek in progress */
#define DONE 0400 /* Done bit */
/* CONI/CONO Flags */
#define SUF_ERR 0000000000100LL
#define SEC_ERR 0000000000200LL
#define ILL_CMD 0000000000400LL
#define ILL_WR 0000000001000LL
#define NOT_RDY 0000000002000LL /* Clear CXR */
#define PRT_ERR 0000000004000LL /* 14-17 Clear CCPE, DSPE, DISK WDPE, CDPE */
#define NXM_ERR 0000000010000LL
#define SLW_CHN 0000000020000LL
#define SRC_ERR 0000000040000LL
#define PWR_FAIL_10 0000000100000LL
#define END_CYL 0000000200000LL /* No effect */
#define SRC_DONE 0000000400000LL /* No effect */
#define DSK_PRTY 0000001000000LL /* No effect */
#define CHN_PRTY 0000002000000LL /* No effect */
#define SEC_PRTY 0000004000000LL /* No effect */
#define CCW_PRTY 0000010000000LL /* No effect */
#define B22_FLAG 0000020000000LL
#define CLRMSK 0000000177710LL
#define CLRMSK2 0000176000000LL
/* DATAO */
#define DWPE_STOP 0000000001000LL
#define SPARE 0000000002000LL
#define DSPE_STOP 0000000004000LL
#define SECTOR 0000000170000LL
#define CYL256 0000000200000LL
#define SURFACE 0000017400000LL
#define CYL 0007760000000LL
#define DRIVE 0070000000000LL
#define OP 0700000000000LL
#define RD 0
#define WR 1
#define RV 2
#define WH 3
#define SK 4
#define CL 5
#define NO 6
#define RC 7
/* DATAI Flags */
#define ATTN 0000000000776LL
#define DEFECT 0000000001000LL
#define SEL_RP03 0000000002000LL
#define SEL_CYL256 0000000004000LL
#define SEL_SPARE 0000000010000LL
#define SEL_SEC 0000000760000LL
#define WR_HD_LK 0000001000000LL
#define RD_ONLY 0000002000000LL
#define NO_DRIVE 0000004000000LL
#define FILE_UNSAFE 0000010000000LL
#define DRV_ONLINE 0000020000000LL
#define ON_CYL 0000040000000LL
#define SEEK_INC 0000100000000LL
#define SEL_CYL 0077600000000LL
#define SEL_DRIVE 0700000000000LL
#define RP01_DTYPE 0
#define RP01_SECT 5
#define RP01_SURF 10
#define RP01_CYL 203
#define RP01_DEV 0
#define RP01_SIZE (RP01_SECT * RP01_SURF * RP01_CYL * RP_NUMWD)
#define RP02_DTYPE 1
#define RP02_SECT 10
#define RP02_SURF 20
#define RP02_CYL 203
#define RP02_DEV 0
#define RP02_SIZE (RP02_SECT * RP02_SURF * RP02_CYL * RP_NUMWD)
#define RP03_DTYPE 2
#define RP03_SECT 10
#define RP03_SURF 20
#define RP03_CYL 406
#define RP03_DEV 1
#define RP03_SIZE (RP03_SECT * RP03_SURF * RP03_CYL * RP_NUMWD)
struct drvtyp {
int32 sect; /* sectors */
int32 surf; /* surfaces */
int32 cyl; /* cylinders */
int32 size; /* #blocks */
int32 devtype; /* device type */
};
struct drvtyp dp_drv_tab[] = {
{ RP01_SECT, RP01_SURF, RP01_CYL, RP01_SIZE, RP01_DTYPE},
{ RP02_SECT, RP02_SURF, RP02_CYL, RP02_SIZE, RP02_DTYPE},
{ RP03_SECT, RP03_SURF, RP03_CYL, RP03_SIZE, RP03_DTYPE},
{ 0 }
};
struct df10 dp_df10[NUM_DEVS_DP];
uint32 dp_cur_unit[NUM_DEVS_DP];
uint64 dp_buf[NUM_DEVS_DP][RP_NUMWD];
int readin_flag = 0;
t_stat dp_devio(uint32 dev, uint64 *data);
t_stat dp_svc(UNIT *);
t_stat dp_boot(int32, DEVICE *);
void dp_ini(UNIT *, t_bool);
t_stat dp_reset(DEVICE *);
t_stat dp_attach(UNIT *, CONST char *);
t_stat dp_detach(UNIT *);
t_stat dp_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dp_set_hdr(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dp_show_hdr(FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat dp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *dp_description (DEVICE *dptr);
UNIT dp_unit[] = {
/* Controller 1 */
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
#if (NUM_DEVS_DP > 1)
/* Controller 2 */
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
#if (NUM_DEVS_DP > 2)
/* Controller 3 */
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
#if (NUM_DEVS_DP > 3)
/* Controller 4 */
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
{ UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RP03_DTYPE << UNIT_V_DTYPE), RP03_SIZE) },
#endif
#endif
#endif
};
DIB dp_dib[] = {
{DP_DEVNUM+000, 1, &dp_devio, NULL},
{DP_DEVNUM+004, 1, &dp_devio, NULL},
{DP_DEVNUM+010, 1, &dp_devio, NULL},
{DP_DEVNUM+014, 1, &dp_devio, NULL}};
MTAB dp_mod[] = {
{UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL},
{UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL},
{MTAB_XTD|MTAB_VDV, 0, NULL, "NOHEADERS",
&dp_set_hdr, &dp_show_hdr, NULL, "Disable header writing"},
{MTAB_XTD|MTAB_VDV, DEV_WHDR, "write header", "HEADERS",
&dp_set_hdr, &dp_show_hdr, NULL, "Enable header writing"},
{UNIT_DTYPE, (RP03_DTYPE << UNIT_V_DTYPE), "RP03", "RP03", &dp_set_type },
{UNIT_DTYPE, (RP02_DTYPE << UNIT_V_DTYPE), "RP02", "RP02", &dp_set_type },
{UNIT_DTYPE, (RP01_DTYPE << UNIT_V_DTYPE), "RP01", "RP01", &dp_set_type },
{0},
};
REG dpa_reg[] = {
{BRDATA(BUFF, &dp_buf[0][0], 16, 64, RP_NUMWD), REG_HRO},
{BRDATA(UNIT, &dp_cur_unit[0], 16, 8, 1), REG_HRO},
{FLDATA(READIN, readin_flag, 0), REG_HRO},
{ORDATA(STATUS, dp_df10[0].status, 18), REG_RO},
{ORDATA(CIA, dp_df10[0].cia, 18)},
{ORDATA(CCW, dp_df10[0].ccw, 18)},
{ORDATA(WCR, dp_df10[0].wcr, 18)},
{ORDATA(CDA, dp_df10[0].cda, 18)},
{ORDATA(DEVNUM, dp_df10[0].devnum, 9), REG_HRO},
{ORDATA(BUF, dp_df10[0].buf, 36), REG_HRO},
{ORDATA(NXM, dp_df10[0].nxmerr, 8), REG_HRO},
{ORDATA(COMP, dp_df10[0].ccw_comp, 8), REG_HRO},
{0}
};
DEVICE dpa_dev = {
"DPA", dp_unit, dpa_reg, dp_mod,
NUM_UNITS_DP, 8, 18, 1, 8, 36,
NULL, NULL, &dp_reset, &dp_boot, &dp_attach, &dp_detach,
&dp_dib[0], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &dp_help, NULL, NULL, &dp_description
};
#if (NUM_DEVS_DP > 1)
REG dpb_reg[] = {
{BRDATA(BUFF, &dp_buf[1][0], 16, 64, RP_NUMWD), REG_HRO},
{BRDATA(DF10, &dp_cur_unit[1], 16, 8, 1), REG_HRO},
{ORDATA(STATUS, dp_df10[1].status, 18), REG_RO},
{ORDATA(CIA, dp_df10[1].cia, 18)},
{ORDATA(CCW, dp_df10[1].ccw, 18)},
{ORDATA(WCR, dp_df10[1].wcr, 18)},
{ORDATA(CDA, dp_df10[1].cda, 18)},
{ORDATA(DEVNUM, dp_df10[1].devnum, 9), REG_HRO},
{ORDATA(BUF, dp_df10[1].buf, 36), REG_HRO},
{ORDATA(NXM, dp_df10[1].nxmerr, 8), REG_HRO},
{ORDATA(COMP, dp_df10[1].ccw_comp, 8), REG_HRO},
{0}
};
DEVICE dpb_dev = {
"DPB", &dp_unit[010], dpb_reg, dp_mod,
NUM_UNITS_DP, 8, 18, 1, 8, 36,
NULL, NULL, &dp_reset, &dp_boot, &dp_attach, &dp_detach,
&dp_dib[1], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &dp_help, NULL, NULL, &dp_description
};
#if (NUM_DEVS_DP > 2)
REG dpc_reg[] = {
{BRDATA(BUFF, &dp_buf[2][0], 16, 64, RP_NUMWD), REG_HRO},
{BRDATA(DF10, &dp_cur_unit[2], 16, 8, 1), REG_HRO},
{ORDATA(STATUS, dp_df10[2].status, 18), REG_RO},
{ORDATA(CIA, dp_df10[2].cia, 18)},
{ORDATA(CCW, dp_df10[2].ccw, 18)},
{ORDATA(WCR, dp_df10[2].wcr, 18)},
{ORDATA(CDA, dp_df10[2].cda, 18)},
{ORDATA(DEVNUM, dp_df10[2].devnum, 9), REG_HRO},
{ORDATA(BUF, dp_df10[2].buf, 36), REG_HRO},
{ORDATA(NXM, dp_df10[2].nxmerr, 8), REG_HRO},
{ORDATA(COMP, dp_df10[2].ccw_comp, 8), REG_HRO},
{0}
};
DEVICE dpc_dev = {
"DPC", &dp_unit[020], dpc_reg, dp_mod,
NUM_UNITS_DP, 8, 18, 1, 8, 36,
NULL, NULL, &dp_reset, &dp_boot, &dp_attach, &dp_detach,
&dp_dib[2], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &dp_help, NULL, NULL, &dp_description
};
#if (NUM_DEVS_DP > 3)
REG dpd_reg[] = {
{BRDATA(BUFF, &dp_buf[3][0], 16, 64, RP_NUMWD), REG_HRO},
{BRDATA(DF10, &dp_cur_unit[3], 16, 8, 1), REG_HRO},
{ORDATA(STATUS, dp_df10[3].status, 18), REG_RO},
{ORDATA(CIA, dp_df10[3].cia, 18)},
{ORDATA(CCW, dp_df10[3].ccw, 18)},
{ORDATA(WCR, dp_df10[3].wcr, 18)},
{ORDATA(CDA, dp_df10[3].cda, 18)},
{ORDATA(DEVNUM, dp_df10[3].devnum, 9), REG_HRO},
{ORDATA(BUF, dp_df10[3].buf, 36), REG_HRO},
{ORDATA(NXM, dp_df10[3].nxmerr, 8), REG_HRO},
{ORDATA(COMP, dp_df10[3].ccw_comp, 8), REG_HRO},
{0}
};
DEVICE dpd_dev = {
"DPD", &dp_unit[030], dpd_reg, dp_mod,
NUM_UNITS_DP, 8, 18, 1, 8, 36,
NULL, NULL, &dp_reset, &dp_boot, &dp_attach, &dp_detach,
&dp_dib[3], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &dp_help, NULL, NULL, &dp_description
};
#endif
#endif
#endif
DEVICE *dp_devs[] = {
&dpa_dev,
#if (NUM_DEVS_DP > 1)
&dpb_dev,
#if (NUM_DEVS_DP > 2)
&dpc_dev,
#if (NUM_DEVS_DP > 3)
&dpd_dev,
#endif
#endif
#endif
};
t_stat dp_devio(uint32 dev, uint64 *data) {
uint64 res;
int ctlr = (dev - DP_DEVNUM) >> 2;
struct df10 *df10;
DEVICE *dptr;
UNIT *uptr;
int unit;
int cyl;
int tmp;
static int sect_count;
if (ctlr < 0 || ctlr >= NUM_DEVS_DP)
return SCPE_OK;
df10 = &dp_df10[ctlr];
dptr = dp_devs[ctlr];
unit = dp_cur_unit[ctlr];
uptr = &dp_unit[(ctlr * NUM_UNITS_DP) + unit];
switch(dev & 3) {
case CONI:
*data = (uint64)(df10->status | uptr->STATUS);
#if KI_22BIT
*data |= B22_FLAG;
#endif
sim_debug(DEBUG_CONI, dptr, "DP %03o CONI %012llo %d PC=%o\n", dev,
*data, ctlr, PC);
break;
case CONO:
clr_interrupt(dev);
df10->status &= ~07LL;
df10->status |= *data & 07LL;
if (*data & BUSY) {
/* Stop controller */
sim_cancel(uptr);
uptr->STATUS &= ~BUSY;
df10_finish_op(df10, 0);
}
/* Clear flags */
uptr->STATUS &= ~(*data & CLRMSK);
if (*data & PRT_ERR)
uptr->STATUS &= ~(CLRMSK2);
if (*data & CCW_COMP) {
df10_writecw(df10);
df10->status &= ~CCW_COMP;
}
if (*data & PI_ENABLE) {
uptr->UFLAGS &= ~DONE;
/* Check if any drives still reporting seek done */
tmp = 1;
uptr = &dp_unit[ctlr * NUM_UNITS_DP];
for(unit = 0; unit < NUM_UNITS_DP; unit++) {
if (uptr->UFLAGS & SEEK_DONE) {
tmp = 0;
break;
}
uptr++;
}
if (tmp)
df10->status &= ~PI_ENABLE;
else
df10_setirq(df10);
}
sim_debug(DEBUG_CONO, dptr, "DP %03o CONO %06o %d PC=%o %06o\n", dev,
(uint32)*data, ctlr, PC, df10->status);
break;
case DATAI:
res = (uint64)(unit) << 33;
if ((dptr->flags & DEV_WHDR) == 0)
res |= WR_HD_LK; /* Can't write headers. */
if (dp_drv_tab[GET_DTYPE(uptr->flags)].devtype == RP03_DTYPE)
res |= SEL_RP03;
if (uptr->flags & UNIT_DIS) {
res |= NO_DRIVE;
} else if (uptr->flags & UNIT_ATT) {
res |= DRV_ONLINE;
cyl = uptr->CUR_CYL;
res |= ((uint64)(cyl & 0377)) << 25;
if (cyl & 0400)
res |= SEL_CYL256;
if (sect_count > 20)
sect_count = 0;
res |= SEL_SEC & (uint64)(sect_count << 13);
sect_count++;
if ((uptr->UFLAGS & SEEK_STATE) == 0)
res |= ON_CYL;
if (uptr->flags & UNIT_WPRT)
res |= RD_ONLY|WR_HD_LK;
}
uptr = &dp_unit[ctlr * NUM_UNITS_DP];
for(unit = 0; unit < NUM_UNITS_DP; unit++) {
if (uptr->UFLAGS & SEEK_DONE)
res |= 0400>>unit;
uptr++;
}
sim_debug(DEBUG_DATAIO, dptr, "DP %03o DATI %012llo %d PC=%o F=%o %o\n",
dev, res, ctlr, PC, uptr->UFLAGS, sect_count);
*data = res;
break;
case DATAO:
sim_debug(DEBUG_DATAIO, dptr, "DP %03o DATO %012llo, %d PC=%o\n",
dev, *data, ctlr, PC);
if (df10->status & BUSY) {
uptr->STATUS |= ILL_CMD;
return SCPE_OK;
}
clr_interrupt(dev);
df10->status &= ~(PI_ENABLE|CCW_COMP);
unit = (*data >> 30) & 07;
dp_cur_unit[ctlr] = unit;
uptr = &dp_unit[(ctlr * NUM_UNITS_DP) + unit];
if ((uptr->STATUS & NOT_RDY) == 0) {
uptr->STATUS &= ~(SUF_ERR|SEC_ERR|SRC_ERR|NXM_ERR|ILL_WR|
NO_DRIVE|NOT_RDY|ILL_CMD|END_CYL|SRC_DONE);
}
cyl = ((*data >> 22) & 0377);
if (*data & CYL256)
cyl += 0400;
tmp = (*data >> 33) & 07;
switch(tmp) {
case WH:
if ((dptr->flags & DEV_WHDR) == 0) {
uptr->UFLAGS |= DONE;
uptr->STATUS |= ILL_WR;
df10_setirq(df10);
return SCPE_OK;
}
*data &= ~SECTOR; /* Clear sector */
/* Fall through */
case WR:
if (uptr->flags & UNIT_WPRT) {
uptr->UFLAGS |= DONE;
uptr->STATUS |= ILL_WR;
df10_setirq(df10);
return SCPE_OK;
}
/* Fall through */
case RD:
case RV:
if (uptr->flags & UNIT_DIS) {
uptr->UFLAGS |= DONE;
uptr->STATUS |= NO_DRIVE;
df10_setirq(df10);
return SCPE_OK;
}
if ((uptr->flags & UNIT_ATT) == 0) {
uptr->UFLAGS |= DONE;
uptr->STATUS |= NOT_RDY;
df10_setirq(df10);
return SCPE_OK;
}
uptr->UFLAGS = ((*data & (SURFACE|SECTOR)) >> 3) | (cyl << 20)
| (tmp << 3) | ctlr;
uptr->DATAPTR = 0; /* Set no data */
CLR_BUF(uptr);
df10_setup(df10, (uint32)*data);
uptr->STATUS |= BUSY;
break;
case RC:
cyl = 0;
/* Fall through */
case SK:
uptr->STATUS |= NOT_RDY;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_OK;
uptr->UFLAGS = (cyl << 20) | (tmp<<3) | ctlr | SEEK_STATE;
break;
case CL:
uptr->UFLAGS &= ~DONE;
uptr = &dp_unit[ctlr * NUM_UNITS_DP];
for(unit = 0; unit < NUM_UNITS_DP; unit++) {
if (*data & (0400 >> unit))
uptr->UFLAGS &= ~(SEEK_DONE);
uptr++;
}
/* Fall through */
case NO:
tmp = 0;
uptr = &dp_unit[ctlr * NUM_UNITS_DP];
for(unit = 0; unit < NUM_UNITS_DP; unit++) {
if (uptr->UFLAGS & SEEK_DONE) {
tmp = 1;
break;
}
uptr++;
}
if (tmp) {
df10_setirq(df10);
}
return SCPE_OK;
}
sim_activate(uptr, 150);
}
return SCPE_OK;
}
t_stat dp_svc (UNIT *uptr)
{
int dtype = GET_DTYPE(uptr->flags);
int ctlr = uptr->UFLAGS & 03;
int cmd = (uptr->UFLAGS & 070) >> 3;
int sect = (uptr->UFLAGS >> 9);
int surf = (sect >> 5) & 037;
int cyl = (uptr->UFLAGS >> 20) & 0777;
DEVICE *dptr = dp_devs[ctlr];
struct df10 *df10 = &dp_df10[ctlr];
int diff, diffs, wc;
int r;
sect &= 017;
switch(cmd) {
case WR:
case RV:
case RD:
/* Cylinder, Surface, Sector all ok */
if (BUF_EMPTY(uptr)) {
sim_debug(DEBUG_DETAIL, dptr,
"DP %d cmd=%o cyl=%d (%o) sect=%d surf=%d %d\n",
ctlr, uptr->UFLAGS, cyl, cyl, sect, surf,uptr->CUR_CYL);
uptr->STATUS |= SRC_DONE;
if (uptr->STATUS & END_CYL) {
if (cmd == WR) {
if(df10_read(df10))
df10_read(df10);
}
uptr->UFLAGS |= DONE;
uptr->STATUS &= ~BUSY;
df10_finish_op(df10, 0);
return SCPE_OK;
}
if (sect >= dp_drv_tab[dtype].sect) {
uptr->STATUS &= ~BUSY;
uptr->STATUS |= SEC_ERR;
}
if (surf >= dp_drv_tab[dtype].surf) {
uptr->STATUS &= ~BUSY;
uptr->STATUS |= SUF_ERR;
}
if (cyl != uptr->CUR_CYL) {
uptr->STATUS &= ~BUSY;
uptr->STATUS |= SRC_ERR;
}
if ((uptr->STATUS & BUSY) == 0) {
uptr->UFLAGS |= DONE;
df10_finish_op(df10, 0);
return SCPE_OK;
}
if (cmd != WR) {
/* Read the block */
int da = ((cyl * dp_drv_tab[dtype].surf + surf)
* dp_drv_tab[dtype].sect + sect) * RP_NUMWD;
(void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fread (&dp_buf[ctlr][0], sizeof(uint64), RP_NUMWD,
uptr->fileref);
for (; wc < RP_NUMWD; wc++)
dp_buf[ctlr][wc] = 0;
uptr->hwmark = RP_NUMWD;
uptr->DATAPTR = 0;
sect = sect + 1;
if (sect >= dp_drv_tab[dtype].sect) {
sect = 0;
surf = surf + 1;
if (surf >= dp_drv_tab[dtype].surf) {
uptr->STATUS |= END_CYL;
} else {
uptr->UFLAGS &= ~(01757000);
uptr->UFLAGS |= (surf << 14);
}
} else {
uptr->UFLAGS &= ~(017000);
uptr->UFLAGS |= (sect << 9);
}
} else {
uptr->DATAPTR = 0;
uptr->hwmark = 0;
}
sim_activate(uptr, 50);
return SCPE_OK;
}
switch(cmd) {
case WR:
r = df10_read(df10);
if (r)
uptr->hwmark = uptr->DATAPTR;
dp_buf[ctlr][uptr->DATAPTR] = df10->buf;
break;
case RV:
case RD:
df10->buf = dp_buf[ctlr][uptr->DATAPTR];
r = df10_write(df10);
break;
}
sim_debug(DEBUG_DATA, dptr, "Xfer %d %08o %012llo %08o\n",
uptr->DATAPTR, df10->cda, df10->buf, df10->wcr);
uptr->DATAPTR++;
if (uptr->DATAPTR >= RP_NUMWD || r == 0 ) {
if (cmd == WR) {
int da = ((cyl * dp_drv_tab[dtype].surf + surf)
* dp_drv_tab[dtype].sect + sect) * RP_NUMWD;
/* write block the block */
for (; uptr->DATAPTR < RP_NUMWD; uptr->DATAPTR++)
dp_buf[ctlr][uptr->DATAPTR] = 0;
(void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fwrite(&dp_buf[ctlr][0],sizeof(uint64), RP_NUMWD,
uptr->fileref);
uptr->STATUS |= SRC_DONE;
sect = sect + 1;
if (sect >= dp_drv_tab[dtype].sect) {
sect = 0;
surf = surf + 1;
if (surf >= dp_drv_tab[dtype].surf) {
uptr->STATUS |= END_CYL;
} else {
uptr->UFLAGS &= ~(01757 << 9);
uptr->UFLAGS |= (surf << 14);
}
} else {
uptr->UFLAGS &= ~(017 << 9);
uptr->UFLAGS |= (sect << 9);
}
}
uptr->DATAPTR = 0;
CLR_BUF(uptr);
}
if (r)
sim_activate(uptr, 25);
else {
uptr->STATUS &= ~(SRC_DONE|END_CYL|BUSY);
uptr->UFLAGS |= DONE;
return SCPE_OK;
}
break;
case WH:
/* Cylinder, Surface, Sector all ok */
if (BUF_EMPTY(uptr)) {
if (uptr->DATAPTR == 0)
sim_debug(DEBUG_DETAIL, dptr,
"DP %d cmd=%o cyl=%d (%o) sect=%d surf=%d %d\n",
ctlr, uptr->UFLAGS, cyl, cyl, sect, surf,uptr->CUR_CYL);
uptr->STATUS |= SRC_DONE;
if (uptr->STATUS & END_CYL) {
if(df10_read(df10))
df10_read(df10);
uptr->UFLAGS |= DONE;
uptr->STATUS &= ~BUSY;
df10_finish_op(df10, 0);
return SCPE_OK;
}
if (sect >= dp_drv_tab[dtype].sect) {
uptr->STATUS &= ~BUSY;
uptr->STATUS |= SEC_ERR;
}
if (surf >= dp_drv_tab[dtype].surf) {
uptr->STATUS &= ~BUSY;
uptr->STATUS |= SUF_ERR;
}
if (cyl != uptr->CUR_CYL) {
uptr->STATUS &= ~BUSY;
uptr->STATUS |= SRC_ERR;
}
if ((uptr->STATUS & BUSY) == 0) {
uptr->UFLAGS |= DONE;
df10_finish_op(df10, 0);
return SCPE_OK;
}
r = df10_read(df10);
uptr->DATAPTR++;
sim_debug(DEBUG_DATA, dptr, "Xfer h%d %012llo\n",
uptr->DATAPTR, df10->buf);
if (uptr->DATAPTR == 36) {
uptr->DATAPTR = 0;
uptr->hwmark = 0;
}
} else {
r = df10_read(df10);
if (r)
uptr->hwmark = uptr->DATAPTR;
dp_buf[ctlr][uptr->DATAPTR] = (df10->buf << 1) & FMASK;
sim_debug(DEBUG_DATA, dptr, "Xfer %d %012llo\n",
uptr->DATAPTR, df10->buf);
uptr->DATAPTR++;
if (uptr->DATAPTR >= RP_NUMWD || r == 0 ) {
int da = ((cyl * dp_drv_tab[dtype].surf + surf)
* dp_drv_tab[dtype].sect + sect) * RP_NUMWD;
/* write block the block */
for (; uptr->DATAPTR < RP_NUMWD; uptr->DATAPTR++)
dp_buf[ctlr][uptr->DATAPTR] = 0;
(void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fwrite(&dp_buf[ctlr][0],sizeof(uint64), RP_NUMWD,
uptr->fileref);
uptr->STATUS |= SRC_DONE;
sect = sect + 1;
if (sect >= dp_drv_tab[dtype].sect) {
uptr->STATUS |= END_CYL;
} else {
uptr->UFLAGS &= ~(017 << 9);
uptr->UFLAGS |= (sect << 9);
}
uptr->DATAPTR = 0;
CLR_BUF(uptr);
}
}
if (r)
sim_activate(uptr, 25);
else {
uptr->STATUS &= ~(SRC_DONE|END_CYL|BUSY);
uptr->UFLAGS |= DONE;
return SCPE_OK;
}
break;
case CL:
case NO:
return SCPE_OK;
case RC:
case SK:
if(uptr->UFLAGS & SEEK_STATE) {
diff = cyl - uptr->CUR_CYL;
diffs = (diff < 0) ? -1 : 1;
sim_debug(DEBUG_DETAIL, dptr, "DP Seek %d %d %d %d\n",
ctlr, cyl, uptr->CUR_CYL, diff);
if (diff == 0) {
uptr->UFLAGS |= SEEK_DONE;
uptr->UFLAGS &= ~SEEK_STATE;
uptr->STATUS &= ~(NOT_RDY);
if ((df10->status & BUSY) == 0)
df10_setirq(df10);
} else if (diff < 10 && diff > -10) {
uptr->CUR_CYL += diffs;
if (uptr->CUR_CYL < 0) {
uptr->UFLAGS |= SEEK_DONE;
uptr->UFLAGS &= ~SEEK_STATE;
uptr->STATUS &= ~(NOT_RDY);
uptr->CUR_CYL = 0;
if ((df10->status & BUSY) == 0)
df10_setirq(df10);
} else if (uptr->CUR_CYL > dp_drv_tab[dtype].cyl) {
uptr->UFLAGS |= SEEK_DONE;
uptr->UFLAGS &= ~SEEK_STATE;
uptr->STATUS &= ~(NOT_RDY);
uptr->CUR_CYL = dp_drv_tab[dtype].cyl;
if ((df10->status & BUSY) == 0)
df10_setirq(df10);
} else
sim_activate(uptr, 500);
} else if (diff > 100 || diff < -100) {
uptr->CUR_CYL += diffs * 100;
sim_activate(uptr, 4000);
} else {
uptr->CUR_CYL += diffs * 10;
sim_activate(uptr, 1000);
}
}
}
return SCPE_OK;
}
t_stat
dp_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int i;
if (uptr == NULL) return SCPE_IERR;
for (i = 0; dp_drv_tab[i].sect != 0; i++) {
if (GET_DTYPE(val) == dp_drv_tab[i].devtype) {
uptr->flags &= ~(UNIT_DTYPE);
uptr->flags |= val;
uptr->capac = dp_drv_tab[i].size;
return SCPE_OK;
}
}
return SCPE_IERR;
}
t_stat
dp_set_hdr(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
dptr = find_dev_from_unit (uptr);
if (dptr == NULL)
return SCPE_IERR;
dptr->flags &= ~DEV_WHDR;
dptr->flags |= val;
return SCPE_OK;
}
t_stat dp_show_hdr (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
DEVICE *dptr;
if (uptr == NULL)
return SCPE_IERR;
dptr = find_dev_from_unit(uptr);
if (dptr == NULL)
return SCPE_IERR;
fprintf (st, "%s", (dptr->flags & DEV_WHDR) ? "HEADERS" : "NOHEADERS");
return SCPE_OK;
}
t_stat
dp_reset(DEVICE * dptr)
{
int unit;
int ctlr;
UNIT *uptr = dptr->units;
for(unit = 0; unit < NUM_UNITS_DP; unit++) {
uptr->UFLAGS = 0;
uptr->STATUS = 0;
uptr->CUR_CYL = 0;
uptr++;
}
for (ctlr = 0; ctlr < NUM_DEVS_DP; ctlr++) {
dp_df10[ctlr].status = 0;
dp_df10[ctlr].devnum = dp_dib[ctlr].dev_num;
dp_df10[ctlr].nxmerr = 12;
dp_df10[ctlr].ccw_comp = 5;
}
return SCPE_OK;
}
/* Boot from given device */
t_stat
dp_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
uint32 addr;
uint32 ptr;
int sect;
int wc;
addr = (MEMSIZE - 512) & RMASK;
for (sect = 4; sect <= 7; sect++) {
(void)sim_fseek(uptr->fileref, (sect * RP_NUMWD) * sizeof(uint64), SEEK_SET);
(void)sim_fread (&dp_buf[0][0], sizeof(uint64), RP_NUMWD, uptr->fileref);
ptr = 0;
for(wc = RP_NUMWD; wc > 0; wc--)
M[addr++] = dp_buf[0][ptr++];
}
PC = (MEMSIZE - 512) & RMASK;
return SCPE_OK;
}
/* Device attach */
t_stat dp_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
DEVICE *dptr;
DIB *dib;
int ctlr;
uptr->capac = dp_drv_tab[GET_DTYPE (uptr->flags)].size;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
return r;
dptr = find_dev_from_unit(uptr);
if (dptr == 0)
return SCPE_OK;
dib = (DIB *) dptr->ctxt;
ctlr = dib->dev_num & 014;
uptr->CUR_CYL = 0;
uptr->UFLAGS = (NO << 3) | SEEK_DONE | (ctlr >> 2);
dp_df10[ctlr].status |= PI_ENABLE;
set_interrupt(DP_DEVNUM + (ctlr), dp_df10[ctlr >> 2].status);
return SCPE_OK;
}
/* Device detach */
t_stat dp_detach (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK;
if (sim_is_active (uptr)) /* unit active? */
sim_cancel (uptr); /* cancel operation */
return detach_unit (uptr);
}
t_stat dp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "RP10 RP01/2/3 Disk Pack Drives (DP)\n\n");
fprintf (st, "The DP controller implements the RP10 disk drives. RP\n");
fprintf (st, "options include the ability to set units write enabled or write locked, to\n");
fprintf (st, "set the drive type to one of three disk types.\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprintf (st, "\nThe type options can be used only when a unit is not attached to a file.\n");
fprintf (st, "The RP device supports the BOOT command.\n");
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *dp_description (DEVICE *dptr)
{
return "RP10 disk controller";
}
#endif

442
PDP10/kx10_dpy.c Normal file
View file

@ -0,0 +1,442 @@
/* ka10_dpy.c: 340 display subsystem simulator w/ PDP-6 344 interface!
Copyright (c) 2018, Philip L. Budne
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
PHILIP BUDNE 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 Philip Budne shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
/*
* NOTE!!! Currently only supports 340 display using the type 344 interface
* for PDP-6 described in
* http://www.bitsavers.org/pdf/dec/graphics/H-340_Type_340_Precision_Incremental_CRT_System_Nov64.pdf
*
* 340C was used in the PDP-10 VB10C display system
* http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/pdp10/periph/VB10C_Interactive_Graphics_Terminal_Jul70.pdf
* "The basic hardware system consists of a 340/C display connected
* directly to PDP-1O memory through a special memory channel. Several
* important features included in the VB-10/C display are memory
* protection and relocation, slave mode operation, raster mode, and
* subroutining."
*
* reading 6.03 VBCSER
* http://pdp-10.trailing-edge.com/dec-10-omona-u-mc9/01/vbcser.mac.html
* There appear to be differences in the DIS (130) CONI/O bits:
* CONI
* 47400 are "DISPLAY INTERRUPT" bits
* 45000 are "ILLEGAL ADDRESS OR EDGE FLAG" (same VE/HE??)
* 40000 is "ILLEGAL ADDR"
* 2000 is LP HIT (same)
* 400 is STOP (same
* CONO
* 20000 "lock display out of memory"
* 100 init? (same)
* 40 "clear flags"
* 20 "resume" (stored @DISIN4)
* 7 PI channel?
* DISCON = CHAN + 140 (continue?)
* *NO* DATAO or BLKO to device 130!
*
* It appears that the reloc/protect mechanism is on I/O device 134.
* (referred to by number, not symbol!)
* DATAO sets reloc/protect, start addr
* possibly:
* high order 8 protection bits are left justified in left half
* high order 8 relocation bits are left justified in right half
*
* Other PDP-6/10 display interfaces:
*
* http://bitsavers.trailing-edge.com/pdf/dec/graphics/348_Manual_1964.pdf
* Type 348 interface to Type 30A or 30E displays.
* "To the display, the interface looks like a PDP-1 computer"
*
* Also VP10/VR30 (phone book p. 487):
* control word format:
* INTENSITY(*),,0 ("4 dimmest, thru 13 brightest" default is 10)
* YPOS,,XPOS (10 bit positions)
*
* (*)6.03 DISSER.MAC says:
* ONLY FOR VP10 and TYPE 30.
* N IS 3 BITS WIDE FOR 30, 2 BITS WIDE FOR VP10.
*
* 348 manual p.10 says: "The three flip-flops are treated as a two bit
* signed binary number. Negative numbers are in two's complement form.
* The most negative number (100) will produce the least intensity.
* The largest positive number (011) results in the greatest intensity.
*/
#include "kx10_defs.h"
#include <time.h>
#ifndef NUM_DEVS_DPY
#define NUM_DEVS_DPY 0
#endif
#if (NUM_DEVS_DPY > 0)
#include "display/type340.h"
#include "display/display.h"
#define DPY_DEVNUM 0130
#define RRZ(W) ((W) & RMASK)
#define XWD(L,R) ((((uint64)(L))<<18)|((uint64)(R)))
#if PDP6 | KA | KI
extern uint64 SW; /* switch register */
#endif
/*
* number of (real?) microseconds between svc calls
* used to age display, poll for WS events
* and delay "data" interrupt
* (VB10C could steal cycles)
*/
#define DPY_CYCLE_US 50
/*
* number of DPY_CYCLES to delay int
* too small and host CPU doesn't run enough!
*/
#define INT_COUNT (500/DPY_CYCLE_US)
#define STAT_REG u3
#define INT_COUNTDOWN u4
#define XPOS us9 /* from LP hit */
#define YPOS us10 /* from LP hit */
/* STAT_REG */
#define STAT_VALID 01000000 /* internal: invisible to PDP-10 */
/* CONI/CONO */
/* http://www.bitsavers.org/pdf/dec/graphics/H-340_Type_340_Precision_Incremental_CRT_System_Nov64.pdf p 2-14 */
#define CONO_MASK 0000077 /* bits changed by CONO */
#define CONI_MASK 0007677 /* bits read by CONI */
#define CONI_INT_SPEC 0007400 /* I- "special conditions" */
#define CONI_INT_VE 0004000 /* I- b24: VER EDGE */
#define CONI_INT_LP 0002000 /* I- b25: LIGHT PEN */
#define CONI_INT_HE 0001000 /* I- b26: HOR EDGE */
#define CONI_INT_SI 0000400 /* I- b27: STOP INT */
#define CONI_INT_DONE 0000200 /* I- b28: done with second half */
#define CONO_INIT 0000100 /* -O b29: init display */
#define CONX_SC 0000070 /* IO special channel */
#define CONX_DC 0000007 /* IO data channel */
#define CONX_SC_SHIFT 3
#define CONX_DC_SHIFT 0
/* make sure ST340_XXX bits match CONI_INT_XXX bits */
#if (ST340_VEDGE^CONI_INT_VE)|(ST340_LPHIT^CONI_INT_LP)|(ST340_HEDGE^CONI_INT_HE)|(ST340_STOP_INT^CONI_INT_SI)
#error ST340 bits do not match CONI_INT bits!!
#endif
t_stat dpy_devio(uint32 dev, uint64 *data);
t_stat dpy_svc (UNIT *uptr);
t_stat dpy_reset (DEVICE *dptr);
const char *dpy_description (DEVICE *dptr);
DIB dpy_dib[] = {
{ DPY_DEVNUM, 1, &dpy_devio, NULL }};
UNIT dpy_unit[] = {
{ UDATA (&dpy_svc, UNIT_IDLE, DPY_CYCLE_US) }
};
#define UPTR(UNIT) (dpy_unit+(UNIT))
DEVICE dpy_dev = {
"DPY", dpy_unit, NULL, NULL,
NUM_DEVS_DPY, 0, 0, 0, 0, 0,
NULL, NULL, dpy_reset,
NULL, NULL, NULL,
&dpy_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL,
NULL, NULL, NULL, NULL, NULL, &dpy_description
};
const char *dpy_description (DEVICE *dptr)
{
return "Type 340 Display on Type 344 interface";
}
/* until it's done just one place! */
static void dpy_set_int_done(UNIT *uptr)
{
uptr->STAT_REG |= CONI_INT_DONE;
uptr->INT_COUNTDOWN = INT_COUNT;
}
/* return true if display not stopped */
int dpy_update_status (UNIT *uptr, ty340word status, int done)
{
int running = !(status & ST340_STOPPED);
/* sub in lastest bits from display */
uptr->STAT_REG &= ~CONI_INT_SPEC;
uptr->STAT_REG |= status & CONI_INT_SPEC;
/* data interrupt sent from svc routine, so CPU can run */
if (done && running) {
/* XXX also set in "rfd" callback: decide! */
dpy_set_int_done(uptr);
}
if (uptr->STAT_REG & CONI_INT_SPEC) {
uint32 sc = uptr->STAT_REG & CONX_SC;
if (sc) { /* PI channel set? */
set_interrupt(DPY_DEVNUM, sc >> CONX_SC_SHIFT);
}
}
return running;
}
t_stat dpy_devio(uint32 dev, uint64 *data) {
int unit = (dev - DPY_DEVNUM) >> 2;
UNIT *uptr;
int32 inst;
if (unit < 0 || unit >= NUM_DEVS_DPY)
return SCPE_OK;
uptr = UPTR(unit);
if (!(uptr->STAT_REG & STAT_VALID)) {
dpy_update_status(uptr, ty340_status(), 0);
sim_activate_after(uptr, DPY_CYCLE_US);
uptr->STAT_REG |= STAT_VALID;
uptr->INT_COUNTDOWN = 0;
}
switch (dev & 3) {
case CONI:
*data = (uint64)(uptr->STAT_REG & CONI_MASK);
/*
* MIT AI only, See Hardware Memo 1
* https://github.com/larsbrinkhoff/its-archives/blob/master/ailab/ITS_Hardware_Memo_1.pdf
* Set sign bit if device assigned to this CPU (KA or PDP-6)
* (Thanks to Lars for figuring this out!)
*/
*data |= SMASK; /* always assigned to us */
sim_debug(DEBUG_CONI, &dpy_dev, "DPY %03o CONI PC=%06o %012llo\n",
dev, PC, *data);
break;
case CONO:
clr_interrupt(dev);
uptr->STAT_REG &= ~CONO_MASK;
uptr->STAT_REG |= *data & CONO_MASK;
if (*data & CONO_INIT)
dpy_update_status( uptr, ty340_reset(&dpy_dev), 1);
sim_debug(DEBUG_CONO, &dpy_dev, "DPY %03o CONO %06o PC=%06o %06o\n",
dev, (uint32)*data, PC, uptr->STAT_REG & ~STAT_VALID);
break;
case DATAO:
uptr->STAT_REG &= ~CONI_INT_DONE;
uptr->INT_COUNTDOWN = 0;
/* if fed using BLKO from interrupt vector, PC will be wrong! */
sim_debug(DEBUG_DATAIO, &dpy_dev, "DPY %03o DATO %012llo PC=%06o\n",
dev, *data, PC);
inst = (uint32)LRZ(*data);
if (dpy_update_status(uptr, ty340_instruction(inst), 0)) {
/* still running */
inst = (uint32)RRZ(*data);
dpy_update_status(uptr, ty340_instruction(inst), 1);
}
break;
case DATAI:
*data = XWD(uptr->YPOS, uptr->XPOS);
sim_debug(DEBUG_DATAIO, &dpy_dev, "DPY %03o DATI %06o,,%06o PC=%06o\n",
dev, uptr->YPOS, uptr->XPOS, PC);
break;
}
return SCPE_OK;
}
/* Timer service - */
t_stat dpy_svc (UNIT *uptr)
{
sim_activate_after(uptr, DPY_CYCLE_US); /* requeue! */
display_age(DPY_CYCLE_US, 0); /* age the display */
if (uptr->INT_COUNTDOWN && --uptr->INT_COUNTDOWN == 0) {
if (uptr->STAT_REG & CONI_INT_DONE) { /* delayed int? */
uint32 dc = uptr->STAT_REG & CONX_DC;
if (dc) { /* PI channel set? */
set_interrupt(DPY_DEVNUM, dc>>CONX_DC_SHIFT);
}
}
}
return SCPE_OK;
}
/* Reset routine */
t_stat dpy_reset (DEVICE *dptr)
{
if (!(dptr->flags & DEV_DIS)) {
display_reset();
ty340_reset(dptr);
}
sim_cancel (&dpy_unit[0]); /* deactivate unit */
return SCPE_OK;
}
/****************
* callbacks from type340.c
*/
/* not used with Type 344 interface */
ty340word
ty340_fetch(ty340word addr)
{
return 0;
}
/* not used with Type 344 interface */
void
ty340_store(ty340word addr, ty340word value)
{
}
void
ty340_lp_int(ty340word x, ty340word y)
{
/*
* real hardware pauses display until the CPU reads out coords
* w/ DATAI which then continues the display
*/
dpy_unit[0].XPOS = x;
dpy_unit[0].YPOS = y;
dpy_update_status(dpy_unit, ty340_status(), 0);
}
void
ty340_rfd(void) { /* request for data */
#ifdef TY340_NODISPLAY
puts("ty340_rfd");
#endif
dpy_set_int_done(dpy_unit);
}
void
cpu_get_switches(unsigned long *p1, unsigned long *p2) {
#if PDP6 | KA | KI
*p1 = LRZ(SW);
*p2 = RRZ(SW);
#endif
}
void
cpu_set_switches(unsigned long w1, unsigned long w2) {
#if PDP6 | KA | KI
SW = XWD(w1,w2);
#endif
}
/*
* MIT Spacewar console switches
* WCNSLS is the mnemonic defined/used in the SPCWAR sources
*/
#if NUM_DEVS_WCNSLS > 0
#define WCNSLS_DEVNUM 0420
t_stat wcnsls_devio(uint32 dev, uint64 *data);
const char *wcnsls_description (DEVICE *dptr);
DIB wcnsls_dib[] = {
{ WCNSLS_DEVNUM, 1, &wcnsls_devio, NULL }};
UNIT wcnsls_unit[] = {
{ UDATA (NULL, UNIT_IDLE, 0) }};
DEVICE wcnsls_dev = {
"WCNSLS", wcnsls_unit, NULL, NULL,
NUM_DEVS_WCNSLS, 0, 0, 0, 0, 0,
NULL, NULL, NULL,
NULL, NULL, NULL,
&wcnsls_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL,
NULL, NULL, NULL, NULL, NULL, &wcnsls_description
};
const char *wcnsls_description (DEVICE *dptr)
{
return "MIT Spacewar Consoles";
}
t_stat wcnsls_devio(uint32 dev, uint64 *data) {
uint64 switches;
switch (dev & 3) {
case CONO:
/* CONO WCNSLS,40 ;enable spacewar consoles */
break;
case DATAI:
switches = 0777777777777LL; /* 1 is off */
/*
* map 32-bit "spacewar_switches" value to what PDP-6/10 game expects
* (four 9-bit bytes)
*/
/* bits inside the bytes */
#define CCW 0400 /* counter clockwise (L) */
#define CW 0200 /* clockwise (R) */
#define THRUST 0100
#define HYPER 040
#define FIRE 020
/* shift values for the players' bytes */
#define UR 0 /* upper right: enterprise "top plug" */
#define LR 9 /* lower right: klingon "second plug" */
#define LL 18 /* lower left: thin ship "third plug" */
#define UL 27 /* upper left: fat ship "bottom plug" */
#if 1
#define DEBUGSW(X) (void)0
#else
#define DEBUGSW(X) printf X
#endif
#define SWSW(UC, LC, BIT, POS36, FUNC36) \
if (spacewar_switches & BIT) { \
switches &= ~(((uint64)FUNC36)<<POS36); \
DEBUGSW(("mapping %#o %s %s to %03o<<%d\r\n", \
(uint32)BIT, #POS36, #FUNC36, FUNC36, POS36)); \
}
SPACEWAR_SWITCHES;
#undef SWSW
if (spacewar_switches)
DEBUGSW(("in %#lo out %#llo\r\n", spacewar_switches, switches));
*data = switches;
sim_debug(DEBUG_DATAIO, &wcnsls_dev, "WCNSLS %03o DATI %012llo PC=%06o\n",
dev, switches, PC);
break;
}
return SCPE_OK;
}
#endif
#endif

1266
PDP10/kx10_dt.c Normal file

File diff suppressed because it is too large Load diff

2186
PDP10/kx10_imp.c Normal file

File diff suppressed because it is too large Load diff

170
PDP10/kx10_lights.c Normal file
View file

@ -0,0 +1,170 @@
/* ka10_lights.c: KA10 console lights.
Copyright (c) 2018, Lars Brinkhoff
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
RICHARD CORNWELL 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.
*/
#include <libusb-1.0/libusb.h>
#include "kx10_defs.h"
static libusb_device_handle *lights_handle = NULL;
static uint64 lights_main = 0;
static int lights_aux = 0;
static void ka10_lights_latch (void)
{
unsigned char buffer[8];
if (lights_handle == NULL)
return;
buffer[0] = (lights_main >> 32) & 0377;
buffer[1] = (lights_main >> 24) & 0377;
buffer[2] = (lights_main >> 16) & 0377;
buffer[3] = (lights_main >> 8) & 0377;
buffer[4] = lights_main & 0377;
buffer[5] = (lights_aux << 4) & 0340;
buffer[6] = 0;
buffer[7] = 0;
libusb_control_transfer(lights_handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
LIBUSB_REQUEST_SET_CONFIGURATION,
0x0000,
0,
buffer,
sizeof buffer,
5000);
}
void ka10_lights_main (uint64 data)
{
lights_main = data;
ka10_lights_latch ();
}
void ka10_lights_set_aux (int n)
{
lights_aux |= 1 << n;
ka10_lights_latch ();
}
void ka10_lights_clear_aux (int n)
{
lights_aux &= ~(1 << n);
ka10_lights_latch ();
}
#define USB_CFG_VENDOR_ID 0xc0, 0x16
#define USB_CFG_DEVICE_ID 0xdf, 0x05
#define USB_CFG_DEVICE_NAME 'P','a','n','d','a',' ','D','i','s','p','l','a','y',
#define USB_CFG_DEVICE_NAME_LEN 13
static libusb_device_handle *get_panda_handle(libusb_device **devs)
{
libusb_device *dev;
libusb_device_handle *handle = NULL;
int i = 0;
int r;
int found = 0;
int openable = 0;
unsigned char prod[256];
char devname[USB_CFG_DEVICE_NAME_LEN] = {USB_CFG_DEVICE_NAME};
unsigned char rawVid[2] = {USB_CFG_VENDOR_ID};
unsigned char rawPid[2] = {USB_CFG_DEVICE_ID};
int vid = rawVid[0] + 256 * rawVid[1];
int pid = rawPid[0] + 256 * rawPid[1];
while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(dev, &desc); /* this always succeeds */
// Do the VID and PID match?
if (desc.idVendor == vid && desc.idProduct == pid) {
found = 1;
r = libusb_open(dev, &handle);
// If we can't open it, keep trying.
// There may be a device with the same pid and vid but not a Panda Display
if (r < 0) {
continue;
}
openable = 1;
r = libusb_get_string_descriptor_ascii(handle, desc.iProduct, prod, sizeof prod);
if (r < 0) {
libusb_close(handle);
return NULL;
}
// Here we have something that matches the free
// VID and PID offered by Objective Development.
// Now we need to Check device name to see if it
// really is a Panda Display.
if ((0 == strncmp((char *)prod, devname, USB_CFG_DEVICE_NAME_LEN)) &&
(desc.idVendor == vid) &&
(desc.idProduct == pid)) {
return handle;
}
libusb_close(handle);
}
}
if (found) {
if (openable)
sim_messagef (SCPE_NOFNC, "Found USB device matching 16c0:05df, but it isn't a Panda Display\n");
else
sim_messagef (SCPE_NOFNC, "Found something that might be a Panda Display, but couldn't open it.\n");
}
return NULL;
}
void ka10_lights_init (void)
{
libusb_device **devs;
libusb_context *ctx = NULL;
ssize_t cnt;
int r, i, pos;
if (lights_handle != NULL)
return;
r = libusb_init(&ctx);
if (r < 0)
return;
cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0)
return;
lights_handle = get_panda_handle(devs);
if (lights_handle == NULL)
return;
if (libusb_kernel_driver_active(lights_handle, 0) == 1)
libusb_detach_kernel_driver(lights_handle, 0);
r = libusb_claim_interface(lights_handle, 0);
if(r < 0)
return;
}

408
PDP10/kx10_lp.c Normal file
View file

@ -0,0 +1,408 @@
/* ka10_lp.c: PDP-10 line printer simulator
Copyright (c) 2011-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
#include "kx10_defs.h"
#include <ctype.h>
#ifndef NUM_DEVS_LP
#define NUM_DEVS_LP 0
#endif
#if (NUM_DEVS_LP > 0)
#define LP_DEVNUM 0124
#define STATUS u3
#define COL u4
#define POS u5
#define LINE u6
#define UNIT_V_CT (UNIT_V_UF + 0)
#define UNIT_UC (1 << UNIT_V_CT)
#define UNIT_UTF8 (2 << UNIT_V_CT)
#define UNIT_CT (3 << UNIT_V_CT)
#define PI_DONE 000007
#define PI_ERROR 000070
#define DONE_FLG 000100
#define BUSY_FLG 000200
#define ERR_FLG 000400
#define CLR_LPT 002000
#define C96 002000
#define C128 004000
#define DEL_FLG 0100000
t_stat lpt_devio(uint32 dev, uint64 *data);
t_stat lpt_svc (UNIT *uptr);
t_stat lpt_reset (DEVICE *dptr);
t_stat lpt_attach (UNIT *uptr, CONST char *cptr);
t_stat lpt_detach (UNIT *uptr);
t_stat lpt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *lpt_description (DEVICE *dptr);
char lpt_buffer[134 * 3];
uint8 lpt_chbuf[5]; /* Read in Character buffers */
/* LPT data structures
lpt_dev LPT device descriptor
lpt_unit LPT unit descriptor
lpt_reg LPT register list
*/
DIB lpt_dib = { LP_DEVNUM, 1, &lpt_devio, NULL };
UNIT lpt_unit = {
UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), 100
};
REG lpt_reg[] = {
{ DRDATA (STATUS, lpt_unit.STATUS, 18), PV_LEFT | REG_UNIT },
{ DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT | REG_UNIT },
{ BRDATA(BUFF, lpt_buffer, 16, 8, sizeof(lpt_buffer)), REG_HRO},
{ BRDATA(CBUFF, lpt_chbuf, 16, 8, sizeof(lpt_chbuf)), REG_HRO},
{ NULL }
};
MTAB lpt_mod[] = {
{UNIT_CT, 0, "Lower case", "LC", NULL},
{UNIT_CT, UNIT_UC, "Upper case", "UC", NULL},
{UNIT_CT, UNIT_UTF8, "UTF8 ouput", "UTF8", NULL},
{ 0 }
};
DEVICE lpt_dev = {
"LPT", &lpt_unit, lpt_reg, lpt_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &lpt_reset,
NULL, &lpt_attach, &lpt_detach,
&lpt_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &lpt_help, NULL, NULL, &lpt_description
};
/* IOT routine */
t_stat lpt_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &lpt_unit;
switch(dev & 3) {
case CONI:
*data = uptr->STATUS & (PI_DONE|PI_ERROR|DONE_FLG|BUSY_FLG|ERR_FLG);
if ((uptr->flags & UNIT_UC) == 0)
*data |= C96;
if ((uptr->flags & UNIT_UTF8) == 0)
*data |= C128;
if ((uptr->flags & UNIT_ATT) == 0)
*data |= ERR_FLG;
sim_debug(DEBUG_CONI, &lpt_dev, "LP CONI %012llo PC=%06o\n", *data, PC);
break;
case CONO:
clr_interrupt(dev);
sim_debug(DEBUG_CONO, &lpt_dev, "LP CONO %012llo PC=%06o\n", *data, PC);
uptr->STATUS &= ~0777;
uptr->STATUS |= ((PI_DONE|PI_ERROR|DONE_FLG|BUSY_FLG|CLR_LPT) & *data);
if (*data & CLR_LPT) {
uptr->STATUS &= ~DONE_FLG;
uptr->STATUS |= BUSY_FLG;
sim_activate (&lpt_unit, lpt_unit.wait);
}
if ((uptr->flags & UNIT_ATT) == 0) {
uptr->STATUS |= ERR_FLG;
set_interrupt(dev, (uptr->STATUS >> 3));
}
if ((uptr->STATUS & DONE_FLG) != 0)
set_interrupt(dev, uptr->STATUS);
break;
case DATAO:
if ((uptr->STATUS & DONE_FLG) != 0) {
int i, j;
for (j = 0, i = 29; i > 0; i-=7)
lpt_chbuf[j++] = ((uint8)(*data >> i)) & 0x7f;
uptr->STATUS &= ~DONE_FLG;
uptr->STATUS |= BUSY_FLG;
clr_interrupt(dev);
sim_activate (&lpt_unit, lpt_unit.wait);
sim_debug(DEBUG_DATAIO, &lpt_dev, "LP DATO %012llo PC=%06o\n", *data, PC);
}
break;
case DATAI:
*data = 0;
break;
}
return SCPE_OK;
}
void
lpt_printline(UNIT *uptr, int nl) {
int trim = 0;
/* Trim off trailing blanks */
while (uptr->COL >= 0 && lpt_buffer[uptr->POS - 1] == ' ') {
uptr->COL--;
uptr->POS--;
trim = 1;
}
lpt_buffer[uptr->POS] = '\0';
sim_debug(DEBUG_DETAIL, &lpt_dev, "LP output %d %d [%s]\n", uptr->COL, nl, lpt_buffer);
/* Stick a carraige return and linefeed as needed */
if (uptr->COL != 0 || trim)
lpt_buffer[uptr->POS++] = '\r';
if (nl) {
lpt_buffer[uptr->POS++] = '\n';
uptr->LINE++;
}
sim_fwrite(&lpt_buffer, 1, uptr->POS, uptr->fileref);
uptr->COL = 0;
uptr->POS = 0;
if (ferror (uptr->fileref)) { /* error? */
perror ("LPT I/O error");
clearerr (uptr->fileref);
uptr->STATUS |= ERR_FLG;
set_interrupt(LP_DEVNUM, (uptr->STATUS >> 3));
return;
}
return;
}
uint16 utf_code[32] = {
0x0000, /* Dot */
0x2193, /* Down arrow */
0x237a, /* APL Alpha */
0x03b2, /* Beta */
0x039b, /* Lambda */
0x2510, /* Box light down and left */
0x03b5, /* Epsilon */
0x03d6, /* Pi */
0x03bb, /* Lambda */
0x221d, /* proportional */
0x222b, /* Integral */
0x00b1, /* Plus minus */
0x2295, /* Circle plus */
0x221e, /* Infinity */
0x2202, /* Partial derivitive */
0x2282, /* Subset of */
0x2283, /* Superset of */
0x2229, /* Intersection */
0x222a, /* union */
0x2200, /* For all */
0x2203, /* Exists */
0x2295, /* Circle plus */
0x2194, /* Left right arrow */
0x2227, /* Logical and */
0x2192, /* Rightwards arror */
0x2014, /* Em dash */
0x2260, /* Not equal */
0x2264, /* Less than or equal */
0x2265, /* Greater than or equal */
0x2261, /* Identical too */
0x2228 /* Logical or */
};
/* Unit service */
void
lpt_output(UNIT *uptr, char c) {
if (c == 0)
return;
if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140)
c &= 0137;
if ((uptr->flags & UNIT_UTF8) && c < 040) {
uint16 u = utf_code[c & 0x1f];
if (u > 0x7ff) {
lpt_buffer[uptr->POS++] = 0xe0 + ((u >> 12) & 0xf);
lpt_buffer[uptr->POS++] = 0x80 + ((u >> 6) & 0x3f);
lpt_buffer[uptr->POS++] = 0x80 + (u & 0x3f);
} else if (u > 0x7f) {
lpt_buffer[uptr->POS++] = 0xc0 + ((u >> 6) & 0x3f);
lpt_buffer[uptr->POS++] = 0x80 + (u & 0x3f);
} else {
lpt_buffer[uptr->POS++] = u & 0x7f;
}
uptr->COL++;
} else if (c >= 040) {
lpt_buffer[uptr->POS++] = c;
uptr->COL++;
}
if (uptr->COL == 132)
lpt_printline(uptr, 1);
return;
}
t_stat lpt_svc (UNIT *uptr)
{
char c;
int pos;
int cpos;
if ((uptr->flags & DONE_FLG) != 0) {
set_interrupt(LP_DEVNUM, uptr->STATUS);
return SCPE_OK;
}
if ((uptr->flags & UNIT_ATT) == 0) {
uptr->STATUS |= ERR_FLG;
set_interrupt(LP_DEVNUM, (uptr->STATUS >> 3));
return SCPE_OK;
}
if (uptr->STATUS & CLR_LPT) {
for (pos = 0; pos < uptr->COL; lpt_buffer[pos++] = ' ');
uptr->POS = uptr->COL;
lpt_printline(uptr, 0);
uptr->STATUS &= ~(DEL_FLG|ERR_FLG|BUSY_FLG|CLR_LPT);
uptr->STATUS |= DONE_FLG;
set_interrupt(LP_DEVNUM, uptr->STATUS);
return SCPE_OK;
}
for (cpos = 0; cpos < 5; cpos++) {
c = lpt_chbuf[cpos];
if (uptr->STATUS & DEL_FLG) {
lpt_output(uptr, c);
uptr->STATUS &= ~DEL_FLG;
} else if (c == 0177) { /* Check for DEL Character */
uptr->STATUS |= DEL_FLG;
} else if (c < 040) { /* Control character */
switch(c) {
case 011: /* Horizontal tab, space to 8'th column */
lpt_output(uptr, ' ');
while ((uptr->COL & 07) != 0)
lpt_output(uptr, ' ');
break;
case 015: /* Carriage return, print line */
lpt_printline(uptr, 0);
break;
case 012: /* Line feed, print line, space one line */
lpt_printline(uptr, 1);
uptr->LINE++;
break;
case 014: /* Form feed, skip to top of page */
lpt_printline(uptr, 0);
sim_fwrite("\014", 1, 1, uptr->fileref);
uptr->LINE = 0;
break;
case 013: /* Vertical tab, Skip mod 20 */
lpt_printline(uptr, 1);
while((uptr->LINE % 20) != 0) {
sim_fwrite("\r\n", 1, 2, uptr->fileref);
uptr->LINE++;
}
break;
case 020: /* Skip even lines */
lpt_printline(uptr, 1);
while((uptr->LINE % 2) != 0) {
sim_fwrite("\r\n", 1, 2, uptr->fileref);
uptr->LINE++;
}
break;
case 021: /* Skip third lines */
lpt_printline(uptr, 1);
while((uptr->LINE % 3) != 0) {
sim_fwrite("\r\n", 1, 2, uptr->fileref);
uptr->LINE++;
}
break;
case 022: /* Skip one line */
lpt_printline(uptr, 1);
sim_fwrite("\r\n", 1, 2, uptr->fileref);
uptr->LINE+=2;
break;
case 023: /* Skip every 10 lines */
lpt_printline(uptr, 1);
while((uptr->LINE % 10) != 0) {
sim_fwrite("\r\n", 1, 2, uptr->fileref);
uptr->LINE++;
}
break;
default: /* Ignore */
break;
}
} else {
lpt_output(uptr, c);
}
}
uptr->STATUS &= ~BUSY_FLG;
uptr->STATUS |= DONE_FLG;
set_interrupt(LP_DEVNUM, uptr->STATUS);
return SCPE_OK;
}
/* Reset routine */
t_stat lpt_reset (DEVICE *dptr)
{
UNIT *uptr = &lpt_unit;
uptr->POS = 0;
uptr->COL = 0;
uptr->LINE = 1;
uptr->STATUS = DONE_FLG;
clr_interrupt(LP_DEVNUM);
sim_cancel (&lpt_unit); /* deactivate unit */
return SCPE_OK;
}
/* Attach routine */
t_stat lpt_attach (UNIT *uptr, CONST char *cptr)
{
t_stat reason;
reason = attach_unit (uptr, cptr);
uptr->STATUS &= ~ERR_FLG;
clr_interrupt(LP_DEVNUM);
return reason;
}
/* Detach routine */
t_stat lpt_detach (UNIT *uptr)
{
uptr->STATUS |= ERR_FLG;
set_interrupt(LP_DEVNUM, uptr->STATUS >> 3);
return detach_unit (uptr);
}
t_stat lpt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Line Printer (LPT)\n\n");
fprintf (st, "The line printer (LPT) writes data to a disk file. The POS register specifies\n");
fprintf (st, "the number of the next data item to be written. Thus, by changing POS, the\n");
fprintf (st, "user can backspace or advance the printer.\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *lpt_description (DEVICE *dptr)
{
return "LP10 line printer" ;
}
#endif

1063
PDP10/kx10_mt.c Normal file

File diff suppressed because it is too large Load diff

422
PDP10/kx10_pt.c Normal file
View file

@ -0,0 +1,422 @@
/* ka10_pt.c: PDP-10 reader/punch simulator
Copyright (c) 2011-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_PT
#define NUM_DEVS_PT 0
#endif
#if (NUM_DEVS_PT > 0)
#define PP_DEVNUM 0100
#define PR_DEVNUM 0104
#define STATUS u3
#define CHR u4
#define CHL u5
#define PI_DONE 000007
#define DONE_FLG 000010
#define BUSY_FLG 000020
#define BIN_FLG 000040
#define NO_TAPE_PP 000100
#if PDP6
#define TAPE_PR 000000
#else
#define TAPE_PR 000400
#endif
t_stat ptp_devio(uint32 dev, uint64 *data);
t_stat ptp_svc (UNIT *uptr);
t_stat ptp_reset (DEVICE *dptr);
t_stat ptp_attach (UNIT *uptr, CONST char *cptr);
t_stat ptp_detach (UNIT *uptr);
t_stat ptp_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr);
const char *ptp_description (DEVICE *dptr);
t_stat ptr_devio(uint32 dev, uint64 *data);
t_stat ptr_svc (UNIT *uptr);
t_stat ptr_boot(int32 unit_num, DEVICE * dptr);
t_stat ptr_reset (DEVICE *dptr);
t_stat ptr_attach (UNIT *uptr, CONST char *cptr);
t_stat ptr_detach (UNIT *uptr);
t_stat ptr_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr);
const char *ptr_description (DEVICE *dptr);
DIB ptp_dib = { PP_DEVNUM, 1, &ptp_devio, NULL };
UNIT ptp_unit = {
UDATA (&ptp_svc, UNIT_ATTABLE+UNIT_TEXT, 0), 10000
};
REG ptp_reg[] = {
{ DRDATA (STATUS, ptp_unit.STATUS, 18), PV_LEFT | REG_UNIT},
{ DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT | REG_UNIT},
{ NULL }
};
MTAB ptp_mod[] = {
{ 0 }
};
DEVICE ptp_dev = {
"PTP", &ptp_unit, ptp_reg, ptp_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptp_reset, NULL, &ptp_attach, &ptp_detach,
&ptp_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &ptp_help, NULL, NULL, &ptp_description
};
DIB ptr_dib = { PR_DEVNUM, 1, &ptr_devio, NULL };
UNIT ptr_unit = {
UDATA (&ptr_svc, UNIT_ATTABLE+UNIT_TEXT, 0), 10000
};
REG ptr_reg[] = {
{ DRDATA (STATUS, ptr_unit.STATUS, 18), PV_LEFT | REG_UNIT},
{ DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT | REG_UNIT},
{ NULL }
};
MTAB ptr_mod[] = {
{ 0 }
};
DEVICE ptr_dev = {
"PTR", &ptr_unit, ptr_reg, ptr_mod,
1, 10, 31, 1, 8, 8,
NULL, NULL, &ptr_reset, &ptr_boot, &ptr_attach, &ptr_detach,
&ptr_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &ptr_help, NULL, NULL, &ptr_description
};
/* IOT routine */
t_stat ptp_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &ptp_unit;
switch(dev & 3) {
case CONI:
*data = uptr->STATUS;
#if WAITS
/* The NXM stop switch is always off */
if (cpu_unit[0].flags & UNIT_WAITS)
*data |= 0200;
#endif
sim_debug(DEBUG_CONI, &ptp_dev, "PP: CONI %012llo\n\r", *data);
break;
case CONO:
clr_interrupt(dev);
uptr->STATUS = (PI_DONE|DONE_FLG|BUSY_FLG|BIN_FLG) & *data;
if ((uptr->flags & UNIT_ATT) == 0)
uptr->STATUS |= NO_TAPE_PP;
if (uptr->STATUS & BUSY_FLG) {
uptr->CHR = 0;
sim_activate (&ptp_unit, ptp_unit.wait);
}
if (uptr->STATUS & DONE_FLG)
set_interrupt(dev, uptr->STATUS);
sim_debug(DEBUG_CONO, &ptp_dev, "PP: CONO %012llo\n\r", *data);
break;
case DATAO:
if ((uptr->STATUS & BUSY_FLG) == 0) {
uptr->CHR = *data & 00377;
if (uptr->STATUS & BIN_FLG) {
uptr->CHR &= 077;
uptr->CHR |= 0200;
}
uptr->STATUS |= BUSY_FLG;
uptr->STATUS &= ~DONE_FLG;
clr_interrupt(dev);
sim_activate (&ptp_unit, ptp_unit.wait);
}
sim_debug(DEBUG_DATAIO, &ptp_dev, "PP: DATAO %012llo\n\r", *data);
break;
case DATAI:
*data = 0;
break;
}
return SCPE_OK;
}
/* Unit service */
t_stat ptp_svc (UNIT *uptr)
{
uptr->STATUS &= ~BUSY_FLG;
uptr->STATUS |= DONE_FLG;
set_interrupt(PP_DEVNUM, uptr->STATUS & 7);
if ((uptr->flags & UNIT_ATT) == 0) {
uptr->STATUS |= NO_TAPE_PP;
return SCPE_OK;
}
fputc (uptr->CHR, uptr->fileref); /* print char */
uptr->pos = ftell (uptr->fileref);
if (ferror (uptr->fileref)) { /* error? */
perror ("PTP I/O error");
clearerr (uptr->fileref);
return SCPE_IOERR;
}
return SCPE_OK;
}
/* Reset routine */
t_stat ptp_reset (DEVICE *dptr)
{
UNIT *uptr = &ptp_unit;
uptr->CHR = 0;
uptr->CHL = 0;
uptr->STATUS = 0;
clr_interrupt(PP_DEVNUM);
sim_cancel (&ptp_unit); /* deactivate unit */
return SCPE_OK;
}
/* Attach routine */
t_stat ptp_attach (UNIT *uptr, CONST char *cptr)
{
t_stat reason;
reason = attach_unit (uptr, cptr);
uptr->STATUS &= ~NO_TAPE_PP;
return reason;
}
/* Detach routine */
t_stat ptp_detach (UNIT *uptr)
{
uptr->STATUS |= NO_TAPE_PP;
return detach_unit (uptr);
}
t_stat ptr_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &ptr_unit;
switch(dev & 3) {
case CONI:
*data = uptr->STATUS;
sim_debug(DEBUG_CONI, &ptr_dev, "PT: CONI %012llo\n\r", *data);
break;
case CONO:
clr_interrupt(dev);
uptr->STATUS = (PI_DONE|DONE_FLG|BUSY_FLG|BIN_FLG) & *data;
if ((uptr->flags & UNIT_ATT))
uptr->STATUS |= TAPE_PR;
if (uptr->STATUS & BUSY_FLG) {
uptr->CHR = 0;
uptr->CHL = 0;
sim_activate (&ptr_unit, ptr_unit.wait);
}
if (uptr->STATUS & DONE_FLG)
set_interrupt(dev, uptr->STATUS);
sim_debug(DEBUG_CONO, &ptr_dev, "PT: CONO %012llo\n\r", *data);
break;
case DATAI:
if ((uptr->STATUS & DONE_FLG)) {
*data = ((uint64)uptr->CHL) << 18;
*data |= ((uint64)uptr->CHR);
uptr->STATUS &= ~DONE_FLG;
clr_interrupt(dev);
sim_activate (&ptr_unit, ptr_unit.wait);
}
uptr->STATUS |= BUSY_FLG;
sim_debug(DEBUG_DATAIO, &ptr_dev, "PT: DATAI %012llo\n\r", *data);
break;
case DATAO:
break;
}
return SCPE_OK;
}
/* Unit service */
t_stat ptr_svc (UNIT *uptr)
{
int32 temp;
uint64 word;
int count = (uptr->STATUS & BIN_FLG) ? 6 : 1;
uptr->STATUS &= ~BUSY_FLG;
uptr->STATUS |= DONE_FLG;
set_interrupt(PR_DEVNUM, uptr->STATUS);
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_OK;
word = 0;
while (count > 0) {
if ((temp = getc (uptr->fileref)) == EOF) {
if (feof (uptr->fileref)) {
uptr->STATUS &= ~TAPE_PR;
break;
}
}
if (uptr->STATUS & BIN_FLG) {
if (temp & 0200) {
word <<= 6;
word |= (uint64)(temp & 077);
count--;
}
} else {
word |= (uint64)(temp);
count--;
}
}
uptr->CHL = (word >> 18) & RMASK;
uptr->CHR = word & RMASK;
return SCPE_OK;
}
uint64
ptr_read_word(UNIT *uptr) {
int i, ch;
uint64 word = 0;
for(i = 0; i < 6;) {
if ((ch = getc (uptr->fileref)) == EOF)
return word;
if (ch & 0200) {
word <<= 6;
word |= (uint64)(ch & 077);
i++;
}
}
return word;
}
/* Boot from given device */
t_stat
ptr_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
uint64 word;
int wc, addr;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT; /* attached? */
word = ptr_read_word(uptr);
wc = (word >> 18) & RMASK;
addr = word & RMASK;
while (wc != 0) {
wc = (wc + 1) & RMASK;
addr = (addr + 1) & RMASK;
word = ptr_read_word(uptr);
if (addr < 020)
FM[addr] = word;
else
M[addr] = word;
}
if (addr < 020)
FM[addr] = word;
else
M[addr] = word;
uptr->STATUS = BUSY_FLG|BIN_FLG|TAPE_PR;
uptr->CHR = 0;
uptr->CHL = 0;
sim_activate (&ptr_unit, ptr_unit.wait);
PC = word & RMASK;
return SCPE_OK;
}
/* Reset routine */
t_stat ptr_reset (DEVICE *dptr)
{
UNIT *uptr = &ptr_unit;
uptr->CHR = 0;
uptr->CHL = 0;
uptr->STATUS = 0;
clr_interrupt(PR_DEVNUM);
sim_cancel (&ptr_unit); /* deactivate unit */
return SCPE_OK;
}
/* Attach routine */
t_stat ptr_attach (UNIT *uptr, CONST char *cptr)
{
t_stat reason;
reason = attach_unit (uptr, cptr);
uptr->STATUS |= TAPE_PR;
return reason;
}
/* Detach routine */
t_stat ptr_detach (UNIT *uptr)
{
uptr->STATUS &= ~TAPE_PR;
return detach_unit (uptr);
}
t_stat ptr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Paper Tape Reader (PTR)\n\n");
fprintf (st, "The paper tape reader (PTR) reads data from a disk file. The POS register\n");
fprintf (st, "specifies the number of the next data item to be read. Thus, by changing\n");
fprintf (st, "POS, the user can backspace or advance the reader.\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *ptr_description (DEVICE *dptr)
{
return "paper tape reader";
}
t_stat ptp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Paper Tape Punch (PTP)\n\n");
fprintf (st, "The paper tape punch (PTP) writes data to a disk file. The POS register\n");
fprintf (st, "specifies the number of the next data item to be written. Thus, by changing\n");
fprintf (st, "POS, the user can backspace or advance the punch.\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *ptp_description (DEVICE *dptr)
{
return "paper tape punch";
}
#endif

574
PDP10/kx10_rc.c Normal file
View file

@ -0,0 +1,574 @@
/* ka10_rc.c: RC10 Disk Controller.
Copyright (c) 2013-2017, Richard Cornwell
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
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_RC
#define NUM_DEVS_RC 0
#endif
#if (NUM_DEVS_RC > 0)
#define RC_DEVNUM 0170 /* 0174 */
#define NUM_UNITS_RC 4
/* Flags in the unit flags word */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
#define UNIT_M_DTYPE 1
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* Parameters in the unit descriptor */
#define CUR_CYL u3 /* current cylinder */
#define DATAPTR u4 /* data pointer */
#define UFLAGS u5 /* Function */
#define DISK_SEL 0600000000000LL
#define TRACK 0177600000000LL
#define SEGMENT 0000177000000LL
#define INIT_PAR 0000000770000LL /* Read */
#define DPE_STOP 0000000004000LL
#define CPE_STOP 0000000002000LL
#define WRITE 0000000001000LL
#define EPAR 0000000000001LL
#define SEC_SEL 0000000001400LL /* Read */
#define SECT_CNT 0000000000377LL /* Read */
#define PI 0000007
#define WCW 0000040
#define SEC_SCTR 0600000
#define RST_MSK 0000000177710LL /* CONO reset bits */
#define B22_FLAG 0040000000000LL /* 22 bit controller. */
#define MAINT_SEG 0010000000000LL
#define PRTLT 0004000000000LL /* Protected area less then bounds */
#define STS 0003777000000LL
#define SCRCHCMP 0000000400000LL /* Tranfer in progress. */
#define S_ERROR 0000000200000LL /* Segment not found */
#define DSK_DES_E 0000000100000LL /* Duplicate disk */
#define TRK_SEL_E 0000000040000LL /* Track not BCD number */
#define NOT_RDY 0000000020000LL /* Drive not ready */
#define PSW_FAIL 0000000010000LL /* Power supply fail */
#define DSK_PAR_E 0000000004000LL /* Disk Parity Error */
#define CH_PAR_D 0000000002000LL /* Channel Data Parity Error */
#define CH_PAR_C 0000000001000LL /* Channel Control Parity Error */
#define NXM_ERR 0000000000400LL /* Non existant memory */
#define ILL_WR 0000000000200LL /* Write to protected area */
#define OVRRUN 0000000000100LL /* Over run */
#define RD10_DTYPE 0
#define RD10_WDS 32
#define RD10_SEGS 80
#define RD10_CYL 200
#define RD10_SIZE (RD10_SEGS * RD10_CYL * RD10_WDS)
#define RM10_DTYPE 1
#define RM10_WDS 64
#define RM10_SEGS 60
#define RM10_CYL 90
#define RM10_SIZE (RM10_SEGS * RM10_CYL * RM10_WDS)
struct drvtyp {
int32 wd_seg; /* Number of words per segment */
int32 seg; /* segments */
int32 cyl; /* cylinders */
int32 size; /* #blocks */
int32 devtype; /* device type */
};
struct drvtyp rc_drv_tab[] = {
{ RD10_WDS, RD10_SEGS, RD10_CYL, RD10_SIZE, RD10_DTYPE},
{ RM10_WDS, RM10_SEGS, RM10_CYL, RM10_SIZE, RM10_DTYPE},
{ 0 }
};
struct df10 rc_df10[NUM_DEVS_RC];
uint64 rc_buf[NUM_DEVS_RC][RM10_WDS];
uint32 rc_ipr[NUM_DEVS_RC];
t_stat rc_devio(uint32 dev, uint64 *data);
t_stat rc_svc(UNIT *);
t_stat rc_boot(int32, DEVICE *);
void rc_ini(UNIT *, t_bool);
t_stat rc_reset(DEVICE *);
t_stat rc_attach(UNIT *, CONST char *);
t_stat rc_detach(UNIT *);
t_stat rc_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *rc_description (DEVICE *dptr);
UNIT rc_unit[] = {
/* Controller 1 */
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
#if (NUM_DEVS_RC > 1)
/* Controller 2 */
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
{ UDATA (&rc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+(RD10_DTYPE << UNIT_V_DTYPE), RD10_SIZE) },
#endif
};
DIB rc_dib[] = {
{RC_DEVNUM+000, 1, &rc_devio, NULL},
{RC_DEVNUM+004, 1, &rc_devio, NULL}
};
MTAB rc_mod[] = {
{UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL},
{UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL},
{UNIT_DTYPE, (RD10_DTYPE << UNIT_V_DTYPE), "RD10", "RD10", &rc_set_type },
{UNIT_DTYPE, (RM10_DTYPE << UNIT_V_DTYPE), "RM10", "RM10", &rc_set_type },
{0}
};
REG rca_reg[] = {
{BRDATA(BUFF, &rc_buf[0][0], 16, 64, RM10_WDS), REG_HRO},
{ORDATA(IPR, rc_ipr[0], 2), REG_HRO},
{ORDATA(STATUS, rc_df10[0].status, 18), REG_RO},
{ORDATA(CIA, rc_df10[0].cia, 18)},
{ORDATA(CCW, rc_df10[0].ccw, 18)},
{ORDATA(WCR, rc_df10[0].wcr, 18)},
{ORDATA(CDA, rc_df10[0].cda, 18)},
{ORDATA(DEVNUM, rc_df10[0].devnum, 9), REG_HRO},
{ORDATA(BUF, rc_df10[0].buf, 36), REG_HRO},
{ORDATA(NXM, rc_df10[0].nxmerr, 8), REG_HRO},
{ORDATA(COMP, rc_df10[0].ccw_comp, 8), REG_HRO},
{0}
};
DEVICE rca_dev = {
"FHA", rc_unit, rca_reg, rc_mod,
NUM_UNITS_RC, 8, 18, 1, 8, 36,
NULL, NULL, &rc_reset, &rc_boot, &rc_attach, &rc_detach,
&rc_dib[0], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &rc_help, NULL, NULL, &rc_description
};
#if (NUM_DEVS_RC > 1)
REG rcb_reg[] = {
{BRDATA(BUFF, &rc_buf[1][0], 16, 64, RM10_WDS), REG_HRO},
{ORDATA(IPR, rc_ipr[1], 2), REG_HRO},
{ORDATA(STATUS, rc_df10[1].status, 18), REG_RO},
{ORDATA(CIA, rc_df10[1].cia, 18)},
{ORDATA(CCW, rc_df10[1].ccw, 18)},
{ORDATA(WCR, rc_df10[1].wcr, 18)},
{ORDATA(CDA, rc_df10[1].cda, 18)},
{ORDATA(DEVNUM, rc_df10[1].devnum, 9), REG_HRO},
{ORDATA(BUF, rc_df10[1].buf, 36), REG_HRO},
{ORDATA(NXM, rc_df10[1].nxmerr, 8), REG_HRO},
{ORDATA(COMP, rc_df10[1].ccw_comp, 8), REG_HRO},
{0}
};
DEVICE rcb_dev = {
"FHB", &rc_unit[010], rcb_reg, rc_mod,
NUM_UNITS_RC, 8, 18, 1, 8, 36,
NULL, NULL, &rc_reset, &rc_boot, &rc_attach, &rc_detach,
&rc_dib[1], DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &rc_help, NULL, NULL, &rc_description
};
#endif
DEVICE *rc_devs[] = {
&rca_dev,
#if (NUM_DEVS_RC > 1)
&rcb_dev,
#endif
};
t_stat rc_devio(uint32 dev, uint64 *data) {
int ctlr = (dev - RC_DEVNUM) >> 2;
struct df10 *df10;
UNIT *uptr;
DEVICE *dptr;
int unit;
int tmp;
int drv;
int cyl;
int dtype;
if (ctlr < 0 || ctlr >= NUM_DEVS_RC)
return SCPE_OK;
df10 = &rc_df10[ctlr];
dptr = rc_devs[ctlr];
switch(dev & 3) {
case CONI:
*data = df10->status;
#if KI_22BIT
*data |= B22_FLAG;
#endif
*data |= PRTLT;
sim_debug(DEBUG_CONI, dptr, "HK %03o CONI %06o PC=%o\n", dev,
(uint32)*data, PC);
break;
case CONO:
if (*data & PI_ENABLE)
df10->status &= ~(PI_ENABLE);
clr_interrupt(dev);
df10->status &= ~07;
df10->status |= *data & 07;
df10->status &= ~(RST_MSK & *data);
if ((*data & BUSY) != 0) {
unit = rc_ipr[ctlr] & 3;
drv = unit + (ctlr * NUM_UNITS_RC);
uptr = &rc_unit[drv];
if ((df10->status & BUSY) != 0) {
sim_cancel(uptr);
df10_finish_op(df10, 0);
} else {
df10->status &= ~BUSY;
df10_setirq(df10);
}
}
rc_ipr[ctlr] &= ~SEC_SCTR;
rc_ipr[ctlr] |= *data & SEC_SCTR;
if ((df10->status & BUSY) != 0 && (*data & CCW_COMP) != 0) {
df10_writecw(df10);
} else
df10->status &= ~CCW_COMP;
sim_debug(DEBUG_CONO, dptr, "HK %03o CONO %06o PC=%o %06o\n", dev,
(uint32)*data, PC, df10->status);
break;
case DATAI:
*data = rc_ipr[ctlr];
unit = (rc_ipr[ctlr] & SEC_SCTR) >> 16;
uptr = &rc_unit[(ctlr * NUM_UNITS_RC) + unit];
*data |= (uptr->UFLAGS >> 3) & 0177;
sim_debug(DEBUG_DATAIO, dptr, "HK %03o DATI %012llo PC=%o F=%o\n",
dev, *data, PC, uptr->UFLAGS);
break;
case DATAO:
sim_debug(DEBUG_DATAIO, dptr, "HK %03o DATO %012llo, PC=%o\n",
dev, *data, PC);
if (df10->status & BUSY) {
return SCPE_OK;
}
df10->status &= ~(PI_ENABLE|S_ERROR);
clr_interrupt(RC_DEVNUM + (ctlr * 4));
rc_ipr[ctlr] &= ~(INIT_PAR|3);
rc_ipr[ctlr] |= *data & INIT_PAR;
unit = (*data >> 34) & 03;
rc_ipr[ctlr] |= unit;
drv = unit + (ctlr * NUM_UNITS_RC);
uptr = &rc_unit[drv];
if ((uptr->flags & UNIT_ATT) == 0) {
df10->status &= ~BUSY;
df10->status |= NOT_RDY;
df10_setirq(df10);
return SCPE_OK;
}
if ((uptr->flags & UNIT_WPRT) && *data & WRITE) {
df10->status &= ~BUSY;
df10->status |= ILL_WR;
df10_setirq(df10);
return SCPE_OK;
}
df10_setup(df10, (uint32)*data);
tmp = (uint32)(*data >> 15) & ~07;
cyl = (tmp >> 10) & 0777;
if (((cyl & 017) > 9) || (((cyl >> 4) & 017) > 9)) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d non-bcd cyl %02x\n",
ctlr, cyl);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
cyl = (((cyl >> 4) & 017) * 10) + (cyl & 017) +
((cyl & 0x100) ? 100 : 0);
dtype = GET_DTYPE(uptr->flags);
if (cyl >= rc_drv_tab[dtype].cyl) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d invalid cyl %d %d\n",
ctlr, cyl, rc_drv_tab[dtype].cyl);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
cyl = (tmp >> 3) & 0177;
if ((cyl & 017) > 9) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d non-bcd seg %02x\n",
ctlr, cyl);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
uptr->UFLAGS = tmp | ((*data & WRITE) != 0) | (ctlr << 1);
uptr->DATAPTR = -1; /* Set no data */
if ((*data & WRITE) != 0)
(void)df10_read(df10);
sim_debug(DEBUG_DETAIL, dptr, "HK %d cyl %o\n", ctlr, uptr->UFLAGS);
sim_activate(uptr, 100);
break;
}
return SCPE_OK;
}
t_stat rc_svc (UNIT *uptr)
{
int dtype = GET_DTYPE(uptr->flags);
int ctlr = (uptr->UFLAGS >> 1) & 03;
int seg = (uptr->UFLAGS >> 3) & 0177;
int cyl = (uptr->UFLAGS >> 10) & 0777;
int wr = (uptr->UFLAGS & 1);
int seg_size = rc_drv_tab[dtype].wd_seg;
struct df10 *df10 = &rc_df10[ctlr];
int tmp, wc;
DEVICE *dptr;
t_stat err, r;
dptr = rc_devs[ctlr];
/* Check if we need to seek */
if (uptr->DATAPTR == -1) {
cyl = (((cyl >> 4) & 017) * 10) + (cyl & 017) +
((cyl & 0x100) ? 100 : 0);
if (cyl >= rc_drv_tab[dtype].cyl) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d invalid cyl %d %d %o\n",
ctlr, cyl, rc_drv_tab[dtype].cyl, uptr->UFLAGS);
df10_finish_op(df10, TRK_SEL_E);
return SCPE_OK;
}
/* Convert segment from BCD to binary */
if ((seg & 017) > 10) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d non-bcd seg %02x %d %o\n",
ctlr, seg, rc_drv_tab[dtype].seg, uptr->UFLAGS);
df10_finish_op(df10, S_ERROR);
return SCPE_OK;
}
seg = (((seg >> 4) & 07) * 10) + (seg & 017);
if (seg >= rc_drv_tab[dtype].seg) {
sim_debug(DEBUG_DETAIL, dptr, "HK %d invalid sec %d %d %o\n",
ctlr, seg, rc_drv_tab[dtype].seg, uptr->UFLAGS);
df10_finish_op(df10, S_ERROR);
return SCPE_OK;
}
/* Check if reading */
if (!wr) {
/* Read the block */
int da;
da = ((cyl * rc_drv_tab[dtype].seg) + seg) * seg_size;
err = sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fread (&rc_buf[ctlr][0], sizeof(uint64),
seg_size, uptr->fileref);
sim_debug(DEBUG_DETAIL, dptr, "HK %d Read %d %d %d %x\n",
ctlr, da, cyl, seg, uptr->UFLAGS << 1 );
for (; wc < seg_size; wc++)
rc_buf[ctlr][wc] = 0;
}
uptr->DATAPTR = 0;
df10->status |= SCRCHCMP;
}
if (wr) {
rc_buf[ctlr][uptr->DATAPTR] = df10->buf;
r = df10_read(df10);
} else {
df10->buf = rc_buf[ctlr][uptr->DATAPTR];
r = df10_write(df10);
}
sim_debug(DEBUG_DATA, dptr, "Xfer %d %012llo %06o %06o\n", uptr->DATAPTR, df10->buf,
df10->wcr, df10->cda);
uptr->DATAPTR++;
if (uptr->DATAPTR >= seg_size || r == 0 ) {
/* Check if writing */
df10->status &= ~SCRCHCMP;
seg = (((seg >> 4) & 017) * 10) + (seg & 017);
cyl = (((cyl >> 4) & 017) * 10) + (cyl & 017) +
((cyl & 0x100) ? 100 : 0);
if (wr) {
int da;
while(uptr->DATAPTR < seg_size) {
rc_buf[ctlr][uptr->DATAPTR] = 0;
uptr->DATAPTR++;
}
da = ((cyl * rc_drv_tab[dtype].seg) + seg) * seg_size;
sim_debug(DEBUG_DETAIL, dptr, "HK %d Write %d %d %d %x %d\n",
ctlr, da, cyl, seg, uptr->UFLAGS << 1, uptr->DATAPTR );
err = sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fwrite(&rc_buf[ctlr][0],sizeof(uint64),
seg_size, uptr->fileref);
}
uptr->DATAPTR = -1;
seg++;
if (seg >= rc_drv_tab[dtype].seg) {
seg = 0;
cyl++;
if (cyl >= rc_drv_tab[dtype].cyl)
cyl = 0;
}
/* Convert seg back to bcd */
tmp = seg % 10;
seg /= 10;
seg <<= 4;
seg += tmp;
wr = 0;
if (cyl >= 100) {
wr = 0x100;
cyl -= 100;
}
tmp = (cyl % 10);
cyl /= 10;
cyl <<= 4;
cyl += wr + tmp;
uptr->UFLAGS = (uptr->UFLAGS & 7) + (seg << 3) + (cyl << 10);
}
if ((df10->status & PI_ENABLE) == 0) {
sim_activate(uptr, 20);
}
return SCPE_OK;
}
t_stat
rc_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int i;
if (uptr == NULL) return SCPE_IERR;
for (i = 0; rc_drv_tab[i].wd_seg != 0; i++) {
if ((val >> UNIT_V_DTYPE) == rc_drv_tab[i].devtype) {
uptr->capac = rc_drv_tab[i].size;
return SCPE_OK;
}
}
return SCPE_IERR;
}
t_stat
rc_reset(DEVICE * dptr)
{
int unit;
int ctlr;
UNIT *uptr = dptr->units;
for(unit = 0; unit < NUM_UNITS_RC; unit++) {
uptr->UFLAGS = 0;
uptr->CUR_CYL = 0;
uptr++;
}
for (ctlr = 0; ctlr < NUM_DEVS_RC; ctlr++) {
rc_ipr[ctlr] = 0;
rc_df10[ctlr].status = 0;
rc_df10[ctlr].devnum = rc_dib[ctlr].dev_num;
rc_df10[ctlr].nxmerr = 8;
rc_df10[ctlr].ccw_comp = 5;
}
return SCPE_OK;
}
/* Boot from given device */
t_stat
rc_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
int dtype = GET_DTYPE(uptr->flags);
uint32 addr;
int wc;
int wps;
int seg;
int sect;
uint32 ptr;
addr = (MEMSIZE - 512) & RMASK;
wps = rc_drv_tab[dtype].wd_seg;
for (sect = 4; sect <= 7; sect++) {
seg = (sect * 128) / wps;
(void)sim_fseek(uptr->fileref, (seg * wps) * sizeof(uint64), SEEK_SET);
(void)sim_fread (&rc_buf[0][0], sizeof(uint64), wps, uptr->fileref);
ptr = 0;
for(wc = wps; wc > 0; wc--) {
M[addr++] = rc_buf[0][ptr++];
}
}
PC = (MEMSIZE - 512) & RMASK;
return SCPE_OK;
}
/* Device attach */
t_stat rc_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
uptr->capac = rc_drv_tab[GET_DTYPE (uptr->flags)].size;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
return r;
uptr->CUR_CYL = 0;
uptr->UFLAGS = 0;
return SCPE_OK;
}
/* Device detach */
t_stat rc_detach (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK;
if (sim_is_active (uptr)) /* unit active? */
sim_cancel (uptr); /* cancel operation */
return detach_unit (uptr);
}
t_stat rc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "RD10/RM10 Disk Pack Drives (RC)\n\n");
fprintf (st, "The RC controller implements the RC-10 disk controller that talked\n");
fprintf (st, "to either RD10 mountable pack or RM10 drum drives.\n");
fprintf (st, "Options include the ability to set units write enabled or write locked, to\n");
fprintf (st, "set the drive type to one of two disk types\n\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprintf (st, "\nThe type options can be used only when a unit is not attached to a file.\n");
fprintf (st, "The RC device supports the BOOT command.\n");
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *rc_description (DEVICE *dptr)
{
return "RD10/RM10 disk controller";
}
#endif

1350
PDP10/kx10_rp.c Normal file

File diff suppressed because it is too large Load diff

948
PDP10/kx10_rs.c Normal file
View file

@ -0,0 +1,948 @@
/* ka10_rs.c: Dec RH10 RS04
Copyright (c) 2017, Richard Cornwell
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 PURSOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_RS
#define NUM_DEVS_RS 0
#endif
#if (NUM_DEVS_RS > 0)
#define RS_NUMWD 128 /* 36bit words/sec */
#define NUM_UNITS_RS 8
/* Flags in the unit flags word */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
#define UNIT_M_DTYPE 7
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define DTYPE(x) (((x) & UNIT_M_DTYPE) << UNIT_V_DTYPE)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
#define CNTRL_V_CTYPE (UNIT_V_UF + 4)
#define CNTRL_M_CTYPE 7
#define GET_CNTRL(x) (((x) >> CNTRL_V_CTYPE) & CNTRL_M_CTYPE)
#define CNTRL(x) (((x) & CNTRL_M_CTYPE) << CNTRL_V_CTYPE)
/* Parameters in the unit descriptor */
/* CONI Flags */
#define IADR_ATTN 0000000000040LL /* Interrupt on attention */
#define IARD_RAE 0000000000100LL /* Interrupt on register access error */
#define DIB_CBOV 0000000000200LL /* Control bus overrun */
#define CXR_PS_FAIL 0000000002000LL /* Power supply fail (not implemented) */
#define CXR_ILC 0000000004000LL /* Illegal function code */
#define CR_DRE 0000000010000LL /* Or Data and Control Timeout */
#define DTC_OVER 0000000020000LL /* DF10 did not supply word on time (not implemented) */
#define CCW_COMP_1 0000000040000LL /* Control word written. */
#define CXR_CHAN_ER 0000000100000LL /* Channel Error */
#define CXR_EXC 0000000200000LL /* Error in drive transfer */
#define CXR_DBPE 0000000400000LL /* Device Parity error (not implemented) */
#define CXR_NXM 0000001000000LL /* Channel non-existent memory (not implemented) */
#define CXR_CWPE 0000002000000LL /* Channel Control word parity error (not implemented) */
#define CXR_CDPE 0000004000000LL /* Channel Data Parity Error (not implemented) */
#define CXR_SD_RAE 0000200000000LL /* Register access error */
#define CXR_ILFC 0000400000000LL /* Illegal CXR function code */
#define B22_FLAG 0004000000000LL /* 22 bit channel */
#define CC_CHAN_PLS 0010000000000LL /* Channel transfer pulse (not implemented) */
#define CC_CHAN_ACT 0020000000000LL /* Channel in use */
#define CC_INH 0040000000000LL /* Disconnect channel */
#define CB_FULL 0200000000000LL /* Set when channel buffer is full (not implemented) */
#define AR_FULL 0400000000000LL /* Set when AR is full (not implemented) */
/* CONO Flags */
#define ATTN_EN 0000000000040LL /* enable attention interrupt. */
#define REA_EN 0000000000100LL /* enable register error interrupt */
#define CBOV_CLR 0000000000200LL /* Clear CBOV */
#define CONT_RESET 0000000002000LL /* Clear All error bits */
#define ILC_CLR 0000000004000LL /* Clear ILC and SD RAE */
#define DRE_CLR 0000000010000LL /* Clear CR_CBTO and CR_DBTO */
#define OVER_CLR 0000000020000LL /* Clear DTC overrun */
#define WRT_CW 0000000040000LL /* Write control word */
#define CHN_CLR 0000000100000LL /* Clear Channel Error */
#define DR_EXC_CLR 0000000200000LL /* Clear DR_EXC */
#define DBPE_CLR 0000000400000LL /* Clear CXR_DBPE */
/* DATAO/DATAI */
#define CR_REG 0770000000000LL /* Register number */
#define LOAD_REG 0004000000000LL /* Load register */
#define CR_MAINT_MODE 0000100000000LL /* Maint mode... not implemented */
#define CR_DRIVE 0000007000000LL
#define CR_GEN_EVD 0000000400000LL /* Enable Parity */
#define CR_DXES 0000000200000LL /* Disable DXES errors */
#define CR_INAD 0000000077600LL
#define CR_WTEVM 0000000000100LL /* Verify Parity */
#define CR_FUNC 0000000000076LL
#define CR_GO 0000000000001LL
#define IRQ_VECT 0000000000177LL /* Interupt vector */
#define IRQ_KI10 0000002000000LL
#define IRQ_KA10 0000001000000LL
#define CMD u3
/* u3 low */
/* RSC - 00 - control */
#define CS1_GO CR_GO /* go */
#define CS1_V_FNC 1 /* function pos */
#define CS1_M_FNC 037 /* function mask */
#define CS1_FNC (CS1_M_FNC << CS1_V_FNC)
#define FNC_NOP 000 /* no operation */
#define FNC_DCLR 004 /* drive clear */
#define FNC_PRESET 010 /* read-in preset */
#define FNC_SEARCH 014 /* search */
#define FNC_XFER 024 /* >=? data xfr */
#define FNC_WCHK 024 /* write check */
#define FNC_WRITE 030 /* write */
#define FNC_READ 034 /* read */
#define CS1_DVA 0004000 /* drive avail NI */
#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC)
/* u3 low */
/* RSDS - 01 - drive status */
#define DS_VV 0000000 /* volume valid */
#define DS_DRY 0000200 /* drive ready */
#define DS_DPR 0000400 /* drive present */
#define DS_PGM 0001000 /* programable NI */
#define DS_LST 0002000 /* last sector */
#define DS_WRL 0004000 /* write locked */
#define DS_MOL 0010000 /* medium online */
#define DS_PIP 0020000 /* pos in progress */
#define DS_ERR 0040000 /* error */
#define DS_ATA 0100000 /* attention active */
#define DS_MBZ 0000076
/* u3 high */
/* RSER1 - 02 - error status 1 */
#define ER1_ILF 0000001 /* illegal func */
#define ER1_ILR 0000002 /* illegal register */
#define ER1_RMR 0000004 /* reg mod refused */
#define ER1_PAR 0000010 /* parity err */
#define ER1_FER 0000020 /* format err NI */
#define ER1_WCF 0000040 /* write clk fail NI */
#define ER1_ECH 0000100 /* ECC hard err NI */
#define ER1_HCE 0000200 /* hdr comp err NI */
#define ER1_HCR 0000400 /* hdr CRC err NI */
#define ER1_AOE 0001000 /* addr ovflo err */
#define ER1_IAE 0002000 /* invalid addr err */
#define ER1_WLE 0004000 /* write lock err */
#define ER1_DTE 0010000 /* drive time err NI */
#define ER1_OPI 0020000 /* op incomplete */
#define ER1_UNS 0040000 /* drive unsafe */
#define ER1_DCK 0100000 /* data check NI */
/* RSMR - 03 - maintenace register */
/* RSAS - 04 - attention summary */
#define AS_U0 0000001 /* unit 0 flag */
#define DA u4
/* u4 high */
/* RSDC - 05 - desired sector */
#define DA_V_SC 0 /* sector pos */
#define DA_M_SC 077 /* sector mask */
#define DA_V_SF 6 /* track pos */
#define DA_M_SF 077 /* track mask */
#define DA_MBZ 0170000
#define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC)
#define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF)
/* RSDT - 06 - drive type */
/* RSLA - 07 - look ahead register */
#define LA_V_SC 6 /* sector pos */
#define GET_DA(c,d) (((GET_SF (c)) * rs_drv_tab[d].sect) + GET_SC (c))
#define DATAPTR u6
/* This controller supports many different disk drive types. These drives
are operated in 576 bytes/sector (128 36b words/sector) mode, which gives
them somewhat different geometry from the PDP-11 variants:
type #sectors/ #surfaces/
surface cylinder
RS03 32 64
RS04 32 64
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 RS03_DTYPE 0
#define RS03_SECT 64
#define RS03_SURF 32
#define RS03_DEV 020002
#define RS03_SIZE (RS03_SECT * RS03_SURF * RS_NUMWD)
#define RS04_DTYPE 1
#define RS04_SECT 64
#define RS04_SURF 32
#define RS04_DEV 020003
#define RS04_SIZE (RS04_SECT * RS04_SURF * RS_NUMWD)
struct drvtyp {
int32 sect; /* sectors */
int32 surf; /* surfaces */
int32 size; /* #blocks */
int32 devtype; /* device type */
};
struct drvtyp rs_drv_tab[] = {
{ RS03_SECT, RS03_SURF, RS03_SIZE, RS03_DEV },
{ RS04_SECT, RS04_SURF, RS04_SIZE, RS04_DEV },
{ 0 }
};
struct df10 rs_df10[NUM_DEVS_RS];
uint32 rs_xfer_drive[NUM_DEVS_RS];
uint64 rs_buf[NUM_DEVS_RS][RS_NUMWD];
int rs_reg[NUM_DEVS_RS];
int rs_ivect[NUM_DEVS_RS];
int rs_imode[NUM_DEVS_RS];
int rs_drive[NUM_DEVS_RS];
int rs_rae[NUM_DEVS_RS];
int rs_attn[NUM_DEVS_RS];
extern int readin_flag;
t_stat rs_devio(uint32 dev, uint64 *data);
int rs_devirq(uint32 dev, int addr);
void rs_write(int ctlr, int unit, int reg, uint32 data);
uint32 rs_read(int ctlr, int unit, int reg);
t_stat rs_svc(UNIT *);
t_stat rs_boot(int32, DEVICE *);
void rs_ini(UNIT *, t_bool);
t_stat rs_reset(DEVICE *);
t_stat rs_attach(UNIT *, CONST char *);
t_stat rs_detach(UNIT *);
t_stat rs_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat rs_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *rs_description (DEVICE *dptr);
UNIT rs_unit[] = {
/* Controller 1 */
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
{ UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+
UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) },
};
DIB rs_dib[] = {
{RH10_DEV, 1, &rs_devio, &rs_devirq}
};
MTAB rs_mod[] = {
{UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL},
{UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL},
{UNIT_DTYPE, (RS03_DTYPE << UNIT_V_DTYPE), "RS03", "RS03", &rs_set_type },
{UNIT_DTYPE, (RS04_DTYPE << UNIT_V_DTYPE), "RS04", "RS04", &rs_set_type },
{0}
};
REG rsa_reg[] = {
{ORDATA(IVECT, rs_ivect[0], 18)},
{FLDATA(IMODE, rs_imode[0], 0)},
{ORDATA(XFER, rs_xfer_drive[0], 3), REG_HRO},
{ORDATA(DRIVE, rs_drive[0], 3), REG_HRO},
{ORDATA(REG, rs_reg[0], 6), REG_RO},
{ORDATA(RAE, rs_rae[0], 8), REG_RO},
{ORDATA(ATTN, rs_attn[0], 8), REG_RO},
{FLDATA(READIN, readin_flag, 0), REG_HRO},
{ORDATA(STATUS, rs_df10[0].status, 18), REG_RO},
{ORDATA(CIA, rs_df10[0].cia, 18)},
{ORDATA(CCW, rs_df10[0].ccw, 18)},
{ORDATA(WCR, rs_df10[0].wcr, 18)},
{ORDATA(CDA, rs_df10[0].cda, 18)},
{ORDATA(DEVNUM, rs_df10[0].devnum, 9), REG_HRO},
{ORDATA(BUF, rs_df10[0].buf, 36), REG_HRO},
{ORDATA(NXM, rs_df10[0].nxmerr, 8), REG_HRO},
{ORDATA(COMP, rs_df10[0].ccw_comp, 8), REG_HRO},
{BRDATA(BUFF, &rs_buf[0][0], 16, 64, RS_NUMWD), REG_HRO},
{0}
};
DEVICE rsa_dev = {
"FSA", rs_unit, rsa_reg, rs_mod,
NUM_UNITS_RS, 8, 18, 1, 8, 36,
NULL, NULL, &rs_reset, &rs_boot, &rs_attach, &rs_detach,
&rs_dib[0], DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug,
NULL, NULL, &rs_help, NULL, NULL, &rs_description
};
DEVICE *rs_devs[] = {
&rsa_dev,
};
t_stat rs_devio(uint32 dev, uint64 *data) {
int ctlr = -1;
DEVICE *dptr = NULL;
struct df10 *df10;
int drive;
for (drive = 0; rh[drive].dev_num != 0; drive++) {
if (rh[drive].dev_num == (dev & 0774)) {
dptr = rh[drive].dev;
break;
}
}
if (dptr == NULL)
return SCPE_OK;
ctlr = GET_CNTRL(dptr->units[0].flags);
df10 = &rs_df10[ctlr];
df10->devnum = dev;
switch(dev & 3) {
case CONI:
*data = df10->status & ~(IADR_ATTN|IARD_RAE);
if (rs_attn[ctlr] != 0 && (df10->status & IADR_ATTN))
*data |= IADR_ATTN;
if (rs_rae[ctlr] != 0 && (df10->status & IARD_RAE))
*data |= IARD_RAE;
#if KI_22BIT
*data |= B22_FLAG;
#endif
sim_debug(DEBUG_CONI, dptr, "RS %03o CONI %06o PC=%o %o\n",
dev, (uint32)*data, PC, rs_attn[ctlr]);
return SCPE_OK;
case CONO:
clr_interrupt(dev);
df10->status &= ~(07LL|IADR_ATTN|IARD_RAE);
df10->status |= *data & (07LL|IADR_ATTN|IARD_RAE);
/* Clear flags */
if (*data & CONT_RESET) {
UNIT *uptr=dptr->units;
for(drive = 0; drive < NUM_UNITS_RS; drive++, uptr++) {
uptr->CMD &= DS_MOL|DS_WRL|DS_DPR|DS_DRY|DS_VV|076;
uptr->DA &= 003400177777;
}
}
if (*data & (DBPE_CLR|DR_EXC_CLR|CHN_CLR))
df10->status &= ~(*data & (DBPE_CLR|DR_EXC_CLR|CHN_CLR));
if (*data & OVER_CLR)
df10->status &= ~(DTC_OVER);
if (*data & CBOV_CLR)
df10->status &= ~(DIB_CBOV);
if (*data & CXR_ILC)
df10->status &= ~(CXR_ILFC|CXR_SD_RAE);
if (*data & WRT_CW)
df10_writecw(df10);
if (*data & PI_ENABLE)
df10->status &= ~PI_ENABLE;
if (df10->status & PI_ENABLE)
set_interrupt(dev, df10->status);
if ((df10->status & IADR_ATTN) != 0 && rs_attn[ctlr] != 0)
set_interrupt(dev, df10->status);
sim_debug(DEBUG_CONO, dptr, "RS %03o CONO %06o %d PC=%06o %06o\n",
dev, (uint32)*data, ctlr, PC, df10->status);
return SCPE_OK;
case DATAI:
*data = 0;
if (df10->status & BUSY && rs_reg[ctlr] != 04) {
df10->status |= CC_CHAN_ACT;
return SCPE_OK;
}
if (rs_reg[ctlr] == 040) {
*data = (uint64)(rs_read(ctlr, rs_drive[ctlr], 0) & 077);
*data |= ((uint64)(df10->cia)) << 6;
*data |= ((uint64)(rs_xfer_drive[ctlr])) << 18;
} else if (rs_reg[ctlr] == 044) {
*data = (uint64)rs_ivect[ctlr];
if (rs_imode[ctlr])
*data |= IRQ_KI10;
else
*data |= IRQ_KA10;
} else if (rs_reg[ctlr] == 054) {
*data = (uint64)(rs_rae[ctlr]);
} else if ((rs_reg[ctlr] & 040) == 0) {
int parity;
*data = (uint64)(rs_read(ctlr, rs_drive[ctlr], rs_reg[ctlr]) & 0177777);
parity = (int)((*data >> 8) ^ *data);
parity = (parity >> 4) ^ parity;
parity = (parity >> 2) ^ parity;
parity = ((parity >> 1) ^ parity) & 1;
*data |= ((uint64)(parity ^ 1)) << 17;
*data |= ((uint64)(rs_drive[ctlr])) << 18;
}
*data |= ((uint64)(rs_reg[ctlr])) << 30;
sim_debug(DEBUG_DATAIO, dptr, "RS %03o DATI %012llo, %d %d PC=%06o\n",
dev, *data, ctlr, rs_drive[ctlr], PC);
return SCPE_OK;
case DATAO:
sim_debug(DEBUG_DATAIO, dptr, "RS %03o DATO %012llo, %d PC=%06o %06o\n",
dev, *data, ctlr, PC, df10->status);
rs_reg[ctlr] = ((int)(*data >> 30)) & 077;
if (rs_reg[ctlr] < 040 && rs_reg[ctlr] != 04) {
rs_drive[ctlr] = (int)(*data >> 18) & 07;
}
if (*data & LOAD_REG) {
if (rs_reg[ctlr] == 040) {
if ((*data & 1) == 0) {
return SCPE_OK;
}
if (df10->status & BUSY) {
df10->status |= CC_CHAN_ACT;
return SCPE_OK;
}
df10->status &= ~(1 << df10->ccw_comp);
df10->status &= ~PI_ENABLE;
if (((*data >> 1) & 077) < FNC_XFER) {
df10->status |= CXR_ILC;
df10_setirq(df10);
sim_debug(DEBUG_DATAIO, dptr,
"RS %03o command abort %012llo, %d[%d] PC=%06o %06o\n",
dev, *data, ctlr, rs_drive[ctlr], PC, df10->status);
return SCPE_OK;
}
/* Start command */
df10_setup(df10, (uint32)(*data >> 6));
rs_xfer_drive[ctlr] = (int)(*data >> 18) & 07;
rs_write(ctlr, rs_drive[ctlr], 0, (uint32)(*data & 077));
sim_debug(DEBUG_DATAIO, dptr,
"RS %03o command %012llo, %d[%d] PC=%06o %06o\n",
dev, *data, ctlr, rs_drive[ctlr], PC, df10->status);
} else if (rs_reg[ctlr] == 044) {
/* Set KI10 Irq vector */
rs_ivect[ctlr] = (int)(*data & IRQ_VECT);
rs_imode[ctlr] = (*data & IRQ_KI10) != 0;
} else if (rs_reg[ctlr] == 050) {
; /* Diagnostic access to mass bus. */
} else if (rs_reg[ctlr] == 054) {
/* clear flags */
rs_rae[ctlr] &= ~(*data & 0377);
if (rs_rae[ctlr] == 0)
clr_interrupt(dev);
} else if ((rs_reg[ctlr] & 040) == 0) {
rs_drive[ctlr] = (int)(*data >> 18) & 07;
/* Check if access error */
if (rs_rae[ctlr] & (1 << rs_drive[ctlr])) {
return SCPE_OK;
}
rs_drive[ctlr] = (int)(*data >> 18) & 07;
rs_write(ctlr, rs_drive[ctlr], rs_reg[ctlr] & 037,
(int)(*data & 0777777));
}
}
return SCPE_OK;
}
return SCPE_OK; /* Unreached */
}
/* Handle KI and KL style interrupt vectors */
int
rs_devirq(uint32 dev, int addr) {
DEVICE *dptr = NULL;
int drive;
for (drive = 0; rh[drive].dev_num != 0; drive++) {
if (rh[drive].dev_num == (dev & 0774)) {
dptr = rh[drive].dev;
break;
}
}
if (dptr != NULL) {
drive = GET_CNTRL(dptr->units[0].flags);
return (rs_imode[drive] ? rs_ivect[drive] : addr);
}
return addr;
}
void
rs_write(int ctlr, int unit, int reg, uint32 data) {
int i;
DEVICE *dptr = rs_devs[ctlr];
struct df10 *df10 = &rs_df10[ctlr];
UNIT *uptr = &dptr->units[unit];
if ((uptr->CMD & CR_GO) && reg != 04) {
uptr->CMD |= (ER1_RMR << 16)|DS_ERR;
return;
}
switch(reg) {
case 000: /* control */
sim_debug(DEBUG_DETAIL, dptr, "RSA%o %d Status=%06o\n", unit, ctlr, uptr->CMD);
/* Set if drive not writable */
if (uptr->flags & UNIT_WLK)
uptr->CMD |= DS_WRL;
/* If drive not ready don't do anything */
if ((uptr->CMD & DS_DRY) == 0) {
uptr->CMD |= (ER1_RMR << 16)|DS_ERR;
sim_debug(DEBUG_DETAIL, dptr, "RSA%o %d busy\n", unit, ctlr);
return;
}
/* Check if GO bit set */
if ((data & 1) == 0) {
uptr->CMD &= ~076;
uptr->CMD |= data & 076;
sim_debug(DEBUG_DETAIL, dptr, "RSA%o %d no go\n", unit, ctlr);
return; /* No, nop */
}
uptr->CMD &= DS_ATA|DS_VV|DS_DPR|DS_MOL|DS_WRL;
uptr->CMD |= data & 076;
switch (GET_FNC(data)) {
case FNC_NOP:
uptr->CMD |= DS_DRY;
break;
case FNC_SEARCH: /* search */
case FNC_WCHK: /* write check */
case FNC_WRITE: /* write */
case FNC_READ: /* read */
uptr->CMD |= DS_PIP|CR_GO;
uptr->DATAPTR = 0;
break;
case FNC_PRESET: /* read-in preset */
uptr->DA = 0;
if ((uptr->flags & UNIT_ATT) != 0)
uptr->CMD |= DS_VV;
uptr->CMD |= DS_DRY;
df10_setirq(df10);
break;
case FNC_DCLR: /* drive clear */
uptr->CMD |= DS_DRY;
uptr->CMD &= ~(DS_ATA|CR_GO);
rs_attn[ctlr] = 0;
clr_interrupt(df10->devnum);
for (i = 0; i < 8; i++) {
if (rs_unit[(ctlr * 8) + i].CMD & DS_ATA)
rs_attn[ctlr] = 1;
}
if ((df10->status & IADR_ATTN) != 0 && rs_attn[ctlr] != 0)
df10_setirq(df10);
break;
default:
uptr->CMD |= DS_DRY|DS_ERR|DS_ATA;
uptr->CMD |= (ER1_ILF << 16);
if ((df10->status & IADR_ATTN) != 0 && rs_attn[ctlr] != 0)
df10_setirq(df10);
}
if (uptr->CMD & CR_GO)
sim_activate(uptr, 100);
sim_debug(DEBUG_DETAIL, dptr, "RSA%o AStatus=%06o\n", unit, uptr->CMD);
return;
case 001: /* status */
break;
case 002: /* error register 1 */
uptr->CMD &= 0177777;
uptr->CMD |= data << 16;
if (data != 0)
uptr->CMD |= DS_ERR;
break;
case 003: /* maintenance */
break;
case 004: /* atten summary */
rs_attn[ctlr] = 0;
for (i = 0; i < 8; i++) {
if (data & (1<<i))
rs_unit[(ctlr * 8) + i].CMD &= ~DS_ATA;
if (rs_unit[(ctlr * 8) + i].CMD & DS_ATA)
rs_attn[ctlr] = 1;
}
clr_interrupt(df10->devnum);
if (((df10->status & IADR_ATTN) != 0 && rs_attn[ctlr] != 0) ||
(df10->status & PI_ENABLE))
df10_setirq(df10);
break;
case 005: /* sector/track */
uptr->DA = data & 0177777;
break;
case 006: /* drive type */
case 007: /* look ahead */
break;
default:
uptr->CMD |= (ER1_ILR<<16)|DS_ERR;
rs_rae[ctlr] &= ~(1<<unit);
}
}
uint32
rs_read(int ctlr, int unit, int reg) {
DEVICE *dptr = rs_devs[ctlr];
struct df10 *df10 = &rs_df10[ctlr];
UNIT *uptr = &dptr->units[unit];
uint32 temp = 0;
int i;
if ((uptr->flags & UNIT_ATT) == 0 && reg != 04) { /* not attached? */
return 0;
}
switch(reg) {
case 000: /* control */
temp = uptr->CMD & 077;
if (uptr->flags & UNIT_ATT)
temp |= CS1_DVA;
if ((df10->status & BUSY) == 0 && (uptr->CMD & CR_GO) == 0)
temp |= CS1_GO;
break;
case 001: /* status */
temp = uptr->CMD & 0177700;
break;
case 002: /* error register 1 */
temp = (uptr->CMD >> 16) & 0177777;
break;
case 004: /* atten summary */
for (i = 0; i < 8; i++) {
if (rs_unit[(ctlr * 8) + i].CMD & DS_ATA) {
temp |= 1 << i;
}
}
break;
case 005: /* sector/track */
temp = uptr->DA & 0177777;
break;
case 006: /* drive type */
temp = rs_drv_tab[GET_DTYPE(uptr->flags)].devtype;
break;
case 003: /* maintenance */
case 007: /* look ahead */
break;
default:
uptr->CMD |= (ER1_ILR<<16);
rs_rae[ctlr] &= ~(1<<unit);
}
return temp;
}
t_stat rs_svc (UNIT *uptr)
{
int dtype = GET_DTYPE(uptr->flags);
int ctlr = GET_CNTRL(uptr->flags);
int unit;
DEVICE *dptr;
struct df10 *df;
int da;
t_stat r;
/* Find dptr, and df10 */
dptr = rs_devs[ctlr];
unit = uptr - dptr->units;
df = &rs_df10[ctlr];
if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */
uptr->CMD |= (ER1_UNS << 16) | DS_ATA|DS_ERR; /* set drive error */
df->status &= ~BUSY;
df10_setirq(df);
return (SCPE_OK);
}
/* Check if seeking */
if (uptr->CMD & DS_PIP) {
uptr->CMD &= ~DS_PIP;
uptr->DATAPTR = 0;
}
switch (GET_FNC(uptr->CMD)) {
case FNC_NOP:
case FNC_DCLR: /* drive clear */
break;
case FNC_PRESET: /* read-in preset */
rs_attn[ctlr] = 1;
uptr->CMD |= DS_DRY|DS_ATA;
uptr->CMD &= ~CR_GO;
df->status &= ~BUSY;
if (df->status & IADR_ATTN)
df10_setirq(df);
sim_debug(DEBUG_DETAIL, dptr, "RSA%o seekdone\n", unit);
break;
case FNC_SEARCH: /* search */
if (GET_SC(uptr->DA) >= rs_drv_tab[dtype].sect ||
GET_SF(uptr->DA) >= rs_drv_tab[dtype].surf)
uptr->CMD |= (ER1_IAE << 16)|DS_ERR;
rs_attn[ctlr] = 1;
uptr->CMD |= DS_DRY|DS_ATA;
uptr->CMD &= ~CR_GO;
df->status &= ~BUSY;
if ((df->status & (IADR_ATTN|BUSY)) == IADR_ATTN)
df10_setirq(df);
sim_debug(DEBUG_DETAIL, dptr, "RSA%o searchdone\n", unit);
break;
case FNC_READ: /* read */
case FNC_WCHK: /* write check */
if (uptr->DATAPTR == 0) {
int wc;
if (GET_SC(uptr->DA) >= rs_drv_tab[dtype].sect ||
GET_SF(uptr->DA) >= rs_drv_tab[dtype].surf) {
uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA;
df->status &= ~BUSY;
uptr->CMD &= ~CR_GO;
sim_debug(DEBUG_DETAIL, dptr, "RSA%o readx done\n", unit);
df10_finish_op(df, 0);
return SCPE_OK;
}
sim_debug(DEBUG_DETAIL, dptr, "RSA%o read (%d,%d)\n", unit,
GET_SC(uptr->DA), GET_SF(uptr->DA));
da = GET_DA(uptr->DA, dtype) * RS_NUMWD;
(void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fread (&rs_buf[ctlr][0], sizeof(uint64), RS_NUMWD,
uptr->fileref);
while (wc < RS_NUMWD)
rs_buf[ctlr][wc++] = 0;
uptr->hwmark = RS_NUMWD;
}
df->buf = rs_buf[ctlr][uptr->DATAPTR++];
sim_debug(DEBUG_DATA, dptr, "RSA%o read word %d %012llo %09o %06o\n",
unit, uptr->DATAPTR, df->buf, df->cda, df->wcr);
if (df10_write(df)) {
if (uptr->DATAPTR == uptr->hwmark) {
/* Increment to next sector. Set Last Sector */
uptr->DATAPTR = 0;
uptr->DA += 1 << DA_V_SC;
if (GET_SC(uptr->DA) >= rs_drv_tab[dtype].sect) {
uptr->DA &= (DA_M_SF << DA_V_SF);
uptr->DA += 1 << DA_V_SF;
if (GET_SF(uptr->DA) >= rs_drv_tab[dtype].surf)
uptr->CMD |= DS_LST;
}
}
sim_activate(uptr, 20);
} else {
sim_debug(DEBUG_DETAIL, dptr, "RSA%o read done\n", unit);
uptr->CMD |= DS_DRY;
uptr->CMD &= ~CR_GO;
df10_finish_op(df, 0);
return SCPE_OK;
}
break;
case FNC_WRITE: /* write */
if (uptr->DATAPTR == 0) {
if (GET_SC(uptr->DA) >= rs_drv_tab[dtype].sect ||
GET_SF(uptr->DA) >= rs_drv_tab[dtype].surf) {
uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA;
uptr->CMD &= ~CR_GO;
sim_debug(DEBUG_DETAIL, dptr, "RSA%o writex done\n", unit);
df10_finish_op(df, 0);
return SCPE_OK;
}
}
r = df10_read(df);
rs_buf[ctlr][uptr->DATAPTR++] = df->buf;
sim_debug(DEBUG_DATA, dptr, "RSA%o write word %d %012llo %09o %06o\n",
unit, uptr->DATAPTR, df->buf, df->cda, df->wcr);
if (r == 0 || uptr->DATAPTR == RS_NUMWD) {
while (uptr->DATAPTR < RS_NUMWD)
rs_buf[ctlr][uptr->DATAPTR++] = 0;
sim_debug(DEBUG_DETAIL, dptr, "RSA%o write (%d,%d)\n", unit,
GET_SC(uptr->DA), GET_SF(uptr->DA));
da = GET_DA(uptr->DA, dtype) * RS_NUMWD;
(void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
(void)sim_fwrite (&rs_buf[ctlr][0], sizeof(uint64), RS_NUMWD,
uptr->fileref);
uptr->DATAPTR = 0;
if (r) {
uptr->DA += 1 << DA_V_SC;
if (GET_SC(uptr->DA) >= rs_drv_tab[dtype].sect) {
uptr->DA &= (DA_M_SF << DA_V_SF);
uptr->DA += 1 << DA_V_SF;
if (GET_SF(uptr->DA) >= rs_drv_tab[dtype].surf)
uptr->CMD |= DS_LST;
}
}
}
if (r) {
sim_activate(uptr, 20);
} else {
sim_debug(DEBUG_DETAIL, dptr, "RSA%o write done\n", unit);
uptr->CMD |= DS_DRY;
uptr->CMD &= ~CR_GO;
df10_finish_op(df, 0);
return SCPE_OK;
}
break;
}
return SCPE_OK;
}
t_stat
rs_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int i;
if (uptr == NULL) return SCPE_IERR;
uptr->flags &= ~(UNIT_DTYPE);
uptr->flags |= val;
i = GET_DTYPE(val);
uptr->capac = rs_drv_tab[i].size;
return SCPE_OK;
}
t_stat
rs_reset(DEVICE * rstr)
{
int ctlr;
for (ctlr = 0; ctlr < NUM_DEVS_RS; ctlr++) {
rs_df10[ctlr].devnum = rs_dib[ctlr].dev_num;
rs_df10[ctlr].nxmerr = 19;
rs_df10[ctlr].ccw_comp = 14;
rs_df10[ctlr].status = 0;
rs_attn[ctlr] = 0;
rs_rae[ctlr] = 0;
}
return SCPE_OK;
}
/* Boot from given device */
t_stat
rs_boot(int32 unit_num, DEVICE * rptr)
{
UNIT *uptr = &rptr->units[unit_num];
int ctlr = GET_CNTRL(uptr->flags);
DEVICE *dptr;
struct df10 *df;
uint32 addr;
uint32 ptr = 0;
uint64 word;
int wc;
df = &rs_df10[ctlr];
dptr = rs_devs[ctlr];
(void)sim_fseek(uptr->fileref, 0, SEEK_SET);
(void)sim_fread (&rs_buf[0][0], sizeof(uint64), RS_NUMWD, uptr->fileref);
uptr->CMD |= DS_VV;
addr = rs_buf[0][ptr] & RMASK;
wc = (rs_buf[0][ptr++] >> 18) & RMASK;
while (wc != 0) {
wc = (wc + 1) & RMASK;
addr = (addr + 1) & RMASK;
word = rs_buf[0][ptr++];
if (addr < 020)
FM[addr] = word;
else
M[addr] = word;
}
addr = rs_buf[0][ptr] & RMASK;
wc = (rs_buf[0][ptr++] >> 18) & RMASK;
word = rs_buf[0][ptr++];
rs_reg[ctlr] = 040;
rs_drive[ctlr] = uptr - dptr->units;
df->status |= CCW_COMP_1|PI_ENABLE;
PC = word & RMASK;
return SCPE_OK;
}
/* Device attach */
t_stat rs_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
DEVICE *rstr;
DIB *dib;
int ctlr;
uptr->capac = rs_drv_tab[GET_DTYPE (uptr->flags)].size;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
return r;
rstr = find_dev_from_unit(uptr);
if (rstr == 0)
return SCPE_OK;
dib = (DIB *) rstr->ctxt;
ctlr = dib->dev_num & 014;
uptr->DA = 0;
uptr->CMD &= ~DS_VV;
uptr->CMD |= DS_DPR|DS_MOL|DS_DRY;
if (uptr->flags & UNIT_WLK)
uptr->CMD |= DS_WRL;
rs_df10[ctlr].status |= PI_ENABLE;
set_interrupt(dib->dev_num, rs_df10[ctlr].status);
return SCPE_OK;
}
/* Device detach */
t_stat rs_detach (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK;
if (sim_is_active (uptr)) /* unit active? */
sim_cancel (uptr); /* cancel operation */
uptr->CMD &= ~(DS_VV|DS_WRL|DS_DPR|DS_DRY);
return detach_unit (uptr);
}
t_stat rs_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "RS04 Disk Pack Drives (RS)\n\n");
fprintf (st, "The RS controller implements the Massbus family of fast disk drives. RS\n");
fprintf (st, "options include the ability to set units write enabled or write locked, to\n");
fprintf (st, "set the drive type to one of six disk types or autosize, and to write a DEC\n");
fprintf (st, "standard 044 compliant bad block table on the last track.\n\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprintf (st, "\nThe type options can be used only when a unit is not attached to a file.\n");
fprintf (st, "The RS device supports the BOOT command.\n");
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *rs_description (DEVICE *dptr)
{
return "RS04 Massbus disk controller";
}
#endif

1187
PDP10/kx10_sys.c Normal file

File diff suppressed because it is too large Load diff

1161
PDP10/kx10_tu.c Normal file

File diff suppressed because it is too large Load diff

482
PDP10/pdp6_dcs.c Normal file
View file

@ -0,0 +1,482 @@
/* pdp6_dcs.c: PDP-6 DC630 communication server simulator
Copyright (c) 2011-2017, Richard Cornwell
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
RICHARD CORNWELL 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 Richard Cornwell shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Richard Cornwell.
*/
#include "kx10_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#ifndef NUM_DEVS_DCS
#define NUM_DEVS_DCS 0
#endif
#if (NUM_DEVS_DCS > 0)
#define DCS_DEVNUM 0300
#define DCS_LINES 16
#define STATUS u3
#define RPI_CHN 000007 /* IN STATUS. */
#define TPI_CHN 000700 /* In STATUS */
#define RLS_SCN 000010 /* CONO DCSA release scanner */
#define RST_SCN 000020 /* CONO DCSA reset to 0 */
#define RSCN_ACT 000040 /* Scanner line is active */
#define XMT_RLS 004000 /* Clear transmitter flag */
#define XSCN_ACT 004000 /* Transmit scanner active */
#define DATA 0000377
#define LINE 0000077 /* Line number in Left */
int dcs_rx_scan = 0; /* Scan counter */
int dcs_tx_scan = 0; /* Scan counter */
int dcs_send_line = 0; /* Send line number */
TMLN dcs_ldsc[DCS_LINES] = { 0 }; /* Line descriptors */
TMXR dcs_desc = { DCS_LINES, 0, 0, dcs_ldsc };
uint32 dcs_tx_enable, dcs_rx_rdy; /* Flags */
uint32 dcs_enable; /* Enable line */
uint32 dcs_rx_conn; /* Connection flags */
extern int32 tmxr_poll;
t_stat dcs_devio(uint32 dev, uint64 *data);
t_stat dcs_svc (UNIT *uptr);
t_stat dcs_doscan (UNIT *uptr);
t_stat dcs_reset (DEVICE *dptr);
t_stat dcs_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dcs_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dcs_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat dcs_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat dcs_attach (UNIT *uptr, CONST char *cptr);
t_stat dcs_detach (UNIT *uptr);
t_stat dcs_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *dcs_description (DEVICE *dptr);
/* Type 630 data structures
dcs_dev Type 630 device descriptor
dcs_unit Type 630 unit descriptor
dcs_reg Type 630 register list
*/
#if !PDP6
#define D DEV_DIS
#else
#define D 0
#endif
DIB dcs_dib = { DCS_DEVNUM, 2, &dcs_devio, NULL };
UNIT dcs_unit = {
UDATA (&dcs_svc, TT_MODE_7B+UNIT_IDLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT
};
REG dcs_reg[] = {
{ DRDATA (TIME, dcs_unit.wait, 24), REG_NZ + PV_LEFT },
{ DRDATA (STATUS, dcs_unit.STATUS, 18), PV_LEFT },
{ NULL }
};
MTAB dcs_mod[] = {
{ TT_MODE, TT_MODE_KSR, "KSR", "KSR", NULL },
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &dcs_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
NULL, &tmxr_show_summ, (void *) &dcs_desc, "Display a summary of line states" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &dcs_desc, "Display current connections" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &dcs_desc, "Display multiplexer statistics" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
&dcs_setnl, &tmxr_show_lines, (void *) &dcs_desc, "Set number of lines" },
{ MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file",
&dcs_set_log, NULL, (void *)&dcs_desc },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG",
&dcs_set_nolog, NULL, (void *)&dcs_desc, "Disable logging on designated line" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL,
NULL, &dcs_show_log, (void *)&dcs_desc, "Display logging for all lines" },
{ 0 }
};
DEVICE dcs_dev = {
"DCS", &dcs_unit, dcs_reg, dcs_mod,
1, 10, 31, 1, 8, 8,
&tmxr_ex, &tmxr_dep, &dcs_reset,
NULL, &dcs_attach, &dcs_detach,
&dcs_dib, DEV_MUX | DEV_DISABLE | DEV_DEBUG | D, 0, dev_debug,
NULL, NULL, &dcs_help, NULL, NULL, &dcs_description
};
/* IOT routine */
t_stat dcs_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &dcs_unit;
TMLN *lp;
int ln;
switch(dev & 7) {
case CONI:
/* Check if we might have any interrupts pending */
if ((uptr->STATUS & (RSCN_ACT|XSCN_ACT)) != 0)
dcs_doscan(uptr);
*data = uptr->STATUS & (RPI_CHN|TPI_CHN);
if ((uptr->STATUS & (RSCN_ACT)) == 0)
*data |= 010LL;
if ((uptr->STATUS & (XSCN_ACT)) == 0)
*data |= 01000LL;
sim_debug(DEBUG_CONI, &dcs_dev, "DCS %03o CONI %06o PC=%o\n",
dev, (uint32)*data, PC);
break;
case CONO:
/* Set PI */
uptr->STATUS &= ~(RPI_CHN|TPI_CHN);
uptr->STATUS |= (RPI_CHN|TPI_CHN) & *data;
if (*data & RST_SCN)
dcs_rx_scan = 0;
if ((*data & (RLS_SCN|RST_SCN)) != 0)
uptr->STATUS |= RSCN_ACT;
if ((*data & (XMT_RLS)) != 0) {
uptr->STATUS |= XSCN_ACT;
dcs_tx_enable &= ~(1 << dcs_tx_scan);
}
sim_debug(DEBUG_CONO, &dcs_dev, "DCS %03o CONO %06o PC=%06o\n",
dev, (uint32)*data, PC);
dcs_doscan(uptr);
break;
case DATAO:
case DATAO|4:
ln = (dev & 4) ? dcs_send_line : dcs_tx_scan;
if (ln < dcs_desc.lines) {
lp = &dcs_ldsc[ln];
if (lp->conn) {
int32 ch = *data & DATA;
ch = sim_tt_outcvt(ch, TT_GET_MODE (dcs_unit.flags) | TTUF_KSR);
tmxr_putc_ln (lp, ch);
dcs_tx_enable |= (1 << ln);
}
}
if (dev & 4) {
uptr->STATUS |= XSCN_ACT;
dcs_doscan(uptr);
}
sim_debug(DEBUG_DATAIO, &dcs_dev, "DC %03o DATO %012llo PC=%06o\n",
dev, *data, PC);
break;
case DATAI:
case DATAI|4:
ln = dcs_rx_scan;
if (ln < dcs_desc.lines) {
/* Nothing happens if no recieve data, which is transmit ready */
lp = &dcs_ldsc[ln];
if (tmxr_rqln (lp) > 0) {
int32 ch = tmxr_getc_ln (lp);
if (ch & SCPE_BREAK) /* break? */
ch = 0;
else
ch = sim_tt_inpcvt (ch, TT_GET_MODE(dcs_unit.flags) | TTUF_KSR);
*data = (uint64)(ch & DATA);
dcs_tx_enable &= ~(1 << ln);
}
dcs_rx_rdy &= ~(1 << ln);
}
if (dev & 4) {
uptr->STATUS |= RSCN_ACT;
dcs_doscan(uptr);
}
sim_debug(DEBUG_DATAIO, &dcs_dev, "DCS %03o DATI %012llo PC=%06o\n",
dev, *data, PC);
break;
case CONI|4:
/* Read in scanner */
if ((uptr->STATUS & (RSCN_ACT)) != 0)
*data = (uint64)(dcs_tx_scan) + 2;
else
*data = (uint64)(dcs_rx_scan) + 2;
sim_debug(DEBUG_CONI, &dcs_dev, "DCS %03o CONI %06o PC=%o recieve line\n",
dev, (uint32)*data, PC);
break;
case CONO|4:
/* Output buffer pointer */
dcs_send_line = (int)(*data & 077) - 2;
sim_debug(DEBUG_CONO, &dcs_dev, "DCS %03o CONO %06o PC=%06o send line\n",
dev, (uint32)*data, PC);
break;
}
return SCPE_OK;
}
/* Unit service */
t_stat dcs_svc (UNIT *uptr)
{
int32 ln;
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_OK;
ln = tmxr_poll_conn (&dcs_desc); /* look for connect */
if (ln >= 0) { /* got one? rcv enb*/
dcs_ldsc[ln].rcve = 1;
dcs_tx_enable |= 1 << ln;
sim_debug(DEBUG_DETAIL, &dcs_dev, "DC line connect %d\n", ln);
}
tmxr_poll_tx(&dcs_desc);
tmxr_poll_rx(&dcs_desc);
for (ln = 0; ln < dcs_desc.lines; ln++) {
/* Check to see if any pending data for this line */
if (tmxr_rqln(&dcs_ldsc[ln]) > 0) {
dcs_rx_rdy |= (1 << ln);
sim_debug(DEBUG_DETAIL, &dcs_dev, "DC recieve %d\n", ln);
}
/* Check if disconnect */
if ((dcs_rx_conn & (1 << ln)) != 0 && dcs_ldsc[ln].conn == 0) {
dcs_tx_enable &= ~(1 << ln);
dcs_rx_conn &= ~(1 << ln);
sim_debug(DEBUG_DETAIL, &dcs_dev, "DC line disconnect %d\n", ln);
}
}
/* If any pending status request, raise the PI signal */
dcs_doscan(uptr);
sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */
return SCPE_OK;
}
/* Scan to see if something to do */
t_stat dcs_doscan (UNIT *uptr) {
clr_interrupt(DCS_DEVNUM);
if ((uptr->STATUS & (RSCN_ACT)) != 0) {
for (;dcs_rx_rdy != 0; dcs_rx_scan++) {
dcs_rx_scan &= 037;
/* Check if we found it */
if (dcs_rx_rdy & (1 << dcs_rx_scan)) {
uptr->STATUS &= ~RSCN_ACT;
/* Stop scanner */
set_interrupt(DCS_DEVNUM, uptr->STATUS);
return SCPE_OK;
}
}
}
if ((uptr->STATUS & (XSCN_ACT)) != 0) {
for (;dcs_tx_enable != 0; dcs_tx_scan++) {
dcs_tx_scan &= 037;
/* Check if we found it */
if (dcs_tx_enable & (1 << dcs_tx_scan)) {
uptr->STATUS &= ~XSCN_ACT;
/* Stop scanner */
set_interrupt(DCS_DEVNUM, (uptr->STATUS >> 6));
return SCPE_OK;
}
}
}
return SCPE_OK;
}
/* Reset routine */
t_stat dcs_reset (DEVICE *dptr)
{
if (dcs_unit.flags & UNIT_ATT) /* if attached, */
sim_activate (&dcs_unit, tmxr_poll); /* activate */
else
sim_cancel (&dcs_unit); /* else stop */
dcs_tx_enable = 0;
dcs_rx_rdy = 0; /* Flags */
dcs_rx_conn = 0;
dcs_send_line = 0;
dcs_tx_scan = 0;
dcs_rx_scan = 0;
dcs_unit.STATUS = 0;
clr_interrupt(DCS_DEVNUM);
return SCPE_OK;
}
/* SET LINES processor */
t_stat dcs_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 newln, i, t;
t_stat r;
if (cptr == NULL)
return SCPE_ARG;
newln = (int32) get_uint (cptr, 10, DCS_LINES, &r);
if ((r != SCPE_OK) || (newln == dcs_desc.lines))
return r;
if ((newln == 0) || (newln >= DCS_LINES) || (newln % 8) != 0)
return SCPE_ARG;
if (newln < dcs_desc.lines) {
for (i = newln, t = 0; i < dcs_desc.lines; i++)
t = t | dcs_ldsc[i].conn;
if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
return SCPE_OK;
for (i = newln; i < dcs_desc.lines; i++) {
if (dcs_ldsc[i].conn) {
tmxr_linemsg (&dcs_ldsc[i], "\r\nOperator disconnected line\r\n");
tmxr_send_buffered_data (&dcs_ldsc[i]);
}
tmxr_detach_ln (&dcs_ldsc[i]); /* completely reset line */
}
}
if (dcs_desc.lines < newln)
memset (dcs_ldsc + dcs_desc.lines, 0, sizeof(*dcs_ldsc)*(newln-dcs_desc.lines));
dcs_desc.lines = newln;
return dcs_reset (&dcs_dev); /* setup lines and auto config */
}
/* SET LOG processor */
t_stat dcs_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
char gbuf[CBUFSIZE];
int32 ln;
if (cptr == NULL)
return SCPE_ARG;
cptr = get_glyph (cptr, gbuf, '=');
if ((cptr == NULL) || (*cptr == 0) || (gbuf[0] == 0))
return SCPE_ARG;
ln = (int32) get_uint (gbuf, 10, dcs_desc.lines, &r);
if ((r != SCPE_OK) || (ln >= dcs_desc.lines))
return SCPE_ARG;
return tmxr_set_log (NULL, ln, cptr, desc);
}
/* SET NOLOG processor */
t_stat dcs_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
t_stat r;
int32 ln;
if (cptr == NULL)
return SCPE_ARG;
ln = (int32) get_uint (cptr, 10, dcs_desc.lines, &r);
if ((r != SCPE_OK) || (ln >= dcs_desc.lines))
return SCPE_ARG;
return tmxr_set_nolog (NULL, ln, NULL, desc);
}
/* SHOW LOG processor */
t_stat dcs_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 i;
for (i = 0; i < dcs_desc.lines; i++) {
fprintf (st, "line %d: ", i);
tmxr_show_log (st, NULL, i, desc);
fprintf (st, "\n");
}
return SCPE_OK;
}
/* Attach routine */
t_stat dcs_attach (UNIT *uptr, CONST char *cptr)
{
t_stat reason;
reason = tmxr_attach (&dcs_desc, uptr, cptr);
if (reason != SCPE_OK)
return reason;
sim_activate (uptr, tmxr_poll);
return SCPE_OK;
}
/* Detach routine */
t_stat dcs_detach (UNIT *uptr)
{
int32 i;
t_stat reason;
reason = tmxr_detach (&dcs_desc, uptr);
for (i = 0; i < dcs_desc.lines; i++)
dcs_ldsc[i].rcve = 0;
sim_cancel (uptr);
return reason;
}
t_stat dcs_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Type 630 Terminal Interfaces\n\n");
fprintf (st, "The Type 630 supported up to 8 blocks of 8 lines. Modem control was on a seperate\n");
fprintf (st, "line. The number of lines is specified with a SET command:\n\n");
fprintf (st, " sim> SET DCS LINES=n set number of additional lines to n [8-32]\n\n");
fprintf (st, "Lines must be set in multiples of 8.\n");
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
tmxr_attach_help (st, dptr, uptr, flag, cptr);
fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n");
fprintf (st, " mode input characters output characters\n\n");
fprintf (st, " UC lower case converted lower case converted to upper case,\n");
fprintf (st, " to upper case, high-order bit cleared,\n");
fprintf (st, " high-order bit cleared non-printing characters suppressed\n");
fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n");
fprintf (st, " non-printing characters suppressed\n");
fprintf (st, " 7B high-order bit cleared high-order bit cleared\n");
fprintf (st, " 8B no changes no changes\n\n");
fprintf (st, "The default mode is 7P.\n");
fprintf (st, "Finally, each line supports output logging. The SET DCn LOG command enables\n");
fprintf (st, "logging on a line:\n\n");
fprintf (st, " sim> SET DCSn LOG=filename log output of line n to filename\n\n");
fprintf (st, "The SET DCSn NOLOG command disables logging and closes the open log file,\n");
fprintf (st, "if any.\n\n");
fprintf (st, "Once DC is attached and the simulator is running, the terminals listen for\n");
fprintf (st, "connections on the specified port. They assume that the incoming connections\n");
fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n");
fprintf (st, "by the Telnet client, a SET DC DISCONNECT command, or a DETACH DC command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, " sim> SHOW DCS CONNECTIONS show current connections\n");
fprintf (st, " sim> SHOW DCS STATISTICS show statistics for active connections\n");
fprintf (st, " sim> SET DCSn DISCONNECT disconnects the specified line.\n");
fprint_reg_help (st, &dcs_dev);
fprintf (st, "\nThe additional terminals do not support save and restore. All open connections\n");
fprintf (st, "are lost when the simulator shuts down or DC is detached.\n");
return SCPE_OK;
}
const char *dcs_description (DEVICE *dptr)
{
return "Type 630 asynchronous line interface";
}
#endif

284
PDP10/pdp6_dct.c Normal file
View file

@ -0,0 +1,284 @@
/* ka10_dct.c: Type 136 Data Control
Copyright (c) 2013-2019, Richard Cornwell
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
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_DCT
#define NUM_DEVS_DCT 0
#endif
#if (NUM_DEVS_DCT > 0)
#define DCT_DEVNUM 0200 /* First device number */
#define STATUS u3
/* CONI/CONO Flags */
#define PIA 0000000000007LL
#define DEV 0000000000070LL
#define PACK 0000000000300LL
#define IN_OUT 0000000000400LL
#define DB_RQ 0000000001000LL /* DCT has data for 10 or needs data */
#define DB_AC 0000000002000LL /* DCT has completed a word. */
#define DB_MV 0000000004000LL /* Data needs to be moved between buffers */
#define MISS 0000000010000LL
#define NUM_CHARS 0000000160000LL
uint64 dct_buf[NUM_DEVS_DCT];
uint64 dct_acc[NUM_DEVS_DCT];
t_stat dct_devio(uint32 dev, uint64 *data);
t_stat dct_svc(UNIT *);
t_stat dct_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *dct_description (DEVICE *dptr);
#if !PDP6
#define D DEV_DIS
#else
#define D 0
#endif
UNIT dct_unit[] = {
/* Controller 1 */
{ UDATA (&dct_svc, UNIT_DISABLE, 0) },
{ UDATA (&dct_svc, UNIT_DISABLE, 0) },
};
DIB dct_dib[] = {
{DCT_DEVNUM, NUM_DEVS_DCT, &dct_devio, NULL},
};
REG dct_reg[] = {
{BRDATA(BUFF, &dct_buf[0], 16, 36, NUM_DEVS_DCT), REG_HRO},
{BRDATA(BUFF, &dct_acc[0], 16, 36, NUM_DEVS_DCT), REG_HRO},
{0}
};
DEVICE dct_dev = {
"DCT", dct_unit, dct_reg, NULL,
NUM_DEVS_DCT, 8, 18, 1, 8, 36,
NULL, NULL, NULL, NULL, NULL, NULL,
&dct_dib[0], DEV_DISABLE | DEV_DEBUG | D, 0, dev_debug,
NULL, NULL, &dct_help, NULL, NULL, &dct_description
};
t_stat
dct_devio(uint32 dev, uint64 *data) {
int u;
UNIT *uptr = NULL;
u = (dev - dct_dib[0].dev_num) >> 2;
if (u >= NUM_DEVS_DCT)
return SCPE_OK;
uptr = &dct_unit[u];
switch(dev & 3) {
case CONI:
*data = (uint64)(uptr->STATUS);
sim_debug(DEBUG_CONI, &dct_dev, "DCT %03o CONI %012llo %d PC=%o\n", dev,
*data, u, PC);
break;
case CONO:
clr_interrupt(dev);
/* Clear flags */
uptr->STATUS = *data & 017777;
if (uptr->STATUS & DB_RQ)
set_interrupt(dev, uptr->STATUS);
sim_debug(DEBUG_CONO, &dct_dev, "DCT %03o CONO %06o %d PC=%o %06o\n", dev,
(uint32)*data, u, PC, uptr->STATUS);
break;
case DATAI:
clr_interrupt(dev);
if (uptr->STATUS & DB_RQ) {
*data = dct_buf[u];
uptr->STATUS &= ~(DB_RQ);
uptr->STATUS |= DB_MV;
sim_activate(uptr, 10);
}
sim_debug(DEBUG_DATAIO, &dct_dev, "DCT %03o DATI %012llo %d PC=%o\n",
dev, *data, u, PC);
break;
case DATAO:
clr_interrupt(dev);
sim_debug(DEBUG_DATAIO, &dct_dev, "DCT %03o DATO %012llo, %d PC=%o\n",
dev, *data, u, PC);
if (uptr->STATUS & DB_RQ) {
dct_buf[u] = *data;
uptr->STATUS &= ~(DB_RQ);
uptr->STATUS |= DB_MV;
sim_activate(uptr, 10);
}
}
return SCPE_OK;
}
/* OUT = 0, dev-> 10, OUT=1 10 -> dev */
/* OUT starts with RQ & AC with MV =0 */
/* IN starts with MV = 1 RQ & AC = 0 */
t_stat
dct_svc (UNIT *uptr)
{
int u = uptr - dct_unit;
int dev = dct_dib[0].dev_num + (u << 2);
/* Transfer from 10 to device */
if ((uptr->STATUS & (DB_MV|IN_OUT|DB_AC|DB_RQ)) == (DB_AC|DB_MV|IN_OUT)) {
dct_acc[u] = dct_buf[u];
uptr->STATUS &= ~(DB_MV|DB_AC);
uptr->STATUS |= DB_RQ;
}
/* Tranfer from device to 10 */
if ((uptr->STATUS & (DB_MV|IN_OUT|DB_AC|DB_RQ)) == (DB_AC|DB_MV)) {
dct_buf[u] = dct_acc[u];
uptr->STATUS &= ~(DB_MV|DB_AC);
uptr->STATUS |= DB_RQ;
}
if (uptr->STATUS & DB_RQ)
set_interrupt(dev, uptr->STATUS);
return SCPE_OK;
}
/* Check if the dct is still connected to this device. */
int
dct_is_connect (int dev)
{
int d = dev & 07;
int u = (dev >> 3) & 07;
UNIT *uptr;
/* Valid device? */
if (u >= NUM_DEVS_DCT)
return 0;
uptr = &dct_unit[u];
/* Is DCT pointed at this device? */
if (((uptr->STATUS & DEV) >> 3) != d)
return 0;
/* If sending processor to device, and no data, terminate */
if ((uptr->STATUS & IN_OUT) != 0 && (uptr->STATUS & DB_AC) != 0)
return 0;
/* Everything ok, still connected */
return 1;
}
/* Read data from memory */
int
dct_read (int dev, uint64 *data, int cnt)
{
int d = dev & 07;
int u = (dev >> 3) & 07;
DEVICE *dptr = &dct_dev;
UNIT *uptr;
/* Valid device? */
if (u >= NUM_DEVS_DCT)
return 0;
uptr = &dct_unit[u];
/* Is DCT pointed at this device? */
if (((uptr->STATUS & DEV) >> 3) != d)
return 0;
/* Check if correct direction */
if ((uptr->STATUS & IN_OUT) == 0)
return 0;
/* If we have data return it */
if ((uptr->STATUS & DB_AC) == 0) {
*data = dct_acc[u];
sim_debug(DEBUG_DATA, dptr, "DCT Read %012llo, %d \n",
*data, u);
uptr->STATUS &= ~(NUM_CHARS);
uptr->STATUS |= DB_AC | DB_MV | ((cnt & 7) << 13);
sim_activate(uptr, 20);
return 1;
}
return 0;
}
/* Write data to memory */
int
dct_write (int dev, uint64 *data, int cnt)
{
int d = dev & 07;
int u = (dev >> 3) & 07;
DEVICE *dptr = &dct_dev;
UNIT *uptr;
/* Valid device? */
if (u >= NUM_DEVS_DCT)
return 0;
uptr = &dct_unit[u];
/* Is DCT pointed at this device? */
if (((uptr->STATUS & DEV) >> 3) != d)
return 0;
/* Check if correct direction */
if ((uptr->STATUS & IN_OUT) != 0)
return 0;
/* If buffer is empty put data in it. */
if ((uptr->STATUS & DB_AC) == 0) {
dct_acc[u] = *data;
sim_debug(DEBUG_DATA, dptr, "DCT Write %012llo, %d %06o\n", *data, u, uptr->STATUS);
uptr->STATUS &= ~(NUM_CHARS);
uptr->STATUS |= DB_AC | DB_MV | ((cnt & 7) << 13);
sim_activate(uptr, 20);
return 1;
}
return 0;
}
t_stat
dct_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Data Controller Type 136 is a data buffer between fast ");
fprintf (st, "devices and the PDP6. Individual devices are hooked up to ports ");
fprintf (st, "on each DCT.\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
return SCPE_OK;
}
const char *
dct_description (DEVICE *dptr)
{
return "Data Controller Type 136";
}
#endif

511
PDP10/pdp6_dsk.c Normal file
View file

@ -0,0 +1,511 @@
/* ka10_dsk.c: 270 Disk Controller.
Copyright (c) 2013-2019, Richard Cornwell
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 MEDSKHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL 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.
*/
#include "kx10_defs.h"
#ifndef NUM_DEVS_DSK
#define NUM_DEVS_DSK 0
#endif
#if (NUM_DEVS_DSK > 0)
#define DSK_DEVNUM 0270 /* 0174 */
#define NUM_UNITS_DSK 4
/* Flags in the unit flags word */
#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */
#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */
#define UNIT_M_DTYPE 1
#define UNIT_WLK (1 << UNIT_V_WLK)
#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE)
#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE)
#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */
/* Parameters in the unit descriptor */
#define CUR_CYL u3 /* current cylinder */
#define DATAPTR u4 /* data pointer */
#define UFLAGS u5 /* Function */
/* CONO bits */
#define PIA 0000007
#define EIS 0000010 /* Enable Idle State */
#define EFR 0000020 /* Enable file ready */
#define EES 0000040 /* Enable end of sector */
#define EFE 0000100 /* Enable file error */
#define SCL 0000200 /* Clear error flags WO */
#define MRB 0000200 /* Meter read bad RO */
#define MRG 0000400 /* Meter read good RO */
#define CMD 0003000 /* Command */
#define WR_CMD 0002000 /* Command is write */
#define RD_CMD 0001000 /* Command is read */
#define END 0010000 /* End */
#define CLR 0020000 /* Clear */
#define MCL 0040000 /* Master clear RO */
/* CONI bits */
/* Upper 18 bits, same as CONO */
#define SECT_END (1 << 18) /* End of sector flag set */
#define DCE 0001000 /* Data clock error */
#define CME 0000400 /* Command error */
#define WLE 0000200 /* Write lock error */
#define ADE 0000100 /* Not operational */
#define ALM 0000040 /* ALARM */
#define DRL 0000020 /* Data request late */
#define RCE 0000010 /* Read Compare error */
#define PER 0000004 /* Parity error */
#define FER 0000002 /* File error */
#define OPR 0000001 /* Not operational */
/* Octoflop states */
#define IDS 0200 /* Selcted idle state */
#define SNA 0100 /* Selected new address */
#define ADT 0040 /* Address terminated */
#define DFR 0020 /* Disc file ready */
#define ALS 0010 /* Alert state */
#define CMS 0004 /* Command selected */
#define SCS 0002 /* Sector started */
#define SCE 0001 /* Sector end */
/* Start in IDS state.
*
* DATAO sets state to SNA.
* 43 us later state to ADT.
* State ADT:
* Seek cylinder/sector.
* DATAO resets to SNA.
* Find cylinder state to DFR.
* State DFR:
* Remain in DFR for 1ms then set to ADT.
* CONO set state to ALS.
* DATAO nop.
* State ALS:
* 15us later advance to CMS if Read/Write command
* State CMS:
* 200us later advance to SCS, before first bit of sector.
* State SCS:
* Transfer data.
* End of sector advance to SCE
* CONO +END or +CLR
* Read command, terminate read. Advance to SCE.
* Write/read cmp.
* State SCE:
* Increase sector by 1.
* If END set CMD = NOP.
* 2us, if CMD = NOP advance to IDS, else CMS.
* set SECT_END flag. (Possible IRQ).
*
* CONO +SCL clear error bits. ADE,CME,DCE,DRL,FER,PER,RCE,SECT_END,WLE.
* CONO +CLR clears CMD=0.
*/
#define DSK_WDS 128
#define DSK_SECS 44
#define DSK_CYL 64 * 16
#define DSK_SIZE (DSK_SECS * DSK_CYL * DSK_WDS)
uint64 dsk_buf[DSK_WDS];
uint8 dsk_octflp;
uint32 dsk_status;
uint32 dsk_cmd;
uint32 dsk_addr;
int dsk_dct = 0;
t_stat dsk_devio(uint32 dev, uint64 *data);
t_stat dsk_svc(UNIT *);
t_stat dsk_boot(int32, DEVICE *);
t_stat dsk_set_dct (UNIT *, int32, CONST char *, void *);
t_stat dsk_show_dct (FILE *, UNIT *, int32, CONST void *);
void dsk_ini(UNIT *, t_bool);
t_stat dsk_reset(DEVICE *);
t_stat dsk_attach(UNIT *, CONST char *);
t_stat dsk_detach(UNIT *);
t_stat dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *dsk_description (DEVICE *dptr);
#if !PDP6
#define D DEV_DIS
#else
#define D 0
#endif
UNIT dsk_unit[] = {
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, DSK_SIZE) },
{ UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, DSK_SIZE) },
};
DIB dsk_dib[] = {
{DSK_DEVNUM, 1, &dsk_devio, NULL},
};
MTAB dsk_mod[] = {
{UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL},
{UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL},
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DCT", "DCT",
&dsk_set_dct, &dsk_show_dct, NULL},
{0}
};
REG dsk_reg[] = {
{BRDATA(BUFF, &dsk_buf[0], 16, 64, DSK_WDS), REG_HRO},
{0}
};
DEVICE dsk_dev = {
"DSK", dsk_unit, dsk_reg, dsk_mod,
NUM_UNITS_DSK, 8, 18, 1, 8, 36,
NULL, NULL, &dsk_reset, &dsk_boot, &dsk_attach, &dsk_detach,
&dsk_dib[0], DEV_DISABLE | DEV_DEBUG | D, 0, dev_debug,
NULL, NULL, &dsk_help, NULL, NULL, &dsk_description
};
t_stat
dsk_devio(uint32 dev, uint64 *data) {
UNIT *uptr = &dsk_unit[(dsk_addr >> 16) & 03];
uint64 res;
switch(dev & 3) {
case CONI:
res = ((uint64)(dsk_cmd) << 18);
res |= ((uint64)(dsk_octflp)) << 10;
res |= ((uint64)(dsk_status & RMASK));
if ((uptr->flags & UNIT_ATT) == 0)
res |= OPR;
if (uptr->flags & UNIT_WLK)
res |= WLE;
*data = res;
sim_debug(DEBUG_CONI, &dsk_dev, "DSK %03o CONI %012llo PC=%o\n", dev,
*data, PC);
break;
case CONO:
clr_interrupt(dev);
if (*data & SCL)
dsk_status &= ADE|CME|DCE|DRL|FER|PER|RCE|SECT_END;
/* If disk controller is busy */
if (dsk_octflp & (ALS|CMS|SCS|SCE)) {
/* Only update IRQ flags and stop flags */
dsk_cmd &= END|CLR|CMD;
dsk_cmd |= *data & ~(CMD|SCL);
} else {
dsk_cmd &= END|CLR;
dsk_cmd |= *data & ~(SCL);
}
if ((dsk_cmd & EIS) != 0 && dsk_octflp == IDS)
set_interrupt(dev, dsk_cmd);
if ((dsk_cmd & EFE) != 0 && (dsk_status & (FER|PER|WLE|RCE|DRL)) != 0)
set_interrupt(dev, dsk_cmd);
if ((dsk_cmd & EFR) != 0 && dsk_octflp == DFR)
set_interrupt(dev, dsk_cmd);
if ((dsk_cmd & EFR) != 0 && dsk_status & SECT_END)
set_interrupt(dev, dsk_cmd);
sim_debug(DEBUG_CONO, &dsk_dev, "DSK %03o CONO %06o PC=%o %06o\n", dev,
(uint32)*data, PC, dsk_status);
break;
case DATAI:
sim_debug(DEBUG_DATAIO, &dsk_dev, "DSK %03o DATI %012llo PC=%o\n",
dev, *data, PC);
break;
case DATAO:
sim_debug(DEBUG_DATAIO, &dsk_dev, "DSK %03o DATO %012llo, PC=%o %03o\n",
dev, *data, PC, dsk_octflp);
/* If not in right state we can't change it */
if (dsk_octflp & (SCE|SCS|CMS|ALS))
break;
/* Zero lower 3 bits of sector if read next sector set */
if (*data & 01000000LL)
*data &= ~07LL;
dsk_addr = (*data & RMASK);
uptr = &dsk_unit[(dsk_addr >> 16) & 03];
/* If we are idle, start controller */
if (dsk_octflp == IDS) {
sim_activate(uptr, 100);
clr_interrupt(dev);
}
dsk_octflp = SNA;
break;
}
return SCPE_OK;
}
t_stat
dsk_svc (UNIT *uptr)
{
int ctlr = (dsk_addr >> 16) & 03;
int cyl;
int sec;
int wc;
uint64 data;
DEVICE *dptr;
t_stat err;
dptr = &dsk_dev;
/* Check if we need to seek */
if (dsk_octflp == SCE) {
if ((dsk_cmd & CMD) == WR_CMD && (uptr->flags & UNIT_WLK) == 0) {
/* Write the block */
int da;
for (; uptr->DATAPTR < DSK_WDS; uptr->DATAPTR++)
dsk_buf[uptr->DATAPTR] = 0;
cyl = (dsk_addr >> 6) & 01777;
sec = dsk_addr & 077;
if (sec > DSK_SECS)
sec -= DSK_SECS;
da = (sec + (cyl * DSK_SECS)) * DSK_WDS;
err = sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
(void)sim_fwrite (&dsk_buf[0], sizeof(uint64),
DSK_WDS, uptr->fileref);
sim_debug(DEBUG_DETAIL, dptr, "DSK %d Write %d %d\n", ctlr, da, cyl);
}
uptr->DATAPTR = 0;
sec = (dsk_addr + 1) & 077;
if (sec >= DSK_SECS)
sec = 0;
dsk_addr = (dsk_addr & ~077) | sec;
if (dsk_cmd & CLR)
dsk_cmd &= ~(CMD|CLR);
dsk_octflp = CMS;
if (dsk_cmd & END || (dsk_cmd & CMD) == 0 || dct_is_connect(dsk_dct) == 0) {
dsk_cmd &= ~(CMD|CLR|END);
dsk_octflp = IDS;
}
} else
/* Do transfer */
if (dsk_octflp == SCS) {
if (dsk_cmd & END) {
dsk_octflp = SCE;
} else if ((dsk_status & DRL) == 0) {
if (dsk_cmd & WR_CMD) {
if (dct_read(dsk_dct, &data, 2) == 0) {
dsk_status |= DRL;
} else if (dsk_cmd & RD_CMD) {
if (dsk_buf[uptr->DATAPTR] != data)
dsk_status |= RCE;
} else {
sim_debug(DEBUG_DETAIL, dptr, "DSK %d Write %012llo %d\n",
ctlr, data, uptr->DATAPTR);
if ((uptr->flags & UNIT_WLK) != 0)
dsk_status |= DCE|PER|FER;
dsk_buf[uptr->DATAPTR] = data;
}
} else if (dsk_cmd & RD_CMD) {
data = dsk_buf[uptr->DATAPTR];
if (dct_write(dsk_dct, &data, 2) == 0)
dsk_status |= DRL;
}
}
uptr->DATAPTR++;
if (uptr->DATAPTR == DSK_WDS)
dsk_octflp = SCE;
}
if (dsk_octflp == CMS) {
sim_debug(DEBUG_DETAIL, dptr, "DSK %d CMS\n", ctlr);
if (dsk_cmd & RD_CMD) {
/* Read the block */
int da;
cyl = (dsk_addr >> 6) & 01777;
sec = dsk_addr & 077;
if (sec > DSK_SECS)
sec -= DSK_SECS;
da = (sec + (cyl * DSK_SECS)) * DSK_WDS;
err = sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET);
wc = sim_fread (&dsk_buf[0], sizeof(uint64),
DSK_WDS, uptr->fileref);
sim_debug(DEBUG_DETAIL, dptr, "DSK %d Read %d %d\n", ctlr, da, cyl);
for (; wc < DSK_WDS; wc++)
dsk_buf[wc] = 0;
} else if (dsk_cmd & WR_CMD) {
/* Check if we can write disk */
if (uptr->flags & UNIT_WLK) {
dsk_status |= CME|FER;
}
}
uptr->DATAPTR = 0;
dsk_octflp = SCS;
}
/* Ready for data transfer */
if (dsk_octflp == DFR) {
if (dsk_cmd & CMD) {
dsk_octflp = ALS;
} else {
dsk_octflp = ADT;
}
sim_activate(uptr, 100);
return SCPE_OK;
}
/* If at ADT then seek to correct cylinder */
if (dsk_octflp == ADT) {
if ((uptr->flags & UNIT_ATT) == 0) {
dsk_status |= ADE|FER;
} else {
cyl = (dsk_addr >> 6) & 077;
if (cyl != uptr->CUR_CYL) {
cyl -= uptr->CUR_CYL;
if (cyl < 0)
cyl = -cyl;
uptr->CUR_CYL = (dsk_addr >> 6) & 077;
sim_activate(uptr, 10000 * cyl);
return SCPE_OK;
}
}
dsk_octflp = DFR;
}
/* Address is correct and we have a command */
if (dsk_octflp == ALS) {
sim_debug(DEBUG_DETAIL, dptr, "DSK %d Alarm\n", ctlr);
dsk_octflp = CMS;
}
/* If at SNA the switch to ADT */
if (dsk_octflp == SNA) {
sim_debug(DEBUG_DETAIL, dptr, "DSK %d Sna\n", ctlr);
dsk_octflp = ADT;
if (uptr->flags & UNIT_WLK)
dsk_status |= WLE|FER;
}
/* If we are in idle state, just return */
if (dsk_octflp == IDS) {
sim_debug(DEBUG_DETAIL, dptr, "DSK %d Idle\n", ctlr);
if ((dsk_cmd & EIS) != 0)
set_interrupt(DSK_DEVNUM, dsk_cmd);
return SCPE_OK;
}
sim_activate(uptr, 100);
if ((dsk_cmd & EIS) != 0 && dsk_octflp == IDS)
set_interrupt(DSK_DEVNUM, dsk_cmd);
if ((dsk_cmd & EFE) != 0 && (dsk_status & (FER|PER|WLE|RCE|DRL)) != 0)
set_interrupt(DSK_DEVNUM, dsk_cmd);
if ((dsk_cmd & EFR) != 0 && dsk_octflp == DFR)
set_interrupt(DSK_DEVNUM, dsk_cmd);
if ((dsk_cmd & EFR) != 0 && dsk_status & SECT_END)
set_interrupt(DSK_DEVNUM, dsk_cmd);
return SCPE_OK;
}
/* set DCT channel and unit. */
t_stat
dsk_set_dct (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 dct;
t_stat r;
if (cptr == NULL)
return SCPE_ARG;
dct = (int32) get_uint (cptr, 8, 20, &r);
if (r != SCPE_OK)
return r;
dsk_dct = dct;
return SCPE_OK;
}
t_stat
dsk_show_dct (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (uptr == NULL)
return SCPE_IERR;
fprintf (st, "DCT=%02o", dsk_dct);
return SCPE_OK;
}
t_stat
dsk_reset(DEVICE * dptr)
{
int unit;
UNIT *uptr = dptr->units;
for(unit = 0; unit < NUM_UNITS_DSK; unit++) {
uptr->UFLAGS = 0;
uptr->CUR_CYL = 0;
uptr++;
}
dsk_octflp = IDS;
return SCPE_OK;
}
/* Boot from given device */
t_stat
dsk_boot(int32 unit_num, DEVICE * dptr)
{
return SCPE_OK;
}
/* Device attach */
t_stat
dsk_attach (UNIT *uptr, CONST char *cptr)
{
t_stat r;
r = attach_unit (uptr, cptr);
if (r != SCPE_OK)
return r;
uptr->CUR_CYL = 0;
uptr->UFLAGS = 0;
return SCPE_OK;
}
/* Device detach */
t_stat
dsk_detach (UNIT *uptr)
{
if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK;
if (sim_is_active (uptr)) /* unit active? */
sim_cancel (uptr); /* cancel operation */
return detach_unit (uptr);
}
t_stat
dsk_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "The DSK controller implements the 270 disk controller for the PDP6\n");
fprintf (st, "Options include the ability to set units write enabled or write locked\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprintf (st, "The DSK device supports the BOOT command.\n");
fprint_reg_help (st, dptr);
return SCPE_OK;
}
const char *dsk_description (DEVICE *dptr)
{
return "270 disk controller";
}
#endif

1286
PDP10/pdp6_dtc.c Normal file

File diff suppressed because it is too large Load diff

942
PDP10/pdp6_mtc.c Normal file
View file

@ -0,0 +1,942 @@
/* ka10_mtc.c: Type 516 Magnetic tape controller
Copyright (c) 2013-2019, Richard Cornwell
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
RICHARD CORNWELL 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.
Magnetic tapes are represented as a series of variable records
of the form:
32b byte count
byte 0
byte 1
:
byte n-2
byte n-1
32b byte count
If the byte count is odd, the record is padded with an extra byte
of junk. File marks are represented by a byte count of 0.
*/
#include "kx10_defs.h"
#include "sim_tape.h"
#ifndef NUM_DEVS_MTC
#define NUM_DEVS_MTC 0
#endif
#if (NUM_DEVS_MTC > 0)
#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF)
#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF
#define MTUF_7TRK (1 << MTUF_V_UF)
#define BUFFSIZE (32 * 1024)
#define UNIT_MT UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE
#define LT 66 /* Time per char low density */
#define HT 16 /* Time per char high density */
/* MTC register */
#define FLAG_PIA 0000007 /* 0 */
#define DIS_EOR 0000010 /* 3 */
#define UNIT_NUM 0000160 /* 4 */
#define HOLD_SEL 0000200 /* 7 */
#define FUNCTION 0007400 /* 8 */
#define NOP 000 /* Nop */
#define NOP_1 010 /* Nop */
#define REWIND 001 /* Rewind */
#define UNLOAD 011 /* Unload */
#define WRITE 002 /* Write */
#define WRITE_1 012 /* Write */
#define WTM 003 /* Write End of File */
#define ERG 013 /* Write blank tape */
#define CMP 004 /* Compare */
#define CMP_1 014 /* Compare */
#define READ 005 /* Read */
#define READ_BK 015 /* Read Backward */
#define SPC_FWD 006 /* Space forward */
#define SPC_EOF 016 /* Space to end of file */
#define SPC_REV 007 /* Space reverse */
#define SPC_REV_EOF 017 /* Space reverse to EOF. */
#define DENS_200 0000000
#define DENS_556 0010000
#define DENS_800 0020000
#define DENS_MSK 0030000 /* 6 */
#define ODD_PARITY 0040000 /*14 */
#define SLICE 0100000
#define WRCLK 0200000
#define FALS_EOR 0400000
#define CMD_FULL 0x8000000
#define CMD_MASK 0777760
/* MTS register */
#define TAPE_FREE 0000001
#define TAPE_RDY 0000002
#define EOR_FLAG 0000004 /* End of record */
#define PARITY_ERR 0000010
#define PARITY_ERRL 0000020
#define READ_CMP 0000040
#define MIS_CHR 0000100 /* Charaters missed on tape */
#define WRITE_LOCK 0000200
#define EOF_FLAG 0000400
#define LD_PT 0001000 /* Tape near load point */
#define END_PT 0002000 /* Tape near end point */
#define BOT_FLAG 0004000
#define EOT_FLAG 0010000
#define REW 0020000
#define TRF_CMD 0040000
#define CONT_MOT 0100000
#define MOT_STOP 0200000
#define ILL_OPR 0400000
/* CONO to MTS */
#define ENB_ICE 0000001 /* Control Ready */
#define ENB_JNU 0000002 /* Set monitor unit */
#define ENB_ERF 0000004 /* End of Record */
#define ENB_XNE 0040000 /* New command rdy */
#define ENB_LIE 0100000 /* Load point */
/* IRQ Masks in status */
#define IRQ_ICE 001000000
#define IRQ_JNU 002000000
#define IRQ_ERF 004000000
#define IRQ_XNE 010000000
#define IRQ_LIE 020000000
#define IRQ_MASK 037000000
/* MTM register */
#define EOR_RD_DLY 0000001
#define EOR_WR_DLY 0000002
#define MIS_CHR_DLY 0000004
#define FR_CHR_INH 0000010
#define UNIT_BUF_FIN 0000160
#define MOT_DLY 0000200
#define FUNC_FIN 0007400
#define UNIT_SEL_NEW 0010000
#define CMD_HOLD 0020000
#define MOT_STOP_DLY 0040000
#define EOR_MOT_DLY 0100000
#define REC_IN_PROG 0200000
#define TRP_SPD_DLY 0400000
#define MTC_DEVCTL 0220
#define MTC_DEVSTA 0224
#define MTC_DEVSTM 0230
#define MTC_MOTION 000000001 /* Mag tape unit in motion */
#define MTC_BUSY 000000002 /* Mag tape unit is busy */
#define MTC_START 000000004 /* Start a command */
#define CNTRL u3
#define STATUS u4 /* Per drive status bits */
#define CPOS u5 /* Character position */
#define BPOS u6 /* Position in buffer */
t_stat mtc_devio(uint32 dev, uint64 *data);
void mtc_checkirq(UNIT * uptr);
t_stat mtc_srv(UNIT *);
t_stat mtc_boot(int32, DEVICE *);
t_stat mtc_reset(DEVICE *);
t_stat mtc_set_dct (UNIT *, int32, CONST char *, void *);
t_stat mtc_show_dct (FILE *, UNIT *, int32, CONST void *);
t_stat mtc_attach(UNIT *, CONST char *);
t_stat mtc_detach(UNIT *);
t_stat mtc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
const char *cptr);
const char *mtc_description (DEVICE *dptr);
uint16 mtc_pia;
uint8 mtc_sel_unit;
uint32 mtc_hold_cmd;
uint32 mtc_status;
int mtc_dct; /* DCT Channel and unit */
static uint8 parity_table[64] = {
/* 0 1 2 3 4 5 6 7 */
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000,
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
0000, 0100, 0100, 0000, 0100, 0000, 0000, 0100,
0100, 0000, 0000, 0100, 0000, 0100, 0100, 0000
};
/* One buffer per channel */
uint8 mtc_buffer[BUFFSIZE];
#if !PDP6
#define D DEV_DIS
#else
#define D 0
#endif
UNIT mtc_unit[] = {
/* Controller 1 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 0 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 1 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 2 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 3 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 4 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 5 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 6 */
{UDATA(&mtc_srv, UNIT_MT, 0)}, /* 7 */
};
DIB mtc_dib = {MTC_DEVCTL, 3, &mtc_devio, NULL};
MTAB mtc_mod[] = {
{MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL},
{MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL},
{MTUF_7TRK, 0, "9T", "9T", NULL, NULL},
{MTUF_7TRK, MTUF_7TRK, "7T", "7T", NULL, NULL},
{MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT",
&sim_tape_set_fmt, &sim_tape_show_fmt, NULL},
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LENGTH", "LENGTH",
&sim_tape_set_capac, &sim_tape_show_capac, NULL},
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DENSITY", "DENSITY",
&sim_tape_set_dens, &sim_tape_show_dens, NULL},
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DCT", "DCT",
&mtc_set_dct, &mtc_show_dct, NULL},
{0}
};
DEVICE mtc_dev = {
"MTC", mtc_unit, NULL, mtc_mod,
8, 8, 15, 1, 8, 8,
NULL, NULL, &mtc_reset, &mtc_boot, &mtc_attach, &mtc_detach,
&mtc_dib, DEV_DISABLE | DEV_DEBUG | DEV_TAPE | D, 0, dev_debug,
NULL, NULL, &mtc_help, NULL, NULL, &mtc_description
};
t_stat
mtc_devio(uint32 dev, uint64 *data) {
uint64 res;
DEVICE *dptr = &mtc_dev;
UNIT *uptr;
int u;
switch(dev & 0374) {
case MTC_DEVCTL:
switch(dev & 03) {
case CONI:
res = (uint64)((mtc_hold_cmd & CMD_MASK) | (mtc_pia & FLAG_PIA));
*data = res;
sim_debug(DEBUG_CONI, dptr, "MTC CONI %03o status %08o %o %o PC=%06o\n",
dev, mtc_status, mtc_sel_unit, mtc_pia, PC);
break;
case CONO:
clr_interrupt(MTC_DEVCTL);
mtc_pia = (uint16)(*data) & (FLAG_PIA);
mtc_hold_cmd = (*data & CMD_MASK);
sim_debug(DEBUG_CONO, dptr, "MTC CONO %03o start %o %o%012llo PC=%06o\n",
dev, mtc_sel_unit, mtc_pia, *data, PC);
/* If nop done */
if ((mtc_hold_cmd & FUNCTION) == 0)
break;
u = (mtc_hold_cmd >> 4) & 07;
uptr = &mtc_unit[u];
if ((uptr->flags & UNIT_ATT) != 0) {
/* If unit is not busy, give it the command to run */
if ((uptr->CNTRL & (MTC_START|MTC_BUSY)) == 0) {
sim_debug(DEBUG_CONO, dptr, "MTC CONO %03o starting %o\n", dev, u);
mtc_sel_unit = u;
mtc_hold_cmd &= ~CMD_FULL;
uptr->CNTRL = (mtc_hold_cmd & ~UNIT_NUM) | MTC_START;
uptr->STATUS = 0;
mtc_status &= IRQ_MASK; /* Clear all flags but IRQ flags */
mtc_status |= TRF_CMD;
sim_activate(uptr, 1000);
}
}
mtc_checkirq(uptr);
break;
case DATAI:
break;
case DATAO:
break;
}
break;
case MTC_DEVSTA:
switch(dev & 03) {
case CONI:
uptr = &mtc_unit[mtc_sel_unit];
res = mtc_status | (uint64)(uptr->STATUS);
if ((uptr->flags & MTUF_WLK) != 0)
res |= WRITE_LOCK;
if (sim_tape_bot(uptr))
res |= BOT_FLAG;
if (sim_tape_eot(uptr))
res |= EOT_FLAG;
if ((uptr->flags & UNIT_ATT) != 0 && (uptr->CNTRL & (MTC_START|MTC_BUSY)) == 0)
res |= TAPE_RDY;
if ((uptr->flags & UNIT_ATT) == 0 || (uptr->CNTRL & (MTC_START|MTC_MOTION|MTC_BUSY)) == 0)
res |= TAPE_FREE;
*data = res;
sim_debug(DEBUG_CONI, dptr, "MTC CONI %03o status %012llo %o %08o PC=%06o\n",
dev, res, mtc_sel_unit, mtc_status, PC);
break;
case CONO:
mtc_status &= 00777777;
mtc_status |= (*data & 07) << 18;
mtc_status |= (*data & (ENB_XNE|ENB_LIE)) << 7;
if (*data & TAPE_RDY && (mtc_hold_cmd & FUNCTION) == 0) {
/* Switch to drive to check status */
mtc_sel_unit = (mtc_hold_cmd >> 4) & 07;
}
sim_debug(DEBUG_CONO, dptr, "MTC CONO %03o status %012llo %o %08o PC=%06o\n",
dev, *data, mtc_sel_unit, mtc_status, PC);
uptr = &mtc_unit[mtc_sel_unit];
mtc_checkirq(uptr);
break;
case DATAI:
break;
case DATAO:
break;
}
break;
case MTC_DEVSTM:
switch(dev & 03) {
case CONI:
uptr = &mtc_unit[mtc_sel_unit];
res = (mtc_sel_unit << 4) | (uptr->CNTRL & FUNC_FIN);
if (mtc_sel_unit != ((mtc_hold_cmd & UNIT_NUM) >> 4))
res |= UNIT_SEL_NEW;
if (mtc_hold_cmd & CMD_FULL)
res |= CMD_HOLD;
sim_debug(DEBUG_CONI, dptr, "MTC CONI %03o status2 %012llo %o %08o PC=%06o\n",
dev, res, mtc_sel_unit, mtc_status, PC);
break;
case CONO:
break;
case DATAI:
break;
case DATAO:
break;
}
break;
}
return SCPE_OK;
}
void
mtc_checkirq(UNIT * uptr)
{
clr_interrupt(MTC_DEVCTL);
if ((mtc_status & IRQ_XNE) != 0 && (mtc_status & TRF_CMD) != 0) {
set_interrupt(MTC_DEVCTL, mtc_pia);
return;
}
if ((mtc_status & IRQ_LIE) != 0 && sim_tape_bot(uptr)) {
set_interrupt(MTC_DEVCTL, mtc_pia);
return;
}
if ((mtc_status & (EOR_FLAG|IRQ_ERF)) == (EOR_FLAG|IRQ_ERF)) {
set_interrupt(MTC_DEVCTL, mtc_pia);
return;
}
if ((mtc_status & IRQ_ICE) != 0 &&
(uptr->CNTRL & (MTC_START|MTC_MOTION|MTC_BUSY)) == 0) {
set_interrupt(MTC_DEVCTL, mtc_pia);
return;
}
#if 0
/* Need to verify if this is real interrupt or not */
if ((mtc_status & IRQ_JNU) != 0 &&
(mtc_hold_cmd & CMD_FULL) == 0 &&
(uptr->CNTRL & (MTC_START|MTC_BUSY)) == 0) {
sim_debug(DEBUG_DETAIL, &mtc_dev, "MTC%o jnu %o %08o\n", mtc_sel_unit, mtc_pia, mtc_status);
set_interrupt(MTC_DEVCTL, mtc_pia);
return;
}
#endif
}
/* Handle processing of tape requests. */
t_stat
mtc_srv(UNIT * uptr)
{
DEVICE *dptr = find_dev_from_unit(uptr);
int unit = (uptr - dptr->units) & 7;
int cmd = (uptr->CNTRL & FUNCTION) >> 8;
t_mtrlnt reclen;
t_stat r = SCPE_ARG; /* Force error if not set */
uint8 ch;
int cc;
uint64 hold_reg;
int cc_max;
int i;
if ((uptr->CNTRL & (MTC_START|MTC_BUSY)) == 0) {
if (uptr->STATUS & (PARITY_ERR|PARITY_ERRL|READ_CMP|MIS_CHR|EOF_FLAG)) {
mtc_hold_cmd &= ~CMD_FULL;
sim_debug(DEBUG_DETAIL, dptr, "MTC%o stoping %o %08o\n", unit, mtc_pia, mtc_status);
}
/* If tape in motion, generate EOR and wait */
if (uptr->CNTRL & MTC_MOTION) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o EOR %08o\n", unit, uptr->STATUS);
uptr->CNTRL &= ~MTC_MOTION;
sim_activate(uptr, 500);
mtc_checkirq(uptr);
return SCPE_OK;
}
sim_debug(DEBUG_DETAIL, dptr, "MTC%o Done %08o %08o\n", unit, mtc_hold_cmd, mtc_status);
/* Check if command pending */
if ((mtc_hold_cmd & CMD_FULL) != 0) {
int u = (mtc_hold_cmd >> 4) & 07;
sim_debug(DEBUG_DETAIL, dptr, "MTC%o New command %o\n", unit, u);
/* Is it for me? */
if (u == unit) {
mtc_hold_cmd &= ~CMD_FULL;
uptr->CNTRL = (mtc_hold_cmd & ~UNIT_NUM) | MTC_START;
uptr->STATUS = 0;
cmd = (uptr->CNTRL & FUNCTION) >> 8;
mtc_status |= TRF_CMD;
sim_activate(uptr, 100);
mtc_checkirq(uptr);
return SCPE_OK;
} else {
uptr = &mtc_unit[u];
/* See if other unit can be started */
mtc_sel_unit = u;
if ((uptr->CNTRL & (MTC_START|MTC_MOTION|MTC_BUSY)) == 0) {
sim_activate(uptr, 100);
}
return SCPE_OK;
}
} else {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o stoping %o %08o\n", unit, mtc_pia, mtc_status);
mtc_checkirq(uptr);
return SCPE_OK;
}
}
if (uptr->flags & MTUF_7TRK) {
cc_max = 6;
} else {
cc_max = 5;
}
if (uptr->CNTRL & MTC_START)
uptr->BPOS = 0;
switch(cmd) {
case NOP:
case NOP_1:
sim_debug(DEBUG_DETAIL, dptr, "MTC%o Idle\n", unit);
uptr->CNTRL &= ~(MTC_BUSY|MTC_START);
break;
case REWIND:
if (uptr->CNTRL & MTC_START) {
UNIT *nuptr = &mtc_unit[(mtc_hold_cmd >> 4) & 07];
uptr->CNTRL &= ~MTC_START;
uptr->CNTRL |= MTC_BUSY|MTC_MOTION;
uptr->STATUS |= REW;
if ((mtc_hold_cmd & CMD_FULL) && ((mtc_hold_cmd >> 4) & 07) != unit &&
(nuptr->CNTRL & (MTC_START|MTC_MOTION|MTC_BUSY)) == 0) {
mtc_hold_cmd &= ~CMD_FULL;
nuptr->CNTRL = (mtc_hold_cmd & ~UNIT_NUM) | MTC_START;
if (mtc_status & IRQ_XNE)
set_interrupt(MTC_DEVCTL, mtc_pia);
sim_activate(nuptr, 100);
}
sim_activate(uptr, 100000);
} else {
uptr->CNTRL &= ~(MTC_BUSY|FUNCTION);
uptr->STATUS &= ~REW;
sim_activate(uptr, 100);
sim_debug(DEBUG_DETAIL, dptr, "MTC%o rewind\n", unit);
sim_tape_rewind(uptr);
}
return SCPE_OK;
case UNLOAD:
if (uptr->CNTRL & MTC_START) {
uptr->CNTRL &= ~MTC_START;
uptr->CNTRL |= MTC_BUSY|MTC_MOTION;
uptr->STATUS |= REW;
sim_activate(uptr, 100000);
} else {
uptr->CNTRL &= ~(MTC_BUSY);
uptr->STATUS &= ~REW;
sim_activate(uptr, 100);
sim_debug(DEBUG_DETAIL, dptr, "MTC%o unload\n", unit);
sim_tape_detach(uptr);
}
return SCPE_OK;
case READ_BK:
if (uptr->CNTRL & MTC_START) {
uptr->CNTRL &= ~MTC_START;
if (sim_tape_bot(uptr)) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read back at bot\n", unit);
uptr->STATUS |= ILL_OPR;
mtc_status |= EOR_FLAG;
break;
}
uptr->CNTRL |= MTC_MOTION;
if ((r = sim_tape_rdrecr(uptr, &mtc_buffer[0], &reclen, BUFFSIZE)) != MTSE_OK) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read back error %d\n", unit, r);
if (r == MTSE_TMK)
uptr->STATUS |= EOF_FLAG;
else
uptr->STATUS |= PARITY_ERRL;
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
break;
}
uptr->CNTRL |= MTC_BUSY;
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read back %d\n", unit, reclen);
uptr->hwmark = reclen;
uptr->BPOS = reclen-1;
break;
}
hold_reg = 0;
for (i = cc_max - 1; i >= 0; i--) {
ch = mtc_buffer[uptr->BPOS];
if (uptr->flags & MTUF_7TRK) {
cc = 6 * (5 - i);
if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^
parity_table[ch & 0x3f]) != 0) {
mtc_status |= PARITY_ERR;
}
hold_reg |= (uint64)(ch & 0x3f) << cc;
} else {
cc = (8 * (3 - i)) + 4;
if (cc < 0)
hold_reg |= (uint64)(ch & 0x0f);
else
hold_reg |= (uint64)(ch & 0xff) << cc;
}
if ((uint32)uptr->BPOS == 0)
break;
uptr->BPOS--;
}
if (dct_write(mtc_dct, &hold_reg, cc_max - i) == 0) {
uptr->CNTRL &= ~(MTC_BUSY);
}
break;
case READ:
if (uptr->CNTRL & MTC_START) {
uptr->CNTRL &= ~MTC_START;
uptr->CNTRL |= MTC_MOTION;
if ((r = sim_tape_rdrecf(uptr, &mtc_buffer[0], &reclen, BUFFSIZE)) != MTSE_OK) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read error %d\n", unit, r);
if (r == MTSE_TMK)
uptr->STATUS |= EOF_FLAG;
else if (r == MTSE_EOM)
uptr->STATUS |= ILL_OPR;
else
uptr->STATUS |= PARITY_ERRL;
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
break;
}
uptr->CNTRL |= MTC_BUSY;
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read %d\n", unit, reclen);
uptr->hwmark = reclen;
uptr->BPOS = 0;
break;
}
hold_reg = 0;
for (i = 0; i < cc_max; i++) {
if ((uint32)uptr->BPOS >= uptr->hwmark)
break;
ch = mtc_buffer[uptr->BPOS];
if (uptr->flags & MTUF_7TRK) {
cc = 6 * (5 - i);
if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^
parity_table[ch & 0x3f]) != 0) {
mtc_status |= PARITY_ERR;
}
hold_reg |= (uint64)(ch & 0x3f) << cc;
} else {
cc = (8 * (3 - i)) + 4;
if (cc < 0)
hold_reg |= (uint64)(ch & 0x0f);
else
hold_reg |= (uint64)(ch & 0xff) << cc;
}
uptr->BPOS++;
}
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read data %012llo\n", unit, hold_reg);
if (dct_write(mtc_dct, &hold_reg, i) == 0 ||(uint32)uptr->BPOS >= uptr->hwmark) {
uptr->CNTRL &= ~(MTC_BUSY);
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read eor %d %08o\n", unit, uptr->BPOS, mtc_status);
}
break;
case CMP:
case CMP_1:
if (uptr->CNTRL & MTC_START) {
uptr->CNTRL &= ~MTC_START;
uptr->CNTRL |= MTC_MOTION;
if ((r = sim_tape_rdrecf(uptr, &mtc_buffer[0], &reclen,
BUFFSIZE)) != MTSE_OK) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o read cmp error %d\n", unit, r);
if (r == MTSE_TMK)
uptr->STATUS |= EOF_FLAG;
else if (r == MTSE_EOM)
uptr->STATUS |= ILL_OPR;
else
uptr->STATUS |= PARITY_ERRL;
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
break;
}
uptr->CNTRL |= MTC_BUSY;
sim_debug(DEBUG_DETAIL, dptr, "MTC%o compare %d\n", unit, reclen);
uptr->hwmark = reclen;
uptr->BPOS = 0;
break;
}
if (uptr->BPOS >= (int32)uptr->hwmark) {
uptr->CNTRL &= ~(MTC_BUSY);
} else if (dct_read(mtc_dct, &hold_reg, cc_max)) {
for(i = 0; i < cc_max; i++) {
if (uptr->flags & MTUF_7TRK) {
ch = mtc_buffer[uptr->BPOS];
if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^
parity_table[ch & 0x3f]) != (ch & 0x40)) {
mtc_status |= PARITY_ERR;
}
mtc_buffer[uptr->BPOS] &= 0x3f;
cc = 6 * (6 - i);
ch = (hold_reg >> cc) & 0x3f;
} else {
if ((uptr->CNTRL & ODD_PARITY) == 0)
mtc_status |= PARITY_ERR;
/* Write next char out */
cc = (8 * (3 - i)) + 4;
if (cc < 0)
ch = hold_reg & 0x0f;
else
ch = (hold_reg >> cc) & 0xff;
}
if (mtc_buffer[uptr->BPOS] != ch) {
uptr->STATUS |= READ_CMP;
}
uptr->BPOS++;
}
} else {
uptr->CNTRL &= ~(MTC_BUSY);
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
}
break;
case WRITE:
case WRITE_1:
/* Writing and Type A, request first data word */
if (uptr->CNTRL & MTC_START) {
uptr->CNTRL &= ~MTC_START;
if ((uptr->flags & MTUF_WLK) != 0) {
uptr->STATUS |= ILL_OPR;
break;
}
uptr->CNTRL |= MTC_MOTION|MTC_BUSY;
sim_debug(DEBUG_EXP, dptr, "MTC%o Init write\n", unit);
uptr->hwmark = 0;
uptr->BPOS = 0;
break;
}
/* Force error if we exceed buffer size */
if (uptr->BPOS >= BUFFSIZE) {
uptr->CNTRL &= ~(MTC_BUSY);
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
break;
}
if (dct_read(mtc_dct, &hold_reg, 0)) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o Write data %012llo\n", unit, hold_reg);
for(i = 0; i < cc_max; i++) {
if (uptr->flags & MTUF_7TRK) {
cc = 6 * (6 - i);
ch = (hold_reg >> cc) & 0x3f;
ch |= ((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^
parity_table[ch & 0x3f];
} else {
/* Write next char out */
cc = (8 * (3 - i)) + 4;
if (cc < 0)
ch = hold_reg & 0x0f;
else
ch = (hold_reg >> cc) & 0xff;
}
mtc_buffer[uptr->BPOS] = ch;
uptr->BPOS++;
uptr->hwmark = uptr->BPOS;
}
} else {
/* Write out the block */
reclen = uptr->hwmark;
r = sim_tape_wrrecf(uptr, &mtc_buffer[0], reclen);
sim_debug(DEBUG_DETAIL, dptr, "MTC%o Write %d %d\n", unit, reclen, r);
if (r == MTSE_EOM)
uptr->STATUS |= ILL_OPR;
else if (r != MTSE_OK)
uptr->STATUS |= PARITY_ERRL;
mtc_status |= EOR_FLAG;
uptr->CNTRL &= ~(MTC_BUSY);
uptr->BPOS = 0;
uptr->hwmark = 0;
}
break;
case WTM:
if (uptr->CNTRL & MTC_START) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o WTM\n", unit);
uptr->CNTRL &= ~MTC_START;
if ((uptr->flags & MTUF_WLK) != 0) {
uptr->STATUS |= ILL_OPR;
mtc_status |= (EOR_FLAG);
break;
}
uptr->CNTRL |= MTC_MOTION;
r = sim_tape_wrtmk(uptr);
if (r != MTSE_OK)
uptr->STATUS |= PARITY_ERRL;
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
}
break;
case ERG:
if (uptr->CNTRL & MTC_START) {
sim_debug(DEBUG_DETAIL, dptr, "MTC%o ERG\n", unit);
uptr->CNTRL &= ~MTC_START;
if ((uptr->flags & MTUF_WLK) != 0) {
uptr->STATUS |= ILL_OPR;
mtc_status |= (EOR_FLAG);
break;
}
uptr->CNTRL |= MTC_MOTION;
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
}
break;
case SPC_REV_EOF:
case SPC_EOF:
case SPC_REV:
case SPC_FWD:
sim_debug(DEBUG_DETAIL, dptr, "MTC%o space %o\n", unit, cmd);
if (uptr->CNTRL & MTC_START) {
uptr->CNTRL &= ~MTC_START;
if ((cmd & 7) == SPC_REV && sim_tape_bot(uptr)) {
uptr->STATUS |= ILL_OPR;
break;
}
uptr->CNTRL |= MTC_MOTION|MTC_BUSY;
}
/* Always skip at least one record */
if ((cmd & 7) == SPC_FWD)
r = sim_tape_sprecf(uptr, &reclen);
else
r = sim_tape_sprecr(uptr, &reclen);
switch (r) {
case MTSE_OK: /* no error */
if ((cmd & 010) != 0)
break;
/* Fall through */
case MTSE_TMK: /* tape mark */
case MTSE_BOT: /* beginning of tape */
case MTSE_EOM: /* end of medium */
/* Stop motion if we recieve any of these */
uptr->CNTRL &= ~(MTC_BUSY);
mtc_status |= EOR_FLAG;
mtc_checkirq(uptr);
}
uptr->hwmark = 0;
sim_activate(uptr, 420 * (reclen/6));
return SCPE_OK;
}
sim_activate(uptr, 420);
return SCPE_OK;
}
uint64
mtc_read_word(UNIT *uptr) {
int i, cc, ch;
uint64 hold_reg = 0;
for(i = 0; i <= 4; i++) {
cc = (8 * (3 - i)) + 4;
ch = mtc_buffer[uptr->BPOS];
if (cc < 0)
hold_reg |= (uint64)(ch & 0x3f);
else
hold_reg |= (uint64)(ch & 0xff) << cc;
uptr->BPOS++;
}
return hold_reg;
}
/* Boot from given device */
t_stat
mtc_boot(int32 unit_num, DEVICE * dptr)
{
UNIT *uptr = &dptr->units[unit_num];
t_mtrlnt reclen;
t_stat r;
uint64 hold_reg;
int wc, addr;
if ((uptr->flags & UNIT_ATT) == 0)
return SCPE_UNATT; /* attached? */
r = sim_tape_rewind(uptr);
if (r != SCPE_OK)
return r;
uptr->CNTRL = 022200; /* Read 800 BPI, Core */
r = sim_tape_rdrecf(uptr, &mtc_buffer[0], &reclen, BUFFSIZE);
if (r != SCPE_OK)
return r;
uptr->BPOS = 0;
uptr->hwmark = reclen;
hold_reg = mtc_read_word(uptr);
wc = (hold_reg >> 18) & RMASK;
addr = hold_reg & RMASK;
while (wc != 0) {
wc = (wc + 1) & RMASK;
addr = (addr + 1) & RMASK;
if ((uint32)uptr->BPOS >= uptr->hwmark) {
r = sim_tape_rdrecf(uptr, &mtc_buffer[0], &reclen, BUFFSIZE);
if (r != SCPE_OK)
return r;
uptr->BPOS = 0;
uptr->hwmark = reclen;
}
hold_reg = mtc_read_word(uptr);
if (addr < 020)
FM[addr] = hold_reg;
else
M[addr] = hold_reg;
}
if (addr < 020)
FM[addr] = hold_reg;
else
M[addr] = hold_reg;
PC = hold_reg & RMASK;
return SCPE_OK;
}
t_stat
mtc_reset(DEVICE * dptr)
{
int i;
for (i = 0 ; i < 8; i++) {
UNIT *uptr = &mtc_unit[i];
if (MT_DENS(uptr->dynflags) == MT_DENS_NONE)
uptr->dynflags = MT_200_VALID | MT_556_VALID;
uptr->CNTRL = 0;
sim_cancel(uptr);
}
mtc_pia = 0;
mtc_status = 0;
mtc_sel_unit = TAPE_FREE|TAPE_RDY;
return SCPE_OK;
}
/* set DCT channel and unit. */
t_stat
mtc_set_dct (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 dct;
t_stat r;
if (cptr == NULL)
return SCPE_ARG;
dct = (int32) get_uint (cptr, 8, 20, &r);
if (r != SCPE_OK)
return r;
mtc_dct = dct;
return SCPE_OK;
}
t_stat
mtc_show_dct (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
if (uptr == NULL)
return SCPE_IERR;
fprintf (st, "DCT=%02o", mtc_dct);
return SCPE_OK;
}
t_stat
mtc_attach(UNIT * uptr, CONST char *file)
{
uptr->CNTRL = 0;
uptr->STATUS = 0;
return sim_tape_attach_ex(uptr, file, 0, 0);
}
t_stat
mtc_detach(UNIT * uptr)
{
uptr->CPOS = 0;
return sim_tape_detach(uptr);
}
t_stat
mtc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "Type 516 Magnetic Tape\n\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
fprintf (st, "\nThe type options can be used only when a unit is not attached to a file. The\n");
fprintf (st, "bad block option can be used only when a unit is attached to a file.\n");
fprintf (st, "The DTC does support the BOOT command, however this did not work on real PDP6.\n");
sim_tape_attach_help (st, dptr, uptr, flag, cptr);
return SCPE_OK;
}
const char *
mtc_description (DEVICE *dptr)
{
return "Type 516 magnetic tape controller" ;
}
#endif