From 90b7d2beac1d55a34bc6e0d412864222110ad337 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Tue, 9 Jul 2019 20:59:01 -0400 Subject: [PATCH] KA10: Initial release of PDP10 KA/KI and PDP6 simulators. --- PDP10/ka10_auxcpu.c | 392 +++ PDP10/ka10_ch10.c | 551 ++++ PDP10/ka10_dkb.c | 98 + PDP10/ka10_dpk.c | 391 +++ PDP10/ka10_imx.c | 118 + PDP10/ka10_mty.c | 304 +++ PDP10/ka10_pd.c | 127 + PDP10/ka10_pmp.c | 2436 +++++++++++++++++ PDP10/ka10_stk.c | 397 +++ PDP10/ka10_ten11.c | 417 +++ PDP10/ka10_tk10.c | 329 +++ PDP10/kx10_cp.c | 260 ++ PDP10/kx10_cpu.c | 6353 +++++++++++++++++++++++++++++++++++++++++++ PDP10/kx10_cr.c | 305 +++ PDP10/kx10_cty.c | 219 ++ PDP10/kx10_dc.c | 579 ++++ PDP10/kx10_defs.h | 492 ++++ PDP10/kx10_df.c | 139 + PDP10/kx10_dk.c | 220 ++ PDP10/kx10_dp.c | 999 +++++++ PDP10/kx10_dpy.c | 442 +++ PDP10/kx10_dt.c | 1266 +++++++++ PDP10/kx10_imp.c | 2186 +++++++++++++++ PDP10/kx10_lights.c | 170 ++ PDP10/kx10_lp.c | 408 +++ PDP10/kx10_mt.c | 1063 ++++++++ PDP10/kx10_pt.c | 422 +++ PDP10/kx10_rc.c | 574 ++++ PDP10/kx10_rp.c | 1350 +++++++++ PDP10/kx10_rs.c | 948 +++++++ PDP10/kx10_sys.c | 1187 ++++++++ PDP10/kx10_tu.c | 1161 ++++++++ PDP10/pdp6_dcs.c | 482 ++++ PDP10/pdp6_dct.c | 284 ++ PDP10/pdp6_dsk.c | 511 ++++ PDP10/pdp6_dtc.c | 1286 +++++++++ PDP10/pdp6_mtc.c | 942 +++++++ 37 files changed, 29808 insertions(+) create mode 100644 PDP10/ka10_auxcpu.c create mode 100644 PDP10/ka10_ch10.c create mode 100644 PDP10/ka10_dkb.c create mode 100644 PDP10/ka10_dpk.c create mode 100644 PDP10/ka10_imx.c create mode 100644 PDP10/ka10_mty.c create mode 100644 PDP10/ka10_pd.c create mode 100644 PDP10/ka10_pmp.c create mode 100644 PDP10/ka10_stk.c create mode 100644 PDP10/ka10_ten11.c create mode 100644 PDP10/ka10_tk10.c create mode 100644 PDP10/kx10_cp.c create mode 100644 PDP10/kx10_cpu.c create mode 100644 PDP10/kx10_cr.c create mode 100644 PDP10/kx10_cty.c create mode 100644 PDP10/kx10_dc.c create mode 100644 PDP10/kx10_defs.h create mode 100644 PDP10/kx10_df.c create mode 100644 PDP10/kx10_dk.c create mode 100644 PDP10/kx10_dp.c create mode 100644 PDP10/kx10_dpy.c create mode 100644 PDP10/kx10_dt.c create mode 100644 PDP10/kx10_imp.c create mode 100644 PDP10/kx10_lights.c create mode 100644 PDP10/kx10_lp.c create mode 100644 PDP10/kx10_mt.c create mode 100644 PDP10/kx10_pt.c create mode 100644 PDP10/kx10_rc.c create mode 100644 PDP10/kx10_rp.c create mode 100644 PDP10/kx10_rs.c create mode 100644 PDP10/kx10_sys.c create mode 100644 PDP10/kx10_tu.c create mode 100644 PDP10/pdp6_dcs.c create mode 100644 PDP10/pdp6_dct.c create mode 100644 PDP10/pdp6_dsk.c create mode 100644 PDP10/pdp6_dtc.c create mode 100644 PDP10/pdp6_mtc.c diff --git a/PDP10/ka10_auxcpu.c b/PDP10/ka10_auxcpu.c new file mode 100644 index 00000000..b3352577 --- /dev/null +++ b/PDP10/ka10_auxcpu.c @@ -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 +//#include +#include + +/* 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 diff --git a/PDP10/ka10_ch10.c b/PDP10/ka10_ch10.c new file mode 100644 index 00000000..09d332ec --- /dev/null +++ b/PDP10/ka10_ch10.c @@ -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=\n"); + fprintf (st, " sim> SET CH PEER=:\n\n"); + fprintf (st, "Then, attach a local port. By default UDP is used:\n\n"); + fprintf (st, " sim> ATTACH CH \n\n"); + fprintf (st, "If TCP is desired, add \"TCP\":\n\n"); + fprintf (st, " sim> ATTACH CH ,TCP\n\n"); + return SCPE_OK; +} +#endif diff --git a/PDP10/ka10_dkb.c b/PDP10/ka10_dkb.c new file mode 100644 index 00000000..30fa1465 --- /dev/null +++ b/PDP10/ka10_dkb.c @@ -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 diff --git a/PDP10/ka10_dpk.c b/PDP10/ka10_dpk.c new file mode 100644 index 00000000..5cc9298c --- /dev/null +++ b/PDP10/ka10_dpk.c @@ -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 +#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"; +} diff --git a/PDP10/ka10_imx.c b/PDP10/ka10_imx.c new file mode 100644 index 00000000..918a5007 --- /dev/null +++ b/PDP10/ka10_imx.c @@ -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 +#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 diff --git a/PDP10/ka10_mty.c b/PDP10/ka10_mty.c new file mode 100644 index 00000000..6a67e4bc --- /dev/null +++ b/PDP10/ka10_mty.c @@ -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 +#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 diff --git a/PDP10/ka10_pd.c b/PDP10/ka10_pd.c new file mode 100644 index 00000000..2aeaeed9 --- /dev/null +++ b/PDP10/ka10_pd.c @@ -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 +#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 diff --git a/PDP10/ka10_pmp.c b/PDP10/ka10_pmp.c new file mode 100644 index 00000000..ea3e69b9 --- /dev/null +++ b/PDP10/ka10_pmp.c @@ -0,0 +1,2436 @@ +/* PMP disk controller interface for WAITS. + + 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 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. + + Structure of a disk. See Hercules CKD disks. + + Numbers are stored least to most significant. + + Devid = "CKD_P370" + + uint8 devid[8] device header. + uint32 heads number of heads per cylinder + uint32 tracksize size of track + uint8 devtype Hex code of last two digits of device type. + uint8 fileseq always 0. + uint16 highcyl highest cylinder. + + uint8 resv[492] pad to 512 byte block + + Each Track has: + uint8 bin Track header. + uint16 cyl Cylinder number + uint16 head Head number. + + Each Record has: + uint16 cyl Cylinder number <- tpos + uint16 head Head number + uint8 rec Record id. + uint8 klen Length of key + uint16 dlen Length of data + + uint8 key[klen] Key data. + uint8 data[dlen] Data len. + + cpos points to where data is actually read/written from + + Pad to being track to multiple of 512 bytes. + + Last record has cyl and head = 0xffffffff + +*/ + +#include "kx10_defs.h" + +#ifndef NUM_DEVS_PMP +#define NUM_DEVS_PMP 0 +#endif + +#if (NUM_DEVS_PMP > 0) +#define UNIT_V_TYPE (UNIT_V_UF + 0) +#define UNIT_TYPE (0xf << UNIT_V_TYPE) + +#define GET_TYPE(x) ((UNIT_TYPE & (x)) >> UNIT_V_TYPE) +#define SET_TYPE(x) (UNIT_TYPE & ((x) << UNIT_V_TYPE)) +#define UNIT_DASD UNIT_ATTABLE | UNIT_DISABLE | UNIT_ROABLE | \ + UNIT_FIX | SET_TYPE(6) + +#define UNIT_V_ADDR (UNIT_V_TYPE + 4) +#define UNIT_ADDR_MASK (0xff << UNIT_V_ADDR) +#define GET_UADDR(x) ((UNIT_ADDR_MASK & x) >> UNIT_V_ADDR) +#define UNIT_ADDR(x) ((x) << UNIT_V_ADDR) + +#define NUM_UNITS_PMP 8 + +#define PMP_DEV 0500 + + +#define CMD u3 + +/* u3 */ +#define DK_NOP 0x03 /* Nop operation */ +#define DK_RELEASE 0x17 /* Release from channel */ +#define DK_RESTORE 0x13 /* Restore */ +#define DK_SEEK 0x07 /* Seek */ +#define DK_SEEKCYL 0x0B /* Seek Cylinder */ +#define DK_SEEKHD 0x1B /* Seek Head */ +#define DK_SETMSK 0x1f /* Set file mask */ +#define DK_SPACE 0x0f /* Space record */ +#define DK_SRCH_HAEQ 0x39 /* Search HA equal */ +#define DK_SRCH_IDEQ 0x31 /* Search ID equal */ +#define DK_SRCH_IDGT 0x51 /* Search ID greater */ +#define DK_SRCH_IDGE 0x71 /* Search ID greater or equal */ +#define DK_SRCH_KYEQ 0x29 /* Search Key equal */ +#define DK_SRCH_KYGT 0x49 /* Search Key greater */ +#define DK_SRCH_KYGE 0x69 /* Search Key greater or equal */ +#define DK_RD_IPL 0x02 /* Read IPL record */ +#define DK_RD_HA 0x1A /* Read home address */ +#define DK_RD_CNT 0x12 /* Read count */ +#define DK_RD_R0 0x16 /* Read R0 */ +#define DK_RD_D 0x06 /* Read Data */ +#define DK_RD_KD 0x0e /* Read key and data */ +#define DK_RD_CKD 0x1e /* Read count, key and data */ +#define DK_WR_HA 0x19 /* Write home address */ +#define DK_WR_R0 0x15 /* Write R0 */ +#define DK_WR_D 0x05 /* Write Data */ +#define DK_WR_KD 0x0d /* Write key and data */ +#define DK_WR_CKD 0x1d /* Write count, key and data */ +#define DK_WR_SCKD 0x01 /* Write special count, key and data */ +#define DK_ERASE 0x11 /* Erase to end of track */ +#define DK_RD_SECT 0x22 /* Read sector counter */ +#define DK_SETSECT 0x23 /* Set sector */ +#define DK_MT 0x80 /* Multi track flag */ + +#define DK_INDEX 0x00100 /* Index seen in command */ +#define DK_NOEQ 0x00200 /* Not equal compare */ +#define DK_HIGH 0x00400 /* High compare */ +#define DK_PARAM 0x00800 /* Parameter in u4 */ +#define DK_MSET 0x01000 /* Mode set command already */ +#define DK_SHORTSRC 0x02000 /* Last search was short */ +#define DK_SRCOK 0x04000 /* Last search good */ +#define DK_CYL_DIRTY 0x08000 /* Current cylinder dirty */ +#define DK_DONE 0x10000 /* Write command done, zero fill */ +#define DK_INDEX2 0x20000 /* Second index seen */ +#define DK_ATTN 0x40000 /* Device has attention set */ + +#define DK_MSK_INHWR0 0x00 /* Inhbit writing of HA/R0 */ +#define DK_MSK_INHWRT 0x40 /* Inhbit all writes */ +#define DK_MSK_ALLWRU 0x80 /* Allow all updates */ +#define DK_MSK_ALLWRT 0xc0 /* Allow all writes */ +#define DK_MSK_WRT 0xc0 /* Write mask */ + +#define DK_MSK_SKALLSKR 0x00 /* Allow all seek/recal */ +#define DK_MSK_SKALLCLY 0x08 /* Allow cyl/head only */ +#define DK_MSK_SKALLHD 0x10 /* Allow head only */ +#define DK_MSK_SKNONE 0x18 /* Allow no seeks */ +#define DK_MSK_SK 0x18 /* Seek mask */ + +#define POS u4 +/* u4 */ +/* Holds the current track and head */ +#define DK_V_TRACK 8 +#define DK_M_TRACK 0x3ff00 /* Max 1024 cylinders */ +#define DK_V_HEAD 0 +#define DK_M_HEAD 0xff /* Max 256 heads */ + +#define SENSE u5 +/* u5 */ +/* Sense byte 0 */ +#define SNS_CMDREJ 0x80 /* Command reject */ +#define SNS_INTVENT 0x40 /* Unit intervention required */ +#define SNS_BUSCHK 0x20 /* Parity error on bus */ +#define SNS_EQUCHK 0x10 /* Equipment check */ +#define SNS_DATCHK 0x08 /* Data Check */ +#define SNS_OVRRUN 0x04 /* Data overrun */ +#define SNS_TRKCND 0x02 /* Track Condition */ +#define SNS_SEEKCK 0x01 /* Seek Check */ + +/* Sense byte 1 */ +#define SNS_DCCNT 0x80 /* Data Check Count */ +#define SNS_TRKOVR 0x40 /* Track Overrun */ +#define SNS_ENDCYL 0x20 /* End of Cylinder */ +#define SNS_INVSEQ 0x10 /* Invalid Sequence */ +#define SNS_NOREC 0x08 /* No record found */ +#define SNS_WRP 0x04 /* Write Protect */ +#define SNS_ADDR 0x02 /* Missing Address Mark */ +#define SNS_OVRINC 0x01 /* Overflow Incomplete */ + +/* Sense byte 2 */ +#define SNS_BYTE2 0x00 /* Diags Use */ +/* Sense byte 3 */ +#define SNS_BYTE3 0x00 /* Diags Use */ + +/* saved in state field of data */ +/* Record position, high 4 bits, low internal short count */ +#define DK_POS_INDEX 0x0 /* At Index Mark */ +#define DK_POS_HA 0x1 /* In home address (c) */ +#define DK_POS_CNT 0x2 /* In count (c) */ +#define DK_POS_KEY 0x3 /* In Key area */ +#define DK_POS_DATA 0x4 /* In Data area */ +#define DK_POS_AM 0x5 /* Address mark before record */ +#define DK_POS_END 0x8 /* Past end of data */ +#define DK_POS_SEEK 0xF /* In seek */ + +#define LASTCMD u6 +/* u6 holds last command */ +/* Held in ccyl entry */ + +#define DATAPTR up7 +/* Pointer held in up7 */ +struct pmp_t +{ + uint8 *cbuf; /* Cylinder buffer */ + uint32 cpos; /* Position of head of cylinder in file */ + uint32 tstart; /* Location of start of track */ + uint16 ccyl; /* Current Cylinder number */ + uint16 cyl; /* Cylinder head at */ + uint16 tpos; /* Track position */ + uint16 rpos; /* Start of current record */ + uint16 dlen; /* remaining in data */ + uint32 tsize; /* Size of one track include rounding */ + uint8 state; /* Current state */ + uint8 klen; /* remaining in key */ + uint8 filemsk; /* Current file mask */ + uint8 rec; /* Current record number */ + uint16 count; /* Remaining in current operation */ +}; + + +/* PDP10 CONO/CONI and DATA bits */ + +/* CONI 500 bits */ +#define NXM_ERR 00200000000000LL /* Non-existent memory */ +#define CHA_ERR 00100000000000LL /* Data chaining error */ +#define SEL_ERR 00040000000000LL /* Selection error */ +#define LST_ADDR 00037700000000LL /* Last address used */ +#define PAR1_ERR 00000040000000LL /* Parity error control */ +#define PAR2_ERR 00000020000000LL /* Parity error memory */ +#define IDLE 00000010100000LL /* Channel idle */ +#define INT_SEL 00000004000000LL /* Initial selection state */ +#define REQ_SEL 00000002000000LL /* Device requestion select */ +#define TRANS 00000001000000LL /* Transfer in progress */ +#define PAR_ERR 00000000400000LL /* Parity error */ +#define HOLD_EMPTY 00000000200000LL /* Command hold empty */ +#define UNU_END 00000000040000LL /* Unusual end */ +#define NEW_STS 00000000020000LL /* New status */ +#define ATTN 00000000010000LL /* Attention */ +#define ST_MOD 00000000004000LL /* Status modifier */ +#define CTL_END 00000000002000LL /* Control unit end */ +#define BSY 00000000001000LL /* Device is busy */ +#define CHN_END 00000000000400LL /* Channel end */ +#define DEV_END 00000000000200LL /* Device end */ +#define UNIT_CHK 00000000000100LL /* Unit check */ +#define UNIT_EXP 00000000000040LL /* Unit exception */ +#define PI_ACT 00000000000020LL /* PI channel active */ +#define PIA 00000000000007LL /* PI channel */ +#define STS_MASK 00000000017740LL /* Status bits to clear */ + +/* CONO 500 bits */ +#define IRQ_ERROR 00000000400000LL /* Disk or Core parity error */ +#define IRQ_EMPTY 00000000200000LL /* Command hold empty */ +#define IRQ_IDLE 00000000100000LL /* Channel is idle */ +#define IRQ_UEND 00000000040000LL /* Unusual end */ +#define IRQ_NSTS 00000000020000LL /* New Status */ +#define IRQ_STS 00000000017740LL /* Status bits set */ + +/* DATAO 500 is -Word Count, Address */ +/* DATAI 500 is Current Address */ + +/* CONI 504 */ +#define OP1 000000010000LL /* Channel in operation */ +#define DAT_CHAIN 000000004000LL /* Data Chaining enabled */ +#define WCMA_LD 000000002000LL /* WCMA hold register full */ +#define CMD_LD 000000001000LL /* Command hold loaded */ +#define IDLE_CH 000000000400LL /* Channel is idle */ +#define REQ_CH 000000000200LL /* Request for channel */ +#define IS_CH 000000000100LL /* Initial select state */ +#define TRANS_CH 000000000040LL /* Tranfer in progress */ +#define CMD_EMP 000000000020LL /* Command hold empty */ +#define CMD_FUL 000000000010LL /* Command hold full */ +#define OPL 000000000004LL /* Operation out */ + +/* CONO 504 */ +#define CLR_UEND 00000004000LL /* Clear Unusual end */ +#define CLR_MUX 00000002000LL /* Clear memory multiplexer */ +#define CLR_DATCH 00000001000LL /* Clear data chaining flag */ +#define CLR_IRQ 00000000400LL /* Clear IRQs */ +#define NSTS_CLR 00000000200LL /* Clear New status flag */ +#define PWR_CLR 00000000100LL /* Power on clear */ +#define STS_CLR 00000000040LL /* Clear device status */ +#define CMD_CLR 00000000020LL /* Clear command hold */ +#define CMD_HOLD 00000000010LL /* Set command hold loaded. */ +#define DEV_RESET 00000000004LL /* Reset current device */ +#define OPL_RESET 00000000002LL /* Reset all devices */ +#define CHN_RESET 00000000001LL /* Reset the channel */ + +/* DATAO 504 */ +#define CMD_MASK 0000000000377LL /* Command */ +#define SKP_MOD_OFF 0000000000400LL /* Skip mod off */ +#define SKP_MOD_ON 0000000001000LL /* Skip mod on */ +#define CMDCH_ON 0000000002000LL /* Command chaining */ +#define CNT_BYT 0000000004000LL /* Count in bytes */ +#define BYTE_MODE 0000000010000LL /* Transfer bytes not words */ +#define SET_HOLD 0000000020000LL /* Set command hold */ +#define DEV_ADDR 0000017740000LL /* Device address */ +#define DATCH_ON 0000020000000LL /* Data chaining on */ +#define HOLD_MASK 0000037777777LL /* Bits in command */ + +/* Channel sense bytes */ +#define SNS_ATTN 0x80 /* Unit attention */ +#define SNS_SMS 0x40 /* Status modifier */ +#define SNS_CTLEND 0x20 /* Control unit end */ +#define SNS_BSY 0x10 /* Unit Busy */ +#define SNS_CHNEND 0x08 /* Channel end */ +#define SNS_DEVEND 0x04 /* Device end */ +#define SNS_UNITCHK 0x02 /* Unit check */ +#define SNS_UNITEXP 0x01 /* Unit exception */ + +/* Channel pmp_cnt values. */ +#define BUFF_EMPTY 0x10 /* Buffer is empty */ +#define BUFF_DIRTY 0x20 /* Buffer is dirty flag */ +#define BUFF_CHNEND 0x40 /* Channel end */ + +struct disk_t +{ + const char *name; /* Type Name */ + int cyl; /* Number of cylinders */ + int heads; /* Number of heads/cylinder */ + int bpt; /* Max bytes per track */ + uint8 sen_cnt; /* Number of sense bytes */ + uint8 dev_type; /* Device type code */ +} +disk_type[] = +{ + {"2301", 1, 200, 20483, 6, 0x01}, /* 4.1 M */ + {"2302", 250, 46, 4984, 6, 0x02}, /* 57.32 M 50ms, 120ms/10, 180ms> 10 */ + {"2303", 80, 10, 4984, 6, 0x03}, /* 4.00 M */ + {"2305", 48, 8, 14568, 6, 0x05}, /* 5.43 M */ + {"2305-2",96, 8, 14858, 6, 0x05}, /* 11.26 M */ + {"2311", 202, 10, 3717, 6, 0x11}, /* 7.32 M 156k/s 30 ms 145 full */ + {"2314", 203, 20, 7294, 6, 0x14}, /* 29.17 M */ + {"3330", 411, 19, 13165, 24, 0x30}, /* 100.00 M */ + {"3330-2",815, 19, 13165, 24, 0x30}, + {0}, +}; + + +/* Header block */ +struct pmp_header +{ + uint8 devid[8]; /* device header. */ + int heads; /* number of heads per cylinder */ + uint32 tracksize; /* size of track */ + uint8 devtype; /* Hex code of last two digits of device type. */ + uint8 fileseq; /* always 0. */ + uint16 highcyl; /* highest cylinder. */ + uint8 resv[492]; /* pad to 512 byte block */ +}; + +int pmp_pia; /* PIA for PMP device */ +uint64 pmp_status; /* CONI status for device 500 */ +int pmp_statusb; +uint32 pmp_cmd_hold; /* Command hold register */ +uint32 pmp_wc_hold; /* Word count hold */ +uint32 pmp_addr_hold; /* Address register hold */ +uint32 pmp_wc; /* Current word count register */ +uint32 pmp_addr; /* Current address register */ +uint64 pmp_data; /* Data assembly register */ +int pmp_cnt; /* Character count in asm register */ +int pmp_cmd; /* Current command */ +uint32 pmp_irq; /* Irq enable flags */ +UNIT *pmp_cur_unit; /* Currently addressed unit */ + + +t_stat pmp_devio(uint32 dev, uint64 *data); +int pmp_checkirq(); +int pmp_posterror(uint64); +int chan_read_byte(uint8 *data); +int chan_write_byte(uint8 *data); +void chan_end(uint8 flags); +void pmp_startcmd(); +void pmp_adjpos(UNIT * uptr); +t_stat pmp_srv(UNIT *); +t_stat pmp_reset(DEVICE *); +t_stat pmp_attach(UNIT *, CONST char *); +t_stat pmp_detach(UNIT *); +t_stat pmp_set_type(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat pmp_get_type(FILE * st, UNIT * uptr, int32 v, + CONST void *desc); +t_stat pmp_set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, + void *desc); +t_stat pmp_get_dev_addr(FILE * st, UNIT * uptr, int32 v, + CONST void *desc); +t_stat pmp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *pmp_description (DEVICE *dptr); + +DIB pmp_dib[] = { + {PMP_DEV, 2, &pmp_devio, NULL}}; + + +MTAB pmp_mod[] = { + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "TYPE", "TYPE", + &pmp_set_type, &pmp_get_type, NULL, "Type of disk"}, + {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &pmp_set_dev_addr, + &pmp_get_dev_addr, NULL}, + {0} +}; + +UNIT pmp_unit[] = { + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x60), 0)}, /* 0 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x61), 0)}, /* 1 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x62), 0)}, /* 2 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x63), 0)}, /* 3 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x64), 0)}, /* 4 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x65), 0)}, /* 5 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x66), 0)}, /* 6 */ + {UDATA(&pmp_srv, UNIT_DASD|UNIT_ADDR(0x67), 0)}, /* 7 */ +}; + +DEVICE pmp_dev = { + "PMP", pmp_unit, NULL, pmp_mod, + NUM_UNITS_PMP, 8, 15, 1, 8, 8, + NULL, NULL, &pmp_reset, NULL, &pmp_attach, &pmp_detach, + &pmp_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_DISK, 0, dev_debug, + NULL, NULL, &pmp_help, NULL, NULL, &pmp_description +}; + + +/* IOT routines */ +t_stat +pmp_devio(uint32 dev, uint64 *data) { + int i; + + switch(dev & 07) { + case CONI: + *data = pmp_status | pmp_pia; + if (pmp_checkirq()) + *data |= PI_ACT; + if (pmp_statusb & IS_CH) + *data |= INT_SEL; + if (pmp_statusb & REQ_CH) + *data |= REQ_SEL; + if (pmp_statusb & IDLE_CH) + *data |= IDLE; + if ((pmp_statusb & (WCMA_LD|CMD_LD)) != (WCMA_LD|CMD_LD)) + *data |= HOLD_EMPTY; + if (pmp_cur_unit != NULL) + *data |= ((uint64)GET_UADDR(pmp_cur_unit->flags)) << 24; + if ((pmp_status & (NXM_ERR|CHA_ERR|SEL_ERR)) != 0) + *data |= UNU_END; + sim_debug(DEBUG_CONI, &pmp_dev, "PMP %03o CONI %012llo PC=%o\n", + dev, *data, PC); + break; + + case CONO: + sim_debug(DEBUG_CONO, &pmp_dev, "PMP %03o CONO %012llo PC=%06o\n", + dev, *data, PC); + if (*data & 010) + pmp_pia = *data & 7; + pmp_irq = (uint32)(*data); + (void)pmp_checkirq(); + break; + + case DATAI: + sim_debug(DEBUG_DATAIO, &pmp_dev, "PMP %03o DATI %012llo PC=%06o\n", + dev, *data, PC); + *data = (uint64)(pmp_addr); + break; + + case DATAO: + pmp_addr_hold = (*data) & RMASK; + pmp_wc_hold = (*data >> 18) & RMASK; + pmp_statusb |= WCMA_LD; + sim_debug(DEBUG_DATAIO, &pmp_dev, "PMP %03o DATO %012llo %d PC=%06o\n", + dev, *data, (int)(((RMASK ^ pmp_wc_hold) + 1) & RMASK), PC); + (void)pmp_checkirq(); + break; + + case CONI|04: + *data = pmp_statusb; + if ((pmp_statusb & WCMA_LD) != 0 && (pmp_statusb & CMD_LD) != 0) + *data |= CMD_FUL; + if ((*data & CMD_FUL) == 0) + *data |= CMD_EMP; + if ((pmp_statusb & (OP1|REQ_CH|IDLE_CH)) == IDLE_CH) + *data |= OPL; + sim_debug(DEBUG_CONI, &pmp_dev, "IBM %03o CONI %012llo PC=%o\n", + dev, *data, PC); + break; + + case CONO|04: + sim_debug(DEBUG_CONO, &pmp_dev, "IBM %03o CONO %012llo PC=%06o\n", + dev, *data, PC); + if (*data & PWR_CLR) { /* Power on clear */ + pmp_statusb = IDLE_CH; + pmp_status = 0; + pmp_pia = 0; + /* Clear command in each unit */ + break; + } + if (*data & CHN_RESET) { + pmp_statusb = IDLE_CH; + pmp_status = 0; + break; + } + if (*data & STS_CLR) /* Clear status bits */ + pmp_status &= ~STS_MASK; + if (*data & CLR_DATCH) /* Data chaining */ + pmp_cmd &= ~DATCH_ON; + if (*data & CMD_CLR) /* Clear pending command */ + pmp_statusb &= ~CMD_LD; + if (*data & CMD_HOLD) { /* Set command buffer full */ + pmp_statusb |= CMD_LD; + } + if (*data & (CLR_UEND|CLR_IRQ)) /* Clear unusual end condtions */ + pmp_status &= ~(UNU_END|NEW_STS|STS_MASK); + if (*data & NSTS_CLR) { /* Clear new status */ + pmp_status &= ~NEW_STS; + if ((pmp_statusb & OP1) == 0) { /* Check if any device requesting attn */ + for (i = 0; i < NUM_UNITS_PMP; i++) { + if ((pmp_dev.units[i].CMD & DK_ATTN) != 0) { + pmp_cur_unit = &pmp_dev.units[i]; + pmp_status |= NEW_STS|DEV_END; + pmp_dev.units[i].CMD &= ~DK_ATTN; + break; + } + } + if ((pmp_statusb & NEW_STS) == 0) { + pmp_statusb &= ~REQ_CH; + if (pmp_statusb & CMD_LD) + pmp_startcmd(); + } + } + } + (void)pmp_checkirq(); + break; + + case DATAI|4: + sim_debug(DEBUG_DATAIO, &pmp_dev, "IBM %03o DATI %012llo PC=%06o\n", + dev, *data, PC); + break; + + case DATAO|4: + sim_debug(DEBUG_DATAIO, &pmp_dev, "IBM %03o DATO %012llo PC=%06o\n", + dev, *data, PC); + pmp_cmd_hold = (*data) & HOLD_MASK; + pmp_statusb |= CMD_LD; + pmp_startcmd(); + (void)pmp_checkirq(); + break; + } + return SCPE_OK; +} + +/* Check if interrupt pending for device */ +int +pmp_checkirq() { + int f = 0; + + clr_interrupt(PMP_DEV); + if ((pmp_irq & IRQ_ERROR) != 0 && (pmp_status & (PAR1_ERR|PAR2_ERR|PAR_ERR)) != 0) { + sim_debug(DEBUG_DETAIL, &pmp_dev, "parity irq\n"); + f = 1; + } + if ((pmp_irq & IRQ_EMPTY) != 0 && (pmp_statusb & (WCMA_LD|CMD_LD)) != (WCMA_LD|CMD_LD)) { + sim_debug(DEBUG_DETAIL, &pmp_dev, "load irq\n"); + f = 1; + } + if ((pmp_irq & IRQ_IDLE) != 0 && (pmp_statusb & (OP1|IDLE_CH)) == IDLE_CH) { + sim_debug(DEBUG_DETAIL, &pmp_dev, "idle irq\n"); + f = 1; +} + if ((pmp_irq & IRQ_UEND) != 0 && (pmp_status & (NXM_ERR|CHA_ERR|SEL_ERR|UNU_END)) != 0) { + sim_debug(DEBUG_DETAIL, &pmp_dev, "uend irq\n"); + f = 1; +} + if ((pmp_status & pmp_irq & (IRQ_NSTS|IRQ_STS)) != 0) { + sim_debug(DEBUG_DETAIL, &pmp_dev, "mem sts %o\n", (int)(pmp_status & pmp_irq & (IRQ_NSTS|IRQ_STS))); + f = 1; +} + if (f) + set_interrupt(PMP_DEV, pmp_pia); + return f; +} + +/* Post and error message and clear channel */ +int +pmp_posterror(uint64 err) { + pmp_status |= err; + pmp_statusb &= ~(OP1|IS_CH|TRANS_CH); + pmp_statusb |= IDLE_CH; + (void)pmp_checkirq(); + return 1; +} + +/* read byte from memory */ +int +chan_read_byte(uint8 *data) { + int byte; + int xfer = 0; + + if ((pmp_cmd & 0x1) == 0) { + return 1; + } + /* Check if finished transfer */ + if (pmp_cnt & BUFF_CHNEND) + return 1; + + pmp_statusb |= TRANS_CH; /* Tranfer in progress */ + /* Read in next work if buffer is in empty status */ + if (pmp_cnt & BUFF_EMPTY) { + if (pmp_addr >= (int)MEMSIZE) + return pmp_posterror(NXM_ERR); + pmp_data = M[pmp_addr]; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_read %06o %012llo\n", pmp_addr, pmp_data); + pmp_addr++; + pmp_cnt = 0; + xfer = 1; /* Read in a word */ + } + /* Handle word vs byte mode */ + if (pmp_cmd & BYTE_MODE) { + byte = (pmp_data >> (4 + (8 * (3 - (pmp_cnt & 0x3))))) & 0xff; + pmp_cnt++; + *data = byte; + if ((pmp_cnt & 03) == 0) + pmp_cnt = BUFF_EMPTY; + } else { + if ((pmp_cnt & 0xf) > 0x3) { + if ((pmp_cnt & 0xf) == 0x4) { /* Split byte */ + byte = (pmp_data << 4) & 0xf0; + if (pmp_addr >= (int)MEMSIZE) + return pmp_posterror(NXM_ERR); + pmp_data = M[pmp_addr]; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_read %06o %012llo\n", pmp_addr, pmp_data); + pmp_addr++; + xfer = 1; /* Read in a word */ + byte |= pmp_data & 0xf; + } else { + byte = (pmp_data >> (4 + (8 * (8 - (pmp_cnt & 0xf))))) & 0xff; + } + } else { + byte = (pmp_data >> (4 + (8 * (3 - (pmp_cnt & 0xf))))) & 0xff; + } + pmp_cnt++; + if ((pmp_cnt & 0xf) == 9) + pmp_cnt = BUFF_EMPTY; + } + *data = byte; + if (pmp_cmd & CNT_BYT) { + pmp_wc ++; + } else if (xfer) { + pmp_wc ++; + } + if (pmp_wc & 07000000) + pmp_cnt |= BUFF_CHNEND; + return 0; +#if 0 +next: + /* If not data channing, let device know there will be no + * more data to come + */ + if ((pmp_cmd & DATCH_ON) == 0) { + pmp_cnt = BUFF_CHNEND; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_read_end\n"); + return 1; + } else { + if (pmp_statusb & WCMA_LD) { + pmp_statusb &= ~(WCMA_LD); + pmp_addr = pmp_addr_hold; + pmp_wc = pmp_wc_hold; + pmp_data = 0; + } else { + return pmp_posterror(CHA_ERR); + } + } + goto load; +#endif +} + +/* write byte to memory */ +int +chan_write_byte(uint8 *data) { + int xfer = 0; + + if ((pmp_cmd & 0x1) != 0) { + return 1; + } + /* Check if at end of transfer */ + if (pmp_cnt == BUFF_CHNEND) { + return 1; + } + + pmp_statusb |= TRANS_CH; /* Tranfer in progress */ + if (pmp_cnt == BUFF_EMPTY) { + pmp_data = 0; + pmp_cnt = 0; + } + /* Handle word vs byte mode */ + if (pmp_cmd & BYTE_MODE) { + if (pmp_cnt & BUFF_CHNEND) + return 1; + pmp_data &= ~(0xff <<(4 + (8 * (3 - (pmp_cnt & 0x3))))); + pmp_data |= (uint64)(*data & 0xff) << (4 + (8 * (3 - (pmp_cnt & 0x3)))); + pmp_cnt++; + pmp_cnt |= BUFF_DIRTY; + if ((pmp_cnt & 03) == 0) { + pmp_cnt &= ~(BUFF_DIRTY|7); + if (pmp_addr >= (int)MEMSIZE) + return pmp_posterror(NXM_ERR); + M[pmp_addr] = pmp_data; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write %06o %012llo\n", pmp_addr, pmp_data); + pmp_addr++; + xfer = 1; + } + } else { + if ((pmp_cnt & 0xf) > 0x3) { + if ((pmp_cnt & 0xf) == 0x4) { /* Split byte */ + pmp_data &= ~0xf; + pmp_data |= (uint64)((*data >> 4) & 0xf); + if (pmp_addr >= (int)MEMSIZE) + return pmp_posterror(NXM_ERR); + M[pmp_addr] = pmp_data; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write %06o %012llo %2x\n", pmp_addr, pmp_data, pmp_cnt); + pmp_addr++; + xfer = 1; /* Read in a word */ + pmp_data = *data & 0xf; + pmp_cnt |= BUFF_DIRTY; + } else { + pmp_data &= ~(0xff <<(4 + (8 * (8 - (pmp_cnt & 0xf))))); + pmp_data |= (uint64)(*data & 0xff) << (4 + (8 * (8 - (pmp_cnt & 0xf)))); + pmp_cnt |= BUFF_DIRTY; + } + } else { + pmp_data &= ~(0xff <<(4 + (8 * (3 - (pmp_cnt & 0xf))))); + pmp_data |= (uint64)(*data & 0xff) << (4 + (8 * (3 - (pmp_cnt & 0xf)))); + pmp_cnt |= BUFF_DIRTY; + } + pmp_cnt++; + if ((pmp_cnt & 0xf) == 9) { + pmp_cnt = BUFF_EMPTY; + if (pmp_addr >= (int)MEMSIZE) + return pmp_posterror(NXM_ERR); + M[pmp_addr] = pmp_data; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write %06o %012llo %2x\n", pmp_addr, pmp_data, pmp_cnt); + pmp_addr++; + xfer = 1; /* Read in a word */ + } + } + if (pmp_cmd & CNT_BYT) { + pmp_wc ++; + } else if (xfer) { + pmp_wc ++; + } + if (pmp_wc & 07000000) { + /* If not data channing, let device know there will be no + * more data to come + */ + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write_wc\n"); + if ((pmp_cmd & DATCH_ON) == 0) { + pmp_cnt = BUFF_CHNEND; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write_end\n"); + return 1; + } else { + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write reload\n"); + if (pmp_statusb & WCMA_LD) { + pmp_statusb &= ~(WCMA_LD); + pmp_addr = pmp_addr_hold; + pmp_wc = pmp_wc_hold; + pmp_data = 0; + } else { + return pmp_posterror(CHA_ERR); + } + } + } + return 0; +} + +/* + * Signal end of transfer by device. + */ +void +chan_end(uint8 flags) { + + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_end(%x) %x\n", flags, pmp_wc); + /* If PCI flag set, trigger interrupt */ + /* Flush buffer if there was any change */ + if (pmp_cnt & BUFF_DIRTY) { + pmp_cnt = BUFF_EMPTY; + if (pmp_addr >= (int)MEMSIZE) { + (void) pmp_posterror(NXM_ERR); + return; + } + M[pmp_addr] = pmp_data; + sim_debug(DEBUG_DATA, &pmp_dev, "chan_write %012llo\n", pmp_data); + pmp_addr++; + } + pmp_statusb &= ~TRANS_CH; /* Clear transfer in progress */ + pmp_statusb |= IDLE_CH; + pmp_status |= NEW_STS | CHN_END | ((uint64)flags) << 5; + + if (pmp_status & (BSY|UNIT_CHK)) + pmp_status |= UNU_END; + + /* If channel is also finished, then skip any more data commands. */ + if (pmp_status & (CHN_END|DEV_END)) { + pmp_cnt = BUFF_CHNEND; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_endc %012llo %06o\n", pmp_status, pmp_cmd); + + /* While command has chain data set, continue to skip */ + if (pmp_cmd & DATCH_ON) { + (void) pmp_posterror(CHA_ERR); + return; + } + + if (pmp_cmd & CMDCH_ON) { + pmp_startcmd(); + (void)pmp_checkirq(); + return; + } + /* Indicate that device is done */ + pmp_statusb &= ~OP1; + } + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_endf %012llo %06o\n", pmp_status, pmp_statusb); + (void)pmp_checkirq(); +} + +/* Issue command to device */ +void +pmp_startcmd() { + uint16 addr; + int i; + int unit; + int cmd; + int old_cmd = pmp_cmd; + uint8 ch; + + sim_debug(DEBUG_CMD, &pmp_dev, "start command %o\n", pmp_statusb); + if ((pmp_statusb & CMD_LD) == 0 || (pmp_statusb & IDLE_CH) == 0) { + sim_debug(DEBUG_CMD, &pmp_dev, "not ready %o\n", pmp_statusb); + return; + } + /* Idle, no device selected. */ + if ((pmp_statusb & OP1) == 0) { + /* Set to initial selection. */ + pmp_statusb |= IS_CH; + pmp_cur_unit = NULL; + + /* Copy over command */ + pmp_cmd = pmp_cmd_hold; + cmd = pmp_cmd & CMD_MASK; + pmp_statusb &= ~(CMD_LD); + if (pmp_statusb & WCMA_LD) { + pmp_statusb &= ~(WCMA_LD); + pmp_addr = pmp_addr_hold; + pmp_wc = pmp_wc_hold; + pmp_cnt = BUFF_EMPTY; + } + addr = (uint16)((pmp_cmd & DEV_ADDR) >> 14); + sim_debug(DEBUG_CMD, &pmp_dev, "initiate on %02x\n", addr); + /* scan units looking for matching device. */ + for (i = 0; i < NUM_UNITS_PMP; i++) { + if (addr == GET_UADDR(pmp_dev.units[i].flags)) { + pmp_cur_unit = &pmp_dev.units[i]; + break; + } + } + } + + + /* If no matching device found, report selection error */ + if (pmp_cur_unit == NULL) { + sim_debug(DEBUG_CMD, &pmp_dev, "No device\n"); + (void)pmp_posterror(SEL_ERR); + return; + } + + /* Check if unit is busy */ + unit = GET_UADDR(pmp_cur_unit->flags) & 0x7; + + /* Check if device busy */ + if ((pmp_cur_unit->CMD & 0xff) != 0) { + sim_debug(DEBUG_CMD, &pmp_dev, "busy %o\n", pmp_statusb); + if (pmp_statusb & IS_CH) + (void)pmp_posterror(SEL_ERR); + pmp_status |= UNU_END|BSY; + (void)pmp_checkirq(); + return; + } + + /* Copy over command */ + if ((pmp_statusb & CMD_LD) != 0) { + pmp_cmd = pmp_cmd_hold; + sim_debug(DEBUG_CMD, &pmp_dev, "load %o\n", pmp_cmd); + pmp_statusb &= ~(CMD_LD); + if (pmp_statusb & WCMA_LD) { + pmp_statusb &= ~(WCMA_LD); + pmp_addr = pmp_addr_hold; + pmp_wc = pmp_wc_hold; + pmp_cnt = BUFF_EMPTY; + } + } + + /* Otherwise if there is command chaining, try new command */ + if (old_cmd & CMDCH_ON) { + /* Channel in operation, must be command chaining */ + if (((old_cmd & SKP_MOD_OFF) != 0) && ((pmp_status & ST_MOD) == 0)) { + pmp_statusb &= ~(CMD_LD); + (void)pmp_checkirq(); + return; + } + if (((old_cmd & SKP_MOD_ON) != 0) && ((pmp_status & ST_MOD) != 0)) { + pmp_statusb &= ~(CMD_LD); + (void)pmp_checkirq(); + return; + } + } + sim_debug(DEBUG_CMD, &pmp_dev, "CMD unit=%d %02x %06o\n", unit, pmp_cmd, pmp_addr); + + (void)pmp_checkirq(); + + cmd = pmp_cmd & CMD_MASK; + /* If device not attached, return error */ + if ((pmp_cur_unit->flags & UNIT_ATT) == 0) { + if (cmd == 0x4) { /* Sense */ + sim_debug(DEBUG_CMD, &pmp_dev, "CMD sense\n"); + ch = pmp_cur_unit->SENSE & 0xff; + sim_debug(DEBUG_DETAIL, &pmp_dev, "sense unit=%d 1 %x\n", unit, ch); + chan_write_byte(&ch) ; + ch = (pmp_cur_unit->SENSE >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, &pmp_dev, "sense unit=%d 2 %x\n", unit, ch); + chan_write_byte(&ch) ; + ch = 0; + sim_debug(DEBUG_DETAIL, &pmp_dev, "sense unit=%d 3 %x\n", unit, ch); + chan_write_byte(&ch) ; + ch = unit; + sim_debug(DEBUG_DETAIL, &pmp_dev, "sense unit=%d 4 %x\n", unit, ch); + chan_write_byte(&ch) ; + ch = 0; + chan_write_byte(&ch) ; + chan_write_byte(&ch) ; + pmp_cur_unit->SENSE = 0; + pmp_status |= NEW_STS|CHN_END|DEV_END; + (void)pmp_posterror(0); + return; + } + if (cmd == 0x0) + return; + pmp_cur_unit->SENSE = SNS_INTVENT|SNS_CMDREJ; + pmp_status |= UNU_END|NEW_STS|CHN_END|DEV_END|UNIT_CHK; + (void)pmp_posterror(0); + return; + } + + /* Issue the actual command */ + switch (cmd & 0x3) { + case 0x3: /* Control */ + if (cmd == 0x3 || cmd == DK_RELEASE) { + pmp_status &= ~(STS_MASK); + pmp_status |= NEW_STS|CHN_END|DEV_END; + if ((pmp_cmd & CMDCH_ON) == 0) + /* Indicate that device is done */ + pmp_statusb &= ~OP1; + (void)pmp_checkirq(); + return; + } + + /* Fall Through */ + + case 0x1: /* Write command */ + case 0x2: /* Read command */ + pmp_statusb &= ~IDLE_CH; + pmp_cur_unit->CMD &= ~(DK_PARAM); + pmp_cur_unit->CMD |= cmd; + sim_debug(DEBUG_CMD, &pmp_dev, "CMD unit=%d CMD=%02x\n", unit, pmp_cur_unit->CMD); + return; + + case 0x0: /* Status */ + if (cmd == 0x4) { /* Sense */ + pmp_statusb &= ~IDLE_CH; + pmp_cur_unit->CMD |= cmd; + return; + } + break; + } + pmp_status &= ~(STS_MASK); + if (pmp_cur_unit->SENSE & 0xff) + pmp_status |= UNU_END|UNIT_CHK; + pmp_status |= NEW_STS|CHN_END|DEV_END; + pmp_statusb |= IDLE_CH; + pmp_statusb &= ~OP1; + sim_debug(DEBUG_CMD, &pmp_dev, "CMD unit=%d finish\n", unit); + (void)pmp_checkirq(); +} + +/* Compute position on new track. */ +void +pmp_adjpos(UNIT * uptr) +{ + uint16 addr = GET_UADDR(uptr->flags); + struct pmp_t *data = (struct pmp_t *)(uptr->DATAPTR); + uint8 *rec; + int pos; + + /* Save current position */ + pos = data->tpos; + + /* Set ourselves to start of track */ + data->state = DK_POS_HA; + data->rec = data->klen = 0; + data->rpos = data->count = data->dlen = 0; + data->tstart = (uptr->POS & 0xff) * data->tsize; + rec = &data->cbuf[data->rpos + data->tstart]; + /* Skip forward until we reach pos */ + for (data->tpos = 0; data->tpos < pos; data->tpos++) { + switch(data->state) { + case DK_POS_HA: /* In home address (c) */ + if (data->count == 4) { + data->tpos = data->rpos = 5; + data->state = DK_POS_CNT; + rec = &data->cbuf[data->rpos + data->tstart]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) + data->state = DK_POS_END; + } + break; + case DK_POS_CNT: /* In count (c) */ + if (data->count == 0) { + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) { + data->state = DK_POS_END; + } + data->klen = rec[5]; + data->dlen = (rec[6] << 8) | rec[7]; + } + if (data->count == 7) { + data->state = DK_POS_KEY; + if (data->klen == 0) + data->state = DK_POS_DATA; + } + break; + case DK_POS_KEY: /* In Key area */ + if (data->count == data->klen) { + data->state = DK_POS_DATA; + data->count = 0; + } + break; + case DK_POS_DATA: /* In Data area */ + if (data->count == data->dlen) { + data->state = DK_POS_AM; + } + break; + case DK_POS_AM: /* Beginning of record */ + data->rpos += data->dlen + data->klen + 8; + data->tpos = data->rpos; + data->rec++; + data->state = DK_POS_CNT; + data->count = 0; + rec = &data->cbuf[data->rpos + data->tstart]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) + data->state = DK_POS_END; + break; + case DK_POS_END: /* Past end of data */ + data->tpos+=10; + data->count = 0; + data->klen = 0; + data->dlen = 0; + return; + } + } +} + +/* Handle processing of disk requests. */ +t_stat pmp_srv(UNIT * uptr) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + struct pmp_t *data = (struct pmp_t *)(uptr->DATAPTR); + int unit = (uptr - dptr->units); + int cmd = uptr->CMD & 0x7f; + int type = GET_TYPE(uptr->flags); + int state = data->state; + int count = data->count; + int trk; + int i; + int rd = ((cmd & 0x3) == 0x1) | ((cmd & 0x3) == 0x2); + uint8 *rec; + uint8 *da; + uint8 ch; + uint8 buf[8]; + + /* Check if read or write command, if so grab correct cylinder */ + if (rd && data->cyl != data->ccyl) { + uint32 tsize = data->tsize * disk_type[type].heads; + if (uptr->CMD & DK_CYL_DIRTY) { + (void)sim_fseek(uptr->fileref, data->cpos, SEEK_SET); + (void)sim_fwrite(data->cbuf, 1, tsize, uptr->fileref); + uptr->CMD &= ~DK_CYL_DIRTY; + } + data->ccyl = data->cyl; + sim_debug(DEBUG_DETAIL, dptr, "Load unit=%d cyl=%d\n", unit, data->cyl); + data->cpos = sizeof(struct pmp_header) + (data->ccyl * tsize); + (void)sim_fseek(uptr->fileref, data->cpos, SEEK_SET); + (void)sim_fread(data->cbuf, 1, tsize, uptr->fileref); + } + sim_debug(DEBUG_EXP, dptr, "state unit=%d %02x %d\n", unit, state, data->tpos); + + rec = &data->cbuf[data->rpos + data->tstart]; + da = &data->cbuf[data->tpos + data->tstart]; + if (state != DK_POS_SEEK && data->tpos >= data->tsize) { + sim_debug(DEBUG_EXP, dptr, "state end unit=%d %d\n", unit, data->tpos); + state = DK_POS_INDEX; + } + switch(state) { + case DK_POS_INDEX: /* At Index Mark */ + /* Read and multi-track advance to next head */ + if ((uptr->CMD & 0x83) == 0x82 || (uptr->CMD & 0x83) == 0x81) { + sim_debug(DEBUG_DETAIL, dptr, "adv head unit=%d %02x %d %d %02x\n", + unit, state, data->tpos, uptr->POS & 0xff, data->filemsk); + if ((data->filemsk & DK_MSK_SK) == DK_MSK_SKNONE) { + sim_debug(DEBUG_DETAIL, dptr, "end cyl skmsk unit=%d %02x %d %02x\n", + unit, state, data->tpos, data->filemsk); + uptr->SENSE = (SNS_WRP << 8); + uptr->CMD &= ~0xff; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + goto index; + } + uptr->POS ++; + if ((uptr->POS & 0xff) >= disk_type[type].heads) { + sim_debug(DEBUG_DETAIL, dptr, "end cyl unit=%d %02x %d\n", + unit, state, data->tpos); + uptr->SENSE = (SNS_ENDCYL << 8); + data->tstart = 0; + uptr->POS &= ~0xff; + uptr->CMD &= ~0xff; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + goto index; + } + if ((uptr->CMD & 0x7) == 1 && (uptr->CMD & 0x60) != 0) + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + } + /* If INDEX set signal no record if read */ + if ((cmd & 0x03) == 0x01 && uptr->CMD & DK_INDEX2) { + sim_debug(DEBUG_DETAIL, dptr, "index unit=%d %02x %d %04x\n", + unit, state, data->tpos, uptr->SENSE); + /* Unless command is Read Header, return No record found */ + if (cmd != DK_RD_HA) + uptr->SENSE |= (SNS_NOREC << 8); + uptr->CMD &= ~0xff; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } +index: + uptr->CMD |= (uptr->CMD & DK_INDEX) ? DK_INDEX2 : DK_INDEX; + uptr->CMD &= ~DK_SRCOK; + data->tstart = data->tsize * (uptr->POS & 0xff); + data->tpos = data->rpos = 0; + data->state = DK_POS_HA; + data->rec = 0; + sim_activate(uptr, 100); + break; + + case DK_POS_HA: /* In home address (c) */ + data->tpos++; + if (data->count == 4) { + data->tpos = data->rpos = 5; + data->state = DK_POS_CNT; + sim_debug(DEBUG_EXP, dptr, "state HA unit=%d %d %d\n", unit, data->count, + data->tpos); + rec = &data->cbuf[data->rpos + data->tstart]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) + data->state = DK_POS_END; + sim_activate(uptr, 40); + } else + sim_activate(uptr, 10); + break; + case DK_POS_CNT: /* In count (c) */ + data->tpos++; + if (data->count == 0) { + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) { + state = DK_POS_END; + data->state = DK_POS_END; + } + data->klen = rec[5]; + data->dlen = (rec[6] << 8) | rec[7]; + sim_debug(DEBUG_EXP, dptr, "state count unit=%d r=%d k=%d d=%d %d\n", + unit, data->rec, data->klen, data->dlen, data->tpos); + } + if (data->count == 7) { + data->state = DK_POS_KEY; + if (data->klen == 0) + data->state = DK_POS_DATA; + sim_activate(uptr, 50); + } else { + sim_activate(uptr, 10); + } + break; + case DK_POS_KEY: /* In Key area */ + data->tpos++; + if (data->count == data->klen) { + sim_debug(DEBUG_EXP, dptr, "state key unit=%d %d %d\n", unit, data->rec, + data->count); + data->state = DK_POS_DATA; + data->count = 0; + count = 0; + state = DK_POS_DATA; + sim_activate(uptr, 50); + } else { + sim_activate(uptr, 10); + } + break; + case DK_POS_DATA: /* In Data area */ + data->tpos++; + if (data->count == data->dlen) { + sim_debug(DEBUG_EXP, dptr, "state data unit=%d %d %d\n", unit, data->rec, + data->count); + data->state = DK_POS_AM; + sim_activate(uptr, 50); + } else { + sim_activate(uptr, 10); + } + break; + case DK_POS_AM: /* Beginning of record */ + data->rpos += data->dlen + data->klen + 8; + data->tpos = data->rpos; + data->rec++; + sim_debug(DEBUG_EXP, dptr, "state am unit=%d %d %d\n", unit, data->rec, + data->count); + data->state = DK_POS_CNT; + data->count = 0; + rec = &data->cbuf[data->rpos + data->tstart]; + /* Check for end of track */ + if ((rec[0] & rec[1] & rec[2] & rec[3]) == 0xff) + data->state = DK_POS_END; + sim_activate(uptr, 60); + break; + case DK_POS_END: /* Past end of data */ + data->tpos+=10; + data->count = 0; + data->klen = 0; + data->dlen = 0; + sim_activate(uptr, 50); + break; + case DK_POS_SEEK: /* In seek */ + /* Compute delay based of difference. */ + /* Set next state = index */ + i = (uptr->POS >> 8) - data->cyl; + sim_debug(DEBUG_DETAIL, dptr, "seek unit=%d %d %d s=%x\n", unit, uptr->POS >> 8, i, + data->state); + if (i == 0) { + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + data->state = DK_POS_INDEX; + sim_activate(uptr, 20); + } else if (i > 0 ) { + if (i > 20) { + data->cyl += 20; + sim_activate(uptr, 1000); + } else { + data->cyl ++; + sim_activate(uptr, 200); + } + } else { + if (i < -20) { + data->cyl -= 20; + sim_activate(uptr, 1000); + } else { + data->cyl --; + sim_activate(uptr, 200); + } + } + sim_debug(DEBUG_DETAIL, dptr, "seek next unit=%d %d %d %x\n", unit, uptr->POS >> 8, + data->cyl, data->state); + break; + } + + if ((pmp_statusb & IS_CH) != 0 && cmd != 0) { + pmp_statusb &= ~IS_CH; + pmp_statusb |= OP1; + uptr->CMD &= ~(DK_INDEX|DK_NOEQ|DK_HIGH|DK_PARAM|DK_MSET|DK_DONE|DK_INDEX2); + data->filemsk = 0; + sim_debug(DEBUG_CMD, dptr, "initial select unit=%d\n", unit); + } + + switch (cmd) { + case 0: /* No command, stop tape */ + break; + + case 0x3: + sim_debug(DEBUG_CMD, dptr, "nop unit=%d\n", unit); + uptr->CMD &= ~0xff; + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + + case 0x4: /* Sense */ + ch = uptr->SENSE & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 1 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = (uptr->SENSE >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 2 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = 0; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 3 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + if (disk_type[type].sen_cnt > 6) { + ch = (unit & 07) | ((~unit & 07) << 3); + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = unit; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 5 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = (uptr->POS >> 8) & 0xff; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 6 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = (uptr->POS & 0x1f) | ((uptr->POS & 0x10000) ? 0x40 : 0); + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 7 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = 0; /* Compute message code */ + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 8 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + i = 8; + } else { + if (disk_type[type].dev_type == 0x11) + ch = 0xc8; + else + ch = 0x40; + if ((uptr->POS >> 8) & SNS_ENDCYL) + ch |= 4; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 4 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + ch = unit; + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d 5 %x\n", unit, ch); + if (chan_write_byte(&ch)) + goto sense_end; + i = 5; + } + ch = 0; + for (; i < disk_type[type].sen_cnt; i++) { + sim_debug(DEBUG_DETAIL, dptr, "sense unit=%d %d %x\n", unit, i, ch); + if (chan_write_byte(&ch)) + goto sense_end; + } +sense_end: + uptr->CMD &= ~(0xff|DK_INDEX|DK_INDEX2); + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + + case DK_SETSECT: + /* Not valid for drives before 3330 */ + sim_debug(DEBUG_DETAIL, dptr, "setsector unit=%d\n", unit); + if (disk_type[type].sen_cnt > 6) { + if (chan_read_byte(&ch)) { + sim_debug(DEBUG_DETAIL, dptr, "setsector rdr\n"); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* Treat as NOP */ + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_DEVEND|SNS_CHNEND); + sim_debug(DEBUG_DETAIL, dptr, "setsector %02x\n", ch); + break; + } + /* Otherwise report as invalid command */ + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + + case DK_SEEK: /* Seek */ + case DK_SEEKCYL: /* Seek Cylinder */ + case DK_SEEKHD: /* Seek Head */ + + /* If we are waiting on seek to finish, check if there yet. */ + if (uptr->CMD & DK_PARAM) { + if ((uptr->POS >> 8) == data->cyl) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff|DK_PARAM); + // uptr->CMD |= DK_ATTN; + // pmp_statusb |= REQ_CH; + sim_debug(DEBUG_DETAIL, dptr, "seek end unit=%d %d %d %x\n", unit, + uptr->POS >> 8, data->cyl, data->state); + chan_end(SNS_DEVEND|SNS_CHNEND); + } + break; + } + + /* Check if seek valid */ + i = data->filemsk & DK_MSK_SK; + if (i == DK_MSK_SKNONE) { /* No seeks allowed, error out */ + sim_debug(DEBUG_DETAIL, dptr, "seek unit=%d not allow\n", unit); + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_WRP << 8; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + if (i != DK_MSK_SKALLSKR) { /* Some restrictions */ + if ((cmd == DK_SEEKHD && i != DK_MSK_SKALLHD) || (cmd == DK_SEEK)) { + sim_debug(DEBUG_DETAIL, dptr, "seek unit=%d not allow\n", unit); + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_WRP << 8; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + } + + /* Read in 6 character seek code */ + for (i = 0; i < 6; i++) { + if (chan_read_byte(&buf[i])) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ|SNS_SEEKCK; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "seek unit=%d %02x %02x %02x %02x %02x %02x\n", unit, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + trk = (buf[2] << 8) | buf[3]; + sim_debug(DEBUG_DETAIL, dptr, "seek unit=%d %d %d\n", unit, trk, buf[5]); + + /* Check if seek valid */ + if ((buf[0] | buf[1] | buf[4]) != 0 || trk > disk_type[type].cyl + || buf[5] >= disk_type[type].heads) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ|SNS_SEEKCK; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + if (cmd == DK_SEEKHD && ((uptr->POS >> 8) & 0x7fff) != trk) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ|SNS_SEEKCK; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + uptr->POS = (trk << 8) | buf[5]; + + /* Check if on correct cylinder */ + if (trk != data->cyl) { + /* Do seek */ + uptr->CMD |= DK_PARAM; + data->state = DK_POS_SEEK; + sim_debug(DEBUG_DETAIL, dptr, "seek unit=%d doing\n", unit); +// chan_end(SNS_CHNEND); + } else { + pmp_adjpos(uptr); + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_DEVEND|SNS_CHNEND); + } + return SCPE_OK; + + case DK_RESTORE: /* Restore */ + + /* If we are waiting on seek to finish, check if there yet. */ + if (uptr->CMD & DK_PARAM) { + if ((uptr->POS >> 8) == data->cyl) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + uptr->CMD |= DK_ATTN; + pmp_statusb |= REQ_CH; + sim_debug(DEBUG_DETAIL, dptr, "seek end unit=%d %d %d %x\n", unit, + uptr->POS >> 8, data->cyl, data->state); + } + break; + } + + sim_debug(DEBUG_DETAIL, dptr, "restore unit=%d\n", unit); + /* Check if restore is valid */ + if ((data->filemsk & DK_MSK_SK) != DK_MSK_SKALLSKR) { + uptr->SENSE |= SNS_CMDREJ; + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff|DK_PARAM); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + uptr->POS = 0; + data->tstart = 0; + /* Check if on correct cylinder */ + if (0 != data->cyl) { + /* Do seek */ + uptr->CMD |= DK_PARAM; + data->state = DK_POS_SEEK; + chan_end(SNS_CHNEND); + } else { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_DEVEND|SNS_CHNEND); + } + return SCPE_OK; + + case DK_SETMSK: /* Set file mask */ + /* If mask already set, error */ + sim_debug(DEBUG_DETAIL, dptr, "setmsk unit=%d\n", unit); + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff|DK_PARAM); + if (uptr->CMD & DK_MSET) { + sim_debug(DEBUG_DETAIL, dptr, "setmsk dup\n"); + uptr->LASTCMD = 0; + uptr->SENSE |= SNS_CMDREJ | (SNS_INVSEQ << 8); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + /* Grab mask */ + if (chan_read_byte(&ch)) { + sim_debug(DEBUG_DETAIL, dptr, "setmsk rdr\n"); + uptr->LASTCMD = 0; + uptr->SENSE |= SNS_CMDREJ; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* Save */ + if (disk_type[type].dev_type >= 0x30) { + /* Clear bits which have no meaning in simulator */ + ch &= 0xFC; + } + if ((ch & ~(DK_MSK_SK|DK_MSK_WRT)) != 0) { + sim_debug(DEBUG_DETAIL, dptr, "setmsk inv\n"); + uptr->LASTCMD = 0; + uptr->SENSE |= SNS_CMDREJ; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + sim_debug(DEBUG_DETAIL, dptr, "setmsk unit=%d %x\n", unit, ch); + data->filemsk = ch; + uptr->CMD |= DK_MSET; + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + + case DK_SPACE: /* Space record */ + /* Not implemented yet */ + break; + + case DK_SRCH_HAEQ: /* Search HA equal */ + + /* Wait until home address is found */ + if (state == DK_POS_HA && count == 0) { + sim_debug(DEBUG_DETAIL, dptr, "search HA unit=%d %x %d %x\n", + unit, state, count, uptr->POS); + uptr->CMD &= ~DK_SRCOK; + uptr->CMD |= DK_PARAM; + break; + } + + /* In home address, do compare */ + if (uptr->CMD & DK_PARAM) { + if (chan_read_byte(&ch)) { + if (count < 4) + uptr->CMD |= DK_SHORTSRC; + } else if (ch != *da) { + uptr->CMD |= DK_NOEQ; + } + sim_debug(DEBUG_DETAIL, dptr, + "search HA unit=%d %d %x %02x=%02x %d\n", unit, + count, state, ch, *da, data->tpos); + /* At end of count */ + if (count == 4 || uptr->CMD & DK_SHORTSRC) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff|DK_PARAM); + if (uptr->CMD & DK_NOEQ) + chan_end(SNS_CHNEND|SNS_DEVEND); + else { + uptr->CMD |= DK_SRCOK; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_SMS); + } + } + } + break; + + case DK_RD_CNT: /* Read count */ + /* Wait for next address mark */ + if (state == DK_POS_AM) + uptr->CMD |= DK_PARAM; + + /* When we are at count segment and passed address mark */ + if (uptr->CMD & DK_PARAM && state == DK_POS_CNT && data->rec != 0) { + ch = *da; + sim_debug(DEBUG_DETAIL, dptr, "readcnt ID unit=%d %d %x %02x %x %d %x\n", + unit, count, state, ch, uptr->POS, data->tpos, uptr->POS); + if (chan_write_byte(&ch) || count == 7) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND); + } + } + break; + + case DK_SRCH_IDEQ: /* Search ID equal */ + case DK_SRCH_IDGT: /* Search ID greater */ + case DK_SRCH_IDGE: /* Search ID greater or equal */ + /* Wait for beginning of count segment */ + if (state == DK_POS_CNT && count == 0) { + sim_debug(DEBUG_DETAIL, dptr, "search ID unit=%d %x %d %x %d\n", + unit, state, count, uptr->POS, data->rec); + sim_debug(DEBUG_DETAIL, dptr, "ID unit=%d %02x %02x %02x %02x %02x %02x %02x %02x\n", + unit, da[0], da[1], da[2], da[3], da[4], da[5], da[6], da[7]); + uptr->CMD &= ~(DK_SRCOK|DK_SHORTSRC|DK_NOEQ|DK_HIGH); + uptr->CMD |= DK_PARAM; + } + + /* In count segment */ + if (uptr->CMD & DK_PARAM) { + /* Wait for start of record */ + if (chan_read_byte(&ch)) { + uptr->CMD |= DK_SHORTSRC; + } else if (ch != *da) { + if ((uptr->CMD & DK_NOEQ) == 0) { + uptr->CMD |= DK_NOEQ; + if (ch < *da) + uptr->CMD |= DK_HIGH; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "search ID unit=%d %d %x %02x=%02x %d %c %c\n", unit, count, + state, ch, *da, data->tpos, + ((uptr->CMD & DK_NOEQ) ? '!' : '='), + ((uptr->CMD & DK_HIGH) ? 'h' : 'l')); + if (count == 4 || uptr->CMD & DK_SHORTSRC) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + i = 0; + if ((cmd & 0x20) && (uptr->CMD & DK_NOEQ) == 0) + i = SNS_SMS; + if ((cmd & 0x40) && (uptr->CMD & DK_HIGH)) + i = SNS_SMS; + if (i) { + uptr->CMD |= DK_SRCOK; + } + chan_end(SNS_CHNEND|SNS_DEVEND|i); + } + } + break; + + case DK_SRCH_KYEQ: /* Search Key equal */ + case DK_SRCH_KYGT: /* Search Key greater */ + case DK_SRCH_KYGE: /* Search Key greater or equal */ + /* Check if at beginning of key */ + if (state == DK_POS_KEY && count == 0) { + /* Check proper sequence */ + sim_debug(DEBUG_DETAIL, dptr, "search Key cn unit=%d %x %d %x %d %x\n", + unit, state, count, uptr->POS, data->rec, uptr->LASTCMD); + if (uptr->LASTCMD == DK_RD_CNT || uptr->LASTCMD == 0x100 + || ((uptr->LASTCMD & 0x1F) == 0x11 && data->rec != 0) + || ((uptr->LASTCMD & 0x1F) == 0x11 && /* Search ID */ + (uptr->CMD & (DK_SRCOK|DK_SHORTSRC)) == DK_SRCOK)) { + uptr->CMD &= ~(DK_SRCOK|DK_SHORTSRC|DK_NOEQ|DK_HIGH); + uptr->CMD |= DK_PARAM; + } + } + /* Check if previous record had zero length key */ + if (state == DK_POS_DATA && count == 0 && data->klen == 0) { + if (uptr->LASTCMD == DK_RD_CNT || ((uptr->LASTCMD & 0x1F) == 0x11 && + (uptr->CMD & (DK_SRCOK|DK_SHORTSRC)) == DK_SRCOK )) { + sim_debug(DEBUG_DETAIL, dptr, "search Key da unit=%d %x %d %x %d\n", + unit, state, count, uptr->POS, data->rec); + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + } + } + /* If we hit address mark, see if over */ + if (state == DK_POS_AM) { + if (uptr->LASTCMD == DK_RD_CNT || ((uptr->LASTCMD & 0x1F) == 0x11 && + (uptr->CMD & (DK_SRCOK|DK_SHORTSRC)) == DK_SRCOK )) { + sim_debug(DEBUG_DETAIL, dptr, "search Key am unit=%d %x %d %x %d\n", + unit, state, count, uptr->POS, data->rec); + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + } else { + uptr->LASTCMD = 0x100; + } + } + if (uptr->CMD & DK_PARAM) { + /* Wait for key */ + if (chan_read_byte(&ch)) { + uptr->CMD |= DK_SHORTSRC; + } else if (ch != *da) { + if ((uptr->CMD & DK_NOEQ) == 0) { + uptr->CMD |= DK_NOEQ; + if (ch < *da) + uptr->CMD |= DK_HIGH; + } + } + sim_debug(DEBUG_DETAIL, dptr, + "search Key unit=%d %d %x %02x=%02x %d %c %c\n", unit, count, + state, ch, *da, data->tpos, + ((uptr->CMD & DK_NOEQ) ? '!' : '='), + ((uptr->CMD & DK_HIGH) ? 'h' : 'l')); + if (count == data->klen-1 || uptr->CMD & DK_SHORTSRC) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + i = 0; + if ((cmd & 0x20) && (uptr->CMD & DK_NOEQ) == 0) + i = SNS_SMS; + if ((cmd & 0x40) && (uptr->CMD & DK_HIGH)) + i = SNS_SMS; + if (i) { + uptr->CMD |= DK_SRCOK; + } + chan_end(SNS_CHNEND|SNS_DEVEND|i); + } + } + break; + + case DK_RD_HA: /* Read home address */ + /* Wait until next index pulse */ + if (state == DK_POS_INDEX) { + uptr->CMD |= DK_PARAM; + } + + /* Read while we are in the home address */ + if (uptr->CMD & DK_PARAM && state == DK_POS_HA) { + ch = *da; + if (chan_write_byte(&ch) || count == 4) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND); + } + } + break; + + case DK_RD_IPL: /* Read IPL record */ + + /* If we are not on cylinder zero, issue a seek */ + if (uptr->POS != 0) { + /* Do a seek */ + uptr->POS = 0; + data->tstart = 0; + data->state = DK_POS_SEEK; + sim_debug(DEBUG_DETAIL, dptr, "RD IPL unit=%d seek\n", unit); + break; + } + + /* Wait for seek to finish */ + if (data->cyl != 0) + break; + + /* Read in the first record on track zero */ + if (count == 0 && state == DK_POS_DATA && data->rec == 1) { + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + sim_debug(DEBUG_DETAIL, dptr, "RD IPL unit=%d %d k=%d d=%d %02x %04x\n", + unit, data->rec, data->klen, data->dlen, data->state, + 8 + data->klen + data->dlen); + } + goto rd; + + case DK_RD_R0: /* Read R0 */ + /* Wait for record zero count */ + if (count == 0 && state == DK_POS_CNT && data->rec == 0) { + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + sim_debug(DEBUG_DETAIL, dptr, "RD R0 unit=%d %d k=%d d=%d %02x %04x\n", + unit, data->rec, data->klen, data->dlen, data->state, + 8 + data->klen + data->dlen); + } + goto rd; + + case DK_RD_CKD: /* Read count, key and data */ + /* Wait for any count */ + if (count == 0 && state == DK_POS_CNT && data->rec != 0) { + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + sim_debug(DEBUG_DETAIL, dptr, "RD CKD unit=%d %d k=%d d=%d %02x %04x %04x\n", + unit, data->rec, data->klen, data->dlen, data->state, data->dlen, + 8 + data->klen + data->dlen); + } + goto rd; + + case DK_RD_KD: /* Read key and data */ + /* Wait for next key */ + if (count == 0 && ((data->klen != 0 && state == DK_POS_KEY) || + (data->klen == 0 && state == DK_POS_DATA))) { + if ((uptr->CMD & DK_INDEX) && data->rec == 0 && + (uptr->CMD & DK_SRCOK) == 0) + break; + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + sim_debug(DEBUG_DETAIL, dptr, "RD KD unit=%d %d k=%d d=%d %02x %04x %04x\n", + unit, data->rec, data->klen, data->dlen, data->state, data->dlen, + 8 + data->klen + data->dlen); + } + goto rd; + + case DK_RD_D: /* Read Data */ + /* Wait for next data */ + if (count == 0 && state == DK_POS_DATA) { + if ((uptr->CMD & DK_INDEX) && data->rec == 0 && + (uptr->CMD & DK_SRCOK) == 0) + break; + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + sim_debug(DEBUG_DETAIL, dptr, + "RD D unit=%d %d k=%d d=%d %02x %04x %04x %d\n", + unit, data->rec, data->klen, data->dlen, data->state, data->dlen, + 8 + data->klen + data->dlen, count); + } + +rd: + if (uptr->CMD & DK_PARAM) { + /* Check for end of file */ + if (state == DK_POS_DATA && data->dlen == 0) { + sim_debug(DEBUG_DETAIL, dptr, "RD EOF unit=%d %x %d %d d=%d\n", + unit, state, count, data->rec, data->dlen); + uptr->CMD &= ~(0xff|DK_PARAM); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } + if (state == DK_POS_INDEX) { + uptr->SENSE = SNS_TRKOVR << 8; + uptr->CMD &= ~(0xff|DK_PARAM); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (state == DK_POS_DATA && count == data->dlen) { + sim_debug(DEBUG_DETAIL, dptr, + "RD next unit=%d %02x %02x %02x %02x %02x %02x %02x %02x\n", + unit, da[0], da[1], da[2], da[3], da[4], da[5], da[6], da[7]); + uptr->CMD &= ~(0xff|DK_PARAM); + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + } + ch = *da; + sim_debug(DEBUG_DATA, dptr, "RD Char %02x %02x %d %d\n", + ch, state, count, data->tpos); + if (chan_write_byte(&ch)) { + sim_debug(DEBUG_DETAIL, dptr, + "RD next unit=%d %02x %02x %02x %02x %02x %02x %02x %02x\n", + unit, da[0], da[1], da[2], da[3], da[4], da[5], da[6], da[7]); + uptr->CMD &= ~(0xff|DK_PARAM); + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + } + } + break; + + case DK_RD_SECT: /* Read sector */ + /* Not valid for drives before 3330 */ + sim_debug(DEBUG_DETAIL, dptr, "readsector unit=%d\n", unit); + if (disk_type[type].sen_cnt > 6) { + ch = data->tpos / 110; + if (chan_write_byte(&ch)) { + sim_debug(DEBUG_DETAIL, dptr, "readsector rdr\n"); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + /* Nothing more to do */ + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff); + chan_end(SNS_DEVEND|SNS_CHNEND); + sim_debug(DEBUG_DETAIL, dptr, "readsector %02x\n", ch); + break; + } + /* Otherwise report as invalid command */ + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + uptr->SENSE |= SNS_CMDREJ; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + + case DK_WR_HA: /* Write home address */ + /* Wait for index */ + if (state == DK_POS_INDEX) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) != DK_MSK_ALLWRT) { + uptr->SENSE |= SNS_CMDREJ; + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + + uptr->CMD |= DK_PARAM; + break; + } + + if (uptr->CMD & DK_PARAM) { + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + sim_debug(DEBUG_DETAIL, dptr, "WR HA unit=%d %x %d %d\n", unit, + state, count, data->rec); + if (chan_read_byte(&ch)) { + ch = 0; + } + *da = ch; + uptr->CMD |= DK_CYL_DIRTY; + if (count == 4) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff|DK_PARAM); + chan_end(SNS_CHNEND|SNS_DEVEND); + for(i = 1; i < 9; i++) + da[i] = 0xff; + } + } + break; + + case DK_WR_R0: /* Write R0 */ + + /* Wait for first record or end of disk */ + if ((state == DK_POS_CNT || state == DK_POS_END) + && data->rec == 0 && count == 0) { + sim_debug(DEBUG_DETAIL, dptr, "WR R0 unit=%d %x %d\n", unit, + state, count); + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) != DK_MSK_ALLWRT) { + uptr->SENSE |= SNS_CMDREJ | (SNS_WRP << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (uptr->LASTCMD == DK_WR_HA || + (uptr->LASTCMD == DK_SRCH_HAEQ && + (uptr->CMD & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + data->tpos = data->rpos; + da = &data->cbuf[data->tpos + data->tstart]; + data->tpos++; + state = data->state = DK_POS_CNT; + uptr->CMD |= DK_PARAM; + } else { + uptr->SENSE |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + goto wrckd; + + case DK_WR_CKD: /* Write count, key and data */ + /* Wait for next non-zero record, or end of disk */ + if ((state == DK_POS_CNT || state == DK_POS_END) + && data->rec != 0 && count == 0) { + sim_debug(DEBUG_DETAIL, dptr, "WR CKD unit=%d %x %d\n", unit, + state, count); + /* Check if command ok based on mask */ + i = data->filemsk & DK_MSK_WRT; + if (i == DK_MSK_INHWRT || i == DK_MSK_ALLWRU) { + sim_debug(DEBUG_DETAIL, dptr, "WR CKD unit=%d mask\n", unit); + uptr->SENSE |= SNS_CMDREJ | (SNS_WRP << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (uptr->LASTCMD == DK_WR_R0 || uptr->LASTCMD == DK_WR_CKD || + ((uptr->LASTCMD & 0x7) == 1 && (uptr->LASTCMD & 0x60) != 0 && + (uptr->CMD & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + sim_debug(DEBUG_DETAIL, dptr, "WR CKD unit=%d ok\n", unit); + data->tpos = data->rpos; + da = &data->cbuf[data->tpos + data->tstart]; + data->tpos++; + state = data->state = DK_POS_CNT; + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~DK_DONE; + } else { + sim_debug(DEBUG_DETAIL, dptr, "WR CKD unit=%d seq\n", unit); + uptr->SENSE |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + goto wrckd; + + case DK_WR_KD: /* Write key and data */ + /* Wait for beginning of next key */ + if (count == 0 && ((data->klen != 0 && state == DK_POS_KEY) || + (data->klen == 0 && state == DK_POS_DATA))) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) == DK_MSK_INHWRT) { + uptr->SENSE |= SNS_CMDREJ | (SNS_WRP << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((uptr->LASTCMD & 0x13) == 0x11 && + (uptr->CMD & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~DK_DONE; + sim_debug(DEBUG_DETAIL, dptr, "WR KD unit=%d %d k=%d d=%d %02x %04x %d\n", + unit, data->rec, data->klen, data->dlen, data->state, + 8 + data->klen + data->dlen, count); + } else { + uptr->SENSE |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + goto wrckd; + + case DK_WR_D: /* Write Data */ + /* Wait for beginning of next data */ + if ((state == DK_POS_DATA) && count == 0) { + /* Check if command ok based on mask */ + if ((data->filemsk & DK_MSK_WRT) == DK_MSK_INHWRT) { + uptr->SENSE |= SNS_CMDREJ | (SNS_WRP << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (((uptr->LASTCMD & 0x3) == 1 && (uptr->LASTCMD & 0xE0) != 0 && + (uptr->CMD & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + uptr->CMD |= DK_PARAM; + uptr->CMD &= ~DK_DONE; + sim_debug(DEBUG_DETAIL, dptr, "WR D unit=%d %d k=%d d=%d %02x %04x %d\n", + unit, data->rec, data->klen, data->dlen, data->state, + 8 + data->klen + data->dlen, count); + } else { + uptr->SENSE |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + } + } + +wrckd: + if (uptr->CMD & DK_PARAM) { + uptr->CMD &= ~(DK_INDEX|DK_INDEX2); + if (state == DK_POS_INDEX) { + uptr->SENSE = SNS_TRKOVR << 8; + uptr->CMD &= ~(0xff|DK_PARAM|DK_DONE); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } else if ((cmd == DK_WR_KD || cmd == DK_WR_D) && state == DK_POS_DATA + && data->dlen == 0) { + sim_debug(DEBUG_DETAIL, dptr, "WR EOF unit=%d %x %d %d d=%d\n", + unit, state, count, data->rec, data->dlen); + uptr->CMD &= ~(0xff|DK_PARAM|DK_DONE); + uptr->LASTCMD = cmd; + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITEXP); + break; + } else if (state == DK_POS_DATA && data->count == data->dlen) { + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff|DK_PARAM|DK_DONE); + if ((cmd & 0x10) != 0) { + for(i = 0; i < 8; i++) + da[i] = 0xff; + } + sim_debug(DEBUG_DETAIL, dptr, "WCKD end unit=%d %d %d %04x\n", + unit, data->tpos+8, count, data->tpos - data->rpos); + chan_end(SNS_CHNEND|SNS_DEVEND); + break; + } + if (uptr->CMD & DK_DONE || chan_read_byte(&ch)) { + ch = 0; + uptr->CMD |= DK_DONE; + } + sim_debug(DEBUG_DATA, dptr, "Char %02x, %02x %d %d\n", ch, state, + count, data->tpos); + *da = ch; + uptr->CMD |= DK_CYL_DIRTY; + if (state == DK_POS_CNT && count == 7) { + data->klen = rec[5]; + data->dlen = (rec[6] << 8) | rec[7]; + sim_debug(DEBUG_DETAIL, dptr, + "WCKD count unit=%d %d k=%d d=%d %02x %04x\n", + unit, data->rec, data->klen, data->dlen, data->state, + 8 + data->klen + data->dlen); + if (data->klen == 0) + data->state = DK_POS_DATA; + else + data->state = DK_POS_KEY; + data->count = 0; + } + } + break; + + case DK_ERASE: /* Erase to end of track */ + if ((state == DK_POS_AM || state == DK_POS_END) && data->count == 0) { + sim_debug(DEBUG_DETAIL, dptr, "Erase unit=%d %d %d\n", + unit, data->rec, data->rpos); + /* Check if command ok based on mask */ + i = data->filemsk & DK_MSK_WRT; + if (i == DK_MSK_INHWRT || i == DK_MSK_ALLWRU) { + uptr->SENSE |= SNS_CMDREJ; + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (uptr->LASTCMD == DK_WR_R0 || uptr->LASTCMD == DK_WR_CKD || + ((uptr->LASTCMD & 0x3) == 1 && (uptr->LASTCMD & 0x70) != 0 && + (uptr->CMD & (DK_SHORTSRC|DK_SRCOK)) == DK_SRCOK)) { + state = data->state = DK_POS_END; + /* Write end mark */ + for(i = 0; i < 8; i++) + rec[i] = 0xff; + + uptr->LASTCMD = cmd; + uptr->CMD &= ~(0xff|DK_PARAM|DK_INDEX|DK_INDEX2); + uptr->CMD |= DK_CYL_DIRTY; + chan_end(SNS_CHNEND|SNS_DEVEND); + } else { + uptr->SENSE |= SNS_CMDREJ | (SNS_INVSEQ << 8); + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + } + + break; + + case DK_WR_SCKD: /* Write special count, key and data */ + default: + sim_debug(DEBUG_DETAIL, dptr, "invalid command=%d %x\n", unit, cmd); + uptr->SENSE |= SNS_CMDREJ; + uptr->LASTCMD = 0; + uptr->CMD &= ~(0xff); + chan_end(SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK); + break; + } + if (state == data->state) + data->count++; + else + data->count = 0; + return SCPE_OK; +} + +t_stat +pmp_reset(DEVICE * dptr) +{ + UNIT *uptr = dptr->units; + int i; + + for (i = 0; i < NUM_UNITS_PMP; i++) { + int t = GET_TYPE(uptr->flags); + uptr->capac = disk_type[t].bpt * disk_type[t].heads * disk_type[t].cyl; + uptr++; + } + pmp_statusb = IDLE_CH; + return SCPE_OK; +} + +/* + * Format the pack for WAITS. 22 128 word sectors per track. Or 576 bytes per sector. + */ +int +pmp_format(UNIT * uptr, int flag) { + struct pmp_header hdr; + struct pmp_t *data; + uint16 addr = GET_UADDR(uptr->flags); + int type = GET_TYPE(uptr->flags); + int tsize; + int cyl; + int sector; + int rec; + int hd; + uint32 pos; + + if (flag || get_yn("Initialize dasd? [Y] ", TRUE)) { + memset(&hdr, 0, sizeof(struct pmp_header)); + memcpy(&hdr.devid[0], "CKD_P370", 8); + hdr.heads = disk_type[type].heads; + hdr.tracksize = (disk_type[type].bpt | 0x1ff) + 1; + hdr.devtype = disk_type[type].dev_type; + hdr.highcyl = disk_type[type].cyl; + (void)sim_fseek(uptr->fileref, 0, SEEK_SET); + sim_fwrite(&hdr, 1, sizeof(struct pmp_header), uptr->fileref); + if ((data = (struct pmp_t *)calloc(1, sizeof(struct pmp_t))) == 0) + return 1; + uptr->DATAPTR = (void *)data; + tsize = hdr.tracksize * hdr.heads; + data->tsize = hdr.tracksize; + if ((data->cbuf = (uint8 *)calloc(tsize, sizeof(uint8))) == 0) + return 1; + for (cyl = 0; cyl <= disk_type[type].cyl; cyl++) { + pos = 0; + for (hd = 0; hd < disk_type[type].heads; hd++) { + uint32 cpos = pos; + rec = 0; + data->cbuf[pos++] = 0; /* HA */ + data->cbuf[pos++] = (cyl >> 8); + data->cbuf[pos++] = (cyl & 0xff); + data->cbuf[pos++] = (hd >> 8); + data->cbuf[pos++] = (hd & 0xff); + data->cbuf[pos++] = (cyl >> 8); /* R0 Rib block */ + data->cbuf[pos++] = (cyl & 0xff); + data->cbuf[pos++] = (hd >> 8); + data->cbuf[pos++] = (hd & 0xff); + data->cbuf[pos++] = rec++; /* Rec */ + data->cbuf[pos++] = 0; /* keylen */ + data->cbuf[pos++] = 0; /* dlen */ + data->cbuf[pos++] = 144; /* */ + pos += 144; + for (sector = 0; sector < 17; sector++) { + data->cbuf[pos++] = (cyl >> 8); /* R1 */ + data->cbuf[pos++] = (cyl & 0xff); + data->cbuf[pos++] = (hd >> 8); + data->cbuf[pos++] = (hd & 0xff); + data->cbuf[pos++] = rec++; /* Rec */ + data->cbuf[pos++] = 0; /* keylen */ + data->cbuf[pos++] = 2; /* dlen = 576 */ + data->cbuf[pos++] = 0100; /* */ + pos += 576; + } + data->cbuf[pos++] = 0xff; /* End record */ + data->cbuf[pos++] = 0xff; + data->cbuf[pos++] = 0xff; + data->cbuf[pos++] = 0xff; + if ((pos - cpos) > data->tsize) { + fprintf(stderr, "Overfull %d %d\n", pos-cpos, data->tsize); + } + pos = cpos + data->tsize; + } + sim_fwrite(data->cbuf, 1, tsize, uptr->fileref); + if ((cyl % 10) == 0) + fputc('.', stderr); + } + (void)sim_fseek(uptr->fileref, sizeof(struct pmp_header), SEEK_SET); + (void)sim_fread(data->cbuf, 1, tsize, uptr->fileref); + data->cpos = sizeof(struct pmp_header); + data->ccyl = 0; + data->ccyl = 0; + uptr->CMD |= DK_ATTN; + pmp_statusb |= REQ_CH; + sim_activate(uptr, 100); + fputc('\n', stderr); + fputc('\r', stderr); + return 0; + } else + return 1; +} + +t_stat +pmp_attach(UNIT * uptr, CONST char *file) +{ + uint16 addr = GET_UADDR(uptr->flags); + int flag = (sim_switches & SWMASK ('I')) != 0; + t_stat r; + int i; + struct pmp_header hdr; + struct pmp_t *data; + int tsize; + + if ((r = attach_unit(uptr, file)) != SCPE_OK) + return r; + + if (sim_fread(&hdr, 1, sizeof(struct pmp_header), uptr->fileref) != + sizeof(struct pmp_header) || strncmp((CONST char *)&hdr.devid[0], "CKD_P370", 8) != 0 || flag) { + if (pmp_format(uptr, flag)) { + detach_unit(uptr); + return SCPE_FMT; + } + return SCPE_OK; + } + + sim_messagef(SCPE_OK, "Drive %03x=%d %d %02x %d\n\r", addr, + hdr.heads, hdr.tracksize, hdr.devtype, hdr.highcyl); + for (i = 0; disk_type[i].name != 0; i++) { + tsize = (disk_type[i].bpt | 0x1ff) + 1; + if (hdr.devtype == disk_type[i].dev_type && hdr.tracksize == tsize && + hdr.heads == disk_type[i].heads && hdr.highcyl == disk_type[i].cyl) { + if (GET_TYPE(uptr->flags) != i) { + /* Ask if we should change */ + fprintf(stderr, "Wrong type %s\n\r", disk_type[i].name); + if (!get_yn("Update dasd type? [N] ", FALSE)) { + detach_unit(uptr); + return SCPE_FMT; + } + uptr->flags &= ~UNIT_TYPE; + uptr->flags |= SET_TYPE(i); + uptr->capac = disk_type[i].bpt * disk_type[i].heads * disk_type[i].cyl; + } + break; + } + } + if (disk_type[i].name == 0) { + detach_unit(uptr); + return SCPE_FMT; + } + if ((data = (struct pmp_t *)calloc(1, sizeof(struct pmp_t))) == 0) + return 0; + uptr->DATAPTR = (void *)data; + tsize = hdr.tracksize * hdr.heads; + data->tsize = hdr.tracksize; + if ((data->cbuf = (uint8 *)calloc(tsize, sizeof(uint8))) == 0) { + detach_unit(uptr); + return SCPE_ARG; + } + (void)sim_fseek(uptr->fileref, sizeof(struct pmp_header), SEEK_SET); + (void)sim_fread(data->cbuf, 1, tsize, uptr->fileref); + data->cpos = sizeof(struct pmp_header); + data->ccyl = 0; + uptr->CMD |= DK_ATTN; + pmp_statusb |= REQ_CH; + sim_activate(uptr, 100); + return SCPE_OK; +} + +t_stat +pmp_detach(UNIT * uptr) +{ + struct pmp_t *data = (struct pmp_t *)uptr->DATAPTR; + int type = GET_TYPE(uptr->flags); + uint16 addr = GET_UADDR(uptr->flags); + int cmd = uptr->CMD & 0x7f; + + if (uptr->CMD & DK_CYL_DIRTY) { + (void)sim_fseek(uptr->fileref, data->cpos, SEEK_SET); + (void)sim_fwrite(data->cbuf, 1, + data->tsize * disk_type[type].heads, uptr->fileref); + uptr->CMD &= ~DK_CYL_DIRTY; + } + if (cmd != 0) + chan_end(SNS_CHNEND|SNS_DEVEND); + sim_cancel(uptr); + free(data->cbuf); + free(data); + uptr->DATAPTR = 0; + uptr->CMD &= ~0xffff; + return detach_unit(uptr); +} + + +/* Disk option setting commands */ + +t_stat +pmp_set_type(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + int i; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + for (i = 0; disk_type[i].name != 0; i++) { + if (strcmp(disk_type[i].name, cptr) == 0) { + uptr->flags &= ~UNIT_TYPE; + uptr->flags |= SET_TYPE(i); + uptr->capac = disk_type[i].bpt * disk_type[i].heads * disk_type[i].cyl; + return SCPE_OK; + } + } + return SCPE_ARG; +} + +t_stat +pmp_get_type(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fputs("TYPE=", st); + fputs(disk_type[GET_TYPE(uptr->flags)].name, st); + return SCPE_OK; +} + +/* Sets the device onto a given channel */ +t_stat +pmp_set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc) +{ + t_value newdev; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + + newdev = get_uint (cptr, 16, 0xff, &r); + + if (r != SCPE_OK) + return r; + + + /* Update device entry */ + uptr->flags &= ~UNIT_ADDR(0xff); + uptr->flags |= UNIT_ADDR(newdev); + fprintf(stderr, "Set dev %x\n\r", GET_UADDR(uptr->flags)); + return r; +} + +t_stat +pmp_get_dev_addr(FILE * st, UNIT * uptr, int32 v, CONST void *desc) +{ + int addr; + + if (uptr == NULL) + return SCPE_IERR; + addr = GET_UADDR(uptr->flags); + fprintf(st, "%02x", addr); + return SCPE_OK; +} + +t_stat +pmp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr) +{ + int i; + fprintf (st, "PMP Disk File Controller\n\n"); + fprintf (st, "Use:\n\n"); + fprintf (st, " sim> SET %sn TYPE=type\n", dptr->name); + fprintf (st, "Type can be: "); + for (i = 0; disk_type[i].name != 0; i++) { + fprintf(st, "%s", disk_type[i].name); + if (disk_type[i+1].name != 0) + fprintf(st, ", "); + } + fprintf (st, ".\nEach drive has the following storage capacity:\n\n"); + for (i = 0; disk_type[i].name != 0; i++) { + int32 size = disk_type[i].bpt * disk_type[i].heads * disk_type[i].cyl; + char sm = 'K'; + size /= 1024; + size = (10 * size) / 1024; + fprintf(st, " %-8s %4d.%1dMB\n", disk_type[i].name, size/10, size%10); + } + fprintf (st, "Attach command switches\n"); + fprintf (st, " -I Initialize the drive. No prompting.\n"); + fprint_set_help (st, dptr); + fprint_show_help (st, dptr); + return SCPE_OK; +} + +const char *pmp_description (DEVICE *dptr) +{ + return "PMP disk file controller"; +} + +#endif + diff --git a/PDP10/ka10_stk.c b/PDP10/ka10_stk.c new file mode 100644 index 00000000..cb423397 --- /dev/null +++ b/PDP10/ka10_stk.c @@ -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 +#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 diff --git a/PDP10/ka10_ten11.c b/PDP10/ka10_ten11.c new file mode 100644 index 00000000..81863c98 --- /dev/null +++ b/PDP10/ka10_ten11.c @@ -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 +//#include +#include +//#include +//#include +//#include +//#include + +/* 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 diff --git a/PDP10/ka10_tk10.c b/PDP10/ka10_tk10.c new file mode 100644 index 00000000..ad6599d2 --- /dev/null +++ b/PDP10/ka10_tk10.c @@ -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 +#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 diff --git a/PDP10/kx10_cp.c b/PDP10/kx10_cp.c new file mode 100644 index 00000000..f8750f18 --- /dev/null +++ b/PDP10/kx10_cp.c @@ -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 + diff --git a/PDP10/kx10_cpu.c b/PDP10/kx10_cpu.c new file mode 100644 index 00000000..60b10fcc --- /dev/null +++ b/PDP10/kx10_cpu.c @@ -0,0 +1,6353 @@ +/* ka10_cpu.c: PDP-10 CPU 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 + + cpu KA10/KL10 central processor + + + The 36b system family had six different implementions: PDP-6, KA10, KI10, + KL10, KL10 extended, and KS10. + + The register state for the KA10 is: + + AC[16] accumulators + PC program counter + flags<0:11> state flags + pi_enb<1:7> enabled PI levels + pi_act<1:7> active PI levels + pi_prq<1:7> program PI requests + apr_enb<0:7> enabled system flags + apr_flg<0:7> system flags + + The PDP-10 had just two instruction formats: memory reference + and I/O. + + 000000000 0111 1 1111 112222222222333333 + 012345678 9012 3 4567 890123456789012345 + +---------+----+-+----+------------------+ + | opcode | ac |i| idx| address | memory reference + +---------+----+-+----+------------------+ + + 000 0000000 111 1 1111 112222222222333333 + 012 3456789 012 3 4567 890123456789012345 + +---+-------+---+-+----+------------------+ + |111|device |iop|i| idx| address | I/O + +---+-------+---+-+----+------------------+ + + This routine is the instruction decode routine for the PDP-10. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until an abort occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + MUUO instruction in executive mode + pager error in interrupt sequence + invalid vector table in interrupt sequence + illegal instruction in interrupt sequence + breakpoint encountered + nested indirects exceeding limit + nested XCT's exceeding limit + I/O error in I/O simulator + + 2. Interrupts. PDP-10's have a seven level priority interrupt + system. Interrupt requests can come from internal sources, + such as APR program requests, or external sources, such as + I/O devices. The requests are stored in pi_prq for program + requests, pi_apr for other internal flags, and pi_ioq for + I/O device flags. Internal and device (but not program) + interrupts must be enabled on a level by level basis. When + an interrupt is granted on a level, interrupts at that level + and below are masked until the interrupt is dismissed. + + + 3. Arithmetic. The PDP-10 is a 2's complement system. + + 4. Adding I/O devices. These modules must be modified: + + ka10_defs.h add device address and interrupt definitions + ka10_sys.c add sim_devices table entry + +*/ + +#include "kx10_defs.h" +#include "sim_timer.h" +#include + +#define HIST_PC 0x40000000 +#define HIST_PC2 0x80000000 +#define HIST_MIN 64 +#define HIST_MAX 500000 +#define TMR_RTC 0 +#define TMR_QUA 1 + + +uint64 M[MAXMEMSIZE]; /* Memory */ +#if KI +uint64 FM[64]; /* Fast memory register */ +#else +uint64 FM[16]; /* Fast memory register */ +#endif +uint64 AR; /* Primary work register */ +uint64 MQ; /* Extension to AR */ +uint64 BR; /* Secondary operand */ +uint64 AD; /* Address Data */ +uint64 MB; /* Memory Bufer Register */ +uint32 AB; /* Memory address buffer */ +uint32 PC; /* Program counter */ +uint32 IR; /* Instruction register */ +uint64 MI; /* Monitor lights */ +uint32 FLAGS; /* Flags */ +uint32 AC; /* Operand accumulator */ +uint64 SW; /* Switch register */ +int BYF5; /* Flag for second half of LDB/DPB instruction */ +int uuo_cycle; /* Uuo cycle in progress */ +int sac_inh; /* Don't store AR in AC */ +int SC; /* Shift count */ +int SCAD; /* Shift count extension */ +int FE; /* Exponent */ +#if KA | PDP6 +int Pl, Ph, Rl, Rh, Pflag; /* Protection registers */ +int push_ovf; /* Push stack overflow */ +int mem_prot; /* Memory protection flag */ +#endif +int nxm_flag; /* Non-existant memory flag */ +int clk_flg; /* Clock flag */ +int ov_irq; /* Trap overflow */ +int fov_irq; /* Trap floating overflow */ +#if PDP6 +int pcchg_irq; /* PC Change flag */ +int ill_op; /* Illegal opcode */ +int user_io; /* User IO flag */ +int ex_uuo_sync; /* Execute a UUO op */ +#endif +uint8 PIR; /* Current priority level */ +uint8 PIH; /* Highest priority */ +uint8 PIE; /* Priority enable mask */ +int pi_enable; /* Interrupts enabled */ +int parity_irq; /* Parity interupt */ +int pi_pending; /* Interrupt pending. */ +int pi_enc; /* Flag for pi */ +int apr_irq; /* Apr Irq level */ +int clk_en; /* Enable clock interrupts */ +int clk_irq; /* Clock interrupt */ +int pi_restore; /* Restore previous level */ +int pi_hold; /* Hold onto interrupt */ +int modify; /* Modify cycle */ +int xct_flag; /* XCT flags */ +#if KI +uint64 ARX; /* Extension to AR */ +uint64 BRX; /* Extension to BR */ +uint64 ADX; /* Extension to AD */ +uint32 ub_ptr; /* User base pointer */ +uint32 eb_ptr; /* Executive base pointer */ +uint8 fm_sel; /* User fast memory block */ +int32 apr_serial = -1; /* CPU Serial number */ +int inout_fail; /* In out fail flag */ +int small_user; /* Small user flag */ +int user_addr_cmp; /* User address compare flag */ +#endif +#if KI | ITS | BBN +uint32 e_tlb[512]; /* Executive TLB */ +uint32 u_tlb[546]; /* User TLB */ +int page_enable; /* Enable paging */ +int page_fault; /* Page fail */ +uint32 ac_stack; /* Register stack pointer */ +uint32 pag_reload; /* Page reload pointer */ +uint64 fault_data; /* Fault data from last fault */ +int trap_flag; /* In trap cycle */ +int last_page; /* Last page mapped */ +#endif +#if BBN +int exec_map; /* Enable executive mapping */ +int next_write; /* Clear next write mapping */ +int mon_base_reg; /* Monitor base register */ +int user_base_reg; /* User base register */ +int user_limit; /* User limit register */ +uint64 pur; /* Process use register */ +#endif +#if MPX_DEV +int mpx_enable; /* Enable MPX device */ +#endif +#if ITS +uint32 dbr1; /* User Low Page Table Address */ +uint32 dbr2; /* User High Page Table Address */ +uint32 dbr3; /* Exec High Page Table Address */ +uint32 jpc; /* Jump program counter */ +uint8 age; /* Age word */ +uint32 fault_addr; /* Fault address */ +uint64 opc; /* Saved PC and Flags */ +uint64 mar; /* Memory address compare */ +uint32 qua_time; /* Quantum clock value */ +#endif +int watch_stop; /* Stop at memory watch point */ +int maoff = 0; /* Offset for traps */ + +uint16 dev_irq[128]; /* Pending irq by device */ +t_stat (*dev_tab[128])(uint32 dev, uint64 *data); +int (*dev_irqv[128])(uint32 dev, int addr); +t_stat rtc_srv(UNIT * uptr); +int32 rtc_tps = 60; +#if ITS +t_stat qua_srv(UNIT * uptr); +int32 qua_tps = 125000; +#endif +int32 tmxr_poll = 10000; + +/* Physical address range for Rubin 10-11 interface. */ +#define T11RANGE(addr) ((addr) >= 03040000) +/* Physical address range for auxiliary PDP-6. */ +#define AUXCPURANGE(addr) ((addr) >= 03000000 && (addr) < 03040000) + +DEVICE *rh_devs[] = { +#if (NUM_DEVS_RS > 0) + &rsa_dev, +#endif +#if (NUM_DEVS_RP > 0) + &rpa_dev, +#if (NUM_DEVS_RP > 1) + &rpb_dev, +#if (NUM_DEVS_RP > 2) + &rpc_dev, +#if (NUM_DEVS_RP > 3) + &rpd_dev, +#endif +#endif +#endif +#endif +#if (NUM_DEVS_TU > 0) + &tua_dev, +#endif + NULL, +}; + +struct rh_dev rh[] = { + { 0270, NULL, }, + { 0274, NULL, }, + { 0360, NULL, }, + { 0364, NULL, }, + { 0370, NULL, }, + { 0374, NULL, }, + { 0, NULL, }, +}; + +typedef struct { + uint32 pc; + uint32 ea; + uint64 ir; + uint64 ac; + uint32 flags; + uint64 mb; + uint64 fmb; + } InstHistory; + +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ + +/* Forward and external declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +#if KI +t_stat cpu_set_serial (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_show_serial (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +#endif +t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *cpu_description (DEVICE *dptr); +void set_ac_display (uint64 *acbase); + +t_bool build_dev_tab (void); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit[] = { { UDATA (&rtc_srv, UNIT_IDLE|UNIT_FIX|UNIT_BINK|UNIT_TWOSEG, 256 * 1024) }, +#if ITS + { UDATA (&qua_srv, UNIT_IDLE|UNIT_DIS, 0) } +#endif + }; + +REG cpu_reg[] = { + { ORDATAD (PC, PC, 18, "Program Counter") }, + { ORDATAD (FLAGS, FLAGS, 18, "Flags") }, + { ORDATAD (FM0, FM[00], 36, "Fast Memory") }, /* addr in memory */ + { ORDATA (FM1, FM[01], 36) }, /* modified at exit */ + { ORDATA (FM2, FM[02], 36) }, /* to SCP */ + { ORDATA (FM3, FM[03], 36) }, + { ORDATA (FM4, FM[04], 36) }, + { ORDATA (FM5, FM[05], 36) }, + { ORDATA (FM6, FM[06], 36) }, + { ORDATA (FM7, FM[07], 36) }, + { ORDATA (FM10, FM[010], 36) }, + { ORDATA (FM11, FM[011], 36) }, + { ORDATA (FM12, FM[012], 36) }, + { ORDATA (FM13, FM[013], 36) }, + { ORDATA (FM14, FM[014], 36) }, + { ORDATA (FM15, FM[015], 36) }, + { ORDATA (FM16, FM[016], 36) }, + { ORDATA (FM17, FM[017], 36) }, +#if KI + { BRDATA (FM, &FM[0], 8, 36, 64)}, +#else + { BRDATA (FM, &FM[0], 8, 36, 16)}, +#endif + { ORDATAD (PIR, PIR, 8, "Priority Interrupt Request") }, + { ORDATAD (PIH, PIH, 8, "Priority Interrupt Hold") }, + { ORDATAD (PIE, PIE, 8, "Priority Interrupt Enable") }, + { ORDATAD (PIENB, pi_enable, 7, "Enable Priority System") }, + { ORDATAD (SW, SW, 36, "Console SW Register"), REG_FIT}, + { ORDATAD (MI, MI, 36, "Monitor Display"), REG_FIT}, + { FLDATAD (BYF5, BYF5, 0, "Byte Flag") }, + { FLDATAD (UUO, uuo_cycle, 0, "UUO Cycle") }, +#if KA | PDP6 + { ORDATAD (PL, Pl, 18, "Program Limit Low") }, + { ORDATAD (PH, Ph, 18, "Program Limit High") }, + { ORDATAD (RL, Rl, 18, "Program Relation Low") }, + { ORDATAD (RH, Rh, 18, "Program Relation High") }, + { FLDATAD (PFLAG, Pflag, 0, "Relocation enable") }, + { FLDATAD (PUSHOVER, push_ovf, 0, "Push overflow flag") }, + { FLDATAD (MEMPROT, mem_prot, 0, "Memory protection flag") }, +#endif + { FLDATAD (NXM, nxm_flag, 0, "Non-existing memory access") }, + { FLDATAD (CLK, clk_flg, 0, "Clock interrupt") }, + { FLDATAD (OV, ov_irq, 0, "Overflow enable") }, +#if PDP6 + { FLDATAD (PCCHG, pcchg_irq, 0, "PC Change interrupt") }, + { FLDATAD (USERIO, user_io, 0, "User I/O") }, + { FLDATAD (UUOSYNC, ex_uuo_sync, 0, "UUO Op") }, +#else + { FLDATAD (FOV, fov_irq, 0, "Floating overflow enable") }, +#endif + { FLDATA (PI_PEND, pi_pending, 0), REG_HRO}, + { FLDATA (PARITY, parity_irq, 0) }, + { ORDATAD (APRIRQ, apr_irq, 0, "APR Interrupt number") }, + { ORDATAD (CLKIRQ, clk_irq, 0, "CLK Interrupt number") }, + { FLDATA (CLKEN, clk_en, 0), REG_HRO}, + { FLDATA (XCT, xct_flag, 0), REG_HRO}, +#if MPX_DEV + { FLDATA (MPX, mpx_enable, 0), REG_HRO}, +#endif + { FLDATA (PIHOLD, pi_hold, 0), REG_HRO}, + { FLDATA (PIREST, pi_restore, 0), REG_HRO}, +#if KI + { ORDATAD (UB, ub_ptr, 18, "User Base Pointer") }, + { ORDATAD (EB, eb_ptr, 18, "Executive Base Pointer") }, + { ORDATAD (FMSEL, fm_sel, 8, "Register set select") }, + { ORDATAD (SERIAL, apr_serial, 10, "System Serial Number") }, + { FLDATA (INOUT, inout_fail, 0), REG_RO}, + { FLDATA (SMALL, small_user, 0), REG_RO}, + { FLDATA (ADRCMP, user_addr_cmp, 0), REG_HRO}, +#endif +#if KI | ITS | BBN + { FLDATAD (PAGE_ENABLE, page_enable, 0, "Paging enabled")}, + { FLDATAD (PAGE_FAULT, page_fault, 0, "Page fault"), REG_RO}, + { ORDATAD (AC_STACK, ac_stack, 18, "AC Stack"), REG_RO}, + { ORDATAD (PAGE_RELOAD, pag_reload, 18, "Page reload"), REG_HRO}, + { ORDATAD (FAULT_DATA, fault_data, 36, "Page fault data"), REG_RO}, + { FLDATAD (TRP_FLG, trap_flag, 0, "Trap flag"), REG_HRO}, + { ORDATAD (LST_PAGE, last_page, 9, "Last page"), REG_HRO}, +#endif +#if BBN + { FLDATAD (EXEC_MAP, exec_map, 0, "Executive mapping"), REG_RO}, + { FLDATAD (NXT_WR, next_write, 0, "Map next write"), REG_RO}, + { ORDATAD (MON_BASE, mon_base_reg, 8, "Monitor base"), REG_RO}, + { ORDATAD (USER_BASE, user_base_reg, 8, "User base"), REG_RO}, + { ORDATAD (USER_LIMIT, user_limit, 3, "User limit"), REG_RO}, + { ORDATAD (PER_USER, pur, 36, "Per user data"), REG_RO}, +#endif +#if ITS + { ORDATAD (DBR1, dbr1, 18, "DB register 1")}, + { ORDATAD (DBR2, dbr2, 18, "DB register 2")}, + { ORDATAD (DBR3, dbr3, 18, "DB register 3")}, + { ORDATAD (JPC, jpc, 18, "Last Jump PC")}, + { ORDATAD (AGE, age, 4, "Age")}, + { ORDATAD (FAULT_ADDR, fault_addr, 18, "Fault address"), REG_RO}, + { ORDATAD (OPC, opc, 36, "Saved PC and flags")}, + { ORDATAD (MAR, mar, 18, "Memory address register")}, + { ORDATAD (QUA_TIME, qua_time, 36, "Quantum timer"), REG_RO}, +#endif + { NULL } + }; + +MTAB cpu_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, 1, "16K", "16K", &cpu_set_size }, + { UNIT_MSIZE, 2, "32K", "32K", &cpu_set_size }, + { UNIT_MSIZE, 3, "48K", "48K", &cpu_set_size }, + { UNIT_MSIZE, 4, "64K", "64K", &cpu_set_size }, + { UNIT_MSIZE, 6, "96K", "96K", &cpu_set_size }, + { UNIT_MSIZE, 8, "128K", "128K", &cpu_set_size }, + { UNIT_MSIZE, 12, "196K", "196K", &cpu_set_size }, + { UNIT_MSIZE, 16, "256K", "256K", &cpu_set_size }, +#if KI_22BIT|KI|ITS + { UNIT_MSIZE, 32, "512K", "512K", &cpu_set_size }, + { UNIT_MSIZE, 48, "768K", "768K", &cpu_set_size }, + { UNIT_MSIZE, 64, "1024K", "1024K", &cpu_set_size }, +#endif +#if KI_22BIT|KI|KL + { UNIT_MSIZE, 128, "2048K", "2048K", &cpu_set_size }, + { UNIT_MSIZE, 256, "4096K", "4096K", &cpu_set_size }, +#endif +#if KI|KL + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "SERIAL", "SERIAL", + &cpu_set_serial, &cpu_show_serial, NULL, "CPU Serial Number" }, +#endif +#if KA + { UNIT_M_PAGE, 0, "ONESEG", "ONESEG", NULL, NULL, NULL, + "One Relocation Register"}, + { UNIT_M_PAGE, UNIT_TWOSEG, "TWOSEG", "TWOSEG", NULL, NULL, + NULL, "Two Relocation Registers"}, +#if ITS + { UNIT_M_PAGE, UNIT_ITSPAGE, "ITS", "ITS", NULL, NULL, NULL, + "Paging hardware for ITS"}, +#endif +#if BBN + { UNIT_M_PAGE, UNIT_BBNPAGE, "BBN", "BBN", NULL, NULL, NULL, + "Paging hardware for TENEX"}, +#endif +#if WAITS + { UNIT_M_WAITS, UNIT_WAITS, "WAITS", "WAITS", NULL, NULL, NULL, + "Support for WAITS XCTR"}, + { UNIT_M_WAITS, 0, NULL, "NOWAITS", NULL, NULL, NULL, + "No support for WAITS XCTR"}, +#endif +#if MPX_DEV + { UNIT_M_MPX, UNIT_MPX, "MPX", "MPX", NULL, NULL, NULL, + "MPX Device for ITS"}, + { UNIT_M_MPX, 0, NULL, "NOMPX", NULL, NULL, NULL, + "Disables the MPX device"}, +#endif + { UNIT_MAOFF, UNIT_MAOFF, "MAOFF", "MAOFF", NULL, NULL, + NULL, "Interrupts relocated to 140"}, + { UNIT_MAOFF, 0, NULL, "NOMAOFF", NULL, NULL, NULL, + "No interrupt relocation"}, +#endif + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +/* Simulator debug controls */ +DEBTAB cpu_debug[] = { + {"IRQ", DEBUG_IRQ, "Debug IRQ requests"}, + {"CONI", DEBUG_CONI, "Show coni instructions"}, + {"CONO", DEBUG_CONO, "Show coni instructions"}, + {"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + {0, 0} +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit[0], cpu_reg, cpu_mod, + 1, 8, 22, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, NULL, DEV_DEBUG, 0, cpu_debug, + NULL, NULL, &cpu_help, NULL, NULL, &cpu_description + }; + +/* Data arrays */ +#define FCE 000001 /* Fetch memory into AR */ +#define FCEPSE 000002 /* Fetch and store memory into AR */ +#define SCE 000004 /* Save AR into memory */ +#define FAC 000010 /* Copy AR to BR, then Fetch AC into AR */ +#define FAC2 000020 /* Fetch AC+1 into MQ */ +#define SAC 000040 /* Save AC into AR */ +#define SACZ 000100 /* Save AC into AR if AC not 0 */ +#define SAC2 000200 /* Save MQ into AC+1 */ +#define SWAR 000400 /* Swap AR */ +#define FBR 001000 /* Load AC into BR */ + +#if PDP6 +#define P6(x) x +#define P10(x) 0 +#else +#define P6(x) 0 +#define P10(x) x +#endif + +int opflags[] = { + /* UUO Opcodes */ + /* UUO00 */ /* LUUO01 */ /* LUUO02 */ /* LUUO03 */ + 0, 0, 0, 0, + /* LUUO04 */ /* LUUO05 */ /* LUUO06 */ /* LUUO07 */ + 0, 0, 0, 0, + /* LUUO10 */ /* LUUO11 */ /* LUUO12 */ /* LUUO13 */ + 0, 0, 0, 0, + /* LUUO14 */ /* LUUO15 */ /* LUUO16 */ /* LUUO17 */ + 0, 0, 0, 0, + /* LUUO20 */ /* LUUO21 */ /* LUUO22 */ /* LUUO23 */ + 0, 0, 0, 0, + /* LUUO24 */ /* LUUO25 */ /* LUUO26 */ /* LUUO27 */ + 0, 0, 0, 0, + /* LUUO30 */ /* LUUO31 */ /* LUUO32 */ /* LUUO33 */ + 0, 0, 0, 0, + /* LUUO34 */ /* LUUO35 */ /* LUUO36 */ /* LUUO37 */ + 0, 0, 0, 0, + /* MUUO40 */ /* MUUO41 */ /* MUUO42 */ /* MUUO43 */ + 0, 0, 0, 0, + /* MUUO44 */ /* MUUO45 */ /* MUUO46 */ /* MUUO47 */ + 0, 0, 0, 0, + /* MUUO50 */ /* MUUO51 */ /* MUUO52 */ /* MUUO53 */ + 0, 0, 0, 0, + /* MUUO54 */ /* MUUO55 */ /* MUUO56 */ /* MUUO57 */ + 0, 0, 0, 0, + /* MUUO60 */ /* MUUO61 */ /* MUUO62 */ /* MUUO63 */ + 0, 0, 0, 0, + /* MUUO64 */ /* MUUO65 */ /* MUUO66 */ /* MUUO67 */ + 0, 0, 0, 0, + /* MUUO70 */ /* MUUO71 */ /* MUUO72 */ /* MUUO73 */ + 0, 0, 0, 0, + /* MUUO74 */ /* MUUO75 */ /* MUUO76 */ /* MUUO77 */ + 0, 0, 0, 0, + + /* Double precsision math */ + /* UJEN */ /* UUO101 */ /* GFAD */ /* GFSB */ + 0, 0, 0, 0, + /* JSYS */ /* ADJSP */ /* GFMP */ /*GFDV */ + 0, 0, 0, 0, + /* DFAD */ /* DFSB */ /* DFMP */ /* DFDV */ + 0, 0, 0, 0, + /* DADD */ /* DSUB */ /* DMUL */ /* DDIV */ + 0, 0, 0, 0, + /* DMOVE */ /* DMOVN */ /* FIX */ /* EXTEND */ + 0, 0, 0, 0, + /* DMOVEM */ /* DMOVNM */ /* FIXR */ /* FLTR */ + 0, 0, 0, 0, + + /* UFA */ /* DFN */ /* FSC */ /* IBP */ + P10(FCE|FBR), P10(FCE|FAC), FAC|SAC, 0, + /* ILDB */ /* LDB */ /* IDPB */ /* DPB */ + 0, 0, 0, 0, + /* Floating point */ + /* FAD */ /* FADL */ /* FADM */ /* FADB */ + SAC|FCE|FBR, SAC|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FADR */ /* FADRI */ /* FADRM */ /* FADRB */ + SAC|FCE|FBR, SAC|P6(SAC2|FCE)|P10(SWAR)|FBR, + FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FSB */ /* FSBL */ /* FSBM */ /* FSBB */ + SAC|FCE|FBR, SAC|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FSBR */ /* FSBRL */ /* FSBRM */ /* FSBRB */ + SAC|FCE|FBR, SAC|P6(SAC2|FCE)|P10(SWAR)|FBR, + FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FMP */ /* FMPL */ /* FMPM */ /* FMPB */ + SAC|FCE|FBR, SAC|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FMPR */ /* FMPRI */ /* FMPRM */ /* FMPRB */ + SAC|FCE|FBR, SAC|P6(SAC2|FCE)|P10(SWAR)|FBR, + FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FDV */ /* FDVL */ /* FDVM */ /* FDVB */ + SAC|FCE|FBR, SAC|FAC2|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, + /* FDVR */ /* FDVRL */ /* FDVRM */ /* FDVRB */ + SAC|FCE|FBR, SAC|P6(FAC2|SAC2|FCE)|P10(SWAR)|FBR, + FCEPSE|FBR, SAC|FBR|FCEPSE, + + /* Full word operators */ + /* MOVE */ /* MOVEI */ /* MOVEM */ /* MOVES */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* MOVS */ /* MOVSI */ /* MOVSM */ /* MOVSS */ + SWAR|SAC|FCE, SWAR|SAC, SWAR|FAC|SCE, SWAR|SACZ|FCEPSE, + /* MOVN */ /* MOVNI */ /* MOVNM */ /* MOVNS */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* MOVM */ /* MOVMI */ /* MOVMM */ /* MOVMS */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* IMUL */ /* IMULI */ /* IMULM */ /* IMULB */ + SAC|FCE|FBR, SAC|FBR, FCEPSE|FBR, SAC|FCEPSE|FBR, + /* MUL */ /* MULI */ /* MULM */ /* MULB */ + SAC2|SAC|FCE|FBR, SAC2|SAC|FBR, FCEPSE|FBR, SAC2|SAC|FCEPSE|FBR, + /* IDIV */ /* IDIVI */ /* IDIVM */ /* IDIVB */ + SAC2|SAC|FCE|FAC, SAC2|SAC|FAC, FCEPSE|FAC, SAC2|SAC|FCEPSE|FAC, + /* DIV */ /* DIVI */ /* DIVM */ /* DIVB */ + SAC2|SAC|FCE|FAC|FAC2, SAC2|SAC|FAC|FAC2, + FCEPSE|FAC|FAC2, SAC2|SAC|FCEPSE|FAC\ + |FAC2, + /* Shift operators */ + /* ASH */ /* ROT */ /* LSH */ /* JFFO */ + FAC|SAC, FAC|SAC, FAC|SAC, FAC, + /* ASHC */ /* ROTC */ /* LSHC */ /* UUO247 */ + FAC|SAC|SAC2|FAC2, FAC|SAC|SAC2|FAC2, FAC|SAC|SAC2|FAC2, 0, + + /* Branch operators */ + /* EXCH */ /* BLT */ /* AOBJP */ /* AOBJN */ + FAC|FCE, FAC, FAC|SAC, FAC|SAC, + /* JRST */ /* JFCL */ /* XCT */ /* MAP */ + 0, 0, 0, 0, + /* PUSHJ */ /* PUSH */ /* POP */ /* POPJ */ + FAC|SAC, FAC|FCE|SAC, FAC|SAC, FAC|SAC, + /* JSR */ /* JSP */ /* JSA */ /* JRA */ + 0, SAC, FBR|SCE, 0, + /* ADD */ /* ADDI */ /* ADDM */ /* ADDB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* SUB */ /* SUBI */ /* SUBM */ /* SUBB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + + /* Compare operators */ + /* CAI */ /* CAIL */ /* CAIE */ /* CAILE */ + FBR, FBR, FBR, FBR, + /* CAIA */ /* CAIGE */ /* CAIN */ /* CAIG */ + FBR, FBR, FBR, FBR, + /* CAM */ /* CAML */ /* CAME */ /* CAMLE */ + FBR|FCE, FBR|FCE, FBR|FCE, FBR|FCE, + /* CAMA */ /* CAMGE */ /* CAMN */ /* CAMG */ + FBR|FCE, FBR|FCE, FBR|FCE, FBR|FCE, + + /* Jump and skip operators */ + /* JUMP */ /* JUMPL */ /* JUMPE */ /* JUMPLE */ + FAC, FAC, FAC, FAC, + /* JUMPA */ /* JUMPGE */ /* JUMPN */ /* JUMPG */ + FAC, FAC, FAC, FAC, + /* SKIP */ /* SKIPL */ /* SKIPE */ /* SKIPLE */ + SACZ|FCE, SACZ|FCE, SACZ|FCE, SACZ|FCE, + /* SKIPA */ /* SKIPGE */ /* SKIPN */ /* SKIPG */ + SACZ|FCE, SACZ|FCE, SACZ|FCE, SACZ|FCE, + /* AOJ */ /* AOJL */ /* AOJE */ /* AOJLE */ + SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, + /* AOJA */ /* AOJGE */ /* AOJN */ /* AOJG */ + SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, + /* AOS */ /* AOSL */ /* AOSE */ /* AOSLE */ + SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, + /* AOSA */ /* AOSGE */ /* AOSN */ /* AOSG */ + SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, + /* SOJ */ /* SOJL */ /* SOJE */ /* SOJLE */ + SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, + /* SOJA */ /* SOJGE */ /* SOJN */ /* SOJG */ + SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, + /* SOS */ /* SOSL */ /* SOSE */ /* SOSLE */ + SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, + /* SOSA */ /* SOSGE */ /* SOSN */ /* SOSG */ + SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, + + /* Boolean operators */ + /* SETZ */ /* SETZI */ /* SETZM */ /* SETZB */ + SAC, SAC, SCE, SAC|SCE, + /* AND */ /* ANDI */ /* ANDM */ /* ANDB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* ANDCA */ /* ANDCAI */ /* ANDCAM */ /* ANDCAB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* SETM */ /* SETMI */ /* SETMM */ /* SETMB */ + SAC|FCE, SAC, 0, SAC|FCE, + /* ANDCM */ /* ANDCMI */ /* ANDCMM */ /* ANDCMB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* SETA */ /* SETAI */ /* SETAM */ /* SETAB */ + FBR|SAC, FBR|SAC, FBR|SCE, FBR|SAC|SCE, + /* XOR */ /* XORI */ /* XORM */ /* XORB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* IOR */ /* IORI */ /* IORM */ /* IORB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* ANDCB */ /* ANDCBI */ /* ANDCBM */ /* ANDCBB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* EQV */ /* EQVI */ /* EQVM */ /* EQVB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* SETCA */ /* SETCAI */ /* SETCAM */ /* SETCAB */ + FBR|SAC, FBR|SAC, FBR|SCE, FBR|SAC|SCE, + /* ORCA */ /* ORCAI */ /* ORCAM */ /* ORCAB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* SETCM */ /* SETCMI */ /* SETCMM */ /* SETCMB */ + SAC|FCE, SAC, FCEPSE, SAC|FCEPSE, + /* ORCM */ /* ORCMI */ /* ORCMM */ /* ORCMB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* ORCB */ /* ORCBI */ /* ORCBM */ /* ORCBB */ + FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, + /* SETO */ /* SETOI */ /* SETOM */ /* SETOB */ + SAC, SAC, SCE, SAC|SCE, + + /* Half word operators */ + /* HLL */ /* HLLI */ /* HLLM */ /* HLLS */ + FBR|SAC|FCE, FBR|SAC, FAC|FCEPSE, SACZ|FCEPSE, + /* HRL */ /* HRLI */ /* HRLM */ /* HRLS */ + SWAR|FBR|SAC|FCE, SWAR|FBR|SAC, FAC|SWAR|FCEPSE,SACZ|FCEPSE, + /* HLLZ */ /* HLLZI */ /* HLLZM */ /* HLLZS */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* HRLZ */ /* HRLZI */ /* HRLZM */ /* HRLZS */ + SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, + /* HLLO */ /* HLLOI */ /* HLLOM */ /* HLLOS */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* HRLO */ /* HRLOI */ /* HRLOM */ /* HRLOS */ + SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, + /* HLLE */ /* HLLEI */ /* HLLEM */ /* HLLES */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* HRLE */ /* HRLEI */ /* HRLEM */ /* HRLES */ + SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, + /* HRR */ /* HRRI */ /* HRRM */ /* HRRS */ + FBR|SAC|FCE, FBR|SAC, FAC|FCEPSE, SACZ|FCEPSE, + /* HLR */ /* HLRI */ /* HLRM */ /* HLRS */ + SWAR|FBR|SAC|FCE, SWAR|FBR|SAC, FAC|SWAR|FCEPSE,SACZ|FCEPSE, + /* HRRZ */ /* HRRZI */ /* HRRZM */ /* HRRZS */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* HLRZ */ /* HLRZI */ /* HLRZM */ /* HLRZS */ + SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, + /* HRRO */ /* HRROI */ /* HRROM */ /* HRROS */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* HLRO */ /* HLROI */ /* HLROM */ /* HLROS */ + SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, + /* HRRE */ /* HRREI */ /* HRREM */ /* HRRES */ + SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, + /* HLRE */ /* HLREI */ /* HLREM */ /* HLRES */ + SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, + + /* Test operators */ + /* TRN */ /* TLN */ /* TRNE */ /* TLNE */ + FBR, FBR|SWAR, FBR, FBR|SWAR, + /* TRNA */ /* TLNA */ /* TRNN */ /* TLNN */ + FBR, FBR|SWAR, FBR, FBR|SWAR, + /* TDN */ /* TSN */ /* TDNE */ /* TSNE */ + FBR|FCE, FBR|SWAR|FCE, FBR|FCE, FBR|SWAR|FCE, + /* TDNA */ /* TSNA */ /* TDNN */ /* TSNN */ + FBR|FCE, FBR|SWAR|FCE, FBR|FCE, FBR|SWAR|FCE, + /* TRZ */ /* TLZ */ /* TRZE */ /* TLZE */ + FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, + /* TRZA */ /* TLZA */ /* TRZN */ /* TLZN */ + FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, + /* TDZ */ /* TSZ */ /* TDZE */ /* TSZE */ + FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, + /* TDZA */ /* TSZA */ /* TDZN */ /* TSZN */ + FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, + /* TRC */ /* TLC */ /* TRCE */ /* TLCE */ + FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, + /* TRCA */ /* TLCA */ /* TRCN */ /* TLCN */ + FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, + /* TDC */ /* TSC */ /* TDCE */ /* TSCE */ + FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, + /* TDCA */ /* TSCA */ /* TDCN */ /* TSCN */ + FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, + /* TRO */ /* TLO */ /* TROE */ /* TLOE */ + FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, + /* TROA */ /* TLOA */ /* TRON */ /* TLON */ + FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, + /* TDO */ /* TSO */ /* TDOE */ /* TSOE */ + FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, + /* TDOA */ /* TSOA */ /* TDON */ /* TSON */ + FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, + + /* IOT Instructions */ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +#if PDP6 +#define PC_CHANGE FLAGS |= PCHNG; check_apr_irq(); +#else +#define PC_CHANGE +#endif +#define SWAP_AR ((RMASK & AR) << 18) | ((AR >> 18) & RMASK) +#define SMEAR_SIGN(x) x = ((x) & SMASK) ? (x) | EXPO : (x) & MANT +#define GET_EXPO(x) ((((x) & SMASK) ? 0377 : 0 ) \ + ^ (((x) >> 27) & 0377)) +#if KI +#define AOB(x) ((x + 1) & RMASK) | ((x + 01000000LL) & (C1|LMASK)) +#define SOB(x) ((x + RMASK) & RMASK) | ((x + LMASK) & (C1|LMASK)); +#else +#define AOB(x) (x + 01000001LL) +#define SOB(x) (x + 0777776777777LL) +#endif +#if ITS +#define QITS (cpu_unit[0].flags & UNIT_ITSPAGE) +#define QTEN11 (ten11_unit[0].flags & UNIT_ATT) +#define QAUXCPU (auxcpu_unit[0].flags & UNIT_ATT) +#else +#define QITS 0 +#endif +#if BBN +#define QBBN (cpu_unit[0].flags & UNIT_BBNPAGE) +#else +#define QBBN 0 +#endif +#if WAITS +#define QWAITS (cpu_unit[0].flags & UNIT_WAITS) +#else +#define QWAITS 0 +#endif + +#if ITS +/* + * Set quantum clock to qua_time. + */ + +void +set_quantum() +{ + sim_cancel(&cpu_unit[1]); + if ((qua_time & RSIGN) == 0) { + double us; + us = (double)(RSIGN - qua_time); + (void)sim_activate_after_d(&cpu_unit[1], us); + } +} + +/* + * Update the qua_time variable. + */ +void +load_quantum() +{ + if (sim_is_active(&cpu_unit[1])) { + double us; + us = sim_activate_time_usecs (&cpu_unit[1]); + qua_time = RSIGN - (uint32)us; + sim_cancel(&cpu_unit[1]); + } +} + +/* + * Get the current quantum time. + */ +uint32 +get_quantum() +{ + uint32 t = 0; + if (sim_is_active(&cpu_unit[1])) { + double us; + us = sim_activate_time_usecs (&cpu_unit[1]); + t = RSIGN - (uint32)us; + } + return t; +} +#endif + +/* + * Set device to interrupt on a given level 1-7 + * Level 0 means that device interrupt is not enabled + */ +void set_interrupt(int dev, int lvl) { + lvl &= 07; + if (lvl) { + dev_irq[dev>>2] = 0200 >> lvl; + pi_pending = 1; + sim_debug(DEBUG_IRQ, &cpu_dev, "set irq %o %o %03o %03o %03o\n", + dev & 0774, lvl, PIE, PIR, PIH); + } +} + +#if MPX_DEV +void set_interrupt_mpx(int dev, int lvl, int mpx) { + lvl &= 07; + if (lvl) { + dev_irq[dev>>2] = 0200 >> lvl; + if (lvl == 1 && mpx != 0) + dev_irq[dev>>2] |= mpx << 8; + pi_pending = 1; + sim_debug(DEBUG_IRQ, &cpu_dev, "set mpx irq %o %o %o %03o %03o %03o\n", + dev & 0774, lvl, mpx, PIE, PIR, PIH); + } +} +#endif + +/* + * Clear the interrupt flag for a device + */ +void clr_interrupt(int dev) { + dev_irq[dev>>2] = 0; + if (dev > 4) + sim_debug(DEBUG_IRQ, &cpu_dev, "clear irq %o\n", dev & 0774); +} + +/* + * Check if there is any pending interrupts return 0 if none, + * else set pi_enc to highest level and return 1. + */ +int check_irq_level() { + int i, lvl; + int pi_req; + + /* If PXCT don't check for interrupts */ + if (xct_flag != 0) + return 0; + check_apr_irq(); + + /* If not enabled, check if any pending Processor IRQ */ + if (pi_enable == 0) { +#if !PDP6 + if (PIR != 0) { + pi_enc = 1; + for(lvl = 0100; lvl != 0; lvl >>= 1) { + if (lvl & PIH) + break; + if (PIR & lvl) + return 1; + pi_enc++; + } + } +#endif + return 0; + } + + /* Scan all devices */ + for(i = lvl = 0; i < 128; i++) + lvl |= dev_irq[i]; + if (lvl == 0) + pi_pending = 0; + pi_req = (lvl & PIE) | PIR; +#if MPX_DEV + /* Check if interrupt on PI channel 1 */ + if (mpx_enable && cpu_unit[0].flags & UNIT_MPX && + (pi_req & 0100) && (PIH & 0100) == 0) { + pi_enc = 010; + for(i = lvl = 0; i < 128; i++) { + int l = dev_irq[i] >> 8; + if (dev_irq[i] & 0100 && l != 0 && l < pi_enc) + pi_enc = l; + } + if (pi_enc != 010) { + pi_enc += 010; + return 1; + } + } +#endif + /* Handle held interrupt requests */ + i = 1; + for(lvl = 0100; lvl != 0; lvl >>= 1, i++) { + if (lvl & PIH) + break; + if (pi_req & lvl) { + pi_enc = i; + return 1; + } + } + return 0; +} + +/* + * Recover from held interrupt. + */ +void restore_pi_hold() { + int lvl; + + if (!pi_enable) + return; + /* Clear HOLD flag for highest interrupt */ + for(lvl = 0100; lvl != 0; lvl >>= 1) { + if (lvl & PIH) { + PIR &= ~lvl; + sim_debug(DEBUG_IRQ, &cpu_dev, "restore irq %o %03o\n", lvl, PIH); + PIH &= ~lvl; + break; + } + } + pi_pending = 1; +} + +/* + * Hold interrupts at the current level. + */ +void set_pi_hold() { + int pi = pi_enc; +#if MPX_DEV + if (mpx_enable && cpu_unit[0].flags & UNIT_MPX && pi > 07) + pi = 1; +#endif + PIR &= ~(0200 >> pi); + if (pi_enable) + PIH |= (0200 >> pi); +} + +/* + * PI device for KA and KI + */ +t_stat dev_pi(uint32 dev, uint64 *data) { + uint64 res = 0; + switch(dev & 3) { + case CONO: + /* Set PI flags */ + res = *data; + if (res & 010000) { /* Bit 23 */ + PIR = PIH = PIE = 0; + pi_enable = 0; +#if MPX_DEV + mpx_enable = 0; +#endif + parity_irq = 0; + } + if (res & 0200) { /* Bit 28 */ + pi_enable = 1; + } + if (res & 0400) /* Bit 27 */ + pi_enable = 0; + if (res & 01000) /* Bit 26 */ + PIE &= ~(*data & 0177); + if (res & 02000) /* Bit 25 */ + PIE |= (*data & 0177); + if (res & 04000) { /* Bit 24 */ + PIR |= (*data & 0177); + pi_pending = 1; + } +#if MPX_DEV + if (res & 020000 && cpu_unit[0].flags & UNIT_MPX) + mpx_enable = 1; +#endif +#if KI + if (res & 020000) { /* Bit 22 */ + PIR &= ~(*data & 0177); + } +#endif + if (res & 040000) /* Bit 21 */ + parity_irq = 1; + if (res & 0100000) /* Bit 20 */ + parity_irq = 0; + check_apr_irq(); + sim_debug(DEBUG_CONO, &cpu_dev, "CONO PI %012llo\n", *data); + break; + + case CONI: + res = PIE; + res |= (pi_enable << 7); + res |= (PIH << 8); +#if KI + res |= ((uint64)(PIR) << 18); +#endif + res |= (parity_irq << 15); + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI PI %012llo\n", *data); + break; + + case DATAO: + MI = *data; +#ifdef PANDA_LIGHTS + /* Set lights */ + ka10_lights_main (*data); +#endif + break; + + case DATAI: + break; + } + return SCPE_OK; +} + +/* + * Non existent device +*/ +t_stat null_dev(uint32 dev, uint64 *data) { + switch(dev & 3) { + case CONI: + case DATAI: + *data = 0; + break; + + case CONO: + case DATAO: + break; + } + return SCPE_OK; +} + + +#if KI +static int timer_irq, timer_flg; + +/* + * Page device for KI10. + */ +t_stat dev_pag(uint32 dev, uint64 *data) { + uint64 res = 0; + int i; + switch(dev & 03) { + case CONI: + /* Complement of vpn */ + *data = (uint64)(pag_reload ^ 040); + *data |= ((uint64)last_page) << 8; + *data |= (uint64)((apr_serial == -1) ? DEF_SERIAL : apr_serial) << 26; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI PAG %012llo\n", *data); + break; + + case CONO: + /* Set Stack AC and Page Table Reload Counter */ + ac_stack = (*data >> 9) & 0760; + pag_reload = (*data & 037) | (pag_reload & 040); + sim_debug(DEBUG_CONO, &cpu_dev, "CONI PAG %012llo\n", *data); + break; + + case DATAO: + res = *data; + if (res & RSIGN) { + eb_ptr = (res & 017777) << 9; + for (i = 0; i < 512; i++) + e_tlb[i] = u_tlb[i] = 0; + for (;i < 546; i++) + u_tlb[i] = 0; + page_enable = (res & 020000) != 0; + } + if (res & SMASK) { + ub_ptr = ((res >> 18) & 017777) << 9; + for (i = 0; i < 512; i++) + e_tlb[i] = u_tlb[i] = 0; + for (;i < 546; i++) + u_tlb[i] = 0; + user_addr_cmp = (res & 00020000000000LL) != 0; + small_user = (res & 00040000000000LL) != 0; + fm_sel = (uint8)(res >> 29) & 060; + } + pag_reload = 0; + sim_debug(DEBUG_DATAIO, &cpu_dev, + "DATAO PAG %012llo ebr=%06o ubr=%06o\n", + *data, eb_ptr, ub_ptr); + break; + + case DATAI: + res = (eb_ptr >> 9); + if (page_enable) + res |= 020000; + res |= ((uint64)(ub_ptr)) << 9; + if (user_addr_cmp) + res |= 00020000000000LL; + if (small_user) + res |= 00040000000000LL; + res |= ((uint64)(fm_sel)) << 29; + *data = res; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI PAG %012llo\n", *data); + break; + } + return SCPE_OK; +} + +/* + * Check if the last operation caused a APR IRQ to be generated. + */ +void check_apr_irq() { + if (pi_enable && apr_irq) { + int flg = 0; + clr_interrupt(0); + flg |= inout_fail | nxm_flag; + if (flg) + set_interrupt(0, apr_irq); + } + if (pi_enable && clk_en && clk_flg) + set_interrupt(4, clk_irq); +} + + +/* + * APR device for KI10. + */ +t_stat dev_apr(uint32 dev, uint64 *data) { + uint64 res = 0; + switch(dev & 03) { + case CONI: + /* Read trap conditions */ + res = clk_irq | (apr_irq << 3) | (nxm_flag << 6); + res |= (inout_fail << 7) | (clk_flg << 9) | (clk_en << 10); + res |= (timer_irq << 14) | (parity_irq << 15) | (timer_flg << 17); + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI APR %012llo\n", *data); + break; + + case CONO: + /* Set trap conditions */ + res = *data; + clk_irq = res & 07; + apr_irq = (res >> 3) & 07; + if (res & 0000100) + nxm_flag = 0; + if (res & 0000200) + inout_fail = 0; + if (res & 0001000) { + clk_flg = 0; + clr_interrupt(4); + } + if (res & 0002000) { + clk_en = 1; + if (clk_flg) + set_interrupt(4, clk_irq); + } + if (res & 0004000) { + clk_en = 0; + clr_interrupt(4); + } + if (res & 0040000) + timer_irq = 1; + if (res & 0100000) + timer_irq = 0; + if (res & 0200000) + reset_all(1); + if (res & 0400000) + timer_flg = 0; + check_apr_irq(); + sim_debug(DEBUG_CONO, &cpu_dev, "CONO APR %012llo\n", *data); + break; + + case DATAO: + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAO APR %012llo\n", *data); + break; + + case DATAI: + /* Read switches */ + *data = SW; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI APR %012llo\n", *data); + break; + } + return SCPE_OK; +} + +#endif + +#if KA + +#if BBN +t_stat dev_pag(uint32 dev, uint64 *data) { + uint64 res = 0; + int i; + int page_limit[] = { + 01000, 0040, 0100, 0140, 0200, 0240, 0300, 0340}; + switch(dev & 03) { + case CONI: + break; + + case CONO: + switch (*data & 07) { + case 0: /* Clear page tables, reload from 71 & 72 */ + for (i = 0; i < 512; i++) + e_tlb[i] = u_tlb[i] = 0; + res = M[071]; + mon_base_reg = (res & 03777) << 9; + ac_stack = (res >> 9) & 0760; + user_base_reg = (res >> 9) & 03777000; + user_limit = page_limit[(res >> 30) & 07]; + pur = M[072]; + break; + + case 1: /* Clear exec mapping */ + for (i = 0; i < 512; i++) + e_tlb[i] = 0; + break; + + case 2: /* Clear mapping for next write */ + next_write = 1; + break; + + case 3: /* Clear user mapping */ + for (i = 0; i < 512; i++) + u_tlb[i] = 0; + break; + + case 4: /* Turn off pager */ + case 5: /* same as 4 */ + page_enable = 0; + break; + + case 6: /* Pager on, no resident mapping */ + page_enable = 1; + exec_map = 0; + break; + + case 7: /* Pager on, resident mapping */ + page_enable = 1; + exec_map = 1; + break; + } + sim_debug(DEBUG_CONO, &cpu_dev, "CONO PAG %012llo\n", *data); + break; + + case DATAO: + break; + + case DATAI: + break; + } + return SCPE_OK; +} +#endif + +/* + * Check if the last operation caused a APR IRQ to be generated. + */ +void check_apr_irq() { + if (pi_enable && apr_irq) { + int flg = 0; + clr_interrupt(0); + flg |= ((FLAGS & OVR) != 0) & ov_irq; + flg |= ((FLAGS & FLTOVR) != 0) & fov_irq; + flg |= nxm_flag | mem_prot | push_ovf; + if (flg) + set_interrupt(0, apr_irq); + } +} + + +/* + * APR Device for KA10. + */ +t_stat dev_apr(uint32 dev, uint64 *data) { + uint64 res = 0; + switch(dev & 03) { + case CONI: + /* Read trap conditions */ + /* 000007 33-35 PIA */ + /* 000010 32 Overflow * */ + /* 000020 31 Overflow enable */ + /* 000040 30 Trap offset */ + /* 000100 29 Floating overflow * */ + /* 000200 28 Floating overflow enable */ + /* 000400 27 */ + /* 001000 26 Clock * */ + /* 002000 25 Clock enable */ + /* 004000 24 */ + /* 010000 23 NXM * */ + /* 020000 22 Memory protection * */ + /* 040000 21 Address break * */ + /* 100000 20 User In-Out */ + /* 200000 19 Push overflow * */ + /* 400000 18 */ + res = apr_irq | (((FLAGS & OVR) != 0) << 3) | (ov_irq << 4) ; + res |= (((FLAGS & FLTOVR) != 0) << 6) | (fov_irq << 7) ; + res |= (clk_flg << 9) | (((uint64)clk_en) << 10) | (nxm_flag << 12); + res |= (mem_prot << 13) | (((FLAGS & USERIO) != 0) << 15); + res |= (push_ovf << 16) | (maoff >> 1); + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI APR %012llo\n", *data); + break; + + case CONO: + /* Set trap conditions */ + res = *data; + clk_irq = apr_irq = res & 07; + clr_interrupt(0); + if (res & 010) + FLAGS &= ~OVR; + if (res & 020) + ov_irq = 1; + if (res & 040) + ov_irq = 0; + if (res & 0100) + FLAGS &= ~FLTOVR; + if (res & 0200) + fov_irq = 1; + if (res & 0400) + fov_irq = 0; + if (res & 0001000) { + clk_flg = 0; + clr_interrupt(4); + } + if (res & 0002000) { + clk_en = 1; + if (clk_flg) + set_interrupt(4, clk_irq); + } + if (res & 0004000) { + clk_en = 0; + clr_interrupt(4); + } + if (res & 010000) + nxm_flag = 0; + if (res & 020000) + mem_prot = 0; + if (res & 0200000) { +#if MPX_DEV + mpx_enable = 0; +#endif +#if BBN + if (QBBN) + exec_map = 0; +#endif + reset_all(1); + } + if (res & 0400000) + push_ovf = 0; + check_apr_irq(); + sim_debug(DEBUG_CONO, &cpu_dev, "CONO APR %012llo\n", *data); + break; + + case DATAO: + /* Set protection registers */ + Rh = (0377 & (*data >> 1)) << 10; + Rl = (0377 & (*data >> 10)) << 10; + Pflag = 01 & (*data >> 18); + Ph = ((0377 & (*data >> 19)) << 10) + 01777; + Pl = ((0377 & (*data >> 28)) << 10) + 01777; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAO APR %012llo\n", *data); +sim_debug(DEBUG_DATAIO, &cpu_dev, "Rl=%06o Pl=%06o, Rh=%06o, Ph=%06o\n", Rl, Pl, Rh, Ph); + break; + + case DATAI: + /* Read switches */ + *data = SW; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI APR %012llo\n", *data); + break; + } + return SCPE_OK; +} +#endif + + +#if KI +/* + * Handle page lookup on KI10 + * + * addr is address to look up. + * flag is set for pi cycle and user overide. + * loc is final address. + * wr indicates whether cycle is read or write. + * cur_context is set when access should ignore xct_flag + * fetch is set for instruction fetches. + */ +int page_lookup(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { + uint64 data; + int base = 0; + int page = (RMASK & addr) >> 9; + int uf = (FLAGS & USER) != 0; + int upmp = 0; + + if (page_fault) + return 0; + + /* If paging is not enabled, address is direct */ + if (!page_enable) { + *loc = addr; + return 1; + } + + /* If fetching byte data, use write access */ + if (BYF5 && (IR & 06) == 6) + wr = 1; + + /* If this is modify instruction use write access */ + wr |= modify; + + /* Figure out if this is a user space access */ + if (flag) + uf = 0; + else if (xct_flag != 0 && !cur_context && !uf) { + if (((xct_flag & 2) != 0 && wr != 0) || + ((xct_flag & 1) != 0 && (wr == 0 || modify))) { + uf = (FLAGS & USERIO) != 0; + } + } + + /* If user, check if small user enabled */ + if (uf) { + if (small_user && (page & 0340) != 0) { + fault_data = (((uint64)(page))<<18) | ((uint64)(uf) << 27) | 020LL; + page_fault = 1; + return 0; + } + } else { + /* Handle system mapping */ + /* Pages 340-377 via UBR */ + if ((page & 0740) == 0340) { + page += 01000 - 0340; + upmp = 1; + /* Pages 400-777 via EBR */ + } else if (page & 0400) { + base = 1; + /* Pages 000-037 direct map */ + } else { + /* Check if supervisory mode */ + *loc = addr; + /* If PUBLIC and private page, make sure we are fetching a Portal */ + if (!flag && ((FLAGS & PUBLIC) != 0) && + (!fetch || (M[addr] & 00777040000000LL) != 0254040000000LL)) { + /* Handle public violation */ + fault_data = (((uint64)(page))<<18) | ((uint64)(uf) << 27) + | 021LL; + page_fault = 1; + return !wr; + } + return 1; + } + } + /* Map the page */ + if (base) { + data = e_tlb[page]; + if (data == 0) { + data = M[eb_ptr + (page >> 1)]; + e_tlb[page & 0776] = RMASK & (data >> 18); + e_tlb[page | 1] = RMASK & data; + data = e_tlb[page]; + pag_reload = ((pag_reload + 1) & 037) | 040; + } + last_page = ((page ^ 0777) << 1)|1; + } else { + data = u_tlb[page]; + if (data == 0) { + data = M[ub_ptr + (page >> 1)]; + u_tlb[page & 01776] = RMASK & (data >> 18); + u_tlb[page | 1] = RMASK & data; + data = u_tlb[page]; + pag_reload = ((pag_reload + 1) & 037) | 040; + } + if (upmp) + last_page = (((page-0440) ^ 0777) << 1) | 1; + else + last_page = ((page ^ 0777) << 1); + } + *loc = ((data & 017777) << 9) + (addr & 0777); + + /* Check for access error */ + if ((data & RSIGN) == 0 || (wr & ((data & 0100000) == 0))) { + page = (RMASK & addr) >> 9; + fault_data = ((((uint64)(page))<<18) | ((uint64)(uf) << 27)) & LMASK; + fault_data |= (data & 0400000) ? 010LL : 0LL; /* A */ + fault_data |= (data & 0100000) ? 004LL : 0LL; /* W */ + fault_data |= (data & 0040000) ? 002LL : 0LL; /* S */ + fault_data |= wr; + page_fault = 1; + return 0; + } + + /* If PUBLIC and private page, make sure we are fetching a Portal */ + if (!flag && ((FLAGS & PUBLIC) != 0) && ((data & 0200000) == 0) && + (!fetch || (M[*loc] & 00777040000000LL) != 0254040000000LL)) { + /* Handle public violation */ + fault_data = (((uint64)(page))<<18) | ((uint64)(uf) << 27) | 021LL; + page_fault = 1; + return 0; + } + + /* If fetching from public page, set public flag */ + if (fetch && ((data & 0200000) != 0)) + FLAGS |= PUBLIC; + return 1; +} + +/* + * Register access on KI 10 + */ +uint64 get_reg(int reg) { + if (FLAGS & USER) + return FM[fm_sel|(reg & 017)]; + else + return FM[reg & 017]; +} + +void set_reg(int reg, uint64 value) { + if (FLAGS & USER) + FM[fm_sel|(reg & 017)] = value; + else + FM[reg & 017] = value; +} + +/* + * Read a location directly from memory. + * + * Return of 0 if successful, 1 if there was an error. + */ +int Mem_read_nopage() { + if (AB < 020) { + MB = FM[AB]; + } else { + sim_interval--; + if (AB >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + MB = M[AB]; + } + return 0; +} + +/* + * Write a directly to a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ +int Mem_write_nopage() { + + if (AB < 020) { + FM[AB] = MB; + } else { + sim_interval--; + if (AB >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + M[AB] = MB; + } + return 0; +} + +int Mem_read(int flag, int cur_context, int fetch) { + int addr; + + if (AB < 020) { + if (FLAGS & USER) { + MB = get_reg(AB); + return 0; + } else { + if (!cur_context && ((xct_flag & 1) != 0)) { + if (FLAGS & USERIO) { + if (fm_sel == 0) + goto read; + MB = FM[fm_sel|AB]; + return 0; + } + MB = M[ub_ptr + ac_stack + AB]; + return 0; + } + } + MB = get_reg(AB); + } else { +read: + sim_interval--; + if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + MB = M[addr]; + } + return 0; +} + +int Mem_write(int flag, int cur_context) { + int addr; + + if (AB < 020) { + if (FLAGS & USER) { + set_reg(AB, MB); + return 0; + } else { + if (!cur_context && + (((xct_flag & 1) != 0 && modify) || + (xct_flag & 2) != 0)) { + if (FLAGS & USERIO) { + if (fm_sel == 0) + goto write; + else + FM[fm_sel|AB] = MB; + } else { + M[ub_ptr + ac_stack + AB] = MB; + } + return 0; + } + } + set_reg(AB, MB); + } else { +write: + sim_interval--; + if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + M[addr] = MB; + } + return 0; +} +#endif + +#if KA + +#define get_reg(reg) FM[(reg) & 017] +#define set_reg(reg, value) FM[(reg) & 017] = value + +#if ITS + +/* + * Load TBL entry for ITS. + */ +int its_load_tlb(uint32 reg, int page, uint32 *tlb) { + uint64 data; + int len = (reg >> 19) & 0177; + int entry = (reg & 01777777) + ((page & 0377) >> 1); + if ((page >> 1) > len) { + fault_data |= 0200; + return 1; + } + if (entry > (int)MEMSIZE) { + nxm_flag = 1; + fault_data |= 0400; + return 1; + } + data = M[entry]; + if (page & 1) { + data &= ~036000LL; + data |= ((uint64)(age & 017)) << 10; + } else { + data &= ~(036000LL << 18); + data |= ((uint64)(age & 017)) << (10+18); + } + M[entry] = data; + if ((page & 1) == 0) + data >>= 18; + data &= RMASK; + *tlb = (uint32)data; + pag_reload = ((pag_reload + 1) & 017); + return 0; +} + +/* + * Translation logic for KA10 + */ + +int page_lookup_its(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { + uint64 data; + int base = 0; + int page = (RMASK & addr) >> 10; + int acc; + int uf = (FLAGS & USER) != 0; + int ofd = (int)fault_data; + + /* If paging is not enabled, address is direct */ + if (!page_enable) { + *loc = addr; + return 1; + } + + /* If fetching byte data, use write access */ + if (BYF5 && (IR & 06) == 6) + wr = 1; + + /* If this is modify instruction use write access */ + wr |= modify; + + /* Figure out if this is a user space access */ + if (flag) + uf = 0; + else if (xct_flag != 0 && !cur_context && !uf) { + if (((xct_flag & 2) != 0 && wr != 0) || + ((xct_flag & 1) != 0 && (wr == 0 || modify))) { + uf = 1; + } + } + + /* AC & 1 = ??? */ + /* AC & 2 = Read User */ + /* AC & 4 = Write User */ + /* AC & 8 = Inhibit mem protect, skip */ + + /* Add in MAR checking */ + if (addr == (mar & RMASK)) { + switch((mar >> 18) & 03) { + case 0: break; + case 1: if (fetch) { + mem_prot = 1; + fault_data |= 2; + } + break; + case 2: if (!wr) + break; + /* Fall through */ + case 3: mem_prot = 1; + fault_data |= 2; + break; + } + } + + + /* Map the page */ + if (!uf) { + /* Handle system mapping */ + if ((page & 0200) == 0 || (fault_data & 04) == 0) { + /* Direct map 0-377 or all if bit 2 off */ + *loc = addr; + return 1; + } + data = e_tlb[page - 0200]; + if (data == 0) { + if (its_load_tlb(dbr3, page - 0200, &e_tlb[page - 0200])) + goto fault; + data = e_tlb[page - 0200]; + } + } else { + data = u_tlb[page]; + if (data == 0) { + if (page & 0200) { + if (its_load_tlb(dbr2, page - 0200, &u_tlb[page])) + goto fault; + } else { + if (its_load_tlb(dbr1, page, &u_tlb[page])) + goto fault; + } + data = u_tlb[page]; + } + } + *loc = ((data & 01777) << 10) + (addr & 01777); + acc = (data >> 16) & 03; + + /* Access check logic */ + switch(acc) { + case 0: /* No access */ + fault_data |= 0010; + break; + case 1: /* Read Only Access */ + if (!wr) + return 1; + if ((fault_data & 00770) == 0) + fault_data |= 0100; + break; + case 2: /* Read write first */ + if (fetch && (FLAGS & PURE)) { + fault_data |= 0020; + break; + } + if (!wr) /* Read is OK */ + return 1; + if ((fault_data & 00770) == 0) + fault_data |= 040; + break; + case 3: /* All access */ + if (fetch && (FLAGS & PURE)) { + fault_data |= 0020; + break; + } + return 1; + } +fault: + /* Update fault data, fault address only if new fault */ + if ((ofd & 00770) == 0) + fault_addr = (page) | ((uf)? 0400 : 0) | ((data & 01777) << 9); + if ((xct_flag & 04) == 0) { + mem_prot = 1; + fault_data |= 01000; + } else { + PC = (PC + 1) & RMASK; + } + return 0; +} + +/* + * Read a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ +int Mem_read_its(int flag, int cur_context, int fetch) { + int addr; + + if (AB < 020) { + if ((xct_flag & 1) != 0 && !cur_context && (FLAGS & USER) == 0) { + MB = M[(ac_stack & 01777777) + AB]; + return 0; + } + MB = get_reg(AB); + } else { + sim_interval--; + if (!page_lookup_its(AB, flag, &addr, 0, cur_context, fetch)) + return 1; +#if NUM_DEVS_TEN11 > 0 + if (T11RANGE(addr) && QTEN11) { + if (ten11_read (addr, &MB)) { + nxm_flag = 1; + return 1; + } + return 0; + } +#endif +#if NUM_DEVS_AUXCPU > 0 + if (AUXCPURANGE(addr) && QAUXCPU) { + if (auxcpu_read (addr, &MB)) { + nxm_flag = 1; + return 1; + } + return 0; + } +#endif + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + MB = M[addr]; + } + return 0; +} + +/* + * Write a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ +int Mem_write_its(int flag, int cur_context) { + int addr; + + if (AB < 020) { + if ((xct_flag & 2) != 0 && !cur_context && (FLAGS & USER) == 0) { + M[(ac_stack & 01777777) + AB] = MB; + return 0; + } + set_reg(AB, MB); + } else { + sim_interval--; + if (!page_lookup_its(AB, flag, &addr, 1, cur_context, 0)) + return 1; +#if NUM_DEVS_TEN11 > 0 + if (T11RANGE(addr) && QTEN11) { + if (ten11_write (addr, MB)) { + nxm_flag = 1; + return 1; + } + return 0; + } +#endif +#if NUM_DEVS_AUXCPU > 0 + if (AUXCPURANGE(addr) && QAUXCPU) { + if (auxcpu_write (addr, MB)) { + nxm_flag = 1; + return 1; + } + return 0; + } +#endif + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + M[addr] = MB; + } + return 0; +} +#endif + +#if BBN +int page_lookup_bbn(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { + /* Group 0, 01 = 00 + bit 2 = Age 00x 0100000 + bit 3 = Age 02x 0040000 + bit 4 = Age 04x 0020000 + bit 5 = Age 06x 0010000 + bit 6 = Monitor after loading AR trap 0004000 */ + /* Group 1, 01 = 01 0200000 + bit 3 = Shared page not in core 0040000 + bit 4 = page table not in core (p.t.2) 0020000 + bit 5 = 2nd indirect, private not in core (p.t.3) 0010000 + bit 6 = Indirect shared not in core (p.t.2 || p.t.3) 0004000 + bit 7 = Indirect page table not in core (p.t.3) 0002000 + bit 8 = Excessive indirect pointers (>2) 0001000 */ + /* Group 2, 01 = 10 0400000 + bit 2 = Private not in core + bit 3 = Write copy trap (bit 9 in p.t.) + bit 4 = user trap (bit 8 in p.t.) + bit 5 = access trap (p.t. bit 12 = 0 or bits 10-11=3) + bit 6 = illegal read or execute + bit 7 = illegal write + bit 8 = address limit register violation or p.t. bits + 0,1 = 3 (illegal format) */ + /* Group 3, 01 = 11 (in 2nd or 3rd p.t.) 060000 + bit 2 = private not in core + bit 3 = write copy trap (bit 9 in p.t.) + bit 4 = user trap (bit 8 in p.t.) + bit 5 = access trap (p.t. bit 12 = 0 or bits 10-11=3) + bit 6 = illegal read or execute + bit 7 = illegal write + bit 8 = address limit register violation or p.t. bits + 0,1 = 3 (illegal format */ + uint64 data; + uint32 tlb_data; + uint64 traps; + int base = 0; + int trap = 0; + int lvl = 0; + int page = (RMASK & addr) >> 9; + int uf = (FLAGS & USER) != 0; + int map = page; + int match; + + if (page_fault) + return 0; + + /* If paging is not enabled, address is direct */ + if (!page_enable) { + *loc = addr; + return 1; + } + + /* If this is modify instruction use write access */ + wr |= modify; + + /* Umove instructions handled here */ + if ((IR & 0774) == 0100 && (FLAGS & EXJSYS) == 0) + uf = 1; + /* Figure out if this is a user space access */ + if (flag) + uf = 0; + else { + if (QWAITS && xct_flag != 0 && !fetch && !uf) { + if (xct_flag & 010 && cur_context) /* Indirect */ + uf = 1; + if (xct_flag & 004 && wr == 0) /* XR */ + uf = 1; + if (xct_flag & 001 && (wr == 1 || BYF5)) /* XW or XLB or XDB */ + uf = 1; + } + if (!QWAITS && (FLAGS & EXJSYS) == 0 && uf == 0 && !fetch && xct_flag != 0) { + if (xct_flag & 010 && cur_context) + uf = 1; + if (xct_flag & 004 && wr == 0) + uf = 1; + if (xct_flag & 002 && BYF5) + uf = 1; + if (xct_flag & 001 && wr == 1) + uf = 1; + } + } + + /* If not really user mode and register access */ + if (addr < 020 && uf && (FLAGS & USER) == 0) { + if (QWAITS) + goto lookup; + addr |= 0775000 | ac_stack; + uf = 0; + } + + /* If still access register, just return */ + if (addr < 020) { + *loc = addr; + return 1; + } + +lookup: + if (uf) { + if (page > user_limit) { + /* over limit violation */ + fault_data = 0401000; + goto fault_bbn; + } + base = user_base_reg; + tlb_data = u_tlb[page]; + } else { + /* 000 - 077 resident map */ + /* 100 - 177 per processor map */ + /* 200 - 577 monitor map */ + /* 600 - 777 per process map */ + if ((page & 0700) == 0 && exec_map == 0) { + *loc = addr; + return 1; + } + if ((page & 0600) == 0600) + base = mon_base_reg; + else + base = 03000; + tlb_data = e_tlb[page]; + } + if (tlb_data != 0) { +access: + *loc = ((tlb_data & 03777) << 9) + (addr & 0777); + /* Check access */ + if (wr && (tlb_data & 0200000) == 0) { + fault_data = 0402000; + goto fault_bbn; + } else if (fetch && (tlb_data & 0100000) == 0) { + fault_data = 0404000; + goto fault_bbn; + } else if ((tlb_data & 0400000) == 0) { + fault_data = 0404000; + goto fault_bbn; + } + return 1; + } + traps = FMASK; + /* Map the page */ + match = 0; + while (!match) { + data = M[base + map]; + + switch ((data >> 34) & 03) { + case 0: /* Direct page */ + /* Bit 4 = execute */ + /* Bit 3 = Write */ + /* Bit 2 = Read */ + traps &= data & (BBN_MERGE|BBN_TRPPG); + tlb_data = (uint32)(((data & (BBN_EXEC|BBN_WRITE|BBN_READ)) >> 16) | + (data & 03777)); + match = 1; + break; + + case 1: /* Shared page */ + /* Check trap */ + base = 020000; + map = (data & BBN_SPT) >> 9; + traps &= data & (BBN_MERGE|BBN_PAGE); + data = 0; + lvl ++; + break; + + case 2: /* Indirect page */ + if (lvl == 2) { + /* Trap */ + fault_data = 0201000; + goto fault_bbn; + } + map = data & BBN_PN; + base = 020000 + ((data & BBN_SPT) >> 9); + traps &= data & (BBN_MERGE|BBN_PAGE); + data = 0; + lvl ++; + break; + + case 3: /* Invalid page */ + /* Trap all */ + fault_data = ((lvl != 0)? 0200000: 0) | 0401000; + goto fault_bbn; + } + if ((traps & (BBN_TRP|BBN_TRP1)) == (BBN_TRP|BBN_TRP1)) { + fault_data = 04000; + goto fault_bbn; + } + } + if (uf) { + u_tlb[page] = tlb_data; + } else { + e_tlb[page] = tlb_data; + } + /* Handle traps */ + if (wr && (traps & BBN_TRPMOD)) { + fault_data = ((lvl != 0)? 0200000: 0) | 0440000; + goto fault_bbn; + } + if ((traps & BBN_TRPUSR)) { + fault_data = ((lvl != 0)? 0200000: 0) | 0420000; + goto fault_bbn; + } + if ((traps & BBN_ACC) == 0 || (traps & BBN_TRP)) { + fault_data = ((lvl != 0)? 0200000: 0) | 0410000; + goto fault_bbn; + } + /* Update CST */ + data = M[04000 + (tlb_data & 03777)]; + if ((data & 00700000000000LL) == 0) { + fault_data = 0100000 >> ((data >> 31) & 03); + goto fault_bbn; + } + data &= ~00777000000000LL; /* Clear age */ + if (wr) + data |= 00000400000000LL; /* Set modify */ + data |= pur; + M[04000 + (tlb_data & 03777)] = data; + goto access; + /* Handle fault */ +fault_bbn: + /* Write location of trap to PSB 571 */ + /* If write write MB to PSB 752 */ + /* Force APR to execute at location 70 */ + + /* Status word */ + /* RH = Effective address */ + /* Bit 17 = Exec Mode 0000001 */ + /* Bit 16 = Execute request 0000002 */ + /* Bit 15 = Write 0000004 */ + /* Bit 14 = Read 0000010 */ + /* Bit 13 = Ind 0000020 */ + /* Bit 12 = PI in progress 0000040 */ + /* Bit 11 = Key in progress 0000100 */ + /* Bit 10 = non-ex-mem 0000200 */ + /* Bit 9 = Parity 0000400 */ + /* Bit 0-8 = status */ + if ((FLAGS & USER) == 0) + fault_data |= 01; + if (fetch) + fault_data |= 02; + if (wr) + fault_data |= 04; + else + fault_data |= 010; + if (cur_context) + fault_data |= 020; + if (uuo_cycle) + fault_data |= 040; + page_fault = 1; + M[mon_base_reg | 0571] = ((uint64)fault_data) << 18 | addr; + if (wr) + M[mon_base_reg | 0572] = MB; + return 0; +} + +/* + * Read a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ +int Mem_read_bbn(int flag, int cur_context, int fetch) { + int addr; + + /* If not doing any special access, just access register */ + if (AB < 020 && ((xct_flag == 0 || fetch || cur_context || (FLAGS & USER) != 0))) { + MB = get_reg(AB); + return 0; + } + sim_interval--; + if (!page_lookup_bbn(AB, flag, &addr, 0, cur_context, fetch)) + return 1; + if (addr < 020) { + MB = get_reg(AB); + return 0; + } + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + MB = M[addr]; + return 0; +} + +/* + * Write a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ +int Mem_write_bbn(int flag, int cur_context) { + int addr; + + /* If not doing any special access, just access register */ + if (AB < 020 && ((xct_flag == 0 || cur_context || (FLAGS & USER) != 0))) { + set_reg(AB, MB); + return 0; + } + sim_interval--; + if (!page_lookup_bbn(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr < 020) { + set_reg(AB, MB); + return 0; + } + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + M[addr] = MB; + return 0; +} +#endif + +#if WAITS +int page_lookup_waits(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { + int uf = (FLAGS & USER) != 0; + + /* If this is modify instruction use write access */ + wr |= modify; + + /* Figure out if this is a user space access */ + if (flag) + uf = 0; + else if (xct_flag != 0 && !fetch && !uf) { + if (xct_flag & 010 && cur_context) /* Indirect */ + uf = 1; + if (xct_flag & 004 && wr == 0) /* XR */ + uf = 1; + if (xct_flag & 001 && (wr == 1 || BYF5)) /* XW or XLB or XDB */ + uf = 1; + } + + if (uf) { + if (addr <= Pl) { + *loc = (addr + Rl) & RMASK; + return 1; + } + if ((addr & 0400000) != 0 && (addr <= Ph)) { + if ((Pflag == 0) || (Pflag == 1 && wr == 0)) { + *loc = (addr + Rh) & RMASK; + return 1; + } + } + mem_prot = 1; + return 0; + } else { + *loc = addr; + } + return 1; +} + +int Mem_read_waits(int flag, int cur_context, int fetch) { + int addr; + + if (AB < 020 && ((xct_flag == 0 || fetch || cur_context || (FLAGS & USER) != 0))) { + MB = get_reg(AB); + return 0; + } + sim_interval--; + if (!page_lookup_waits(AB, flag, &addr, 0, cur_context, fetch)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + MB = M[addr]; + return 0; +} + +/* + * Write a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ + +int Mem_write_waits(int flag, int cur_context) { + int addr; + + + /* If not doing any special access, just access register */ + if (AB < 020 && ((xct_flag == 0 || cur_context || (FLAGS & USER) != 0))) { + set_reg(AB, MB); + return 0; + } + sim_interval--; + if (!page_lookup_waits(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + M[addr] = MB; + return 0; +} +#endif + +int page_lookup_ka(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { + if (!flag && (FLAGS & USER) != 0) { + if (addr <= Pl) { + *loc = (addr + Rl) & RMASK; + return 1; + } + if (cpu_unit[0].flags & UNIT_TWOSEG && + (addr & 0400000) != 0 && (addr <= Ph)) { + if ((Pflag == 0) || (Pflag == 1 && wr == 0)) { + *loc = (addr + Rh) & RMASK; + return 1; + } + } + mem_prot = 1; + return 0; + } else { + *loc = addr; + } + return 1; +} + +int Mem_read_ka(int flag, int cur_context, int fetch) { + int addr; + + if (AB < 020) { + MB = get_reg(AB); + } else { + sim_interval--; + if (!page_lookup_ka(AB, flag, &addr, 0, cur_context, fetch)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + MB = M[addr]; + } + return 0; +} + +/* + * Write a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ + +int Mem_write_ka(int flag, int cur_context) { + int addr; + + if (AB < 020) { + set_reg(AB, MB); + } else { + sim_interval--; + if (!page_lookup_ka(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + M[addr] = MB; + } + return 0; +} + +int (*Mem_read)(int flag, int cur_context, int fetch); +int (*Mem_write)(int flag, int cur_context); +#endif + +#if PDP6 +/* + * Check if the last operation caused a APR IRQ to be generated. + */ +void check_apr_irq() { + if (pi_enable && apr_irq) { + int flg = 0; + clr_interrupt(0); + flg |= ((FLAGS & OVR) != 0) & ov_irq; + flg |= ((FLAGS & PCHNG) != 0) & pcchg_irq; + flg |= nxm_flag | mem_prot | push_ovf; + if (flg) + set_interrupt(0, apr_irq); + } +} + +/* + * APR Device for PDP6. + */ +t_stat dev_apr(uint32 dev, uint64 *data) { + uint64 res = 0; + switch(dev & 03) { + case CONI: + /* Read trap conditions */ + res = apr_irq | (((FLAGS & OVR) != 0) << 3) | (ov_irq << 4) ; + res |= (((FLAGS & PCHNG) != 0) << 6) | (pcchg_irq << 7) ; + res |= (clk_flg << 9) | (((uint64)clk_en) << 10) | (nxm_flag << 12); + res |= (mem_prot << 13) | (((FLAGS & USER) != 0) << 14) | (user_io << 15); + res |= (push_ovf << 16); + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI APR %012llo\n", *data); + break; + + case CONO: + /* Set trap conditions */ + res = *data; + clk_irq = apr_irq = res & 07; + clr_interrupt(0); + if (res & 010) /* Bit 32 */ + FLAGS &= ~OVR; + if (res & 020) /* Bit 31 */ + ov_irq = 1; + if (res & 040) /* Bit 30 */ + ov_irq = 0; + if (res & 0100) /* Bit 29 */ + FLAGS &= ~PCHNG; + if (res & 0200) /* Bit 28 */ + pcchg_irq = 1; + if (res & 0400) /* Bit 27 */ + pcchg_irq = 0; + if (res & 0001000) { /* Bit 26 */ + clk_flg = 0; + clr_interrupt(4); + } + if (res & 0002000) { /* Bit 25 */ + clk_en = 1; + if (clk_flg) + set_interrupt(4, clk_irq); + } + if (res & 0004000) { /* Bit 24 */ + clk_en = 0; + clr_interrupt(4); + } + if (res & 010000) /* Bit 23 */ + nxm_flag = 0; + if (res & 020000) /* Bit 22 */ + mem_prot = 0; + if (res & 040000) /* Bit 21 */ + user_io = 0; + if (res & 0100000) /* Bit 20 */ + user_io = 1; + if (res & 0200000) { /* Bit 19 */ + reset_all(1); + mem_prot = 0; + user_io = 0; + FLAGS &= ~(USERIO); + } + if (res & 0400000) /* Bit 18 */ + push_ovf = 0; + check_apr_irq(); + sim_debug(DEBUG_CONO, &cpu_dev, "CONO APR %012llo\n", *data); + break; + + case DATAO: + /* Set protection registers */ + Rl = 0776000 & *data; + Pl = (0776000 & (*data >> 18)) + 01777; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAO APR %012llo\n", *data); + break; + + case DATAI: + /* Read switches */ + *data = SW; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI APR %012llo\n", *data); + break; + } + return SCPE_OK; +} + +#define get_reg(reg) FM[(reg) & 017] +#define set_reg(reg, value) FM[(reg) & 017] = value + +int page_lookup(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { + if (!flag && (FLAGS & USER) != 0) { + if (addr <= Pl) { + *loc = (addr + Rl) & RMASK; + return 1; + } + mem_prot = 1; + return 0; + } else { + *loc = addr; + } + return 1; +} + +int Mem_read(int flag, int cur_context, int fetch) { + int addr; + + if (AB < 020) { + MB = get_reg(AB); + } else { + sim_interval--; + if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + MB = M[addr]; + } + return 0; +} + +/* + * Write a location in memory. + * + * Return of 0 if successful, 1 if there was an error. + */ + +int Mem_write(int flag, int cur_context) { + int addr; + + if (AB < 020) { + set_reg(AB, MB); + } else { + sim_interval--; + if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr >= (int)MEMSIZE) { + nxm_flag = 1; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + M[addr] = MB; + } + return 0; +} +#endif + +/* + * Function to determine number of leading zero bits in a work + */ +int nlzero(uint64 w) { + int n = 0; + if (w == 0) return 36; + if ((w & 00777777000000LL) == 0) { n += 18; w <<= 18; } + if ((w & 00777000000000LL) == 0) { n += 9; w <<= 9; } + if ((w & 00770000000000LL) == 0) { n += 6; w <<= 6; } + if ((w & 00700000000000LL) == 0) { n += 3; w <<= 3; } + if ((w & 00600000000000LL) == 0) { n ++; w <<= 1; } + if ((w & 00400000000000LL) == 0) { n ++; } + return n; +} + +t_stat sim_instr (void) +{ +t_stat reason; +int i_flags; /* Instruction mode flags */ +int pi_rq; /* Interrupt request */ +int pi_ov; /* Overflow during PI cycle */ +int pi_cycle; /* Executing an interrupt */ +int ind; /* Indirect bit */ +int f_load_pc; /* Load AB from PC at start of instruction */ +int f_inst_fetch; /* Fetch new instruction */ +int f_pc_inh; /* Inhibit PC increment after instruction */ +int nrf; /* Normalize flag */ +int fxu_hold_set; /* Negitive exponent */ +int sac_inh; /* Inihibit saving AC after instruction */ +int f; /* Temporary variables */ +int flag1; +int flag3; +int instr_count = 0; /* Number of instructions to execute */ +uint32 IA; +#if ITS +char one_p_arm = 0; /* One proceed arm */ +#endif + +if (sim_step != 0) { + instr_count = sim_step; + sim_cancel_step(); +} + +/* Build device table */ +if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ + return reason; + + +/* Main instruction fetch/decode loop: check clock queue, intr, trap, bkpt */ + f_load_pc = 1; + f_inst_fetch = 1; + ind = 0; + uuo_cycle = 0; + pi_cycle = 0; + pi_rq = 0; + pi_ov = 0; + BYF5 = 0; +#if KI | KL + page_fault = 0; +#endif +#if ITS + if (QITS) { + one_p_arm = 0; + set_quantum(); + } +#endif + watch_stop = 0; + + while ( reason == 0) { /* loop until ABORT */ + if (sim_interval <= 0) { /* check clock queue */ + if ((reason = sim_process_event()) != SCPE_OK) {/* error? stop sim */ +#if ITS + if (QITS) + load_quantum(); +#endif + return reason; + } + } + + if (sim_brk_summ && f_load_pc && sim_brk_test(PC, SWMASK('E'))) { + reason = STOP_IBKPT; + break; + } + + if (watch_stop) { + reason = STOP_IBKPT; + break; + } + + check_apr_irq(); + /* Normal instruction */ + if (f_load_pc) { + modify = 0; + xct_flag = 0; +#if KI | KL + trap_flag = 0; +#endif + AB = PC; + uuo_cycle = 0; + f_pc_inh = 0; + } + + if (f_inst_fetch) { +#if !(KI | KL) +fetch: +#endif +#if ITS + if (QITS && pi_cycle == 0 && mem_prot == 0) { + opc = PC | (FLAGS << 18); + if ((FLAGS & ONEP) != 0) { + one_p_arm = 1; + FLAGS &= ~ONEP; + } + } +#endif + + if (Mem_read(pi_cycle | uuo_cycle, 1, 1)) { + pi_rq = check_irq_level(); + if (pi_rq) + goto st_pi; + goto last; + } + +no_fetch: + IR = (MB >> 27) & 0777; + AC = (MB >> 23) & 017; + AD = MB; /* Save for historical sake */ + IA = AB; + i_flags = opflags[IR]; + BYF5 = 0; + } + +#if KI | KL + /* Handle page fault and traps */ + if (page_enable && trap_flag == 0 && (FLAGS & (TRP1|TRP2))) { + AB = 0420 + ((FLAGS & (TRP1|TRP2)) >> 2); + trap_flag = FLAGS & (TRP1|TRP2); + FLAGS &= ~(TRP1|TRP2); + pi_cycle = 1; + AB += (FLAGS & USER) ? ub_ptr : eb_ptr; + Mem_read_nopage(); + goto no_fetch; + } +#endif + + + /* Handle indirection repeat until no longer indirect */ + do { + if ((!pi_cycle) & pi_pending +#if KI | KL + & (!trap_flag) +#endif + ) { + pi_rq = check_irq_level(); + } + ind = (MB & 020000000) != 0; + AR = MB; + AB = MB & RMASK; + if (MB & 017000000) { + AR = MB = (AB + get_reg((MB >> 18) & 017)) & FMASK; + AB = MB & RMASK; + } + if (IR != 0254) + AR &= RMASK; + if (ind & !pi_rq) + if (Mem_read(pi_cycle | uuo_cycle, 1, 0)) + goto last; + /* Handle events during a indirect loop */ + if (sim_interval-- <= 0) { + if ((reason = sim_process_event()) != SCPE_OK) { + return reason; + } + } + } while (ind & !pi_rq); + + + /* If there is a interrupt handle it. */ + if (pi_rq) { +st_pi: + sim_debug(DEBUG_IRQ, &cpu_dev, "trap irq %o %03o %03o \n", + pi_enc, PIR, PIH); + pi_cycle = 1; + pi_rq = 0; + pi_hold = 0; + pi_ov = 0; + AB = 040 | (pi_enc << 1) | maoff; +#if KI | KL + xct_flag = 0; + /* + * Scan through the devices and allow KI devices to have first + * hit at a given level. + */ + for (f = 0; f < 128; f++) { + if (dev_irqv[f] != 0 && dev_irq[f] & (0200 >> pi_enc)) { + AB = dev_irqv[f](f << 2, AB); + break; + } + } + AB |= eb_ptr; + Mem_read_nopage(); + goto no_fetch; +#else + goto fetch; +#endif + } + + +#if KI | KL + if (page_enable && page_fault) { + if (!f_pc_inh && !pi_cycle) + PC = (PC + 1) & RMASK; + goto last; + } +#endif + + /* Check if possible idle loop */ + if (sim_idle_enab && (FLAGS & USER) != 0 && PC < 020 && AB < 020 && + (IR & 0760) == 0340) { + sim_idle (TMR_RTC, FALSE); + } + + /* Update history */ + if (hst_lnt) { + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) { + hst_p = 0; + } + hst[hst_p].pc = HIST_PC | ((BYF5)? (HIST_PC2|PC) : IA); + hst[hst_p].ea = AB; + hst[hst_p].ir = AD; + hst[hst_p].flags = (FLAGS << 5) |(clk_flg << 2) | (nxm_flag << 1) +#if KA | PDP6 + | (mem_prot << 4) | (push_ovf << 3) +#endif +#if PDP6 + | ill_op +#endif + ; + hst[hst_p].ac = get_reg(AC); + } + + + /* Set up to execute instruction */ + f_inst_fetch = 1; + f_load_pc = 1; + nrf = 0; + fxu_hold_set = 0; + sac_inh = 0; + modify = 0; + f_pc_inh = 0; + + /* Load pseudo registers based on flags */ + if (i_flags & (FCEPSE|FCE)) { + if (i_flags & FCEPSE) + modify = 1; + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + } + + if (i_flags & FAC) { + BR = AR; + AR = get_reg(AC); + } + + if (i_flags & FBR) { + BR = get_reg(AC); + } + + if (hst_lnt) { + hst[hst_p].mb = AR; + } + + if (i_flags & FAC2) { + MQ = get_reg(AC + 1); + } else if (!BYF5) { + MQ = 0; + } + + if (i_flags & SWAR) { + AR = SWAP_AR; + } + + /* Process the instruction */ + switch (IR) { +muuo: + case 0000: /* UUO */ + case 0040: case 0041: case 0042: case 0043: + case 0044: case 0045: case 0046: case 0047: + case 0050: case 0051: case 0052: case 0053: + case 0054: case 0055: case 0056: case 0057: + case 0060: case 0061: case 0062: case 0063: + case 0064: case 0065: case 0066: case 0067: + case 0070: case 0071: case 0072: case 0073: + case 0074: case 0075: case 0076: case 0077: + + /* MUUO */ + +#if KI | KL + case 0100: case 0101: case 0102: case 0103: + case 0104: case 0105: case 0106: case 0107: + case 0123: + case 0247: /* UUO */ +unasign: + MB = ((uint64)(IR) << 27) | ((uint64)(AC) << 23) | (uint64)(AB); + AB = ub_ptr | 0424; + Mem_write_nopage(); + AB |= 1; + MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + (trap_flag == 0)) & RMASK); + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + } + Mem_write_nopage(); + FLAGS &= ~ (PRV_PUB|BYTI|ADRFLT|TRP1|TRP2); + AB = ub_ptr | 0430; + if (trap_flag != 0) + AB |= 1; + if (FLAGS & PUBLIC) + AB |= 2; + if (FLAGS & USER) + AB |= 4; + Mem_read_nopage(); + FLAGS = (MB >> 23) & 017777; + /* If transistioning from user to executive adjust flags */ + if ((FLAGS & USER) != 0 && (AB & 4) != 0) + FLAGS |= USERIO; + if ((FLAGS & USER) == 0 && (AB & 2 || (FLAGS & OVR) != 0)) + FLAGS |= PRV_PUB|OVR; + PC = MB & RMASK; + f_pc_inh = 1; + break; +#else + uuo_cycle = 1; +#endif + + /* LUUO */ + case 0001: case 0002: case 0003: + case 0004: case 0005: case 0006: case 0007: + case 0010: case 0011: case 0012: case 0013: + case 0014: case 0015: case 0016: case 0017: + case 0020: case 0021: case 0022: case 0023: + case 0024: case 0025: case 0026: case 0027: + case 0030: case 0031: case 0032: case 0033: + case 0034: case 0035: case 0036: case 0037: +#if PDP6 + ill_op = 1; + ex_uuo_sync = 1; +#endif + MB = ((uint64)(IR) << 27) | ((uint64)(AC) << 23) | (uint64)(AB); +#if KI | KL + if ((FLAGS & USER) == 0) { + AB = eb_ptr + 040; + Mem_write_nopage(); + AB += 1; + Mem_read_nopage(); + uuo_cycle = 1; + goto no_fetch; + } +#endif + AB = 040; + if (maoff && uuo_cycle) + AB |= maoff; + Mem_write(uuo_cycle, 1); + AB += 1; + f_load_pc = 0; +#if ITS + if (QITS && one_p_arm) { + FLAGS |= ONEP; + one_p_arm = 0; + } +#endif + f_pc_inh = 1; + break; + +#if KI | KL + case 0110: /* DFAD */ + case 0111: /* DFSB */ + /* On Load AR,MQ has memory operand */ + /* AR,MQ = AC BR,MB = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + BR = AR; + AR = get_reg(AC); + MQ = get_reg(AC + 1); + + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + /* Make into 64 bit numbers */ + SC = GET_EXPO(BR); + SMEAR_SIGN(BR); + BR <<= 35; + BR |= (MB & CMASK); + FE = GET_EXPO(AR); + SMEAR_SIGN(AR); + AR <<= 35; + AR |= (MQ & CMASK); + if (IR & 01) { + BR = (FPFMASK ^ BR) + 1; + } + SCAD = (SC - 200) + (FE - 200); + if (FE > SC) { /* Swap if BR larger */ + AD = AR; + AR = BR; + BR = AD; + SCAD = FE; + FE = SC; + SC = SCAD; + } + SCAD = SC - FE; + flag3 = 0; + MQ = 0; + if (SCAD > 0) { /* Align numbers */ + if (SCAD > 64) /* Outside range */ + AR = 0; + else { + while (SCAD > 0) { + MQ >>= 1; + if (AR & 1) + MQ |= SMASK; + AR = (AR & (FPHBIT|FPSBIT)) | (AR >> 1); + SCAD--; + } + } + } + AR = AR + BR + flag3; + /* Set flag1 to sign */ + flag1 = (AR & FPHBIT) != 0; +dpnorm: + /* Make sure High bit and sign bit same */ + while (((AR & FPHBIT) != 0) != ((AR & FPSBIT) != 0)) { + SC += 1; + MQ >>= 1; + if (AR & 1) + MQ |= SMASK; + AR = (AR & FPHBIT) | (AR >> 1); + } + + /* Check for potiential underflow */ + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) + fxu_hold_set = 1; + if (AR != 0) { + while (AR != 0 && + (((AR & (FPSBIT|FPNBIT)) == (FPSBIT|FPNBIT)) || + ((AR & (FPSBIT|FPNBIT)) == 0))) { + SC --; + AR <<= 1; + if (MQ & SMASK) + AR |= 1; + MQ <<= 1; + } + /* Handle special minus case */ + if (AR == (FPHBIT|FPSBIT)) { + SC += 1; + AR = (AR & FPHBIT) | (AR >> 1); + } + } else { + AR = MQ = 0; + SC = 0; + } + + /* Check if we need to round */ + if (!nrf && ((MQ & SMASK) != 0) && (((AR & FPSBIT) == 0) || + (((AR & FPSBIT) != 0) && ((MQ & 0377700000000LL) != 0)))) { + AR++; + nrf = 1; + /* Clean things up if we overflowed */ + if ((AR & FPHBIT) == 0) + goto dpnorm; + } + /* Extract result */ + MQ = (AR & CMASK); + AR >>= 35; + AR &= MMASK; + if (flag1) /* Append sign */ + AR |= SMASK; + /* Check for over/under flow */ + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (!fxu_hold_set) { + FLAGS |= FLTUND; + } + } + /* Add exponent */ + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR &= SMASK|MMASK; + if (AR != 0 || MQ != 0) + AR |= ((uint64)(SCAD & 0377)) << 27; + + set_reg(AC, AR); + set_reg(AC+1, MQ); + break; + + case 0112: /* DFMP */ + /* On Load AR,MQ has memory operand */ + /* AR,MQ = AC BR,MB = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + BR = AR; + AR = get_reg(AC); + MQ = get_reg(AC + 1); + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + /* Make into 64 bit numbers */ + SC = GET_EXPO(AR); + SMEAR_SIGN(AR); + AR <<= 35; + AR |= (MQ & CMASK); + FE = GET_EXPO(BR); + SMEAR_SIGN(BR); + BR <<= 35; + BR |= MB & CMASK; + flag1 = 0; + /* Make both numbers positive */ + if (AR & FPSBIT) { + AR = (FPFMASK ^ AR) + 1; + flag1 = 1; + } + if (BR & FPSBIT) { + BR = (FPFMASK ^ BR) + 1; + flag1 = !flag1; + } + /* Compute exponent */ + SC = SC + FE - 0200; + ARX = 0; + /* Do multiply */ + for (FE = 0; FE < 62; FE++) { + if (FE == 35) /* Clear MQ so it has correct lower product digits */ + MQ = 0; + if (BR & 1) + ARX += AR; + MQ >>= 1; + if (ARX & 1) + MQ |= BIT1; + ARX >>= 1; + BR >>= 1; + } + AR = ARX; + /* Make result negative if needed */ + if (flag1) { + MQ = (MQ ^ CMASK) + 0400; + AR = (AR ^ FPFMASK); + if (MQ & SMASK) { + AR ++; + MQ &= FMASK; + } + /* Check for overflow */ + if ((AR & (FPHBIT|FPSBIT)) == (FPHBIT)) { + SC += 1; + MQ >>= 1; + if (AR & 1) + MQ |= BIT1; + AR = (AR >> 1) | (FPHBIT & AR); + } + } + /* Check if we need to normalize */ + if (AR != 0) { + /* Check for fast shift */ + if ((AR & ~MMASK) == 0 || ((AR & ~MMASK) + BIT8) == 0) { + SC -= 35; + AR <<= 35; + AR |= MQ & CMASK; + MQ = 0; + if ((AR & 0777) == 0777) + AR &= (FPFMASK << 8); + } + if (((AR & (FPSBIT|FPNBIT)) == (FPSBIT|FPNBIT)) || + ((AR & (FPSBIT|FPNBIT)) == 0)) { + SC --; + AR <<= 1; + if (MQ & BIT1) + AR |= 1; + MQ <<= 1; + MQ &= FMASK; + nrf = 1; + } + } else { + AR = MQ = 0; + SC = 0; + flag1 = 0; + } + /* Round if needed */ + if (MQ & BIT1) + AR++; + /* Build results */ + MQ = (AR & CMASK); + AR >>= 35; + AR &= MMASK; + if (flag1) + AR |= SMASK; + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (SC < 0) { + FLAGS |= FLTUND; + } + } + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR &= SMASK|MMASK; + if (AR != 0 || MQ != 0) + AR |= ((uint64)(SCAD & 0377)) << 27; + + set_reg(AC, AR); + set_reg(AC+1, MQ); + break; + + case 0113: /* DFDV */ + /* On Load AR,MQ has memory operand */ + /* AR,MQ = AC BR,MB = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + BR = AR; + AR = get_reg(AC); + MQ = get_reg(AC + 1); + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + /* Make into 64 bit numbers */ + SC = GET_EXPO(AR); + SMEAR_SIGN(AR); + AR <<= 35; + AR |= (MQ & CMASK); + FE = GET_EXPO(BR); + SMEAR_SIGN(BR); + BR <<= 35; + BR |= MB & CMASK; + /* Make both positive */ + flag1 = 0; + if (AR & FPSBIT) { + AR = (FPFMASK ^ AR) + 1; + flag1 = 1; + } + if (BR & FPSBIT) { + BR = (FPFMASK ^ BR) + 1; + flag1 = !flag1; + } + /* Precheck if divide ok */ + if (AR >= (BR << 1)) { + if (!pi_cycle) + FLAGS |= OVR|FLTOVR|NODIV|TRP1; + AR = 0; /* For clean history */ + sac_inh = 1; + break; + } + /* Divide by zero */ + if (AR == 0) { + sac_inh = 1; + break; + } + /* Compute exponents */ + SC = SC - FE + 0201; + /* Precheck divider */ + if (AR < BR) { + AR <<= 1; + SC--; + } + if (SC < 0 && !pi_cycle) + FLAGS |= FLTUND|OVR|FLTOVR|TRP1; + /* Do divide */ + AD = 0; + for (FE = 0; FE < 62; FE++) { + AD <<= 1; + if (AR >= BR) { + AR = AR - BR; + AD |= 1; + } + AR <<= 1; + } + AR = AD; + if (flag1) { + AR = (AR ^ FPFMASK) + 1; + } + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0) || SC == 0600) + fxu_hold_set = 1; + while (((AR & FPHBIT) != 0) != ((AR & FPSBIT) != 0)) { + SC += 1; + AR = (AR & FPHBIT) | (AR >> 1); + } + MQ = (AR & CMASK); + AR >>= 35; + AR &= MMASK; + if (flag1) + AR |= SMASK; + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (!fxu_hold_set) { + FLAGS |= FLTUND; + } + } + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR &= SMASK|MMASK; + if (AR != 0 || MQ != 0) + AR |= ((uint64)(SCAD & 0377)) << 27; + + set_reg(AC, AR); + set_reg(AC+1, MQ); + break; + + case 0114: /* DADD */ + case 0115: /* DSUB */ + case 0116: /* DMUL */ + case 0117: /* DDIV */ + goto unasign; + + case 0120: /* DMOVE */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + MQ = MB; + set_reg(AC, AR); + set_reg(AC+1, MQ); + break; + + case 0121: /* DMOVN */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + MQ = ((MB & CMASK) ^ CMASK) + 1; /* Low */ + /* High */ + AR = (CM(AR) + ((MQ & SMASK) != 0)) & FMASK; + MQ &= CMASK; + set_reg(AC, AR); + set_reg(AC+1, MQ); + break; + + case 0124: /* DMOVEM */ + AR = get_reg(AC); + /* Handle each half as seperate instruction */ + if ((FLAGS & BYTI) == 0) { + MB = AR; + if (Mem_write(0, 0)) + goto last; + FLAGS |= BYTI; + } + MQ = get_reg(AC + 1); + if ((FLAGS & BYTI)) { + AB = (AB + 1) & RMASK; + MB = MQ; + if (Mem_write(0, 0)) + goto last; + FLAGS &= ~BYTI; + } + break; + + case 0125: /* DMOVNM */ + AR = get_reg(AC); + MQ = get_reg(AC + 1); + /* Handle each half as seperate instruction */ + if ((FLAGS & BYTI) == 0) { + BR = AR = CM(AR); + BR = (BR + 1); + MQ = (((MQ & CMASK) ^ CMASK) + 1); + if (MQ & SMASK) + AR = BR; + AR &= FMASK; + MB = AR; + if (Mem_write(0, 0)) + goto last; + FLAGS |= BYTI; + } + if ((FLAGS & BYTI)) { + MQ = get_reg(AC + 1); + MQ = (CM(MQ) + 1) & CMASK; + AB = (AB + 1) & RMASK; + MB = MQ; + if (Mem_write(0, 0)) + goto last; + FLAGS &= ~BYTI; + } + break; + + case 0122: /* FIX */ + case 0126: /* FIXR */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + MQ = 0; + SC = ((((AR & SMASK) ? 0377 : 0 ) + ^ ((AR >> 27) & 0377)) + 0600) & 0777; + SMEAR_SIGN(AR); + SC -= 27; + SC &= 0777; + if (SC < 9) { + /* 0 < N < 8 */ + AR = (AR << SC) & FMASK; + } else if ((SC & 0400) != 0) { + /* -27 < N < 0 */ + SC = 01000 - SC; + if (SC > 27) { + AR = MQ = 0; + } else { + MQ = (AR << (36 - SC)) & FMASK /*- flag1*/ ; + AR = (AR >> SC) | (FMASK & (((AR & SMASK)? FMASK << (27 - SC): 0))); + } + if (((IR & 04) != 0 && (MQ & SMASK) != 0) || + ((IR & 04) == 0 && (AR & SMASK) != 0 && + ((MQ & CMASK) != 0 || (MQ & SMASK) != 0))) + AR ++; + } else { + if (!pi_cycle) + FLAGS |= OVR|TRP1; /* OV & T1 */ + sac_inh = 1; + } + if (!sac_inh) + set_reg(AC, AR); + break; + + case 0127: /* FLTR */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + AR <<= 27; + if (AR & FPSBIT) { + flag1 = 1; + AR |= FPHBIT; + } else + flag1 = 0; + i_flags = SAC; + SC = 162; + goto fnorm; +#else + case 0100: /* TENEX UMOVE */ +#if BBN + if (QBBN) { + if (Mem_read(0, 0, 0)) { + IR = 0; + goto last; + } + AR = MB; + set_reg(AC, AR); /* blank, I, B */ + IR = 0; + break; + } +#endif + goto unasign; + case 0101: /* TENEX UMOVEI */ +#if BBN + if (QBBN) { + set_reg(AC, AR); /* blank, I, B */ + IR = 0; + break; + } +#endif + goto unasign; + case 0102: /* TENEX UMOVEM */ /* ITS LPM */ +#if ITS + if (QITS && (FLAGS & USER) == 0) { + /* Load store ITS pager info */ + /* AC & 1 = Store */ + if (AC & 1) { + if ((AB + 8) > MEMSIZE) { + fault_data |= 0400; + mem_prot = 1; + break; + } + MB = ((uint64)age) << 27 | + ((uint64)fault_addr & 0777) << 18 | + (uint64)jpc; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = opc; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = (mar & 00777607777777LL) | ((uint64)pag_reload) << 21; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = ((uint64)get_quantum()) | ((uint64)fault_data) << 18; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = ((uint64)fault_addr & 00760000) << 13 | + (uint64)dbr1; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = ((uint64)fault_addr & 00037000) << 17 | + (uint64)dbr2; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = (uint64)dbr3; + M[AB] = MB; + AB = (AB + 1) & RMASK; + MB = (uint64)ac_stack; + M[AB] = MB; + } else { + if ((AB + 8) > MEMSIZE) { + fault_data |= 0400; + mem_prot = 1; + break; + } + MB = M[AB]; /* WD 0 */ + age = (MB >> 27) & 017; + jpc = (MB & RMASK); + fault_addr = (MB >> 18) & 0777; + AB = (AB + 1) & RMASK; + MB = M[AB]; + opc = MB; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 2 */ + mar = /*03777777 &*/ MB; + pag_reload = 0; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 3 */ + /* Store Quantum */ + qua_time = MB & RMASK; + set_quantum(); + fault_data = (MB >> 18) & RMASK; + mem_prot = 0; + if ((fault_data & 0777772) != 0) + mem_prot = 1; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 4 */ + dbr1 = ((0377 << 18) | RMASK) & MB; + fault_addr |= (MB >> 13) & 00760000; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 5 */ + fault_addr |= (MB >> 17) & 00037000; + dbr2 = ((0377 << 18) | RMASK) & MB; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 6 */ + dbr3 = ((0377 << 18) | RMASK) & MB; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 7 */ + ac_stack = (uint32)MB; + page_enable = 1; + } + /* AC & 2 = Clear TLB */ + if (AC & 2) { + for (f = 0; f < 512; f++) + e_tlb[f] = u_tlb[f] = 0; + mem_prot = 0; + } + /* AC & 4 = Set Prot Interrupt */ + if (AC & 4) { + mem_prot = 1; + set_interrupt(0, apr_irq); + } + break; + } +#endif +#if BBN + if (QBBN) { + AR = get_reg(AC); + MB = AR; + if (Mem_write(0, 0)) { + IR = 0; + goto last; + } + IR = 0; + break; + } +#endif + goto unasign; + + case 0103: /* TENEX UMOVES */ /* ITS XCTR */ +#if ITS + if (QITS && (FLAGS & USER) == 0) { + /* AC & 1 = Read User */ + /* AC & 2 = Write User */ + /* AC & 4 = Inhibit mem protect, skip */ + /* AC & 8 = ??? */ + f_load_pc = 0; + f_pc_inh = 1; + xct_flag = AC; + break; + } +#endif +#if BBN + if (QBBN) { + if (Mem_read(0, 0, 0)) { + IR = 0; + goto last; + } + modify = 1; + AR = MB; + if (Mem_write(0, 0)) { + IR = 0; + goto last; + } + if (AC != 0) + set_reg(AC, AR); /* blank, I, B */ + IR = 0; + break; + } +#endif + goto unasign; + + /* MUUO */ + case 0104: /* TENEX JSYS */ +#if BBN + if (QBBN) { + BR = ((uint64)(FLAGS) << 23) | ((PC + !pi_cycle) & RMASK); + if (AB < 01000) { + AB += 01000; + if ((FLAGS & USER) == 0) + FLAGS |= EXJSYS; + FLAGS &= ~USER; + } + if (Mem_read(0, 0, 0)) { + FLAGS = (uint32)(BR >> 23); /* On error restore flags */ + goto last; + } + AR = MB; + AB = (AR >> 18) & RMASK; + MB = BR; + if (Mem_write(0, 0)) { + FLAGS = (uint32)(BR >> 23); /* On error restore flags */ + goto last; + } + PC = AR & RMASK; + break; + } +#endif + goto unasign; + + case 0247: /* UUO or ITS CIRC instruction */ +#if ITS + if (QITS) { + BR = AR; + AR = get_reg(AC); + if (hst_lnt) { + hst[hst_p].mb = AR; + } + MQ = get_reg(AC + 1); + SC = ((AB & RSIGN) ? (0777 ^ AB) + 1 : AB) & 0777; + if (SC == 0) + break; + SC = SC % 72; + if (AB & RSIGN) + SC = 72 - SC; + /* Have to do this the long way */ + while (SC > 0) { + AD = ((AR << 1) | (MQ & 1)) & FMASK; + MQ = ((MQ >> 1) | (AR & SMASK)) & FMASK; + AR = AD; + SC--; + } + set_reg(AC, AR); + set_reg(AC+1, MQ); + break; + } +#endif +#if WAITS + if (QWAITS) { /* WAITS FIX instruction */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + BR = get_reg(AC); + if (hst_lnt) { + hst[hst_p].mb = AR; + } + MQ = 0; + AR = SWAP_AR; + goto ufa; + } +#endif + + /* UUO */ + case 0105: case 0106: case 0107: + case 0110: case 0111: case 0112: case 0113: + case 0114: case 0115: case 0116: case 0117: + case 0120: case 0121: case 0122: case 0123: + case 0124: case 0125: case 0126: case 0127: +#if PDP6 + case 0130: /* UFA */ +#endif + +unasign: +#if !PDP6 + MB = ((uint64)(IR) << 27) | ((uint64)(AC) << 23) | (uint64)(AB); + AB = 060 | maoff; + uuo_cycle = 1; + Mem_write(uuo_cycle, 0); + AB += 1; +#if ITS + if (QITS && one_p_arm) { + FLAGS |= ONEP; + one_p_arm = 0; + } +#endif + f_load_pc = 0; +#endif + break; +#endif + + case 0133: /* IBP/ADJBP */ + case 0134: /* ILDB */ + case 0136: /* IDPB */ + if ((FLAGS & BYTI) == 0) { /* BYF6 */ + modify = 1; + if (Mem_read(0, !QITS, 0)) { +#if PDP6 + FLAGS |= BYTI; +#endif + goto last; + } + AR = MB; + SC = (AR >> 24) & 077; + SCAD = (((AR >> 30) & 077) + (0777 ^ SC) + 1) & 0777; + if (SCAD & 0400) { + SC = ((0777 ^ ((AR >> 24) & 077)) + 044 + 1) & 0777; +#if KI | KL + AR = (AR & LMASK) | ((AR + 1) & RMASK); +#else + AR = (AR + 1) & FMASK; +#endif + } else + SC = SCAD; + AR &= PMASK; + AR |= (uint64)(SC & 077) << 30; + MB = AR; + if (Mem_write(0, !QITS)) + goto last; + if ((IR & 04) == 0) + break; + goto ldb_ptr; + } + /* Fall through */ + + case 0135:/* LDB */ + case 0137:/* DPB */ + if ((FLAGS & BYTI) == 0 || !BYF5) { + if (Mem_read(0, !QITS, 0)) + goto last; + AR = MB; +ldb_ptr: + SC = (AR >> 30) & 077; + MQ = (uint64)(1) << ( 077 & (AR >> 24)); + MQ -= 1; + f_load_pc = 0; + f_inst_fetch = 0; + f_pc_inh = 1; + FLAGS |= BYTI; + BYF5 = 1; +#if ITS + if (QITS && pi_cycle == 0 && mem_prot == 0) { + opc = PC | (FLAGS << 18); + } +#endif + } else { + AB = AR & RMASK; + if ((IR & 06) == 6) + modify = 1; + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + if ((IR & 06) == 4) { + AR = AR >> SC; + AR &= MQ; + set_reg(AC, AR); + } else { + BR = get_reg(AC); + BR = BR << SC; + MQ = MQ << SC; + AR &= CM(MQ); + AR |= BR & MQ; + MB = AR & FMASK; + Mem_write(0, 0); + } + FLAGS &= ~BYTI; + BYF5 = 0; + } + break; + + case 0131:/* DFN */ +#if !PDP6 + AD = (CM(BR) + 1) & FMASK; + SC = (BR >> 27) & 0777; + BR = AR; + AR = AD; + AD = (CM(BR) + ((AD & MANT) == 0)) & FMASK; + AR &= MANT; + AR |= ((uint64)(SC & 0777)) << 27; + BR = AR; + AR = AD; + MB = BR; + set_reg(AC, AR); + if (Mem_write(0, 0)) + goto last; +#endif + break; + + case 0132:/* FSC */ + SC = ((AB & RSIGN) ? 0400 : 0) | (AB & 0377); + SCAD = GET_EXPO(AR); + SC = (SCAD + SC) & 0777; + + flag1 = 0; + if (AR & SMASK) + flag1 = 1; +#if !PDP6 + SMEAR_SIGN(AR); + AR <<= 34; + goto fnorm; +#else + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) + fxu_hold_set = 1; + if ((SC & 0400) != 0 && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (!fxu_hold_set) + FLAGS |= FLTUND; + check_apr_irq(); + } + if (flag1) { + SC ^= 0377; + } else if (AR == 0) + SC = 0; + AR &= SMASK|MMASK; + AR |= ((uint64)((SC) & 0377)) << 27; + break; +#endif + + + case 0150: /* FSB */ + case 0151: /* FSBL */ + case 0152: /* FSBM */ + case 0153: /* FSBB */ + case 0154: /* FSBR */ + case 0155: /* FSBRI, FSBRL on PDP6 */ + case 0156: /* FSBRM */ + case 0157: /* FSBRB */ + AD = (CM(AR) + 1) & FMASK; + AR = BR; + BR = AD; + /* Fall through */ + +#if !PDP6 + case 0130: /* UFA */ +#endif +#if WAITS +ufa: +#endif + case 0140: /* FAD */ + case 0141: /* FADL */ + case 0142: /* FADM */ + case 0143: /* FADB */ + case 0144: /* FADR */ + case 0145: /* FADRI, FSBRL on PDP6 */ + case 0146: /* FADRM */ + case 0147: /* FADRB */ + flag3 = 0; + SC = ((BR >> 27) & 0777); + if ((BR & SMASK) == (AR & SMASK)) { + SCAD = SC + (((AR >> 27) & 0777) ^ 0777) + 1; + } else { + SCAD = SC + ((AR >> 27) & 0777); + } + SCAD &= 0777; + if (((BR & SMASK) != 0) == ((SCAD & 0400) != 0)) { + AD = AR; + AR = BR; + BR = AD; + } + if ((SCAD & 0400) == 0) { + if ((AR & SMASK) == (BR & SMASK)) + SCAD = ((SCAD ^ 0777) + 1) & 0777; + else + SCAD = (SCAD ^ 0777); + } else { + if ((AR & SMASK) != (BR & SMASK)) + SCAD = (SCAD + 1) & 0777; + } + + /* Get exponent */ + SC = GET_EXPO(AR); + /* Smear the signs */ + SMEAR_SIGN(AR); + SMEAR_SIGN(BR); + AR <<= 34; + BR <<= 34; + + /* Shift smaller right */ + if (SCAD & 0400) { + SCAD = (01000 - SCAD); + if (SCAD < 61) { + AD = (BR & FPSBIT)? FPFMASK : 0; + BR = (BR >> SCAD) | (AD << (61 - SCAD)); + } else { +#if PDP6 + if (SCAD < 64) /* Under limit */ +#else + if (SCAD < 65) /* Under limit */ +#endif + BR = (BR & FPSBIT)? FPFMASK: 0; + else + BR = 0; + } + } + /* Do the addition now */ + AR = (AR + BR); + + /* Set flag1 to sign and make positive */ + flag1 = (AR & FPSBIT) != 0; +fnorm: + if (((AR & FPSBIT) != 0) != ((AR & FPNBIT) != 0)) { + SC += 1; + flag3 = AR & 1; + AR = (AR & FPHBIT) | (AR >> 1); + } + if (AR != 0) { +#if !PDP6 + AR &= ~077; /* Save one extra bit */ +#endif + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) + fxu_hold_set = 1; + if (IR != 0130) { /* !UFA */ +fnormx: + while (AR != 0 && ((AR & FPSBIT) != 0) == ((AR & FPNBIT) != 0) && + ((AR & FPNBIT) != 0) == ((AR & FP1BIT) != 0)) { + SC --; + AR <<= 1; +#if PDP6 + AR |= flag3; + flag3 = 0; +#endif + } + /* Handle edge case of a - and overflow bit */ + if ((AR & 000777777777600000000000LL) == (FPSBIT|FPNBIT)) { + SC += 1; + AR = (AR & FPHBIT) | (AR >> 1); + } + if (!nrf && ((IR & 04) != 0)) { + f = (AR & FP1BIT) != 0; + if ((AR & FPRBIT2) != 0) { +#if !PDP6 + /* FADR & FSBR do not rount if negative and equal round */ + /* FMPR does not round if result negative and equal round */ + if (((IR & 070) != 070 && + (AR & FPSBIT) != 0 && + (AR & FPRMASK) != FPRBIT2) || + (AR & FPSBIT) == 0 || + (AR & FPRMASK) != FPRBIT2) +#endif + AR += FPRBIT1; + nrf = 1; +#if !PDP6 + AR &= ~FPRMASK; +#endif + flag3 = 0; + if (((AR & FP1BIT) != 0) != f) { + SC += 1; + flag3 = AR & 1; + AR = (AR & FPHBIT) | (AR >> 1); + } + goto fnormx; + } + } + } + + MQ = AR & FPRMASK; + AR >>= 34; + if (flag1) + AR |= SMASK; + } else { + AR = MQ = 0; + SC = 0; + } + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; +#if !PDP6 + if (!fxu_hold_set) { + FLAGS |= FLTUND; + MQ = 0; + } +#endif + check_apr_irq(); + } + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR &= SMASK|MMASK; + AR |= ((uint64)(SCAD & 0377)) << 27; +#if PDP6 + /* FADL FADRL FSBL FSBRL FMPL FMPRL */ + if ((IR & 03) == 1) { + MQ = ((MQ << 1) & CMASK) | flag3/*| (flag3 << nrf)*/; + if (flag1) + MQ |= SMASK; + } +#else + /* FADL FSBL FMPL */ + if ((IR & 07) == 1) { + SC = (SC + (0777 ^ 26)) & 0777; + if ((SC & 0400) != 0) + MQ = 0; + MQ = (MQ >> 7) & MMASK; + if (MQ != 0) { + SC ^= (SC & SMASK) ? 0377 : 0; + MQ |= ((uint64)(SC & 0377)) << 27; + } + } +#endif + if ((AR & MMASK) == 0) + AR = 0; + + + /* Handle UFA */ + if (IR == 0130) { + set_reg(AC + 1, AR); + break; + } + break; + + case 0160: /* FMP */ + case 0161: /* FMPL */ + case 0162: /* FMPM */ + case 0163: /* FMPB */ + case 0164: /* FMPR */ + case 0165: /* FMPRI, FMPRL on PDP6 */ + case 0166: /* FMPRM */ + case 0167: /* FMPRB */ + /* Compute exponent */ + SC = (((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777; + SC += (((AR & SMASK) ? 0777 : 0) ^ (AR >> 27)) & 0777; + SC += 0600; + SC &= 0777; + /* Make positive and compute result sign */ + flag1 = 0; + flag3 = 0; + if (AR & SMASK) { + if ((AR & MMASK) == 0) { + AR = BIT9; + SC++; + } else + AR = CM(AR) + 1; + flag1 = 1; + flag3 = 1; + } + if (BR & SMASK) { + if ((BR & MMASK) == 0) { + BR = BIT9; + SC++; + } else + BR = CM(BR) + 1; + flag1 = !flag1; + } + AR &= MMASK; + BR &= MMASK; + AR = (AR * BR) << 7; + if (flag1) { + AR = (AR ^ FPFMASK) + 1; + } +#if PDP6 + AR &= ~0177; + if (flag3) + AR |= 0177; +#endif + goto fnorm; + + case 0170: /* FDV */ + case 0172: /* FDVM */ + case 0173: /* FDVB */ + case 0174: /* FDVR */ +#if !PDP6 + case 0175: /* FDVRI */ +#endif + case 0176: /* FDVRM */ + case 0177: /* FDVRB */ + flag1 = 0; + flag3 = 0; + SC = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777); + SCAD = (int)((((AR & SMASK) ? 0777 : 0) ^ (AR >> 27)) & 0777); + if ((BR & (MMASK)) == 0) { + if (BR == SMASK) { + BR = BIT9; + SC--; + } else { + AR = BR; + break; + } + } + if (BR & SMASK) { + BR = CM(BR) + 1; + flag1 = 1; + flag3 = 1; + } + if (AR & SMASK) { + if ((AR & MMASK) == 0) { + AR = BIT9; + SC--; + } else + AR = CM(AR) + 1; + flag1 = !flag1; + } + SC = (SC + ((0777 ^ SCAD) + 1) + 0201) & 0777; + /* Clear exponents */ + AR &= MMASK; + BR &= MMASK; + /* Check if we need to fix things */ + if (BR >= (AR << 1)) { + if (!pi_cycle) + FLAGS |= OVR|NODIV|FLTOVR|TRP1; + check_apr_irq(); + sac_inh = 1; + break; /* Done */ + } + BR = (BR << 28); + MB = AR; + AR = BR / AR; + if (AR != 0) { +#if !PDP6 + if ((AR & BIT7) != 0) { + AR >>= 1; + } else { + SC--; + } + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0) || SC == 0600) + fxu_hold_set = 1; + if (IR & 04) { + AR++; + } + AR >>= 1; + while ((AR & BIT9) == 0) { + AR <<= 1; + SC--; + } +#else + if (flag1) { + AR = ((AR ^ FMASK) + 1) & FMASK; + if ((AR & BIT7) == 0) { + AR >>= 1; + } else { + SC--; + } + } else { + if ((AR & BIT7) != 0) { + AR >>= 1; + } else { + SC--; + } + } + if (IR & 04) { + AR++; + } + AR >>= 1; + while ((((AR << 1) ^ AR) & BIT8) == 0) { + AR <<= 1; + SC--; + } + AR &= MMASK; + if (flag1) { + AR |= SMASK; + } +#endif + } else if (flag1) { + AR = SMASK | BIT9; + SC++; + flag1 = 0; + } else { + AR = 0; + SC = 0; + } + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (!fxu_hold_set) { + FLAGS |= FLTUND; + } + check_apr_irq(); + } +#if !PDP6 + if (flag1) { + AR = ((AR ^ MMASK) + 1) & MMASK; + AR |= SMASK; + } +#endif + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR |= ((uint64)(SCAD & 0377)) << 27; + break; + + case 0171: /* FDVL */ +#if PDP6 + case 0175: /* FDVRL */ + flag1 = flag3 = 0; + MQ = 0; + if (BR & SMASK) { + BR = CM(BR); + if (MQ == 0) + BR = BR + 1; + flag1 = 1; + flag3 = 1; + } + if (AR & SMASK) + flag1 = !flag1; + SC = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777); + SC += (int)((((AR & SMASK) ? 0 : 0777) ^ (AR >> 27)) & 0777); + SC = (SC + 0201) & 0777; + FE = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777) - 26; + SMEAR_SIGN(AR); + SMEAR_SIGN(BR); + /* FDT1 */ + MQ = (BR & 1) ? SMASK : 0; + BR >>= 1; + if (((AR & SMASK) == 0)) + AD = (CM(AR) + BR + 1) ; + else + AD = (AR + BR) ; + + /* Do actual divide */ + /* DST14 & DST15 */ + for (SCAD = 0; SCAD < 29; SCAD++) { + BR = (AD << 1) | ((MQ & SMASK) ? 1 : 0); + BR &= FMASK; + MQ = (MQ << 1); + MQ |= (AD & SMASK) == 0; + MQ &= FMASK; + if (((AR & SMASK) != 0) ^ ((MQ & 1) != 0)) + AD = (CM(AR) + BR + 1) ; + else + AD = (AR + BR) ; + } + /* DST16 */ + BR = AD | ((MQ & SMASK) ? 1 : 0); + BR &= FMASK; + MQ = (MQ << 1); + MQ |= (AD & SMASK) == 0; + MQ &= FMASK; + if (((AR & SMASK) != 0) ^ ((MQ & 1) != 0)) + AD = (CM(AR) + BR + 1) ; + else + AD = (AR + BR) ; + if ((AD & C1) != 0) + BR = AD & FMASK; + AR = MQ; + if (flag3) + BR = ((BR ^ FMASK) + 1) & FMASK; + MQ = BR; + if (flag1) + AR = ((AR ^ FMASK) + 1) & FMASK; + + /* FDT1 */ + if (AR != 0) { + MQ = (MQ >> 1) & (CMASK >> 1); + if (AR & 1) + MQ |= BIT1; + AR >>= 1; + if (AR & BIT1) + AR |= SMASK; + /* NRT0 */ +left: + SC++; + MQ = (MQ >> 1) & (CMASK >> 1); + if (AR & 1) + MQ |= BIT1; + AR >>= 1; + if (AR & BIT1) + AR |= SMASK; + while ((((AR >> 1) ^ AR) & BIT9) == 0) { + AR = (AR << 1) & FMASK; + if (MQ & BIT1) + AR |= 1; + MQ = (MQ << 1) & CMASK; + SC--; + } + if (!nrf && IR & 04) { + nrf = 1; + if ((MQ & BIT1) != 0) { + AR++; + goto left; + } + } + if (AR & SMASK) + MQ |= SMASK; + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) + fxu_hold_set = 1; + } else { + SC = 0; + } + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (!fxu_hold_set) { + FLAGS |= FLTUND; + } + check_apr_irq(); + } + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR &= SMASK|MMASK; + AR |= ((uint64)(SCAD & 0377)) << 27; + +#else + flag1 = flag3 = 0; + SC = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777); + SC += (int)((((AR & SMASK) ? 0 : 0777) ^ (AR >> 27)) & 0777); + SC = (SC + 0201) & 0777; + FE = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777) - 26; + if (BR & SMASK) { + MQ = (CM(MQ) + 1) & MMASK; + BR = CM(BR); + if (MQ == 0) + BR = BR + 1; + flag1 = 1; + flag3 = 1; + } + MQ &= MMASK; + if (AR & SMASK) { + AR = CM(AR) + 1; + flag1 = !flag1; + } + /* Clear exponents */ + AR &= MMASK; + BR &= MMASK; + /* Check if we need to fix things */ + if (BR >= (AR << 1)) { + if (!pi_cycle) + FLAGS |= OVR|NODIV|FLTOVR|TRP1; + check_apr_irq(); + sac_inh = 1; + break; /* Done */ + } + BR = (BR << 27) + MQ; + MB = AR; + AR <<= 27; + AD = 0; + if (BR < AR) { + BR <<= 1; + SC--; + FE--; + } + for (SCAD = 0; SCAD < 27; SCAD++) { + AD <<= 1; + if (BR >= AR) { + BR = BR - AR; + AD |= 1; + } + BR <<= 1; + } + MQ = BR >> 28; + AR = AD; + SC++; + if (AR != 0) { + if ((AR & BIT8) != 0) { + SC++; + FE++; + AR >>= 1; + } + while ((AR & BIT9) == 0) { + AR <<= 1; + SC--; + } + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) + fxu_hold_set = 1; + if (flag1) { + AR = (AR ^ MMASK) + 1; + AR |= SMASK; + } + } else if (flag1) { + FE = SC = 0; + } else { + AR = 0; + SC = 0; + FE = 0; + } + if (((SC & 0400) != 0) && !pi_cycle) { + FLAGS |= OVR|FLTOVR|TRP1; + if (!fxu_hold_set) { + FLAGS |= FLTUND; + } + check_apr_irq(); + } + SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); + AR &= SMASK|MMASK; + AR |= ((uint64)(SCAD & 0377)) << 27; + + if (MQ != 0) { + MQ &= MMASK; + if (flag3) { + MQ = (MQ ^ MMASK) + 1; + MQ |= SMASK; + } + if (FE < 0 /*FE & 0400*/) { + MQ = 0; + FE = 0; + } else + FE ^= (flag3) ? 0377 : 0; + MQ |= ((uint64)(FE & 0377)) << 27; + } +#endif + break; + + /* FWT */ + case 0200: /* MOVE */ + case 0201: /* MOVEI */ + case 0202: /* MOVEM */ + case 0203: /* MOVES */ + case 0204: /* MOVS */ + case 0205: /* MOVSI */ + case 0206: /* MOVSM */ + case 0207: /* MOVSS */ + case 0503: /* HLLS */ + case 0543: /* HRRS */ + break; + + case 0214: /* MOVM */ + case 0215: /* MOVMI */ + case 0216: /* MOVMM */ + case 0217: /* MOVMS */ + if ((AR & SMASK) == 0) + break; + /* Fall though */ + + case 0210: /* MOVN */ + case 0211: /* MOVNI */ + case 0212: /* MOVNM */ + case 0213: /* MOVNS */ + flag1 = flag3 = 0; + AD = CM(AR) + 1; + if ((((AR & CMASK) ^ CMASK) + 1) & SMASK) { +#if !PDP6 + FLAGS |= CRY1; +#endif + flag1 = 1; + } + if (AD & C1) { +#if !PDP6 + FLAGS |= CRY0; +#endif + flag3 = 1; + } + if (flag1 != flag3 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } +#if KI | KL + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; +#endif + AR = AD & FMASK; + break; + + case 0220: /* IMUL */ + case 0221: /* IMULI */ + case 0222: /* IMULM */ + case 0223: /* IMULB */ + case 0224: /* MUL */ + case 0225: /* MULI */ + case 0226: /* MULM */ + case 0227: /* MULB */ + flag3 = 0; + if (AR & SMASK) { + AR = (CM(AR) + 1) & FMASK; + flag3 = 1; + } + if (BR & SMASK) { + BR = (CM(BR) + 1) & FMASK; + flag3 = !flag3; + } + + if ((AR == 0) || (BR == 0)) { + AR = MQ = 0; + break; + } +#if KA + if (BR == SMASK) /* Handle special case */ + flag3 = !flag3; +#endif + MQ = AR * (BR & RMASK); /* 36 * low 18 = 54 bits */ + AR = AR * ((BR >> 18) & RMASK); /* 36 * high 18 = 54 bits */ + MQ += (AR << 18) & LMASK; /* low order bits */ + AR >>= 18; + AR = (AR << 1) + (MQ >> 35); + MQ &= CMASK; /* low order only has 35 bits */ + if ((IR & 4) == 0) { /* IMUL */ + if (AR > flag3 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + if (flag3) { + MQ ^= CMASK; + MQ++; + MQ |= SMASK; + } + AR = MQ; + break; + } + if ((AR & SMASK) != 0 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + if (flag3) { + AR ^= FMASK; + MQ ^= CMASK; + MQ += 1; + if ((MQ & SMASK) != 0) { + AR += 1; + MQ &= CMASK; + } + } + AR &= FMASK; + MQ = (MQ & ~SMASK) | (AR & SMASK); +#if KA + if (BR == SMASK && (AR & SMASK)) /* Handle special case */ + FLAGS |= OVR; +#endif + break; + + case 0230: /* IDIV */ + case 0231: /* IDIVI */ + case 0232: /* IDIVM */ + case 0233: /* IDIVB */ + flag1 = 0; + flag3 = 0; + if (BR & SMASK) { + BR = (CM(BR) + 1) & FMASK; + flag1 = !flag1; + } + + if (BR == 0) { /* Check for overflow */ + FLAGS |= OVR|NODIV; /* Overflow and No Divide */ + sac_inh=1; /* Don't touch AC */ + check_apr_irq(); + break; /* Done */ + } + +#if !PDP6 + if (AR == SMASK && BR == 1) { + FLAGS |= OVR|NODIV; /* Overflow and No Divide */ + sac_inh=1; /* Don't touch AC */ + check_apr_irq(); + break; /* Done */ + } +#else + if (AR == SMASK && BR == 1) { + MQ = 0; + AR = 0; + break; /* Done */ + } +#endif + + if (AR & SMASK) { + AR = (CM(AR) + 1) & FMASK; + flag1 = !flag1; + flag3 = 1; + } + + MQ = AR % BR; + AR = AR / BR; + if (flag1) + AR = (CM(AR) + 1) & FMASK; + if (flag3) + MQ = (CM(MQ) + 1) & FMASK; + break; + + case 0234: /* DIV */ + case 0235: /* DIVI */ + case 0236: /* DIVM */ + case 0237: /* DIVB */ + flag1 = 0; + if (AR & SMASK) { + AD = (CM(MQ) + 1) & FMASK; + MQ = AR; + AR = AD; + AD = (CM(MQ)) & FMASK; + MQ = AR; + AR = AD; + if ((MQ & CMASK) == 0) + AR = (AR + 1) & FMASK; + flag1 = 1; + } + + if (BR & SMASK) + AD = (AR + BR) & FMASK; + else + AD = (AR + CM(BR) + 1) & FMASK; + MQ = (MQ << 1) & FMASK; + MQ |= (AD & SMASK) != 0; + SC = 35; + if ((AD & SMASK) == 0) { + FLAGS |= OVR|NODIV|TRP1; /* Overflow and No Divide */ + i_flags = 0; + sac_inh=1; + check_apr_irq(); + break; /* Done */ + } + + while (SC != 0) { + if (((BR & SMASK) != 0) ^ ((MQ & 01) != 0)) + AD = (AR + CM(BR) + 1); + else + AD = (AR + BR); + AR = (AD << 1) | ((MQ & SMASK) ? 1 : 0); + AR &= FMASK; + MQ = (MQ << 1) & FMASK; + MQ |= (AD & SMASK) == 0; + SC--; + } + if (((BR & SMASK) != 0) ^ ((MQ & 01) != 0)) + AD = (AR + CM(BR) + 1); + else + AD = (AR + BR); + AR = AD & FMASK; + MQ = (MQ << 1) & FMASK; + MQ |= (AD & SMASK) == 0; + if (AR & SMASK) { + if (BR & SMASK) + AD = (AR + CM(BR) + 1) & FMASK; + else + AD = (AR + BR) & FMASK; + AR = AD; + } + + if (flag1) + AR = (CM(AR) + 1) & FMASK; + if (flag1 ^ ((BR & SMASK) != 0)) { + AD = (CM(MQ) + 1) & FMASK; + MQ = AR; + AR = AD; + } else { + AD = MQ; + MQ = AR; + AR = AD; + } + break; + + /* Shift */ + case 0240: /* ASH */ + SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; + if (SC == 0) + break; + AD = (AR & SMASK) ? FMASK : 0; + if (AB & RSIGN) { + if (SC < 35) + AR = ((AR >> SC) | (AD << (36 - SC))) & FMASK; + else + AR = AD; + } else { + if (((AD << SC) & ~CMASK) != ((AR << SC) & ~CMASK)) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + AR = ((AR << SC) & CMASK) | (AR & SMASK); + } + break; + + case 0241: /* ROT */ + SC = (AB & RSIGN) ? + ((AB & 0377) ? (((0377 ^ AB) + 1) & 0377) : 0400) + : (AB & 0377); + if (SC == 0) + break; + SC = SC % 36; + if (AB & RSIGN) + SC = 36 - SC; + AR = ((AR << SC) | (AR >> (36 - SC))) & FMASK; + break; + + case 0242: /* LSH */ + SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; + if (SC != 0) { + if (SC > 36){ + AR = 0; + } else if (AB & RSIGN) { + AR = AR >> SC; + } else { + AR = (AR << SC) & FMASK; + } + } + break; + + case 0243: /* JFFO */ +#if !PDP6 + SC = 0; + if (AR != 0) { +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC = AB; + f_pc_inh = 1; + SC = nlzero(AR); + } + set_reg(AC + 1, SC); +#endif + break; + + case 0244: /* ASHC */ + SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; + if (SC == 0) + break; + if (SC > 70) + SC = 70; + AD = (AR & SMASK) ? FMASK : 0; + AR &= CMASK; + MQ &= CMASK; + if (AB & RSIGN) { + if (SC >= 35) { + MQ = ((AR >> (SC - 35)) | (AD << (70 - SC))) & FMASK; + AR = AD; + } else { + MQ = (AD & SMASK) | (MQ >> SC) | + ((AR << (35 - SC)) & CMASK); + AR = ((AD & SMASK) | + ((AR >> SC) | (AD << (35 - SC)))) & FMASK; + } + } else { + if (SC >= 35) { +#if !PDP6 + if (((AD << SC) & ~CMASK) != ((AR << SC) & ~CMASK)) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } +#endif + AR = (AD & SMASK) | ((MQ << (SC - 35)) & CMASK); + MQ = (AD & SMASK); + } else { + if ((((AD & CMASK) << SC) & ~CMASK) != ((AR << SC) & ~CMASK)) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + AR = (AD & SMASK) | ((AR << SC) & CMASK) | + (MQ >> (35 - SC)); + MQ = (AD & SMASK) | ((MQ << SC) & CMASK); + } + } + break; + + case 0245: /* ROTC */ + SC = (AB & RSIGN) ? + ((AB & 0377) ? (((0377 ^ AB) + 1) & 0377) : 0400) + : (AB & 0377); + if (SC == 0) + break; + SC = SC % 72; + if (AB & RSIGN) + SC = 72 - SC; + if (SC >= 36) { + AD = MQ; + MQ = AR; + AR = AD; + SC -= 36; + } + AD = ((AR << SC) | (MQ >> (36 - SC))) & FMASK; + MQ = ((MQ << SC) | (AR >> (36 - SC))) & FMASK; + AR = AD; + break; + + case 0246: /* LSHC */ + SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; + if (SC == 0) + break; + if (SC > 71) { + AR = 0; + MQ = 0; + } else { + if (SC > 36) { + if (AB & RSIGN) { + MQ = AR; + AR = 0; + } else { + AR = MQ; + MQ = 0; + } + SC -= 36; + } + if (AB & RSIGN) { + MQ = ((MQ >> SC) | (AR << (36 - SC))) & FMASK; + AR = AR >> SC; + } else { + AR = ((AR << SC) | (MQ >> (36 - SC))) & FMASK; + MQ = (MQ << SC) & FMASK; + } + } + break; + + /* Branch */ + case 0250: /* EXCH */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, BR); + break; + + case 0251: /* BLT */ + BR = AB; + do { + if (sim_interval <= 0) { + sim_process_event(); + } + /* Allow for interrupt */ + if (pi_pending) { + pi_rq = check_irq_level(); + if (pi_rq) { + f_pc_inh = 1; + f_load_pc = 0; + f_inst_fetch = 0; + set_reg(AC, AR); + break; + } + } + AB = (AR >> 18) & RMASK; + if (Mem_read(0, 0, 0)) { +#if ITS + /* On ITS if access error, allow for skip */ + if (QITS && (xct_flag & 04) != 0) + f_pc_inh =0; + else +#endif +#if PDP6 + AR = AOB(AR) & FMASK; +#endif + f_pc_inh = 1; +#if KA & ITS + if (QITS) + set_reg(AC, AR); +#else + set_reg(AC, AR); +#endif + goto last; + } + AB = (AR & RMASK); + if (Mem_write(0, 0)) { +#if ITS + /* On ITS if access error, allow for skip */ + if (QITS && (xct_flag & 04) != 0) + f_pc_inh =0; + else +#endif +#if PDP6 + AR = AOB(AR) & FMASK; +#endif + f_pc_inh = 1; +#if KA & ITS + if (QITS) + set_reg(AC, AR); +#else + set_reg(AC, AR); +#endif + goto last; + } + AD = (AR & RMASK) + CM(BR) + 1; + AR = AOB(AR); + } while ((AD & C1) == 0); + break; + + case 0252: /* AOBJP */ + AR = AOB(AR); + if ((AR & SMASK) == 0) { +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = AB; + f_pc_inh = 1; + } + break; + + case 0253: /* AOBJN */ + AR = AOB(AR); + if ((AR & SMASK) != 0) { +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = AB; + f_pc_inh = 1; + } + break; + + case 0254: /* JRST */ /* AR Frm PC */ + if (uuo_cycle | pi_cycle) { + FLAGS &= ~USER; /* Clear USER */ +#if ITS + if (QITS && one_p_arm) { + FLAGS |= ONEP; + one_p_arm = 0; + } +#endif + } + /* JEN */ + if (AC & 010) { /* Restore interrupt level. */ +#if KI | KL + if ((FLAGS & (USER|USERIO)) == USER || + (FLAGS & (USER|PUBLIC)) == PUBLIC) { +#else + if ((FLAGS & (USER|USERIO)) == USER) { +#endif + goto muuo; + } else { + pi_restore = 1; + } + } + /* HALT */ + if (AC & 04) { +#if KI | KL + if ((FLAGS & (USER|USERIO)) == USER || + (FLAGS & (USER|PUBLIC)) == PUBLIC) { +#else + if ((FLAGS & (USER|USERIO)) == USER) { +#endif + goto muuo; + } else { + reason = STOP_HALT; + } + } +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC = AR & RMASK; + PC_CHANGE + /* JRSTF */ + if (AC & 02) { + FLAGS &= ~(OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|TRP1|TRP2|PCHNG); + AR >>= 23; /* Move into position */ + /* If executive mode, copy USER and UIO */ + if ((FLAGS & (PUBLIC|USER)) == 0) + FLAGS |= AR & (USER|USERIO|PUBLIC); + /* Can always clear UIO */ + if ((AR & USERIO) == 0) { + FLAGS &= ~USERIO; + } +#if PDP6 + user_io = (FLAGS & USERIO) != 0; +#endif + FLAGS |= AR & (OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|\ + TRP1|TRP2|PUBLIC|PCHNG); +#if ITS + if (QITS) + FLAGS |= AR & (PURE|ONEP); +#endif +#if KI + FLAGS &= ~PRV_PUB; + if ((FLAGS & USER) == 0) + FLAGS |= (AR & OVR) ? PRV_PUB : 0; +#endif + check_apr_irq(); + } + + if (AC & 01) { /* Enter User Mode */ +#if KI | KL + FLAGS &= ~(PUBLIC|PRV_PUB); +#else + FLAGS |= USER; +#endif + } + f_pc_inh = 1; + break; + + case 0255: /* JFCL */ + if ((FLAGS >> 9) & AC) { +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC = AR & RMASK; + f_pc_inh = 1; + } + FLAGS &= 037777 ^ (AC << 9); + break; + + case 0256: /* XCT */ + f_load_pc = 0; + f_pc_inh = 1; + xct_flag = 0; +#if BBN + if (QBBN && (FLAGS & USER) == 0) + xct_flag = AC; +#endif +#if KI | KL + if ((FLAGS & USER) == 0) + xct_flag = AC; +#endif +#if WAITS + if (QWAITS && (FLAGS & USER) == 0) + xct_flag = AC; +#endif +#if ITS + if (QITS && one_p_arm) { + FLAGS |= ONEP; + one_p_arm = 0; + } +#endif + break; + + case 0257: /* MAP */ +#if KI | KL + f = AB >> 9; + /* Check if Paging Enabled */ + if (!page_enable || AB < 020) { + AR = 0020000LL + f; /* direct map */ + set_reg(AC, AR); + break; + } + flag1 = (FLAGS & USER) != 0; + + /* Figure out if this is a user space access */ + if (xct_flag != 0 && !flag1) { + if ((xct_flag & 2) != 0) { + flag1 = (FLAGS & USERIO) != 0; + } + } + + flag3 = 0; + /* If user, check if small user enabled */ + if (flag1) { + if (small_user && (f & 0340) != 0) { + AR = 0420000LL; /* Outside small user space registers */ + set_reg(AC, AR); + break; + } + } else { + /* Handle system mapping */ + /* Pages 340-377 via UBR */ + if ((f & 0740) == 0340) { + f += 01000 - 0340; + flag3 = 2; + /* Pages 400-777 via EBR */ + } else if (f & 0400) { + flag3 = 1; + /* Pages 000-037 direct map */ + } else { + AR = 0020000LL + f; /* direct map */ + set_reg(AC, AR); + break; + } + } + /* Map the page */ + if (flag3&1) { + AR = e_tlb[f]; + if (AR == 0) { + AR = M[eb_ptr + (f >> 1)]; + e_tlb[f & 0776] = RMASK & (AR >> 18); + e_tlb[f | 1] = RMASK & AR; + AR = e_tlb[f]; + if (AR == 0) { + AR = 0437777; + set_reg(AC, AR); + break; + } + pag_reload = ((pag_reload + 1) & 037) | 040; + } + last_page = ((f ^ 0777) << 1) | 1; + } else { + AR = u_tlb[f]; + if (AR == 0) { + AR = M[ub_ptr + (f >> 1)]; + u_tlb[f & 01776] = RMASK & (AR >> 18); + u_tlb[f | 1] = RMASK & AR; + AR = u_tlb[f]; + if (AR == 0) { + AR = 0437777; + set_reg(AC, AR); + break; + } + pag_reload = ((pag_reload + 1) & 037) | 040; + } + if (flag3 & 2) + last_page = (((f-0440) ^ 0777) << 1) | 1; + else + last_page = ((f ^ 0777) << 1); + } + if ((AR & 0400000LL) == 0) + AR &= 0437777LL; /* Return valid entry for page */ + AR ^= 0400000LL; /* Flip access status. */ + set_reg(AC, AR); +#endif + break; + + /* Stack, JUMP */ + case 0260: /* PUSHJ */ /* AR Frm PC */ + MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + !pi_cycle) & RMASK); +#if KI + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + } +#endif + BR = AB; + AR = AOB(AR); + AB = AR & RMASK; + if (Mem_write(uuo_cycle | pi_cycle, 0)) + goto last; + FLAGS &= ~ (BYTI|ADRFLT|TRP1|TRP2); + if (AR & C1) { +#if KI | KL + if (!pi_cycle) + FLAGS |= TRP2; +#else + push_ovf = 1; + check_apr_irq(); +#endif + } +#if !PDP6 + if (uuo_cycle | pi_cycle) { + FLAGS &= ~(USER|PUBLIC); /* Clear USER */ +#if ITS + if (QITS && one_p_arm) { + FLAGS |= ONEP; + one_p_arm = 0; + } +#endif + } +#endif +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC = BR & RMASK; + PC_CHANGE + f_pc_inh = 1; + break; + + case 0261: /* PUSH */ + AR = AOB(AR); + AB = AR & RMASK; + if (AR & C1) { +#if KI | KL + if (!pi_cycle) + FLAGS |= TRP2; +#else + push_ovf = 1; + check_apr_irq(); +#endif + } + MB = BR; + if (Mem_write(0, 0)) + goto last; + break; + + case 0262: /* POP */ + AB = AR & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + AR = SOB(AR); + AB = BR & RMASK; + if (Mem_write(0, 0)) + goto last; + if ((AR & C1) == 0) { +#if KI | KL + if (!pi_cycle) + FLAGS |= TRP2; +#else + push_ovf = 1; + check_apr_irq(); +#endif + } + break; + + case 0263: /* POPJ */ + AB = AR & RMASK; + if (Mem_read(0, 0, 0)) + goto last; +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = MB & RMASK; + AR = SOB(AR); + if ((AR & C1) == 0) { +#if KI | KL + if (!pi_cycle) + FLAGS |= TRP2; +#else + push_ovf = 1; + check_apr_irq(); +#endif + } + f_pc_inh = 1; + break; + + case 0264: /* JSR */ /* AR Frm PC */ + MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + !pi_cycle) & RMASK); +#if KI + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + } +#endif +#if PDP6 + if (ill_op | uuo_cycle | pi_cycle | ex_uuo_sync) { + ill_op = 0; + ex_uuo_sync = 0; +#else + if (uuo_cycle | pi_cycle) { +#endif + FLAGS &= ~(USER|PUBLIC); /* Clear USER */ + } + if (Mem_write(0, 0)) + goto last; + FLAGS &= ~ (BYTI|ADRFLT|TRP1|TRP2); +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = (AR + 1) & RMASK; + f_pc_inh = 1; + break; + + case 0265: /* JSP */ /* AR Frm PC */ + AD = (((uint64)(FLAGS) << 23) & LMASK) | + ((PC + !pi_cycle) & RMASK); + FLAGS &= ~ (BYTI|ADRFLT|TRP1|TRP2); +#if KI + if ((FLAGS & USER) == 0) { + AD &= ~SMASK; + AD |= (FLAGS & PRV_PUB) ? SMASK : 0; + } +#endif +#if !PDP6 + if (uuo_cycle | pi_cycle) { + FLAGS &= ~(USER|PUBLIC); /* Clear USER */ + } +#endif +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = AR & RMASK; + AR = AD; + f_pc_inh = 1; + break; + + case 0266: /* JSA */ /* AR Frm PC */ + set_reg(AC, (AR << 18) | ((PC + 1) & RMASK)); +#if !PDP6 + if (uuo_cycle | pi_cycle) { + FLAGS &= ~(USER|PUBLIC); /* Clear USER */ + } +#endif +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = AR & RMASK; + AR = BR; + break; + + case 0267: /* JRA */ + AD = AB; + AB = (get_reg(AC) >> 18) & RMASK; + if (Mem_read(uuo_cycle | pi_cycle, 0, 0)) + goto last; + set_reg(AC, MB); +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = AD & RMASK; + f_pc_inh = 1; + break; + + case 0270: /* ADD */ + case 0271: /* ADDI */ + case 0272: /* ADDM */ + case 0273: /* ADDB */ + flag1 = flag3 = 0; + if (((AR & CMASK) + (BR & CMASK)) & SMASK) { + FLAGS |= CRY1; + flag1 = 1; + } + AR = AR + BR; + if (AR & C1) { + if (!pi_cycle) + FLAGS |= CRY0; + flag3 = 1; + } + if (flag1 != flag3) { + if (!pi_cycle) + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + break; + + case 0274: /* SUB */ + case 0275: /* SUBI */ + case 0276: /* SUBM */ + case 0277: /* SUBB */ + flag1 = flag3 = 0; + if ((((AR & CMASK) ^ CMASK) + (BR & CMASK) + 1) & SMASK) { + FLAGS |= CRY1; + flag1 = 1; + } + AR = CM(AR) + BR + 1; + if (AR & C1) { + if (!pi_cycle) + FLAGS |= CRY0; + flag3 = 1; + } + if (flag1 != flag3) { + if (!pi_cycle) + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + break; + + case 0300: /* CAI */ + case 0301: /* CAIL */ + case 0302: /* CAIE */ + case 0303: /* CAILE */ + case 0304: /* CAIA */ + case 0305: /* CAIGE */ + case 0306: /* CAIN */ + case 0307: /* CAIG */ + case 0310: /* CAM */ + case 0311: /* CAML */ + case 0312: /* CAME */ + case 0313: /* CAMLE */ + case 0314: /* CAMA */ + case 0315: /* CAMGE */ + case 0316: /* CAMN */ + case 0317: /* CAMG */ + f = 0; + AD = (CM(AR) + BR) + 1; +#if PDP6 + if (AD & C1) + FLAGS |= CRY0; + if ((AR & SMASK) != (BR & SMASK)) + FLAGS |= CRY1; +#endif + if (((BR & SMASK) != 0) && (AR & SMASK) == 0) + f = 1; + if (((BR & SMASK) == (AR & SMASK)) && + (AD & SMASK) != 0) + f = 1; + goto skip_op; + + case 0320: /* JUMP */ + case 0321: /* JUMPL */ + case 0322: /* JUMPE */ + case 0323: /* JUMPLE */ + case 0324: /* JUMPA */ + case 0325: /* JUMPGE */ + case 0326: /* JUMPN */ + case 0327: /* JUMPG */ + AD = AR; + f = ((AD & SMASK) != 0); + goto jump_op; /* JUMP, SKIP */ + + case 0330: /* SKIP */ + case 0331: /* SKIPL */ + case 0332: /* SKIPE */ + case 0333: /* SKIPLE */ + case 0334: /* SKIPA */ + case 0335: /* SKIPGE */ + case 0336: /* SKIPN */ + case 0337: /* SKIPG */ + AD = AR; + f = ((AD & SMASK) != 0); + goto skip_op; /* JUMP, SKIP */ + + case 0340: /* AOJ */ + case 0341: /* AOJL */ + case 0342: /* AOJE */ + case 0343: /* AOJLE */ + case 0344: /* AOJA */ + case 0345: /* AOJGE */ + case 0346: /* AOJN */ + case 0347: /* AOJG */ + case 0360: /* SOJ */ + case 0361: /* SOJL */ + case 0362: /* SOJE */ + case 0363: /* SOJLE */ + case 0364: /* SOJA */ + case 0365: /* SOJGE */ + case 0366: /* SOJN */ + case 0367: /* SOJG */ + flag1 = flag3 = 0; + AD = (IR & 020) ? FMASK : 1; + if (((AR & CMASK) + (AD & CMASK)) & SMASK) { + if (!pi_cycle) + FLAGS |= CRY1; + flag1 = 1; + } + AD = AR + AD; +#if PDP6 + if (AD == FMASK && !pi_cycle) + FLAGS |= CRY0; + if ((AD & CMASK) == CMASK && !pi_cycle) + FLAGS |= CRY1; +#endif + if (AD & C1) { + if (!pi_cycle) + FLAGS |= CRY0; + flag3 = 1; + } + if (flag1 != flag3 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + f = ((AD & SMASK) != 0); +jump_op: + AD &= FMASK; + AR = AD; + f |= ((AD == 0) << 1); + f = f & IR; + if (((IR & 04) != 0) == (f == 0)) { +#if ITS + if ((FLAGS & USER) && QITS) { + jpc = PC; + } +#endif + PC_CHANGE + PC = AB; + f_pc_inh = 1; + } + break; + + case 0350: /* AOS */ + case 0351: /* AOSL */ + case 0352: /* AOSE */ + case 0353: /* AOSLE */ + case 0354: /* AOSA */ + case 0355: /* AOSGE */ + case 0356: /* AOSN */ + case 0357: /* AOSG */ + case 0370: /* SOS */ + case 0371: /* SOSL */ + case 0372: /* SOSE */ + case 0373: /* SOSLE */ + case 0374: /* SOSA */ + case 0375: /* SOSGE */ + case 0376: /* SOSN */ + case 0377: /* SOSG */ + flag1 = flag3 = 0; + AD = (IR & 020) ? FMASK : 1; + if (((AR & CMASK) + (AD & CMASK)) & SMASK) { + if (!pi_cycle) + FLAGS |= CRY1; + flag1 = 1; + } + AD = AR + AD; + if (AD & C1) { + if (!pi_cycle) + FLAGS |= CRY0; + flag3 = 1; + } + if (flag1 != flag3 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + f = ((AD & SMASK) != 0); +skip_op: + AD &= FMASK; + AR = AD; + f |= ((AD == 0) << 1); + f = f & IR; + if (((IR & 04) != 0) == (f == 0)) { +#if PDP6 + if (pi_cycle) + f_pc_inh = 1; +#endif + PC_CHANGE + PC = (PC + 1) & RMASK; +#if KI | KL + } else if (pi_cycle) { + pi_ov = pi_hold = 1; +#endif + } + break; + + /* Bool */ + case 0400: /* SETZ */ + case 0401: /* SETZI */ + case 0402: /* SETZM */ + case 0403: /* SETZB */ + AR = 0; /* SETZ */ + break; + + case 0404: /* AND */ + case 0405: /* ANDI */ + case 0406: /* ANDM */ + case 0407: /* ANDB */ + AR = AR & BR; /* AND */ + break; + + case 0410: /* ANDCA */ + case 0411: /* ANDCAI */ + case 0412: /* ANDCAM */ + case 0413: /* ANDCAB */ + AR = AR & CM(BR); /* ANDCA */ + break; + + case 0414: /* SETM */ + case 0415: /* SETMI */ + case 0416: /* SETMM */ + case 0417: /* SETMB */ + /* SETM */ + break; + + case 0420: /* ANDCM */ + case 0421: /* ANDCMI */ + case 0422: /* ANDCMM */ + case 0423: /* ANDCMB */ + AR = CM(AR) & BR; /* ANDCM */ + break; + + case 0424: /* SETA */ + case 0425: /* SETAI */ + case 0426: /* SETAM */ + case 0427: /* SETAB */ + AR = BR; /* SETA */ + break; + + case 0430: /* XOR */ + case 0431: /* XORI */ + case 0432: /* XORM */ + case 0433: /* XORB */ + AR = AR ^ BR; /* XOR */ + break; + + case 0434: /* IOR */ + case 0435: /* IORI */ + case 0436: /* IORM */ + case 0437: /* IORB */ + AR = CM(CM(AR) & CM(BR)); /* IOR */ + break; + + case 0440: /* ANDCB */ + case 0441: /* ANDCBI */ + case 0442: /* ANDCBM */ + case 0443: /* ANDCBB */ + AR = CM(AR) & CM(BR); /* ANDCB */ + break; + + case 0444: /* EQV */ + case 0445: /* EQVI */ + case 0446: /* EQVM */ + case 0447: /* EQVB */ + AR = CM(AR ^ BR); /* EQV */ + break; + + case 0450: /* SETCA */ + case 0451: /* SETCAI */ + case 0452: /* SETCAM */ + case 0453: /* SETCAB */ + AR = CM(BR); /* SETCA */ + break; + + case 0454: /* ORCA */ + case 0455: /* ORCAI */ + case 0456: /* ORCAM */ + case 0457: /* ORCAB */ + AR = CM(CM(AR) & BR); /* ORCA */ + break; + + case 0460: /* SETCM */ + case 0461: /* SETCMI */ + case 0462: /* SETCMM */ + case 0463: /* SETCMB */ + AR = CM(AR); /* SETCM */ + break; + + case 0464: /* ORCM */ + case 0465: /* ORCMI */ + case 0466: /* ORCMM */ + case 0467: /* ORCMB */ + AR = CM(AR & CM(BR)); /* ORCM */ + break; + + case 0470: /* ORCB */ + case 0471: /* ORCBI */ + case 0472: /* ORCBM */ + case 0473: /* ORCBB */ + AR = CM(AR & BR); /* ORCB */ + break; + + case 0474: /* SETO */ + case 0475: /* SETOI */ + case 0476: /* SETOM */ + case 0477: /* SETOB */ + AR = FMASK; /* SETO */ + break; + + + case 0547: /* HLRS */ + BR = SWAP_AR; + /* Fall Through */ + + case 0500: /* HLL */ + case 0501: /* HLLI */ + case 0502: /* HLLM */ + case 0504: /* HRL */ + case 0505: /* HRLI */ + case 0506: /* HRLM */ + AR = (AR & LMASK) | (BR & RMASK); + break; + + case 0510: /* HLLZ */ + case 0511: /* HLLZI */ + case 0512: /* HLLZM */ + case 0513: /* HLLZS */ + case 0514: /* HRLZ */ + case 0515: /* HRLZI */ + case 0516: /* HRLZM */ + case 0517: /* HRLZS */ + AR = (AR & LMASK); + break; + + case 0520: /* HLLO */ + case 0521: /* HLLOI */ + case 0522: /* HLLOM */ + case 0523: /* HLLOS */ + case 0524: /* HRLO */ + case 0525: /* HRLOI */ + case 0526: /* HRLOM */ + case 0527: /* HRLOS */ + AR = (AR & LMASK) | RMASK; + break; + + case 0530: /* HLLE */ + case 0531: /* HLLEI */ + case 0532: /* HLLEM */ + case 0533: /* HLLES */ + case 0534: /* HRLE */ + case 0535: /* HRLEI */ + case 0536: /* HRLEM */ + case 0537: /* HRLES */ + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + break; + + case 0507: /* HRLS */ + BR = SWAP_AR; + /* Fall Through */ + + case 0540: /* HRR */ + case 0541: /* HRRI */ + case 0542: /* HRRM */ + case 0544: /* HLR */ + case 0545: /* HLRI */ + case 0546: /* HLRM */ + AR = (BR & LMASK) | (AR & RMASK); + break; + + case 0550: /* HRRZ */ + case 0551: /* HRRZI */ + case 0552: /* HRRZM */ + case 0553: /* HRRZS */ + case 0554: /* HLRZ */ + case 0555: /* HLRZI */ + case 0556: /* HLRZM */ + case 0557: /* HLRZS */ + AR = (AR & RMASK); + break; + + case 0560: /* HRRO */ + case 0561: /* HRROI */ + case 0562: /* HRROM */ + case 0563: /* HRROS */ + case 0564: /* HLRO */ + case 0565: /* HLROI */ + case 0566: /* HLROM */ + case 0567: /* HLROS */ + AR = LMASK | (AR & RMASK); + break; + + case 0570: /* HRRE */ + case 0571: /* HRREI */ + case 0572: /* HRREM */ + case 0573: /* HRRES */ + case 0574: /* HLRE */ + case 0575: /* HLREI */ + case 0576: /* HLREM */ + case 0577: /* HLRES */ + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + break; + + case 0600: /* TRN */ + case 0601: /* TLN */ + case 0602: /* TRNE */ + case 0603: /* TLNE */ + case 0604: /* TRNA */ + case 0605: /* TLNA */ + case 0606: /* TRNN */ + case 0607: /* TLNN */ + case 0610: /* TDN */ + case 0611: /* TSN */ + case 0612: /* TDNE */ + case 0613: /* TSNE */ + case 0614: /* TDNA */ + case 0615: /* TSNA */ + case 0616: /* TDNN */ + case 0617: /* TSNN */ + MQ = AR; /* N */ + goto test_op; + + case 0620: /* TRZ */ + case 0621: /* TLZ */ + case 0622: /* TRZE */ + case 0623: /* TLZE */ + case 0624: /* TRZA */ + case 0625: /* TLZA */ + case 0626: /* TRZN */ + case 0627: /* TLZN */ + case 0630: /* TDZ */ + case 0631: /* TSZ */ + case 0632: /* TDZE */ + case 0633: /* TSZE */ + case 0634: /* TDZA */ + case 0635: /* TSZA */ + case 0636: /* TDZN */ + case 0637: /* TSZN */ + MQ = CM(AR) & BR; /* Z */ + goto test_op; + + case 0640: /* TRC */ + case 0641: /* TLC */ + case 0642: /* TRCE */ + case 0643: /* TLCE */ + case 0644: /* TRCA */ + case 0645: /* TLCA */ + case 0646: /* TRCN */ + case 0647: /* TLCN */ + case 0650: /* TDC */ + case 0651: /* TSC */ + case 0652: /* TDCE */ + case 0653: /* TSCE */ + case 0654: /* TDCA */ + case 0655: /* TSCA */ + case 0656: /* TDCN */ + case 0657: /* TSCN */ + MQ = AR ^ BR; /* C */ + goto test_op; + + case 0660: /* TRO */ + case 0661: /* TLO */ + case 0662: /* TROE */ + case 0663: /* TLOE */ + case 0664: /* TROA */ + case 0665: /* TLOA */ + case 0666: /* TRON */ + case 0667: /* TLON */ + case 0670: /* TDO */ + case 0671: /* TSO */ + case 0672: /* TDOE */ + case 0673: /* TSOE */ + case 0674: /* TDOA */ + case 0675: /* TSOA */ + case 0676: /* TDON */ + case 0677: /* TSON */ + MQ = AR | BR; /* O */ +test_op: + AR &= BR; + f = ((AR == 0) & ((IR >> 1) & 1)) ^ ((IR >> 2) & 1); + if (f) { + PC_CHANGE + PC = (PC + 1) & RMASK; + } + AR = MQ; + break; + + /* IOT */ + case 0700: case 0701: case 0702: case 0703: + case 0704: case 0705: case 0706: case 0707: + case 0710: case 0711: case 0712: case 0713: + case 0714: case 0715: case 0716: case 0717: + case 0720: case 0721: case 0722: case 0723: + case 0724: case 0725: case 0726: case 0727: + case 0730: case 0731: case 0732: case 0733: + case 0734: case 0735: case 0736: case 0737: + case 0740: case 0741: case 0742: case 0743: + case 0744: case 0745: case 0746: case 0747: + case 0750: case 0751: case 0752: case 0753: + case 0754: case 0755: case 0756: case 0757: + case 0760: case 0761: case 0762: case 0763: + case 0764: case 0765: case 0766: case 0767: + case 0770: case 0771: case 0772: case 0773: + case 0774: case 0775: case 0776: case 0777: +#if KI + if (!pi_cycle && ((((FLAGS & (USER|USERIO)) == USER) && (IR & 040) == 0) + || ((FLAGS & (USER|PUBLIC)) == PUBLIC))) { + +#elif PDP6 + if ((FLAGS & USER) != 0 && user_io == 0 && !pi_cycle) { +#else + if ((FLAGS & (USER|USERIO)) == USER && !pi_cycle) { +#endif + /* User and not User I/O */ + goto muuo; + } else { + int d = ((IR & 077) << 1) | ((AC & 010) != 0); +fetch_opr: + switch(AC & 07) { + case 0: /* 00 BLKI */ + case 2: /* 10 BLKO */ + if (Mem_read(pi_cycle, 0, 0)) + goto last; + AR = MB; + if (hst_lnt) { + hst[hst_p].mb = AR; + } + AC |= 1; /* Make into DATAI/DATAO */ + AR = AOB(AR); + if (AR & C1) { + pi_ov = 1; + } + else if (!pi_cycle) + PC = (PC + 1) & RMASK; + AR &= FMASK; + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + AB = AR & RMASK; + goto fetch_opr; + + case 1: /* 04 DATAI */ + dev_tab[d](DATAI|(d<<2), &AR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + break; + case 3: /* 14 DATAO */ + if (Mem_read(pi_cycle, 0, 0)) + goto last; + AR = MB; + dev_tab[d](DATAO|(d<<2), &AR); + break; + case 4: /* 20 CONO */ + dev_tab[d](CONO|(d<<2), &AR); + break; + case 5: /* 24 CONI */ + dev_tab[d](CONI|(d<<2), &AR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + break; + case 6: /* 30 CONSZ */ + dev_tab[d](CONI|(d<<2), &AR); + AR &= AB; + if (AR == 0) + PC = (PC + 1) & RMASK; + break; + case 7: /* 34 CONSO */ + dev_tab[d](CONI|(d<<2), &AR); + AR &= AB; + if (AR != 0) + PC = (PC + 1) & RMASK; + break; + } + } + break; + } + + AR &= FMASK; + if (!sac_inh && (i_flags & (SCE|FCEPSE))) { + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + } + if (!sac_inh && ((i_flags & SAC) || ((i_flags & SACZ) && AC != 0))) + set_reg(AC, AR); /* blank, I, B */ + + if (!sac_inh && (i_flags & SAC2)) { + MQ &= FMASK; + set_reg(AC+1, MQ); + } + + if (hst_lnt) { + hst[hst_p].fmb = AR; + } + +last: +#if BBN + if (QBBN && page_fault) { + page_fault = 0; + AB = 070 + maoff; + f_pc_inh = 1; + pi_cycle = 1; + goto fetch; + } +#endif +#if KI | KL + /* Handle page fault and traps */ + if (page_enable && page_fault) { + page_fault = 0; + AB = ub_ptr + ((FLAGS & USER) ? 0427 : 0426); + MB = fault_data; + Mem_write_nopage(); + FLAGS |= trap_flag & (TRP1|TRP2); + trap_flag = 1; + AB = ((FLAGS & USER) ? ub_ptr : eb_ptr) | 0420; + f_pc_inh = 1; + pi_cycle = 1; + Mem_read_nopage(); + goto no_fetch; + } +#endif + +#if KI | KL + if (!f_pc_inh && (trap_flag == 0) && !pi_cycle) { +#else + if (!f_pc_inh && !pi_cycle) { +#endif + PC = (PC + 1) & RMASK; + } + +#if ITS + if (QITS && one_p_arm && (FLAGS & BYTI) == 0) { + fault_data |= 02000; + mem_prot = 1; + one_p_arm = 0; + } +#endif + + /* Dismiss an interrupt */ + if (pi_cycle) { +#if KI | KL + if (page_enable && page_fault) { + page_fault = 0; + inout_fail = 1; + } +#endif + + if ((IR & 0700) == 0700 && ((AC & 04) == 0)) { + pi_hold = pi_ov; + if ((!pi_hold) & f_inst_fetch) { + pi_cycle = 0; + } else { + AB = 040 | (pi_enc << 1) | pi_ov | maoff; +#if KI | KL + AB |= eb_ptr; + Mem_read_nopage(); +#else + Mem_read(1, 0, 1); +#endif + goto no_fetch; + } + } else if (pi_hold && !f_pc_inh) { + if ((IR & 0700) == 0700) { + (void)check_irq_level(); + } + AB = 040 | (pi_enc << 1) | pi_ov | maoff; + pi_ov = 0; + pi_hold = 0; +#if KI | KL + AB |= eb_ptr; + Mem_read_nopage(); +#else + Mem_read(1, 0, 1); +#endif + goto no_fetch; + } else { +#if KI | KL + if (f_pc_inh && trap_flag == 0) + set_pi_hold(); /* Hold off all lower interrupts */ +#else + if (!QITS || f_pc_inh) + set_pi_hold(); /* Hold off all lower interrupts */ +#endif +#if PDP6 + if ((IR & 0700) == 0700) + pi_cycle = 1; + else +#endif + pi_cycle = 0; + f_inst_fetch = 1; + f_load_pc = 1; + } + } + + if (pi_restore) { + restore_pi_hold(); + pi_restore = 0; + } + sim_interval--; + if (!pi_cycle && instr_count != 0 && --instr_count == 0) { +#if ITS + if (QITS) + load_quantum(); +#endif + return SCPE_STEP; + } +} +/* Should never get here */ +#if ITS +if (QITS) + load_quantum(); +#endif + +return reason; +} + +t_stat +rtc_srv(UNIT * uptr) +{ + int32 t; + t = sim_rtcn_calb (rtc_tps, TMR_RTC); + sim_activate_after(uptr, 1000000/rtc_tps); + tmxr_poll = t/2; + clk_flg = 1; + if (clk_en) { + sim_debug(DEBUG_CONO, &cpu_dev, "CONO timmer\n"); + set_interrupt(4, clk_irq); + } + return SCPE_OK; +} + +#if ITS +t_stat +qua_srv(UNIT * uptr) +{ + if ((fault_data & 1) == 0 && pi_enable && !pi_pending && (FLAGS & USER) != 0) { + mem_prot = 1; + fault_data |= 1; + } + qua_time = RSIGN; + return SCPE_OK; +} +#endif + + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int i; +BYF5 = uuo_cycle = 0; +#if KA | PDP6 +Pl = Ph = 01777; +Rl = Rh = Pflag = 0; +push_ovf = mem_prot = 0; +#if PDP6 +user_io = 0; +#endif +#if ITS | BBN +page_enable = 0; +#endif +#endif +nxm_flag = clk_flg = 0; +PIR = PIH = PIE = pi_enable = parity_irq = 0; +pi_pending = pi_enc = apr_irq = 0; +ov_irq =fov_irq =clk_en =clk_irq = 0; +pi_restore = pi_hold = 0; +#if KI | KL +ub_ptr = eb_ptr = 0; +pag_reload = ac_stack = 0; +fm_sel = small_user = user_addr_cmp = page_enable = 0; +#endif +#if BBN +exec_map = 0; +#endif + +for(i=0; i < 128; dev_irq[i++] = 0); +sim_brk_types = SWMASK('E') | SWMASK('W') | SWMASK('R'); +sim_brk_dflt = SWMASK ('E'); +sim_rtcn_init_unit (&cpu_unit[0], cpu_unit[0].wait, TMR_RTC); +sim_activate(&cpu_unit[0], 10000); +#if MPX_DEV +mpx_enable = 0; +#endif +#ifdef PANDA_LIGHTS +ka10_lights_init (); +#endif +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw) +{ +if (vptr == NULL) + return SCPE_ARG; +if (ea < 020) + *vptr = FM[ea] & FMASK; +else { + if (sw & SWMASK ('V')) { + if (ea >= MAXMEMSIZE) + return SCPE_REL; + } + if (ea >= MEMSIZE) + return SCPE_NXM; + *vptr = M[ea] & FMASK; + } +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) +{ +if (ea < 020) + FM[ea] = val & FMASK; +else { + if (sw & SWMASK ('V')) { + if (ea >= MAXMEMSIZE) + return SCPE_REL; + } + if (ea >= MEMSIZE) + return SCPE_NXM; + M[ea] = val & FMASK; + } +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 sval, CONST char *cptr, void *desc) +{ +int32 i; +int32 val = (int32)sval; + +if ((val <= 0) || ((val * 16 * 1024) > MAXMEMSIZE)) + return SCPE_ARG; +val = val * 16 * 1024; +if (val < (int32)MEMSIZE) { + uint64 mc = 0; + for (i = val-1; i < (int32)MEMSIZE; i++) + mc = mc | M[i]; + if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +} +for (i = (int32)MEMSIZE; i < val; i++) + M[i] = 0; +cpu_unit[0].capac = (uint32)val; +return SCPE_OK; +} + +/* Build device dispatch table */ +t_bool build_dev_tab (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j, d; + +/* Set trap offset based on MAOFF flag */ +maoff = (cpu_unit[0].flags & UNIT_MAOFF)? 0100 : 0; + +#if KA +/* Set up memory access routines based on current CPU type. */ + +/* Default to KA */ +Mem_read = &Mem_read_ka; +Mem_write = &Mem_write_ka; +#if ITS +if (QITS) { + Mem_read = &Mem_read_its; + Mem_write = &Mem_write_its; +} +#endif +#if BBN +if (QBBN) { + Mem_read = &Mem_read_bbn; + Mem_write = &Mem_write_bbn; +} +#endif +#if WAITS /* Waits without BBN pager */ +if (QWAITS && !QBBN) { + Mem_read = &Mem_read_waits; + Mem_write = &Mem_write_waits; +} +#endif +#endif + +/* Clear device and interrupt table */ +for (i = 0; i < 128; i++) { + dev_tab[i] = &null_dev; + dev_irqv[i] = NULL; +} + +/* Set up basic devices. */ +dev_tab[0] = &dev_apr; +dev_tab[1] = &dev_pi; +#if KI | KL +dev_tab[2] = &dev_pag; +#endif +#if BBN +if (QBBN) + dev_tab[024>>2] = &dev_pag; +#endif + +/* Assign all RH10 devices */ +for (j = i = 0; (dptr = rh_devs[i]) != NULL; i++) { + dibp = (DIB *) dptr->ctxt; + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + if (rh[j].dev_num == 0) + break; + d = rh[j].dev_num; + dev_tab[(d >> 2)] = dibp->io; + dev_irqv[(d >> 2)] = dibp->irq; + rh[j].dev = dptr; + j++; + } +} + +/* Make sure all are assigned */ +if (j == 4 && rh_devs[i] != NULL) { + sim_printf ("To many RH10 devices %s\n", sim_dname (dptr)); + return TRUE; +} + +/* Assign all remaining devices */ +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + dibp = (DIB *) dptr->ctxt; + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + for (j = 0; j < dibp->num_devs; j++) { /* loop thru disp */ + if (dibp->io) { /* any dispatch? */ + d = dibp->dev_num; + if (d & RH10_DEV) /* Skip RH10 devices */ + continue; + if (dev_tab[(d >> 2) + j] != &null_dev) { + /* already filled? */ + sim_printf ("%s device number conflict at %02o\n", + sim_dname (dptr), d + (j << 2)); + return TRUE; + } + dev_tab[(d >> 2) + j] = dibp->io; /* fill */ + dev_irqv[(d >> 2) + j] = dibp->irq; + } /* end if dsp */ + } /* end for j */ + } /* end if enb */ + } /* end for i */ + return FALSE; +} + +#if KI | KL + +/* Set serial */ +t_stat cpu_set_serial (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 lnt; +t_stat r; + +if (cptr == NULL) { + apr_serial = -1; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, 001777, &r); +if ((r != SCPE_OK) || (lnt <= 0)) + return SCPE_ARG; +apr_serial = lnt & 01777; +return SCPE_OK; +} + +/* Show serial */ +t_stat cpu_show_serial (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +fprintf (st, "Serial: " ); +if (apr_serial == -1) { + fprintf (st, "%d (default)", DEF_SERIAL); + return SCPE_OK; + } +fprintf (st, "%d", apr_serial); +return SCPE_OK; +} +#endif + +/* Set history */ +t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +int32 k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +t_value sim_eval; +InstHistory *h; + +if (hst_lnt == 0) /* enabled? */ + return SCPE_NOFNC; +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) + di = di + hst_lnt; +fprintf (st, "PC AC EA AR RES FLAGS IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + fprintf (st, "%06o ", h->pc & 0777777); + fprint_val (st, h->ac, 8, 36, PV_RZRO); + fputs (" ", st); + fprintf (st, "%06o ", h->ea); + fputs (" ", st); + fprint_val (st, h->mb, 8, 36, PV_RZRO); + fputs (" ", st); + fprint_val (st, h->fmb, 8, 36, PV_RZRO); + fputs (" ", st); +#if KI | KL + fprintf (st, "%c%06o ", ((h->flags & (PRV_PUB << 5))? 'p':' '), h->flags & 0777777); +#else + fprintf (st, "%06o ", h->flags); +#endif + if ((h->pc & HIST_PC2) == 0) { + sim_eval = h->ir; + fprint_val (st, sim_eval, 8, 36, PV_RZRO); + fputs (" ", st); + if ((fprint_sym (st, h->pc & RMASK, &sim_eval, &cpu_unit[0], SWMASK ('M'))) > 0) { + fputs ("(undefined) ", st); + fprint_val (st, h->ir, 8, 36, PV_RZRO); + } + } + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} + +t_stat +cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf(st, "%s\n\n", cpu_description(dptr)); + 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", CTY_SWITCH); + fprint_set_help(st, dptr); + fprint_show_help(st, dptr); + return SCPE_OK; +} + +const char * +cpu_description (DEVICE *dptr) +{ +#if KL + return "KL10A CPU"; +#endif +#if KI + return "KI10 CPU"; +#endif +#if KA + return "KA10 CPU"; +#endif +#if PDP6 + return "PDP6 CPU"; +#endif +} diff --git a/PDP10/kx10_cr.c b/PDP10/kx10_cr.c new file mode 100644 index 00000000..ef1bc18a --- /dev/null +++ b/PDP10/kx10_cr.c @@ -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 + diff --git a/PDP10/kx10_cty.c b/PDP10/kx10_cty.c new file mode 100644 index 00000000..32315ff3 --- /dev/null +++ b/PDP10/kx10_cty.c @@ -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 diff --git a/PDP10/kx10_dc.c b/PDP10/kx10_dc.c new file mode 100644 index 00000000..54001e46 --- /dev/null +++ b/PDP10/kx10_dc.c @@ -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<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 diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h new file mode 100644 index 00000000..26f2b5e0 --- /dev/null +++ b/PDP10/kx10_defs.h @@ -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 diff --git a/PDP10/kx10_df.c b/PDP10/kx10_df.c new file mode 100644 index 00000000..ceeb46ea --- /dev/null +++ b/PDP10/kx10_df.c @@ -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<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<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<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; +} diff --git a/PDP10/kx10_dk.c b/PDP10/kx10_dk.c new file mode 100644 index 00000000..ca05e979 --- /dev/null +++ b/PDP10/kx10_dk.c @@ -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 + +#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 diff --git a/PDP10/kx10_dp.c b/PDP10/kx10_dp.c new file mode 100644 index 00000000..2f41a4f5 --- /dev/null +++ b/PDP10/kx10_dp.c @@ -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 diff --git a/PDP10/kx10_dpy.c b/PDP10/kx10_dpy.c new file mode 100644 index 00000000..e8b38222 --- /dev/null +++ b/PDP10/kx10_dpy.c @@ -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 + +#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)< 0) +#define DT_DEVNUM 0320 +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ +#define D11_FILSIZ (D18_CAPAC * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ +#define DTC_FLAG_PIA 07 /* PI Channel */ +#define DTC_DATA_PIA 070 /* PI Channel */ +#define DTC_V_FNC 6 +#define DTC_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_RALL 01 /* read all */ +#define FNC_SRCH 02 /* search */ +#define FNC_READ 03 /* read */ +#define FNC_WMRK 04 /* write timing */ +#define FNC_WALL 05 /* write All */ +#define FNC_WBLK 06 /* Write Block */ +#define FNC_WRIT 07 /* write data */ +#define DTC_V_UNIT 9 /* unit select */ +#define DTC_M_UNIT 07 +#define DTC_DESEL 0010000 /* Deslect all units */ +#define DTC_SEL 0020000 /* Select unit */ +#define DTC_NODELAY 0040000 /* Don't delay */ +#define DTC_RVDRV 0100000 /* Move unit reverse */ +#define DTC_FWDRV 0200000 /* Move unit forward */ +#define DTC_STSTOP 0400000 /* Stop unit */ + +/* Flags in lower bits of u3 */ +#define DTC_FNC_STOP 001 /* Unit stopping */ +#define DTC_FNC_START 002 /* Start unit motion */ +#define DTC_FNC_REV 004 /* Unit to change direction */ + +/* CONO Unit +4 bit */ +#define DTS_FUNC_STOP 0000001 +#define DTS_STOP_ALL 0000002 +#define DTS_BLK_MISS 0010000 +#define DTS_END_ZONE 0020000 +#define DTS_ILL_OP 0040000 +#define DTS_JOB_DONE 0100000 +#define DTS_DATA_MISS 0200000 +#define DTS_PAR_ERR 0400000 + +#define DTC_GETFNC(x) (((x) >> DTC_V_FNC) & DTC_M_FNC) +#define DTC_GETUNI(x) (((x) >> DTC_V_UNIT) & DTC_M_UNIT) + + +/* Status register B */ +#define DTB_PARENB 0400000000000LL /* Parity Error Enable */ +#define DTB_TIMENB 0200000000000LL /* Data missed Enable */ +#define DTB_JOBENB 0100000000000LL /* Job done Enable */ +#define DTB_ILLENB 0040000000000LL /* Illegal Operation Enable */ +#define DTB_ENDENB 0020000000000LL /* End Zone Enable */ +#define DTB_MISENB 0010000000000LL /* Block Missed Enable */ +#define DTB_DLY 0004000000000LL /* Delay in progress */ +#define DTB_ACT 0002000000000LL /* Active */ +#define DTB_SPD 0001000000000LL /* Controller up to speed */ +#define DTB_BLK 0000400000000LL /* Block number */ +#define DTB_REV 0000200000000LL /* Reverse Check */ +#define DTB_DAT 0000100000000LL /* Data */ +#define DTB_FIN 0000040000000LL /* Final */ +#define DTB_CHK 0000020000000LL /* Checksum */ +#define DTB_IDL 0000010000000LL /* Idle */ +#define DTB_BLKRD 0000004000000LL /* Block Number Read */ +#define DTB_STOP 0000001000000LL /* Function Stop */ +#define DTB_PAR 0000000400000LL /* Parity Error */ +#define DTB_MIS 0000000200000LL /* Data Missed */ +#define DTB_DONE 0000000100000LL /* Job Done */ +#define DTB_ILL 0000000040000LL /* Illegal operation */ +#define DTB_END 0000000020000LL /* End Zone */ +#define DTB_BLKMIS 0000000010000LL /* Block Missed */ +#define DTB_WRLK 0000000004000LL /* Write Lock */ +#define DTB_WRMK 0000000002000LL /* Write Mark Switch */ +#define DTB_INCBLK 0000000001000LL /* Incomplete Block */ +#define DTB_MRKERR 0000000000200LL /* Mark Track Error */ +#define DTB_SELERR 0000000000100LL /* Select error */ +#define DTB_FLGREQ 0000000000002LL /* Flag Request */ +#define DTB_DATREQ 0000000000001LL /* Data Request */ + +#define DSTATE u5 /* Dectape current state */ +/* Current Dectape state in u5 */ +#define DTC_FEND 0 /* Tape in endzone */ +#define DTC_FBLK 1 /* In forward block number */ +#define DTC_FCHK 2 /* In forward checksum */ +#define DTC_BLOCK 3 /* In block */ +#define DTC_RCHK 4 /* In reverse checksum */ +#define DTC_RBLK 5 /* In reverse block number */ +#define DTC_REND 7 /* In final endzone */ + +#define DTC_MOTMASK 0170 +#define DTC_MOT 0010 /* Tape in motion */ +#define DTC_REV 0020 /* Tape in reverse */ +#define DTC_STOP 0040 /* Tape to stop */ +#define DTC_ACCL 0100 /* Tape accel or decl */ + +#define DTC_V_WORD 8 /* Shift for word count */ +#define DTC_M_WORD 0177 /* 128 words per block */ +#define DTC_V_BLK 16 /* Shift for Block number */ +#define DTC_M_BLK 01777 /* Block mask */ + +/* Logging */ + +#define LOG_MS 00200 /* move, search */ +#define LOG_RW 00400 /* read, write */ +#define LOG_RA 01000 /* read all */ +#define LOG_BL 02000 /* block # lblk */ + +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +#define DT_WRDTIM 15000 + +int32 dtsa = 0; /* status A */ +uint64 dtsb = 0; /* status B */ +uint64 dtdb = 0; /* data buffer */ +int dt_mpx_lvl; + +t_stat dt_devio(uint32 dev, uint64 *data); +t_stat dt_svc (UNIT *uptr); +t_stat dt_boot(int32 unit_num, DEVICE * dptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, CONST char *cptr); +t_stat dt_detach (UNIT *uptr); +#if MPX_DEV +t_stat dt_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) ; +t_stat dt_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +#endif + + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +DIB dt_dib = { DT_DEVNUM, 2, &dt_devio, NULL}; + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) } + }; + +REG dt_reg[] = { + { ORDATA (DTSA, dtsa, 18) }, + { ORDATA (DTSB, dtsb, 18) }, + { ORDATA (DTDB, dtdb, 18) }, + { ORDATA (MPX, dt_mpx_lvl, 3) }, + { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO | REG_UNIT) }, + { NULL } + }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, +#if MPX_DEV + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MPX", "MPX", + &dt_set_mpx, &dt_show_mpx, NULL}, +#endif + { 0 } + }; + +DEBTAB dt_deb[] = { + { "CMD", DEBUG_CMD, "Show command execution to devices"}, + { "DATA", DEBUG_DATA, "Show data transfers"}, + { "DETAIL", DEBUG_DETAIL, "Show details about device"}, + { "EXP", DEBUG_EXP, "Show exception information"}, + { "CONI", DEBUG_CONI, "Show coni instructions"}, + { "CONO", DEBUG_CONO, "Show coni instructions"}, + { "DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "READALL", LOG_RA }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &dt_reset, &dt_boot, &dt_attach, &dt_detach, + &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, + dt_deb, NULL, NULL + }; + +/* IOT routines */ +t_stat dt_devio(uint32 dev, uint64 *data) { + int i; + + switch(dev & 07) { + case CONI: + *data = (uint64)dtsa; + sim_debug(DEBUG_CONI, &dt_dev, "DTA %03o CONI %06o PC=%o\n", + dev, (uint32)*data, PC); + break; + + case CONO: + clr_interrupt(dev); + clr_interrupt(dev|4); + /* Copy over command and priority */ + dtsa &= ~0777; + dtsa |= (*data & 0777); + dtsb = 0; + sim_debug(DEBUG_CONO, &dt_dev, "DTA %03o CONO %06o PC=%o\n", + dev, (uint32)*data, PC); + /* Check bits in command register */ + if (*data & DTC_DESEL) { + /* Stop all drives and clear drive unit */ + dtsa &= 0770777; + for (i = 0; i < DT_NUMDR; i++) { + dt_unit[i].u3 &= ~0700; + } + if ((*data & DTC_SEL) == 0) + break; + } + if (*data & DTC_SEL) { + dtsa |= *data & 07000; + i = DTC_GETUNI(dtsa); + if ((dt_unit[i].flags & UNIT_ATT) == 0) { + dtsb |= DTB_ILL|DTB_SELERR; + dtsb &= ~DTB_IDL; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + return SCPE_OK; + } + if (i < DT_NUMDR && !sim_is_active(&dt_unit[i])) + sim_activate(&dt_unit[i], 1000); + if (dt_unit[i].DSTATE & DTC_MOT) { + switch (dt_unit[i].DSTATE & 7) { + case DTC_FEND: /* Tape in endzone */ + case DTC_REND: /* In final endzone */ + dtsb |= DTB_END|DTB_IDL; + break; + + case DTC_FBLK: /* In forward block number */ + case DTC_RBLK: /* In reverse block number */ + dtsb |= DTB_BLK|DTB_IDL; + break; + + case DTC_RCHK: /* In reverse checksum */ + case DTC_FCHK: /* In forward checksum */ + dtsb |= DTB_CHK|DTB_IDL; + break; + + case DTC_BLOCK: /* In block */ + dtsb |= DTB_DAT; + break; + } + } else { + dtsb |= DTB_IDL; + } + } + if (*data & (DTC_FWDRV|DTC_RVDRV|DTC_STSTOP)) { + i = DTC_GETUNI(dtsa); +#if DT_NUMDR < 8 + if (i >= DT_NUMDR) + break; +#endif + if ((dt_unit[i].flags & UNIT_ATT) == 0) { + dtsb |= DTB_ILL; + dtsb &= ~DTB_IDL; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + return SCPE_OK; + } + if (*data & DTC_STSTOP) { + if ((dt_unit[i].DSTATE & (DTC_MOT)) != 0) { + dt_unit[i].u3 |= DTC_FNC_STOP; + } + dtsa &=~ (DTC_FWDRV|DTC_RVDRV); + } else { + /* Start the unit if not already running */ + dt_unit[i].u3 &= ~DTC_FNC_STOP; + if ((dt_unit[i].DSTATE & (DTC_MOT)) == 0) { + dt_unit[i].u3 |= DTC_FNC_START; + dtsb |= DTB_DLY; + if (!sim_is_active(&dt_unit[i])) + sim_activate(&dt_unit[i], 10000); + } + dtsa &=~ (DTC_FWDRV|DTC_RVDRV); + switch(*data & (DTC_FWDRV|DTC_RVDRV)) { + case DTC_FWDRV: + if (dt_unit[i].DSTATE & DTC_REV) { + dt_unit[i].u3 |= DTC_FNC_REV; + dtsa |= (DTC_RVDRV); + } else + dtsa |= (DTC_FWDRV); + break; + case DTC_RVDRV: + if ((dt_unit[i].DSTATE & DTC_REV) == 0) { + dt_unit[i].u3 |= DTC_FNC_REV; + dtsa |= (DTC_RVDRV); + } else + dtsa |= (DTC_FWDRV); + break; + case DTC_FWDRV|DTC_RVDRV: + dt_unit[i].u3 |= DTC_FNC_REV; + if ((dt_unit[i].DSTATE & DTC_REV) == 0) + dtsa |= (DTC_RVDRV); + else + dtsa |= (DTC_FWDRV); + break; + } + } + } + break; + + case DATAI: + *data = dtdb; + dtsb &= ~DTB_DATREQ; + clr_interrupt(dev|4); + sim_debug(DEBUG_DATAIO, &dt_dev, "DTA %03o DATI %012llo PC=%06o\n", + dev, *data, PC); + + break; + + case DATAO: + dtdb = *data; + dtsb &= ~DTB_DATREQ; + clr_interrupt(dev|4); + sim_debug(DEBUG_DATAIO, &dt_dev, "DTA %03o DATO %012llo PC=%06o\n", + dev, *data, PC); + break; + + case CONI|04: + *data = dtsb; + if (dtsb & 0770000) + *data |= DTB_FLGREQ; + sim_debug(DEBUG_CONI, &dt_dev, "DTB %03o CONI %012llo PC=%o\n", + dev, *data, PC); + break; + + case CONO|04: + dtsb = 0; + clr_interrupt(dev); + clr_interrupt(dev|4); + if (*data & DTS_STOP_ALL) { + /* Stop all other drives */ + for (i = 0; i < DT_NUMDR; i++) { + if (i != DTC_GETUNI(dtsa) && + (dt_unit[i].DSTATE & DTC_MOT) != 0) + dt_unit[i].u3 |= DTC_FNC_STOP; + } + } + dtsb = (uint64)((*data & (DTS_PAR_ERR|DTS_DATA_MISS|DTS_JOB_DONE| \ + DTS_ILL_OP|DTS_END_ZONE|DTS_BLK_MISS)) << 18); + if (*data & DTS_FUNC_STOP) + dtsb |= DTB_STOP; + + sim_debug(DEBUG_CONO, &dt_dev, "DTB %03o CONO %06o PC=%o DTSB=%012llo\n", + dev, (uint32)*data, PC, dtsb); + break; + + case DATAI|4: + sim_debug(DEBUG_DATAIO, &dt_dev, "DTB %03o DATI %012llo PC=%06o\n", + dev, *data, PC); + break; + case DATAO|4: + sim_debug(DEBUG_DATAIO, &dt_dev, "DTB %03o DATO %012llo PC=%06o\n", + dev, *data, PC); + break; + + } + return SCPE_OK; +} + +void dt_getword(uint64 *data, int req) { + int dev = dt_dib.dev_num; + clr_interrupt(dev|4); + if (dtsb & DTB_DATREQ) { + dtsb |= DTB_MIS; + return; + } + *data = dtdb; + if (req) { + dtsb |= DTB_DATREQ; + set_interrupt_mpx(dev|4, dtsa >> 3, dt_mpx_lvl); + } +} + +void dt_putword(uint64 *data) { + int dev = dt_dib.dev_num; + clr_interrupt(dev|4); + if (dtsb & DTB_DATREQ) { + dtsb |= DTB_MIS; + return; + } + dtdb = *data; + dtsb |= DTB_DATREQ; + set_interrupt_mpx(dev|4, dtsa >> 3, dt_mpx_lvl); +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ + int word; + uint64 data = 0; + uint32 *fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + int u = uptr-dt_unit; + int blk; + int off; +/* + * Check if in motion or stopping. + */ +if (uptr->DSTATE & DTC_MOT) { + /* Check if stoping */ + if (uptr->u3 & DTC_FNC_STOP) { + /* Stop delay */ + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o stopping\n", u); + sim_activate(uptr, DT_WRDTIM*10); + uptr->u3 &= ~DTC_FNC_STOP; + uptr->DSTATE &= ~(DTC_MOT); + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = (0100 << DTC_V_WORD) | DTC_BLOCK | (DTC_MOTMASK & uptr->DSTATE); + if (uptr->DSTATE & DTC_REV) { + if (blk <= 0) { + blk = 0; + uptr->DSTATE = DTC_FEND | (DTC_MOTMASK & uptr->DSTATE); + } else { + blk--; + } + } else { + if (blk <= 01100) + blk++; + } + uptr->DSTATE |= (blk << DTC_V_BLK); + return SCPE_OK; + } + if (uptr->u3 & DTC_FNC_REV) { + sim_activate(uptr, DT_WRDTIM*10); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o reversing\n", u); + uptr->u3 &= ~DTC_FNC_REV; + uptr->DSTATE ^= DTC_REV; + return SCPE_OK; + } + + if (DTC_GETUNI(dtsa) == u) { + dtsb |= DTB_SPD; + dtsb &= ~(DTB_DLY|DTB_IDL); + } + /* Moving in reverse direction */ + if (uptr->DSTATE & DTC_REV) { + if (DTC_GETUNI(dtsa) == u) { + dtsb |= DTB_REV; + dtsa &=~ DTC_FWDRV; + dtsa |= DTC_RVDRV; + } + switch (uptr->DSTATE & 7) { + case DTC_FEND: /* Tape in endzone */ + /* Set stop */ + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o rev forward end\n", u); + uptr->u3 |= DTC_FNC_STOP; + uptr->u6 = 0; + dtsb |= DTB_END; + dtsb &= ~DTB_IDL; + if (dtsb & DTB_ENDENB) + set_interrupt(DT_DEVNUM, dtsa); + sim_activate(uptr, DT_WRDTIM*10); + break; + + case DTC_FBLK: /* In forward block number */ + sim_activate(uptr,DT_WRDTIM); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word--; + if (word == 0) + uptr->DSTATE = DTC_FEND | (DTC_MOTMASK & uptr->DSTATE); + else + uptr->DSTATE = DTC_RBLK|(word << DTC_V_BLK) | (DTC_MOTMASK & uptr->DSTATE); + dtsb &= ~(DTB_CHK); + dtsb |= DTB_IDL; + if (dtsb & DTB_STOP) + dtsa &= ~0700; /* Clear command */ + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o rev forward block\n", u); + switch (DTC_GETFNC(uptr->u3)) { + case FNC_MOVE: + case FNC_SRCH: + case FNC_WBLK: + if ((dtsb & DTB_STOP) == 0) + break; + /* Fall through */ + case FNC_WALL: + case FNC_RALL: + case FNC_WRIT: + case FNC_READ: + uptr->u3 &= 077077; + dtsb |= DTB_DONE; + if (dtsb & DTB_JOBENB) + set_interrupt(DT_DEVNUM, dtsa); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o rev stop\n", u); + dtsb &= ~DTB_STOP; + break; + case FNC_WMRK: + dtsb |= DTS_ILL_OP; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + } + if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { + uptr->u3 |= DTC_FNC_STOP; + } + if (DTC_GETUNI(dtsa) == u) { + uptr->u3 &= 077077; + uptr->u3 |= dtsa & 0700; /* Copy command */ + } + if (word <= 0) { + uptr->DSTATE = DTC_FEND | (DTC_MOTMASK & uptr->DSTATE); + } + break; + + case DTC_FCHK: /* In forward checksum */ + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o rev forward check\n", u); + sim_activate(uptr,DT_WRDTIM*2); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = DTC_FBLK|(word << DTC_V_BLK) | (DTC_MOTMASK & uptr->DSTATE); + dtsb &= ~(DTB_DAT|DTB_FIN); + dtsb |= DTB_CHK; + break; + + case DTC_BLOCK: /* In block */ + sim_activate(uptr,DT_WRDTIM); + dtsb |= DTB_DAT; + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word = (uptr->DSTATE >> DTC_V_WORD) & DTC_M_WORD; + off = ((blk << 7) + word) << 1; + /* Check if at end of block */ + if (word == 0) { + uptr->DSTATE &= ~((DTC_M_WORD << DTC_V_WORD) | 7); + uptr->DSTATE |= DTC_FCHK; /* Move to Checksum */ + dtsb &= ~DTB_DAT; + dtsb |= DTB_FIN; + } else { + uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD); + uptr->DSTATE |= (word - 1) << DTC_V_WORD; + } + uptr->u6-=2; + switch (DTC_GETFNC(uptr->u3)) { + case FNC_MOVE: + case FNC_SRCH: + case FNC_WBLK: + break; + case FNC_WMRK: + dtsb |= DTS_ILL_OP; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + case FNC_RALL: + case FNC_READ: + data = ((uint64)fbuf[off]) << 18; + data |= ((uint64)fbuf[off+1]); + if ((dtsb & DTB_STOP) == 0) + dt_putword(&data); + break; + + case FNC_WRIT: + case FNC_WALL: + if ((dtsb & DTB_STOP) == 0) + dt_getword(&data, (word != 0)); + else + data = dtdb; + fbuf[off] = (data >> 18) & RMASK; + fbuf[off+1] = data & RMASK; + uptr->hwmark = uptr->capac; + break; + } + if (word == 0) { + dtsb &= ~DTB_DAT; + dtsb |= DTB_FIN; + } + sim_debug(DEBUG_DETAIL, &dt_dev, + "DTA %o rev data word %o:%o %012llo %d %06o %06o\n", + u, blk, word, data, off, fbuf[off], fbuf[off+1]); + break; + + case DTC_RCHK: /* In reverse checksum */ + sim_activate(uptr,DT_WRDTIM*2); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o rev reverse check\n", u); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = DTC_BLOCK|(word << DTC_V_BLK)|(DTC_M_WORD << DTC_V_WORD) | (DTC_MOTMASK & uptr->DSTATE); + if (dtsb & DTB_STOP) + dtsa &= ~0700; /* Clear command */ + if (DTC_GETUNI(dtsa) == u) { + uptr->u3 &= 077077; + uptr->u3 |= dtsa & 0700; /* Copy command */ + } + dtsb &= ~DTB_BLKRD; + switch (DTC_GETFNC(uptr->u3)) { + case FNC_WRIT: + case FNC_WALL: + dtsb |= DTB_DATREQ; + set_interrupt_mpx(DT_DEVNUM|4, dtsa >> 3, dt_mpx_lvl); + break; + case FNC_RALL: + case FNC_MOVE: + case FNC_READ: + case FNC_WBLK: + break; + case FNC_SRCH: + dtsb |= DTB_DONE; + dtsb &= ~DTB_STOP; + if (dtsb & DTB_JOBENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + case FNC_WMRK: + dtsb |= DTS_ILL_OP; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + } + if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { + uptr->u3 |= DTC_FNC_STOP; + } + break; + + case DTC_RBLK: /* In reverse block number */ + sim_activate(uptr,DT_WRDTIM*2); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + data = (uint64)word; + uptr->DSTATE = DTC_RCHK|(word << DTC_V_BLK)|(DTC_M_WORD << DTC_V_WORD) | (DTC_MOTMASK & uptr->DSTATE); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o rev reverse block %04o\n", u, word); + dtsb &= ~DTB_END; + dtsb |= DTB_BLKRD; + if (DTC_GETUNI(dtsa) == u) { + uptr->u3 &= 077077; + uptr->u3 |= dtsa & 0700; /* Copy command */ + } + switch (DTC_GETFNC(uptr->u3)) { + case FNC_MOVE: + case FNC_READ: + case FNC_WMRK: + case FNC_WRIT: + break; + case FNC_RALL: + dt_putword(&data); + break; + case FNC_SRCH: + dt_putword(&data); + break; + case FNC_WALL: + case FNC_WBLK: + dt_getword(&data, 0); + break; + } + break; + + case DTC_REND: /* In final endzone */ + sim_activate(uptr, DT_WRDTIM*10); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word--; + uptr->DSTATE = DTC_RBLK|(word << DTC_V_BLK) | (DTC_MOTMASK & uptr->DSTATE); + break; + } + } else { + if (DTC_GETUNI(dtsa) == u) { + dtsb &= ~DTB_REV; + dtsa &=~ DTC_RVDRV; + dtsa |= DTC_FWDRV; + } + /* Moving in forward direction */ + switch (uptr->DSTATE & 7) { + case DTC_FEND: /* Tape in endzone */ + sim_activate(uptr, DT_WRDTIM*10); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o forward end\n", u); + uptr->DSTATE = DTC_FBLK | (DTC_MOTMASK & uptr->DSTATE); /* Move to first block */ + uptr->u6 = 0; + dtsb &= ~DTB_IDL; + break; + + case DTC_FBLK: /* In forward block number */ + sim_activate(uptr,DT_WRDTIM*2); + dtsb &= ~DTB_END; + dtsb |= DTB_BLKRD; + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = DTC_FCHK|(word << DTC_V_BLK) | (DTC_MOTMASK & uptr->DSTATE); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o forward block %04o\n", u, word); + data = (uint64)word; + if (DTC_GETUNI(dtsa) == u) { + uptr->u3 &= 077077; + uptr->u3 |= dtsa & 0700; /* Copy command */ + } + switch (DTC_GETFNC(uptr->u3)) { + case FNC_RALL: + case FNC_SRCH: + dt_putword(&data); + break; + case FNC_MOVE: + case FNC_READ: + case FNC_WRIT: + break; + case FNC_WALL: + case FNC_WBLK: + dt_getword(&data, 0); + break; + case FNC_WMRK: + dtsb |= DTS_ILL_OP; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + } + break; + + case DTC_FCHK: /* In forward checksum */ + sim_activate(uptr,DT_WRDTIM*2); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o forward check\n", u); + dtsb &= ~DTB_BLKRD; + uptr->DSTATE &= ~7; + uptr->DSTATE |= DTC_BLOCK; /* Move to datablock */ + if (dtsb & DTB_STOP) + dtsa &= ~0700; /* Clear command */ + if (DTC_GETUNI(dtsa) == u) { + uptr->u3 &= 077077; + uptr->u3 |= dtsa & 0700; /* Copy command */ + } + switch (DTC_GETFNC(uptr->u3)) { + case FNC_WRIT: + case FNC_WALL: + dtsb |= DTB_DATREQ; + set_interrupt_mpx(DT_DEVNUM|4, dtsa >> 3, dt_mpx_lvl); + break; + case FNC_SRCH: + dtsb |= DTB_DONE; + dtsb &= ~DTB_STOP; + if (dtsb & DTB_JOBENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + case FNC_WMRK: + dtsb |= DTS_ILL_OP; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + case FNC_RALL: + case FNC_READ: + case FNC_WBLK: + case FNC_MOVE: + break; + } + if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { + uptr->u3 |= DTC_FNC_STOP; + } + break; + + case DTC_BLOCK: /* In block */ + sim_activate(uptr,DT_WRDTIM); + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word = (uptr->DSTATE >> DTC_V_WORD) & DTC_M_WORD; + off = ((blk << 7) + word) << 1; + dtsb |= DTB_DAT; + /* Check if at end of block */ + if (word == DTC_M_WORD) { + uptr->DSTATE &= ~7; + uptr->DSTATE |= DTC_RCHK; /* Move to checksum */ + dtsb |= DTB_FIN; + } else { + uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD); + uptr->DSTATE |= (word + 1) << DTC_V_WORD; + } + switch (DTC_GETFNC(uptr->u3)) { + case FNC_MOVE: + case FNC_SRCH: + case FNC_WALL: + case FNC_WBLK: + break; + case FNC_RALL: + case FNC_READ: + data = ((uint64)fbuf[off]) << 18; + data |= (uint64)fbuf[off+1]; + if ((dtsb & DTB_STOP) == 0) + dt_putword(&data); + else + uptr->u3 &= 077077; + break; + case FNC_WRIT: + if ((dtsb & DTB_STOP) == 0) + dt_getword(&data, (word != DTC_M_WORD)); + else { + uptr->u3 &= 077077; + data = dtdb; + } + fbuf[off] = (data >> 18) & RMASK; + fbuf[off+1] = data & RMASK; + uptr->hwmark = uptr->capac; + break; + case FNC_WMRK: + dtsb |= DTS_ILL_OP; + if (dtsb & DTB_ILLENB) + set_interrupt(DT_DEVNUM, dtsa); + break; + } + if (word == DTC_M_WORD) { + dtsb &= ~DTB_DAT; + dtsb |= DTB_FIN; + } + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o data word %o:%o %012llo %d %06o %06o\n", + u, blk, word, data, off, fbuf[off], fbuf[off+1]); + break; + + case DTC_RCHK: /* In reverse checksum */ + sim_activate(uptr,DT_WRDTIM*2); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o reverse check\n", u); + uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD) | 7; + uptr->DSTATE |= DTC_RBLK; /* Move to end of block */ + dtsb &= ~(DTB_DAT|DTB_FIN); + dtsb |= DTB_CHK; + break; + + case DTC_RBLK: /* In reverse block number */ + sim_activate(uptr,DT_WRDTIM*2); + dtsb &= ~(DTB_CHK); + dtsb |= DTB_IDL; + if (DTC_GETUNI(dtsa) == u) { + uptr->u3 &= 077077; + uptr->u3 |= dtsa & 0700; /* Copy command */ + } + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word++; + if (word > 01101) { + uptr->DSTATE = DTC_REND|(word << DTC_V_BLK)|(DTC_M_WORD << DTC_V_WORD) | (DTC_MOTMASK & uptr->DSTATE); + } else { + uptr->DSTATE = DTC_FBLK|(word << DTC_V_BLK) | (DTC_MOTMASK & uptr->DSTATE); + } + if (dtsb & DTB_STOP) + dtsa &= ~0700; /* Clear command */ + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o reverse block %o\n", u, word); + switch (DTC_GETFNC(uptr->u3)) { + case FNC_MOVE: + case FNC_WBLK: + case FNC_SRCH: + if ((dtsb & DTB_STOP) == 0) + break; + /* Fall through */ + case FNC_WALL: + case FNC_RALL: + case FNC_WRIT: + case FNC_READ: + case FNC_WMRK: + uptr->u3 &= 077077; + dtsb |= DTB_DONE; + if (dtsb & DTB_JOBENB) + set_interrupt(DT_DEVNUM, dtsa); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o stop\n", u); + dtsb &= ~DTB_STOP; + break; + } + if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { + uptr->u3 |= DTC_FNC_STOP; + } + break; + + case DTC_REND: /* In final endzone */ + /* Set stop */ + uptr->u3 |= DTC_FNC_STOP; + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o reverse end\n", u); + dtsb &= ~DTB_IDL; + dtsb |= DTB_END; + if (dtsb & DTB_ENDENB) + set_interrupt(DT_DEVNUM, dtsa); + sim_activate(uptr, DT_WRDTIM*10); + break; + } + } +/* Check if starting */ +} else if (uptr->u3 & DTC_FNC_START) { + /* Start up delay */ + sim_activate(uptr, DT_WRDTIM*10); + uptr->u3 &= ~(0700 | DTC_FNC_START); + if (DTC_GETUNI(dtsa) == u) + uptr->u3 |= dtsa & 0700; /* Copy command */ + uptr->DSTATE |= DTC_MOT; + if (uptr->u3 & DTC_FNC_REV) { + uptr->u3 &= ~DTC_FNC_REV; + uptr->DSTATE ^= DTC_REV; + } + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o start %06o\n", u, uptr->u3); + return SCPE_OK; +} +return SCPE_OK; +} + +/* Boot from given device */ +t_stat +dt_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + uint32 *fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + uint64 word; + int off; + int wc, addr; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + off = 0; + wc = fbuf[off++]; + addr = fbuf[off++]; + while (wc != 0) { + wc = (wc + 1) & RMASK; + addr = (addr + 1) & RMASK; + word = ((uint64)fbuf[off++]) << 18; + word |= (uint64)fbuf[off++]; + if (addr < 020) + FM[addr] = word; + else + M[addr] = word; + } + if (addr < 020) + FM[addr] = word; + else + M[addr] = word; + uptr->DSTATE = (1 << DTC_V_BLK) | DTC_BLOCK | DTC_MOT; + sim_activate(uptr,30000); + PC = word & RMASK; + return SCPE_OK; +} + +#if MPX_DEV +/* set MPX level number */ +t_stat dt_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 mpx; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + mpx = (int32) get_uint (cptr, 8, 8, &r); + if (r != SCPE_OK) + return r; + dt_mpx_lvl = mpx; + return SCPE_OK; +} + +t_stat dt_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + + fprintf (st, "MPX=%o", dt_mpx_lvl); + return SCPE_OK; +} +#endif + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ + int i; + + dtsb = dtsa = 0; /* clear status */ + for (i = 0; i < DT_NUMDR; i++) { + if ((dt_unit[i].DSTATE & DTC_MOT) != 0) + dt_unit[i].u3 |= DTC_FNC_STOP; + } + clr_interrupt(DT_DEVNUM); + clr_interrupt(DT_DEVNUM|4); + return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 12b, read 12b format and convert to 18b in buffer + If 16b, read 16b format and convert to 18b in buffer + If 18b/36b, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, CONST char *cptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, sz, k, *fbuf; +int32 u = uptr - dt_dev.units; +t_stat r; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) /* error? */ + return r; +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */ + if (sim_switches & SWMASK ('T')) /* att 12b? */ + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = uptr->flags | UNIT_11FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D8_FILSIZ) + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sz == D11_FILSIZ) + uptr->flags = uptr->flags | UNIT_11FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +printf ("%s%d: ", sim_dname (&dt_dev), u); +if (uptr->flags & UNIT_8FMT) + printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) + printf ("16b format"); +else printf ("18b/36b format"); +printf (", buffering file in memory\n"); +if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (k == 0) + break; + for ( ; k < D8_NBSIZE; k++) + pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | + ((uint32) (pdp8b[k + 1] >> 6) & 077); + fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | + ((uint32) pdp8b[k + 2] & 07777); + ba = ba + 2; /* end blk loop */ + } + } /* end file loop */ + uptr->hwmark = ba; + } /* end if */ +else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (k == 0) + break; + for ( ; k < D18_BSIZE; k++) + pdp11b[k] = 0; + for (k = 0; k < D18_BSIZE; k++) + fbuf[ba++] = pdp11b[k]; + } + uptr->hwmark = ba; + } /* end elif */ +else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), + uptr->capac, uptr->fileref); +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, convert 18b buffer to 12b and write to file + If 16b, convert 18b buffer to 16b and write to file + If 18b/36b, write buffer to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, k, *fbuf; +int32 u = uptr - dt_dev.units; + +if (!(uptr->flags & UNIT_ATT)) + return SCPE_OK; +if (sim_is_active (uptr)) { + sim_cancel (uptr); + uptr->u3 = uptr->pos = 0; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (fbuf[ba] >> 6) & 07777; + pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | + ((fbuf[ba + 1] >> 12) & 077); + pdp8b[k + 2] = fbuf[ba + 1] & 07777; + ba = ba + 2; + } /* end loop blk */ + fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (ferror (uptr->fileref)) + break; + } /* end loop file */ + } /* end if 12b */ + else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D18_BSIZE; k++) /* loop blk */ + pdp11b[k] = fbuf[ba++] & 0177777; + fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (ferror (uptr->fileref)) + break; + } /* end loop file */ + } /* end if 16b */ + else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ + uptr->hwmark, uptr->fileref); + if (ferror (uptr->fileref)) + perror ("I/O error"); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} +#endif diff --git a/PDP10/kx10_imp.c b/PDP10/kx10_imp.c new file mode 100644 index 00000000..e4582234 --- /dev/null +++ b/PDP10/kx10_imp.c @@ -0,0 +1,2186 @@ +/* ka10_imp.c: IMP, interface message processor. + + Copyright (c) 2018, Richard Cornwell based on code provided by + Lars Brinkhoff and Danny Gasparovski. + + 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 emulates the MIT-AI/ML/MC Host/IMP interface. +*/ + + +#include "kx10_defs.h" +#include "sim_ether.h" + +#if NUM_DEVS_IMP > 0 +#define IMP_DEVNUM 0460 +#define WA_IMP_DEVNUM 0400 + +#define DEVNUM imp_dib.dev_num + +#define UNIT_V_DHCP (UNIT_V_UF + 0) /* DHCP enable flag */ +#define UNIT_DHCP (1 << UNIT_V_DHCP) +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* Type of IMP interface */ +#define UNIT_M_DTYPE 3 +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) + +#define TYPE_MIT 0 /* MIT Style KAIMP ITS */ +#define TYPE_BBN 1 /* BBN style interface TENEX */ +#define TYPE_WAITS 2 /* IMP connected to waits system. */ + +/* ITS IMP Bits */ + +/* CONI */ +#define IMPID 010 /* Input done. */ +#define IMPI32 020 /* Input in 32 bit mode. */ +#define IMPIB 040 /* Input busy. */ +#define IMPOD 0100 /* Output done. */ +#define IMPO32 0200 /* Output in 32-bit mode. */ +#define IMPOB 0400 /* Output busy. */ +#define IMPERR 01000 /* IMP error. */ +#define IMPR 02000 /* IMP ready. */ +#define IMPIC 04000 /* IMP interrupt condition. */ +#define IMPHER 010000 /* Host error. */ +#define IMPHR 020000 /* Host ready. */ +#define IMPIHE 040000 /* Inhibit interrupt on host error. */ +#define IMPLW 0100000 /* Last IMP word. */ + +/* CONO */ +#define IMPIDC 010 /* Clear input done */ +#define IMI32S 020 /* Set 32-bit output */ +#define IMI32C 040 /* Clear 32-bit output */ +#define IMPODC 0100 /* Clear output done */ +#define IMO32S 0200 /* Set 32-bit input */ +#define IMO32C 0400 /* Clear 32-bit input */ +#define IMPODS 01000 /* Set output done */ +#define IMPIR 04000 /* Enable interrupt on IMP ready */ +#define IMPHEC 010000 /* Clear host error */ +#define IMIIHE 040000 /* Inhibit interrupt on host error */ +#define IMPLHW 0200000 /* Set last host word. */ + +/* BBN IMP BITS */ + +/* CONO bits */ +#define IMP_EN_IN 00000010 /* Enable input PIA channel */ +#define IMP_EN_OUT 00000200 /* Enable output PIA channel */ +#define IMP_EN_END 00004000 /* Enable end PIA channel */ +#define IMP_END_IN 00010000 /* End of input */ +#define IMP_END_OUT 00020000 /* End of output */ +#define IMP_STOP 00040000 /* Stop the imp */ +#define IMP_PDP_DN 00100000 /* PDP-10 is down */ +#define IMP_CLR 00200000 /* Clear imp down flag */ +#define IMP_RST 00400000 /* Reset IMP */ + +/* CONI bits */ +#define IMP_IFULL 00000010 /* Input full */ +#define IMP_OEMPY 00000200 /* Output empty */ +#define IMP_ENDIN 00014000 /* End of input */ +#define IMP_DN 00020000 /* IMP down */ +#define IMP_WAS_DN 00040000 /* IMP was down */ +#define IMP_PWR 00200000 /* IMP Rdy */ + +/* WAITS IMP BITS */ + +/* CONO bits */ +#define IMP_ODPIEN 0000010 /* Enable change of output done PIA, also set byte size */ +#define IMP_IDPIEN 0000020 /* Enable change of input done PIA, also set byte size */ +#define IMP_IEPIEN 0000040 /* Change end of input PIA */ +#define IMP_FINO 0000100 /* Last bit of output */ +#define IMP_STROUT 0000200 /* Start output */ +#define IMP_CLRWT 0002000 /* Clear waiting to input bit */ +#define IMP_CLRST 0004000 /* Clear stop after input bit */ +#define IMP_O32 0010000 /* Set output to 32bit */ +#define IMP_I32 0020000 /* Set input to 32bit */ +#define IMP_STRIN 0040000 /* Start input */ +#define IMP_TEST 0100000 /* Test mode */ + +/* CONI bits */ +#define IMP_ODONE 0004000 /* Output done */ +#define IMP_IEND 0010000 /* Input end. */ +#define IMP_IDONE 0020000 /* Input done */ +#define IMP_ERR 0040000 /* Imp error */ +#define IMP_RDY 0200000 /* Imp ready */ +#define IMP_OCHN 0000007 +#define IMP_ICHN 0000070 +#define IMP_ECHN 0000700 + +/* CONI timeout. If no CONI instruction is executed for 3-5 seconds, + the interface will raise the host error signal. */ +#define CONI_TIMEOUT 3000000 + +#define STATUS u3 +#define OPOS u4 /* Output bit position */ +#define IPOS u5 /* Input bit position */ +#define ILEN u6 /* Size of input buffer in bits */ + + +#ifdef _MSC_VER +# define PACKED_BEGIN __pragma( pack(push, 1) ) +# define PACKED_END __pragma( pack(pop) ) +# define QEMU_PACKED +#else +# define PACKED_BEGIN +#if defined(_WIN32) +# define PACKED_END __attribute__((gcc_struct, packed)) +# define QEMU_PACKED __attribute__((gcc_struct, packed)) +#else +# define PACKED_END __attribute__((packed)) +# define QEMU_PACKED __attribute__((packed)) +#endif +#endif + +#define IMP_ARPTAB_SIZE 8 + +uint32 mask[] = { + 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, + 0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, + 0xFFFFFF00, 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800, + 0xFFFFF000, 0xFFFFE000, 0xFFFFC000, 0xFFFF8000, + 0xFFFF0000, 0xFFFE0000, 0xFFFC0000, 0xFFF80000, + 0xFFF00000, 0xFFE00000, 0xFFC00000, 0xFF800000, + 0xFF000000, 0xFE000000, 0xFC000000, 0xF8000000, + 0xF0000000, 0xE0000000, 0xC0000000, 0x80000000, + 0x00000000}; + +typedef uint32 in_addr_T; + +PACKED_BEGIN +struct imp_eth_hdr { + ETH_MAC dest; + ETH_MAC src; + uint16 type; +} PACKED_END; + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 + +/* + * Structure of an internet header, naked of options. + */ +PACKED_BEGIN +struct ip { + uint8 ip_v_hl; /* version,header length */ + uint8 ip_tos; /* type of service */ + uint16 ip_len; /* total length */ + uint16 ip_id; /* identification */ + uint16 ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* don't fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + uint8 ip_ttl; /* time to live */ + uint8 ip_p; /* protocol */ + uint16 ip_sum; /* checksum */ + in_addr_T ip_src; + in_addr_T ip_dst; /* source and dest address */ +} PACKED_END; + +#define TCP_PROTO 6 +PACKED_BEGIN +struct tcp { + uint16 tcp_sport; /* Source port */ + uint16 tcp_dport; /* Destination port */ + uint32 seq; /* Sequence number */ + uint32 ack; /* Ack number */ + uint16 flags; /* Flags */ + uint16 window; /* Window size */ + uint16 chksum; /* packet checksum */ + uint16 urgent; /* Urgent pointer */ +} PACKED_END; + +#define UDP_PROTO 17 +PACKED_BEGIN +struct udp { + uint16 udp_sport; /* Source port */ + uint16 udp_dport; /* Destination port */ + uint16 len; /* Length */ + uint16 chksum; /* packet checksum */ +} PACKED_END; + +PACKED_BEGIN +struct udp_hdr { + in_addr_T ip_src; + in_addr_T ip_dst; /* source and dest address */ + uint8 zero; + uint8 proto; /* Protocol */ + uint16 hlen; /* Length of header and data */ +} PACKED_END; + +#define ICMP_PROTO 1 +PACKED_BEGIN +struct icmp { + uint8 type; /* Type of packet */ + uint8 code; /* Code */ + uint16 chksum; /* packet checksum */ +} PACKED_END; + +PACKED_BEGIN +struct ip_hdr { + struct imp_eth_hdr ethhdr; + struct ip iphdr; +} PACKED_END; + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 +#define ARP_HWTYPE_ETH 1 + +PACKED_BEGIN +struct arp_hdr { + struct imp_eth_hdr ethhdr; + uint16 hwtype; + uint16 protocol; + uint8 hwlen; + uint8 protolen; + uint16 opcode; + ETH_MAC shwaddr; + in_addr_T sipaddr; + ETH_MAC dhwaddr; + in_addr_T dipaddr; + uint8 padding[18]; +} PACKED_END; + +struct arp_entry { + in_addr_T ipaddr; + ETH_MAC ethaddr; + uint16 time; +}; + +/* DHCP client states */ +#define DHCP_STATE_OFF 0 +#define DHCP_STATE_REQUESTING 1 +#define DHCP_STATE_INIT 2 +#define DHCP_STATE_REBOOTING 3 +#define DHCP_STATE_REBINDING 4 +#define DHCP_STATE_RENEWING 5 +#define DHCP_STATE_SELECTING 6 +#define DHCP_STATE_INFORMING 7 +#define DHCP_STATE_CHECKING 8 +#define DHCP_STATE_PERMANENT 9 /* not yet implemented */ +#define DHCP_STATE_BOUND 10 +#define DHCP_STATE_RELEASING 11 /* not yet implemented */ +#define DHCP_STATE_BACKING_OFF 12 + +/* DHCP op codes */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* DHCP message types */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 +#define DHCP_INFORM 8 + +/** DHCP hardware type, currently only ethernet is supported */ +#define DHCP_HTYPE_ETH 1 + +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */ + +/* BootP options */ +#define DHCP_OPTION_PAD 0 +#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */ +#define DHCP_OPTION_ROUTER 3 +#define DHCP_OPTION_DNS_SERVER 6 +#define DHCP_OPTION_HOSTNAME 12 +#define DHCP_OPTION_IP_TTL 23 +#define DHCP_OPTION_MTU 26 +#define DHCP_OPTION_BROADCAST 28 +#define DHCP_OPTION_TCP_TTL 37 +#define DHCP_OPTION_NTP 42 +#define DHCP_OPTION_END 255 + +/* DHCP options */ +#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */ +#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */ +#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */ + +#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */ +#define DHCP_OPTION_MESSAGE_TYPE_LEN 1 + +#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */ +#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */ + +#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */ +#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2 + +#define DHCP_OPTION_T1 58 /* T1 renewal time */ +#define DHCP_OPTION_T2 59 /* T2 rebinding time */ +#define DHCP_OPTION_US 60 +#define DHCP_OPTION_CLIENT_ID 61 +#define DHCP_OPTION_TFTP_SERVERNAME 66 +#define DHCP_OPTION_BOOTFILE 67 + +/* possible combinations of overloading the file and sname fields with options */ +#define DHCP_OVERLOAD_NONE 0 +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_SNAME_FILE 3 + +#define DHCP_CHADDR_LEN 16 +#define DHCP_SNAME_LEN 64 +#define DHCP_FILE_LEN 128 + +#define XID 0x3903F326 + +PACKED_BEGIN +struct dhcp { + uint8 op; /* Operation */ + uint8 htype; /* Header type */ + uint8 hlen; /* Ether Header len */ + uint8 hops; /* ops? */ + uint32 xid; /* id number */ + uint16 secs; + uint16 flags; + in_addr_T ciaddr; /* Client IP address */ + in_addr_T yiaddr; /* Your IP address */ + in_addr_T siaddr; /* Server IP address */ + in_addr_T giaddr; /* Gateway IP address */ + uint8 chaddr[DHCP_CHADDR_LEN]; + uint8 sname[DHCP_SNAME_LEN]; + uint8 file[DHCP_FILE_LEN]; + uint32 cookie; /* magic cookie */ + uint8 options[100]; /* Space for options */ +} PACKED_END; + +struct imp_packet { + struct imp_packet *next; /* Link to packets */ + ETH_PACK packet; + in_addr_T dest; /* Destination IP address */ + uint16 msg_id; /* Message ID */ + int life; /* How many ticks to wait */ +} imp_buffer[8]; + +struct imp_map { + uint16 sport; /* Port to fix */ + uint16 dport; /* Port to fix */ + uint16 cls_tim; /* Close timer */ + uint32 adj; /* Amount to adjust */ + uint32 lseq; /* Sequence number last adjusted */ +}; + + +struct imp_stats { + int recv; /* received packets */ + int dropped; /* received packets dropped */ + int xmit; /* transmitted packets */ + int fail; /* transmit failed */ + int runt; /* runts */ + int reset; /* reset count */ + int giant; /* oversize packets */ + int setup; /* setup packets */ + int loop; /* loopback packets */ + int recv_overrun; /* receiver overruns */ +}; + + +struct imp_device { + ETH_PCALLBACK rcallback; /* read callback routine */ + ETH_PCALLBACK wcallback; /* write callback routine */ + ETH_MAC mac; /* Hardware MAC address */ + struct imp_packet *sendq; /* Send queue */ + struct imp_packet *freeq; /* Free queue */ + in_addr_T ip; /* Local IP address */ + in_addr_T ip_mask; /* Local IP mask */ + in_addr_T hostip; /* IP address of local host */ + in_addr_T gwip; /* Gateway IP address */ + int maskbits; /* Mask length */ + struct imp_map port_map[64]; /* Ports to adjust */ + in_addr_T dhcpip; /* DHCP server address */ + int dhcp; /* Use dhcp */ + uint8 dhcp_state; /* State of DHCP */ + int dhcp_lease; /* DHCP lease time */ + int dhcp_renew; /* DHCP renew time */ + int dhcp_rebind; /* DHCP rebind time */ + int sec_tim; /* 1 second timer */ + int init_state; /* Initialization state */ + uint32 dhcp_xid; /* Transaction ID */ + int padding; /* Type zero padding */ + uint64 obuf; /* Output buffer */ + uint64 ibuf; /* Input buffer */ + int obits; /* Output bits */ + int ibits; /* Input bits */ + struct imp_stats stats; + uint8 sbuffer[ETH_FRAME_SIZE]; /* Temp send buffer */ + uint8 rbuffer[ETH_FRAME_SIZE]; /* Temp receive buffer */ + ETH_DEV etherface; + ETH_QUE ReadQ; + int imp_error; + int host_error; + int rfnm_count; /* Number of pending RFNM packets */ + int pia; /* PIA channels */ +} imp_data; + +extern int32 tmxr_poll; + +static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff}; + +static CONST in_addr_T broadcast_ipaddr = {0xffffffff}; + +static struct arp_entry arp_table[IMP_ARPTAB_SIZE]; + +t_stat imp_devio(uint32 dev, uint64 *data); +t_stat imp_srv(UNIT *); +t_stat imp_eth_srv(UNIT *); +t_stat imp_reset (DEVICE *dptr); +t_stat imp_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat imp_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat imp_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat imp_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat imp_show_ip (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat imp_set_ip (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat imp_show_gwip (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat imp_set_gwip (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat imp_show_hostip (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat imp_set_hostip (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat imp_show_dhcpip (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +void imp_timer_task(struct imp_device *imp); +void imp_send_rfmn(struct imp_device *imp); +void imp_packet_in(struct imp_device *imp); +void imp_send_packet (struct imp_device *imp_data, int len); +void imp_free_packet(struct imp_device *imp, struct imp_packet *p); +struct imp_packet * imp_get_packet(struct imp_device *imp); +void imp_arp_update(in_addr_T ipaddr, ETH_MAC *ethaddr); +void imp_arp_arpin(struct imp_device *imp, ETH_PACK *packet); +void imp_packet_out(struct imp_device *imp, ETH_PACK *packet); +void imp_do_dhcp_client(struct imp_device *imp, ETH_PACK *packet); +void imp_dhcp_timer(struct imp_device *imp); +void imp_dhcp_discover(struct imp_device *imp); +void imp_dhcp_release(struct imp_device *imp); +t_stat imp_attach (UNIT * uptr, CONST char * cptr); +t_stat imp_detach (UNIT * uptr); +t_stat imp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +const char *imp_description (DEVICE *dptr); + + +int imp_mpx_lvl = 0; +int last_coni; + +UNIT imp_unit[] = { + {UDATA(imp_srv, UNIT_IDLE+UNIT_ATTABLE+UNIT_DISABLE, 0)}, /* 0 */ + {UDATA(imp_eth_srv, UNIT_IDLE+UNIT_DISABLE, 0)}, /* 0 */ +}; +DIB imp_dib = {IMP_DEVNUM, 1, &imp_devio, NULL}; + +MTAB imp_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", + &imp_set_mac, &imp_show_mac, NULL, "MAC address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MPX", "MPX", + &imp_set_mpx, &imp_show_mpx, NULL}, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "IP", "IP=ddd.ddd.ddd.ddd/dd", + &imp_set_ip, &imp_show_ip, NULL, "IP address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "GW", "GW=ddd.ddd.ddd.ddd", + &imp_set_gwip, &imp_show_gwip, NULL, "GW address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "HOST", "HOST=ddd.ddd.ddd.ddd", + &imp_set_hostip, &imp_show_hostip, NULL, "HOST IP address" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "ETH", NULL, NULL, + ð_show, NULL, "Display attachedable devices" }, + { UNIT_DHCP, 0, "DHCP disabled", "NODHCP", NULL, NULL, NULL, + "Don't aquire address from DHCP"}, + { UNIT_DHCP, UNIT_DHCP, "DHCP", "DHCP", NULL, NULL, NULL, + "Use DHCP to set IP address"}, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DHCPIP", "DHCPIP=ddd.ddd.ddd.ddd", + NULL, &imp_show_dhcpip, NULL, "DHCP server address" }, + { UNIT_DTYPE, (TYPE_MIT << UNIT_V_DTYPE), "MIT", "MIT", NULL, NULL, NULL, + "ITS/MIT style interface"}, + { UNIT_DTYPE, (TYPE_BBN << UNIT_V_DTYPE), "BBN", "BBN", NULL, NULL, NULL, + "Tenex/BBN style interface"}, + { UNIT_DTYPE, (TYPE_WAITS << UNIT_V_DTYPE), "WAITS", "WAITS", NULL, NULL, NULL, + "WAITS style interface"}, + { 0 } + }; + +DEVICE imp_dev = { + "IMP", imp_unit, NULL, imp_mod, + 1, 8, 0, 1, 8, 36, + NULL, NULL, &imp_reset, NULL, &imp_attach, &imp_detach, + &imp_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &imp_help, NULL, NULL, &imp_description +}; +#define IMP_OCHN 0000007 +#define IMP_ICHN 0000070 +#define IMP_ECHN 0000700 + +static void check_interrupts (UNIT *uptr) +{ + clr_interrupt (DEVNUM); + + if ((uptr->STATUS & (IMPERR | IMPIC)) == IMPERR) + set_interrupt(DEVNUM, imp_data.pia >> 6); + if ((uptr->STATUS & (IMPR | IMPIC)) == (IMPR | IMPIC)) + set_interrupt(DEVNUM, imp_data.pia >> 6); + if ((uptr->STATUS & (IMPHER | IMPIHE)) == IMPHER) + set_interrupt(DEVNUM, imp_data.pia >> 6); + if (uptr->STATUS & IMPID) { + if (uptr->STATUS & IMPLW) + set_interrupt(DEVNUM, imp_data.pia); + else + set_interrupt_mpx(DEVNUM, imp_data.pia, imp_mpx_lvl); + } + if (uptr->STATUS & IMPOD) + set_interrupt_mpx(DEVNUM, imp_data.pia >> 3, imp_mpx_lvl + 1); +} + +t_stat imp_devio(uint32 dev, uint64 *data) +{ + DEVICE *dptr = &imp_dev; + UNIT *uptr = imp_unit; + + switch(dev & 07) { + case CONO: + sim_debug(DEBUG_CONO, dptr, "IMP %03o CONO %06o PC=%o\n", dev, + (uint32)*data, PC); + switch (GET_DTYPE(uptr->flags)) { + case TYPE_MIT: + imp_data.pia = *data & 7; + imp_data.pia = (imp_data.pia << 6) | (imp_data.pia << 3) | imp_data.pia; + if (*data & IMPIDC) /* Clear input done. */ + uptr->STATUS &= ~IMPID; + if (*data & IMI32S) /* Set 32-bit input. */ + uptr->STATUS |= IMPI32; + if (*data & IMI32C) /* Clear 32-bit input */ + uptr->STATUS &= ~IMPI32; + if (*data & IMPODC) /* Clear output done. */ + uptr->STATUS &= ~IMPOD; + if (*data & IMO32C) /* Clear 32-bit output. */ + uptr->STATUS &= ~IMPO32; + if (*data & IMO32S) /* Set 32-bit output. */ + uptr->STATUS |= IMPO32; + if (*data & IMPODS) /* Set output done. */ + uptr->STATUS |= IMPOD; + if (*data & IMPIR) { /* Enable interrupt on IMP ready. */ + uptr->STATUS |= IMPIC; + uptr->STATUS &= ~IMPERR; + } + if (*data & IMPHEC) { /* Clear host error. */ + /* Only if there has been a CONI lately. */ + if (last_coni - sim_interval < CONI_TIMEOUT) + uptr->STATUS &= ~IMPHER; + } + if (*data & IMIIHE) /* Inhibit interrupt on host error. */ + uptr->STATUS |= IMPIHE; + if (*data & IMPLHW) /* Last host word. */ + uptr->STATUS |= IMPLHW; + break; + case TYPE_BBN: + break; + case TYPE_WAITS: + if (*data & IMP_ODPIEN) { + imp_data.pia &= ~07; + imp_data.pia |= *data & 07; + uptr->STATUS &= ~(IMPO32|IMPLHW|IMPOD); + if (*data & IMP_O32) + uptr->STATUS |= IMPO32; + } + if (*data & IMP_IDPIEN) { + imp_data.pia &= ~070; + imp_data.pia |= (*data & 07) << 3; + uptr->STATUS &= ~(IMPI32|IMPID); + if (*data & IMP_I32) + uptr->STATUS |= IMPI32; + } + if (*data & IMP_IEPIEN) { + imp_data.pia &= ~0700; + imp_data.pia |= (*data & 07) << 6; + } + if (*data & IMP_FINO) { + if (uptr->STATUS & IMPOD) { + imp_send_packet (&imp_data, uptr->OPOS >> 3); + /* Allow room for ethernet header for later */ + memset(imp_data.sbuffer, 0, ETH_FRAME_SIZE); + uptr->OPOS = 0; + uptr->STATUS &= ~(IMPLHW); + } else + uptr->STATUS |= IMPLHW; + } + if (*data & IMP_STROUT) + uptr->STATUS &= ~(IMPOD|IMPLHW); + if (*data & IMP_CLRWT) { /* Not sure about this yet. */ + uptr->STATUS &= ~IMPID; + } + if (*data & IMP_CLRST) /* Not sure about this yet. */ + uptr->STATUS &= ~IMPID; + if (*data & IMP_STRIN) { + uptr->STATUS &= ~IMPID; + uptr->ILEN = 0; + } + check_interrupts(uptr); + break; + } + break; + case CONI: + switch (GET_DTYPE(uptr->flags)) { + case TYPE_MIT: + last_coni = sim_interval; + *data = (uint64)(uptr->STATUS | (imp_data.pia & 07)); + break; + case TYPE_BBN: + break; + case TYPE_WAITS: + *data = (uint64)(imp_data.pia & 0777); + if (uptr->STATUS & IMPOD) + *data |= IMP_ODONE; + if (uptr->STATUS & IMPID) + *data |= IMP_IDONE; + if (uptr->STATUS & IMPR) + *data |= IMP_RDY; + if (uptr->STATUS & IMPLW) + *data |= IMP_IEND; + if (uptr->STATUS & (IMPERR|IMPHER)) + *data |= IMP_ERR; + break; + } + sim_debug(DEBUG_CONI, dptr, "IMP %03o CONI %012llo PC=%o\n", dev, + *data, PC); + break; + case DATAO: + uptr->STATUS |= IMPOB; + uptr->STATUS &= ~IMPOD; + imp_data.obuf = *data; + imp_data.obits = (uptr->STATUS & IMPO32) ? 32 : 36; + sim_debug(DEBUG_DATAIO, dptr, "IMP %03o DATO %012llo %d %08x PC=%o\n", + dev, *data, imp_data.obits, (uint32)(*data >> 4), PC); + sim_activate(uptr, 100); + break; + case DATAI: + *data = imp_data.ibuf; + uptr->STATUS &= ~(IMPID|IMPLW); + sim_debug(DEBUG_DATAIO, dptr, "IMP %03o DATI %012llo %08x PC=%o\n", + dev, *data, (uint32)(*data >> 4), PC); + if (uptr->ILEN != 0) + uptr->STATUS |= IMPIB; + sim_activate(uptr, 100); + break; + } + + check_interrupts (uptr); + return SCPE_OK; +} + +t_stat imp_srv(UNIT * uptr) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + int i; + int l; + + if (uptr->STATUS & IMPOB && imp_data.sendq == NULL) { + if (imp_data.obits == 32) + imp_data.obuf >>= 4; + for (i = imp_data.obits - 1; i >= 0; i--) { + imp_data.sbuffer[uptr->OPOS>>3] |= + ((imp_data.obuf >> i) & 1) << (7-(uptr->OPOS & 7)); + uptr->OPOS++; + } + if (uptr->STATUS & IMPLHW) { + imp_send_packet (&imp_data, uptr->OPOS >> 3); + /* Allow room for ethernet header for later */ + memset(imp_data.sbuffer, 0, ETH_FRAME_SIZE); + uptr->OPOS = 0; + uptr->STATUS &= ~IMPLHW; + } + uptr->STATUS &= ~IMPOB; + uptr->STATUS |= IMPOD; + check_interrupts (uptr); + } + if (uptr->STATUS & IMPIB) { + uptr->STATUS &= ~(IMPIB|IMPLW); + imp_data.ibuf = 0; + l = (uptr->STATUS & IMPI32) ? 4 : 0; + for (i = 35; i >= l; i--) { + if ((imp_data.rbuffer[uptr->IPOS>>3] >> (7-(uptr->IPOS & 7))) & 1) + imp_data.ibuf |= ((uint64)1) << i; + uptr->IPOS++; + if (uptr->IPOS > uptr->ILEN) { + uptr->STATUS |= IMPLW; + uptr->ILEN = 0; + break; + } + } + uptr->STATUS |= IMPID; + check_interrupts (uptr); + } + if (uptr->ILEN == 0 && (uptr->STATUS & (IMPIB|IMPID)) == 0) + imp_packet_in(&imp_data); + return SCPE_OK; +} + +void +ip_checksum(uint8 *chksum, uint8 *ptr, int len) +{ + /* + * Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + int32 sum = 0; + + while( len > 1 ) { + /* This is the inner loop */ + sum += (ptr[0]<<8)+ptr[1]; + ptr+=2; + len -= 2; + } + + /* Add left-over byte, if any */ + if( len > 0 ) + sum += ptr[0]<<8; + + /* Fold 32-bit sum to 16 bits */ + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + sum=(~sum & 0xffff); + chksum[0]=(sum>>8) & 0xff; + chksum[1]=sum & 0xff; +} + + +/* + * Update the checksum based on code from RFC1631 + */ +void +checksumadjust(uint8 *chksum, uint8 *optr, + int olen, uint8 *nptr, int nlen) + /* assuming: unsigned char is 8 bits, long is 32 bits. + - chksum points to the chksum in the packet + - optr points to the old data in the packet + - nptr points to the new data in the packet + - even number of octets updated. + */ +{ + int32 sum, old, new_sum; + sum=(chksum[0]<<8)+chksum[1]; + sum=(~sum & 0xffff); + while (olen > 1) { + old=(optr[0]<<8)+optr[1]; + optr+=2; + sum-=old & 0xffff; + if (sum<=0) { sum--; sum&=0xffff; } + olen-=2; + } + if (olen > 0) { + old=optr[0]<<8; + sum-=old & 0xffff; + if (sum<=0) { sum--; sum&=0xffff; } + } + while (nlen > 1) { + new_sum=(nptr[0]<<8)+nptr[1]; + nptr+=2; + sum+=new_sum & 0xffff; + if (sum & 0x10000) { sum++; sum&=0xffff; } + nlen-=2; + } + if (nlen > 0) { + new_sum=(nptr[0]<<8); + sum+=new_sum & 0xffff; + if (sum & 0x10000) { sum++; sum&=0xffff; } + } + sum=(~sum & 0xffff); + chksum[0]=sum>>8; + chksum[1]=sum & 0xff; +} + +t_stat imp_eth_srv(UNIT * uptr) +{ + sim_clock_coschedule(uptr, 1000); /* continue poll */ + + imp_timer_task(&imp_data); + if (uptr->ILEN == 0 && (uptr->STATUS & (IMPIB|IMPID)) == 0) + imp_packet_in(&imp_data); + + if (imp_data.init_state >= 3 && imp_data.init_state < 6) { + if (imp_unit[0].flags & UNIT_DHCP && imp_data.dhcp_state != DHCP_STATE_BOUND) + return SCPE_OK; + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP init Nop %d\n", + imp_data.init_state); + if (imp_unit[0].ILEN == 0) { + /* Queue up a nop packet */ + imp_data.rbuffer[0] = 0x4; +#if 0 + imp_data.rbuffer[0] = 0xf; + imp_data.rbuffer[3] = 4; +#endif + imp_unit[0].STATUS |= IMPIB; + imp_unit[0].IPOS = 0; + imp_unit[0].ILEN = 12*8; + imp_data.init_state++; + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP Send Nop %d\n", + imp_data.init_state); + check_interrupts (&imp_unit[0]); + sim_activate(&imp_unit[0], 100); + } + } + return SCPE_OK; +} + +void +imp_timer_task(struct imp_device *imp) +{ + struct imp_packet *nq = NULL; /* New send queue */ + int n; + + /* If DHCP enabled, send a discover packet */ + if (imp_data.init_state >= 1 && + imp_unit[0].flags & UNIT_DHCP && imp->dhcp_state == DHCP_STATE_OFF) { + imp_dhcp_discover(&imp_data); + } + + /* Scan through adjusted ports and remove old ones */ + for (n = 0; n < 64; n++) { + if (imp->port_map[n].cls_tim > 0) { + if (--imp->port_map[n].cls_tim == 0) { + imp->port_map[n].dport = 0; + imp->port_map[n].sport = 0; + imp->port_map[n].adj = 0; + } + } + } + + /* Scan the send queue and see if any packets have timed out */ + while (imp->sendq != NULL) { + struct imp_packet *temp = imp->sendq; + imp->sendq = temp->next; + + if (--temp->life == 0) { + imp_free_packet(imp, temp); + sim_debug(DEBUG_DETAIL, &imp_dev, + "IMP packet timed out %08x\n", temp->dest); + } else { + /* Not yet, put back on queue */ + temp->next = nq; + nq = temp; + } + } + imp->sendq = nq; + + if (imp->sec_tim-- == 0) { + imp_dhcp_timer(&imp_data); + imp->sec_tim = 1000; + } +} + +void +imp_packet_in(struct imp_device *imp) +{ + ETH_PACK read_buffer; + struct imp_eth_hdr *hdr; + int type; + int n; + int pad; + + if (eth_read (&imp_data.etherface, &read_buffer, NULL) <= 0) { + /* Any pending packet notifications? */ + if (imp->rfnm_count != 0) { + /* Create RFNM packet */ + memset(&imp->rbuffer[0], 0, 256); + imp->rbuffer[0] = 0xf; + imp->rbuffer[3] = 4; + imp_unit[0].STATUS |= IMPIB; + imp_unit[0].IPOS = 0; + imp_unit[0].ILEN = 12*8; + if (!sim_is_active(&imp_unit[0])) + sim_activate(&imp_unit[0], 100); + imp->rfnm_count--; + } + return; + } + hdr = (struct imp_eth_hdr *)(&read_buffer.msg[0]); + type = ntohs(hdr->type); + if (type == ETHTYPE_ARP) { + imp_arp_arpin(imp, &read_buffer); + } else if (type == ETHTYPE_IP) { + struct ip *ip_hdr = + (struct ip *)(&read_buffer.msg[sizeof(struct imp_eth_hdr)]); + /* Process DHCP is this is IP broadcast */ + if (ip_hdr->ip_dst == broadcast_ipaddr || + memcmp(&hdr->dest, &imp->mac, 6) == 0) { + uint8 *payload = (uint8 *)(&read_buffer.msg[sizeof(struct imp_eth_hdr) + + (ip_hdr->ip_v_hl & 0xf) * 4]); + struct udp *udp_hdr = (struct udp *)payload; + /* Check for DHCP traffic */ + if (ip_hdr->ip_p == UDP_PROTO && ntohs(udp_hdr->udp_dport) == 68 && + ntohs(udp_hdr->udp_sport) == 67) { + imp_do_dhcp_client(imp, &read_buffer); + return; + } + } + /* Process as IP if it is for us */ + if (ip_hdr->ip_dst == imp_data.ip || ip_hdr->ip_dst == 0) { + /* Add mac address since we will probably need it later */ + imp_arp_update(ip_hdr->ip_src, &hdr->src); + /* Clear beginning of message */ + memset(&imp->rbuffer[0], 0, 256); + imp->rbuffer[0] = 0xf; + imp->rbuffer[3] = 0; + imp->rbuffer[5] = (ntohl(ip_hdr->ip_src) >> 16) & 0xff; + imp->rbuffer[7] = 14; + imp->rbuffer[8] = 0233; + imp->rbuffer[18] = 0; + imp->rbuffer[19] = 0x80; + imp->rbuffer[21] = 0x30; + + /* Copy message over */ + pad = 12 + (imp->padding / 8); + n = read_buffer.len; + memcpy(&imp->rbuffer[pad], ip_hdr ,n); + ip_hdr = (struct ip *)(&imp->rbuffer[pad]); + /* Re-point IP header to copy packet */ + /* + * If local IP defined, change destination to ip, + * and update checksum + */ + if (ip_hdr->ip_dst == imp_data.ip && imp_data.hostip != 0) { + uint8 *payload = (uint8 *)(&imp->rbuffer[pad + + (ip_hdr->ip_v_hl & 0xf) * 4]); + uint16 chk = ip_hdr->ip_sum; + /* If TCP packet update the TCP checksum */ + if (ip_hdr->ip_p == TCP_PROTO) { + struct tcp *tcp_hdr = (struct tcp *)payload; + uint16 dport = ntohs(tcp_hdr->tcp_dport); + uint16 sport = ntohs(tcp_hdr->tcp_sport); + int thl = ((ntohs(tcp_hdr->flags) >> 12) & 0xf) * 4; + int hl = (ip_hdr->ip_v_hl & 0xf) * 4; + uint8 *tcp_payload = &imp->rbuffer[ + sizeof(struct imp_eth_hdr) + hl + thl]; + checksumadjust((uint8 *)&tcp_hdr->chksum, + (uint8 *)(&ip_hdr->ip_dst), sizeof(in_addr_T), + (uint8 *)(&imp_data.hostip), sizeof(in_addr_T)); + if ((ntohs(tcp_hdr->flags) & 0x10) != 0) { + for (n = 0; n < 64; n++) { + if (imp->port_map[n].sport == sport && + imp->port_map[n].dport == dport) { + /* Check if SYN */ + if (ntohs(tcp_hdr->flags) & 02) { + imp->port_map[n].sport = 0; + imp->port_map[n].dport = 0; + imp->port_map[n].adj = 0; + } else { + uint32 new_seq = ntohl(tcp_hdr->ack); + if (new_seq > imp->port_map[n].lseq) { + new_seq = htonl(new_seq - imp->port_map[n].adj); + checksumadjust((uint8 *)&tcp_hdr->chksum, + (uint8 *)(&tcp_hdr->ack), 4, + (uint8 *)(&new_seq), 4); + tcp_hdr->ack = new_seq; + } + } + if (ntohs(tcp_hdr->flags) & 01) + imp->port_map[n].cls_tim = 100; + break; + } + } + } + /* Check if recieving to FTP */ + if (sport == 21 && strncmp((CONST char *)&tcp_payload[0], "PORT ", 5) == 0) { + /* We need to translate the IP address to new port number. */ + int l = ntohs(ip_hdr->ip_len) - thl - hl; + uint32 nip = ntohl(imp->hostip); + int nlen; + int i; + char port_buffer[100]; + struct udp_hdr udp_hdr; + /* Count out 4 commas */ + for (i = nlen = 0; i < l && nlen < 4; i++) { + if (tcp_payload[i] == ',') + nlen++; + } + nlen = sprintf(port_buffer, "PORT %d,%d,%d,%d,", + (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, + (nip >> 8) & 0xFF, nip & 0xff); + /* Copy over rest of string */ + while(i < l) { + port_buffer[nlen++] = tcp_payload[i++]; + } + port_buffer[nlen] = '\0'; + memcpy(tcp_payload, port_buffer, nlen); + /* Check if we need to update the sequence numbers */ + if (nlen != l && (ntohs(tcp_hdr->flags) & 02) == 0) { + int n = -1; + /* See if we need to change the sequence number */ + for (i = 0; i < 64; i++) { + if (imp->port_map[i].sport == sport && + imp->port_map[i].dport == dport) { + n = i; + break; + } + if (n < 0 && imp->port_map[i].dport == 0) + n = 0; + } + if (n >= 0) { + imp->port_map[n].dport = dport; + imp->port_map[n].sport = sport; + imp->port_map[n].adj += nlen - l; + imp->port_map[n].cls_tim = 0; + imp->port_map[n].lseq = ntohl(tcp_hdr->seq); + } + } + /* Now we need to update the checksums */ + tcp_hdr->chksum = 0; + ip_hdr->ip_len = htons(nlen + thl + hl); + ip_checksum((uint8 *)&tcp_hdr->chksum, (uint8 *)tcp_hdr, + nlen + thl); + udp_hdr.ip_src = ip_hdr->ip_src; + udp_hdr.ip_dst = imp->hostip; + udp_hdr.zero = 0; + udp_hdr.proto = TCP_PROTO; + udp_hdr.hlen = htons(nlen + thl); + checksumadjust((uint8 *)&tcp_hdr->chksum, (uint8 *)(&udp_hdr), 0, + (uint8 *)(&udp_hdr), sizeof(udp_hdr)); + ip_hdr->ip_sum = 0; + ip_checksum((uint8 *)(&ip_hdr->ip_sum), (uint8 *)ip_hdr, 20); + } + /* Check if UDP */ + } else if (ip_hdr->ip_p == UDP_PROTO) { + struct udp *udp_hdr = (struct udp *)payload; + if (ip_hdr->ip_p == UDP_PROTO && + htons(udp_hdr->udp_dport) == 68 && + htons(udp_hdr->udp_sport) == 67) { + imp_do_dhcp_client(imp, &read_buffer); + return; + } + checksumadjust((uint8 *)&udp_hdr->chksum, + (uint8 *)(&ip_hdr->ip_src), sizeof(in_addr_T), + (uint8 *)(&imp_data.hostip), sizeof(in_addr_T)); + /* Lastly check if ICMP */ + } else if (ip_hdr->ip_p == ICMP_PROTO) { + struct icmp *icmp_hdr = (struct icmp *)payload; + checksumadjust((uint8 *)&icmp_hdr->chksum, + (uint8 *)(&ip_hdr->ip_src), sizeof(in_addr_T), + (uint8 *)(&imp_data.hostip), sizeof(in_addr_T)); + } + checksumadjust((uint8 *)&ip_hdr->ip_sum, + (uint8 *)(&ip_hdr->ip_dst), sizeof(in_addr_T), + (uint8 *)(&imp_data.hostip), sizeof(in_addr_T)); + ip_hdr->ip_dst = imp_data.hostip; + } + /* If we are not initializing queue it up for host */ + if (imp_data.init_state >= 6) { + n = pad + ntohs(ip_hdr->ip_len); + imp_unit[0].STATUS |= IMPIB; + imp_unit[0].IPOS = 0; + imp_unit[0].ILEN = n*8; + } + if (!sim_is_active(&imp_unit[0])) + sim_activate(&imp_unit[0], 100); + } + /* Otherwise just ignore it */ + } +} + +void +imp_send_packet (struct imp_device *imp, int len) +{ + ETH_PACK write_buffer; + int i; + UNIT *uptr = &imp_unit[1]; + int n; + int st; + int lk; + int mt; + + lk = 0; + n = len; + switch (imp->sbuffer[0] & 0xF) { + default: + /* Send back invalid leader message */ + sim_printf("Invalid header\n"); + return; + case 0x0: + mt = 0; + st = imp->sbuffer[3] & 0xf; + lk = 0233; + break; + case 0x4: + mt = 4; + st = imp->sbuffer[3] & 0xf; + break; + case 0xf: + st = imp->sbuffer[9] & 0xf; + lk = imp->sbuffer[8]; + mt = imp->sbuffer[3]; + n = (imp->sbuffer[10] << 8) + (imp->sbuffer[11]); + break; + } + sim_debug(DEBUG_DETAIL, &imp_dev, + "IMP packet Type=%d ht=%d dh=%d imp=%d lk=%d %d st=%d Len=%d\n", + imp->sbuffer[3], imp->sbuffer[4], imp->sbuffer[5], + (imp->sbuffer[6] * 256) + imp->sbuffer[7], + lk, imp->sbuffer[9] >> 4, st, n); + switch(mt) { + case 0: /* Regular packet */ + switch(st) { + case 0: /* Regular */ + case 1: /* Refusable */ + if (lk == 0233) { + i = 12 + (imp->padding / 8); + n = len - i; + memcpy(&write_buffer.msg[sizeof(struct imp_eth_hdr)], + &imp->sbuffer[i], n); + write_buffer.len = n+sizeof(struct imp_eth_hdr); + imp_packet_out(imp, &write_buffer); + } + break; + case 2: /* Getting ready */ + case 3: /* Uncontrolled */ + default: + break; + } + break; + case 1: /* Error */ + break; + case 2: /* Host going down */ +fprintf(stderr, "IMP: Host shutdown\n\r"); + break; + case 4: /* Nop */ + if (imp->init_state < 3) + imp->init_state++; + imp->padding = st * 16; + sim_debug(DEBUG_DETAIL, &imp_dev, + "IMP recieve Nop %d padding= %d\n", + imp->init_state, imp->padding); + sim_activate(uptr, tmxr_poll); /* Start reciever task */ + break; + case 8: /* Error with Message */ + break; + default: + break; + } + return; +} + +/* + * Check if this packet can be sent to given IP. + * If it can we fill in the mac address and return false. + * If we can't we need to queue up and send a ARP packet and return true. + */ +void +imp_packet_out(struct imp_device *imp, ETH_PACK *packet) { + struct ip_hdr *pkt = (struct ip_hdr *)(&packet->msg[0]); + struct imp_packet *send; + struct arp_entry *tabptr; + struct arp_hdr *arp; + ETH_PACK arp_pkt; + in_addr_T ipaddr; + int i; + + /* If local IP defined, change source to ip, and update checksum */ + if (imp->hostip != 0) { + int hl = (pkt->iphdr.ip_v_hl & 0xf) * 4; + uint8 *payload = (uint8 *)(&packet->msg[ + sizeof(struct imp_eth_hdr) + hl]); + /* If TCP packet update the TCP checksum */ + if (pkt->iphdr.ip_p == TCP_PROTO) { + struct tcp *tcp_hdr = (struct tcp *)payload; + int thl = ((ntohs(tcp_hdr->flags) >> 12) & 0xf) * 4; + uint16 sport = ntohs(tcp_hdr->tcp_sport); + uint16 dport = ntohs(tcp_hdr->tcp_dport); + uint8 *tcp_payload = &packet->msg[ + sizeof(struct imp_eth_hdr) + hl + thl]; + /* Update pseudo header checksum */ + checksumadjust((uint8 *)&tcp_hdr->chksum, + (uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_T), + (uint8 *)(&imp->ip), sizeof(in_addr_T)); + /* See if we need to change the sequence number */ + for (i = 0; i < 64; i++) { + if (imp->port_map[i].sport == sport && + imp->port_map[i].dport == dport) { + /* Check if SYN */ + if (ntohs(tcp_hdr->flags) & 02) { + imp->port_map[i].sport = 0; + imp->port_map[i].dport = 0; + imp->port_map[i].adj = 0; + } else { + uint32 new_seq = ntohl(tcp_hdr->seq); + if (new_seq > imp->port_map[i].lseq) { + new_seq = htonl(new_seq + imp->port_map[i].adj); + checksumadjust((uint8 *)&tcp_hdr->chksum, + (uint8 *)(&tcp_hdr->seq), 4, + (uint8 *)(&new_seq), 4); + tcp_hdr->seq = new_seq; + } + } + if (ntohs(tcp_hdr->flags) & 01) + imp->port_map[i].cls_tim = 100; + break; + } + } + /* Check if sending to FTP */ + if (dport == 21 && strncmp((CONST char *)&tcp_payload[0], "PORT ", 5) == 0) { + /* We need to translate the IP address to new port number. */ + int l = ntohs(pkt->iphdr.ip_len) - thl - hl; + uint32 nip = ntohl(imp->ip); + int nlen; + char port_buffer[100]; + struct udp_hdr udp_hdr; + /* Count out 4 commas */ + for (i = nlen = 0; i < l && nlen < 4; i++) { + if (tcp_payload[i] == ',') + nlen++; + } + nlen = sprintf(port_buffer, "PORT %d,%d,%d,%d,", + (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, + (nip >> 8) & 0xFF, nip&0xff); + /* Copy over rest of string */ + while(i < l) { + port_buffer[nlen++] = tcp_payload[i++]; + } + port_buffer[nlen] = '\0'; + memcpy(tcp_payload, port_buffer, nlen); + /* Check if we need to update the sequence numbers */ + if (nlen != l && (ntohs(tcp_hdr->flags) & 02) == 0) { + int n = -1; + /* See if we need to change the sequence number */ + for (i = 0; i < 64; i++) { + if (imp->port_map[i].sport == sport && + imp->port_map[i].dport == dport) { + n = i; + break; + } + if (n < 0 && imp->port_map[i].dport == 0) + n = 0; + } + if (n >= 0) { + imp->port_map[n].dport = dport; + imp->port_map[n].sport = sport; + imp->port_map[n].adj += nlen - l; + imp->port_map[n].cls_tim = 0; + imp->port_map[n].lseq = ntohl(tcp_hdr->seq); + } + } + /* Now we need to update the checksums */ + tcp_hdr->chksum = 0; + pkt->iphdr.ip_len = htons(nlen + thl + hl); + ip_checksum((uint8 *)&tcp_hdr->chksum, (uint8 *)tcp_hdr, + nlen + thl); + udp_hdr.ip_src = imp->ip; + udp_hdr.ip_dst = pkt->iphdr.ip_dst; + udp_hdr.zero = 0; + udp_hdr.proto = TCP_PROTO; + udp_hdr.hlen = htons(nlen + thl); + checksumadjust((uint8 *)&tcp_hdr->chksum, (uint8 *)(&udp_hdr), 0, + (uint8 *)(&udp_hdr), sizeof(udp_hdr)); + pkt->iphdr.ip_sum = 0; + ip_checksum((uint8 *)(&pkt->iphdr.ip_sum), (uint8 *)&pkt->iphdr, 20); + packet->len = nlen + thl + hl + sizeof(struct imp_eth_hdr); + } + /* Check if UDP */ + } else if (pkt->iphdr.ip_p == UDP_PROTO) { + struct udp *udp_hdr = (struct udp *)payload; + checksumadjust((uint8 *)&udp_hdr->chksum, + (uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_T), + (uint8 *)(&imp->ip), sizeof(in_addr_T)); + /* Lastly check if ICMP */ + } else if (pkt->iphdr.ip_p == ICMP_PROTO) { + struct icmp *icmp_hdr = (struct icmp *)payload; + checksumadjust((uint8 *)&icmp_hdr->chksum, + (uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_T), + (uint8 *)(&imp->ip), sizeof(in_addr_T)); + } + /* Lastly update the header and IP address */ + checksumadjust((uint8 *)&pkt->iphdr.ip_sum, + (uint8 *)(&pkt->iphdr.ip_src), sizeof(in_addr_T), + (uint8 *)(&imp->ip), sizeof(in_addr_T)); + pkt->iphdr.ip_src = imp->ip; + } + + /* Try to send the packed */ + ipaddr = pkt->iphdr.ip_dst; + packet->len = sizeof(struct imp_eth_hdr) + ntohs(pkt->iphdr.ip_len); + /* Enforce minimum packet size */ + while (packet->len < 60) + packet->msg[packet->len++] = 0; + + /* Check if on our subnet */ + if ((imp->ip & imp->ip_mask) != (ipaddr & imp->ip_mask)) + ipaddr = imp->gwip; + + for (i = 0; i < IMP_ARPTAB_SIZE; i++) { + tabptr = &arp_table[i]; + if (ipaddr == tabptr->ipaddr) { + memcpy(&pkt->ethhdr.dest, &tabptr->ethaddr, 6); + memcpy(&pkt->ethhdr.src, &imp->mac, 6); + pkt->ethhdr.type = htons(ETHTYPE_IP); + eth_write(&imp->etherface, packet, NULL); + imp->rfnm_count++; + return; + } + } + + /* Queue packet for later send */ + send = imp_get_packet(imp); + send->next = imp->sendq; + imp->sendq = send; + send->packet.len = packet->len; + send->life = 1000; + send->dest = pkt->iphdr.ip_dst; + memcpy(&send->packet.msg[0], pkt, send->packet.len); + + /* We did not find it, so construct and send a ARP packet */ + memset(&arp_pkt, 0, sizeof(ETH_PACK)); + arp = (struct arp_hdr *)(&arp_pkt.msg[0]); + memcpy(&arp->ethhdr.dest, &broadcast_ethaddr, 6); + memcpy(&arp->ethhdr.src, &imp->mac, 6); + arp->ethhdr.type = htons(ETHTYPE_ARP); + memset(&arp->dhwaddr, 0x00, 6); + memcpy(&arp->shwaddr, &imp->mac, 6); + arp->dipaddr = ipaddr; + arp->sipaddr = imp->ip; + arp->opcode = htons(ARP_REQUEST); + arp->hwtype = htons(ARP_HWTYPE_ETH); + arp->protocol = htons(ETHTYPE_IP); + arp->hwlen = 6; + arp->protolen = 4; + + arp_pkt.len = sizeof(struct arp_hdr); + eth_write(&imp->etherface, &arp_pkt, NULL); +} + + +/* + * Update the ARP table, first use free entry, else use oldest. + */ +void +imp_arp_update(in_addr_T ipaddr, ETH_MAC *ethaddr) +{ + struct arp_entry *tabptr; + int i; + static int arptime = 0; + + /* Check if entry already in the table. */ + for (i = 0; i < IMP_ARPTAB_SIZE; i++) { + tabptr = &arp_table[i]; + + if (tabptr->ipaddr != 0) { + if (tabptr->ipaddr == ipaddr) { + memcpy(&tabptr->ethaddr, ethaddr, sizeof(ETH_MAC)); + tabptr->time = ++arptime; + return; + } + } + } + + /* See if we can find an unused entry. */ + for (i = 0; i < IMP_ARPTAB_SIZE; i++) { + tabptr = &arp_table[i]; + + if (tabptr->ipaddr == 0) + break; + } + + /* If no empty entry search for oldest one. */ + if (tabptr->ipaddr != 0) { + int fnd = 0; + uint16 tmpage = 0; + for (i = 0; i < IMP_ARPTAB_SIZE; i++) { + tabptr = &arp_table[i]; + if (arptime - tabptr->time > tmpage) { + tmpage = arptime - tabptr->time; + fnd = i; + } + } + tabptr = &arp_table[fnd]; + } + + /* Now update the entry */ + memcpy(&tabptr->ethaddr, ethaddr, sizeof(ETH_MAC)); + tabptr->ipaddr = ipaddr; + tabptr->time = ++arptime; +} + + +/* + * Process incomming ARP packet. + */ + +void +imp_arp_arpin(struct imp_device *imp, ETH_PACK *packet) +{ + struct arp_hdr *arp; + int op; + + /* Ignore packet if too short */ + if (packet->len < sizeof(struct arp_hdr)) + return; + arp = (struct arp_hdr *)(&packet->msg[0]); + op = ntohs(arp->opcode); + + switch (op) { + case ARP_REQUEST: + if (arp->dipaddr == imp->ip) { + imp_arp_update(arp->sipaddr, &arp->shwaddr); + + arp->opcode = htons(ARP_REPLY); + memcpy(&arp->dhwaddr, &arp->shwaddr, 6); + memcpy(&arp->shwaddr, &imp->mac, 6); + memcpy(&arp->ethhdr.src, &imp->mac, 6); + memcpy(&arp->ethhdr.dest, &arp->dhwaddr, 6); + + arp->dipaddr = arp->sipaddr; + arp->sipaddr = imp->ip; + arp->ethhdr.type = htons(ETHTYPE_ARP); + packet->len = sizeof(struct arp_hdr); + eth_write(&imp->etherface, packet, NULL); + } + break; + + case ARP_REPLY: + /* Check if this is our address */ + if (arp->dipaddr == imp->ip) { + struct imp_packet *nq = NULL; /* New send queue */ + imp_arp_update(arp->sipaddr, &arp->shwaddr); + /* Scan send queue, and send all packets for this host */ + while (imp->sendq != NULL) { + struct imp_packet *temp = imp->sendq; + imp->sendq = temp->next; + + if (temp->dest == arp->sipaddr) { + struct ip_hdr *pkt = (struct ip_hdr *) + (&temp->packet.msg[0]); + memcpy(&pkt->ethhdr.dest, &arp->shwaddr, 6); + memcpy(&pkt->ethhdr.src, &imp->mac, 6); + pkt->ethhdr.type = htons(ETHTYPE_IP); + eth_write(&imp->etherface, &temp->packet, NULL); + imp->rfnm_count++; + imp_free_packet(imp, temp); + } else { + temp->next = nq; + nq = temp; + } + } + imp->sendq = nq; + } + break; + } + return; +} + + +static int sent_flag = 0; +void sent(int status) { + sent_flag = 1; +} + +/* Send out a DHCP packet, fill in IP and Ethernet data. */ +void +imp_do_send_dhcp(struct imp_device *imp, ETH_PACK *packet, uint8 *last) +{ + struct ip_hdr *pkt; + struct udp *udp; + struct udp_hdr udp_hdr; + int len; + + pkt = (struct ip_hdr *)(&packet->msg[0]); + udp = (struct udp *)(&packet->msg[sizeof(struct imp_eth_hdr) + + sizeof(struct ip)]); + len = last - (uint8 *)udp; + /* Fill in ethernet and IP packet */ + memcpy(&pkt->ethhdr.dest, &broadcast_ethaddr, 6); + memcpy(&pkt->ethhdr.src, &imp->mac, 6); + pkt->ethhdr.type = htons(ETHTYPE_IP); + pkt->iphdr.ip_v_hl = 0x45; + pkt->iphdr.ip_id = 1; + pkt->iphdr.ip_ttl = 128; + pkt->iphdr.ip_p = UDP_PROTO; + pkt->iphdr.ip_dst = broadcast_ipaddr; + udp->udp_sport = htons(68); + udp->udp_dport = htons(67); + udp->len = htons(len); + pkt->iphdr.ip_len = htons(len + sizeof(struct ip)); + ip_checksum((uint8 *)(&pkt->iphdr.ip_sum), (uint8 *)&pkt->iphdr, 20); + ip_checksum((uint8 *)(&udp->chksum), (uint8 *)(udp), len); + /* Compute checksum of psuedo header and data */ + udp_hdr.ip_src = pkt->iphdr.ip_src; + udp_hdr.ip_dst = pkt->iphdr.ip_dst; + udp_hdr.zero = 0; + udp_hdr.proto = UDP_PROTO; + udp_hdr.hlen = udp->len; + checksumadjust((uint8 *)&udp->chksum, (uint8 *)(&udp_hdr), 0, + (uint8 *)(&udp_hdr), sizeof(udp_hdr)); + packet->len = len + sizeof(struct ip_hdr); + sent_flag = 0; + eth_write(&imp->etherface, packet, &sent); +} + +/* Handle incoming DCHP offer and other requests */ +void +imp_do_dhcp_client(struct imp_device *imp, ETH_PACK *read_buffer) +{ + struct ip *ip_hdr = (struct ip *) + (&read_buffer->msg[sizeof(struct imp_eth_hdr)]); + ETH_PACK dhcp_pkt; + int hl = (ip_hdr->ip_v_hl & 0xf) * 4; + struct dhcp *dhcp; + struct udp *upkt; + struct udp_hdr udp_hdr; + uint8 *opt; + uint16 sum; + int len; + in_addr_T my_ip = 0; /* Local IP address */ + in_addr_T my_mask = 0; /* Local IP mask */ + in_addr_T my_gw = 0; /* Gateway IP address */ + int lease_time = 0; /* Lease time */ + in_addr_T dhcpip = 0; /* DHCP server address */ + int opr = -1; /* Dhcp operation */ + + + upkt = (struct udp *)(&((uint8 *)(ip_hdr))[hl]); + dhcp = (struct dhcp *)(&((uint8 *)(upkt))[sizeof(struct udp)]); + + ip_checksum((uint8 *)&sum, (uint8 *)ip_hdr, hl); + if (sum != 0) { + sim_printf("IP checksum error %x\n\r", sum); + return; + } + ip_checksum((uint8 *)(&sum), (uint8 *)(upkt), ntohs(upkt->len)); + udp_hdr.ip_src = ip_hdr->ip_src; + udp_hdr.ip_dst = ip_hdr->ip_dst; + udp_hdr.zero = 0; + udp_hdr.proto = UDP_PROTO; + udp_hdr.hlen = upkt->len; + checksumadjust((uint8 *)&sum, 0, 0, (uint8 *)(&udp_hdr), sizeof(udp_hdr)); + if (sum != 0) { + sim_printf("UDP checksum error %x\n\r", sum); + return; + } + + if (memcmp(&dhcp->chaddr, &imp->mac, 6) != 0 || dhcp->xid != imp->dhcp_xid) + return; + + if (dhcp->op != DHCP_BOOTREPLY) + return; + + opt = &dhcp->options[0]; + + /* Scan and collect the options we care about */ + while (*opt != DHCP_OPTION_END) { + switch(*opt++) { + case DHCP_OPTION_PAD: + break; + default: + len = *opt++; + opt += len; + break; + case DHCP_OPTION_SUBNET_MASK: + len = *opt++; + memcpy(&my_mask, opt, 4); + opt += len; + break; + case DHCP_OPTION_ROUTER: + len = *opt++; + memcpy(&my_gw, opt, 4); + opt += len; + break; + case DHCP_OPTION_REQUESTED_IP: + len = *opt++; + memcpy(&my_ip, opt, 4); + opt += len; + break; + case DHCP_OPTION_LEASE_TIME: + len = *opt++; + memcpy(&lease_time, opt, 4); + opt += len; + break; + case DHCP_OPTION_SERVER_ID: + len = *opt++; + memcpy(&dhcpip, opt, 4); + opt += len; + break; + case DHCP_OPTION_MESSAGE_TYPE: + len = *opt++; + opr = *opt; + opt += len; + break; + } + } + + /* Process an offer message */ + if (opr == DHCP_OFFER && imp->dhcp_state == DHCP_STATE_SELECTING) { + /* Create a REQUEST Packet with IP address given */ + struct dhcp *dhcp_rply; + in_addr_T ip_t; + + memset(&dhcp_pkt.msg[0], 0, ETH_FRAME_SIZE); + dhcp_rply = (struct dhcp *)(&dhcp_pkt.msg[sizeof(struct imp_eth_hdr) + + sizeof(struct ip) + sizeof(struct udp)]); + + dhcp_rply->op = DHCP_BOOTREQUEST; + dhcp_rply->htype = DHCP_HTYPE_ETH; + dhcp_rply->hlen = 6; + dhcp_rply->xid = ++imp->dhcp_xid; + dhcp_rply->cookie = htonl(DHCP_MAGIC_COOKIE); + memcpy(&dhcp_rply->chaddr, &imp->mac, 6); + opt = &dhcp_rply->options[0]; + *opt++ = DHCP_OPTION_MESSAGE_TYPE; + *opt++ = 1; + *opt++ = DHCP_REQUEST; + *opt++ = DHCP_OPTION_REQUESTED_IP; + *opt++ = 4; + ip_t = htonl(dhcp->yiaddr); + *opt++ = (ip_t >> 24) & 0xff; + *opt++ = (ip_t >> 16) & 0xff; + *opt++ = (ip_t >> 8) & 0xff; + *opt++ = ip_t & 0xff; + *opt++ = DHCP_OPTION_SERVER_ID; + *opt++ = 4; + ip_t = imp->dhcpip; + *opt++ = (ip_t >> 24) & 0xff; + *opt++ = (ip_t >> 16) & 0xff; + *opt++ = (ip_t >> 8) & 0xff; + *opt++ = ip_t & 0xff; + *opt++ = DHCP_OPTION_CLIENT_ID; + *opt++ = 6; + for (len = 0; len < 6; len++) + *opt++ = imp->mac[len]; + *opt++ = DHCP_OPTION_PARAMETER_REQUEST_LIST; + *opt++ = 2; /* Number */ + *opt++ = DHCP_OPTION_SUBNET_MASK; /* Subnet mask */ + *opt++ = DHCP_OPTION_ROUTER; /* Routers */ + *opt++ = DHCP_OPTION_END; /* Last option */ + imp_do_send_dhcp(imp, &dhcp_pkt, opt); + imp->dhcp_state = DHCP_STATE_REQUESTING; + } + if (opr == DHCP_ACK && (imp->dhcp_state == DHCP_STATE_REQUESTING || + imp->dhcp_state == DHCP_STATE_REBINDING || + imp->dhcp_state == DHCP_STATE_RENEWING)) { + /* Set my IP address to one offered */ + imp->ip = dhcp->yiaddr; + imp->ip_mask = my_mask; + imp->gwip = my_gw; + imp->dhcpip = dhcpip; + imp->dhcp_state = DHCP_STATE_BOUND; + imp->dhcp_lease = ntohl(lease_time); + imp->dhcp_renew = imp->dhcp_lease / 2; + imp->dhcp_rebind = (7 * imp->dhcp_lease) / 8; + for (len = 0; len < 33; len++) { + if (mask[len] == my_mask) { + imp->maskbits = 32 - len; + break; + } + } + } + if (opr == DHCP_NAK && (imp->dhcp_state == DHCP_STATE_REQUESTING || + imp->dhcp_state == DHCP_STATE_REBINDING || + imp->dhcp_state == DHCP_STATE_RENEWING)) + imp->dhcp_state = DHCP_STATE_OFF; +} + +void +imp_dhcp_timer(struct imp_device *imp) +{ + ETH_PACK dhcp_pkt; + uint8 *opt; + int len; + in_addr_T ip_t; + struct dhcp *dhcp_rply; + + if (imp->dhcp_lease-- == 0) + imp->dhcp_state = DHCP_STATE_OFF; + else if (imp->dhcp_rebind-- == 0) { + imp->dhcp_state = DHCP_STATE_REBINDING; + imp->dhcpip = 0; + } else if (imp->dhcp_renew-- == 0) { + imp->dhcp_state = DHCP_STATE_RENEWING; + } + + switch (imp->dhcp_state) { + case DHCP_STATE_REBINDING: + case DHCP_STATE_RENEWING: + /* Create a REQUEST Packet with IP address given */ + + memset(&dhcp_pkt.msg[0], 0, ETH_FRAME_SIZE); + dhcp_rply = (struct dhcp *)(&dhcp_pkt.msg[sizeof(struct imp_eth_hdr) + + sizeof(struct ip) + sizeof(struct udp)]); + + dhcp_rply->op = DHCP_BOOTREQUEST; + dhcp_rply->htype = DHCP_HTYPE_ETH; + dhcp_rply->hlen = 6; + dhcp_rply->xid = ++imp->dhcp_xid; + dhcp_rply->cookie = htonl(DHCP_MAGIC_COOKIE); + memcpy(&dhcp_rply->chaddr, &imp->mac, 6); + opt = &dhcp_rply->options[0]; + *opt++ = DHCP_OPTION_MESSAGE_TYPE; + *opt++ = 1; + *opt++ = DHCP_REQUEST; + *opt++ = DHCP_OPTION_REQUESTED_IP; + *opt++ = 4; + ip_t = htonl(imp->ip); + *opt++ = (ip_t >> 24) & 0xff; + *opt++ = (ip_t >> 16) & 0xff; + *opt++ = (ip_t >> 8) & 0xff; + *opt++ = ip_t & 0xff; + *opt++ = DHCP_OPTION_SERVER_ID; + *opt++ = 4; + ip_t = imp->dhcpip; + *opt++ = (ip_t >> 24) & 0xff; + *opt++ = (ip_t >> 16) & 0xff; + *opt++ = (ip_t >> 8) & 0xff; + *opt++ = ip_t & 0xff; + *opt++ = DHCP_OPTION_CLIENT_ID; + *opt++ = 6; + for (len = 0; len < 6; len++) + *opt++ = imp->mac[len]; + *opt++ = DHCP_OPTION_PARAMETER_REQUEST_LIST; + *opt++ = 2; /* Number */ + *opt++ = DHCP_OPTION_SUBNET_MASK; /* Subnet mask */ + *opt++ = DHCP_OPTION_ROUTER; /* Routers */ + *opt++ = DHCP_OPTION_END; /* Last option */ + imp_do_send_dhcp(imp, &dhcp_pkt, opt); + break; + + case DHCP_STATE_OFF: + imp_dhcp_discover(imp); + break; + + default: + break; + } +} + +void +imp_dhcp_discover(struct imp_device *imp) +{ + ETH_PACK dhcp_pkt; + struct dhcp *dhcp; + uint8 *opt; + + /* Fill in Discover packet */ + memset(&dhcp_pkt.msg[0], 0, ETH_FRAME_SIZE); + dhcp = (struct dhcp *)(&dhcp_pkt.msg[sizeof(struct imp_eth_hdr) + + sizeof(struct ip) + sizeof(struct udp)]); + + dhcp->op = DHCP_BOOTREQUEST; + dhcp->htype = DHCP_HTYPE_ETH; + dhcp->hlen = 6; + dhcp->xid = ++imp->dhcp_xid; + dhcp->cookie = htonl(DHCP_MAGIC_COOKIE); + memcpy(&dhcp->chaddr, &imp->mac, 6); + opt = &dhcp->options[0]; + *opt++ = DHCP_OPTION_MESSAGE_TYPE; + *opt++ = 1; + *opt++ = DHCP_DISCOVER; + if (imp->ip != 0) { + in_addr_T ip_t = htonl(imp->ip); + *opt++ = DHCP_OPTION_REQUESTED_IP; + *opt++ = 0x04; + *opt++ = (ip_t >> 24) & 0xff; + *opt++ = (ip_t >> 16) & 0xff; + *opt++ = (ip_t >> 8) & 0xff; + *opt++ = ip_t & 0xff; + } + *opt++= DHCP_OPTION_PARAMETER_REQUEST_LIST; + *opt++= 2; /* Number */ + *opt++= DHCP_OPTION_SUBNET_MASK; /* Subnet mask */ + *opt++= DHCP_OPTION_ROUTER; /* Routers */ + *opt++= DHCP_OPTION_END; /* Last option */ + /* Fill in ethernet and IP packet */ + imp_do_send_dhcp(imp, &dhcp_pkt, opt); + imp->dhcp_state = DHCP_STATE_SELECTING; +} + +void +imp_dhcp_release(struct imp_device *imp) +{ + ETH_PACK dhcp_pkt; + struct dhcp *dhcp; + uint8 *opt; + int len; + + + /* Nothing to send if we are not bound */ + if (imp_data.dhcp_state != DHCP_STATE_OFF) + return; + /* Fill in DHCP RELEASE PACKET */ + memset(&dhcp_pkt.msg[0], 0, ETH_FRAME_SIZE); + dhcp = (struct dhcp *)(&dhcp_pkt.msg[sizeof(struct imp_eth_hdr) + + sizeof(struct ip) + sizeof(struct udp)]); + + dhcp->op = DHCP_BOOTREQUEST; + dhcp->htype = DHCP_HTYPE_ETH; + dhcp->hlen = 6; + dhcp->xid = ++imp->dhcp_xid; + dhcp->ciaddr = htonl(imp->ip); + dhcp->cookie = htonl(DHCP_MAGIC_COOKIE); + memcpy(&dhcp->chaddr, &imp->mac, 6); + opt = &dhcp->options[0]; + *opt++ = DHCP_OPTION_SERVER_ID; + *opt++ = 4; + *opt++ = (imp->dhcpip >> 24) & 0xff; + *opt++ = (imp->dhcpip >> 16) & 0xff; + *opt++ = (imp->dhcpip >> 8) & 0xff; + *opt++ = imp->dhcpip & 0xff; + *opt++ = DHCP_OPTION_MESSAGE_TYPE; + *opt++ = 1; + *opt++ = DHCP_RELEASE; + if (imp->ip != 0) { + in_addr_T ip_t = htonl(imp->ip); + *opt++ = DHCP_OPTION_REQUESTED_IP; + *opt++ = 0x04; + *opt++ = (ip_t >> 24) & 0xff; + *opt++ = (ip_t >> 16) & 0xff; + *opt++ = (ip_t >> 8) & 0xff; + *opt++ = ip_t & 0xff; + } + *opt++ = DHCP_OPTION_CLIENT_ID; + *opt++ = 6; + for (len = 0; len < 6; len++) + *opt++ = imp->mac[len]; + *opt++= DHCP_OPTION_END; /* Last option */ + imp_do_send_dhcp(imp, &dhcp_pkt, opt); + while(sent_flag == 0); + imp->dhcp_state = DHCP_STATE_OFF; +} + + + +static char * +ipv4_inet_ntoa(struct in_addr ip) +{ + static char str[20]; + + sprintf (str, "%d.%d.%d.%d", ip.s_addr & 0xFF, (ip.s_addr >> 8) & 0xFF, + (ip.s_addr >> 16) & 0xFF, (ip.s_addr >> 24) & 0xFF); + return str; +} + +static +int ipv4_inet_aton(const char *str, struct in_addr *inp) +{ + unsigned long bytes[4]; + int i = 0; + char *end; + in_addr_T val; + + for (i=0; (i < 4) && isdigit (*str); i++) { + bytes[i] = strtoul (str, &end, 0); + if (str == end) + return 0; + str = end; + if (*str == '.') + ++str; + } + if (*str && (*str != '/')) + return 0; + switch (i) { + case 1: + val = bytes[0]; + break; + case 2: + if ((bytes[0] > 0xFF) || (bytes[1] > 0xFFFFFF)) + return 0; + val = (bytes[0] << 24) | bytes[1]; + break; + case 3: + if ((bytes[0] > 0xFF) || (bytes[1] > 0xFF) || (bytes[2] > 0xFFFF)) + return 0; + val = (bytes[0] << 24) | (bytes[1] << 16) | bytes[2]; + break; + case 4: + if ((bytes[0] > 0xFF) || (bytes[1] > 0xFF) || (bytes[2] > 0xFF) + || (bytes[3] > 0xFF)) + return 0; + val = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; + break; + default: + return 0; + } + if (inp) + *(in_addr_T *)inp = htonl (val); + return 1; +} + +t_stat imp_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 mpx; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + mpx = (int32) get_uint (cptr, 8, 8, &r); + if (r != SCPE_OK) + return r; + imp_mpx_lvl = mpx; + return SCPE_OK; +} + +t_stat imp_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + + fprintf (st, "MPX=%o", imp_mpx_lvl); + return SCPE_OK; +} + +t_stat imp_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ + char buffer[20]; + eth_mac_fmt(&imp_data.mac, buffer); + fprintf(st, "MAC=%s", buffer); + return SCPE_OK; +} + +t_stat imp_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + t_stat status; + + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + status = eth_mac_scan_ex(&imp_data.mac, cptr, uptr); + if (status != SCPE_OK) + return status; + + return SCPE_OK; +} + +t_stat imp_show_ip (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + struct in_addr ip; + ip.s_addr = imp_data.ip; + fprintf (st, "IP=%s/%d", ipv4_inet_ntoa(ip), imp_data.maskbits); + return SCPE_OK; +} + +t_stat imp_set_ip (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + char abuf[CBUFSIZE]; + struct in_addr ip; + + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + cptr = get_glyph (cptr, abuf, '/'); + if (cptr && *cptr) { + imp_data.maskbits = atoi (cptr); + if (imp_data.maskbits < 0) + imp_data.maskbits = 0; + if (imp_data.maskbits > 32) + imp_data.maskbits = 32; + } else + imp_data.maskbits = 32; + if (ipv4_inet_aton (abuf, &ip)) { + imp_data.ip = ip.s_addr; + imp_data.ip_mask = htonl(mask[imp_data.maskbits]); + return SCPE_OK; + } + return SCPE_ARG; +} + +t_stat imp_show_gwip (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + struct in_addr ip; + + ip.s_addr = imp_data.gwip; + fprintf (st, "GW=%s", ipv4_inet_ntoa(ip)); + return SCPE_OK; +} + +t_stat imp_set_gwip (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + struct in_addr ip; + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + if (ipv4_inet_aton (cptr, &ip)) { + imp_data.gwip = ip.s_addr; + return SCPE_OK; + } + return SCPE_ARG; +} + +t_stat imp_show_dhcpip (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + struct in_addr ip; + ip.s_addr = imp_data.dhcpip; + fprintf (st, "DHCPIP=%s", ipv4_inet_ntoa(ip)); + return SCPE_OK; +} + +t_stat imp_show_hostip (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + struct in_addr ip; + + ip.s_addr = imp_data.hostip; + fprintf (st, "HOST=%s", ipv4_inet_ntoa(ip)); + return SCPE_OK; +} + +t_stat imp_set_hostip (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + struct in_addr ip; + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + if (ipv4_inet_aton (cptr, &ip)) { + imp_data.hostip = ip.s_addr; + return SCPE_OK; + } + return SCPE_ARG; +} + +struct imp_packet * +imp_get_packet(struct imp_device *imp) { + struct imp_packet *ret; + /* Check if list empty */ + if ((ret = imp->freeq) != NULL) { + imp->freeq = ret->next; + ret->next = NULL; + } + return ret; +} + +void +imp_free_packet(struct imp_device *imp, struct imp_packet *p) { + p->next = imp->freeq; + imp->freeq = p; +} + +t_stat imp_reset (DEVICE *dptr) +{ + int i; + struct imp_packet *p; + + /* Clear ARP table. */ + for (i = 0; i < IMP_ARPTAB_SIZE; i++) { + arp_table[i].ipaddr = 0; + } + /* Clear queues. */ + imp_data.sendq = NULL; + /* Set up free queue */ + p = NULL; + for (i = 0; i < (sizeof(imp_buffer)/sizeof(struct imp_packet)); i++) { + imp_buffer[i].next = p; + p = &imp_buffer[i]; + } + /* Fix last entry */ + imp_data.freeq = p; + imp_data.init_state = 0; + last_coni = sim_interval; + imp_data.dhcp_state = DHCP_STATE_OFF; + return SCPE_OK; +} + +/* attach device: */ +t_stat imp_attach(UNIT* uptr, CONST char* cptr) +{ + t_stat status; + char* tptr; + + /* Set to correct device number */ + switch(GET_DTYPE(imp_unit[0].flags)) { + case TYPE_MIT: + case TYPE_BBN: + imp_dib.dev_num = IMP_DEVNUM; + break; + case TYPE_WAITS: + imp_dib.dev_num = WA_IMP_DEVNUM; + break; + } + tptr = (char *) malloc(strlen(cptr) + 1); + if (tptr == NULL) return SCPE_MEM; + strcpy(tptr, cptr); + + status = eth_open(&imp_data.etherface, cptr, &imp_dev, 0xFFFF); + if (status != SCPE_OK) { + free(tptr); + return status; + } + if (SCPE_OK != eth_check_address_conflict (&imp_data.etherface, &imp_data.mac)) { + char buf[32]; + + eth_mac_fmt(&imp_data.mac, buf); /* format ethernet mac address */ + sim_printf("%s: MAC Address Conflict on LAN for address %s\n", imp_dev.name, buf); + eth_close(&imp_data.etherface); + free(tptr); + return SCPE_NOATT; + } + if (SCPE_OK != eth_filter(&imp_data.etherface, 1, &imp_data.mac, 1, 0)) { + eth_close(&imp_data.etherface); + free(tptr); + return SCPE_NOATT; + } + + uptr->filename = tptr; + uptr->flags |= UNIT_ATT; + eth_setcrc(&imp_data.etherface, 0); /* Don't need CRC */ + + /* init read queue (first time only) */ + status = ethq_init(&imp_data.ReadQ, 8); + if (status != SCPE_OK) { + eth_close(&imp_data.etherface); + free(tptr); + return status; + } + + imp_data.sec_tim = 1000; + imp_data.dhcp_xid = XID; + imp_data.dhcp_state = DHCP_STATE_OFF; + + return SCPE_OK; +} + +/* detach device: */ + +t_stat imp_detach(UNIT* uptr) +{ + + if (uptr->flags & UNIT_ATT) { + /* If DHCP, release our IP address */ + if (uptr->flags & UNIT_DHCP) { + imp_dhcp_release(&imp_data); + } + eth_close (&imp_data.etherface); + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + sim_cancel (uptr+1); /* stop the timer services */ + } + return SCPE_OK; +} + +t_stat imp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "IMP interface\n\n"); +fprintf (st, "The IMP acted as an interface to the early internet. "); +fprintf (st, "This interface operated\nat the TCP/IP level rather than the "); +fprintf (st, "Ethernet level. This interface allows for\nITS or Tenex to be "); +fprintf (st, "placed on the internet. The interface connects up to a TAP\n"); +fprintf (st, "or direct ethernet connection. If the host is to be run at an "); +fprintf (st, "arbitrary IP\naddress, then the HOST should be set to the IP "); +fprintf (st, "of ITS. The network interface\nwill translate this IP address "); +fprintf (st, "to the one set in IP. If HOST is set to 0.0.0.0,\nno "); +fprintf (st, "translation will take place. IP should be set to the external "); +fprintf (st, "address of\nthe IMP, along the number of bits in the net mask. "); +fprintf (st, "GW points to the default\nrouter. If DHCP is enabled these "); +fprintf (st, "will be set from DHCP when the IMP is attached.\nIf IP is set "); +fprintf (st, "and DHCP is enabled, when the IMP is attached it will inform\n"); +fprintf (st, "the local DHCP server of it's address.\n\n"); +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +eth_attach_help(st, dptr, uptr, flag, cptr); +return SCPE_OK; +} + + +const char *imp_description (DEVICE *dptr) +{ + return "KA Host/IMP interface"; +} +#endif diff --git a/PDP10/kx10_lights.c b/PDP10/kx10_lights.c new file mode 100644 index 00000000..9fa4224c --- /dev/null +++ b/PDP10/kx10_lights.c @@ -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 + +#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; +} diff --git a/PDP10/kx10_lp.c b/PDP10/kx10_lp.c new file mode 100644 index 00000000..ceb0ecf9 --- /dev/null +++ b/PDP10/kx10_lp.c @@ -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 + +#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 diff --git a/PDP10/kx10_mt.c b/PDP10/kx10_mt.c new file mode 100644 index 00000000..0ce3ee87 --- /dev/null +++ b/PDP10/kx10_mt.c @@ -0,0 +1,1063 @@ +/* ka10_mt.c: TM10A/B Magnetic tape 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. + + 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_MT +#define NUM_DEVS_MT 0 +#endif + +#if (NUM_DEVS_MT > 0) + +#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF) +#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF + +#define MTDF_TYPEB (1 << DEV_V_UF) +#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 */ + +#define NOP_CLR 000 /* Nop clear, interrupt */ +#define NOP_IDLE 010 /* Nop interrupt when idle */ +#define REWIND 001 /* Rewind */ +#define UNLOAD 011 /* Unload */ +#define READ 002 /* Read */ +#define READ_NOEOR 012 /* Read no end of record. */ +#define CMP 003 /* Compare */ +#define CMP_NOEOR 013 /* Compare no end of record. */ +#define WRITE 004 /* Write */ +#define WRITE_LONG 014 /* Write with long record gap. */ +#define WTM 005 /* Write End of File */ +#define ERG 015 /* Write blank tape */ +#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 DATA_REQUEST 00000000001 +#define NEXT_UNIT 00000000002 +#define SEVEN_CHAN 00000000004 +#define WRITE_LOCK 00000000010 +#define CHAN_ERR 00000000020 +#define IDLE_UNIT 00000000040 +#define JOB_DONE 00000000100 +#define BAD_TAPE 00000000200 +#define DATA_LATE 00000000400 +#define RLC_ERR 00000001000 +#define READ_CMP 00000002000 +#define EOT_FLAG 00000004000 +#define EOF_FLAG 00000010000 +#define PARITY_ERR 00000020000 +#define ILL_OPR 00000040000 +#define BOT_FLAG 00000100000 +#define REW_FLAG 00000200000 +#define TRAN_HUNG 00000400000 +#define CHAR_COUNT 00007000000 +#define WT_CW_DONE 00010000000 +#define DATA_PARITY 00020000000 +#define NXM_ERR 00040000000 +#define CW_PAR_ERR 00100000000 +#define B22_FLAG 00400000000 + +#define DATA_PIA 000000007 /* 0 */ +#define FLAG_PIA 000000070 /* 3 */ +#define DENS_200 000000000 +#define DENS_556 000000100 +#define DENS_800 000000200 +#define DENS_MSK 000000300 /* 6 */ +#define NEXT_UNIT_ENAB 000000400 /* 8 */ +#define FUNCTION 000017000 /* 9 */ +#define CORE_DUMP 000020000 /*13 */ +#define ODD_PARITY 000040000 /*14 */ +#define UNIT_NUM 000700000 /*15 */ +#define NEXT_UNIT_NUM 007000000 /*18 */ + +#define MT_DEVNUM 0340 +#define MT_MOTION 000000001 /* Mag tape unit in motion */ +#define MT_BUSY 000000002 /* Mag tape unit is busy */ +#define MT_BUFFUL 000000004 /* Buffer full of data */ +#define MT_BRFUL 000000010 /* BR register full */ +#define MT_STOP 000000020 /* DF10 End of Channel */ +#define MT_LASTWD 000000040 /* Last data word of record */ + +#define CNTRL u3 +#define CPOS u5 /* Character position */ +#define BPOS u6 /* Position in buffer */ + +t_stat mt_devio(uint32 dev, uint64 *data); +t_stat mt_srv(UNIT *); +t_stat mt_boot(int32, DEVICE *); +t_stat mt_set_mta (UNIT *uptr, int32 val, CONST char *cptr, void *desc) ; +t_stat mt_show_mta (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +#if MPX_DEV +t_stat mt_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) ; +t_stat mt_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +#endif +t_stat mt_reset(DEVICE *); +t_stat mt_attach(UNIT *, CONST char *); +t_stat mt_detach(UNIT *); +t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *mt_description (DEVICE *dptr); + +struct df10 mt_df10; +uint16 mt_pia; +uint8 mt_sel_unit; +uint8 mt_next_unit; +uint8 wr_eor; +uint64 mt_status; +uint64 mt_hold_reg; +int mt_mpx_lvl = 0; +int hri_mode; /* Read in mode for TM10B */ + +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 mt_buffer[BUFFSIZE]; + +UNIT mt_unit[] = { +/* Controller 1 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 0 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 1 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 2 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 3 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 4 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 5 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 6 */ + {UDATA(&mt_srv, UNIT_MT, 0)}, /* 7 */ +}; + +DIB mt_dib = {MT_DEVNUM, 2, &mt_devio, NULL}; + +MTAB mt_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL}, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, MTDF_TYPEB, "TYPE", "TYPE", &mt_set_mta, &mt_show_mta}, + {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}, +#if MPX_DEV + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MPX", "MPX", + &mt_set_mpx, &mt_show_mpx, NULL}, +#endif + {0} +}; + +REG mt_reg[] = { + {BRDATA(BUFF, &mt_buffer[0], 16, 64, BUFFSIZE), REG_HRO}, + {ORDATA(PIA, mt_pia, 3)}, + {ORDATA(UNIT, mt_sel_unit, 3)}, + {ORDATA(NUNIT, mt_next_unit, 3)}, + {FLDATA(READIN, hri_mode, 0), REG_HRO}, + {FLDATA(WREOR, wr_eor, 0), REG_HRO}, + {ORDATA(STATUS, mt_status, 18), REG_HRO}, + {ORDATA(HOLD, mt_hold_reg, 36), REG_HRO}, + {ORDATA(MPX, mt_mpx_lvl, 3)}, + {ORDATA(DSTATUS, mt_df10.status, 18), REG_RO}, + {ORDATA(CIA, mt_df10.cia, 18)}, + {ORDATA(CCW, mt_df10.ccw, 18)}, + {ORDATA(WCR, mt_df10.wcr, 18)}, + {ORDATA(CDA, mt_df10.cda, 18)}, + {ORDATA(DEVNUM, mt_df10.devnum, 9), REG_HRO}, + {ORDATA(BUF, mt_df10.buf, 36), REG_HRO}, + {ORDATA(NXM, mt_df10.nxmerr, 8), REG_HRO}, + {ORDATA(COMP, mt_df10.ccw_comp, 8), REG_HRO}, + {0} +}; + +DEVICE mt_dev = { + "MTA", mt_unit, mt_reg, mt_mod, + 8, 8, 15, 1, 8, 8, + NULL, NULL, &mt_reset, &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug, + NULL, NULL, &mt_help, NULL, NULL, &mt_description +}; + +t_stat mt_devio(uint32 dev, uint64 *data) { + uint64 res; + DEVICE *dptr = &mt_dev; + UNIT *uptr = &mt_unit[mt_sel_unit]; + + switch(dev & 07) { + case CONI: + res = (uint64)(mt_pia & (NEXT_UNIT_ENAB|FLAG_PIA|DATA_PIA)); + res |= (uint64)(uptr->CNTRL & 077300); + res |= ((uint64)mt_sel_unit) << 15; + res |= ((uint64)mt_next_unit) << 18; + res |= ((uint64)wr_eor) << 21; + if (dptr->flags & MTDF_TYPEB) + res |= 7LL; /* Force DATA PIA to 7 on type B */ + *data = res; + sim_debug(DEBUG_CONI, dptr, "MT CONI %03o status %06o %o %o PC=%06o\n", + dev, (uint32)res, mt_sel_unit, mt_pia, PC); + break; + + case CONO: + clr_interrupt(MT_DEVNUM); + clr_interrupt(MT_DEVNUM+4); + mt_next_unit = (*data >> 15) & 07; + mt_pia = (uint16)(*data) & (NEXT_UNIT_ENAB|FLAG_PIA|DATA_PIA); + mt_status &= ~(DATA_REQUEST|CHAN_ERR|JOB_DONE|DATA_LATE| \ + BAD_TAPE|RLC_ERR|READ_CMP|EOF_FLAG|EOT_FLAG|BOT_FLAG| \ + PARITY_ERR|ILL_OPR|REW_FLAG|TRAN_HUNG| \ + WT_CW_DONE|DATA_PARITY|NXM_ERR|CW_PAR_ERR|IDLE_UNIT| \ + SEVEN_CHAN|NEXT_UNIT); + /* Check if we can switch to new unit */ + if (mt_next_unit != mt_sel_unit) { + sim_cancel(uptr); + mt_sel_unit = mt_next_unit; + uptr = &mt_unit[mt_sel_unit]; + } + if (mt_pia & NEXT_UNIT_ENAB) { + set_interrupt(dev, mt_pia >> 3); + } + uptr->CNTRL = (int32)(*data & 077300); + mt_df10.buf = 0; + sim_debug(DEBUG_CONO, dptr, + "MT CONO %03o start %o %o %o %012llo %012llo PC=%06o\n", + dev, uptr->CNTRL, mt_sel_unit, mt_pia, *data, mt_status, PC); + if ((uptr->flags & UNIT_ATT) != 0) { + /* Check if Write */ + int cmd = (uptr->CNTRL & FUNCTION) >> 9; + uptr->CNTRL &= ~(MT_BRFUL|MT_BUFFUL|MT_STOP); + switch(cmd & 07) { + case NOP_CLR: + uptr->CNTRL &= ~MT_BUSY; + wr_eor = 0; + mt_status |= NEXT_UNIT; + if (cmd & 010) { + mt_status |= JOB_DONE; + set_interrupt(MT_DEVNUM+4, mt_pia >> 3); + } else { + clr_interrupt(MT_DEVNUM+4); + } + clr_interrupt(MT_DEVNUM); + sim_debug(DEBUG_EXP, dptr, "Setting status %012llo\n", mt_status); + return SCPE_OK; + + case REWIND: + mt_status |= REW_FLAG; + break; + + case WRITE: + if ((uptr->flags & MTUF_WLK) != 0) { + mt_status |= IDLE_UNIT|ILL_OPR|EOF_FLAG; + break; + } + /* Fall through */ + + case WTM: + case READ: + case CMP: + CLR_BUF(uptr); + uptr->CPOS = 0; + break; + + case SPC_REV: + if (sim_tape_bot(uptr)) { + mt_status |= JOB_DONE|ILL_OPR; + set_interrupt(MT_DEVNUM+4, mt_pia >> 3); + return SCPE_OK; + } + /* Fall through */ + + case SPC_FWD: + if ((dptr->flags & MTDF_TYPEB) == 0 && (cmd & 010) == 0) { + mt_status |= DATA_REQUEST; + set_interrupt_mpx(MT_DEVNUM, mt_pia, mt_mpx_lvl); + } + } + mt_status |= IDLE_UNIT; + uptr->CNTRL |= MT_BUSY; + sim_activate(uptr, 1000); + } else { + sim_activate(uptr, 9999999); + sim_debug(DEBUG_CONO, dptr, "MT CONO %03o hung PC=%06o\n", dev, PC); + } + break; + + case DATAI: + /* Xfer data */ + clr_interrupt(MT_DEVNUM); + *data = mt_hold_reg; + uptr->CNTRL &= ~MT_BUFFUL; + mt_status &= ~DATA_REQUEST; + if (uptr->CNTRL & MT_BRFUL) { + mt_hold_reg = mt_df10.buf; + mt_df10.buf = 0; + uptr->CNTRL &= ~MT_BRFUL; + uptr->CNTRL |= MT_BUFFUL; + if ((dptr->flags & MTDF_TYPEB) == 0) { + mt_status |= DATA_REQUEST; + set_interrupt_mpx(MT_DEVNUM, mt_pia, mt_mpx_lvl); + } + } + sim_debug(DEBUG_DATA, dptr, "MT %03o >%012llo\n", dev, *data); + break; + + case DATAO: + /* Xfer data */ + mt_hold_reg = *data; + mt_status &= ~DATA_REQUEST; + clr_interrupt(MT_DEVNUM); + uptr->CNTRL |= MT_BUFFUL; + sim_debug(DEBUG_DATA, dptr, "MT %03o <%012llo, %012llo\n", + dev, mt_hold_reg, mt_df10.buf); + break; + + case CONI|04: + res = mt_status; + if ((uptr->CNTRL & MT_BUSY) == 0) + res |= NEXT_UNIT; + if ((uptr->CNTRL & (06000|MT_STOP)) == 02000 && (mt_status & JOB_DONE) != 0) + res |= RLC_ERR; + if ((uptr->flags & MTUF_7TRK) != 0) + res |= SEVEN_CHAN; + if ((uptr->flags & UNIT_ATT) != 0 && (uptr->CNTRL & MT_MOTION) == 0) + res |= IDLE_UNIT; + 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 ((dptr->flags & MTDF_TYPEB) == 0) + res |= WT_CW_DONE|DATA_PARITY|NXM_ERR|CW_PAR_ERR; +#if KI_22BIT + if (dptr->flags & MTDF_TYPEB) + res |= B22_FLAG; +#endif + *data = res; + sim_debug(DEBUG_CONI, dptr, "MT CONI %03o status2 %012llo %o %012llo PC=%06o\n", + dev, res, mt_sel_unit, mt_status, PC); + break; + + case CONO|04: + if (*data & 1) { + uptr->CNTRL |= MT_STOP; + hri_mode = 0; + sim_debug(DEBUG_DETAIL, dptr, "MT stop %03o %012llo\n", dev, mt_status); + } + if (*data & 2) { + mt_hold_reg ^= mt_df10.buf; + } + if (dptr->flags & MTDF_TYPEB) { + if (*data & 04) + df10_writecw(&mt_df10); + if (*data & 010) + mt_status &= ~(WT_CW_DONE); + } + sim_debug(DEBUG_CONO, dptr, "MT CONO %03o control %o %o %012llo %012llo\n", + dev, uptr->CNTRL, mt_sel_unit, mt_hold_reg, mt_df10.buf); + break; + + case DATAI|04: + *data = 0; + break; + + case DATAO|04: + /* Set Initial CCW */ + if (dptr->flags & MTDF_TYPEB) + df10_setup(&mt_df10, (uint32) *data); + else + mt_df10.buf ^= mt_hold_reg; + sim_debug(DEBUG_DATAIO, dptr, "MT DATAO %03o %012llo\n", dev, *data); + break; + } + return SCPE_OK; +} + +/* Wrapper to handle reading of hold register or via DF10 */ +void mt_df10_read(DEVICE *dptr, UNIT *uptr) { + if (dptr->flags & MTDF_TYPEB) { + if (!df10_read(&mt_df10)) { + uptr->CNTRL |= MT_STOP; + } + sim_debug(DEBUG_DATA, dptr, "MT <%012llo %o\n", mt_df10.buf, uptr->CPOS); + } else { + if (uptr->CNTRL & MT_BUFFUL) { + mt_df10.buf = mt_hold_reg; + if ((uptr->CNTRL & MT_STOP) == 0) { + mt_status |= DATA_REQUEST; + set_interrupt_mpx(MT_DEVNUM, mt_pia, mt_mpx_lvl); + } + } else { + if ((uptr->CNTRL & MT_STOP) == 0) { + mt_status |= DATA_LATE; + uptr->CNTRL |= MT_STOP; + } + return; + } + } + uptr->CNTRL &= ~MT_BUFFUL; + uptr->CNTRL |= MT_BRFUL; + uptr->CPOS = 0; +} + +/* Wrapper to handle writing of hold register or via DF10 */ +void mt_df10_write(DEVICE *dptr, UNIT *uptr) { + if (dptr->flags & MTDF_TYPEB) { + if (hri_mode) { + mt_hold_reg = mt_df10.buf; + mt_status |= DATA_REQUEST; + } else if (!df10_write(&mt_df10)) { + uptr->CNTRL |= MT_STOP; + return; + } + sim_debug(DEBUG_DATA, dptr, "MT >%012llo %o\n", mt_df10.buf, uptr->CPOS); + uptr->CNTRL &= ~(MT_BUFFUL|MT_BRFUL); + } else { + if ((uptr->CNTRL & MT_BUFFUL) == 0) { + mt_hold_reg = mt_df10.buf; + mt_status |= DATA_REQUEST; + uptr->CNTRL &= ~(MT_BRFUL); + uptr->CNTRL |= MT_BUFFUL; + set_interrupt_mpx(MT_DEVNUM, mt_pia, mt_mpx_lvl); + } else { + uptr->CNTRL |= MT_BRFUL; + } + } + mt_df10.buf = 0; + uptr->CPOS = 0; +} + + +/* Map simH errors into machine errors */ +t_stat mt_error(UNIT * uptr, t_stat r, DEVICE * dptr) +{ + switch (r) { + case MTSE_OK: /* no error */ + break; + + case MTSE_TMK: /* tape mark */ + mt_status |= EOF_FLAG; + break; + + case MTSE_WRP: /* write protected */ + mt_status |= WRITE_LOCK; + break; + + case MTSE_UNATT: /* unattached */ + mt_status |= TRAN_HUNG; + break; + + case MTSE_IOERR: /* IO error */ + case MTSE_FMT: /* invalid format */ + mt_status |= ILL_OPR; + break; + + case MTSE_RECE: /* error in record */ + mt_status |= BAD_TAPE; + break; + + case MTSE_BOT: /* beginning of tape */ + mt_status |= BOT_FLAG; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + break; + + case MTSE_EOM: /* end of medium */ + mt_status |= EOT_FLAG; + break; + } + if (mt_next_unit != mt_sel_unit) { + mt_sel_unit = mt_next_unit; + mt_status |= NEXT_UNIT; + if (mt_pia & NEXT_UNIT_ENAB) + set_interrupt(MT_DEVNUM+4, mt_pia >> 3); + } + mt_status |= JOB_DONE; + uptr->CNTRL &= ~MT_BUSY; + sim_debug(DEBUG_EXP, dptr, "Setting status %d %012llo\n", r, mt_status); + set_interrupt(MT_DEVNUM+4, mt_pia >> 3); + return SCPE_OK; +} + +/* Handle processing of tape requests. */ +t_stat mt_srv(UNIT * uptr) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + int unit = (uptr - dptr->units) & 7; + int cmd = (uptr->CNTRL & FUNCTION) >> 9; + t_mtrlnt reclen; + t_stat r = SCPE_ARG; /* Force error if not set */ + uint8 ch; + int cc; + int cc_max; + + if ((uptr->flags & UNIT_ATT) == 0) { + uptr->CNTRL &= ~MT_MOTION; + return mt_error(uptr, MTSE_UNATT, dptr); /* attached? */ + } + + if ((cmd & 6) != 0 && (uptr->CNTRL & DENS_MSK) != DENS_800) { + uptr->CNTRL &= ~MT_MOTION; + return mt_error(uptr, MTSE_FMT, dptr); /* wrong density? */ + } + + if (uptr->flags & MTUF_7TRK) { + cc_max = 6; + } else { + cc_max = (4 + ((uptr->CNTRL & CORE_DUMP) != 0)); + } + + switch(cmd) { + case NOP_IDLE: + sim_debug(DEBUG_DETAIL, dptr, "MT%o Idle\n", unit); + uptr->CNTRL &= ~MT_MOTION; + return mt_error(uptr, MTSE_OK, dptr); + + case NOP_CLR: + sim_debug(DEBUG_DETAIL, dptr, "MT%o nop\n", unit); + return mt_error(uptr, MTSE_OK, dptr); /* Nop */ + + case REWIND: + mt_status &= ~IDLE_UNIT; + sim_debug(DEBUG_DETAIL, dptr, "MT%o rewind\n", unit); + uptr->CNTRL &= ~MT_MOTION; + mt_status |= BOT_FLAG; + return mt_error(uptr, sim_tape_rewind(uptr), dptr); + + case UNLOAD: + mt_status &= ~IDLE_UNIT; + sim_debug(DEBUG_DETAIL, dptr, "MT%o unload\n", unit); + uptr->CNTRL &= ~MT_MOTION; + return mt_error(uptr, sim_tape_detach(uptr), dptr); + + case READ: + case READ_NOEOR: + if (uptr->CNTRL & MT_STOP) { + if ((uptr->CNTRL & MT_LASTWD) == 0) + mt_status |= RLC_ERR; + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + return mt_error(uptr, MTSE_OK, dptr); + } + if (BUF_EMPTY(uptr)) { + uptr->CNTRL |= MT_MOTION; + mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOF_FLAG|EOT_FLAG|PARITY_ERR|CHAR_COUNT); + if ((r = sim_tape_rdrecf(uptr, &mt_buffer[0], &reclen, + BUFFSIZE)) != MTSE_OK) { + sim_debug(DEBUG_DETAIL, dptr, "MT%o read error %d\n", unit, r); + uptr->CNTRL &= ~MT_MOTION; + if (dptr->flags & MTDF_TYPEB && r == MTSE_TMK) { + df10_write(&mt_df10); + df10_writecw(&mt_df10); + } + return mt_error(uptr, r, dptr); + } + sim_debug(DEBUG_DETAIL, dptr, "MT%o read %d\n", unit, reclen); + uptr->hwmark = reclen; + uptr->BPOS = 0; + } + if (uptr->CNTRL & MT_BRFUL) { + mt_status |= DATA_LATE; + sim_debug(DEBUG_EXP, dptr, "data late\n"); + break; + } + if ((uint32)uptr->BPOS < uptr->hwmark) { + if (uptr->flags & MTUF_7TRK) { + cc = 6 * (5 - uptr->CPOS); + ch = mt_buffer[uptr->BPOS]; + if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ + parity_table[ch & 0x3f]) != 0) { + mt_status |= PARITY_ERR; + } + mt_df10.buf |= (uint64)(ch & 0x3f) << cc; + } else { + if ((uptr->CNTRL & ODD_PARITY) == 0) + mt_status |= PARITY_ERR; + cc = (8 * (3 - uptr->CPOS)) + 4; + ch = mt_buffer[uptr->BPOS]; + if (cc < 0) + mt_df10.buf |= (uint64)(ch & 0x3f); + else + mt_df10.buf |= (uint64)(ch & 0xff) << cc; + } + uptr->BPOS++; + uptr->CPOS++; + if ((uint32)(uptr->BPOS + cc_max) >= uptr->hwmark) + uptr->CNTRL |= MT_LASTWD; + mt_status &= ~CHAR_COUNT; + mt_status |= (uint64)(uptr->CPOS) << 18; + if (uptr->CPOS == cc_max) + mt_df10_write(dptr, uptr); + } else { + if ((cmd & 010) == 0) { + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + uptr->CNTRL &= ~(MT_MOTION|MT_BUSY); + return mt_error(uptr, MTSE_OK, dptr); + } else { + CLR_BUF(uptr); + } + } + break; + + case CMP: + case CMP_NOEOR: + if (uptr->CNTRL & MT_STOP) { + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + return mt_error(uptr, MTSE_OK, dptr); + } + if (BUF_EMPTY(uptr)) { + uptr->CNTRL |= MT_MOTION; + mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOF_FLAG|EOT_FLAG|PARITY_ERR|CHAR_COUNT); + if ((r = sim_tape_rdrecf(uptr, &mt_buffer[0], &reclen, + BUFFSIZE)) != MTSE_OK) { + sim_debug(DEBUG_DETAIL, dptr, "MT%o read error %d\n", unit, r); + uptr->CNTRL &= ~MT_MOTION; + if (dptr->flags & MTDF_TYPEB && r == MTSE_TMK) + mt_df10_read(dptr, uptr); + return mt_error(uptr, r, dptr); + } + sim_debug(DEBUG_DETAIL, dptr, "MT%o compare %d\n", unit, reclen); + uptr->hwmark = reclen; + uptr->BPOS = 0; + if ((dptr->flags & MTDF_TYPEB) == 0) { + mt_status |= DATA_REQUEST; + set_interrupt_mpx(MT_DEVNUM, mt_pia, mt_mpx_lvl); + } + break; + } + if (uptr->BPOS >= (int32)uptr->hwmark) { + if (cmd == CMP_NOEOR) { + CLR_BUF(uptr); + uptr->CNTRL &= ~MT_LASTWD; + } else { + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + uptr->CNTRL &= ~(MT_MOTION|MT_BUSY); + return mt_error(uptr, MTSE_INVRL, dptr); + } + } else if ((uptr->CNTRL & MT_BRFUL) == 0) { + /* Write out first character. */ + mt_df10_read(dptr, uptr); + } + if ((uptr->CNTRL & MT_BRFUL) != 0) { + if (uptr->flags & MTUF_7TRK) { + ch = mt_buffer[uptr->BPOS]; + if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ + parity_table[ch & 0x3f]) != (ch & 0x40)) { + mt_status |= PARITY_ERR; + } + mt_buffer[uptr->BPOS] &= 0x3f; + cc = 6 * (5 - uptr->CPOS); + ch = (mt_df10.buf >> cc) & 0x3f; + } else { + if ((uptr->CNTRL & ODD_PARITY) == 0) + mt_status |= PARITY_ERR; + /* Write next char out */ + cc = (8 * (3 - uptr->CPOS)) + 4; + if (cc < 0) + ch = mt_df10.buf & 0x3f; + else + ch = (mt_df10.buf >> cc) & 0xff; + } + if (mt_buffer[uptr->BPOS] != ch) { + mt_status |= READ_CMP; + if ((dptr->flags & MTDF_TYPEB) == 0) { + uptr->BPOS = uptr->hwmark; + mt_status &= ~CHAR_COUNT; + mt_status |= (uint64)(uptr->CPOS+1) << 18; + uptr->CNTRL &= ~(MT_MOTION|MT_BUSY); + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + return mt_error(uptr, MTSE_OK, dptr); + } + } + uptr->BPOS++; + uptr->CPOS++; + if (uptr->BPOS == uptr->hwmark) + uptr->CNTRL |= MT_LASTWD; + if (uptr->CPOS == cc_max) { + uptr->CPOS = 0; + uptr->CNTRL &= ~MT_BRFUL; + } + mt_status &= ~CHAR_COUNT; + mt_status |= (uint64)(uptr->CPOS+1) << 18; + } + break; + + case WRITE: + case WRITE_LONG: + /* Writing and Type A, request first data word */ + if (BUF_EMPTY(uptr)) { + uptr->CNTRL |= MT_MOTION; + mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOF_FLAG|EOT_FLAG|PARITY_ERR|CHAR_COUNT); + sim_debug(DEBUG_EXP, dptr, "MT%o Init write\n", unit); + uptr->hwmark = 0; + uptr->CPOS = 0; + uptr->BPOS = 0; + mt_status |= (uint64)(1) << 18; + if ((dptr->flags & MTDF_TYPEB) == 0) { + mt_status |= DATA_REQUEST; + set_interrupt_mpx(MT_DEVNUM, mt_pia, mt_mpx_lvl); + } + break; + } + /* Force error if we exceed buffer size */ + if (uptr->BPOS >= BUFFSIZE) + return mt_error(uptr, MTSE_RECE, dptr); + if ((uptr->CNTRL & MT_BRFUL) == 0) + mt_df10_read(dptr, uptr); + if ((uptr->CNTRL & MT_BRFUL) != 0) { + if (uptr->flags & MTUF_7TRK) { + cc = 6 * (5 - uptr->CPOS); + ch = (mt_df10.buf >> cc) & 0x3f; + ch |= ((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ + parity_table[ch & 0x3f]; + } else { + /* Write next char out */ + cc = (8 * (3 - uptr->CPOS)) + 4; + if (cc < 0) + ch = mt_df10.buf & 0x3f; + else + ch = (mt_df10.buf >> cc) & 0xff; + } + mt_buffer[uptr->BPOS] = ch; + uptr->BPOS++; + uptr->hwmark = uptr->BPOS; + uptr->CPOS++; + if (uptr->CPOS == cc_max) { + uptr->CPOS = 0; + uptr->CNTRL &= ~MT_BRFUL; + } + mt_status &= ~CHAR_COUNT; + mt_status |= (uint64)(uptr->CPOS+1) << 18; + } + if ((uptr->CNTRL & (MT_STOP|MT_BRFUL|MT_BUFFUL)) == MT_STOP) { + /* Write out the block */ + wr_eor = 1; + reclen = uptr->hwmark; + mt_status &= ~(BOT_FLAG|EOF_FLAG|EOT_FLAG|CHAR_COUNT); + r = sim_tape_wrrecf(uptr, &mt_buffer[0], reclen); + sim_debug(DEBUG_DETAIL, dptr, "MT%o Write %d\n", unit, reclen); + uptr->BPOS = 0; + uptr->hwmark = 0; + uptr->CNTRL &= ~MT_MOTION; + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + return mt_error(uptr, r, dptr); /* Record errors */ + } + break; + + case WTM: + if ((uptr->flags & MTUF_WLK) != 0) + return mt_error(uptr, MTSE_WRP, dptr); + if (uptr->CPOS == 0) { + mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOT_FLAG); + sim_debug(DEBUG_DETAIL, dptr, "MT%o WTM\n", unit); + r = sim_tape_wrtmk(uptr); + if (r != MTSE_OK) + return mt_error(uptr, r, dptr); + uptr->CPOS++; + wr_eor = 1; + } else { + wr_eor = 0; + mt_status |= EOF_FLAG; + uptr->CNTRL &= ~MT_MOTION; + return mt_error(uptr, MTSE_OK, dptr); + } + break; + + case ERG: + if ((uptr->flags & MTUF_WLK) != 0) + return mt_error(uptr, MTSE_WRP, dptr); + uptr->CNTRL &= ~MT_MOTION; + mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOT_FLAG); + sim_debug(DEBUG_DETAIL, dptr, "MT%o ERG\n", unit); + return mt_error(uptr, sim_tape_wrgap(uptr, 35), dptr); + + case SPC_REV_EOF: + case SPC_EOF: + case SPC_REV: + case SPC_FWD: + sim_debug(DEBUG_DETAIL, dptr, "MT%o space %o\n", unit, cmd); + uptr->CNTRL |= MT_MOTION; + mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOT_FLAG); + /* 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 */ + break; + 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 &= ~MT_MOTION; + mt_status &= ~DATA_REQUEST; + clr_interrupt(MT_DEVNUM); + return mt_error(uptr, r, dptr); + } + /* Clear tape mark, command, idle since we will need to change dir */ + if ((cmd & 010) == 0) { + mt_df10_read(dptr, uptr); + if ((uptr->CNTRL & MT_BRFUL) == 0) { + mt_status &= ~DATA_LATE; + uptr->CNTRL &= ~MT_MOTION; + if (dptr->flags & MTDF_TYPEB) + df10_writecw(&mt_df10); + return mt_error(uptr, MTSE_OK, dptr); + } + uptr->CNTRL &= ~MT_BRFUL; + } + uptr->hwmark = 0; + sim_activate(uptr, 5000); + return SCPE_OK; + } + sim_activate(uptr, 420); + return SCPE_OK; +} + +void mt_read_word(UNIT *uptr) { + int i, cc, ch; + + mt_df10.buf = 0; + for(i = 0; i <= 4; i++) { + cc = (8 * (3 - i)) + 4; + ch = mt_buffer[uptr->BPOS]; + if (cc < 0) + mt_df10.buf |= (uint64)(ch & 0x3f); + else + mt_df10.buf |= (uint64)(ch & 0xff) << cc; + uptr->BPOS++; + } +} + +/* Boot from given device */ +t_stat +mt_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + t_mtrlnt reclen; + t_stat r; + 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, &mt_buffer[0], &reclen, BUFFSIZE); + if (r != SCPE_OK) + return r; + uptr->BPOS = 0; + uptr->hwmark = reclen; + + mt_read_word(uptr); + wc = (mt_df10.buf >> 18) & RMASK; + addr = mt_df10.buf & RMASK; + while (wc != 0) { + wc = (wc + 1) & RMASK; + addr = (addr + 1) & RMASK; + if ((uint32)uptr->BPOS >= uptr->hwmark) { + r = sim_tape_rdrecf(uptr, &mt_buffer[0], &reclen, BUFFSIZE); + if (r != SCPE_OK) + return r; + uptr->BPOS = 0; + uptr->hwmark = reclen; + } + mt_read_word(uptr); + if (addr < 020) + FM[addr] = mt_df10.buf; + else + M[addr] = mt_df10.buf; + } + if (addr < 020) + FM[addr] = mt_df10.buf; + else + M[addr] = mt_df10.buf; + + PC = mt_df10.buf & RMASK; + /* If not at end of record and TMA continue record read */ + if ((uint32)uptr->BPOS < uptr->hwmark) { + uptr->CNTRL |= MT_MOTION|MT_BUSY; + uptr->CNTRL &= ~(MT_BRFUL|MT_BUFFUL); + mt_hold_reg = mt_df10.buf = 0; + if ((dptr->flags & MTDF_TYPEB) != 0) { + mt_df10.cia = 020; + mt_df10.cda = addr; + } + hri_mode = 1; + sim_activate(uptr, 300); + } + return SCPE_OK; +} + +t_stat mt_set_mta (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 &= ~MTDF_TYPEB; + if (*cptr == 'B') + dptr->flags |= val; + else if (*cptr != 'A') + return SCPE_ARG; + return SCPE_OK; +} + +t_stat mt_show_mta (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; + if (dptr->flags & val) { + fprintf (st, "TM10B"); + } else { + fprintf (st, "TM10A"); + } + return SCPE_OK; +} + +#if MPX_DEV +/* set MPX level number */ +t_stat mt_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 mpx; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + mpx = (int32) get_uint (cptr, 8, 8, &r); + if (r != SCPE_OK) + return r; + mt_mpx_lvl = mpx; + return SCPE_OK; +} + +t_stat mt_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + + fprintf (st, "MPX=%o", mt_mpx_lvl); + return SCPE_OK; +} +#endif + +t_stat +mt_reset(DEVICE * dptr) +{ + int i; + for (i = 0 ; i < 8; i++) { + UNIT *uptr = &mt_unit[i]; + + if (MT_DENS(uptr->dynflags) == MT_DENS_NONE) + uptr->dynflags = MT_200_VALID | MT_556_VALID; + uptr->CNTRL = 0; + sim_cancel(uptr); + } + mt_df10.devnum = mt_dib.dev_num; + mt_df10.nxmerr = 24; + mt_df10.ccw_comp = 25; + mt_pia = 0; + mt_status = 0; + mt_sel_unit = 0; + mt_next_unit = 0; + mt_hold_reg = 0; + return SCPE_OK; +} + +t_stat +mt_attach(UNIT * uptr, CONST char *file) +{ + return sim_tape_attach_ex(uptr, file, 0, 0); +} + +t_stat +mt_detach(UNIT * uptr) +{ + uptr->CPOS = 0; + return sim_tape_detach(uptr); +} + +t_stat mt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "MT10 Magnetic Tape\n\n"); +fprintf (st, "The MT10 tape controller can be set to either type A or B\n"); +fprintf (st, "The A model lacks a DF10, so all I/O must be polled mode. To set the\n"); +fprintf (st, "tape controller to a B model with DF10 do:\n\n"); +fprintf (st, " sim> SET %s TYPE=B \n", dptr->name); +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 MT10 does support the BOOT command.\n"); +sim_tape_attach_help (st, dptr, uptr, flag, cptr); +return SCPE_OK; +} + +const char *mt_description (DEVICE *dptr) +{ +return "MT10 magnetic tape controller" ; +} + +#endif diff --git a/PDP10/kx10_pt.c b/PDP10/kx10_pt.c new file mode 100644 index 00000000..5d9457ef --- /dev/null +++ b/PDP10/kx10_pt.c @@ -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 diff --git a/PDP10/kx10_rc.c b/PDP10/kx10_rc.c new file mode 100644 index 00000000..5ddeeade --- /dev/null +++ b/PDP10/kx10_rc.c @@ -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 diff --git a/PDP10/kx10_rp.c b/PDP10/kx10_rp.c new file mode 100644 index 00000000..29ec6b4b --- /dev/null +++ b/PDP10/kx10_rp.c @@ -0,0 +1,1350 @@ +/* ka10_rp.c: Dec RH10 RP04/5/6 + + 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_RP +#define NUM_DEVS_RP 0 +#endif + +#if (NUM_DEVS_RP > 0) +#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF) +#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF + +#define RP_NUMWD 128 /* 36bit words/sec */ +#define RP_DEVNUM 0270 /* First device number */ +#define NUM_UNITS_RP 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 */ +/* RPC - 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_UNLOAD 001 /* unload */ +#define FNC_SEEK 002 /* seek */ +#define FNC_RECAL 003 /* recalibrate */ +#define FNC_DCLR 004 /* drive clear */ +#define FNC_RELEASE 005 /* port release */ +#define FNC_OFFSET 006 /* offset */ +#define FNC_RETURN 007 /* return to center */ +#define FNC_PRESET 010 /* read-in preset */ +#define FNC_PACK 011 /* pack acknowledge */ +#define FNC_SEARCH 014 /* search */ +#define FNC_XFER 024 /* >=? data xfr */ +#define FNC_WCHK 024 /* write check */ +#define FNC_WCHKH 025 /* write check headers */ +#define FNC_WRITE 030 /* write */ +#define FNC_WRITEH 031 /* write w/ headers */ +#define FNC_READ 034 /* read */ +#define FNC_READH 035 /* read w/ headers */ +#define CS1_DVA 0004000 /* drive avail NI */ +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) + +/* u3 low */ +/* RPDS - 01 - drive status */ + +#define DS_OFF 0000001 /* offset mode */ +#define DS_VV 0000100 /* 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 */ +/* RPER1 - 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 */ + +/* RPMR - 03 - maintenace register */ + +/* RPAS - 04 - attention summary */ + +#define AS_U0 0000001 /* unit 0 flag */ + +#define DA u4 +/* u4 high */ +/* RPDC - 05 - desired sector */ + +#define DA_V_SC 16 /* sector pos */ +#define DA_M_SC 077 /* sector mask */ +#define DA_V_SF 24 /* track pos */ +#define DA_M_SF 077 /* track mask */ +#define DA_MBZ 0140300 +#define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) + +/* RPDT - 06 - drive type */ + +/* RPLA - 07 - look ahead register */ + +#define LA_V_SC 6 /* sector pos */ + +/* RPER2 - 10 - error status 2 - drive unsafe conditions - unimplemented */ +/* us10 */ +/* RPOF - 11 - offset register */ +/* u4 low */ +/* RPDC - 12 - desired cylinder */ +#define DC_V_CY 0 /* cylinder pos */ +#define DC_M_CY 01777 /* cylinder mask */ +#define DC_MBZ 0176000 +#define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) +#define GET_DA(c,d) ((((GET_CY (c) * rp_drv_tab[d].surf) + GET_SF (c)) \ + * rp_drv_tab[d].sect) + GET_SC (c)) +#define CCYL u5 +/* u5 low */ +/* RPCC - 13 - current cylinder */ + +/* RPSN - 14 - serial number */ +/* RPER3 - 15 - error status 3 - more unsafe conditions - unimplemented */ +#define ERR2 us9 +/* us9 */ +#define ERR3 us10 + +/* RPDB - 176722 - data buffer */ + + +#define OF_HCI 0002000 /* hdr cmp inh NI */ +#define OF_ECI 0004000 /* ECC inhibit NI */ +#define OF_F22 0010000 /* format NI */ +#define OF_MBZ 0161400 + +#define DATAPTR u6 +/* RPEC1 - 16 - ECC status 1 - unimplemented */ +/* RPEC2 - 17 - ECC status 2 - unimplemented */ + + +/* 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/ #cylinders/ + surface cylinder drive + + RP04/5 20 19 411 =88MB + RP06 20 19 815 =176MB + RP07 43 32 630 =516MB + + 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. + + The RP07, despite its name, uses an RM-style controller. +*/ + +#define RP04_DTYPE 0 +#define RP04_SECT 20 +#define RP04_SURF 19 +#define RP04_CYL 411 +#define RP04_DEV 020020 +#define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD) + +#define RP06_DTYPE 1 +#define RP06_SECT 20 +#define RP06_SURF 19 +#define RP06_CYL 815 +#define RP06_DEV 020022 +#define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD) + +#define RP07_DTYPE 2 +#define RP07_SECT 43 +#define RP07_SURF 32 +#define RP07_CYL 630 +#define RP07_DEV 020042 +#define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD) + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 size; /* #blocks */ + int32 devtype; /* device type */ + }; + +struct drvtyp rp_drv_tab[] = { + { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV }, + { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV }, + { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV }, + { 0 } + }; + + +struct df10 rp_df10[NUM_DEVS_RP]; +int rp_xfer_drive[NUM_DEVS_RP]; +uint64 rp_buf[NUM_DEVS_RP][RP_NUMWD]; +int rp_reg[NUM_DEVS_RP]; +int rp_ivect[NUM_DEVS_RP]; +int rp_imode[NUM_DEVS_RP]; +int rp_drive[NUM_DEVS_RP]; +int rp_rae[NUM_DEVS_RP]; +int rp_attn[NUM_DEVS_RP]; +extern int readin_flag; + +t_stat rp_devio(uint32 dev, uint64 *data); +int rp_devirq(uint32 dev, int addr); +void rp_write(int ctlr, int unit, int reg, uint32 data); +uint32 rp_read(int ctlr, int unit, int reg); +t_stat rp_svc(UNIT *); +t_stat rp_boot(int32, DEVICE *); +void rp_ini(UNIT *, t_bool); +t_stat rp_reset(DEVICE *); +t_stat rp_attach(UNIT *, CONST char *); +t_stat rp_detach(UNIT *); +t_stat rp_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat rp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *rp_description (DEVICE *dptr); + + +UNIT rp_unit[] = { +/* Controller 1 */ + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, +#if (NUM_DEVS_RP > 1) +/* Controller 2 */ + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, +#if (NUM_DEVS_RP > 2) +/* Controller 3 */ + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, +#if (NUM_DEVS_RP > 3) +/* Controller 4 */ + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, +#endif +#endif +#endif +}; + +DIB rp_dib[] = { + {RH10_DEV, 1, &rp_devio, &rp_devirq}, + {RH10_DEV, 1, &rp_devio, &rp_devirq}, + {RH10_DEV, 1, &rp_devio, &rp_devirq}, + {RH10_DEV, 1, &rp_devio, &rp_devirq}}; + +MTAB rp_mod[] = { + {UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL}, + {UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL}, + {UNIT_DTYPE, (RP07_DTYPE << UNIT_V_DTYPE), "RP07", "RP07", &rp_set_type }, + {UNIT_DTYPE, (RP06_DTYPE << UNIT_V_DTYPE), "RP06", "RP06", &rp_set_type }, + {UNIT_DTYPE, (RP04_DTYPE << UNIT_V_DTYPE), "RP04", "RP04", &rp_set_type }, + {0} +}; + +REG rpa_reg[] = { + {ORDATA(IVECT, rp_ivect[0], 18)}, + {FLDATA(IMODE, rp_imode[0], 0)}, + {ORDATA(XFER, rp_xfer_drive[0], 3), REG_HRO}, + {ORDATA(DRIVE, rp_drive[0], 3), REG_HRO}, + {ORDATA(REG, rp_reg[0], 6), REG_RO}, + {ORDATA(RAE, rp_rae[0], 8), REG_RO}, + {ORDATA(ATTN, rp_attn[0], 8), REG_RO}, + {FLDATA(READIN, readin_flag, 0), REG_HRO}, + {ORDATA(STATUS, rp_df10[0].status, 18), REG_RO}, + {ORDATA(CIA, rp_df10[0].cia, 18)}, + {ORDATA(CCW, rp_df10[0].ccw, 18)}, + {ORDATA(WCR, rp_df10[0].wcr, 18)}, + {ORDATA(CDA, rp_df10[0].cda, 18)}, + {ORDATA(DEVNUM, rp_df10[0].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_df10[0].buf, 36), REG_HRO}, + {ORDATA(NXM, rp_df10[0].nxmerr, 8), REG_HRO}, + {ORDATA(COMP, rp_df10[0].ccw_comp, 8), REG_HRO}, + {BRDATA(BUFF, &rp_buf[0][0], 16, 64, RP_NUMWD), REG_HRO}, + {0} +}; + +DEVICE rpa_dev = { + "RPA", rp_unit, rpa_reg, rp_mod, + NUM_UNITS_RP, 8, 18, 1, 8, 36, + NULL, NULL, &rp_reset, &rp_boot, &rp_attach, &rp_detach, + &rp_dib[0], DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &rp_help, NULL, NULL, &rp_description +}; + +#if (NUM_DEVS_RP > 1) +REG rpb_reg[] = { + {ORDATA(IVECT, rp_ivect[1], 18)}, + {FLDATA(IMODE, rp_imode[1], 0)}, + {ORDATA(XFER, rp_xfer_drive[1], 3), REG_HRO}, + {ORDATA(DRIVE, rp_drive[1], 3), REG_HRO}, + {ORDATA(REG, rp_reg[1], 6), REG_RO}, + {ORDATA(RAE, rp_rae[1], 8), REG_RO}, + {ORDATA(ATTN, rp_attn[1], 8), REG_RO}, + {ORDATA(STATUS, rp_df10[1].status, 18), REG_RO}, + {ORDATA(CIA, rp_df10[1].cia, 18)}, + {ORDATA(CCW, rp_df10[1].ccw, 18)}, + {ORDATA(WCR, rp_df10[1].wcr, 18)}, + {ORDATA(CDA, rp_df10[1].cda, 18)}, + {ORDATA(DEVNUM, rp_df10[1].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_df10[1].buf, 36), REG_HRO}, + {ORDATA(NXM, rp_df10[1].nxmerr, 8), REG_HRO}, + {ORDATA(COMP, rp_df10[1].ccw_comp, 8), REG_HRO}, + {BRDATA(BUFF, &rp_buf[1][0], 16, 64, RP_NUMWD), REG_HRO}, + {0} +}; + +DEVICE rpb_dev = { + "RPB", &rp_unit[010], rpb_reg, rp_mod, + NUM_UNITS_RP, 8, 18, 1, 8, 36, + NULL, NULL, &rp_reset, &rp_boot, &rp_attach, &rp_detach, + &rp_dib[1], DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &rp_help, NULL, NULL, &rp_description +}; + +#if (NUM_DEVS_RP > 2) +REG rpc_reg[] = { + {ORDATA(IVECT, rp_ivect[2], 18)}, + {FLDATA(IMODE, rp_imode[2], 0)}, + {ORDATA(XFER, rp_xfer_drive[2], 3), REG_HRO}, + {ORDATA(DRIVE, rp_drive[2], 3), REG_HRO}, + {ORDATA(REG, rp_reg[2], 6), REG_RO}, + {ORDATA(RAE, rp_rae[2], 8), REG_RO}, + {ORDATA(ATTN, rp_attn[2], 8), REG_RO}, + {ORDATA(STATUS, rp_df10[2].status, 18), REG_RO}, + {ORDATA(CIA, rp_df10[2].cia, 18)}, + {ORDATA(CCW, rp_df10[2].ccw, 18)}, + {ORDATA(WCR, rp_df10[2].wcr, 18)}, + {ORDATA(CDA, rp_df10[2].cda, 18)}, + {ORDATA(DEVNUM, rp_df10[2].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_df10[2].buf, 36), REG_HRO}, + {ORDATA(NXM, rp_df10[2].nxmerr, 8), REG_HRO}, + {ORDATA(COMP, rp_df10[2].ccw_comp, 8), REG_HRO}, + {BRDATA(BUFF, &rp_buf[2][0], 16, 64, RP_NUMWD), REG_HRO}, + {0} +}; + +DEVICE rpc_dev = { + "RPC", &rp_unit[020], rpc_reg, rp_mod, + NUM_UNITS_RP, 8, 18, 1, 8, 36, + NULL, NULL, &rp_reset, &rp_boot, &rp_attach, &rp_detach, + &rp_dib[2], DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &rp_help, NULL, NULL, &rp_description +}; + +#if (NUM_DEVS_RP > 3) +REG rpd_reg[] = { + {ORDATA(IVECT, rp_ivect[3], 18)}, + {FLDATA(IMODE, rp_imode[3], 0)}, + {ORDATA(XFER, rp_xfer_drive[3], 3), REG_HRO}, + {ORDATA(DRIVE, rp_drive[3], 3), REG_HRO}, + {ORDATA(REG, rp_reg[3], 6), REG_RO}, + {ORDATA(RAE, rp_rae[3], 8), REG_RO}, + {ORDATA(ATTN, rp_attn[3], 8), REG_RO}, + {ORDATA(STATUS, rp_df10[3].status, 18), REG_RO}, + {ORDATA(CIA, rp_df10[3].cia, 18)}, + {ORDATA(CCW, rp_df10[3].ccw, 18)}, + {ORDATA(WCR, rp_df10[3].wcr, 18)}, + {ORDATA(CDA, rp_df10[3].cda, 18)}, + {ORDATA(DEVNUM, rp_df10[3].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_df10[3].buf, 36), REG_HRO}, + {ORDATA(NXM, rp_df10[3].nxmerr, 8), REG_HRO}, + {ORDATA(COMP, rp_df10[3].ccw_comp, 8), REG_HRO}, + {BRDATA(BUFF, &rp_buf[3][0], 16, 64, RP_NUMWD), REG_HRO}, + {0} +}; + +DEVICE rpd_dev = { + "RPD", &rp_unit[030], rpd_reg, rp_mod, + NUM_UNITS_RP, 8, 18, 1, 8, 36, + NULL, NULL, &rp_reset, &rp_boot, &rp_attach, &rp_detach, + &rp_dib[3], DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &rp_help, NULL, NULL, &rp_description +}; + +#endif +#endif +#endif + +DEVICE *rp_devs[] = { + &rpa_dev, +#if (NUM_DEVS_RP > 1) + &rpb_dev, +#if (NUM_DEVS_RP > 2) + &rpc_dev, +#if (NUM_DEVS_RP > 3) + &rpd_dev, +#endif +#endif +#endif +}; + + +t_stat rp_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 = &rp_df10[ctlr]; + df10->devnum = dev; + switch(dev & 3) { + case CONI: + *data = df10->status & ~(IADR_ATTN|IARD_RAE); + if (rp_attn[ctlr] != 0 && (df10->status & IADR_ATTN)) + *data |= IADR_ATTN; + if (rp_rae[ctlr] != 0 && (df10->status & IARD_RAE)) + *data |= IARD_RAE; +#if KI_22BIT + *data |= B22_FLAG; +#endif + sim_debug(DEBUG_CONI, dptr, "RP %03o CONI %06o PC=%o %o\n", + dev, (uint32)*data, PC, rp_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_RP; drive++, uptr++) { + uptr->CMD &= DS_MOL|DS_WRL|DS_DPR|DS_DRY|DS_VV|076; + uptr->DA &= 003400177777; + uptr->CCYL &= 0177777; + uptr->ERR2 = 0; + uptr->ERR3 = 0; + } + } + 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 && rp_attn[ctlr] != 0) + set_interrupt(dev, df10->status); + sim_debug(DEBUG_CONO, dptr, "RP %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 && rp_reg[ctlr] != 04) { + df10->status |= CC_CHAN_ACT; + return SCPE_OK; + } + if (rp_reg[ctlr] == 040) { + *data = (uint64)(rp_read(ctlr, rp_drive[ctlr], 0) & 077); + *data |= ((uint64)(df10->cia)) << 6; + *data |= ((uint64)(rp_xfer_drive[ctlr])) << 18; + } else if (rp_reg[ctlr] == 044) { + *data = (uint64)rp_ivect[ctlr]; + if (rp_imode[ctlr]) + *data |= IRQ_KI10; + else + *data |= IRQ_KA10; + } else if (rp_reg[ctlr] == 054) { + *data = (uint64)(rp_rae[ctlr]); + } else if ((rp_reg[ctlr] & 040) == 0) { + int parity; + *data = (uint64)(rp_read(ctlr, rp_drive[ctlr], rp_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)(rp_drive[ctlr])) << 18; + } + *data |= ((uint64)(rp_reg[ctlr])) << 30; + sim_debug(DEBUG_DATAIO, dptr, "RP %03o DATI %012llo, %d %d PC=%06o\n", + dev, *data, ctlr, rp_drive[ctlr], PC); + return SCPE_OK; + + case DATAO: + sim_debug(DEBUG_DATAIO, dptr, "RP %03o DATO %012llo, %d PC=%06o %06o\n", + dev, *data, ctlr, PC, df10->status); + rp_reg[ctlr] = ((int)(*data >> 30)) & 077; + if (rp_reg[ctlr] < 040 && rp_reg[ctlr] != 04) { + rp_drive[ctlr] = (int)(*data >> 18) & 07; + } + if (*data & LOAD_REG) { + if (rp_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) & 037) < FNC_XFER) { + df10->status |= CXR_ILC; + df10_setirq(df10); + sim_debug(DEBUG_DATAIO, dptr, + "RP %03o command abort %012llo, %d[%d] PC=%06o %06o\n", + dev, *data, ctlr, rp_drive[ctlr], PC, df10->status); + return SCPE_OK; + } + /* Start command */ + df10_setup(df10, (uint32)(*data >> 6)); + rp_xfer_drive[ctlr] = (int)(*data >> 18) & 07; + rp_write(ctlr, rp_drive[ctlr], 0, (uint32)(*data & 077)); + sim_debug(DEBUG_DATAIO, dptr, + "RP %03o command %012llo, %d[%d] PC=%06o %06o\n", + dev, *data, ctlr, rp_drive[ctlr], PC, df10->status); + } else if (rp_reg[ctlr] == 044) { + /* Set KI10 Irq vector */ + rp_ivect[ctlr] = (int)(*data & IRQ_VECT); + rp_imode[ctlr] = (*data & IRQ_KI10) != 0; + } else if (rp_reg[ctlr] == 050) { + ; /* Diagnostic access to mass bus. */ + } else if (rp_reg[ctlr] == 054) { + /* clear flags */ + rp_rae[ctlr] &= ~(*data & 0377); + if (rp_rae[ctlr] == 0) + clr_interrupt(dev); + } else if ((rp_reg[ctlr] & 040) == 0) { + rp_drive[ctlr] = (int)(*data >> 18) & 07; + /* Check if access error */ + if (rp_rae[ctlr] & (1 << rp_drive[ctlr])) { + return SCPE_OK; + } + rp_write(ctlr, rp_drive[ctlr], rp_reg[ctlr] & 037, + (int)(*data & 0777777)); + } + } + return SCPE_OK; + } + return SCPE_OK; /* Unreached */ +} + +/* Handle KI and KL style interrupt vectors */ +int +rp_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 (rp_imode[drive] ? rp_ivect[drive] : addr); + } + return addr; +} + +void +rp_write(int ctlr, int unit, int reg, uint32 data) { + int i; + DEVICE *dptr = rp_devs[ctlr]; + UNIT *uptr = &dptr->units[unit]; + struct df10 *df10 = &rp_df10[ctlr]; + int dtype = GET_DTYPE(uptr->flags); + + 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, "RP%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, "RP%o %d not ready\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, "RP%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_RECAL: /* recalibrate */ + uptr->DA &= ~0177777; + /* Fall through */ + + case FNC_RETURN: /* return to center */ + case FNC_OFFSET: /* offset */ + case FNC_UNLOAD: /* unload */ + uptr->CMD &= ~DS_OFF; + /* Fall through */ + + case FNC_SEARCH: /* search */ + case FNC_SEEK: /* seek */ + case FNC_WCHK: /* write check */ + case FNC_WRITE: /* write */ + case FNC_WRITEH: /* write w/ headers */ + case FNC_READ: /* read */ + case FNC_READH: /* read w/ headers */ + uptr->CMD |= DS_PIP; + + if (GET_CY(uptr->DA) >= rp_drv_tab[dtype].cyl || + GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || + GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { + rp_attn[ctlr] &= ~(1<CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; + uptr->CMD &= ~DS_PIP; + df10->status &= ~BUSY; + if ((df10->status & IADR_ATTN) != 0 && rp_attn[ctlr] != 0) + df10_setirq(df10); + break; + } + + uptr->CMD |= CR_GO; + CLR_BUF(uptr); + uptr->DATAPTR = 0; + break; + + + case FNC_DCLR: /* drive clear */ + uptr->CMD |= DS_DRY; + uptr->CMD &= ~(DS_ATA|CR_GO); + uptr->DA &= 003400177777; + uptr->CCYL &= 0177777; + uptr->ERR2 = 0; + uptr->ERR3 = 0; + rp_attn[ctlr] &= ~(1<status & IADR_ATTN) != 0 && rp_attn[ctlr] != 0) + df10_setirq(df10); + break; + + case FNC_PRESET: /* read-in preset */ + uptr->DA = 0; + uptr->CCYL &= 0177777; + uptr->CMD &= ~DS_OFF; + /* Fall through */ + + case FNC_RELEASE: /* port release */ + case FNC_PACK: /* pack acknowledge */ + if ((uptr->flags & UNIT_ATT) != 0) + uptr->CMD |= DS_VV; + uptr->CMD |= DS_DRY; + if ((df10->status & IADR_ATTN) != 0 && rp_attn[ctlr] != 0) + df10_setirq(df10); + break; + + default: + uptr->CMD |= DS_DRY|DS_ERR|DS_ATA; + uptr->CMD |= (ER1_ILF << 16); + rp_attn[ctlr] |= (1<status & IADR_ATTN) != 0 && rp_attn[ctlr] != 0) + df10_setirq(df10); + } + if (uptr->CMD & CR_GO) + sim_activate(uptr, 1000); + clr_interrupt(df10->devnum); + if ((df10->status & (IADR_ATTN|BUSY)) == IADR_ATTN && rp_attn[ctlr] != 0) + df10_setirq(df10); + sim_debug(DEBUG_DETAIL, dptr, "RP%o AStatus=%06o\n", unit, uptr->CMD); + return; + case 001: /* status */ + break; + case 002: /* error register 1 */ + uptr->CMD &= 0177777; + uptr->CMD |= data << 16; + uptr->CMD &= ~DS_ERR; + if ((((uptr->CMD >> 16) & 0177777) | uptr->ERR2 | uptr->ERR3) != 0) + uptr->CMD |= DS_ERR; + break; + case 003: /* maintenance */ + break; + case 004: /* atten summary */ + for (i = 0; i < 8; i++) { + if (data & (1<devnum); + break; + case 005: /* sector/track */ + uptr->DA &= 0177777; + uptr->DA |= data << 16; + break; + case 014: /* error register 2 */ + uptr->ERR2 = data; + uptr->CMD &= ~DS_ERR; + if ((((uptr->CMD >> 16) & 0177777) | uptr->ERR2 | uptr->ERR3) != 0) + uptr->CMD |= DS_ERR; + break; + case 006: /* drive type */ + case 007: /* look ahead */ + break; + case 011: /* offset */ + uptr->CCYL &= 0177777; + uptr->CCYL |= data << 16; + break; + case 012: /* desired cylinder */ + uptr->DA &= ~0177777; + uptr->DA |= data; + break; + case 015: /* error register 3 */ + uptr->ERR3 = data; + uptr->CMD &= ~DS_ERR; + if ((((uptr->CMD >> 16) & 0177777) | uptr->ERR2 | uptr->ERR3) != 0) + uptr->CMD |= DS_ERR; + break; + case 013: /* current cylinder */ + case 010: /* serial no */ + case 016: /* ecc position */ + case 017: /* ecc pattern */ + break; + default: + uptr->CMD |= (ER1_ILR<<16)|DS_ERR; + rp_rae[ctlr] &= ~(1<units[unit]; + struct df10 *df10; + uint32 temp = 0; + int i; + + if ((uptr->flags & UNIT_ATT) == 0 && reg != 04) { /* not attached? */ + return 0; + } + switch(reg) { + case 000: /* control */ + df10 = &rp_df10[ctlr]; + temp = uptr->CMD & 076; + if (uptr->flags & UNIT_ATT) + temp |= CS1_DVA; + if (df10->status & BUSY || uptr->CMD & CR_GO) + temp |= CS1_GO; + break; + case 001: /* status */ + temp = uptr->CMD & 0177700; + break; + case 002: /* error register 1 */ + temp = (uptr->CMD >> 16) & 0177777; + break; + case 003: /* maintenance */ + break; + case 004: /* atten summary */ + for (i = 0; i < 8; i++) { + if (rp_unit[(ctlr * 8) + i].CMD & DS_ATA) { + temp |= 1 << i; + } + } + break; + case 005: /* sector/track */ + temp = (uptr->DA >> 16) & 0177777; + break; + case 006: /* drive type */ + temp = rp_drv_tab[GET_DTYPE(uptr->flags)].devtype; + break; + case 011: /* offset */ + temp = (uptr->CCYL >> 16) & 0177777; + break; + case 012: /* desired cylinder */ + temp = uptr->DA & 0177777; + break; + case 013: /* current cylinder */ + temp = uptr->CCYL & 0177777; + break; + case 010: /* serial no */ + temp = (020 * ctlr) + (unit + 1); + break; + case 014: /* error register 2 */ + temp = uptr->ERR2; + break; + case 015: /* error register 3 */ + temp = uptr->ERR3; + break; + case 007: /* look ahead */ + case 016: /* ecc position */ + case 017: /* ecc pattern */ + break; + default: + uptr->CMD |= (ER1_ILR<<16); + rp_rae[ctlr] &= ~(1<flags); + int ctlr = GET_CNTRL(uptr->flags); + int unit; + DEVICE *dptr; + struct df10 *df; + int cyl = GET_CY(uptr->DA); + int diff, da; + t_stat r; + + /* Find dptr, and df10 */ + dptr = rp_devs[ctlr]; + unit = uptr - dptr->units; + df = &rp_df10[ctlr]; + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + uptr->CMD |= (ER1_UNS << 16) | DS_ATA|DS_ERR; /* set drive error */ + if (GET_FNC(uptr->CMD) >= FNC_XFER) { /* xfr? set done */ + df->status &= ~BUSY; + df10_setirq(df); + } + return (SCPE_OK); + } + + /* Check if seeking */ + if (uptr->CMD & DS_PIP) { + sim_debug(DEBUG_DETAIL, dptr, "RP%o seek %d %d\n", unit, cyl, uptr->CCYL); + if (cyl >= rp_drv_tab[dtype].cyl) { + uptr->CMD &= ~DS_PIP; + uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; + } + diff = cyl - (uptr->CCYL & 01777); + if (diff < 0) { + if (diff < -50) { + uptr->CCYL -= 50; + sim_activate(uptr, 500); + } else if (diff < -10) { + uptr->CCYL -= 10; + sim_activate(uptr, 200); + } else { + uptr->CCYL -= 1; + sim_activate(uptr, 100); + } + return SCPE_OK; + } else if (diff > 0) { + if (diff > 50) { + uptr->CCYL += 50; + sim_activate(uptr, 500); + } else if (diff > 10) { + uptr->CCYL += 10; + sim_activate(uptr, 200); + } else { + uptr->CCYL += 1; + sim_activate(uptr, 100); + } + return SCPE_OK; + } else { + uptr->CMD &= ~DS_PIP; + uptr->DATAPTR = 0; + } + } + + switch (GET_FNC(uptr->CMD)) { + case FNC_NOP: + case FNC_DCLR: /* drive clear */ + case FNC_RELEASE: /* port release */ + case FNC_PACK: /* pack acknowledge */ + break; + case FNC_UNLOAD: /* unload */ + rp_detach(uptr); + /* Fall through */ + case FNC_OFFSET: /* offset */ + uptr->CMD |= DS_OFF; + /* Fall through */ + case FNC_RETURN: /* return to center */ + case FNC_PRESET: /* read-in preset */ + case FNC_RECAL: /* recalibrate */ + case FNC_SEEK: /* seek */ + if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || + GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) + uptr->CMD |= (ER1_IAE << 16)|DS_ERR; + rp_attn[ctlr] |= 1<CMD |= DS_DRY|DS_ATA; + uptr->CMD &= ~CR_GO; + if ((df->status & (IADR_ATTN|BUSY)) == IADR_ATTN) + df10_setirq(df); + sim_debug(DEBUG_DETAIL, dptr, "RP%o seekdone %d %o\n", unit, cyl, uptr->CMD); + break; + + case FNC_SEARCH: /* search */ + if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || + GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) + uptr->CMD |= (ER1_IAE << 16)|DS_ERR; + rp_attn[ctlr] |= 1<CMD |= DS_DRY|DS_ATA; + uptr->CMD &= ~CR_GO; + if ((df->status & (IADR_ATTN|BUSY)) == IADR_ATTN) + df10_setirq(df); + sim_debug(DEBUG_DETAIL, dptr, "RP%o searchdone %d %o\n", unit, cyl, uptr->CMD); + break; + + case FNC_READ: /* read */ + case FNC_READH: /* read w/ headers */ + case FNC_WCHK: /* write check */ + if (uptr->CMD & DS_ERR) { + sim_debug(DEBUG_DETAIL, dptr, "RP%o read error\n", unit); + goto rd_end; + } + + if (BUF_EMPTY(uptr)) { + int wc; + + if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || + GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { + uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; + uptr->CMD &= ~CR_GO; + df10_finish_op(df, 0); + sim_debug(DEBUG_DETAIL, dptr, "RP%o readx done\n", unit); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, dptr, "RP%o read (%d,%d,%d)\n", unit, cyl, + GET_SF(uptr->DA), GET_SC(uptr->DA)); + da = GET_DA(uptr->DA, dtype) * RP_NUMWD; + (void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET); + wc = sim_fread (&rp_buf[ctlr][0], sizeof(uint64), RP_NUMWD, + uptr->fileref); + while (wc < RP_NUMWD) + rp_buf[ctlr][wc++] = 0; + uptr->hwmark = RP_NUMWD; + uptr->DATAPTR = 0; + /* On read headers, transfer 2 words to start */ + if (GET_FNC(uptr->CMD) == FNC_READH) { + df->buf = (((uint64)cyl) << 18) | + ((uint64)((GET_SF(uptr->DA) << 8) | GET_SF(uptr->DA))); + sim_debug(DEBUG_DATA, dptr, "RP%o read word h1 %012llo %09o %06o\n", + unit, df->buf, df->cda, df->wcr); + if (df10_write(df) == 0) + goto rd_end; + df->buf = ((uint64)((020 * ctlr) + (unit + 1)) << 18) | (uint64)(unit); + sim_debug(DEBUG_DATA, dptr, "RP%o read word h2 %012llo %09o %06o\n", + unit, df->buf, df->cda, df->wcr); + if (df10_write(df) == 0) + goto rd_end; + } + } + + df->buf = rp_buf[ctlr][uptr->DATAPTR++]; + sim_debug(DEBUG_DATA, dptr, "RP%o read word %d %012llo %09o %06o\n", + unit, uptr->DATAPTR, df->buf, df->cda, df->wcr); + if (df10_write(df)) { + if (uptr->DATAPTR == RP_NUMWD) { + /* Increment to next sector. Set Last Sector */ + uptr->DATAPTR = 0; + CLR_BUF(uptr); + uptr->DA += 1 << DA_V_SC; + if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect) { + uptr->DA &= (DA_M_SF << DA_V_SF) | (DC_M_CY << DC_V_CY); + uptr->DA += 1 << DA_V_SF; + if (GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { + uptr->DA &= (DC_M_CY << DC_V_CY); + uptr->DA += 1 << DC_V_CY; + uptr->CMD |= DS_PIP; + } + } + } + sim_activate(uptr, 50); + } else { +rd_end: + sim_debug(DEBUG_DETAIL, dptr, "RP%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 */ + case FNC_WRITEH: /* write w/ headers */ + if (uptr->CMD & DS_ERR) { + sim_debug(DEBUG_DETAIL, dptr, "RP%o read error\n", unit); + goto wr_end; + } + + if (BUF_EMPTY(uptr)) { + if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || + GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { + uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; + uptr->CMD &= ~CR_GO; + df10_finish_op(df, 0); + sim_debug(DEBUG_DETAIL, dptr, "RP%o writex done\n", unit); + return SCPE_OK; + } + /* On Write headers, transfer 2 words to start */ + if (GET_FNC(uptr->CMD) == FNC_WRITEH) { + if (df10_read(df) == 0) + goto wr_end; + sim_debug(DEBUG_DATA, dptr, "RP%o write word h1 %012llo %06o\n", + unit, df->buf, df->wcr); + if (df10_read(df) == 0) + goto wr_end; + sim_debug(DEBUG_DATA, dptr, "RP%o write word h2 %012llo %06o\n", + unit, df->buf, df->wcr); + } + uptr->DATAPTR = 0; + uptr->hwmark = 0; + } + r = df10_read(df); + sim_debug(DEBUG_DATA, dptr, "RP%o write word %d %012llo %06o\n", + unit, uptr->DATAPTR, df->buf, df->wcr); + rp_buf[ctlr][uptr->DATAPTR++] = df->buf; + if (r == 0 || uptr->DATAPTR == RP_NUMWD) { + while (uptr->DATAPTR < RP_NUMWD) + rp_buf[ctlr][uptr->DATAPTR++] = 0; + sim_debug(DEBUG_DETAIL, dptr, "RP%o write (%d,%d,%d)\n", unit, cyl, + GET_SF(uptr->DA), GET_SC(uptr->DA)); + da = GET_DA(uptr->DA, dtype) * RP_NUMWD; + (void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET); + (void)sim_fwrite (&rp_buf[ctlr][0], sizeof(uint64), RP_NUMWD, + uptr->fileref); + uptr->DATAPTR = 0; + CLR_BUF(uptr); + if (r) { + uptr->DA += 1 << DA_V_SC; + if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect) { + uptr->DA &= (DA_M_SF << DA_V_SF) | (DC_M_CY << DC_V_CY); + uptr->DA += 1 << DA_V_SF; + if (GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { + uptr->DA &= (DC_M_CY << DC_V_CY); + uptr->DA += 1 << DC_V_CY; + uptr->CMD |= DS_PIP; + } + } + } + } + if (r) { + sim_activate(uptr, 50); + } else { +wr_end: + sim_debug(DEBUG_DETAIL, dptr, "RP%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 +rp_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 = rp_drv_tab[i].size; + return SCPE_OK; +} + + +t_stat +rp_reset(DEVICE * rptr) +{ + int ctlr; + for (ctlr = 0; ctlr < NUM_DEVS_RP; ctlr++) { + rp_df10[ctlr].devnum = rp_dib[ctlr].dev_num; + rp_df10[ctlr].nxmerr = 19; + rp_df10[ctlr].ccw_comp = 14; + rp_df10[ctlr].status = 0; + rp_attn[ctlr] = 0; + rp_rae[ctlr] = 0; + } + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +rp_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 = &rp_df10[ctlr]; + dptr = rp_devs[ctlr]; + (void)sim_fseek(uptr->fileref, 0, SEEK_SET); + (void)sim_fread (&rp_buf[0][0], sizeof(uint64), RP_NUMWD, uptr->fileref); + uptr->CMD |= DS_VV; + addr = rp_buf[0][ptr] & RMASK; + wc = (rp_buf[0][ptr++] >> 18) & RMASK; + while (wc != 0) { + wc = (wc + 1) & RMASK; + addr = (addr + 1) & RMASK; + word = rp_buf[0][ptr++]; + if (addr < 020) + FM[addr] = word; + else + M[addr] = word; + } + addr = rp_buf[0][ptr] & RMASK; + wc = (rp_buf[0][ptr++] >> 18) & RMASK; + word = rp_buf[0][ptr++]; + + rp_reg[ctlr] = 040; + rp_drive[ctlr] = uptr - dptr->units; + df->status |= CCW_COMP_1|PI_ENABLE; + PC = word & RMASK; + return SCPE_OK; +} + +/* Device attach */ + +t_stat rp_attach (UNIT *uptr, CONST char *cptr) +{ + t_stat r; + DEVICE *rptr; + DIB *dib; + int ctlr; + + uptr->capac = rp_drv_tab[GET_DTYPE (uptr->flags)].size; + r = attach_unit (uptr, cptr); + if (r != SCPE_OK) + return r; + rptr = find_dev_from_unit(uptr); + if (rptr == 0) + return SCPE_OK; + dib = (DIB *) rptr->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; + rp_df10[ctlr].status |= PI_ENABLE; + set_interrupt(dib->dev_num, rp_df10[ctlr].status); + return SCPE_OK; +} + +/* Device detach */ + +t_stat rp_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 rp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "RP04/05/06/07 Disk Pack Drives (RP)\n\n"); +fprintf (st, "The RP controller implements the Massbus family of large 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 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 RP device supports the BOOT command.\n"); +fprint_reg_help (st, dptr); +return SCPE_OK; +} + +const char *rp_description (DEVICE *dptr) +{ + return "RP04/05/06/07 Massbus disk controller"; +} + + +#endif diff --git a/PDP10/kx10_rs.c b/PDP10/kx10_rs.c new file mode 100644 index 00000000..d550c27e --- /dev/null +++ b/PDP10/kx10_rs.c @@ -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<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<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<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 diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c new file mode 100644 index 00000000..534942c5 --- /dev/null +++ b/PDP10/kx10_sys.c @@ -0,0 +1,1187 @@ +/* ka10_sys.c: PDP-10 simulator interface + Derived from Bob Supnik's pdp10_sys.c + + + Copyright (c) 2005-2009, Robert M Supnik + Copyright (c) 2011-2018, 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_card.h" +#include + + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +#if KLB +char sim_name[] = "KL-10B"; +#endif +#if KLA +char sim_name[] = "KL-10A"; +#endif +#if KI +char sim_name[] = "KI-10"; +#endif +#if KA +char sim_name[] = "KA-10"; +#endif +#if PDP6 +char sim_name[] = "PDP6"; +#endif + +extern REG cpu_reg[]; +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, +#if PDP6 | KA | KI + &cty_dev, +#endif +#if (NUM_DEVS_PT > 0) + &ptp_dev, + &ptr_dev, +#endif +#if (NUM_DEVS_LP > 0) + &lpt_dev, +#endif +#if (NUM_DEVS_CR > 0) + &cr_dev, +#endif +#if (NUM_DEVS_CP > 0) + &cp_dev, +#endif +#if (NUM_DEVS_DCT > 0) + &dct_dev, +#endif +#if (NUM_DEVS_MT > 0) + &mt_dev, +#endif +#if (NUM_DEVS_MTC > 0) + &mtc_dev, +#endif +#if (NUM_DEVS_DP > 0) + &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 +#endif +#if (NUM_DEVS_RS > 0) + &rsa_dev, +#endif +#if (NUM_DEVS_RP > 0) + &rpa_dev, +#if (NUM_DEVS_RP > 1) + &rpb_dev, +#if (NUM_DEVS_RP > 2) + &rpc_dev, +#if (NUM_DEVS_RP > 3) + &rpd_dev, +#endif +#endif +#endif +#endif +#if (NUM_DEVS_TU > 0) + &tua_dev, +#endif +#if (NUM_DEVS_DSK > 0) + &dsk_dev, +#endif +#if (NUM_DEVS_RC > 0) + &rca_dev, +#if (NUM_DEVS_RC > 1) + &rcb_dev, +#endif +#endif +#if (NUM_DEVS_PMP > 0) + &pmp_dev, +#endif +#if (NUM_DEVS_DT > 0) + &dt_dev, +#endif +#if (NUM_DEVS_DTC > 0) + &dtc_dev, +#endif +#if (NUM_DEVS_DC > 0) + &dc_dev, +#endif +#if (NUM_DEVS_DCS > 0) + &dcs_dev, +#endif +#if (NUM_DEVS_DK > 0) + &dk_dev, +#endif +#if (NUM_DEVS_PD > 0) + &pd_dev, +#endif +#if (NUM_DEVS_DPY > 0) + &dpy_dev, +#if (NUM_DEVS_WCNSLS > 0) + &wcnsls_dev, +#endif +#endif +#if NUM_DEVS_IMP > 0 + &imp_dev, +#endif +#if NUM_DEVS_CH10 > 0 + &ch10_dev, +#endif +#if NUM_DEVS_IMX > 0 + &imx_dev, +#endif +#if NUM_DEVS_STK > 0 +#ifdef USE_DISPLAY + &stk_dev, +#endif +#endif +#if NUM_DEVS_TK10 > 0 + &tk10_dev, +#endif +#if NUM_DEVS_MTY > 0 + &mty_dev, +#endif +#if NUM_DEVS_TEN11 > 0 + &ten11_dev, +#endif +#if NUM_DEVS_AUXCPU > 0 + &auxcpu_dev, +#endif +#if NUM_DEVS_DKB > 0 + &dkb_dev, +#endif +#if NUM_DEVS_DPK > 0 + &dpk_dev, +#endif + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint" + }; + +/* Simulator debug controls */ +DEBTAB dev_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"CONI", DEBUG_CONI, "Show coni instructions"}, + {"CONO", DEBUG_CONO, "Show coni instructions"}, + {"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + {0, 0} +}; + +/* Simulator debug controls */ +DEBTAB crd_debug[] = { + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"CONI", DEBUG_CONI, "Show coni instructions"}, + {"CONO", DEBUG_CONO, "Show coni instructions"}, + {"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + {"CARD", DEBUG_CARD, "Show Card read/punches"}, + {0, 0} +}; + +/* Binary loader, supports RIM10, SAV, EXE */ + +#define FMT_R 1 /* RIM10 */ +#define FMT_S 2 /* SAV */ +#define FMT_E 3 /* EXE */ +#define FMT_D 4 /* WAITS DMP */ +#define FMT_I 5 /* ITS SBLK */ + +#define EXE_DIR 01776 /* EXE directory */ +#define EXE_VEC 01775 /* EXE entry vec */ +#define EXE_PDV 01774 /* EXE ignored */ +#define EXE_END 01777 /* EXE end */ + +/* WAITS Octal dump loader. + + Simple octal ASCII file, one word per line. All lines which don't + start with 0-7 are ignored. Everything after the octal constant is + also ignored. + +*/ +t_stat load_dmp (FILE *fileref) +{ + char buffer[100]; + char *p; + uint32 addr = 074; + uint64 data; + int high = 0; + + while (fgets((char *)buffer, 80, fileref) != 0) { + p = (char *)buffer; + while (*p >= '0' && *p <= '7') { + data = 0; + while (*p >= '0' && *p <= '7') { + data = (data << 3) + *p - '0'; + p++; + } + if (addr == 0135 && data != 0) + high = (uint32)(data & RMASK); + if (high != 0 && high == addr) { + addr = 0400000; + high = 0; + } + M[addr++] = data; + if (*p == ' ' || *p == '\t') + p++; + } + } + return SCPE_OK; +} + + +int get_evac(FILE *fileref, uint64 *word) +{ + static uint64 data = 0; + static int bits = 0; + unsigned char buf[4]; + unsigned char octet; + + while (bits < 36) { + if (sim_fread(&octet, 1, 1, fileref) != 1) + return 1; + + if (octet <= 011 || + (octet >= 013 && octet <= 014) || + (octet >= 016 && octet <= 0176)) { + data <<= 7; + data |= octet; + bits += 7; + } else if (octet == 012) { + data <<= 14; + data |= 015 << 7 | 012; + bits += 14; + } else if (octet == 015) { + data <<= 7; + data |= 012; + bits += 7; + } else if (octet == 0177) { + data <<= 14; + data |= 0177 << 7 | 7; + bits += 14; + } else if ((octet >= 0200 && octet <= 0206) || + (octet >= 0210 && octet <= 0211) || + (octet >= 0213 && octet <= 0214) || + (octet >= 0216 && octet <= 0355)) { + data <<= 14; + data |= 0177 << 7 | (octet - 0200); + bits += 14; + } else if (octet == 0207) { + data <<= 14; + data |= 0177 << 7 | 0177; + bits += 14; + } else if (octet == 0212) { + data <<= 14; + data |= 0177 << 7 | 015; + bits += 14; + } else if (octet == 0215) { + data <<= 14; + data |= 0177 << 7 | 012; + bits += 14; + } else if (octet == 0356) { + data <<= 7; + data |= 015; + bits += 7; + } else if (octet == 0357) { + data <<= 7; + data |= 0177; + bits += 7; + } else if (octet >= 0360) { + if (bits != 0) + return 1; + + if (sim_fread(&buf, 1, 4, fileref) != 4) + return 1; + + data = (uint64)(octet & 017) << 32; + data |= (uint64)(buf[0]) << 24; + data |= (uint64)(buf[1]) << 16; + data |= (uint64)(buf[2]) << 8; + data |= (uint64)(buf[3]); + bits = 36; + } + + if (bits == 35) { + data <<= 1; + bits++; + } + } + + if (bits == 42) { + *word = (data >> 6) & -2LL; + data &= 0177; + bits = 7; + } else { + *word = data; + data = 0; + bits = 0; + } + + return 0; +} + +/* ITS SBLK loader. + + The SBLK format is similar to SAV. However, ITS files stored as + octet streams are usually in the "evacuate" format. +*/ +t_stat load_sblk (FILE *fileref) +{ + uint64 word; + uint64 check; + int count, addr; + + /* First, skip over the paper tape bootstrap. It ends with a + JRST 1. */ + do { + if (get_evac (fileref, &word)) + return SCPE_FMT; + } while (word != JRST1); + + /* Load all "simple blocks". They start with an AOBJN pointer, + and then comes the data. Last is a checksum word. */ + while (get_evac (fileref, &word) == 0 && (word & SMASK)) { + check = word; + count = (int)((((word >> 18) ^ RMASK) + 1) & RMASK); + addr = word & RMASK; + while (count-- > 0) { + if (get_evac (fileref, &word)) + return SCPE_FMT; + M[addr++] = word; + check = (check << 1) + (check >> 35) + word; + check &= FMASK; + } + if (get_evac (fileref, &word)) + return SCPE_FMT; + if (check != word) + return SCPE_FMT; + } + + /* After the simple blocks comes a start instruction. It should + be a JRST or JUMPA. */ + if ((word >> 27) != OP_JRST && (word >> 27) != OP_JUMPA) + return SCPE_FMT; + + PC = word & RMASK; + + return SCPE_OK; +} + + +/* RIM10 loader + + RIM10 format is a binary paper tape format (all data frames + are 200 or greater). It consists of blocks containing + + -count,,origin-1 + word + : + word + checksum (includes IOWD) + : + JRST start +*/ + +#define RIM_EOF 0xFFFFFFFFFFFFFFFFLL +uint64 getrimw (FILE *fileref) +{ +int32 i, tmp; +uint64 word; + +word = 0; +for (i = 0; i < 6;) { + if ((tmp = getc (fileref)) == EOF) + return RIM_EOF; + if (tmp & 0200) { + word = (word << 6) | ((uint64) tmp & 077); + i++; + } + } +return word; +} +#define TSTS(x) SMASK & (x) +#define AOB(x) FMASK & ((x) + 01000001LL) +t_stat load_rim (FILE *fileref) +{ +uint64 count, cksm, data; +t_bool its_rim; +uint32 pa; +int32 op, i, ldrc; + +data = getrimw (fileref); /* get first word */ +if ((data & AMASK) != 0) /* error? SA != 0? */ + return SCPE_FMT; +ldrc = 1 + (RMASK ^ ((int32) ((data >> 18) & RMASK))); /* get loader count */ +if (ldrc == 016) /* 16? RIM10B */ + its_rim = FALSE; +else if (ldrc == 017) /* 17? ITS RIM */ + its_rim = TRUE; +else return SCPE_FMT; /* unknown */ + +for (i = 0; i < ldrc; i++) { /* skip the loader */ + data = getrimw (fileref); + if (data == RIM_EOF) + return SCPE_FMT; + } + +for ( ;; ) { /* loop until JRST */ + count = cksm = getrimw (fileref); /* get header */ + if (count == RIM_EOF) /* read err? */ + return SCPE_FMT; + if (TSTS (count)) { /* hdr = IOWD? */ + for ( ; TSTS (count); count = AOB (count)) { + data = getrimw (fileref); /* get data wd */ + if (data == RIM_EOF) + return SCPE_FMT; + if (its_rim) { /* ITS RIM? */ + cksm = (((cksm << 1) | (cksm >> 35))) & FMASK; + /* add to rotated cksm */ + pa = ((uint32) count) & RMASK; /* store */ + } + else { /* RIM10B */ + pa = ((uint32) count + 1) & RMASK; /* store */ + } + cksm = (cksm + data) & FMASK; /* add to cksm */ + M[pa] = data; + } /* end for */ + data = getrimw (fileref); /* get cksm */ + if (data == RIM_EOF) + return SCPE_FMT; + if (cksm != data) /* test cksm */ + return SCPE_CSUM; + } /* end if count */ + else { + op = GET_OP (count); /* not IOWD */ + if (op != OP_JRST) /* JRST? */ + return SCPE_FMT; + PC = (uint32) count & RMASK; /* set PC */ + break; + } /* end else */ + } /* end for */ +return SCPE_OK; +} + + +int get_word(FILE *fileref, uint64 *word) +{ + char cbuf[5]; + + if (sim_fread(cbuf, 1, 5, fileref) != 5) + return 1; + *word = ((uint64)(cbuf[0]) << 29) | + ((uint64)(cbuf[1]) << 22) | + ((uint64)(cbuf[2]) << 15) | + ((uint64)(cbuf[3]) << 8) | + ((uint64)(cbuf[4] & 0177) << 1) | + ((uint64)(cbuf[4] & 0200) >> 7); + return 0; +} + +/* SAV file loader + + SAV format is a disk file format (36b words). It consists of + blocks containing: + + -count,,origin-1 + word + : + word + : + JRST start +*/ + +t_stat load_sav (FILE *fileref) +{ + uint64 data; + uint32 pa; + int32 wc; + + for ( ;; ) { /* loop */ + if (get_word(fileref, &data)) + return SCPE_OK; + wc = (int32)(data >> 18); + pa = (uint32) (data & RMASK); + if (wc == (OP_JRST << 9)) { + printf("Start addr=%06o\n", pa); + PC = pa; + return SCPE_OK; + } + while (wc != 0) { + pa++; + pa &= RMASK; + wc++; + wc &= RMASK; + if (get_word(fileref, &data)) + return SCPE_FMT; + M[pa] = data; + } /* end if count*/ + } + return SCPE_OK; +} + +/* EXE file loader + + EXE format is a disk file format (36b words). It consists of + blocks containing: + + block type,,total words = n + n - 1 data words + + Block types are + + EXE_DIR (1776) directory + EXE_VEC (1775) entry vector + EXE_PDV (1774) optional blocks + EXE_END (1777) end block + + The directory blocks are the most important and contain doubleword + page loading information: + + word0<0:8> = flags + <9:35> = page in file (0 if 0 page) + word1<0:8> = repeat count - 1 + <9:35> = page in memory +*/ +#define PAG_SIZE 01000 +#define PAG_V_PN 9 +#define DIRSIZ (2 * PAG_SIZE) + +t_stat load_exe (FILE *fileref) +{ +uint64 data, dirbuf[DIRSIZ], pagbuf[PAG_SIZE], entbuf[2]; +int32 ndir, entvec, i, j, k, cont, bsz, bty, rpt, wc; +int32 fpage, mpage; +uint32 ma; + +ndir = entvec = 0; /* no dir, entvec */ +cont = 1; +do { + wc = sim_fread (&data, sizeof (uint64), 1, fileref);/* read blk hdr */ + if (wc == 0) /* error? */ + return SCPE_FMT; + bsz = (int32) ((data & RMASK) - 1); /* get count */ + if (bsz < 0) /* zero? */ + return SCPE_FMT; + bty = (int32) LRZ (data); /* get type */ + switch (bty) { /* case type */ + + case EXE_DIR: /* directory */ + if (ndir != 0) /* got one */ + return SCPE_FMT; + ndir = sim_fread (dirbuf, sizeof (uint64), bsz, fileref); + if (ndir < bsz) /* error */ + return SCPE_FMT; + break; + + case EXE_PDV: /* optional */ + (void)sim_fseek (fileref, bsz * sizeof (uint64), SEEK_CUR); + break; + + case EXE_VEC: /* entry vec */ + if (bsz != 2) /* must be 2 wds */ + return SCPE_FMT; + entvec = sim_fread (entbuf, sizeof (uint64), bsz, fileref); + if (entvec < 2) /* error? */ + return SCPE_FMT; + cont = 0; /* stop */ + break; + + case EXE_END: /* end */ + if (bsz != 0) /* must be hdr */ + return SCPE_FMT; + cont = 0; /* stop */ + break; + + default: + return SCPE_FMT; + } /* end switch */ + } while (cont); /* end do */ + +for (i = 0; i < ndir; i = i + 2) { /* loop thru dir */ + fpage = (int32) (dirbuf[i] & RMASK); /* file page */ + mpage = (int32) (dirbuf[i + 1] & RMASK); /* memory page */ + rpt = ((int32) ((dirbuf[i + 1] >> 27) + 1)) & 0777; /* repeat count */ + for (j = 0; j < rpt; j++, mpage++) { /* loop thru rpts */ + if (fpage) { /* file pages? */ + (void)sim_fseek (fileref, (fpage << PAG_V_PN) * sizeof (uint64), SEEK_SET); + wc = sim_fread (pagbuf, sizeof (uint64), PAG_SIZE, fileref); + if (wc < PAG_SIZE) + return SCPE_FMT; + fpage++; + } + ma = mpage << PAG_V_PN; /* mem addr */ + for (k = 0; k < PAG_SIZE; k++, ma++) { /* copy buf to mem */ + if (ma > MEMSIZE) + return SCPE_NXM; + M[ma] = fpage? (pagbuf[k] & FMASK): 0; + } /* end copy */ + } /* end rpt */ + } /* end directory */ +if (entvec && entbuf[1]) + PC = (int32) (entbuf[1] & RMASK); /* start addr */ +return SCPE_OK; +} + +/* Master loader */ + +t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ +uint64 data; +int32 wc, fmt; +extern int32 sim_switches; + +fmt = 0; /* no fmt */ +if (sim_switches & SWMASK ('R')) /* -r? */ + fmt = FMT_R; +else if (sim_switches & SWMASK ('S')) /* -s? */ + fmt = FMT_S; +else if (sim_switches & SWMASK ('E')) /* -e? */ + fmt = FMT_E; +else if (sim_switches & SWMASK ('D')) /* -d? */ + fmt = FMT_D; +else if (sim_switches & SWMASK ('I')) /* -i? */ + fmt = FMT_I; +else if (match_ext (fnam, "RIM")) /* .RIM? */ + fmt = FMT_R; +else if (match_ext (fnam, "SAV")) /* .SAV? */ + fmt = FMT_S; +else if (match_ext (fnam, "EXE")) /* .EXE? */ + fmt = FMT_E; +else if (match_ext (fnam, "DMP")) /* .DMP? */ + fmt = FMT_D; +else if (match_ext (fnam, "BIN")) /* .BIN? */ + fmt = FMT_I; +else { + wc = sim_fread (&data, sizeof (uint64), 1, fileref);/* read hdr */ + if (wc == 0) /* error? */ + return SCPE_FMT; + if (LRZ (data) == EXE_DIR) /* EXE magic? */ + fmt = FMT_E; + else if (TSTS (data)) /* SAV magic? */ + fmt = FMT_S; + fseek (fileref, 0, SEEK_SET); /* rewind */ + } + +switch (fmt) { /* case fmt */ + + case FMT_R: /* RIM */ + return load_rim (fileref); + + case FMT_S: /* SAV */ + return load_sav (fileref); + + case FMT_E: /* EXE */ + return load_exe (fileref); + + case FMT_D: /* DMP */ + return load_dmp (fileref); + + case FMT_I: /* SBLK */ + return load_sblk (fileref); + } + +printf ("Can't determine load file format\n"); +return SCPE_FMT; +} + +/* Symbol tables */ + +#define I_V_FL 39 /* inst class */ +#define I_M_FL 03 /* class mask */ +#define I_AC 000000000000000 /* AC, address */ +#define I_OP 010000000000000 /* address only */ +#define I_IO 020000000000000 /* classic I/O */ +#define I_V_AC 00 +#define I_V_OP 01 +#define I_V_IO 02 + +static const uint64 masks[] = { + 0777000000000, 0777740000000, + 0700340000000, 0777777777777 + }; + +static const char *opcode[] = { +"PORTAL", "JRSTF", "HALT", /* AC defines op */ +"XJRSTF", "XJEN", "XPCW", +"JEN", "SFM", "XJRST", "IBP", +"JFOV", "JCRY1", "JCRY0", "JCRY", "JOV", + + +"LUUO00", "LUUO01", "LUUO02", "LUUO03", "LUUO04", "LUUO05", "LUUO06", "LUUO07", +"LUUO10", "LUUO11", "LUUO12", "LUUO13", "LUUO14", "LUUO15", "LUUO16", "LUUO17", +"LUUO20", "LUUO21", "LUUO22", "LUUO23", "LUUO24", "LUUO25", "LUUO26", "LUUO27", +"LUUO30", "LUUO31", "LUUO32", "LUUO33", "LUUO34", "LUUO35", "LUUO36", "LUUO37", +"MUUO40", "MUUO41", "MUUO42", "MUUO43", "MUUO44", "MUUO45", "MUUO46", "MUUO47", +"MUUO50", "MUUO51", "MUUO52", "MUUO53", "MUUO54", "MUUO55", "MUUO56", "MUUO57", +"MUUO60", "MUUO61", "MUUO62", "MUUO63", "MUUO64", "MUUO65", "MUUO66", "MUUO67", +"MUUO70", "MUUO71", "MUUO72", "MUUO73", "MUUO74", "MUUO75", "MUUO76", "MUUO77", + +"UJEN", "MUUO101", "MUUO102", "JSYS", "MUUO104", "MUUO105", "MUUO106", +"DFAD", "DFSB", "DFMP", "DFDV", "DADD", "DSUB", "DMUL", "DDIV", +"DMOVE", "DMOVN", "FIX", "EXTEND", "DMOVEM", "DMOVNM", "FIXR", "FLTR", +"UFA", "DFN", "FSC", "ADJBP", "ILDB", "LDB", "IDPB", "DPB", +"FAD", "FADL", "FADM", "FADB", "FADR", "FADRL", "FADRM", "FADRB", +"FSB", "FSBL", "FSBM", "FSBB", "FSBR", "FSBRL", "FSBRM", "FSBRB", +"FMP", "FMPL", "FMPM", "FMPB", "FMPR", "FMPRL", "FMPRM", "FMPRB", +"FDV", "FDVL", "FDVM", "FDVB", "FDVR", "FDVRL", "FDVRM", "FDVRB", + +"MOVE", "MOVEI", "MOVEM", "MOVES", "MOVS", "MOVSI", "MOVSM", "MOVSS", +"MOVN", "MOVNI", "MOVNM", "MOVNS", "MOVM", "MOVMI", "MOVMM", "MOVMS", +"IMUL", "IMULI", "IMULM", "IMULB", "MUL", "MULI", "MULM", "MULB", +"IDIV", "IDIVI", "IDIVM", "IDIVB", "DIV", "DIVI", "DIVM", "DIVB", +"ASH", "ROT", "LSH", "JFFO", "ASHC", "ROTC", "LSHC", +"EXCH", "BLT", "AOBJP", "AOBJN", "JRST", "JFCL", "XCT", "MAP", +"PUSHJ", "PUSH", "POP", "POPJ", "JSR", "JSP", "JSA", "JRA", +"ADD", "ADDI", "ADDM", "ADDB", "SUB", "SUBI", "SUBM", "SUBB", + +"CAI", "CAIL", "CAIE", "CAILE", "CAIA", "CAIGE", "CAIN", "CAIG", +"CAM", "CAML", "CAME", "CAMLE", "CAMA", "CAMGE", "CAMN", "CAMG", +"JUMP", "JUMPL", "JUMPE", "JUMPLE", "JUMPA", "JUMPGE", "JUMPN", "JUMPG", +"SKIP", "SKIPL", "SKIPE", "SKIPLE", "SKIPA", "SKIPGE", "SKIPN", "SKIPG", +"AOJ", "AOJL", "AOJE", "AOJLE", "AOJA", "AOJGE", "AOJN", "AOJG", +"AOS", "AOSL", "AOSE", "AOSLE", "AOSA", "AOSGE", "AOSN", "AOSG", +"SOJ", "SOJL", "SOJE", "SOJLE", "SOJA", "SOJGE", "SOJN", "SOJG", +"SOS", "SOSL", "SOSE", "SOSLE", "SOSA", "SOSGE", "SOSN", "SOSG", + +"SETZ", "SETZI", "SETZM", "SETZB", "AND", "ANDI", "ANDM", "ANDB", +"ANDCA", "ANDCAI", "ANDCAM", "ANDCAB", "SETM", "SETMI", "SETMM", "SETMB", +"ANDCM", "ANDCMI", "ANDCMM", "ANDCMB", "SETA", "SETAI", "SETAM", "SETAB", +"XOR", "XORI", "XORM", "XORB", "IOR", "IORI", "IORM", "IORB", +"ANDCB", "ANDCBI", "ANDCBM", "ANDCBB", "EQV", "EQVI", "EQVM", "EQVB", +"SETCA", "SETCAI", "SETCAM", "SETCAB", "ORCA", "ORCAI", "ORCAM", "ORCAB", +"SETCM", "SETCMI", "SETCMM", "SETCMB", "ORCM", "ORCMI", "ORCMM", "ORCMB", +"ORCB", "ORCBI", "ORCBM", "ORCBB", "SETO", "SETOI", "SETOM", "SETOB", + +"HLL", "HLLI", "HLLM", "HLLS", "HRL", "HRLI", "HRLM", "HRLS", +"HLLZ", "HLLZI", "HLLZM", "HLLZS", "HRLZ", "HRLZI", "HRLZM", "HRLZS", +"HLLO", "HLLOI", "HLLOM", "HLLOS", "HRLO", "HRLOI", "HRLOM", "HRLOS", +"HLLE", "HLLEI", "HLLEM", "HLLES", "HRLE", "HRLEI", "HRLEM", "HRLES", +"HRR", "HRRI", "HRRM", "HRRS", "HLR", "HLRI", "HLRM", "HLRS", +"HRRZ", "HRRZI", "HRRZM", "HRRZS", "HLRZ", "HLRZI", "HLRZM", "HLRZS", +"HRRO", "HRROI", "HRROM", "HRROS", "HLRO", "HLROI", "HLROM", "HLROS", +"HRRE", "HRREI", "HRREM", "HRRES", "HLRE", "HLREI", "HLREM", "HLRES", + +"TRN", "TLN", "TRNE", "TLNE", "TRNA", "TLNA", "TRNN", "TLNN", +"TDN", "TSN", "TDNE", "TSNE", "TDNA", "TSNA", "TDNN", "TSNN", +"TRZ", "TLZ", "TRZE", "TLZE", "TRZA", "TLZA", "TRZN", "TLZN", +"TDZ", "TSZ", "TDZE", "TSZE", "TDZA", "TSZA", "TDZN", "TSZN", +"TRC", "TLC", "TRCE", "TLCE", "TRCA", "TLCA", "TRCN", "TLCN", +"TDC", "TSC", "TDCE", "TSCE", "TDCA", "TSCA", "TDCN", "TSCN", +"TRO", "TLO", "TROE", "TLOE", "TROA", "TLOA", "TRON", "TLON", +"TDO", "TSO", "TDOE", "TSOE", "TDOA", "TSOA", "TDON", "TSON", + + +"BLKI", "DATAI", "BLKO", "DATAO", /* classic I/O */ +"CONO", "CONI", "CONSZ", "CONSO", + +NULL +}; + +static const t_int64 opc_val[] = { + 0254040000000+I_OP, 0254100000000+I_OP, + 0254200000000+I_OP, 0254240000000+I_OP, 0254300000000+I_OP, 0254340000000+I_OP, + 0254500000000+I_OP, 0254600000000+I_OP, 0254640000000+I_OP, 0133000000000+I_OP, + 0255040000000+I_OP, 0255100000000+I_OP, 0255200000000+I_OP, 0255300000000+I_OP, + 0255400000000+I_OP, + + + 0000000000000+I_AC, 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, + 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, + 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, + 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, + 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, + 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, + 0030000000000+I_AC, 0031000000000+I_AC, 0032000000000+I_AC, 0033000000000+I_AC, + 0034000000000+I_AC, 0035000000000+I_AC, 0036000000000+I_AC, 0037000000000+I_AC, + 0040000000000+I_AC, 0041000000000+I_AC, 0042000000000+I_AC, 0043000000000+I_AC, + 0044000000000+I_AC, 0045000000000+I_AC, 0046000000000+I_AC, 0047000000000+I_AC, + 0050000000000+I_AC, 0051000000000+I_AC, 0052000000000+I_AC, 0053000000000+I_AC, + 0054000000000+I_AC, 0055000000000+I_AC, 0056000000000+I_AC, 0057000000000+I_AC, + 0060000000000+I_AC, 0061000000000+I_AC, 0062000000000+I_AC, 0063000000000+I_AC, + 0064000000000+I_AC, 0065000000000+I_AC, 0066000000000+I_AC, 0067000000000+I_AC, + 0070000000000+I_AC, 0071000000000+I_AC, 0072000000000+I_AC, 0073000000000+I_AC, + 0074000000000+I_AC, 0075000000000+I_AC, 0076000000000+I_AC, 0077000000000+I_AC, + + 0100000000000+I_AC, 0102000000000+I_AC, 0103000000000+I_AC, + 0104000000000+I_AC, 0105000000000+I_AC, 0106000000000+I_AC, 0107000000000+I_AC, + 0110000000000+I_AC, 0111000000000+I_AC, 0112000000000+I_AC, 0113000000000+I_AC, + 0114000000000+I_AC, 0115000000000+I_AC, 0116000000000+I_AC, 0117000000000+I_AC, + 0120000000000+I_AC, 0121000000000+I_AC, 0122000000000+I_AC, 0123000000000+I_AC, + 0124000000000+I_AC, 0125000000000+I_AC, 0126000000000+I_AC, 0127000000000+I_AC, + 0130000000000+I_AC, 0131000000000+I_AC, 0132000000000+I_AC, 0133000000000+I_AC, + 0134000000000+I_AC, 0135000000000+I_AC, 0136000000000+I_AC, 0137000000000+I_AC, + 0140000000000+I_AC, 0141000000000+I_AC, 0142000000000+I_AC, 0143000000000+I_AC, + 0144000000000+I_AC, 0145000000000+I_AC, 0146000000000+I_AC, 0147000000000+I_AC, + 0150000000000+I_AC, 0151000000000+I_AC, 0152000000000+I_AC, 0153000000000+I_AC, + 0154000000000+I_AC, 0155000000000+I_AC, 0156000000000+I_AC, 0157000000000+I_AC, + 0160000000000+I_AC, 0161000000000+I_AC, 0162000000000+I_AC, 0163000000000+I_AC, + 0164000000000+I_AC, 0165000000000+I_AC, 0166000000000+I_AC, 0167000000000+I_AC, + 0170000000000+I_AC, 0171000000000+I_AC, 0172000000000+I_AC, 0173000000000+I_AC, + 0174000000000+I_AC, 0175000000000+I_AC, 0176000000000+I_AC, 0177000000000+I_AC, + + 0200000000000+I_AC, 0201000000000+I_AC, 0202000000000+I_AC, 0203000000000+I_AC, + 0204000000000+I_AC, 0205000000000+I_AC, 0206000000000+I_AC, 0207000000000+I_AC, + 0210000000000+I_AC, 0211000000000+I_AC, 0212000000000+I_AC, 0213000000000+I_AC, + 0214000000000+I_AC, 0215000000000+I_AC, 0216000000000+I_AC, 0217000000000+I_AC, + 0220000000000+I_AC, 0221000000000+I_AC, 0222000000000+I_AC, 0223000000000+I_AC, + 0224000000000+I_AC, 0225000000000+I_AC, 0226000000000+I_AC, 0227000000000+I_AC, + 0230000000000+I_AC, 0231000000000+I_AC, 0232000000000+I_AC, 0233000000000+I_AC, + 0234000000000+I_AC, 0235000000000+I_AC, 0236000000000+I_AC, 0237000000000+I_AC, + 0240000000000+I_AC, 0241000000000+I_AC, 0242000000000+I_AC, 0243000000000+I_AC, + 0244000000000+I_AC, 0245000000000+I_AC, 0246000000000+I_AC, + 0250000000000+I_AC, 0251000000000+I_AC, 0252000000000+I_AC, 0253000000000+I_AC, + 0254000000000+I_AC, 0255000000000+I_AC, 0256000000000+I_AC, 0257000000000+I_AC, + 0260000000000+I_AC, 0261000000000+I_AC, 0262000000000+I_AC, 0263000000000+I_AC, + 0264000000000+I_AC, 0265000000000+I_AC, 0266000000000+I_AC, 0267000000000+I_AC, + 0270000000000+I_AC, 0271000000000+I_AC, 0272000000000+I_AC, 0273000000000+I_AC, + 0274000000000+I_AC, 0275000000000+I_AC, 0276000000000+I_AC, 0277000000000+I_AC, + + 0300000000000+I_AC, 0301000000000+I_AC, 0302000000000+I_AC, 0303000000000+I_AC, + 0304000000000+I_AC, 0305000000000+I_AC, 0306000000000+I_AC, 0307000000000+I_AC, + 0310000000000+I_AC, 0311000000000+I_AC, 0312000000000+I_AC, 0313000000000+I_AC, + 0314000000000+I_AC, 0315000000000+I_AC, 0316000000000+I_AC, 0317000000000+I_AC, + 0320000000000+I_AC, 0321000000000+I_AC, 0322000000000+I_AC, 0323000000000+I_AC, + 0324000000000+I_AC, 0325000000000+I_AC, 0326000000000+I_AC, 0327000000000+I_AC, + 0330000000000+I_AC, 0331000000000+I_AC, 0332000000000+I_AC, 0333000000000+I_AC, + 0334000000000+I_AC, 0335000000000+I_AC, 0336000000000+I_AC, 0337000000000+I_AC, + 0340000000000+I_AC, 0341000000000+I_AC, 0342000000000+I_AC, 0343000000000+I_AC, + 0344000000000+I_AC, 0345000000000+I_AC, 0346000000000+I_AC, 0347000000000+I_AC, + 0350000000000+I_AC, 0351000000000+I_AC, 0352000000000+I_AC, 0353000000000+I_AC, + 0354000000000+I_AC, 0355000000000+I_AC, 0356000000000+I_AC, 0357000000000+I_AC, + 0360000000000+I_AC, 0361000000000+I_AC, 0362000000000+I_AC, 0363000000000+I_AC, + 0364000000000+I_AC, 0365000000000+I_AC, 0366000000000+I_AC, 0367000000000+I_AC, + 0370000000000+I_AC, 0371000000000+I_AC, 0372000000000+I_AC, 0373000000000+I_AC, + 0374000000000+I_AC, 0375000000000+I_AC, 0376000000000+I_AC, 0377000000000+I_AC, + + 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, + 0404000000000+I_AC, 0405000000000+I_AC, 0406000000000+I_AC, 0407000000000+I_AC, + 0410000000000+I_AC, 0411000000000+I_AC, 0412000000000+I_AC, 0413000000000+I_AC, + 0414000000000+I_AC, 0415000000000+I_AC, 0416000000000+I_AC, 0417000000000+I_AC, + 0420000000000+I_AC, 0421000000000+I_AC, 0422000000000+I_AC, 0423000000000+I_AC, + 0424000000000+I_AC, 0425000000000+I_AC, 0426000000000+I_AC, 0427000000000+I_AC, + 0430000000000+I_AC, 0431000000000+I_AC, 0432000000000+I_AC, 0433000000000+I_AC, + 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, + 0440000000000+I_AC, 0441000000000+I_AC, 0442000000000+I_AC, 0443000000000+I_AC, + 0444000000000+I_AC, 0445000000000+I_AC, 0446000000000+I_AC, 0447000000000+I_AC, + 0450000000000+I_AC, 0451000000000+I_AC, 0452000000000+I_AC, 0453000000000+I_AC, + 0454000000000+I_AC, 0455000000000+I_AC, 0456000000000+I_AC, 0457000000000+I_AC, + 0460000000000+I_AC, 0461000000000+I_AC, 0462000000000+I_AC, 0463000000000+I_AC, + 0464000000000+I_AC, 0465000000000+I_AC, 0466000000000+I_AC, 0467000000000+I_AC, + 0470000000000+I_AC, 0471000000000+I_AC, 0472000000000+I_AC, 0473000000000+I_AC, + 0474000000000+I_AC, 0475000000000+I_AC, 0476000000000+I_AC, 0477000000000+I_AC, + + 0500000000000+I_AC, 0501000000000+I_AC, 0502000000000+I_AC, 0503000000000+I_AC, + 0504000000000+I_AC, 0505000000000+I_AC, 0506000000000+I_AC, 0507000000000+I_AC, + 0510000000000+I_AC, 0511000000000+I_AC, 0512000000000+I_AC, 0513000000000+I_AC, + 0514000000000+I_AC, 0515000000000+I_AC, 0516000000000+I_AC, 0517000000000+I_AC, + 0520000000000+I_AC, 0521000000000+I_AC, 0522000000000+I_AC, 0523000000000+I_AC, + 0524000000000+I_AC, 0525000000000+I_AC, 0526000000000+I_AC, 0527000000000+I_AC, + 0530000000000+I_AC, 0531000000000+I_AC, 0532000000000+I_AC, 0533000000000+I_AC, + 0534000000000+I_AC, 0535000000000+I_AC, 0536000000000+I_AC, 0537000000000+I_AC, + 0540000000000+I_AC, 0541000000000+I_AC, 0542000000000+I_AC, 0543000000000+I_AC, + 0544000000000+I_AC, 0545000000000+I_AC, 0546000000000+I_AC, 0547000000000+I_AC, + 0550000000000+I_AC, 0551000000000+I_AC, 0552000000000+I_AC, 0553000000000+I_AC, + 0554000000000+I_AC, 0555000000000+I_AC, 0556000000000+I_AC, 0557000000000+I_AC, + 0560000000000+I_AC, 0561000000000+I_AC, 0562000000000+I_AC, 0563000000000+I_AC, + 0564000000000+I_AC, 0565000000000+I_AC, 0566000000000+I_AC, 0567000000000+I_AC, + 0570000000000+I_AC, 0571000000000+I_AC, 0572000000000+I_AC, 0573000000000+I_AC, + 0574000000000+I_AC, 0575000000000+I_AC, 0576000000000+I_AC, 0577000000000+I_AC, + + 0600000000000+I_AC, 0601000000000+I_AC, 0602000000000+I_AC, 0603000000000+I_AC, + 0604000000000+I_AC, 0605000000000+I_AC, 0606000000000+I_AC, 0607000000000+I_AC, + 0610000000000+I_AC, 0611000000000+I_AC, 0612000000000+I_AC, 0613000000000+I_AC, + 0614000000000+I_AC, 0615000000000+I_AC, 0616000000000+I_AC, 0617000000000+I_AC, + 0620000000000+I_AC, 0621000000000+I_AC, 0622000000000+I_AC, 0623000000000+I_AC, + 0624000000000+I_AC, 0625000000000+I_AC, 0626000000000+I_AC, 0627000000000+I_AC, + 0630000000000+I_AC, 0631000000000+I_AC, 0632000000000+I_AC, 0633000000000+I_AC, + 0634000000000+I_AC, 0635000000000+I_AC, 0636000000000+I_AC, 0637000000000+I_AC, + 0640000000000+I_AC, 0641000000000+I_AC, 0642000000000+I_AC, 0643000000000+I_AC, + 0644000000000+I_AC, 0645000000000+I_AC, 0646000000000+I_AC, 0647000000000+I_AC, + 0650000000000+I_AC, 0651000000000+I_AC, 0652000000000+I_AC, 0653000000000+I_AC, + 0654000000000+I_AC, 0655000000000+I_AC, 0656000000000+I_AC, 0657000000000+I_AC, + 0660000000000+I_AC, 0661000000000+I_AC, 0662000000000+I_AC, 0663000000000+I_AC, + 0664000000000+I_AC, 0665000000000+I_AC, 0666000000000+I_AC, 0667000000000+I_AC, + 0670000000000+I_AC, 0671000000000+I_AC, 0672000000000+I_AC, 0673000000000+I_AC, + 0674000000000+I_AC, 0675000000000+I_AC, 0676000000000+I_AC, 0677000000000+I_AC, + + 0700000000000+I_IO, 0700040000000+I_IO, 0700100000000+I_IO, 0700140000000+I_IO, + 0700200000000+I_IO, 0700240000000+I_IO, 0700300000000+I_IO, 0700340000000+I_IO, + + -1 + }; + +#define NUMDEV 6 + +static const char *devnam[NUMDEV] = { + "APR", "PI", "PAG", "CCA", "TIM", "MTR" + }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) ((x) + 040) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, j, c, cflag, ac, xr, y, dev; +uint64 inst; + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit[0]); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) + return SCPE_ARG; + fprintf (of, FMTASC ((int32) (inst & 0177))); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + for (i = 30; i >= 0; i = i - 6) { + c = (int32) ((inst >> i) & 077); + fprintf (of, "%c", SIXTOASC (c)); + } + return SCPE_OK; + } +if (sw & SWMASK ('P')) { /* packed? */ + for (i = 29; i >= 0; i = i - 7) { + c = (int32) ((inst >> i) & 0177); + fprintf (of, FMTASC (c)); + } + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) + return SCPE_ARG; + +/* Instruction decode */ + +ac = GET_AC (inst); +xr = GET_XR (inst); +y = GET_ADDR (inst); +dev = GET_DEV (inst); +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ + if (((opc_val[i] & FMASK) == (inst & masks[j]))) { /* match? */ + fprintf (of, "%s ", opcode[i]); /* opcode */ + switch (j) { /* case on class */ + + case I_V_AC: /* AC + address */ + fprintf (of, "%-o,", ac); /* print AC, fall thru */ + case I_V_OP: /* address only */ + if (inst & INST_IND) + fprintf (of, "@"); + if (xr) + fprintf (of, "%-o(%-o)", y, xr); + else fprintf (of, "%-o", y); + break; + + case I_V_IO: /* I/O */ + if (dev < NUMDEV) + fprintf (of, "%s,", devnam[dev]); + else fprintf (of, "%-o,", dev<<2); + if (inst & INST_IND) + fprintf (of, "@"); + if (xr) + fprintf (of, "%-o(%-o)", y, xr); + else fprintf (of, "%-o", y); + break; + } /* end case */ + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get operand, including indirect and index + + Inputs: + *cptr = pointer to input string + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_opnd (const char *cptr, t_stat *status) +{ +int32 sign = 0; +t_value val, xr = 0, ind = 0; +const char *tptr; + +*status = SCPE_ARG; /* assume fail */ +if (*cptr == '@') { + ind = INST_IND; + cptr++; + } +if (*cptr == '+') + cptr++; +else if (*cptr == '-') { + sign = 1; + cptr++; + } +val = strtotv (cptr, &tptr, 8); +if (val > 0777777) + return 0; +if (sign) + val = (~val + 1) & 0777777; +cptr = tptr; +if (*cptr == '(') { + cptr++; + xr = strtotv (cptr, &tptr, 8); + if ((cptr == tptr) || (*tptr != ')') || + (xr > 017) || (xr == 0)) + return 0; + cptr = ++tptr; + } +if (*cptr == 0) + *status = SCPE_OK; +return (ind | (xr << 18) | val); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, i, j; +t_value ac, dev; +t_stat r; +char gbuf[CBUFSIZE], cbuf[2*CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit[0]); +while (isspace (*cptr)) cptr++; +memset (cbuf, '\0', sizeof(cbuf)); +strncpy (cbuf, cptr, sizeof(cbuf)-7); +cptr = cbuf; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + val[0] = (t_value) cptr[0]; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + for (i = 0; i < 6; i++) { + val[0] = (val[0] << 6); + if (cptr[i]) val[0] = val[0] | + ((t_value) ((cptr[i] + 040) & 077)); + } + return SCPE_OK; + } +if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + for (i = 0; i < 5; i++) + val[0] = (val[0] << 7) | ((t_value) cptr[i]); + val[0] = val[0] << 1; + return SCPE_OK; + } + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) + return SCPE_ARG; +val[0] = opc_val[i] & FMASK; /* get value */ +j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ +switch (j) { /* case on class */ + + case I_V_AC: /* AC + operand */ + if (strchr (cptr, ',')) { /* AC specified? */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if (gbuf[0]) { /* can be omitted */ + ac = get_uint (gbuf, 8, 017 - 1, &r); + if (r != SCPE_OK) + return SCPE_ARG; + val[0] = val[0] | (ac << INST_V_AC); + } + } /* fall through */ + case I_V_OP: /* operand */ + cptr = get_glyph (cptr, gbuf, 0); + val[0] = val[0] | get_opnd (gbuf, &r); + if (r != SCPE_OK) + return SCPE_ARG; + break; + + case I_V_IO: /* I/O */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + for (dev = 0; (dev < NUMDEV) && (strcmp (devnam[dev], gbuf) != 0); dev++); + if (dev >= NUMDEV) { + dev = get_uint (gbuf, 8, INST_M_DEV, &r); + if (r != SCPE_OK) + return SCPE_ARG; + } + val[0] = val[0] | (dev << INST_V_DEV); + cptr = get_glyph (cptr, gbuf, 0); + val[0] = val[0] | get_opnd (gbuf, &r); + if (r != SCPE_OK) + return SCPE_ARG; + break; + } /* end case */ + +if (*cptr != 0) /* junk at end? */ + return SCPE_ARG; +return SCPE_OK; +} diff --git a/PDP10/kx10_tu.c b/PDP10/kx10_tu.c new file mode 100644 index 00000000..ae7d1de8 --- /dev/null +++ b/PDP10/kx10_tu.c @@ -0,0 +1,1161 @@ +/* ka10_tu.c: Dec RH10 TM03/TU10 tape controller + + 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 PUTUOSE 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" +#include "sim_tape.h" + +#ifndef NUM_DEVS_TU +#define NUM_DEVS_TU 0 +#endif + +#if (NUM_DEVS_TU > 0) + +#define NUM_UNITS_TU 8 +#define TU_NUMFR (64*1024) + +#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF) +#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF + +/* Flags in the unit flags word */ + +#define TU_UNIT UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE +#define CNTRL_V_CTYPE (MTUF_V_UF) +#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) + +/* 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 */ +/* TUC - 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_UNLOAD 001 /* unload */ +#define FNC_REWIND 003 /* rewind */ +#define FNC_DCLR 004 /* drive clear */ +#define FNC_PRESET 010 /* read-in preset */ +#define FNC_ERASE 012 /* Erase */ +#define FNC_WTM 013 /* Write Tape Mark */ +#define FNC_SPACEF 014 /* Space record forward */ +#define FNC_SPACEB 015 /* Space record backward */ +#define FNC_XFER 024 /* >=? data xfr */ +#define FNC_WCHK 024 /* write check */ +#define FNC_WCHKREV 027 /* write check reverse */ +#define FNC_WRITE 030 /* write */ +#define FNC_READ 034 /* read */ +#define FNC_READREV 037 /* read reverse */ +#define CS1_DVA 0004000 /* drive avail NI */ +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) +#define CS_TM 001000 /* Tape mark sensed */ +#define CS_MOTION 002000 /* Tape moving */ +#define CS_PIP 004000 /* Tape Position command */ +#define CS_ATA 010000 /* Tape signals attention */ +#define CS_CHANGE 020000 /* Status changed */ + +#define STATUS u5 +/* u5 low */ +/* TUDS - 01 - drive status */ + +#define DS_SLA 0000001 /* Drive has become ready */ +#define DS_BOT 0000002 /* Beginning of tape */ +#define DS_TM 0000004 /* Tape mark */ +#define DS_IDB 0000010 /* Identification burst */ +#define DS_SDWN 0000020 /* Tape stoped */ +#define DS_PES 0000040 /* Phase Encoding */ +#define DS_SSC 0000100 /* Status change */ +#define DS_DRY 0000200 /* drive ready */ +#define DS_DPR 0000400 /* drive present */ +#define DS_PGM 0001000 /* programable NI */ +#define DS_EOT 0002000 /* end of tape */ +#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 */ + +/* u5 high */ +/* TUER1 - 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_CPAR 0000010 /* control parity err NI */ +#define ER1_FMT 0000020 /* format err */ +#define ER1_DPAR 0000040 /* data parity error */ +#define ER1_INC 0000100 /* Incorrectable data */ +#define ER1_PEF 0000200 /* format error */ +#define ER1_NSG 0000400 /* Nonstandard gap NI */ +#define ER1_FCE 0001000 /* Frame count error */ +#define ER1_ITM 0002000 /* Illegal tape mark */ +#define ER1_NEF 0004000 /* Non executable function */ +#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 */ + +/* TUMR - 03 - maintenace register */ + +/* TUAS - 04 - attention summary */ + +#define AS_U0 0000001 /* unit 0 flag */ + +/* TUDC - 05 - frame count */ + +/* TUDT - 06 - drive type */ + +/* TULA - 07 - look ahead register */ + +/* TUSN - 10 - serial number */ + +/* TUTC - 11 - Tape control register */ +#define TC_SS 0000007 /* Slave select mask */ +#define TC_EVPAR 0000010 /* Even parity */ +#define TC_FMTSEL 0000360 /* Format select */ +#define TC_10CORE 000 /* PDP 10 Core */ + /* 4 8 bit chars + 1 4 bit char */ +#define TC_15CORE 001 /* PDP 15 core */ + /* 3 6 bit chars per word */ +#define TC_10NORM 003 /* PDP 10 Compatible */ + /* 4 8 bit chars per word */ +#define TC_11NORM 014 /* PDP 11 Normal */ + /* 2 8 bit chars per word */ +#define TC_11CORE 015 /* PDP 11 Core */ + /* 4 4 bit chars per word */ +#define TC_15NORM 016 /* PDP 15 Normal */ + /* 2 8 bit chars per word */ +#define TC_DENS 0003400 /* Density (ignored) */ +#define TC_800 0001400 /* 800 BPI */ +#define TC_1600 0002000 /* 1600 BPI */ +#define TC_EAODTE 0010000 /* Enable abort */ +#define TC_SAC 0020000 /* Slave address change */ +#define TC_FCS 0040000 /* Frame count status */ +#define TC_ACCL 0100000 /* Acceleration */ + + +/* TUER3 - 15 - error status 3 - more unsafe conditions - unimplemented */ + +#define CPOS u4 +#define DATAPTR u6 + +struct df10 tu_df10[NUM_DEVS_TU]; +int tu_xfer_drive[NUM_DEVS_TU]; +uint8 tu_buf[NUM_DEVS_TU][TU_NUMFR]; +int tu_reg[NUM_DEVS_TU]; +int tu_ivect[NUM_DEVS_TU]; +int tu_imode[NUM_DEVS_TU]; +int tu_drive[NUM_DEVS_TU]; +int tu_rae[NUM_DEVS_TU]; +int tu_attn[NUM_DEVS_TU]; +uint16 tu_frame[NUM_DEVS_TU]; +uint16 tu_tcr[NUM_DEVS_TU]; +extern int readin_flag; +static uint64 tu_boot_buffer; + +t_stat tu_devio(uint32 dev, uint64 *data); +int tu_devirq(uint32 dev, int addr); +void tu_write(int ctlr, int unit, int reg, uint32 data); +uint32 tu_read(int ctlr, int unit, int reg); +t_stat tu_srv(UNIT *); +t_stat tu_boot(int32, DEVICE *); +void tu_ini(UNIT *, t_bool); +t_stat tu_reset(DEVICE *); +t_stat tu_attach(UNIT *, CONST char *); +t_stat tu_detach(UNIT *); +t_stat tu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *tu_description (DEVICE *dptr); + + +UNIT tu_unit[] = { +/* Controller 1 */ + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL(0), 0) }, +}; + +DIB tu_dib[] = { + {RH10_DEV, 1, &tu_devio, &tu_devirq} +}; + +MTAB tu_mod[] = { + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", 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}, + {0} +}; + +REG tua_reg[] = { + {ORDATA(IVECT, tu_ivect[0], 18)}, + {FLDATA(IMODE, tu_imode[0], 0)}, + {ORDATA(FRAME, tu_frame[0], 16)}, + {ORDATA(TCR, tu_tcr[0], 16)}, + {ORDATA(XFER, tu_xfer_drive[0], 3), REG_HRO}, + {ORDATA(DRIVE, tu_drive[0], 3), REG_HRO}, + {ORDATA(REG, tu_reg[0], 6), REG_RO}, + {ORDATA(RAE, tu_rae[0], 8), REG_RO}, + {ORDATA(ATTN, tu_attn[0], 8), REG_RO}, + {FLDATA(READIN, readin_flag, 0), REG_HRO}, + {ORDATA(STATUS, tu_df10[0].status, 18), REG_RO}, + {ORDATA(CIA, tu_df10[0].cia, 18)}, + {ORDATA(CCW, tu_df10[0].ccw, 18)}, + {ORDATA(WCR, tu_df10[0].wcr, 18)}, + {ORDATA(CDA, tu_df10[0].cda, 18)}, + {ORDATA(DEVNUM, tu_df10[0].devnum, 9), REG_HRO}, + {ORDATA(BUF, tu_df10[0].buf, 36), REG_HRO}, + {ORDATA(NXM, tu_df10[0].nxmerr, 8), REG_HRO}, + {ORDATA(COMP, tu_df10[0].ccw_comp, 8), REG_HRO}, + {BRDATA(BUFF, &tu_buf[0][0], 16, 64, TU_NUMFR), REG_HRO}, + {0} +}; + +DEVICE tua_dev = { + "TUA", tu_unit, NULL, tu_mod, + NUM_UNITS_TU, 8, 18, 1, 8, 36, + NULL, NULL, &tu_reset, &tu_boot, &tu_attach, &tu_detach, + &tu_dib[0], DEV_DISABLE | DEV_DEBUG | DEV_TAPE, 0, dev_debug, + NULL, NULL, &tu_help, NULL, NULL, &tu_description +}; + + +DEVICE *tu_devs[] = { + &tua_dev, +}; + + +t_stat tu_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 = &tu_df10[ctlr]; + df10->devnum = dev; + switch(dev & 3) { + case CONI: + *data = df10->status & ~(IADR_ATTN|IARD_RAE); + if (tu_attn[ctlr] != 0 && (df10->status & IADR_ATTN)) + *data |= IADR_ATTN; + if (tu_rae[ctlr] != 0 && (df10->status & IARD_RAE)) + *data |= IARD_RAE; +#if KI_22BIT + *data |= B22_FLAG; +#endif + sim_debug(DEBUG_CONI, dptr, "TU %03o CONI %06o PC=%o %o\n", + dev, (uint32)*data, PC, tu_attn[ctlr]); + return SCPE_OK; + + case CONO: + clr_interrupt(dev); + df10->status &= ~07LL; + df10->status |= *data & (07LL|IADR_ATTN|IARD_RAE); + /* Clear flags */ + 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 && tu_attn[ctlr] != 0) + set_interrupt(dev, df10->status); + sim_debug(DEBUG_CONO, dptr, "TU %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 && tu_reg[ctlr] != 04) { + df10->status |= CC_CHAN_ACT; + return SCPE_OK; + } + if (tu_reg[ctlr] == 040) { + *data = (uint64)(tu_read(ctlr, tu_drive[ctlr], 0) & 077); + *data |= ((uint64)(df10->cia)) << 6; + *data |= ((uint64)(tu_xfer_drive[ctlr])) << 18; + } else if (tu_reg[ctlr] == 044) { + *data = (uint64)tu_ivect[ctlr]; + if (tu_imode[ctlr]) + *data |= IRQ_KI10; + else + *data |= IRQ_KA10; + } else if (tu_reg[ctlr] == 054) { + *data = (uint64)(tu_rae[ctlr]); + } else if ((tu_reg[ctlr] & 040) == 0) { + *data = (uint64)(tu_read(ctlr, tu_drive[ctlr], tu_reg[ctlr]) & 0177777); + *data |= ((uint64)(tu_drive[ctlr])) << 18; + } + *data |= ((uint64)(tu_reg[ctlr])) << 30; + sim_debug(DEBUG_DATAIO, dptr, "TU %03o DATI %012llo, %d %d PC=%06o\n", + dev, *data, ctlr, tu_drive[ctlr], PC); + return SCPE_OK; + + case DATAO: + sim_debug(DEBUG_DATAIO, dptr, "TU %03o DATO %012llo, %d PC=%06o %06o\n", + dev, *data, ctlr, PC, df10->status); + tu_reg[ctlr] = ((int)(*data >> 30)) & 077; + if (tu_reg[ctlr] < 040 && tu_reg[ctlr] != 04) { + tu_drive[ctlr] = (int)(*data >> 18) & 07; + } + if (*data & LOAD_REG) { + if (tu_reg[ctlr] == 040) { + if ((*data & 1) == 0) { + return SCPE_OK; + } + if (df10->status & BUSY) { + df10->status |= CC_CHAN_ACT; + sim_debug(DEBUG_DATAIO, dptr, + "TU %03o command busy %012llo, %d[%d] PC=%06o %06o\n", + dev, *data, ctlr, tu_drive[ctlr], PC, df10->status); + 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, + "TU %03o command abort %012llo, %d[%d] PC=%06o %06o\n", + dev, *data, ctlr, tu_drive[ctlr], PC, df10->status); + return SCPE_OK; + } + /* Check if access error */ + if (tu_rae[ctlr] & (1 << tu_drive[ctlr])) { + return SCPE_OK; + } + + /* Start command */ + df10_setup(df10, (uint32)(*data >> 6)); + tu_xfer_drive[ctlr] = (int)(*data >> 18) & 07; + tu_write(ctlr, tu_drive[ctlr], 0, (uint32)(*data & 077)); + sim_debug(DEBUG_DATAIO, dptr, + "TU %03o command %012llo, %d[%d] PC=%06o %06o\n", + dev, *data, ctlr, tu_drive[ctlr], PC, df10->status); + } else if (tu_reg[ctlr] == 044) { + /* Set KI10 Irq vector */ + tu_ivect[ctlr] = (int)(*data & IRQ_VECT); + tu_imode[ctlr] = (*data & IRQ_KI10) != 0; + } else if (tu_reg[ctlr] == 050) { + ; /* Diagnostic access to mass bus. */ + } else if (tu_reg[ctlr] == 054) { + /* clear flags */ + tu_rae[ctlr] &= ~(*data & 0377); + if (tu_rae[ctlr] == 0) + clr_interrupt(dev); + } else if ((tu_reg[ctlr] & 040) == 0) { + tu_drive[ctlr] = (int)(*data >> 18) & 07; + /* Check if access error */ + if (tu_rae[ctlr] & (1 << tu_drive[ctlr])) { + return SCPE_OK; + } + tu_drive[ctlr] = (int)(*data >> 18) & 07; + tu_write(ctlr, tu_drive[ctlr], tu_reg[ctlr] & 037, + (int)(*data & 0777777)); + } + } + return SCPE_OK; + } + return SCPE_OK; /* Unreached */ +} + +/* Handle KI and KL style interrupt vectors */ +int +tu_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 (tu_imode[drive] ? tu_ivect[drive] : addr); + } + return addr; +} + +void +tu_write(int ctlr, int unit, int reg, uint32 data) { + UNIT *uptr = &tu_unit[(ctlr * 8) + (tu_tcr[ctlr] & 07)]; + DEVICE *dptr = tu_devs[ctlr]; + struct df10 *df10 = &tu_df10[ctlr]; + int i; + + if (uptr->CMD & CR_GO) { + uptr->STATUS |= (ER1_RMR); + return; + } + + switch(reg) { + case 000: /* control */ + sim_debug(DEBUG_DETAIL, dptr, "TUA%o %d Status=%06o\n", + unit, ctlr, uptr->STATUS); + df10->status &= ~(1 << df10->ccw_comp); + if ((data & 01) != 0 && (uptr->flags & UNIT_ATT) != 0) { + uptr->CMD = data & 076; + switch (GET_FNC(data)) { + case FNC_NOP: + break; + + case FNC_PRESET: /* read-in preset */ + case FNC_READ: /* read */ + case FNC_READREV: /* read w/ headers */ + tu_frame[ctlr] = 0; + /* Fall through */ + + case FNC_ERASE: /* Erase gap */ + case FNC_WRITE: /* write */ + case FNC_WTM: /* Write tape mark */ + case FNC_SPACEF: /* Space forward */ + case FNC_SPACEB: /* Space backward */ + case FNC_WCHK: /* write check */ + case FNC_REWIND: /* rewind */ + case FNC_UNLOAD: /* unload */ + case FNC_WCHKREV: /* write w/ headers */ + uptr->CMD |= CS_PIP|CR_GO; + uptr->CMD &= ~CS_TM; + CLR_BUF(uptr); + uptr->DATAPTR = 0; + df10->status &= ~PI_ENABLE; + sim_activate(uptr, 100); + break; + + case FNC_DCLR: /* drive clear */ + uptr->CMD &= ~(CS_ATA|CR_GO|CS_TM); + uptr->STATUS = 0; + tu_attn[ctlr] = 0; + clr_interrupt(df10->devnum); + for (i = 0; i < 8; i++) { + if (tu_unit[(ctlr * 8) + i].CMD & CS_ATA) + tu_attn[ctlr] = 1; + } + if ((df10->status & IADR_ATTN) != 0 && tu_attn[ctlr] != 0) + df10_setirq(df10); + break; + default: + uptr->STATUS |= (ER1_ILF); + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + if ((df10->status & IADR_ATTN) != 0) + df10_setirq(df10); + } + sim_debug(DEBUG_DETAIL, dptr, "TUA%o AStatus=%06o\n", unit, + uptr->CMD); + } + return; + case 001: /* status */ + break; + case 002: /* error register 1 */ + uptr->STATUS &= ~0177777; + uptr->STATUS |= data; + break; + case 003: /* maintenance */ + break; + case 004: /* atten summary */ + tu_attn[ctlr] = 0; + for (i = 0; i < 8; i++) { + if (data & (1<devnum); + if (((df10->status & IADR_ATTN) != 0 && tu_attn[ctlr] != 0) || + (df10->status & PI_ENABLE)) + df10_setirq(df10); + break; + case 005: /* frame count */ + tu_frame[ctlr] = data & 0177777; + break; + case 006: /* drive type */ + case 007: /* look ahead */ + case 011: /* tape control register */ + tu_tcr[ctlr] = data & 0177777 ; + break; + default: + uptr->STATUS |= ER1_ILR; + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + tu_rae[ctlr] |= (1<status & IADR_ATTN) != 0) + df10_setirq(df10); + } +} + +uint32 +tu_read(int ctlr, int unit, int reg) { + UNIT *uptr = &tu_unit[(ctlr * 8) + (tu_tcr[ctlr] & 07)]; + struct df10 *df10 = &tu_df10[ctlr]; + uint32 temp = 0; + int i; + + switch(reg) { + case 000: /* control */ + temp = uptr->CMD & 076; + if (uptr->flags & UNIT_ATT) + temp |= CS1_DVA; + if (df10->status & BUSY || uptr->CMD & CR_GO) + temp |= CS1_GO; + break; + case 001: /* status */ + temp = DS_DPR; + if (tu_attn[ctlr] != 0) + temp |= DS_ATA; + if (uptr->CMD & CS_CHANGE) + temp |= DS_SSC; + if ((uptr->STATUS & 0177777) != 0) + temp |= DS_ERR|DS_ATA; + if ((uptr->flags & UNIT_ATT) != 0) { + temp |= DS_MOL; + if (uptr->CMD & CS_TM) + temp |= DS_TM; + if (uptr->flags & MTUF_WLK) + temp |= DS_WRL; + if ((uptr->CMD & (CS_MOTION|CS_PIP|CR_GO)) == 0) + temp |= DS_DRY; + if (sim_tape_bot(uptr)) + temp |= DS_BOT; + if (sim_tape_eot(uptr)) + temp |= DS_EOT; + if ((uptr->CMD & CS_MOTION) == 0) + temp |= DS_SDWN; + if (uptr->CMD & CS_PIP) + temp |= DS_PIP; + } + break; + case 002: /* error register 1 */ + temp = uptr->STATUS & 0177777; + break; + case 004: /* atten summary */ + for (i = 0; i < 8; i++) { + if (tu_unit[(ctlr * 8) + i].CMD & CS_ATA) { + temp |= 1 << i; + } + } + break; + case 005: /* frame count */ + temp = tu_frame[ctlr]; + break; + case 006: /* drive type */ + temp = 040054; + break; + case 011: /* tape control register */ + temp = tu_tcr[ctlr]; + break; + case 010: /* serial no */ + temp = 020 + (unit + 1); + break; + case 003: /* maintenance */ + case 007: /* look ahead */ + break; + default: + uptr->STATUS |= (ER1_ILR); + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + tu_rae[ctlr] |= (1<status & IADR_ATTN) != 0) + df10_setirq(df10); + } + return temp; +} + + +/* Map simH errors into machine errors */ +void tu_error(UNIT * uptr, t_stat r) +{ + int ctlr = GET_CNTRL(uptr->flags); + DEVICE *dptr = tu_devs[ctlr]; + + switch (r) { + case MTSE_OK: /* no error */ + break; + + case MTSE_TMK: /* tape mark */ + uptr->CMD |= CS_TM; + break; + + case MTSE_WRP: /* write protected */ + uptr->STATUS |= (ER1_NEF); + uptr->CMD |= CS_ATA; + break; + + case MTSE_UNATT: /* unattached */ + case MTSE_BOT: /* beginning of tape */ + case MTSE_EOM: /* end of medium */ + break; + + case MTSE_IOERR: /* IO error */ + case MTSE_FMT: /* invalid format */ + uptr->STATUS |= (ER1_PEF); + uptr->CMD |= CS_ATA; + break; + + case MTSE_RECE: /* error in record */ + uptr->STATUS |= (ER1_DPAR); + uptr->CMD |= CS_ATA; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + uptr->STATUS |= (ER1_FCE); + uptr->CMD |= CS_ATA; + break; + + } + if (uptr->CMD & CS_ATA) { + tu_attn[ctlr] = 1; + } + uptr->CMD &= ~(CS_MOTION|CS_PIP|CR_GO); + sim_debug(DEBUG_EXP, dptr, "Setting status %d\n", r); +} + +/* Handle processing of tape requests. */ +t_stat tu_srv(UNIT * uptr) +{ + int ctlr = GET_CNTRL(uptr->flags); + int unit; + DEVICE *dptr; + struct df10 *df; + t_stat r; + t_mtrlnt reclen; + uint8 ch; + int cc; + int cc_max; + + /* Find dptr, and df10 */ + dptr = tu_devs[ctlr]; + unit = uptr - dptr->units; + df = &tu_df10[ctlr]; + cc_max = (4 + ((tu_tcr[ctlr] & TC_FMTSEL) == 0)); + if ((uptr->flags & UNIT_ATT) == 0) { + tu_error(uptr, MTSE_UNATT); /* attached? */ + df10_setirq(df); + return SCPE_OK; + } + switch (GET_FNC(uptr->CMD)) { + case FNC_NOP: + case FNC_DCLR: + sim_debug(DEBUG_DETAIL, dptr, "TU%o nop\n", unit); + tu_error(uptr, MTSE_OK); /* Nop */ + df10_setirq(df); + return SCPE_OK; + + case FNC_REWIND: + sim_debug(DEBUG_DETAIL, dptr, "TU%o rewind\n", unit); + if (uptr->CMD & CR_GO) { + sim_activate(uptr,40000); + uptr->CMD |= CS_MOTION; + uptr->CMD &= ~(CR_GO); + } else { + uptr->CMD &= ~(CS_MOTION|CS_PIP); + uptr->CMD |= CS_CHANGE|CS_ATA; + tu_attn[ctlr] = 1; + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + tu_error(uptr, sim_tape_rewind(uptr)); + } + return SCPE_OK; + + case FNC_UNLOAD: + sim_debug(DEBUG_DETAIL, dptr, "TU%o unload\n", unit); + uptr->CMD &= ~(CR_GO); + uptr->CMD |= CS_CHANGE|CS_ATA; + tu_attn[ctlr] = 1; + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + tu_error(uptr, sim_tape_detach(uptr)); + return SCPE_OK; + + case FNC_WCHKREV: + case FNC_READREV: + if (BUF_EMPTY(uptr)) { + uptr->CMD &= ~CS_PIP; + if ((r = sim_tape_rdrecr(uptr, &tu_buf[ctlr][0], &reclen, + TU_NUMFR)) != MTSE_OK) { + sim_debug(DEBUG_DETAIL, dptr, "TU%o read error %d\n", unit, r); + if (r == MTSE_BOT) + uptr->STATUS |= ER1_NEF; + tu_error(uptr, r); + df10_finish_op(df, 0); + } else { + sim_debug(DEBUG_DETAIL, dptr, "TU%o read %d\n", unit, reclen); + uptr->CMD |= CS_MOTION; + uptr->hwmark = reclen; + uptr->DATAPTR = uptr->hwmark-1; + uptr->CPOS = cc_max; + df->buf = 0; + sim_activate(uptr, 100); + } + return SCPE_OK; + } + if (uptr->DATAPTR >= 0) { + tu_frame[ctlr]++; + cc = (8 * (3 - uptr->CPOS)) + 4; + ch = tu_buf[ctlr][uptr->DATAPTR]; + if (cc < 0) + df->buf |= (uint64)(ch & 0x0f); + else + df->buf |= (uint64)(ch & 0xff) << cc; + uptr->DATAPTR--; + uptr->CPOS--; + if (uptr->CPOS == 0) { + uptr->CPOS = cc_max; + if (GET_FNC(uptr->CMD) == FNC_READREV && + df10_write(df) == 0) { + tu_error(uptr, MTSE_OK); + return SCPE_OK; + } + sim_debug(DEBUG_DATA, dptr, "TU%o readrev %012llo\n", + unit, df->buf); + df->buf = 0; + } + } else { + if (uptr->CPOS != cc_max) + df10_write(df); + tu_error(uptr, MTSE_OK); + return SCPE_OK; + } + break; + + case FNC_WCHK: + case FNC_READ: + if (BUF_EMPTY(uptr)) { + uptr->CMD &= ~CS_PIP; + uptr->CMD |= CS_MOTION; + if ((r = sim_tape_rdrecf(uptr, &tu_buf[ctlr][0], &reclen, + TU_NUMFR)) != MTSE_OK) { + sim_debug(DEBUG_DETAIL, dptr, "TU%o read error %d\n", unit, r); + tu_error(uptr, r); + df10_finish_op(df, 0); + } else { + sim_debug(DEBUG_DETAIL, dptr, "TU%o read %d\n", unit, reclen); + uptr->hwmark = reclen; + uptr->DATAPTR = 0; + uptr->CPOS = 0; + df->buf = 0; + sim_activate(uptr, 100); + } + return SCPE_OK; + } + if ((uint32)uptr->DATAPTR < uptr->hwmark) { + tu_frame[ctlr]++; + cc = (8 * (3 - uptr->CPOS)) + 4; + ch = tu_buf[ctlr][uptr->DATAPTR]; + if (cc < 0) + df->buf |= (uint64)(ch & 0x0f); + else + df->buf |= (uint64)(ch & 0xff) << cc; + uptr->DATAPTR++; + uptr->CPOS++; + if (uptr->CPOS == cc_max) { + uptr->CPOS = 0; + if (GET_FNC(uptr->CMD) == FNC_READ && + df10_write(df) == 0) { + tu_error(uptr, MTSE_OK); + return SCPE_OK; + } + sim_debug(DEBUG_DATA, dptr, "TU%o read %012llo\n", + unit, df->buf); + df->buf = 0; + } + } else { + if (uptr->CPOS != 0) { + sim_debug(DEBUG_DATA, dptr, "TU%o read %012llo\n", + unit, df->buf); + df10_write(df); + } + tu_error(uptr, MTSE_OK); + df10_finish_op(df, 0); + return SCPE_OK; + } + break; + + case FNC_WRITE: + if (BUF_EMPTY(uptr)) { + uptr->CMD &= ~CS_PIP; + if (tu_frame[ctlr] == 0) { + uptr->STATUS |= ER1_NEF; + uptr->CMD &= ~(CR_GO); + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + tu_error(uptr, MTSE_OK); + df10_finish_op(df, 0); + return SCPE_OK; + } + if ((uptr->flags & MTUF_WLK) != 0) { + tu_error(uptr, MTSE_WRP); + df10_finish_op(df, 0); + return SCPE_OK; + } + uptr->CMD |= CS_MOTION; + sim_debug(DEBUG_EXP, dptr, "TU%o Init write\n", unit); + uptr->hwmark = 0; + uptr->CPOS = 0; + uptr->DATAPTR = 0; + df->buf = 0; + } + if (tu_frame[ctlr] != 0 && uptr->CPOS == 0 && df10_read(df) == 0) + uptr->CPOS |= 010; + + if ((uptr->CMD & CS_MOTION) != 0) { + if (uptr->CPOS == 0) + sim_debug(DEBUG_DATA, dptr, "TU%o write %012llo\n", + unit, df->buf); + /* Write next char out */ + cc = (8 * (3 - (uptr->CPOS & 07))) + 4; + if (cc < 0) + ch = df->buf & 0x0f; + else + ch = (df->buf >> cc) & 0xff; + tu_buf[ctlr][uptr->DATAPTR] = ch; + uptr->DATAPTR++; + uptr->hwmark = uptr->DATAPTR; + uptr->CPOS = (uptr->CPOS & 010) | ((uptr->CPOS & 07) + 1); + if ((uptr->CPOS & 7) == cc_max) { + uptr->CPOS &= 010; + } + tu_frame[ctlr] = 0177777 & (tu_frame[ctlr] + 1); + if (tu_frame[ctlr] == 0) + uptr->CPOS = 010; + } + if (uptr->CPOS == 010) { + /* Write out the block */ + reclen = uptr->hwmark; + r = sim_tape_wrrecf(uptr, &tu_buf[ctlr][0], reclen); + sim_debug(DEBUG_DETAIL, dptr, "TU%o Write %d %d\n", + unit, reclen, uptr->CPOS); + uptr->DATAPTR = 0; + uptr->hwmark = 0; + df10_finish_op(df,0 ); + tu_error(uptr, r); /* Record errors */ + return SCPE_OK; + } + break; + + case FNC_WTM: + if ((uptr->flags & MTUF_WLK) != 0) { + tu_error(uptr, MTSE_WRP); + } else { + tu_error(uptr, sim_tape_wrtmk(uptr)); + } + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + sim_debug(DEBUG_DETAIL, dptr, "TU%o WTM\n", unit); + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + return SCPE_OK; + + case FNC_ERASE: + if ((uptr->flags & MTUF_WLK) != 0) { + tu_error(uptr, MTSE_WRP); + } else { + tu_error(uptr, sim_tape_wrgap(uptr, 35)); + } + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + sim_debug(DEBUG_DETAIL, dptr, "TU%o ERG\n", unit); + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + return SCPE_OK; + + case FNC_SPACEF: + case FNC_SPACEB: + sim_debug(DEBUG_DETAIL, dptr, "TU%o space %o\n", unit, GET_FNC(uptr->CMD)); + if (tu_frame[ctlr] == 0) { + uptr->STATUS |= ER1_NEF; + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + tu_error(uptr, MTSE_OK); + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + return SCPE_OK; + } + uptr->CMD |= CS_MOTION; + /* Always skip at least one record */ + if (GET_FNC(uptr->CMD) == FNC_SPACEF) + r = sim_tape_sprecf(uptr, &reclen); + else + r = sim_tape_sprecr(uptr, &reclen); + switch (r) { + case MTSE_OK: /* no error */ + break; + + case MTSE_BOT: /* beginning of tape */ + uptr->STATUS |= ER1_NEF; + /* Fall Through */ + + case MTSE_TMK: /* tape mark */ + case MTSE_EOM: /* end of medium */ + if (tu_frame[ctlr] != 0) + uptr->STATUS |= ER1_FCE; + uptr->CMD &= ~(CR_GO); + uptr->CMD |= CS_ATA; + tu_attn[ctlr] = 1; + /* Stop motion if we recieve any of these */ + tu_error(uptr, r); + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + return SCPE_OK; + } + tu_frame[ctlr] = 0177777 & (tu_frame[ctlr] + 1); + if (tu_frame[ctlr] == 0) { + tu_error(uptr, MTSE_OK); + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + return SCPE_OK; + } else + sim_activate(uptr, 5000); + return SCPE_OK; + } + sim_activate(uptr, 200); + return SCPE_OK; +} + + + + +t_stat +tu_reset(DEVICE * dptr) +{ + int ctlr; + for (ctlr = 0; ctlr < NUM_DEVS_TU; ctlr++) { + tu_df10[ctlr].devnum = tu_dib[ctlr].dev_num; + tu_df10[ctlr].nxmerr = 19; + tu_df10[ctlr].ccw_comp = 14; + tu_attn[ctlr] = 0; + tu_rae[ctlr] = 0; + } + return SCPE_OK; +} + +void tu_read_word(UNIT *uptr) { + int i, cc, ch; + + tu_boot_buffer = 0; + for(i = 0; i <= 4; i++) { + cc = (8 * (3 - i)) + 4; + ch = tu_buf[0][uptr->DATAPTR]; + if (cc < 0) + tu_boot_buffer |= (uint64)(ch & 0x0f); + else + tu_boot_buffer |= (uint64)(ch & 0xff) << cc; + uptr->DATAPTR++; + } +} + +/* Boot from given device */ +t_stat +tu_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + t_mtrlnt reclen; + t_stat r; + uint32 addr; + int wc; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + r = sim_tape_rewind(uptr); + if (r != SCPE_OK) + return r; + r = sim_tape_rdrecf(uptr, &tu_buf[0][0], &reclen, TU_NUMFR); + if (r != SCPE_OK) + return r; + uptr->DATAPTR = 0; + uptr->hwmark = reclen; + + tu_read_word(uptr); + wc = (tu_boot_buffer >> 18) & RMASK; + addr = tu_boot_buffer & RMASK; + while (wc != 0) { + wc = (wc + 1) & RMASK; + addr = (addr + 1) & RMASK; + if ((uint32)uptr->DATAPTR >= uptr->hwmark) { + r = sim_tape_rdrecf(uptr, &tu_buf[0][0], &reclen, TU_NUMFR); + if (r != SCPE_OK) + return r; + uptr->DATAPTR = 0; + uptr->hwmark = reclen; + } + tu_read_word(uptr); + if (addr < 020) + FM[addr] = tu_boot_buffer; + else + M[addr] = tu_boot_buffer; + } + if (addr < 020) + FM[addr] = tu_boot_buffer; + else + M[addr] = tu_boot_buffer; + + PC = tu_boot_buffer & RMASK; + return SCPE_OK; +} + + +t_stat +tu_attach(UNIT * uptr, CONST char *file) +{ t_stat r; + int ctlr = GET_CNTRL(uptr->flags); + struct df10 *df; + + /* Find df10 */ + df = &tu_df10[ctlr]; + + uptr->CMD = 0; + uptr->STATUS = 0; + r = sim_tape_attach_ex(uptr, file, 0, 0); + if (r == SCPE_OK) { + uptr->CMD = CS_ATA|CS_CHANGE; + tu_attn[ctlr] = 1; + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + } + return r; +} + +t_stat +tu_detach(UNIT * uptr) +{ + int ctlr = GET_CNTRL(uptr->flags); + struct df10 *df; + + /* Find df10 */ + df = &tu_df10[ctlr]; + uptr->STATUS = 0; + uptr->CMD = CS_ATA|CS_CHANGE; + tu_attn[ctlr] = 1; + if ((df->status & IADR_ATTN) != 0) + df10_setirq(df); + return sim_tape_detach(uptr); +} + + + +t_stat tu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "TU Tape Drives with TM03 formatter. (TU)\n\n"); +fprintf (st, "The TU controller implements the Massbus tape formatter the TM03. TU\n"); +fprintf (st, "options include the ability to set units write enabled or write locked\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 TU device supports the BOOT command.\n"); +sim_tape_attach_help (st, dptr, uptr, flag, cptr); +fprint_reg_help (st, dptr); +return SCPE_OK; +} + +const char *tu_description (DEVICE *dptr) +{ + return "TU04/05/06/07 Massbus disk controller"; +} + + +#endif diff --git a/PDP10/pdp6_dcs.c b/PDP10/pdp6_dcs.c new file mode 100644 index 00000000..3938d3a8 --- /dev/null +++ b/PDP10/pdp6_dcs.c @@ -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 diff --git a/PDP10/pdp6_dct.c b/PDP10/pdp6_dct.c new file mode 100644 index 00000000..9ca0fd57 --- /dev/null +++ b/PDP10/pdp6_dct.c @@ -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 diff --git a/PDP10/pdp6_dsk.c b/PDP10/pdp6_dsk.c new file mode 100644 index 00000000..f55e4804 --- /dev/null +++ b/PDP10/pdp6_dsk.c @@ -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 diff --git a/PDP10/pdp6_dtc.c b/PDP10/pdp6_dtc.c new file mode 100644 index 00000000..5663bfce --- /dev/null +++ b/PDP10/pdp6_dtc.c @@ -0,0 +1,1286 @@ +/* pdp10_dtc.c: 18b 551 DECtape simulator + + 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 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. + + dt (PDP-4, PDP-7) Type 550/555 DECtape + (PDP-6) Type 551 Dectape + (PDP-9) TC02/TU55 DECtape + (PDP-10) TD10/TU55 DECtape + (PDP-15) TC15/TU56 DECtape + + 18b DECtapes are represented in memory by fixed length buffer of 32b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the PDP-7 formatter) is: + + reverse end zone 7144 reverse end zone codes ~ 12 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 7144 forward end zone codes ~ 12 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). PDP-4/7 DECtapes came in two + formats. The first 5 controllers used a 4 word header/trailer (missing + word 0/4). All later serial numbers used the standard header. The later, + standard header/trailer is simulated here. + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. + +*/ + +#include "kx10_defs.h" +#ifndef NUM_DEVS_DTC +#define NUM_DEVS_DTC 0 +#endif + +#if (NUM_DEVS_DTC > 0) +#define DTC_DEVNUM 0210 +#define DTC_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ +#define D11_FILSIZ (D18_CAPAC * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Command register, status A */ +#define CMD u3 +#define DTC_FLAG_PIA 07 /* PI Channel */ +#define DTC_V_UNIT 3 /* unit select */ +#define DTC_M_UNIT 07 +#define DTC_V_FNC 6 +#define DTC_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_RALL 01 /* read all */ +#define FNC_SRCH 02 /* search */ +#define FNC_READ 03 /* read */ +#define FNC_WMRK 04 /* write timing */ +#define FNC_WALL 05 /* write All */ +#define FNC_WBLK 06 /* Write Block */ +#define FNC_WRIT 07 /* write data */ +#define DTC_DELAY 0003000 /* Initial delay time */ +#define DTC_TIME 0004000 /* Delay */ +#define DTC_RVDRV 0010000 /* Move unit reverse */ +#define DTC_START 0020000 /* Start unit */ +#define DTC_JDONE 0040000 /* Enable Job done */ +#define DTC_ETF 0100000 /* Enable End of tape flag */ +#define DTC_SEL 0200000 /* Select unit */ + +/* Flags in lower bits of u3 (unit position) */ +#define DTC_FNC_STOP 010 /* Unit stopping */ +#define DTC_FNC_START DTC_START /* Start unit motion */ +#define DTC_FNC_REV DTC_RVDRV /* Unit to change direction */ + +#define DTC_GETFNC(x) (((x) >> DTC_V_FNC) & DTC_M_FNC) +#define DTC_GETUNI(x) (((x) >> DTC_V_UNIT) & DTC_M_UNIT) + + +/* Status register B */ +#define DTB_DONE 0000001 +#define DTB_EOT 0000002 +#define DTB_ILL 0000004 +#define DTB_PAR 0000010 +#define DTB_TIME 0000020 +#define DTB_WR 0000040 +#define DTB_INCBLK 0000100 +#define DTB_NULL 0000200 +#define DTB_ACT 0000400 +#define DTB_REQ 0001000 +#define DTB_DLY 0002000 + +#define DSTATE u5 /* Dectape current state */ +/* Current Dectape state in u5 */ +#define DTC_FEND 0 /* Tape in endzone */ +#define DTC_FBLK 1 /* In forward block number */ +#define DTC_FCHK 2 /* In forward checksum */ +#define DTC_BLOCK 3 /* In block */ +#define DTC_RCHK 4 /* In reverse checksum */ +#define DTC_RBLK 5 /* In reverse block number */ +#define DTC_REND 7 /* In final endzone */ + +#define DTC_MOTMASK 0370 +#define DTC_MOT 0010 /* Tape in motion */ +#define DTC_REV 0020 /* Tape in reverse */ +#define DTC_XFR 0040 /* Tranfer block */ +#define DTC_STOP 0100 /* Tape to stop */ +#define DTC_ACCL 0200 /* Tape accel or decl */ + +#define DTC_V_WORD 8 /* Shift for word count */ +#define DTC_M_WORD 0177 /* 128 words per block */ +#define DTC_V_BLK 16 /* Shift for Block number */ +#define DTC_M_BLK 01777 /* Block mask */ + +#define DELAY u4 /* Hold delay time in DT WORDS */ +/* Logging */ + +#define LOG_MS 00200 /* move, search */ +#define LOG_RW 00400 /* read, write */ +#define LOG_RA 01000 /* read all */ +#define LOG_BL 02000 /* block # lblk */ + +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +#define DT_WRDTIM 15000 + +int32 dtc_dtsa = 0; /* status A */ +int32 dtc_dtsb = 0; /* status B */ +int dtc_dct = 0; + +t_stat dtc_devio(uint32 dev, uint64 *data); +t_stat dtc_svc (UNIT *uptr); +t_stat dtc_boot(int32 unit_num, DEVICE * dptr); +t_stat dtc_set_dct (UNIT *, int32, CONST char *, void *); +t_stat dtc_show_dct (FILE *, UNIT *, int32, CONST void *); +t_stat dtc_reset (DEVICE *dptr); +t_stat dtc_attach (UNIT *uptr, CONST char *cptr); +t_stat dtc_detach (UNIT *uptr); + +/* DT data structures + + dtc_dev DTC device descriptor + dtc_unit DTC unit list + dtc_reg DTC register list + dtc_mod DTC modifier list +*/ + + +#if !PDP6 +#define D DEV_DIS +#else +#define D 0 +#endif + +DIB dtc_dib = { DTC_DEVNUM, 2, &dtc_devio, NULL}; + +UNIT dtc_unit[] = { + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dtc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) } + }; + +REG dtc_reg[] = { + { ORDATA (DTSA, dtc_dtsa, 18) }, + { URDATA (POS, dtc_unit[0].pos, 10, T_ADDR_W, 0, + DTC_NUMDR, PV_LEFT | REG_RO) }, + { NULL } + }; + +MTAB dtc_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DCT", "DCT", + &dtc_set_dct, &dtc_show_dct, NULL}, + { 0 } + }; + +DEBTAB dtc_deb[] = { + { "CMD", DEBUG_CMD, "Show command execution to devices"}, + { "DATA", DEBUG_DATA, "Show data transfers"}, + { "DETAIL", DEBUG_DETAIL, "Show details about device"}, + { "EXP", DEBUG_EXP, "Show exception information"}, + { "CONI", DEBUG_CONI, "Show coni instructions"}, + { "CONO", DEBUG_CONO, "Show coni instructions"}, + { "DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "READALL", LOG_RA }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dtc_dev = { + "DTC", dtc_unit, dtc_reg, dtc_mod, + DTC_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &dtc_reset, &dtc_boot, &dtc_attach, &dtc_detach, + &dtc_dib, DEV_DISABLE | DEV_DEBUG | D, 0, + dtc_deb, NULL, NULL + }; + +int delay[] = { 0, 50, 100, 500 }; + +/* IOT routines */ +t_stat +dtc_devio(uint32 dev, uint64 *data) { + int i; + + switch(dev & 07) { + case CONI: + *data = (uint64)dtc_dtsa; + sim_debug(DEBUG_CONI, &dtc_dev, "DTC %03o CONI %06o PC=%o\n", + dev, (uint32)*data, PC); + break; + + case CONO: + clr_interrupt(dev); + /* Copy over command and priority */ + dtc_dtsa = *data & (DTC_FLAG_PIA|(DTC_M_FNC << DTC_V_FNC)|DTC_TIME|DTC_RVDRV| \ + DTC_START|DTC_JDONE|DTC_ETF|DTC_SEL); + dtc_dtsb = 0; + sim_debug(DEBUG_CONO, &dtc_dev, "DTC %03o CONO %06o PC=%o\n", + dev, (uint32)*data, PC); + i = DTC_GETUNI(*data); +#if DTC_NUMDR < 8 + if (i >= DTC_NUMDR) { + dtc_dtsb |= DTB_ILL; + set_interrupt(DTC_DEVNUM, dtc_dtsa); + return SCPE_OK; + } +#endif + if (*data & DTC_DELAY) { + dtc_dtsb |= DTB_DLY; + dtc_unit[i].DELAY = delay[(*data >> 9) & 3]; + } + + /* Check if we are selecting a drive or not */ + if (*data & DTC_SEL) { + if ((dtc_unit[i].flags & UNIT_ATT) == 0) { + dtc_dtsb |= DTB_ILL; + set_interrupt(DTC_DEVNUM, dtc_dtsa); + return SCPE_OK; + } + if ((*data & DTC_START) != 0) { + /* Start the unit if not already running */ + dtc_unit[i].CMD = (dtc_dtsa & 0377707); + if ((dtc_unit[i].DSTATE & DTC_MOT) == 0) { + if (!sim_is_active(&dtc_unit[i])) { + sim_activate(&dtc_unit[i], 10000); + } + } + } else { + dtc_unit[i].CMD |= DTC_FNC_STOP; + } + dtc_dtsb |= DTB_REQ; + } else { + /* If not selecting, but delaying, give it to a unit to handle */ + if (dtc_dtsb & DTB_DLY) { + dtc_unit[i].CMD = (dtc_dtsa & 0007007); + if ((dtc_unit[i].DSTATE & DTC_MOT) == 0) { + if (!sim_is_active(&dtc_unit[i])) { + sim_activate(&dtc_unit[i], 10000); + } + } + } + /* Not selecting any, stop all */ + for (i = 0; i < DTC_NUMDR; i++) + dtc_unit[i].CMD = DTC_FNC_STOP; + dtc_dtsb |= DTB_NULL; + } + break; + + case DATAI: + break; + + case DATAO: + break; + + case CONI|04: + *data = dtc_dtsb; + sim_debug(DEBUG_CONI, &dtc_dev, "DTB %03o CONI %012llo PC=%o\n", + dev, *data, PC); + break; + + case CONO|04: + break; + + case DATAI|4: + break; + case DATAO|4: + break; + + } + return SCPE_OK; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat +dtc_svc (UNIT *uptr) +{ + int word; + uint64 data = 0; + uint32 *fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + int u = uptr-dtc_unit; + int blk; + int off; + /* + * Check if in motion or stopping. + */ + if (uptr->DSTATE & DTC_MOT) { + /* Check if stoping */ + if ((uptr->CMD & DTC_FNC_STOP) != 0) { + /* Stop delay */ + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o stopping\n", u); + sim_activate(uptr, DT_WRDTIM*10); + uptr->DSTATE &= ~(DTC_MOT); + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = (0100 << DTC_V_WORD) | DTC_BLOCK | (uptr->DSTATE & DTC_MOTMASK); + if (uptr->DSTATE & DTC_REV) { + if (blk <= 0) { + blk = 0; + uptr->DSTATE = DTC_FEND | (uptr->DSTATE & DTC_MOTMASK); + } else { + blk--; + } + } else { + if (blk <= 01100) + blk++; + } + dtc_dtsb |= DTB_DONE; + if ((uptr->CMD & DTC_JDONE) != 0) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + + /* If we were delaying, all done, notify CPU if it asked to know */ + if ((dtc_dtsb & DTB_DLY) != 0) { + uptr->DELAY = 0; + dtc_dtsb |= DTB_TIME; + dtc_dtsb &= ~DTB_DLY; + if(uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + + dtc_dtsb &= ~(DTB_REQ|DTB_ACT); + dtc_dtsb |= DTB_NULL; + uptr->CMD &= 077077; + uptr->DSTATE |= (blk << DTC_V_BLK); + return SCPE_OK; + } + + /* Set tape to move in correct direction */ + if (uptr->CMD & DTC_RVDRV) { + if ((uptr->DSTATE & DTC_REV) == 0) { + sim_activate(uptr, DT_WRDTIM*10); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o reversing\n", u); + uptr->DSTATE |= DTC_REV; + uptr->DELAY -= 10; + return SCPE_OK; + } + } else { + if ((uptr->DSTATE & DTC_REV) != 0) { + sim_activate(uptr, DT_WRDTIM*10); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o reversing\n", u); + uptr->DSTATE &= ~DTC_REV; + uptr->DELAY -= 10; + return SCPE_OK; + } + } + + /* Moving in reverse direction */ + if (uptr->DSTATE & DTC_REV) { + switch (uptr->DSTATE & 7) { + case DTC_FEND: /* Tape in endzone */ + /* Set stop */ + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o rev forward end\n", u); + uptr->u6 = 0; + dtc_dtsb |= DTB_EOT|DTB_NULL; + dtc_dtsb &= ~(DTB_REQ|DTB_ACT); + if (uptr->CMD & DTC_ETF) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + uptr->CMD |= DTC_FNC_STOP; + sim_activate(uptr, DT_WRDTIM*10); + if ((dtc_dtsb & DTB_DLY) != 0) { + uptr->DELAY = 0; + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + + case DTC_FBLK: /* In forward block number */ + sim_activate(uptr,DT_WRDTIM); + uptr->DELAY --; + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word--; + if (word <= 0) + uptr->DSTATE = DTC_FEND | (uptr->DSTATE & DTC_MOTMASK); + else + uptr->DSTATE = DTC_RBLK|(word << DTC_V_BLK) | (uptr->DSTATE & DTC_MOTMASK); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o rev forward block\n", u); + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_RALL: + if (dtc_dtsb & DTB_ACT) { + if (dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + } + break; + case FNC_READ: + case FNC_WRIT: + if (dtc_dtsb & DTB_REQ) { + dtc_dtsb &= ~DTB_REQ; + dtc_dtsb |= DTB_ACT; + } + break; + case FNC_SRCH: + case FNC_MOVE: + break; + case FNC_WALL: + case FNC_WBLK: + (void)dct_read(dtc_dct, &data, 6); + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + } + break; + + case DTC_FCHK: /* In forward checksum */ + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o rev forward check\n", u); + sim_activate(uptr,DT_WRDTIM*2); + /* Disconnect if DCT no longer attached */ + if ((dtc_dtsb & DTB_ACT) != 0 && dct_is_connect(dtc_dct) == 0) + dtc_dtsb |= DTB_DONE; + uptr->DELAY -= 2; + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = DTC_FBLK|(word << DTC_V_BLK) | (uptr->DSTATE & DTC_MOTMASK); + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_RALL: + if (dtc_dtsb & DTB_ACT) { + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + if (blk < 075) + data = 0721200220107LL; + else if (blk > 075) + data = 0721200233107LL; + else + data = 0577777777777LL; + if (dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + } + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + case FNC_SRCH: + case FNC_WRIT: + case FNC_WALL: + case FNC_READ: + case FNC_WBLK: + case FNC_MOVE: + break; + } + break; + + case DTC_BLOCK: /* In block */ + uptr->DELAY --; + sim_activate(uptr,DT_WRDTIM); + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word = (uptr->DSTATE >> DTC_V_WORD) & DTC_M_WORD; + off = ((blk << 7) + word) << 1; + /* Check if at end of block */ + if (word == 0) { + uptr->DSTATE &= ~((DTC_M_WORD << DTC_V_WORD) | 7); + uptr->DSTATE |= DTC_FCHK; /* Move to Checksum */ + } else { + uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD); + uptr->DSTATE |= (word - 1) << DTC_V_WORD; + } + uptr->u6-=2; + if ((dtc_dtsb & DTB_DLY) || (dtc_dtsb & DTB_ACT) == 0) + break; + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_MOVE: + case FNC_SRCH: + case FNC_WBLK: + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + case FNC_RALL: + case FNC_READ: + data = ((uint64)fbuf[off]) << 18; + data |= ((uint64)fbuf[off+1]); + if (dct_write(dtc_dct, &data, 6) == 0) { + dtc_dtsb &= ~DTB_ACT; + dtc_dtsb |= DTB_INCBLK|DTB_DONE; + } + break; + + case FNC_WRIT: + case FNC_WALL: + if (dct_read(dtc_dct, &data, 6) == 0) { + dtc_dtsb &= ~DTB_ACT; + dtc_dtsb |= DTB_INCBLK|DTB_DONE; + } + fbuf[off] = (data >> 18) & RMASK; + fbuf[off+1] = data & RMASK; + uptr->hwmark = uptr->capac; + break; + } + sim_debug(DEBUG_DETAIL, &dtc_dev, + "DTC %o rev data word %o:%o %012llo %d %06o %06o\n", + u, blk, word, data, off, fbuf[off], fbuf[off+1]); + break; + + case DTC_RCHK: /* In reverse checksum */ + sim_activate(uptr,DT_WRDTIM*2); + uptr->DELAY -= 2; + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o rev reverse check %06o %06o\n", + u, uptr->CMD, dtc_dtsb); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = DTC_BLOCK|(word << DTC_V_BLK)|(DTC_M_WORD << DTC_V_WORD) | + (uptr->DSTATE & DTC_MOTMASK); + if ((dtc_dtsb & DTB_ACT) != 0 && dct_is_connect(dtc_dct) == 0) + dtc_dtsb |= DTB_DONE; + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_WRIT: + case FNC_WALL: + case FNC_SRCH: + case FNC_RALL: + case FNC_MOVE: + case FNC_READ: + case FNC_WBLK: + if (dtc_dtsb & DTB_REQ) { + dtc_dtsb |= DTB_ACT; + dtc_dtsb &= ~(DTB_REQ|DTB_NULL); + } + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + } + break; + + case DTC_RBLK: /* In reverse block number */ + sim_activate(uptr,DT_WRDTIM*2); + uptr->DELAY -= 2; + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + data = (uint64)word; + uptr->DSTATE = DTC_RCHK|(word << DTC_V_BLK)|(DTC_M_WORD << DTC_V_WORD) | + (uptr->DSTATE & DTC_MOTMASK); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o rev reverse block %04o\n", u, word); + dtc_dtsb &= ~DTB_EOT; + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_MOVE: + case FNC_READ: + case FNC_WMRK: + case FNC_WRIT: + break; + case FNC_RALL: + if (dtc_dtsb & DTB_ACT && dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + break; + case FNC_SRCH: + if (dtc_dtsb & DTB_ACT) { + (void)dct_write(dtc_dct, &data, 6); + dtc_dtsb |= DTB_DONE; + } + break; + case FNC_WALL: + case FNC_WBLK: + if (dtc_dtsb & DTB_ACT) { + (void)dct_read(dtc_dct, &data, 6); + dtc_dtsb |= DTB_DONE; + } + break; + } + if (dtc_dtsb & DTB_REQ) { + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o activate\n", u); + dtc_dtsb &= ~(DTB_REQ|DTB_NULL); + dtc_dtsb |= DTB_ACT; + } + break; + + case DTC_REND: /* In final endzone */ + sim_activate(uptr, DT_WRDTIM*10); + uptr->DELAY -= 10; + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word--; + uptr->DSTATE = DTC_RBLK|(word << DTC_V_BLK) | (uptr->DSTATE & DTC_MOTMASK); + dtc_dtsb &= ~DTB_EOT; + break; + } + } else { + /* Moving in forward direction */ + switch (uptr->DSTATE & 7) { + case DTC_FEND: /* Tape in endzone */ + sim_activate(uptr, DT_WRDTIM*10); + uptr->DELAY -= 10; + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o forward end\n", u); + /* Move to first block */ + uptr->DSTATE = DTC_FBLK | (uptr->DSTATE & DTC_MOTMASK); + uptr->u6 = 0; + break; + + case DTC_FBLK: /* In forward block number */ + uptr->DELAY -= 2; + sim_activate(uptr,DT_WRDTIM*2); + dtc_dtsb &= ~DTB_EOT; + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + uptr->DSTATE = DTC_FCHK|(word << DTC_V_BLK) | (uptr->DSTATE & DTC_MOTMASK); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o forward block %04o %06o\n", + u, word, dtc_dtsb); + data = (uint64)word; + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + if ((dtc_dtsb & DTB_ACT) != 0 && dct_is_connect(dtc_dct) == 0) + dtc_dtsb |= DTB_DONE; + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_SRCH: + if (dtc_dtsb & DTB_ACT) { + (void)dct_write(dtc_dct, &data, 6); + dtc_dtsb |= DTB_DONE; + } + break; + case FNC_RALL: + if ((dtc_dtsb & DTB_ACT) != 0 && dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + break; + case FNC_READ: + case FNC_WRIT: + if (dtc_dtsb & DTB_REQ) { + dtc_dtsb &= ~(DTB_REQ|DTB_NULL); + dtc_dtsb |= DTB_ACT; + } + break; + case FNC_MOVE: + break; + case FNC_WALL: + case FNC_WBLK: + (void)dct_read(dtc_dct, &data, 6); + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + } + break; + + case DTC_FCHK: /* In forward checksum */ + uptr->DELAY -= 2; + sim_activate(uptr,DT_WRDTIM*2); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o forward check %06o\n", u, dtc_dtsb); + uptr->DSTATE &= ~7; + uptr->DSTATE |= DTC_BLOCK; /* Move to datablock */ + if ((dtc_dtsb & DTB_ACT) != 0 && dct_is_connect(dtc_dct) == 0) + dtc_dtsb |= DTB_DONE; + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_RALL: + if (dtc_dtsb & DTB_ACT) { + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + if (blk < 075) + data = 0721200220107LL; + else if (blk > 075) + data = 0721200233107LL; + else + data = 0577777777777LL; + if (dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + } + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + case FNC_SRCH: + case FNC_WRIT: + case FNC_WALL: + case FNC_READ: + case FNC_WBLK: + if (dtc_dtsb & DTB_REQ) { + dtc_dtsb &= ~(DTB_REQ|DTB_NULL); + dtc_dtsb |= DTB_ACT; + } + break; + case FNC_MOVE: + break; + } + break; + + case DTC_BLOCK: /* In block */ + uptr->DELAY --; + sim_activate(uptr,DT_WRDTIM); + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word = (uptr->DSTATE >> DTC_V_WORD) & DTC_M_WORD; + off = ((blk << 7) + word) << 1; + /* Check if at end of block */ + if (word == DTC_M_WORD) { + uptr->DSTATE &= ~7; + uptr->DSTATE |= DTC_RCHK; /* Move to checksum */ + } else { + uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD); + uptr->DSTATE |= (word + 1) << DTC_V_WORD; + } + if (dtc_dtsb & DTB_DLY) + break; + if ((dtc_dtsb & DTB_ACT) == 0) + break; + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_MOVE: + case FNC_SRCH: + case FNC_WALL: + case FNC_WBLK: + break; + case FNC_RALL: + case FNC_READ: + data = ((uint64)fbuf[off]) << 18; + data |= (uint64)fbuf[off+1]; + if (dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + break; + case FNC_WRIT: + if (dct_read(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + fbuf[off] = (data >> 18) & RMASK; + fbuf[off+1] = data & RMASK; + uptr->hwmark = uptr->capac; + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + } + sim_debug(DEBUG_DETAIL, &dtc_dev, + "DTC %o data word %o:%o %012llo %d %06o %06o\n", + u, blk, word, data, off, fbuf[off], fbuf[off+1]); + break; + + case DTC_RCHK: /* In reverse checksum */ + uptr->DELAY -=2; + sim_activate(uptr,DT_WRDTIM*2); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o reverse check\n", u); + uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD) | 7; + uptr->DSTATE |= DTC_RBLK; /* Move to end of block */ + if ((dtc_dtsb & DTB_ACT) != 0 && dct_is_connect(dtc_dct) == 0) + dtc_dtsb |= DTB_DONE; + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_RALL: + if (dtc_dtsb & DTB_ACT) { + blk = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + if (blk < 073) + data = 0721200220107LL; + else + data = 0721200233107LL; + if (dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + } + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + break; + case FNC_SRCH: + case FNC_WRIT: + case FNC_WALL: + case FNC_READ: + case FNC_WBLK: + case FNC_MOVE: + break; + } + break; + + case DTC_RBLK: /* In reverse block number */ + uptr->DELAY -=2; + sim_activate(uptr,DT_WRDTIM*2); + word = (uptr->DSTATE >> DTC_V_BLK) & DTC_M_BLK; + word++; + if (word > 01101) { + uptr->DSTATE = DTC_REND|(word << DTC_V_BLK)|(DTC_M_WORD << DTC_V_WORD) | + (uptr->DSTATE & DTC_MOTMASK); + } else { + uptr->DSTATE = DTC_FBLK|(word << DTC_V_BLK)|(uptr->DSTATE & DTC_MOTMASK); + } + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o reverse block %o\n", u, word); + if ((dtc_dtsb & DTB_DLY) != 0) { + if (uptr->DELAY < 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + break; + } + /* Check if DCC has disconnected */ + if ((dtc_dtsb & DTB_ACT) != 0 && dct_is_connect(dtc_dct) == 0) + dtc_dtsb |= DTB_DONE; + /* Check if request pending */ + if (dtc_dtsb & DTB_REQ) { + dtc_dtsb &= ~(DTB_REQ|DTB_NULL); + dtc_dtsb |= DTB_ACT; + } + switch (DTC_GETFNC(uptr->CMD)) { + case FNC_RALL: + if ((dtc_dtsb & DTB_ACT) != 0 && dct_write(dtc_dct, &data, 6) == 0) + dtc_dtsb |= DTB_DONE; + break; + case FNC_SRCH: + case FNC_MOVE: + case FNC_READ: + case FNC_WRIT: + case FNC_WALL: + case FNC_WBLK: + break; + case FNC_WMRK: + dtc_dtsb |= DTB_ILL; + set_interrupt(DTC_DEVNUM, dtc_dtsa); + break; + } + if (dtc_dtsb & DTB_REQ) { + dtc_dtsb &= ~(DTB_REQ|DTB_NULL); + dtc_dtsb |= DTB_ACT; + } + break; + + case DTC_REND: /* In final endzone */ + /* Set stop */ + uptr->CMD |= DTC_FNC_STOP; + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o reverse end\n", u); + dtc_dtsb |= DTB_EOT; + if (dtc_dtsa & DTC_ETF) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + if ((dtc_dtsb & DTB_DLY) != 0) { + dtc_dtsb &= ~DTB_DLY; + dtc_dtsb |= DTB_TIME; + if (uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + sim_activate(uptr, DT_WRDTIM*10); + break; + } + } + + if ((dtc_dtsb & DTB_DONE) != 0) { + if ((uptr->CMD & DTC_JDONE) != 0) { + set_interrupt(DTC_DEVNUM, dtc_dtsa); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o post done\n", u); + } + + dtc_dtsb &= ~(DTB_REQ|DTB_ACT); + dtc_dtsb |= DTB_NULL; + uptr->CMD &= 077077; + } + + if ((dtc_dtsb & (DTB_ILL|DTB_PAR|DTB_EOT)) != 0) { + set_interrupt(DTC_DEVNUM, dtc_dtsa); + uptr->CMD = DTC_FNC_STOP; + } + + /* Check if starting */ + } else if (uptr->CMD & DTC_START) { + /* Start up delay */ + sim_activate(uptr, DT_WRDTIM*10); + if ((dtc_dtsb & DTB_DLY) != 0) { + uptr->DELAY = 0; + dtc_dtsb |= DTB_TIME; + dtc_dtsb &= ~DTB_DLY; + if(uptr->CMD & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + } + + uptr->DSTATE |= DTC_MOT; + if (uptr->CMD & DTC_RVDRV) { + uptr->DSTATE |= DTC_REV; + } else { + uptr->DSTATE &= ~DTC_REV; + } + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o start %06o\n", u, uptr->CMD); + return SCPE_OK; + } else if ((dtc_dtsb & DTB_DLY) != 0) { + uptr->DELAY = 0; + dtc_dtsb |= DTB_TIME; + dtc_dtsb &= ~DTB_DLY; + if(dtc_dtsa & DTC_TIME) + set_interrupt(DTC_DEVNUM, dtc_dtsa); + sim_debug(DEBUG_DETAIL, &dtc_dev, "DTC %o delay over %06o\n", u, dtc_dtsa); + } + return SCPE_OK; +} + +/* Boot from given device */ +t_stat +dtc_boot(int32 unit_num, DEVICE * dptr) +{ + UNIT *uptr = &dptr->units[unit_num]; + uint32 *fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + uint64 word; + int off; + int wc, addr; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; /* attached? */ + + off = 0; + wc = fbuf[off++]+1; + addr = fbuf[off++]; + while (wc != 0777777) { + wc = (wc + 1) & RMASK; + addr = (addr + 1) & RMASK; + word = ((uint64)fbuf[off++]) << 18; + word |= (uint64)fbuf[off++]; + if (addr < 020) + FM[addr] = word; + else + M[addr] = word; + } + if (addr < 020) + FM[addr] = word; + else + M[addr] = word; + uptr->DSTATE = (1 << DTC_V_BLK) | DTC_BLOCK | DTC_MOT; + sim_activate(uptr,30000); + PC = word & RMASK; + return SCPE_OK; +} + +/* set DCT channel and unit. */ +t_stat +dtc_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; + dtc_dct = dct; + return SCPE_OK; +} + +t_stat +dtc_show_dct (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + + fprintf (st, "DCT=%02o", dtc_dct); + return SCPE_OK; +} + +/* Reset routine */ + +t_stat +dtc_reset (DEVICE *dptr) +{ + int i; + + dtc_dtsb = dtc_dtsa = 0; /* clear status */ + for (i = 0; i < DTC_NUMDR; i++) { + if ((dtc_unit[i].DSTATE & DTC_MOT) != 0) + dtc_unit[i].CMD |= DTC_FNC_STOP; + } + clr_interrupt(DTC_DEVNUM); + clr_interrupt(DTC_DEVNUM|4); + return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 12b, read 12b format and convert to 18b in buffer + If 16b, read 16b format and convert to 18b in buffer + If 18b/36b, read data into buffer +*/ + +t_stat +dtc_attach (UNIT *uptr, CONST char *cptr) +{ + uint16 pdp8b[D8_NBSIZE]; + uint16 pdp11b[D18_BSIZE]; + uint32 ba, sz, k, *fbuf; + int32 u = uptr - dtc_dev.units; + t_stat r; + + r = attach_unit (uptr, cptr); /* attach */ + if (r != SCPE_OK) /* error? */ + return r; + if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */ + if (sim_switches & SWMASK ('T')) /* att 12b? */ + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = uptr->flags | UNIT_11FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D8_FILSIZ) + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sz == D11_FILSIZ) + uptr->flags = uptr->flags | UNIT_11FMT; + } + } + uptr->capac = DTU_CAPAC (uptr); /* set capacity */ + uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); + if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } + fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + printf ("%s%d: ", sim_dname (&dtc_dev), u); + if (uptr->flags & UNIT_8FMT) + printf ("12b format"); + else if (uptr->flags & UNIT_11FMT) + printf ("16b format"); + else printf ("18b/36b format"); + printf (", buffering file in memory\n"); + if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (k == 0) + break; + for ( ; k < D8_NBSIZE; k++) + pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | + ((uint32) (pdp8b[k + 1] >> 6) & 077); + fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | + ((uint32) pdp8b[k + 2] & 07777); + ba = ba + 2; /* end blk loop */ + } + } /* end file loop */ + uptr->hwmark = ba; + } /* end if */ + else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (k == 0) + break; + for ( ; k < D18_BSIZE; k++) + pdp11b[k] = 0; + for (k = 0; k < D18_BSIZE; k++) + fbuf[ba++] = pdp11b[k]; + } + uptr->hwmark = ba; + } /* end elif */ + else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), + uptr->capac, uptr->fileref); + uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ + uptr->pos = DT_EZLIN; /* beyond leader */ + return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, convert 18b buffer to 12b and write to file + If 16b, convert 18b buffer to 16b and write to file + If 18b/36b, write buffer to file + Deallocate buffer +*/ + +t_stat +dtc_detach (UNIT* uptr) +{ + uint16 pdp8b[D8_NBSIZE]; + uint16 pdp11b[D18_BSIZE]; + uint32 ba, k, *fbuf; + int32 u = uptr - dtc_dev.units; + + if (!(uptr->flags & UNIT_ATT)) + return SCPE_OK; + if (sim_is_active (uptr)) { + sim_cancel (uptr); + uptr->CMD = uptr->pos = 0; + } + fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&dtc_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (fbuf[ba] >> 6) & 07777; + pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | + ((fbuf[ba + 1] >> 12) & 077); + pdp8b[k + 2] = fbuf[ba + 1] & 07777; + ba = ba + 2; + } /* end loop blk */ + fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (ferror (uptr->fileref)) + break; + } /* end loop file */ + } /* end if 12b */ + else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D18_BSIZE; k++) /* loop blk */ + pdp11b[k] = fbuf[ba++] & 0177777; + fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (ferror (uptr->fileref)) + break; + } /* end loop file */ + } /* end if 16b */ + else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ + uptr->hwmark, uptr->fileref); + if (ferror (uptr->fileref)) + perror ("I/O error"); + } /* end if hwmark */ + free (uptr->filebuf); /* release buf */ + uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ + uptr->filebuf = NULL; /* clear buf ptr */ + uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */ + uptr->capac = DT_CAPAC; /* default size */ + return detach_unit (uptr); +} +#endif diff --git a/PDP10/pdp6_mtc.c b/PDP10/pdp6_mtc.c new file mode 100644 index 00000000..df82286d --- /dev/null +++ b/PDP10/pdp6_mtc.c @@ -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