From c686f75894d9206dd9da80f8b2203a3ef402d30e Mon Sep 17 00:00:00 2001 From: "Author: Richard Cornwell" Date: Mon, 9 Mar 2020 23:07:47 -0700 Subject: [PATCH] KA10: Added support for KL10A/B. --- .travis.yml | 2 +- PDP10/ka10_ai.c | 4 +- PDP10/ka10_auxcpu.c | 71 +- PDP10/ka10_ch10.c | 2 - PDP10/ka10_dkb.c | 366 +- PDP10/ka10_dpk.c | 121 +- PDP10/ka10_iii.c | 669 ++++ PDP10/ka10_imx.c | 255 +- PDP10/ka10_pd.c | 6 - PDP10/ka10_pmp.c | 134 +- PDP10/ka10_stk.c | 3 +- PDP10/ka10_ten11.c | 8 +- PDP10/kl10_fe.c | 2484 ++++++++++++ PDP10/kl10_nia.c | 1674 ++++++++ PDP10/kx10_cp.c | 4 +- PDP10/kx10_cpu.c | 4888 ++++++++++++++++++++++-- PDP10/kx10_cr.c | 4 +- PDP10/kx10_cty.c | 21 +- PDP10/kx10_dc.c | 4 +- PDP10/kx10_defs.h | 249 +- PDP10/kx10_df.c | 32 +- PDP10/kx10_disk.c | 289 ++ PDP10/kx10_disk.h | 81 + PDP10/kx10_dk.c | 4 +- PDP10/kx10_dp.c | 56 +- PDP10/kx10_dpy.c | 237 +- PDP10/kx10_dt.c | 366 +- PDP10/kx10_imp.c | 40 +- PDP10/kx10_lp.c | 6 +- PDP10/kx10_mt.c | 4 +- PDP10/kx10_pt.c | 4 +- PDP10/kx10_rc.c | 8 +- PDP10/kx10_rh.c | 801 ++++ PDP10/kx10_rp.c | 778 ++-- PDP10/kx10_rs.c | 538 +-- PDP10/kx10_sys.c | 221 +- PDP10/kx10_tu.c | 911 ++--- PDP10/pdp6_dtc.c | 93 +- PDP10/pdp6_slave.c | 329 ++ README.md | 8 +- Visual Studio Projects/PDP10-KA.vcproj | 24 + Visual Studio Projects/PDP10-KI.vcproj | 12 + Visual Studio Projects/PDP10-KL.vcproj | 573 +++ Visual Studio Projects/PDP6.vcproj | 4 + Visual Studio Projects/Simh.sln | 6 + doc/ka10_doc.doc | Bin 242688 -> 230400 bytes doc/ki10_doc.doc | Bin 148992 -> 158720 bytes doc/kl10_doc.doc | Bin 0 -> 164352 bytes makefile | 35 +- 49 files changed, 13982 insertions(+), 2447 deletions(-) create mode 100644 PDP10/ka10_iii.c create mode 100644 PDP10/kl10_fe.c create mode 100644 PDP10/kl10_nia.c create mode 100644 PDP10/kx10_disk.c create mode 100644 PDP10/kx10_disk.h create mode 100644 PDP10/kx10_rh.c create mode 100644 PDP10/pdp6_slave.c create mode 100644 Visual Studio Projects/PDP10-KL.vcproj create mode 100644 doc/kl10_doc.doc diff --git a/.travis.yml b/.travis.yml index 87ffcab3..073efcca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ language: c env: # These are supposed to match ALL in makefile. # Each job builds 15 simulators. - - SIM="pdp1 pdp4 pdp6 pdp7 pdp8 pdp9 pdp10 pdp10-ka pdp10-ki pdp11 pdp15 vax microvax3900 microvax1 rtvax1000" + - SIM="pdp1 pdp4 pdp6 pdp7 pdp8 pdp9 pdp10 pdp10-ka pdp10-ki pdp10-kl pdp11 pdp15 vax microvax3900 microvax1 rtvax1000" - SIM="microvax2 vax730 vax750 vax780 vax8200 vax8600 microvax2000 infoserver100 infoserver150vxt microvax3100 microvax3100e vaxstation3100m30 vaxstation3100m38 vaxstation3100m76 vaxstation4000m60" - SIM="microvax3100m80 vaxstation4000vlc infoserver1000 nova eclipse hp2100 hp3000 i1401 i1620 s3 altair altairz80 gri i7094 ibm1130" - SIM="id16 id32 sds lgp h316 cdc1700 swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 isys8030 isys8024" diff --git a/PDP10/ka10_ai.c b/PDP10/ka10_ai.c index 263bafa5..a9e1da20 100644 --- a/PDP10/ka10_ai.c +++ b/PDP10/ka10_ai.c @@ -279,7 +279,7 @@ DEVICE ai_dev = { AI_NAME, ai_unit, ai_reg, ai_mod, NUM_UNITS, 8, 18, 1, 8, 36, NULL, NULL, &ai_reset, NULL, &ai_attach, &ai_detach, - &ai_dib, DEV_DISABLE | DEV_DEBUG, 0, ai_debug, + &ai_dib, DEV_DISABLE | DEV_DEBUG | DEV_DIS, 0, ai_debug, NULL, NULL, &ai_help, NULL, NULL, &ai_description }; @@ -997,7 +997,7 @@ t_stat ai_attach (UNIT *uptr, CONST char *cptr) DIB *dib; r = attach_unit (uptr, cptr); - if (r != SCPE_OK) + if (r != SCPE_OK || (sim_switches & SIM_SW_REST) != 0) return r; rptr = find_dev_from_unit(uptr); if (rptr == 0) diff --git a/PDP10/ka10_auxcpu.c b/PDP10/ka10_auxcpu.c index f0031002..01f200ea 100644 --- a/PDP10/ka10_auxcpu.c +++ b/PDP10/ka10_auxcpu.c @@ -31,9 +31,6 @@ #endif #if NUM_DEVS_AUXCPU > 0 -#include -//#include -#include /* External bus interface. */ #define DATO 1 @@ -51,12 +48,11 @@ #define AUXCPU_POLL 1000 +#define PIA u3 +#define STATUS u4 +t_addr auxcpu_base = 03000000; -static int pia = 0; -static int status = 0; -int auxcpu_base = 03000000; - -static t_stat auxcpu_devio(uint32 dev, t_uint64 *data); +static t_stat auxcpu_devio(uint32 dev, 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); @@ -144,7 +140,6 @@ static t_stat auxcpu_attach (UNIT *uptr, CONST char *cptr) return r; sim_debug(DBG_TRC, &auxcpu_dev, "activate connection\n"); sim_activate (uptr, 10); /* start poll */ - uptr->flags |= UNIT_ATT; return SCPE_OK; } @@ -156,8 +151,6 @@ static t_stat auxcpu_detach (UNIT *uptr) return SCPE_OK; sim_cancel (uptr); r = tmxr_detach (&auxcpu_desc, uptr); - uptr->flags &= ~UNIT_ATT; - free (uptr->filename); uptr->filename = NULL; return r; } @@ -176,9 +169,9 @@ static t_stat auxcpu_svc (UNIT *uptr) tmxr_reset_ln (&auxcpu_ldsc); } - /* If incoming interrput => status |= 010 */ - if (status & 010) - set_interrupt(AUXCPU_DEVNUM, pia); + /* If incoming interrput => uptr->STATUS |= 010 */ + if (uptr->STATUS & 010) + set_interrupt(AUXCPU_DEVNUM, uptr->PIA); else clr_interrupt(AUXCPU_DEVNUM); @@ -187,6 +180,7 @@ static t_stat auxcpu_svc (UNIT *uptr) auxcpu_ldsc.rcve = 1; uptr->wait = AUXCPU_POLL; } + sim_clock_coschedule (uptr, uptr->wait); return SCPE_OK; } @@ -239,25 +233,18 @@ static int transaction (unsigned char *request, unsigned char *response) stat = tmxr_get_packet_ln (&auxcpu_ldsc, &auxcpu_request, &size); } while (stat != SCPE_OK || size == 0); - if (size > 7) + if (size > 9) return error ("Malformed transaction"); memcpy (response, auxcpu_request, size); return 0; } -int auxcpu_read (int addr, t_uint64 *data) +int auxcpu_read (t_addr addr, 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); @@ -271,11 +258,11 @@ int auxcpu_read (int addr, t_uint64 *data) 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; + *data = (uint64)response[1]; + *data |= (uint64)response[2] << 8; + *data |= (uint64)response[3] << 16; + *data |= (uint64)response[4] << 24; + *data |= (uint64)response[5] << 32; break; case ERR: fprintf (stderr, "AUXCPU: Read error %06o\r\n", addr); @@ -286,23 +273,18 @@ int auxcpu_read (int addr, t_uint64 *data) *data = 0; break; default: + fprintf (stderr, "AUXCPU: recieved %o\r\n", response[0]); return error ("Protocol error"); } return 0; } -int auxcpu_write (int addr, t_uint64 data) +int auxcpu_write (t_addr addr, 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); @@ -318,8 +300,7 @@ int auxcpu_write (int addr, t_uint64 data) transaction (request, response); - switch (response[0]) - { + switch (response[0]) { case ACK: break; case ERR: @@ -329,9 +310,9 @@ int auxcpu_write (int addr, t_uint64 data) fprintf (stderr, "AUXCPU: Write timeout %06o\r\n", addr); break; default: + fprintf (stderr, "AUXCPU: recieved %o\r\n", response[0]); return error ("Protocol error"); } - return 0; } @@ -362,25 +343,26 @@ static int auxcpu_interrupt (void) return 0; } -t_stat auxcpu_devio(uint32 dev, t_uint64 *data) +t_stat auxcpu_devio(uint32 dev, uint64 *data) { DEVICE *dptr = &auxcpu_dev; + UNIT *uptr = &auxcpu_unit[0]; switch(dev & 07) { case CONO: sim_debug(DEBUG_CONO, &auxcpu_dev, "CONO %012llo\n", *data); - pia = *data & 7; + uptr->PIA = *data & 7; if (*data & 010) { // Clear interrupt from the PDP-6. - status &= ~010; + uptr->STATUS &= ~010; clr_interrupt(AUXCPU_DEVNUM); } if (*data & 020) auxcpu_interrupt (); break; case CONI: - *data = (status & 010) | pia; + *data = (uptr->STATUS & 010) | uptr->PIA; sim_debug(DEBUG_CONI, &auxcpu_dev, "CONI %012llo\n", *data); break; case DATAI: @@ -407,13 +389,14 @@ static t_stat auxcpu_set_base (UNIT *uptr, int32 val, CONST char *cptr, void *de if (r != SCPE_OK) return SCPE_ARG; - auxcpu_base = (int)x; + auxcpu_base = (t_addr)(x&03777777); return SCPE_OK; } static t_stat auxcpu_show_base (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { - fprintf (st, "Base: %06o", auxcpu_base); + fprintf (st, "Base: %011o", auxcpu_base); return SCPE_OK; } #endif + diff --git a/PDP10/ka10_ch10.c b/PDP10/ka10_ch10.c index 0d682d80..23936356 100644 --- a/PDP10/ka10_ch10.c +++ b/PDP10/ka10_ch10.c @@ -335,8 +335,6 @@ void ch10_command (uint32 data) 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); diff --git a/PDP10/ka10_dkb.c b/PDP10/ka10_dkb.c index 30fa1465..00c80eed 100644 --- a/PDP10/ka10_dkb.c +++ b/PDP10/ka10_dkb.c @@ -1,6 +1,6 @@ /* ka10_dkb.c:Stanford Microswitch scanner. - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2019-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -32,20 +32,40 @@ #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); +#include "sim_video.h" #define DKB_DEVNUM 0310 +#define DONE 010 /* Device has character */ +#define SPW 020 /* Scanner in SPW mode */ + +#define VALID 010000 +#define SPW_FLG 020000 +#define CHAR 001777 +#define SHFT 000100 +#define TOP 000200 +#define META 000400 +#define CTRL 001000 + #define STATUS u3 #define DATA u4 #define PIA u5 +#define LINE u6 t_stat dkb_devio(uint32 dev, uint64 *data); +int dkb_keyboard (SIM_KEY_EVENT *kev); +t_stat dkb_svc(UNIT *uptr); +t_stat dkb_reset(DEVICE *dptr); +t_stat dkb_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *dkb_description (DEVICE *dptr); + + +int dkb_kmod = 0; DIB dkb_dib = { DKB_DEVNUM, 1, dkb_devio, NULL}; UNIT dkb_unit[] = { + {UDATA (&dkb_svc, UNIT_IDLE, 0) }, { 0 } }; @@ -57,42 +77,368 @@ MTAB dkb_mod[] = { DEVICE dkb_dev = { "DKB", dkb_unit, NULL, dkb_mod, 2, 10, 31, 1, 8, 8, - NULL, NULL, NULL, + NULL, NULL, dkb_reset, 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; */ + UNIT *uptr = &dkb_unit[0]; switch(dev & 3) { case CONI: - *data = status; + *data = (uint64)(uptr->STATUS|uptr->PIA); sim_debug(DEBUG_CONI, &dkb_dev, "DKB %03o CONI %06o\n", dev, (uint32)*data); break; case CONO: - status = (int)(*data&7); + uptr->PIA = (int)(*data&7); + if (*data & DONE) + uptr->STATUS = 0; + clr_interrupt(DKB_DEVNUM); sim_debug(DEBUG_CONO, &dkb_dev, "DKB %03o CONO %06o\n", dev, (uint32)*data); break; case DATAI: + *data = (uint64)((uptr->LINE << 18) | (uptr->DATA)); + uptr->STATUS = 0; + clr_interrupt(DKB_DEVNUM); sim_debug(DEBUG_DATAIO, &dkb_dev, "DKB %03o DATAI %06o\n", dev, (uint32)*data); break; case DATAO: + if (*data & 010000) { + uptr->STATUS |= SPW; + uptr->LINE = (int)(*data & 077); + } sim_debug(DEBUG_DATAIO, &dkb_dev, "DKB %03o DATAO %06o\n", dev, (uint32)*data); break; } return SCPE_OK; } +int dkb_modifiers (SIM_KEY_EVENT *kev) +{ + if (kev->state == SIM_KEYPRESS_UP) { + switch (kev->key) { + case SIM_KEY_SHIFT_L: + case SIM_KEY_SHIFT_R: + case SIM_KEY_CAPS_LOCK: + dkb_kmod |= SHFT; + return 1; + case SIM_KEY_CTRL_L: + case SIM_KEY_CTRL_R: + dkb_kmod |= CTRL; + return 1; + case SIM_KEY_WIN_L: + case SIM_KEY_WIN_R: + dkb_kmod |= META; + return 1; + case SIM_KEY_ALT_L: + case SIM_KEY_ALT_R: + dkb_kmod |= TOP; + return 1; + } + return 0; + } + if (kev->state == SIM_KEYPRESS_DOWN) { + switch (kev->key) { + case SIM_KEY_SHIFT_L: + case SIM_KEY_SHIFT_R: + case SIM_KEY_CAPS_LOCK: + dkb_kmod &= ~SHFT; + return 1; + case SIM_KEY_CTRL_L: + case SIM_KEY_CTRL_R: + dkb_kmod &= ~CTRL; + return 1; + case SIM_KEY_WIN_L: + case SIM_KEY_WIN_R: + dkb_kmod &= ~META; + return 1; + case SIM_KEY_ALT_L: + case SIM_KEY_ALT_R: + dkb_kmod &= ~TOP; + return 1; + } + return 0; + } + return 0; +} +int dkb_keys (SIM_KEY_EVENT *kev, UNIT *uptr) +{ + if (kev->state == SIM_KEYPRESS_UP) + return 0; + + switch (kev->key) { + case SIM_KEY_0: /* ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = dkb_kmod | 051; /* ) */ + else + uptr->DATA = dkb_kmod | 060; + return 1; + case SIM_KEY_1: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 054) & ~TOP; /* ! */ + else + uptr->DATA = dkb_kmod | 061; + return 1; + case SIM_KEY_2: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 052) & ~TOP; /* Circle Star */ + else + uptr->DATA = dkb_kmod | 062; + return 1; + case SIM_KEY_3: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 022) & ~TOP; /* # */ + else + uptr->DATA = dkb_kmod | 063; + return 1; + case SIM_KEY_4: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 066) & ~TOP; /* $ */ + else + uptr->DATA = dkb_kmod | 064; + return 1; + case SIM_KEY_5: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 067) & ~TOP; /* % */ + else + uptr->DATA = dkb_kmod | 065; + return 1; + case SIM_KEY_6: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 073) & ~TOP; /* ^ */ + else + uptr->DATA = dkb_kmod | 066; + return 1; + case SIM_KEY_7: + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 024) & ~TOP; /* & */ + else + uptr->DATA = dkb_kmod | 067; + return 1; + case SIM_KEY_8: /* ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 052) & ~TOP; /* * */ + else + uptr->DATA = dkb_kmod | 070; + return 1; + case SIM_KEY_9: /* ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = dkb_kmod | 050; /* ( */ + else + uptr->DATA = dkb_kmod | 071; + return 1; + case SIM_KEY_A: + uptr->DATA = dkb_kmod | 001; + return 1; + case SIM_KEY_B: + uptr->DATA = dkb_kmod | 002; + return 1; + case SIM_KEY_C: + if (dkb_kmod == (META|TOP|SHFT)) /* Control C */ + uptr->DATA = dkb_kmod | 043; + else + uptr->DATA = dkb_kmod | 003; + return 1; + case SIM_KEY_D: + uptr->DATA = dkb_kmod | 004; + return 1; + case SIM_KEY_E: + uptr->DATA = dkb_kmod | 005; + return 1; + case SIM_KEY_F: + uptr->DATA = dkb_kmod | 006; + return 1; + case SIM_KEY_G: + uptr->DATA = dkb_kmod | 007; + return 1; + case SIM_KEY_H: + uptr->DATA = dkb_kmod | 010; + return 1; + case SIM_KEY_I: + uptr->DATA = dkb_kmod | 011; + return 1; + case SIM_KEY_J: + uptr->DATA = dkb_kmod | 012; + return 1; + case SIM_KEY_K: + uptr->DATA = dkb_kmod | 013; + return 1; + case SIM_KEY_L: + uptr->DATA = dkb_kmod | 014; + return 1; + case SIM_KEY_M: + uptr->DATA = dkb_kmod | 015; + return 1; + case SIM_KEY_N: + uptr->DATA = dkb_kmod | 016; + return 1; + case SIM_KEY_O: + uptr->DATA = dkb_kmod | 017; + return 1; + case SIM_KEY_P: + uptr->DATA = dkb_kmod | 020; + return 1; + case SIM_KEY_Q: + uptr->DATA = dkb_kmod | 021; + return 1; + case SIM_KEY_R: + uptr->DATA = dkb_kmod | 022; + return 1; + case SIM_KEY_S: + uptr->DATA = dkb_kmod | 023; + return 1; + case SIM_KEY_T: + uptr->DATA = dkb_kmod | 024; + return 1; + case SIM_KEY_U: + uptr->DATA = dkb_kmod | 025; + return 1; + case SIM_KEY_V: + uptr->DATA = dkb_kmod | 026; + return 1; + case SIM_KEY_W: + uptr->DATA = dkb_kmod | 027; + return 1; + case SIM_KEY_X: + uptr->DATA = dkb_kmod | 030; + return 1; + case SIM_KEY_Y: + uptr->DATA = dkb_kmod | 031; + return 1; + case SIM_KEY_Z: + uptr->DATA = dkb_kmod | 032; + return 1; + case SIM_KEY_BACKQUOTE: /* ` ~ */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = dkb_kmod | 043; + else + uptr->DATA = dkb_kmod | 00; + return 1; + case SIM_KEY_MINUS: /* - not */ + uptr->DATA = dkb_kmod | 055; + return 1; + case SIM_KEY_EQUALS: /* = + */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = dkb_kmod | 053; + else + uptr->DATA = (dkb_kmod | 010) & ~TOP; + return 1; + case SIM_KEY_LEFT_BRACKET: /* [ { */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 017) & ~TOP; + else + uptr->DATA = (dkb_kmod | 050) & ~TOP;; + return 1; + case SIM_KEY_RIGHT_BRACKET: /* ] } */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 020) & ~TOP; + else + uptr->DATA = (dkb_kmod | 051) & ~TOP;; + return 1; + case SIM_KEY_SEMICOLON: /* ; : */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = dkb_kmod | 072 | TOP; + else + uptr->DATA = dkb_kmod | 073 | TOP; + return 1; + case SIM_KEY_SINGLE_QUOTE: /* ok */ /* ' " */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) + uptr->DATA = (dkb_kmod | 031) & ~TOP; + else + uptr->DATA = (dkb_kmod | 011) & ~TOP; + return 1; + case SIM_KEY_BACKSLASH: /* Ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) /* \ | */ + uptr->DATA = (dkb_kmod | 053) & ~TOP; + else if ((dkb_kmod & (TOP|SHFT)) == SHFT) + uptr->DATA = (dkb_kmod | 034) & ~TOP; + else + uptr->DATA = dkb_kmod | 034 | TOP; + return 1; + case SIM_KEY_LEFT_BACKSLASH: + uptr->DATA = dkb_kmod | 034; + return 1; + case SIM_KEY_COMMA: /* ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) /* , < */ + uptr->DATA = (dkb_kmod | 04) & ~TOP; + else + uptr->DATA = dkb_kmod | 054 | TOP; + return 1; + case SIM_KEY_PERIOD: /* Ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) /* . > */ + uptr->DATA = (dkb_kmod | 06) & ~TOP; + else + uptr->DATA = dkb_kmod | 056; + return 1; + case SIM_KEY_SLASH: /* Ok */ + if ((dkb_kmod & (TOP|SHFT)) == TOP) /* / ? */ + uptr->DATA = (dkb_kmod | 056) & ~TOP; + else + uptr->DATA = dkb_kmod | 057; + return 1; + case SIM_KEY_ESC: + uptr->DATA = dkb_kmod | 042; + return 1; + case SIM_KEY_BACKSPACE: + uptr->DATA = dkb_kmod | 074; + return 1; + case SIM_KEY_DELETE: + uptr->DATA = dkb_kmod | 044; + return 1; + case SIM_KEY_TAB: + uptr->DATA = dkb_kmod | 045; + return 1; + case SIM_KEY_ENTER: + uptr->DATA = dkb_kmod | 033; + return 1; + case SIM_KEY_SPACE: + uptr->DATA = dkb_kmod | 040; + return 1; + default: + return 0; + } +} + +int dkb_keyboard (SIM_KEY_EVENT *kev) +{ + sim_debug(DEBUG_DETAIL, &dkb_dev, "DKB key %d %o\n", kev->key, kev->state); + if (dkb_modifiers (kev)) + return 0; + + if (dkb_keys (kev, &dkb_unit[0])) { + dkb_unit[0].DATA |= VALID; + dkb_unit[0].STATUS |= DONE; + set_interrupt(DKB_DEVNUM, dkb_unit[0].PIA); + return 0; + } + + return 1; +} + + +t_stat dkb_svc( UNIT *uptr) +{ + return SCPE_OK; +} + +t_stat dkb_reset( DEVICE *dptr) +{ + if ((dkb_dev.flags & DEV_DIS) == 0) + vid_display_kb_event_process = dkb_keyboard; + dkb_kmod = SHFT|TOP|META|CTRL; + return SCPE_OK; +} + t_stat dkb_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { -return SCPE_OK; + fprintf (stderr, "This is the keyboard input for the Stanford III display\n"); + return SCPE_OK; } const char *dkb_description (DEVICE *dptr) { - return "Console TTY Line"; + return "Keyboard scanner for III display devices"; } #endif diff --git a/PDP10/ka10_dpk.c b/PDP10/ka10_dpk.c index d9e69d98..4f6b9201 100644 --- a/PDP10/ka10_dpk.c +++ b/PDP10/ka10_dpk.c @@ -1,6 +1,6 @@ /* ka10_dpk.c: Systems Concepts DK-10, Datapoint kludge. - Copyright (c) 2018-2019, Lars Brinkhoff + Copyright (c) 2018-2020, Lars Brinkhoff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -65,7 +65,8 @@ #define PORT_INPUT 2 static t_stat dpk_devio(uint32 dev, uint64 *data); -static t_stat dpk_svc (UNIT *uptr); +static t_stat dpk_input_svc (UNIT *uptr); +static t_stat dpk_output_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); @@ -84,9 +85,11 @@ static int dpk_port[16]; static int dpk_ibuf[16]; static int dpk_ird = 0; static int dpk_iwr = 0; +static int dpk_port_done = 0; UNIT dpk_unit[] = { - {UDATA(dpk_svc, TT_MODE_8B|UNIT_IDLE|UNIT_ATTABLE, 0)}, /* 0 */ + {UDATA(dpk_input_svc, TT_MODE_8B|UNIT_IDLE|UNIT_ATTABLE, 0)}, + {UDATA(dpk_output_svc, UNIT_DIS|UNIT_IDLE, 0)}, }; DIB dpk_dib = {DPK_DEVNUM, 1, &dpk_devio, NULL}; @@ -107,14 +110,26 @@ MTAB dpk_mod[] = { DEVICE dpk_dev = { DPK_NAME, dpk_unit, NULL, dpk_mod, - 1, 8, 0, 1, 8, 36, + 2, 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 void dpk_set_ospeed (int port, uint64 data) +{ + static const char *map[] = + { "134", "600", "110", "150", "300", "1200", "2400", "4800" }; + static const char *speed; + speed = map[(data & DPK_SPEED) >> 9]; + sim_debug(DEBUG_CMD, &dpk_dev, "Set port %d output speed %s\n", + port, speed); + tmxr_set_line_speed(&dpk_ldsc[port], speed); +} + static t_stat dpk_devio(uint32 dev, uint64 *data) { + static int scan = 0; DEVICE *dptr = &dpk_dev; int port; @@ -138,22 +153,27 @@ static t_stat dpk_devio(uint32 dev, uint64 *data) break; case DPK_OSTART: dpk_port[port] |= PORT_OUTPUT; - dpk_status &= ~DPK_ODONE; + dpk_port_done &= ~(1 << port); + if (dpk_port_done == 0) + dpk_status &= ~DPK_ODONE; + sim_activate_abs (&dpk_unit[1], 0); break; case DPK_ISTOP: dpk_port[port] &= ~PORT_INPUT; break; case DPK_ISTART: - dpk_port[port] |= PORT_OUTPUT; + dpk_port[port] |= PORT_INPUT; break; case DPK_OSTOP: dpk_port[port] &= ~PORT_OUTPUT; - dpk_status &= ~DPK_ODONE; + dpk_port_done &= ~(1 << port); + if (dpk_port_done == 0) + 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_set_ospeed (port, *data); dpk_port[port] |= PORT_OUTPUT; + sim_activate_abs (&dpk_unit[1], 0); break; case DPK_ISPEED_STOP: dpk_port[port] &= ~PORT_INPUT; @@ -173,13 +193,20 @@ static t_stat dpk_devio(uint32 dev, uint64 *data) dpk_status |= *data & DPK_PIA; break; case CONI|4: + dpk_status &= ~DPK_OLINE; + for (port = 0; port < DPK_LINES; port++) { + scan = (scan + 1) & 017; + if (dpk_port_done & (1 << scan)) { + dpk_status |= scan << 18; + break; + } + } *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; + dpk_ien = ((*data & DPK_IEN) != 0); sim_debug(DEBUG_DATAIO, &dpk_dev, "DATAO %06llo\n", *data); break; case DATAI|4: @@ -230,73 +257,60 @@ static int ildb (uint64 *pointer) return ch; } -static int dpk_output (int port, TMLN *lp) +static void dpk_output (int port, TMLN *lp) { uint64 count; int ch; if ((dpk_port[port] & PORT_OUTPUT) == 0) - return 0; + return; + + if (tmxr_txdone_ln (lp) == 0) + return; if (M[dpk_base + 2*port] == 0777777777777LL) { dpk_port[port] &= ~PORT_OUTPUT; - dpk_status &= ~DPK_OLINE; - dpk_status |= port << 18; dpk_status |= DPK_ODONE; + dpk_port_done |= 1 << port; if (dpk_ien) set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA); - return 0; + return; } ch = ildb (&M[dpk_base + 2*port + 1]); - if (lp->conn) { - ch = sim_tt_outcvt(ch & 0377, TT_GET_MODE (dpk_unit[0].flags)); - tmxr_putc_ln (lp, ch); - } + 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 t_stat dpk_input_svc (UNIT *uptr) { static int scan = 0; + int32 ch; int i; - /* 16 ports at 4800 baud, rounded up. */ - sim_clock_coschedule (uptr, 200); + sim_clock_coschedule (uptr, 1000); 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])) { + ch = tmxr_getc_ln(&dpk_ldsc[scan]); + if (ch & TMXR_VALID) { if ((dpk_port[scan] & PORT_INPUT) == 0) continue; - dpk_ibuf[dpk_iwr++] = (scan << 18) | (tmxr_getc_ln (&dpk_ldsc[scan]) & 0177); + dpk_ibuf[dpk_iwr++] = (scan << 18) | (ch & 0177); dpk_iwr &= 15; dpk_status |= DPK_IDONE; if (dpk_ien) @@ -308,26 +322,42 @@ static t_stat dpk_svc (UNIT *uptr) return SCPE_OK; } +static t_stat dpk_output_svc (UNIT *uptr) +{ + int i; + + for (i = 0; i < DPK_LINES; i++) { + dpk_output (i, &dpk_ldsc[i]); + } + + tmxr_poll_tx (&dpk_desc); + sim_activate_after (uptr, 1000000); + return SCPE_OK; +} + static t_stat dpk_reset (DEVICE *dptr) { int i; 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); + sim_activate (&dpk_unit[0], tmxr_poll); + else { + sim_cancel (&dpk_unit[0]); + sim_cancel (&dpk_unit[1]); + } dpk_ien = 0; dpk_base = 0; dpk_status = 0; dpk_ird = dpk_iwr = 0; memset (dpk_port, 0, sizeof dpk_port); + dpk_port_done = 0; clr_interrupt(DPK_DEVNUM); for (i = 0; i < DPK_LINES; i++) { - tmxr_set_line_unit (&dpk_desc, i, dpk_unit); - tmxr_set_line_output_unit (&dpk_desc, i, dpk_unit); + tmxr_set_line_unit (&dpk_desc, i, &dpk_unit[0]); + tmxr_set_line_output_unit (&dpk_desc, i, &dpk_unit[1]); } return SCPE_OK; @@ -358,7 +388,8 @@ static t_stat dpk_detach (UNIT *uptr) dpk_ldsc[i].xmte = 0; } dpk_status = 0; - sim_cancel (uptr); + sim_cancel (&dpk_unit[0]); + sim_cancel (&dpk_unit[1]); return stat; } diff --git a/PDP10/ka10_iii.c b/PDP10/ka10_iii.c new file mode 100644 index 00000000..0c9cc1be --- /dev/null +++ b/PDP10/ka10_iii.c @@ -0,0 +1,669 @@ +/* ka10_iii.c: Triple III display processor. + + Copyright (c) 2019-2020, 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_III +#define NUM_DEVS_III 0 +#endif + +#if NUM_DEVS_III > 0 +#include "display/display.h" +#include "display/iii.h" + +#define III_DEVNUM 0430 + +#define STATUS u3 +#define MAR u4 +#define PIA u5 +#define POS u6 + +/* CONO Bits */ +#define SET_PIA 000000010 /* Set if this bit is zero */ +#define STOP 000000020 /* Stop processor after instruction */ +#define CONT 000000040 /* Start execution at address */ +#define F 000000100 /* Clear flags */ +#define SET_MSK 000360000 /* Set mask */ +#define RST_MSK 007400000 /* Reset mask */ + +/* CONI Bits */ +#define PIA_MSK 000000007 +#define INST_HLT 000000010 /* 32 - Halt instruction */ +#define WRAP_ENB 000000020 /* 31 - Wrap around mask */ +#define EDGE_ENB 000000040 /* 30 - Edge interrupt mask */ +#define LIGH_ENB 000000100 /* 29 - Light pen enable mask */ +#define CLK_STOP 000000200 /* 28 - Clock stop */ + /* 27 - Not used */ +#define CLK_BIT 000001000 /* 26 - Clock */ +#define NXM_BIT 000002000 /* 25 - Non-existent memory */ +#define IRQ_BIT 000004000 /* 24 - Interrupt pending */ +#define DATAO_LK 000010000 /* 23 - PDP10 gave DATAO when running */ +#define CONT_BIT 000020000 /* 22 - Control bit */ +#define LIGHT_FLG 000040000 /* 21 - Light pen flag */ +#define WRAP_FLG 000100000 /* 20 - Wrap around flag */ +#define EDGE_FLG 000200000 /* 19 - Edge overflow */ +#define HLT_FLG 000400000 /* 18 - Not running */ + +#define WRAP_MSK 00001 +#define EDGE_MSK 00002 +#define LIGH_MSK 00004 +#define HLT_MSK 00010 +#define WRP_FBIT 00020 +#define EDG_FBIT 00040 +#define LIT_FBIT 00100 +#define CTL_FBIT 00200 +#define HLT_FBIT 00400 +#define NXM_FLG 01000 +#define DATA_FLG 02000 +#define RUN_FLG 04000 + +#define TSS_INST 012 /* Test */ +#define LVW_INST 006 /* Long Vector */ +#define SVW_INST 002 /* Short vector */ +#define JMP_INST 000 /* Jump or Halt */ +#define JSR_INST 004 /* JSR(1) or JMS(0), SAVE(3) */ +#define RES_INST 014 /* Restore */ +#define SEL_INST 010 /* Select instruction */ + +#define POS_X 01777400000 +#define POS_Y 00000377700 +#define CBRT 00000000070 /* Current brightness */ +#define CSIZE 00000000007 /* Current char size */ +#define POS_X_V 16 +#define POS_Y_V 6 +#define CBRT_V 3 +#define CSIZE_V 0 + +/* + * Character map. + * M(x,y) moves pointer to x,y. + * V(x,y) draws a vector between current pointer and x,y. + * All characters start at 0,6 and end at 8,6. + * In the map there are up to 18 points per character. For a character a M(0,0) indicates + * that drawing is done and a move to 8,6 should be done. + */ +#define M(x,y) (x << 4)|y|0000 +#define V(x,y) (x << 4)|y|0200 + +uint8 map[128][18] = { + /* Blank */ { 0 }, + /* Down */ { M(0,9), V(3,6), V(3,14), M(3,6), V(6,9) }, + /* Alpha */ { M(6,6), V(3,9), V(1,9), V(0,8), V(0,7), V(1,6), V(3,6), V(6,9) }, + /* Beta */ { V(2,8), V(2,13), V(3,14), V(5,14), V(6,13), V(6,12), V(5,11), + V(2,11), M(5,11), V(6,10), V(6,9), V(5,8), V(3,8), V(2,9) }, + /* ^ */ { M(0,8), V(3,11), V(6,8) }, + /* Not */ { M(0,10), V(6,10), V(6,7) }, + /* Epsilon */ { M(3,9), V(2,10), V(1,10), V(0,9), V(0,7), V(1,6), V(2,6), V(3,7), + M(2,8), V(0,8) }, + /* Pi */ { M(0,10), V(6,10), M(4,10), V(4,6), M(2,6), V(2,10) }, + /* Lambda */ { V(3,9), M(0,11), V(1,11), V(6,6) }, + /* ?? */ { M(0,11), V(1,12), V(2,12), V(5,9), V(5,7), V(4,6), V(3,6), V(2,7), + V(2,8), V(6,12) }, + /* Delta */ { M(2,10), V(1,10), V(0,9), V(0,7), V(1,6), V(3,6), V(4,7), V(4,9), + V(3,10), V(2,10), V(2,12), V(4,12) }, + /* Integ */ { M(0,7), V(1,6), V(2,6), V(3,7), V(3,12), V(4,13), V(5,13), V(6,12) }, + /* PlusMinus */{ M(0,9), V(4,9), M(2,11), V(2,7), M(0,7), V(4,7) }, + /* Circross */ { M(0,8), V(0,7), V(1,6), V(3,6), V(4,7), V(4,9), V(3,10), V(1,10), + V(0,9), V(0,8), V(4,8), M(2,10), V(2,6) }, + /* Sigma */ { M(0,10), V(1,9), V(2,9), V(4,11), V(5,11), V(6,10), V(5,9), V(4,9), + V(2,11), V(1,11), V(0,10) }, + /* Union */ { M(4,8), V(3,9), V(1,9), V(0,8), V(0,7), V(1,6), V(3,6), V(4,7), + V(4,10), V(2,12), V(1,12) }, + /* Intersect */{ M(3,11), V(1,11), V(0,10), V(0,8), V(1,7), V(3,7) }, + /* Cap */ { M(0,11), V(2,11), V(3,10), V(3,8), V(2,7), V(0,7) }, + /* Cup */ { M(0,7), V(0,9), V(1,10), V(3,10), V(4,9), V(4,7) }, + /* A */ { M(0,10), V(0,8), V(1,7), V(3,7), V(4,8), V(4,10) }, + /* E */ { M(0,13), V(0,8), V(2,6), V(4,6), V(6,8), V(6,13), M(0,10), V(6,10) }, + /* cx */ { V(6,6), V(6,14), V(0,14), M(2,10), V(6,10) }, + { V(4,10), M(0,10), V(4,6), M(3,6), V(1,6), V(0,7), V(0,9), V(1,10), + V(3,10), V(4,9), V(4,7), V(3,6) }, + /* Dbl arrow */{ M(2,8), V(0,10), V(2,12), M(0,10), V(6,10), M(4,12), V(6,10), + V(4,8)}, + /* Under */ { M(0,5), V(6,5) }, + { M(0,10), V(6,10), M(3,13), V(6,10), V(3,7) }, + { M(0,12), V(2,14), V(4,12), V(6,14) }, + { V(6,12), M(0,10), V(6,10), M(0,8), V(6,8) }, + { V(3,6), M(3,7), V(0,10), V(3,13) }, + { V(3,6), M(0,7), V(3,10), V(0,13) }, + { M(0,7), V(6,7), M(6,9), V(0,9), M(0,11), V(6,11) }, + { M(0,11), V(3,8), V(6,11) }, + /* Blank */ { 0, }, + /* ! */ { M(2,6), V(2,7), M(2,8), V(2,13) }, + /* " */ { M(2,12), V(2,14), M(4,14), V(4,12) }, + /* # */ { M(2,7), V(2,13), M(4,13), V(4,7), M(6,9), V(0,9), M(0,11), V(6,11) }, + /* $ */ { M(0,8), V(2,6), V(4,6), V(6,8), V(4,10), V(2,10), V(0,12), V(2,14), + V(4,14), V(6,12), M(4,14), V(4,6), M(2,6), V(2,14) }, + /* % */ { V(6,12), V(1,12), V(0,11), V(0,10), V(1,9), V(2,9), V(3,10), V(3,11), + V(2,12), M(4,9), V(3,8), V(3,7), V(4,6), V(5,6), V(6,7), V(6,8), + V(5,9), V(4,9) }, + /* & */ { M(6,6), V(1,11), V(1,13), V(2,14), V(3,14), V(4,13), V(0,9), V(0,7), + V(1,6), V(3,6), V(5,8) }, + /* ' */ { M(2,12), V(4,14) }, + /* ( */ { M(2,6), V(0,8), V(0,12), V(2,14) }, + /* ) */ { V(2,8), V(2,12), V(0,14) }, + /* * */ { M(1,8), V(5,12), M(3,13), V(3,7), M(5,8), V(1,12), M(0,10), + V(6,10) }, + /* + */ { M(2,7), V(2,11), M(0,9), V(4,9) }, + /* , */ { M(0,7), V(1,6), V(1,5), V(0,4) }, + /* - */ { M(0,9), V(4,9) }, + /* . */ { M(2,6), V(3,6), V(3,7), V(2,7), V(2,6) }, + /* / */ { V(6,12) }, + /* 0 */ { M(0,7), V(6,13), M(6,12), V(4,14), V(2,14), V(0,12), V(0,8), V(2,6), + V(4,6), V(6,8), V(6,12) }, + /* 1 */ { M(1,12), V(3,14), V(3,6) }, + /* 2 */ { M(0,13), V(1,14), V(4,14), V(6,12), V(6,11), V(5,10), V(2,10), + V(0,8), V(0,6), V(6,6) }, + /* 3 */ { M(0,14), V(6,14), V(6,12), V(4,10), V(5,10), V(6,9), V(6,7), V(5,6), + V(0,6) }, + /* 4 */ { M(5,6), V(5,14), V(0,9), V(6,9) }, + /* 5 */ { M(0,7), V(1,6), V(4,6), V(6,8), V(6,9), V(5,10), V(1,10), V(0,9), + V(0,14), V(6,14) }, + /* 6 */ { M(0,9), V(1,10), V(5,10), V(6,9), V(6,7), V(5,6), V(1,6), V(0,7), + V(0,10), V(4,14) }, + /* 7 */ { V(6,12), V(6,14), V(0,14) }, + /* 8 */ { M(1,10), V(0,9), V(0,7), V(1,6), V(5,6), V(6,7), V(6,9), V(5,10), + V(6,11), V(6,13), V(5,14), V(1,14), V(0,13), V(0,11), V(1,10), + V(5,10) }, + /* 9 */ { M(2,6), V(6,10), V(6,13), V(5,14), V(1,14), V(0,13), V(0,11), + V(1,10), V(5,10), V(6,11) }, + /* : */ { M(2,6), V(3,6), V(3,7), V(2,7), V(2,6), M(2,10), V(3,10), V(3,11), + V(2,11), V(2,10) }, + /* ; */ { M(2,7), V(3,6), V(3,5), V(2,4), M(2,10), V(3,10), V(3,11), V(2,11), + V(2,10) }, + /* < */ { M(3,7), V(0,10), V(3,13) }, + /* = */ { M(0,8), V(6,8), M(6,10), V(0,10) }, + /* > */ { M(0,7), V(3,10), V(0,13) }, + /* ? */ { M(0,13), V(1,14), V(2,13), V(2,12), V(1,11), V(1,8), M(1,7), + V(1,6) }, + /* @ */ { M(1,6), V(0,7), V(0,11), V(1,12), V(5,12), V(6,11), V(6,8), V(5,7), + V(4,8), V(4,11), M(4,10), V(3,11), V(2,11), V(1,10), V(1,9), V(2,8), + V(3,8), V(4,9) }, + /* A */ { V(0,12), V(2,14), V(4,14), V(6,12), V(6,9), V(0,9), V(6,9), V(6,6) }, + /* B */ { V(0,14), V(5,14), V(6,13), V(6,11), V(5,10), V(0,10), V(5,10), + V(6,9), V(6,7), V(5,6), V(0,6) }, + /* C */ { M(6,13), V(5,14), V(2,14), V(0,12), V(0,8), V(2,6), V(5,6), V(6,7) }, + /* D */ { V(0,14), V(4,14), V(6,12), V(6,8), V(4,6), V(0,6) }, + /* E */ { M(6,6), V(0,6), V(0,10), V(4,10), V(0,10), V(0,14), V(6,14) }, + /* F */ { V(0,10), V(4,10), V(0,10), V(0,14), V(6,14) }, + /* G */ { M(6,13), V(5,14), V(2,14), V(0,12), V(0,8), V(2,6), V(4,6), V(6,8), + V(6,10), V(4,10) }, + /* H */ { V(0,14), V(0,10), V(6,10), V(6,14), V(6,6) }, + /* I */ { M(1,6), V(5,6), V(3,6), V(3,14), V(1,14), V(5,14) }, + /* J */ { M(1,9), V(1,7), V(2,6), V(3,6), V(4,7), V(4,14), V(2,14), V(6,14) }, + /* K */ { V(0,14), V(0,8), V(6,14), V(2,10), V(6,6) }, + /* L */ { M(0,14), V(0,6), V(6,6) }, + /* M */ { V(0,14), V(3,11), V(6,14), V(6,6) }, + /* N */ { V(0,14), V(0,13), V(6,7), V(6,6), V(6,14) }, + /* O */ { M(0,8), V(0,12), V(2,14), V(4,14), V(6,12), V(6,8), V(4,6), V(2,6), + V(0,8) }, + /* P */ { V(0,14), V(5,14), V(6,13), V(6,11), V(5,10), V(0,10) }, + /* Q */ { M(0,8), V(0,12), V(2,14), V(4,14), V(6,12), V(6,8), V(4,6), V(2,6), + V(0,8), M(3,9), V(6,6) }, + /* R */ { V(0,14), V(5,14), V(6,13), V(6,11), V(5,10), V(0,10), V(2,10), + V(6,6) }, + /* S */ { M(0,8), V(2,6), V(4,6), V(6,8), V(4,10), V(2,10), V(0,12), V(2,14), + V(4,14), V(6,12) }, + /* T */ { M(3,6), V(3,14), V(0,14), V(6,14) }, + /* U */ { M(0,14), V(0,7), V(1,6), V(5,6), V(6,7), V(6,14) }, + /* V */ { M(0,14), V(0,9), V(3,6), V(6,9), V(6,14) }, + /* W */ { M(0,14), V(0,6), V(3,9), V(6,6), V(6,14) }, + /* X */ { V(0,7), V(6,13), V(6,14), M(0,14), V(0,13), V(6,7), V(6,6) }, + /* Y */ { M(0,14), V(3,11), V(6,14), V(3,11), V(3,6) }, + /* Z */ { M(0,14), V(6,14), V(6,13), V(0,7), V(0,6), V(6,6) }, + /* [ */ { M(3,5), V(0,5), V(0,15), V(3,15) }, + /* \ */ { M(0,12), V(6,6) }, + /* ] */ { M(0,5), V(3,5), V(3,15), V(0,15) }, + /* up arrow */ { M(0,11), V(3,14), V(6,11), M(3,14), V(3,6) }, + /* left arrow*/{ M(3,7), V(0,10), V(3,13), M(0,10), V(6,10) }, + /* ` */ { M(2,14), V(4,12) }, + /* a */ { M(0,9), V(1,10), V(3,10), V(4,9), V(4,6), M(4,8), V(3,9), V(1,9), + V(0,8), V(0,7), V(1,6), V(3,6), V(4,7) }, + /* b */ { V(0,13), M(0,9), V(1,10), V(3,10), V(4,9), V(4,7), V(3,6), V(1,6), + V(0,7) }, + /* c */ { M(4,9), V(3,10), V(1,10), V(0,9), V(0,7), V(1,6), V(3,6), V(4,7) }, + /* d */ { M(0,7), V(0,9), V(1,10), V(3,10), V(4,9), V(4,7), V(3,6), V(1,6), + V(0,7), M(4,6), V(4,13) }, + /* e */ { M(4,7), V(3,6), V(1,6), V(0,7), V(0,9), V(1,10), V(3,10), V(4,9), + V(4,8), V(0,8) }, + /* f */ { M(2,6), V(2,12), V(3,13), V(4,13), V(5,12), M(0,11), V(4,11) }, + /* g */ { M(4,9), V(3,10), V(1,10), V(0,9), V(0,7), V(1,6), V(3,6), V(4,7), + M(4,10), V(4,5), V(3,4), V(1,4), V(0,5) }, + /* h */ { V(0,13), M(0,9), V(1,10), V(3,10), V(4,9), V(4,6) }, + /* i */ { M(3,12), V(3,11), M(3,10), V(3,7), V(4,6), V(5,6) }, + /* k */ { M(3,12), V(3,11), M(3,10), V(3,5), V(2,4), V(1,3) }, + /* j */ { V(0,13), M(0,8), V(2,10), M(0,8), V(2,6) }, + /* l */ { M(2,6), V(2,13) }, + /* m */ { V(0,10), M(0,9), V(1,10), V(2,10), V(3,9), V(3,6), M(3,9), V(4,10), + V(5,10), V(6,9), V(6,6) }, + /* n */ { V(0,10), M(0,9), V(1,10), V(2,10), V(3,9), V(3,6) }, + /* o */ { M(0,7), V(0,9), V(1,10), V(3,10), V(4,9), V(4,7), V(3,6), V(1,6), + V(0,7) }, + /* p */ { M(0,4), V(0,10), M(0,9), V(1,10), V(3,10), V(4,9), V(4,7), V(3,6), + V(1,6), V(0,7) }, + /* q */ { M(4,9), V(3,10), V(1,10), V(0,9), V(0,7), V(1,6), V(3,6), V(4,7), + M(4,10), V(4,4) }, + /* r */ { V(0,10), M(0,9), V(1,10), V(3,10), V(4,9) }, + /* s */ { M(0,7), V(1,6), V(3,6), V(4,7), V(3,8), V(1,8), V(0,9), V(1,10), + V(3,10), V(4,9) }, + /* t */ { M(2,13), V(2,7), V(3,6), V(4,6), V(5,7), M(1,11), V(3,11) }, + /* u */ { M(0,10), V(0,7), V(1,6), V(3,6), V(4,7), V(4,10), V(4,6) }, + /* v */ { M(0,9), V(3,6), V(6,9) }, + /* w */ { M(0,10), V(0,6), V(2,8), V(4,6), V(4,10) }, + /* x */ { V(4,10), M(0,10), V(4,6) }, + /* y */ { M(0,9), V(3,6), M(6,9), V(1,4), V(0,4) }, + /* z */ { M(0,10), V(4,10), V(0,6), V(4,6) }, + /* { */ { M(3,15), V(2,14), V(2,12), V(0,10), V(2,8), V(2,6), V(3,5) }, + /* | */ { M(2,4), V(2,14) }, + /* diamon */ { M(3,6), V(0,9), V(3,12), V(6,9), V(3,6) }, + /* } */ { M(0,15), V(1,14), V(1,12), V(3,10), V(1,8), V(1,6), V(0,5) }, + /* \ */ { M(0,12), V(6,6) }, +}; + +float scale[] = { 1.0F, + 1.0F, /* 128 chars per line */ + 1.3F, /* 96 chars per line */ + 2.0F, /* 64 chars per line */ + 2.5F, /* 48 chars per line */ + 4.0F, /* 32 chars per line */ + 5.3F, /* 24 chars per line */ + 8.0F /* 16 chars per line */ +}; + + +uint64 iii_instr; /* Currently executing instruction */ +int iii_sel; /* Select mask */ + +t_stat iii_devio(uint32 dev, uint64 *data); +t_stat iii_svc(UNIT *uptr); +t_stat iii_reset(DEVICE *dptr); +static void draw_point(int x, int y, int b, UNIT *uptr); +static void draw_line(int x1, int y1, int x2, int y2, int b, UNIT *uptr); +t_stat iii_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *iii_description (DEVICE *dptr); + +DIB iii_dib = { III_DEVNUM, 1, iii_devio, NULL}; + +UNIT iii_unit[] = { + {UDATA (&iii_svc, UNIT_IDLE, 0) }, + { 0 } + }; + + +MTAB iii_mod[] = { + { 0 } + }; + +DEVICE iii_dev = { + "III", iii_unit, NULL, iii_mod, + 2, 10, 31, 1, 8, 8, + NULL, NULL, iii_reset, + NULL, NULL, NULL, &iii_dib, DEV_DEBUG | DEV_DISABLE | DEV_DIS, 0, dev_debug, + NULL, NULL, &iii_help, NULL, NULL, &iii_description + }; + +t_stat iii_devio(uint32 dev, uint64 *data) { + UNIT *uptr = &iii_unit[0]; + switch(dev & 3) { + case CONI: + *data = (((uint64)iii_sel) << 18) | (uint64)(uptr->PIA); + if ((iii_instr & 037) == 0) + *data |= INST_HLT; + *data |= (uptr->STATUS & 07) << 4; + if (uptr->STATUS & NXM_FLG) + *data |= NXM_BIT; + if (uptr->STATUS & DATA_FLG) + *data |= DATAO_LK; + if ((uptr->STATUS & RUN_FLG) == 0) + *data |= HLT_FLG; + if (uptr->STATUS & CTL_FBIT) + *data |= CONT_BIT; + if (uptr->STATUS & WRP_FBIT) + *data |= WRAP_FLG; + if (uptr->STATUS & EDG_FBIT) + *data |= EDGE_FLG; + if (uptr->STATUS & LIT_FBIT) + *data |= LIGHT_FLG; + sim_debug(DEBUG_CONI, &iii_dev, "III %03o CONI %06o %06o\n", dev, (uint32)*data, + PC); + break; + case CONO: + clr_interrupt(III_DEVNUM); + if (*data & SET_PIA) + uptr->PIA = (int)(*data&PIA_MSK); + if (*data & F) + uptr->STATUS &= ~(WRP_FBIT|EDG_FBIT|LIT_FBIT|DATA_FLG|NXM_FLG); + uptr->STATUS &= ~(017 & ((*data >> 14) ^ (*data >> 10))); + uptr->STATUS ^= (017 & (*data >> 10)); + if (*data & STOP) + uptr->STATUS &= ~RUN_FLG; + if (*data & CONT) { + uptr->STATUS |= RUN_FLG; + iii_instr = M[uptr->MAR]; + sim_activate(uptr, 10); + } + if (((uptr->STATUS >> 3) & (uptr->STATUS & (WRAP_MSK|EDGE_MSK|LIGH_MSK))) != 0) + set_interrupt(III_DEVNUM, uptr->PIA); + if (uptr->STATUS & HLT_MSK) + set_interrupt(III_DEVNUM, uptr->PIA); + sim_debug(DEBUG_CONO, &iii_dev, "III %03o CONO %06o %06o\n", dev, + (uint32)*data, PC); + break; + case DATAI: + sim_debug(DEBUG_DATAIO, &iii_dev, "III %03o DATAI %06o\n", dev, (uint32)*data); + break; + case DATAO: + if (uptr->STATUS & RUN_FLG) + uptr->STATUS |= DATA_FLG; + else { + iii_instr = *data; + sim_activate(uptr, 10); + } + sim_debug(DEBUG_DATAIO, &iii_dev, "III %03o DATAO %06o\n", dev, (uint32)*data); + break; + } + return SCPE_OK; +} + +t_stat +iii_svc (UNIT *uptr) +{ + uint64 temp; + int A; + int ox, oy, nx, ny, br, sz; + int i, j, ch; + float ch_sz; + + iii_cycle(10, 0); + + /* Extract X,Y,Bright and Size */ + sz = (uptr->POS & CSIZE) >> CSIZE_V; + br = (uptr->POS & CBRT) >> CBRT_V; + ox = (uptr->POS & POS_X) >> POS_X_V; + oy = (uptr->POS & POS_Y) >> POS_Y_V; + nx = ox = (ox ^ 02000) - 02000; + ny = oy = (oy ^ 02000) - 02000; + ch_sz = scale[sz]; + + sim_debug(DEBUG_DETAIL, &iii_dev, "III: pos %d %d %d %d %o\n", ox, oy, br, sz, + uptr->STATUS ); + switch(iii_instr & 017) { + case 000: /* JMP and HLT */ + if (iii_instr & 020) { + uptr->MAR = (iii_instr >> 18) & RMASK; + } else { + uptr->STATUS &= ~RUN_FLG; + if (uptr->STATUS & HLT_MSK) + set_interrupt(III_DEVNUM, uptr->PIA); + return SCPE_OK; + } + goto skip_up; + case 001: + case 003: + case 005: + case 007: + case 011: + case 013: + case 015: + case 017: /* Draw 4 characters */ + for (i = 29; i >= 1; i -= 7) { + /* Extract character and compute initial point */ + int cx, cy; + int lx, ly; + ch = (iii_instr >> i) & 0177; + cx = 0; + cy = (int)(6.0 * ch_sz); + lx = ox; + ly = oy + cy; + sim_debug(DEBUG_DETAIL, &iii_dev, "III: ch %d %d %o '%c' %o %o\n", + lx, ly, ch, (ch < ' ')? '.' : ch, sz, br); + if (ch == '\t' || ch == 0) + continue; + if (ch == '\r') { + ox = -512; + continue; + } + if (ch == '\n') { + oy -= (int)(16.0 * ch_sz); + continue; + } + /* Scan map and draw lines as needed */ + if ((iii_sel & 04000) != 0) { + for(j = 0; j < 18; j++) { + uint8 v = map[ch][j]; + if (v == 0) + break; + cx = (int)((float)((v >> 4) & 07) * ch_sz); + cy = (int)((float)(v & 017) * ch_sz); + nx = ox + cx; + ny = oy + cy; + sim_debug(DEBUG_DATA, &iii_dev, "III: map %d %d %d %d %02x\n", + lx, ly, nx, ny, v); + if (v & 0200) + draw_line(lx, ly, nx, ny, br, uptr); + lx = nx; + ly = ny; + } + } + ox += (int)(8.0 * ch_sz); + } + nx = ox; + ny = oy; + break; + + case 002: /* Short Vector */ + if ((iii_sel & 04000) == 0) + break; + /* Do first point */ + nx = (iii_instr >> 26) & 077; + ny = (iii_instr >> 20) & 077; + /* Sign extend */ + nx = (nx ^ 040) - 040; + ny = (ny ^ 040) - 040; + /* Compute relative position. */ + sim_debug(DEBUG_DETAIL, &iii_dev, "III: short %d %d %o %d\n", + nx, ny, sz, br); + nx += ox; + ny += oy; + if (nx < -512 || nx > 512 || ny < -512 || ny > 512) + uptr->STATUS |= EDG_FBIT; + i = (int)((iii_instr >> 18) & 3); + if ((i & 02) == 0 && (iii_sel & 04000) != 0) { /* Check if visible */ + if ((i & 01) == 0) { /* Draw a line */ + draw_line(ox, oy, nx, ny, br, uptr); + } else { + draw_point(nx, ny, br, uptr); + } + } + ox = nx; + oy = ny; + /* Do second point */ + nx = (iii_instr >> 12) & 0177; + ny = (iii_instr >> 6) & 0177; + /* Sign extend */ + nx = (nx ^ 040) - 040; + ny = (ny ^ 040) - 040; + sim_debug(DEBUG_DETAIL, &iii_dev, "III: short2 %d %d %o %d\n", + nx, ny, sz, br); + /* Compute relative position. */ + nx += ox; + ny += oy; + if (nx < -512 || nx > 512 || ny < -512 || ny > 512) + uptr->STATUS |= EDG_FBIT; + /* Check if visible */ + if ((iii_instr & 040) == 0 && (iii_sel & 04000) != 0) { + if ((iii_instr & 020) == 0) { /* Draw a line */ + draw_line(ox, oy, nx, ny, br, uptr); + } else { + draw_point(nx, ny, br, uptr); + } + } + break; + + case 004: /* JSR, JMS, SAVE */ + temp = (((uint64)uptr->MAR) << 18) | 020 /* | CPC */; + A = (iii_instr >> 18) & RMASK; + if ((iii_instr & 030) != 030) { + M[A] = temp; + A++; + } + if ((iii_instr & 020) != 020) { + temp = uptr->STATUS & 0377; + temp |= ((uint64)uptr->POS) << 8; + M[A] = temp; + A++; + } + if ((iii_instr & 030) != 030) { + uptr->MAR = A; + } + goto skip_up; + + case 006: /* Long Vector */ + /* Update sizes if needed */ + if (((iii_instr >> 8) & CSIZE) != 0) + sz = (iii_instr >> 8) & CSIZE; + if (((iii_instr >> 11) & 7) != 0) + br = (iii_instr > 11) & 7; + nx = (iii_instr >> 25) & 03777; + ny = (iii_instr >> 14) & 03777; + nx = (nx ^ 02000) - 02000; + ny = (ny ^ 02000) - 02000; + sim_debug(DEBUG_DETAIL, &iii_dev, "III: long %d %d %o %o\n", + nx, ny, sz, br); + if ((iii_instr & 0100) == 0) { /* Relative mode */ + nx += ox; + ny += oy; + if (nx < -512 || nx > 512 || ny < -512 || ny > 512) + uptr->STATUS |= EDG_FBIT; + } + /* Check if visible */ + if ((iii_instr & 040) == 0 && (iii_sel & 04000) != 0) { + if ((iii_instr & 020) == 0) /* Draw a line */ + draw_line(ox, oy, nx, ny, br, uptr); + else + draw_point(nx, ny, br, uptr); + } + break; + + case 010: /* Select instruction */ + i = (iii_instr >> 24) & 07777; /* Set mask */ + j = (iii_instr >> 12) & 07777; /* Reset mask */ + ch = i & j; /* Compliment mask */ + i &= ~ch; + j &= ~ch; + iii_sel = ((iii_sel | i) & ~j) ^ ch; + goto skip_up; + + case 012: /* Test instruction */ + A = (uptr->STATUS & (int32)(iii_instr >> 12) & 0377) != 0; + j = (int)((iii_instr >> 20) & 0377); /* set mask */ + i = (int)((iii_instr >> 28) & 0377); /* Reset */ + uptr->STATUS &= ~(i ^ j); + uptr->STATUS ^= j; + if (A ^ ((iii_instr & 020) != 0)) + uptr->MAR++; + goto skip_up; + + case 014: /* Restore */ + A = (iii_instr >> 18) & RMASK; + temp = M[A]; + if ((iii_instr & 020) != 0) { + uptr->STATUS &= ~0377; + uptr->STATUS |= temp & 0377; + } + if ((iii_instr & 040) != 0) { + uptr->POS = (temp >> 8) & (POS_X|POS_Y|CBRT|CSIZE); + } + goto skip_up; + + case 016: /* Nop */ + break; + } + /* Repack to new position. */ + sim_debug(DEBUG_DATA, &iii_dev, "III: update %d %d %8o ", nx, ny, uptr->POS); + uptr->POS = (POS_X & ((nx & 03777) << POS_X_V)) | + (POS_Y & ((ny & 03777) << POS_Y_V)) | + (CBRT & (br << CBRT_V)) | + (CSIZE & (sz << CSIZE_V)); + sim_debug(DEBUG_DATA, &iii_dev, "-> %8o\n", uptr->POS); +skip_up: + if (uptr->STATUS & RUN_FLG) { + iii_instr = M[uptr->MAR]; + sim_debug(DEBUG_DETAIL, &iii_dev, "III: fetch %06o %012llo\n", + uptr->MAR, iii_instr); + uptr->MAR++; + uptr->MAR &= RMASK; + sim_activate(uptr, 50); + } + + if (((uptr->STATUS >> 3) & (uptr->STATUS & (WRAP_MSK|EDGE_MSK|LIGH_MSK))) != 0) + set_interrupt(III_DEVNUM, uptr->PIA); + + return SCPE_OK; +} + +t_stat iii_reset (DEVICE *dptr) +{ + if (dptr->flags & DEV_DIS) { + display_close(dptr); + } else { + display_reset(); + dptr->units[0].POS = 0; + iii_init(dptr, 1); + } + return SCPE_OK; +} + +/* Draw a point at x,y with intensity b. */ +/* X and Y runs from -512 to 512. */ +static void +draw_point(int x, int y, int b, UNIT *uptr) +{ + if (x < -512 || x > 512 || y < -512 || y > 512) + uptr->STATUS |= WRP_FBIT; + iii_point(x, y, b); +} + +/* Draw a line between two points */ +static void +draw_line(int x1, int y1, int x2, int y2, int b, UNIT *uptr) +{ + if (x1 < -512 || x1 > 512 || y1 < -512 || y1 > 512) + uptr->STATUS |= WRP_FBIT; + if (x2 < -512 || x2 > 512 || y2 < -512 || y2 > 512) + uptr->STATUS |= WRP_FBIT; + iii_draw_line(x1, y1, x2, y2, b); +} + +t_stat iii_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +return SCPE_OK; +} + +const char *iii_description (DEVICE *dptr) +{ + return "Triple III Display"; +} +#endif diff --git a/PDP10/ka10_imx.c b/PDP10/ka10_imx.c index 918a5007..b4623b12 100644 --- a/PDP10/ka10_imx.c +++ b/PDP10/ka10_imx.c @@ -1,6 +1,6 @@ /* ka10_imx.c: Input multplexor for A/D. - Copyright (c) 2018, Lars Brinkhoff + Copyright (c) 2018,2020, Lars Brinkhoff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -25,6 +25,7 @@ #include #include "kx10_defs.h" +#include "sim_video.h" #ifndef NUM_DEVS_IMX #define NUM_DEVS_IMX 0 @@ -47,33 +48,78 @@ #define IMX_CHANNEL 0000177 -t_stat imx_devio(uint32 dev, uint64 *data); -const char *imx_description (DEVICE *dptr); +#define JOY_MAX_UNITS 4 +#define JOY_MAX_AXES 4 +#define JOY_NO_CHAN (IMX_CHANNEL + 1) + +t_stat imx_devio(uint32 dev, uint64 *data); +t_stat imx_svc (UNIT *uptr); +t_stat imx_reset (DEVICE *dptr); +const char *imx_description (DEVICE *dptr); +#if MPX_DEV +t_stat imx_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) ; +t_stat imx_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +#endif +t_stat imx_show_channel (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat imx_set_channel (UNIT* uptr, int32 val, CONST char* cptr, void* desc); static uint64 status = IMX_ASSIGNED; +static uint64 imx_data; +static uint64 imx_samples; static int initial_channel = 0; static int current_channel = 0; +static int imx_mpx_lvl; + +static int imx_inputs[0200]; +static int imx_map[JOY_MAX_UNITS][JOY_MAX_AXES]; UNIT imx_unit[] = { - {UDATA(NULL, UNIT_DISABLE, 0)}, /* 0 */ + { UDATA (&imx_svc, UNIT_IDLE, 0) } }; DIB imx_dib = {IMX_DEVNUM, 1, &imx_devio, NULL}; MTAB imx_mod[] = { +#if MPX_DEV + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "MPX", "MPX", + &imx_set_mpx, &imx_show_mpx, NULL}, +#endif + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CHANNEL", "CHANNEL", + &imx_set_channel, &imx_show_channel, NULL}, { 0 } }; DEVICE imx_dev = { "IMX", imx_unit, NULL, imx_mod, 1, 8, 0, 1, 8, 36, - NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, imx_reset, NULL, NULL, NULL, &imx_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL, NULL, NULL, NULL, NULL, NULL, &imx_description }; +static void imx_joy_motion(int which, int axis, int value) +{ + int chan; + + if (which < JOY_MAX_UNITS && axis < JOY_MAX_AXES) { + chan = imx_map[which][axis]; + if (chan == JOY_NO_CHAN) + return; + + value += 32768; + + if (chan < 0) { + chan = -chan; + value = 65535 - value; + } + imx_inputs[chan] = value >> 5; + sim_debug(DEBUG_DETAIL, &imx_dev, "Channel %d value %o\n", + chan, imx_inputs[chan]); + } +} + static int imx_sample (void) { - int sample = 2048; + int sample = imx_inputs[current_channel]; if (status & IMX_SEQUENCE) current_channel = (current_channel + 1) & IMX_CHANNEL; else @@ -81,36 +127,217 @@ static int imx_sample (void) return sample; } +static void imx_activate (void) +{ + int micros; + + if (status & IMX_DONE) { + sim_cancel (imx_unit); + sim_debug(DEBUG_IRQ, &imx_dev, "Cancel\n"); + return; + } + + micros = (status >> 16) & 0377; + if (micros < 10) + micros = 10; + sim_activate_after (imx_unit, micros); + sim_debug(DEBUG_IRQ, &imx_dev, "Activate\n"); +} + +t_stat imx_reset (DEVICE *dptr) +{ + static int init = 1; + int i, j; + + if (dptr->flags & DEV_DIS) { + imx_samples = 0; + imx_data = 0; + + for (i = 0; i <= IMX_CHANNEL; i++) + imx_inputs[i] = 1000; + + for (i = 0; i < JOY_MAX_UNITS; i++) { + for (j = 0; j < JOY_MAX_UNITS; j++) + imx_map[i][j] = JOY_NO_CHAN; + } + } else { + if (init) { + vid_register_gamepad_motion_callback (imx_joy_motion); + init = 0; + } + } + return SCPE_OK; +} + t_stat imx_devio(uint32 dev, uint64 *data) { DEVICE *dptr = &imx_dev; switch(dev & 07) { case CONO|4: - status &= ~IMX_CONO; + sim_debug(DEBUG_CONO, &imx_dev, "%06llo\n", *data); + status &= ~(IMX_CONO|IMX_DONE); status |= *data & IMX_CONO; + imx_data = 0; + imx_samples = 0; current_channel = initial_channel; + clr_interrupt (IMX_DEVNUM); + imx_activate (); break; case CONI|4: - status |= IMX_DONE; *data = status & IMX_CONI; + sim_debug(DEBUG_CONI, &imx_dev, "%012llo\n", *data); break; case DATAO|4: + sim_debug(DEBUG_DATAIO, &imx_dev, "DATAO %012llo\n", *data); 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(); - } + *data = imx_data; + sim_debug(DEBUG_DATAIO, &imx_dev, "DATAI %012llo\n", *data); + imx_data = 0; + imx_samples = 0; + status &= ~IMX_DONE; + clr_interrupt (IMX_DEVNUM); + sim_debug(DEBUG_IRQ, &imx_dev, "Clear interrupt\n"); + imx_activate (); break; } return SCPE_OK; } +t_stat imx_svc (UNIT *uptr) +{ + int max_samples; + + if (status & IMX_PACK) { + max_samples = 3; + } else { + max_samples = 1; + } + + if (imx_samples < max_samples) { + imx_data <<= 12; + imx_data |= imx_sample(); + imx_samples++; + } + + if (imx_samples == max_samples) { + status |= IMX_DONE; + if (status & 7) { + set_interrupt_mpx (IMX_DEVNUM, status & 7, imx_mpx_lvl); + sim_debug(DEBUG_IRQ, &imx_dev, "Raise interrupt\n"); + } + } + + imx_activate (); + + return SCPE_OK; +} + +#if MPX_DEV +/* set MPX level number */ +t_stat imx_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; + imx_mpx_lvl = mpx; + return SCPE_OK; +} + +t_stat imx_show_mpx (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + + fprintf (st, "MPX=%o", imx_mpx_lvl); + return SCPE_OK; +} +#endif + +t_stat imx_set_channel (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int chan, unit, axis, negate = 0; + char gbuf[CBUFSIZE]; + CONST char *tptr; + t_stat r; + + if (cptr == NULL || *cptr == 0) + return SCPE_ARG; + + tptr = get_glyph (cptr, gbuf, ';'); + if (tptr == NULL) + return SCPE_ARG; + chan = (int) get_uint (gbuf, 8, IMX_CHANNEL, &r); + if (r != SCPE_OK) + return r; + + tptr = get_glyph (tptr, gbuf, ';'); + if (tptr == NULL || strncasecmp (gbuf, "unit", 4) != 0) + return SCPE_ARG; + unit = (int) get_uint (gbuf + 4, 10, JOY_MAX_UNITS - 1, &r); + if (r != SCPE_OK) + return r; + + tptr = get_glyph (tptr, gbuf, ';'); + if (strncasecmp (gbuf, "axis", 4) != 0) + return SCPE_ARG; + axis = (int) get_uint (gbuf + 4, 10, JOY_MAX_AXES - 1, &r); + if (r != SCPE_OK) + return r; + + if (*tptr != 0) { + if (strcasecmp (tptr, "negate") != 0) + return SCPE_ARG; + negate = 1; + } + + if (negate) + chan = -chan; + imx_map[unit][axis] = chan; + + return SCPE_OK; +} + +t_stat imx_show_channel (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int nothing = 1; + const char *negate, *comma = ""; + int chan, i, j; + + if (uptr == NULL) + return SCPE_IERR; + + for (i = 0; i < JOY_MAX_UNITS; i++) { + for (j = 0; j < JOY_MAX_AXES; j++) { + chan = imx_map[i][j]; + if (chan != JOY_NO_CHAN) { + if (chan < 0) { + chan = -chan; + negate = ";NEGATE"; + } else + negate = ""; + + fprintf (st, "%sCHANNEL=%o;JOY%d;AXIS%d%s", comma, chan, i, j, negate); + comma = ", "; + nothing = 0; + } + } + } + + if (nothing) + fprintf (st, "CHANNEL=(NO MAPPINGS)"); + + return SCPE_OK; +} + const char *imx_description (DEVICE *dptr) { return "A/D input multiplexor"; diff --git a/PDP10/ka10_pd.c b/PDP10/ka10_pd.c index d428cec9..47438ab9 100644 --- a/PDP10/ka10_pd.c +++ b/PDP10/ka10_pd.c @@ -47,8 +47,6 @@ #define PIA_FLG 07 #define CLK_IRQ 010 -#define TMR_PD 3 - int pd_tps = 60; t_stat pd_devio(uint32 dev, uint64 *data); @@ -126,9 +124,6 @@ t_stat pd_devio(uint32 dev, uint64 *data) t_stat pd_srv(UNIT * uptr) { - int32 t; - - t = sim_rtcn_calb (pd_tps, TMR_PD); sim_activate_after(uptr, 1000000/pd_tps); if (uptr->PIA_CH & PIA_FLG) { uptr->PIA_CH |= CLK_IRQ; @@ -139,7 +134,6 @@ pd_srv(UNIT * uptr) return SCPE_OK; } - const char *pd_description (DEVICE *dptr) { return "Paul DeCoriolis clock"; diff --git a/PDP10/ka10_pmp.c b/PDP10/ka10_pmp.c index ea3e69b9..95311c38 100644 --- a/PDP10/ka10_pmp.c +++ b/PDP10/ka10_pmp.c @@ -1,6 +1,6 @@ /* PMP disk controller interface for WAITS. - Copyright (c) 2017, Richard Cornwell + Copyright (c) 2017-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -142,7 +142,7 @@ #define DK_MSK_SKNONE 0x18 /* Allow no seeks */ #define DK_MSK_SK 0x18 /* Seek mask */ -#define POS u4 +#define POS u4 /* u4 */ /* Holds the current track and head */ #define DK_V_TRACK 8 @@ -348,9 +348,9 @@ 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 */ +t_addr pmp_addr_hold; /* Address register hold */ uint32 pmp_wc; /* Current word count register */ -uint32 pmp_addr; /* Current address register */ +t_addr pmp_addr; /* Current address register */ uint64 pmp_data; /* Data assembly register */ int pmp_cnt; /* Character count in asm register */ int pmp_cmd; /* Current command */ @@ -382,7 +382,7 @@ t_stat pmp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); const char *pmp_description (DEVICE *dptr); -DIB pmp_dib[] = { +DIB pmp_dib[] = { {PMP_DEV, 2, &pmp_devio, NULL}}; @@ -433,13 +433,13 @@ pmp_devio(uint32 dev, uint64 *data) { 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; + *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); @@ -550,22 +550,25 @@ pmp_checkirq() { 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)) { + 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) { + } + 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))); + 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; @@ -597,10 +600,10 @@ chan_read_byte(uint8 *data) { 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) + if (Mem_read_word(pmp_addr, &pmp_data, 0)) 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); + 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 */ @@ -616,10 +619,10 @@ chan_read_byte(uint8 *data) { if ((pmp_cnt & 0xf) > 0x3) { if ((pmp_cnt & 0xf) == 0x4) { /* Split byte */ byte = (pmp_data << 4) & 0xf0; - if (pmp_addr >= (int)MEMSIZE) + if (Mem_read_word(pmp_addr, &pmp_data, 0)) 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); + 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; @@ -639,30 +642,9 @@ chan_read_byte(uint8 *data) { } else if (xfer) { pmp_wc ++; } - if (pmp_wc & 07000000) + 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 */ @@ -693,11 +675,11 @@ chan_write_byte(uint8 *data) { pmp_cnt |= BUFF_DIRTY; if ((pmp_cnt & 03) == 0) { pmp_cnt &= ~(BUFF_DIRTY|7); - if (pmp_addr >= (int)MEMSIZE) + if (Mem_write_word(pmp_addr, &pmp_data, 0)) 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++; + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_write %06o %012llo\n", + pmp_addr, pmp_data); + pmp_addr++; xfer = 1; } } else { @@ -705,10 +687,10 @@ chan_write_byte(uint8 *data) { if ((pmp_cnt & 0xf) == 0x4) { /* Split byte */ pmp_data &= ~0xf; pmp_data |= (uint64)((*data >> 4) & 0xf); - if (pmp_addr >= (int)MEMSIZE) + if (Mem_write_word(pmp_addr, &pmp_data, 0)) 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); + 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; @@ -726,10 +708,10 @@ chan_write_byte(uint8 *data) { pmp_cnt++; if ((pmp_cnt & 0xf) == 9) { pmp_cnt = BUFF_EMPTY; - if (pmp_addr >= (int)MEMSIZE) + if (Mem_write_word(pmp_addr, &pmp_data, 0)) 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); + 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 */ } @@ -774,16 +756,15 @@ chan_end(uint8 flags) { /* Flush buffer if there was any change */ if (pmp_cnt & BUFF_DIRTY) { pmp_cnt = BUFF_EMPTY; - if (pmp_addr >= (int)MEMSIZE) { + if (Mem_write_word(pmp_addr, &pmp_data, 0)) { (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_statusb |= IDLE_CH; pmp_status |= NEW_STS | CHN_END | ((uint64)flags) << 5; if (pmp_status & (BSY|UNIT_CHK)) @@ -792,13 +773,14 @@ chan_end(uint8 flags) { /* 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); + 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(); @@ -808,7 +790,8 @@ chan_end(uint8 flags) { /* Indicate that device is done */ pmp_statusb &= ~OP1; } - sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_endf %012llo %06o\n", pmp_status, pmp_statusb); + sim_debug(DEBUG_DETAIL, &pmp_dev, "chan_endf %012llo %06o\n", + pmp_status, pmp_statusb); (void)pmp_checkirq(); } @@ -959,7 +942,8 @@ pmp_startcmd() { 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); + sim_debug(DEBUG_CMD, &pmp_dev, "CMD unit=%d CMD=%02x\n", unit, + pmp_cur_unit->CMD); return; case 0x0: /* Status */ @@ -1233,8 +1217,8 @@ index: /* 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); + 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; @@ -1256,8 +1240,8 @@ index: 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); + sim_debug(DEBUG_DETAIL, dptr, "seek next unit=%d %d %d %x\n", unit, + uptr->POS >> 8, data->cyl, data->state); break; } @@ -1449,7 +1433,6 @@ sense_end: 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; @@ -1598,8 +1581,9 @@ sense_end: 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]); + 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; } @@ -1770,7 +1754,8 @@ sense_end: 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", + 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); } @@ -2251,7 +2236,8 @@ pmp_attach(UNIT * uptr, CONST char *file) 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) { + 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; @@ -2292,12 +2278,14 @@ pmp_attach(UNIT * uptr, CONST char *file) 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; + if ((sim_switches & SIM_SW_REST) == 0) { + (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; } diff --git a/PDP10/ka10_stk.c b/PDP10/ka10_stk.c index 98911033..1297777c 100644 --- a/PDP10/ka10_stk.c +++ b/PDP10/ka10_stk.c @@ -387,7 +387,8 @@ t_stat stk_devio(uint32 dev, uint64 *data) static t_stat stk_reset (DEVICE *dptr) { - vid_display_kb_event_process = stk_keyboard; + if ((stk_dev.flags & DEV_DIS) == 0) + vid_display_kb_event_process = stk_keyboard; return SCPE_OK; } diff --git a/PDP10/ka10_ten11.c b/PDP10/ka10_ten11.c index 1ff255bb..df87ab9c 100644 --- a/PDP10/ka10_ten11.c +++ b/PDP10/ka10_ten11.c @@ -239,7 +239,7 @@ static int transaction (unsigned char *request, unsigned char *response) return 0; } -static int read_word (int addr, int *data) +static int read_word (t_addr addr, int *data) { unsigned char request[8]; unsigned char response[8]; @@ -285,7 +285,7 @@ static int read_word (int addr, int *data) return 0; } -int ten11_read (int addr, uint64 *data) +int ten11_read (t_addr addr, uint64 *data) { int offset = addr & 01777; int word1, word2; @@ -328,7 +328,7 @@ int ten11_read (int addr, uint64 *data) return 0; } -static int write_word (int addr, uint16 data) +static int write_word (t_addr addr, uint16 data) { unsigned char request[8]; unsigned char response[8]; @@ -366,7 +366,7 @@ static int write_word (int addr, uint16 data) return 0; } -int ten11_write (int addr, uint64 data) +int ten11_write (t_addr addr, uint64 data) { int offset = addr & 01777; diff --git a/PDP10/kl10_fe.c b/PDP10/kl10_fe.c new file mode 100644 index 00000000..b2593963 --- /dev/null +++ b/PDP10/kl10_fe.c @@ -0,0 +1,2484 @@ +/* kl10_fe.c: KL-10 front end (console terminal) simulator + + Copyright (c) 2019-2020, 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" +#include + +#if KL +#define UNIT_DUMMY (1 << UNIT_V_UF) + +#define DTE_DEVNUM 0200 +#define DEV_V_OS (DEV_V_UF + 1) /* Type RSX10/RSX20 */ +#define DEV_M_OS (1 << DEV_V_OS) +#define TYPE_RSX10 (0 << DEV_V_OS) +#define TYPE_RSX20 (1 << DEV_V_OS) + + + +/* DTE10 CONI bits */ + +#define DTE_RM 00100000 /* Restricted mode */ +#define DTE_D11 00040000 /* Dead-11 */ +#define DTE_11DB 00020000 /* TO11 Door bell request */ +#define DTE_10DB 00001000 /* TO10 Door bell request */ +#define DTE_11ER 00000400 /* Error during TO11 transfer */ +#define DTE_11DN 00000100 /* TO 11 transfer done */ +#define DTE_10DN 00000040 /* TO 10 transfer done */ +#define DTE_10ER 00000020 /* Error during TO10 transfer */ +#define DTE_PIE 00000010 /* PIO enabled */ +#define DTE_PIA 00000007 /* PI channel assigment */ + +/* internal flags */ +#define DTE_11RELD 01000000 /* Reload 11. */ +#define DTE_TO11 02000000 /* Transfer to 11 */ +#define DTE_SEC 04000000 /* In secondary protocol */ +#define DTE_IND 010000000 /* Next transfer will be indirect */ +#define DTE_SIND 020000000 /* Send indirect data next */ + +/* DTE CONO bits */ +#define DTE_CO11DB 0020000 /* Set TO11 Door bell */ +#define DTE_CO11CR 0010000 /* Clear reload 11 button */ +#define DTE_CO11SR 0004000 /* Set reload 11 button */ +#define DTE_CO10DB 0001000 /* Clear TO10 Door bell */ +#define DTE_CO11CL 0000100 /* Clear TO11 done and error */ +#define DTE_CO10CL 0000040 /* Clear TO10 done and error */ +#define DTE_PIENB 0000020 /* Load PI and enable bit */ + +/* DTE DATAO */ +#define DTE_TO10IB 010000 /* Interrupt after transfer */ +#define DTE_TO10BC 007777 /* Byte count for transfer */ + +/* Secondary protocol addresses */ +#define SEC_DTFLG 0444 /* Operation complete flag */ +#define SEC_DTCLK 0445 /* Clock interrupt flag */ +#define SEC_DTCI 0446 /* Clock interrupt instruction */ +#define SEC_DTT11 0447 /* 10 to 11 argument */ +#define SEC_DTF11 0450 /* 10 from 11 argument */ +#define SEC_DTCMD 0451 /* To 11 command word */ +#define SEC_DTSEQ 0452 /* Operation sequence number */ +#define SEC_DTOPR 0453 /* Operational DTE # */ +#define SEC_DTCHR 0454 /* Last typed character */ +#define SEC_DTMTD 0455 /* Monitor tty output complete flag */ +#define SEC_DTMTI 0456 /* Monitor tty input flag */ +#define SEC_DTSWR 0457 /* 10 switch register */ + +#define SEC_PGMCTL 00400 +#define SEC_ENDPASS 00404 +#define SEC_LOOKUP 00406 +#define SEC_RDWRD 00407 +#define SEC_RDBYT 00414 +#define SEC_ESEC 00440 +#define SEC_EPRI 00500 +#define SEC_ERTM 00540 +#define SEC_CLKCTL 01000 +#define SEC_CLKOFF 01000 +#define SEC_CLKON 01001 +#define SEC_CLKWT 01002 +#define SEC_CLKRD 01003 +#define SEC_RDSW 01400 +#define SEC_CLRDDT 03000 +#define SEC_SETDDT 03400 +#define SEC_MONO 04000 +#define SEC_MONON 04400 +#define SEC_SETPRI 05000 +#define SEC_RTM 05400 +#define SEC_CMDMSK 07400 +#define DTE_MON 00000001 /* Save in unit1 STATUS */ +#define SEC_CLK 00000002 /* Clock enabled */ +#define ITS_ON 00000004 /* ITS Is alive */ + +/* Primary or Queued protocol addresses */ +#define PRI_CMTW_0 0 +#define PRI_CMTW_PPT 1 /* Pointer to com region */ +#define PRI_CMTW_STS 2 /* Status word */ +#define PRI_CMT_PWF SMASK /* Power failure */ +#define PRI_CMT_L11 BIT1 /* Load 11 */ +#define PRI_CMT_INI BIT2 /* Init */ +#define PRI_CMT_TST BIT3 /* Valid examine bit */ +#define PRI_CMT_QP 020000000LL /* Do Queued protocol */ +#define PRI_CMT_FWD 001000000LL /* Do full word transfers */ +#define PRI_CMT_IP RSIGN /* Indirect transfer */ +#define PRI_CMT_TOT 0200000LL /* TOIT bit */ +#define PRI_CMT_10IC 0177400LL /* TO10 IC for queued transfers */ +#define PRI_CMT_11IC 0000377LL /* TO11 IC for queued transfers */ +#define PRI_CMTW_CNT 3 /* Queue Count */ +#define PRI_CMTW_KAC 5 /* Keep alive count */ +#define PRI_IND_FLG 0100000 /* Flag function as indirect */ + +#define PRI_EM2EI 001 /* Initial message to 11 */ +#define PRI_EM2TI 002 /* Replay to initial message. */ +#define PRI_EMSTR 003 /* String data */ +#define PRI_EMLNC 004 /* Line-Char */ +#define PRI_EMRDS 005 /* Request device status */ +#define PRI_EMOPS 006 +#define PRI_EMHDS 007 /* Here is device status */ +#define PRI_EMRDT 011 /* Request Date/Time */ +#define PRI_EMHDR 012 /* Here is date and time */ +#define PRI_EMFLO 013 /* Flush output */ +#define PRI_EMSNA 014 /* Send all (ttys) */ +#define PRI_EMDSC 015 /* Dataset connect */ +#define PRI_EMHUD 016 /* Hang up dataset */ +#define PRI_EMLBE 017 /* Acknowledge line */ +#define PRI_EMXOF 020 /* XOFF line */ +#define PRI_EMXON 021 /* XON line */ +#define PRI_EMHLS 022 /* Here is line speeds */ +#define PRI_EMHLA 023 /* Here is line allocation */ +#define PRI_EMRBI 024 /* Reboot information */ +#define PRI_EMAKA 025 /* Ack ALL */ +#define PRI_EMTDO 026 /* Turn device On/Off */ +#define PRI_EMEDR 027 /* Enable/Disable line */ +#define PRI_EMLDR 030 /* Load LP RAM */ +#define PRI_EMLDV 031 /* Load LP VFU */ + +#define PRI_EMCTY 001 /* Device code for CTY */ +#define PRI_EMDL1 002 /* DL11 */ +#define PRI_EMDH1 003 /* DH11 #1 */ +#define PRI_EMDLS 004 /* DLS (all ttys combined) */ +#define PRI_EMLPT 005 /* Front end LPT */ +#define PRI_EMCDR 006 /* CDR */ +#define PRI_EMCLK 007 /* Clock */ +#define PRI_EMFED 010 /* Front end device */ +#define PRI_CTYDV 000 /* Line number for CTY */ +#define NUM_DLS 5 /* Number of first DH Line */ + +#if KL_ITS +/* ITS Timesharing protocol locations */ +#define ITS_DTEVER 0400 /* Protocol version and number of devices */ +#define ITS_DTECHK 0401 /* Increment at 60Hz. Ten setom 2 times per second */ +#define ITS_DTEINP 0402 /* Input from 10 to 11. Line #, Count */ +#define ITS_DTEOUT 0403 /* Output from 10 to 11 Line #, Count */ +#define ITS_DTELSP 0404 /* Line # to set speed of */ +#define ITS_DTELPR 0405 /* Parameter */ +#define ITS_DTEOST 0406 /* Line # to start output on */ +#define ITS_DTETYI 0410 /* Received char (Line #, char) */ +#define ITS_DTEODN 0411 /* Output done (Line #, buffer size) */ +#define ITS_DTEHNG 0412 /* Hangup/dialup */ +#endif + +extern int32 tmxr_poll; +t_stat dte_devio(uint32 dev, uint64 *data); +t_addr dte_devirq(uint32 dev, t_addr addr); +void dte_second(UNIT *uptr); +void dte_primary(UNIT *uptr); +#if KL_ITS +void dte_its(UNIT *uptr); +#endif +void dte_transfer(UNIT *uptr); +void dte_function(UNIT *uptr); +void dte_input(); +int dte_start(UNIT *uptr); +int dte_queue(int func, int dev, int dcnt, uint16 *data); +t_stat dtei_svc (UNIT *uptr); +t_stat dte_svc (UNIT *uptr); +t_stat dteo_svc (UNIT *uptr); +t_stat dtertc_srv(UNIT * uptr); +t_stat dte_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat dte_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat dte_reset (DEVICE *dptr); +t_stat dte_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 dte_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *dte_description (DEVICE *dptr); +extern uint64 SW; /* Switch register */ + +CONST char *pri_name[] = { "(0)", "EM2EI", "EM2TI", "EMSTR", "EMLNC", "EMRDS", "(6)", + "EMHDS", "(10)", "EMRDT", "EMHDR", "EMFLO", "EMSNA", "EMDSC", "EMHUD", + "EMLBE", "EMXOF", "EMXON", "EMHLS", "EMHLA", "EMRBI", "EMAKA", "EMTDO", + "EMEDR", "EMLDR", "EMLDV" }; + +#if KL_ITS +#define QITS (cpu_unit[0].flags & UNIT_ITSPAGE) +#else +#define QITS 0 +#endif + +#define STATUS u3 +#define CNT u4 + +extern uint32 eb_ptr; +static int32 rtc_tps = 60; +uint16 rtc_tick; +uint16 rtc_wait = 0; + +struct _dte_queue { + int dptr; /* Pointer to working item */ + uint16 cnt; /* Number of bytes in packet */ + uint16 func; /* Function code */ + uint16 dev; /* Dev code */ + uint16 spare; /* Dev code */ + uint16 dcnt; /* Data count */ + uint16 data[258]; /* Data packet */ + uint16 sdev; /* Secondary device code */ + uint16 sz; /* Byte size */ +} dte_in[32], dte_out[32]; + +int32 dte_in_ptr; +int32 dte_in_cmd; +int32 dte_out_ptr; +int32 dte_out_res; +int32 dte_base; /* Base */ +int32 dte_off; /* Our offset */ +int32 dte_dt10_off; /* Offset to 10 deposit region */ +int32 dte_et10_off; /* Offset to 10 examine region */ +int32 dte_et11_off; /* Offset to 11 examine region */ +int32 dte_proc_num; /* Our processor number */ + +struct _buffer { + int in_ptr; /* Insert pointer */ + int out_ptr; /* Remove pointer */ + char buff[256]; /* Buffer */ +} cty_in, cty_out; +int32 cty_done; + +#define full(q) ((((q)->in_ptr + 1) & 0xff) == (q)->out_ptr) +#define empty(q) ((q)->in_ptr == (q)->out_ptr) +#define not_empty(q) ((q)->in_ptr != (q)->out_ptr) +#define inco(q) (q)->out_ptr = ((q)->out_ptr + 1) & 0xff +#define inci(q) (q)->in_ptr = ((q)->in_ptr + 1) & 0xff + +DIB dte_dib[] = { + { DTE_DEVNUM|000, 1, dte_devio, dte_devirq}, +}; + +MTAB dte_mod[] = { + { UNIT_DUMMY, 0, NULL, "STOP", &dte_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 }, + {MTAB_XTD|MTAB_VDV, TYPE_RSX10, NULL, "RSX10", &dte_set_type, NULL, + NULL, "Sets DTE to RSX10 mode"}, + {MTAB_XTD|MTAB_VDV, TYPE_RSX20, "RSX20", "RSX20", &dte_set_type, &dte_show_type, + NULL, "Sets DTE to RSX20 mode"}, + { 0 } + }; + +UNIT dte_unit[] = { + { UDATA (&dte_svc, TT_MODE_7B, 0), 100}, + { UDATA (&dteo_svc, TT_MODE_7B, 0), 100}, + { UDATA (&dtei_svc, TT_MODE_7B|UNIT_DIS, 0), 1000 }, + { UDATA (&dtertc_srv, UNIT_IDLE|UNIT_DIS, 0), 1000 } + }; + +REG dte_reg[] = { + {SAVEDATA(IN, dte_in) }, + {SAVEDATA(OUT, dte_out) }, + {HRDATA(IN_PTR, dte_in_ptr, 32), REG_HRO}, + {HRDATA(IN_CMD, dte_in_cmd, 32), REG_HRO}, + {HRDATA(OUT_PTR, dte_out_ptr, 32), REG_HRO}, + {HRDATA(OUT_RES, dte_out_res, 32), REG_HRO}, + {HRDATA(BASE, dte_base, 32), REG_HRO}, + {HRDATA(OFF, dte_off, 32), REG_HRO}, + {HRDATA(DTOFF, dte_dt10_off, 32), REG_HRO}, + {HRDATA(ETOFF, dte_et10_off, 32), REG_HRO}, + {HRDATA(E1OFF, dte_et11_off, 32), REG_HRO}, + {HRDATA(PROC, dte_proc_num, 32), REG_HRO}, + {SAVEDATA(CTYIN, cty_in) }, + {SAVEDATA(CTYOUT, cty_out) }, + {HRDATA(DONE, cty_done, 8), REG_HRO}, + {HRDATAD(WRU, sim_int_char, 8, "interrupt character") }, + { 0 }, + }; + + +DEVICE dte_dev = { + "CTY", dte_unit, dte_reg, dte_mod, + 4, 10, 31, 1, 8, 8, + NULL, NULL, &dte_reset, + NULL, NULL, NULL, &dte_dib, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dte_help, NULL, NULL, &dte_description + }; + + + +#ifndef NUM_DEVS_LP20 +#define NUM_DEVS_LP20 0 +#endif + +#if (NUM_DEVS_LP20 > 0) + +#define COL u4 +#define POS u5 +#define LINE u6 +#define LPST us9 +#define LPCNT us10 + +#define EOFFLG 001 /* Tops 20 wants EOF */ +#define HDSFLG 002 /* Tell Tops 20 The current device status */ +#define ACKFLG 004 /* Post an acknowwledge message */ +#define INTFLG 010 /* Send interrupt */ +#define DELFLG 020 /* Previous character was delimiter */ + +#define MARGIN 6 + +#define UNIT_V_CT (UNIT_V_UF + 0) +#define UNIT_UC (1 << UNIT_V_CT) +#define UNIT_CT (3 << UNIT_V_CT) + + + +t_stat lp20_svc (UNIT *uptr); +t_stat lp20_reset (DEVICE *dptr); +t_stat lp20_attach (UNIT *uptr, CONST char *cptr); +t_stat lp20_detach (UNIT *uptr); +t_stat lp20_setlpp(UNIT *, int32, CONST char *, void *); +t_stat lp20_getlpp(FILE *, UNIT *, int32, CONST void *); +t_stat lp20_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *lp20_description (DEVICE *dptr); + +char lp20_buffer[134 * 3]; + +#define LP20_RAM_RAP 010000 /* RAM Parity */ +#define LP20_RAM_INT 04000 /* Interrrupt bit */ +#define LP20_RAM_DEL 02000 /* Delimiter bit */ +#define LP20_RAM_TRN 01000 /* Translation bite */ +#define LP20_RAM_PI 00400 /* Paper Instruction */ +#define LP20_RAM_CHR 00377 /* Character translation */ + +uint16 lp20_vfu[256]; +uint16 lp20_ram[256]; +uint16 lp20_dvfu[] = { /* Default VFU */ + /* 66 line page with 6 line margin */ + 00377, /* Line 0 8 7 6 5 4 3 2 1 */ + 00220, /* Line 1 8 5 */ + 00224, /* Line 2 8 5 3 */ + 00230, /* Line 3 8 5 4 */ + 00224, /* Line 4 8 5 3 */ + 00220, /* Line 5 8 5 */ + 00234, /* Line 6 8 5 4 3 */ + 00220, /* Line 7 8 5 */ + 00224, /* Line 8 8 5 3 */ + 00230, /* Line 9 8 5 4 */ + 00264, /* Line 10 8 6 5 3 */ + 00220, /* Line 11 8 5 */ + 00234, /* Line 12 8 5 4 3 */ + 00220, /* Line 13 8 5 */ + 00224, /* Line 14 8 5 3 */ + 00230, /* Line 15 8 5 4 */ + 00224, /* Line 16 8 5 3 */ + 00220, /* Line 17 8 5 */ + 00234, /* Line 18 8 5 4 3 */ + 00220, /* Line 19 8 5 */ + 00364, /* Line 20 8 7 6 5 3 */ + 00230, /* Line 21 8 5 4 */ + 00224, /* Line 22 8 5 3 */ + 00220, /* Line 23 8 5 */ + 00234, /* Line 24 8 5 4 3 */ + 00220, /* Line 25 8 5 */ + 00224, /* Line 26 8 5 3 */ + 00230, /* Line 27 8 5 4 */ + 00224, /* Line 28 8 5 3 */ + 00220, /* Line 29 8 5 */ + 00276, /* Line 30 8 6 5 4 3 2 */ + 00220, /* Line 31 8 5 */ + 00224, /* Line 32 8 5 3 */ + 00230, /* Line 33 8 5 4 */ + 00224, /* Line 34 8 5 3 */ + 00220, /* Line 35 8 5 */ + 00234, /* Line 36 8 5 4 3 */ + 00220, /* Line 37 8 5 */ + 00224, /* Line 38 8 5 3 */ + 00230, /* Line 39 8 5 4 */ + 00364, /* Line 40 8 7 6 5 3 */ + 00220, /* Line 41 8 5 */ + 00234, /* Line 42 8 5 4 3 */ + 00220, /* Line 43 8 5 */ + 00224, /* Line 44 8 5 3 */ + 00230, /* Line 45 8 5 4 */ + 00224, /* Line 46 8 5 3 */ + 00220, /* Line 47 8 5 */ + 00234, /* Line 48 8 5 4 3 */ + 00220, /* Line 49 8 5 */ + 00264, /* Line 50 8 6 5 3 */ + 00230, /* Line 51 8 5 4 */ + 00224, /* Line 52 8 5 3 */ + 00220, /* Line 53 8 5 */ + 00234, /* Line 54 8 5 4 3 */ + 00220, /* Line 55 8 5 */ + 00224, /* Line 56 8 5 3 */ + 00230, /* Line 57 8 5 4 */ + 00224, /* Line 58 8 5 3 */ + 00220, /* Line 59 8 5 */ + 00020, /* Line 60 5 */ + 00020, /* Line 61 5 */ + 00020, /* Line 62 5 */ + 00020, /* Line 63 5 */ + 00020, /* Line 64 5 */ + 04020, /* Line 65 12 5 */ + 010000, /* End of form */ +}; + +struct _buffer lp20_queue; + +/* LPT data structures + + lp20_dev LPT device descriptor + lp20_unit LPT unit descriptor + lp20_reg LPT register list +*/ + +UNIT lp20_unit = { + UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 66), 100 + }; + +REG lp20_reg[] = { + {BRDATA(BUFFER, lp20_buffer, 16, 8, sizeof(lp20_buffer)), REG_HRO}, + {BRDATA(VFU, lp20_vfu, 16, 16, (sizeof(lp20_vfu)/sizeof(uint16))), REG_HRO}, + {BRDATA(RAM, lp20_ram, 16, 16, (sizeof(lp20_ram)/sizeof(uint16))), REG_HRO}, + {SAVEDATA(QUEUE, lp20_queue) }, + { NULL } +}; + +MTAB lp20_mod[] = { + {UNIT_CT, 0, "Lower case", "LC", NULL}, + {UNIT_CT, UNIT_UC, "Upper case", "UC", NULL}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lp20_setlpp, &lp20_getlpp, NULL, "Number of lines per page"}, + { 0 } +}; + +DEVICE lp20_dev = { + "LP20", &lp20_unit, lp20_reg, lp20_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp20_reset, + NULL, &lp20_attach, &lp20_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &lp20_help, NULL, NULL, &lp20_description +}; +#endif + +#ifndef NUM_DEVS_TTY +#define NUM_DEVS_TTY 0 +#endif + +#if (NUM_DEVS_TTY > 0) + +struct _buffer tty_out[NUM_LINES_TTY], tty_in[NUM_LINES_TTY]; +TMLN tty_ldsc[NUM_LINES_TTY] = { 0 }; /* Line descriptors */ +TMXR tty_desc = { NUM_LINES_TTY, 0, 0, tty_ldsc }; +int32 tty_connect[NUM_LINES_TTY]; +int32 tty_done[NUM_LINES_TTY]; +int tty_enable = 0; +extern int32 tmxr_poll; + +t_stat ttyi_svc (UNIT *uptr); +t_stat ttyo_svc (UNIT *uptr); +t_stat tty_reset (DEVICE *dptr); +t_stat tty_set_modem (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat tty_show_modem (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat tty_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat tty_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat tty_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat tty_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat tty_attach (UNIT *uptr, CONST char *cptr); +t_stat tty_detach (UNIT *uptr); +t_stat tty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *tty_description (DEVICE *dptr); + +/* TTY data structures + + tty_dev TTY device descriptor + tty_unit TTY unit descriptor + tty_reg TTY register list +*/ + +UNIT tty_unit[] = { + { UDATA (&ttyi_svc, TT_MODE_7B+UNIT_IDLE+UNIT_DISABLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT}, + { UDATA (&ttyo_svc, TT_MODE_7B+UNIT_IDLE+UNIT_DIS, 0), KBD_POLL_WAIT}, + }; + +REG tty_reg[] = { + { DRDATA (TIME, tty_unit[0].wait, 24), REG_NZ + PV_LEFT }, + { SAVEDATA (OUT, tty_out) }, + { SAVEDATA (IN, tty_in) }, + { BRDATA (CONN, tty_connect, 8, 32, sizeof(tty_connect)/sizeof(int32)), REG_HRO }, + { BRDATA (DONE, tty_done, 8, 32, sizeof(tty_done)/sizeof(int32)), REG_HRO }, + { ORDATA (EN, tty_enable, 1), REG_HRO }, + { 0 } + }; + +MTAB tty_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, &tty_desc, "Disconnect a specific line" }, + { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, + NULL, &tmxr_show_summ, (void *) &tty_desc, "Display a summary of line states" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &tty_desc, "Display current connections" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &tty_desc, "Display multiplexer statistics" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &tty_setnl, &tmxr_show_lines, (void *) &tty_desc, "Set number of lines" }, + { MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file", + &tty_set_log, NULL, (void *)&tty_desc }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG", + &tty_set_nolog, NULL, (void *)&tty_desc, "Disable logging on designated line" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL, + NULL, &tty_show_log, (void *)&tty_desc, "Display logging for all lines" }, + { 0 } + }; + +DEVICE tty_dev = { + "TTY", tty_unit, tty_reg, tty_mod, + 2, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &tty_reset, + NULL, &tty_attach, &tty_detach, + NULL, DEV_NET | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &tty_help, NULL, NULL, &tty_description + }; +#endif + + +t_stat dte_devio(uint32 dev, uint64 *data) { + uint32 res; + switch(dev & 3) { + case CONI: + *data = (uint64)(dte_unit[0].STATUS) & RMASK; + sim_debug(DEBUG_CONI, &dte_dev, "CTY %03o CONI %06o\n", dev, (uint32)*data); + break; + case CONO: + res = (uint32)(*data & RMASK); + clr_interrupt(dev); + if (res & DTE_PIENB) { + dte_unit[0].STATUS &= ~(DTE_PIA|DTE_PIE); + dte_unit[0].STATUS |= res & (DTE_PIA|DTE_PIE); + } + if (res & DTE_CO11CL) + dte_unit[0].STATUS &= ~(DTE_11DN|DTE_11ER); + if (res & DTE_CO10CL) { + dte_unit[0].STATUS &= ~(DTE_10DN|DTE_10ER); + dte_start(&dte_unit[0]); + } + if (res & DTE_CO10DB) + dte_unit[0].STATUS &= ~(DTE_10DB); + if (res & DTE_CO11CR) + dte_unit[0].STATUS &= ~(DTE_11RELD); + if (res & DTE_CO11SR) + dte_unit[0].STATUS |= (DTE_11RELD); + if (res & DTE_CO11DB) { + sim_debug(DEBUG_CONO, &dte_dev, "CTY Ring 11 DB\n"); + dte_unit[0].STATUS |= DTE_11DB; + sim_activate(&dte_unit[0], 200); + } + if (dte_unit[0].STATUS & (DTE_10DB|DTE_11DN|DTE_10DN|DTE_11ER|DTE_10ER)) + set_interrupt(dev, dte_unit[0].STATUS); + sim_debug(DEBUG_CONO, &dte_dev, "CTY %03o CONO %06o %06o\n", dev, + (uint32)*data, PC); + break; + case DATAI: + sim_debug(DEBUG_DATAIO, &dte_dev, "CTY %03o DATAI %06o\n", dev, + (uint32)*data); + break; + case DATAO: + sim_debug(DEBUG_DATAIO, &dte_dev, "CTY %03o DATAO %06o\n", dev, + (uint32)*data); + if (*data == 01365) { + dte_unit[0].STATUS |= DTE_SEC|DTE_10ER; + dte_unit[0].STATUS &= ~(DTE_10DB|DTE_IND|DTE_11DB); + break; + } + dte_unit[0].CNT = (*data & (DTE_TO10IB|DTE_TO10BC)); + dte_unit[0].STATUS |= DTE_TO11; + sim_activate(&dte_unit[0], 10); + break; + } + return SCPE_OK; +} + +/* Handle KL style interrupt vectors */ +t_addr +dte_devirq(uint32 dev, t_addr addr) { + return 0142; +} + +/* Handle TO11 interrupts */ +t_stat dte_svc (UNIT *uptr) +{ + /* Did the 10 knock? */ + if (uptr->STATUS & DTE_11DB) { + /* If in secondary mode, do that protocol */ + if (uptr->STATUS & DTE_SEC) + dte_second(uptr); + else + dte_primary(uptr); /* Retrieve data */ + } else if (uptr->STATUS & DTE_TO11) { + /* Does 10 want us to send it what we have? */ + dte_transfer(uptr); + } + return SCPE_OK; +} + +/* Handle secondary protocol */ +void dte_second(UNIT *uptr) { + uint64 word; + int32 ch; + uint32 base = 0; + +#if KI_22BIT +#if KL_ITS + if (!QITS) +#endif + base = eb_ptr; +#endif + /* read command */ + word = M[SEC_DTCMD + base]; +#if KL_ITS + if (word == 0 && QITS && (uptr->STATUS & ITS_ON) != 0) { + dte_its(uptr); + uptr->STATUS &= ~DTE_11DB; + return; + } +#endif + /* Do it */ + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY secondary %012llo\n", word); + switch(word & SEC_CMDMSK) { + default: + case SEC_MONO: /* Ouput character in monitor mode */ + ch = (int32)(word & 0177); + if (full(&cty_out)) { + sim_activate(uptr, 200); + return; + } + if (ch != 0) { + cty_out.buff[cty_out.in_ptr] = ch & 0x7f; + inci(&cty_out); + sim_activate(&dte_unit[1], 200); + } + M[SEC_DTCHR + base] = ch; + M[SEC_DTMTD + base] = FMASK; + break; + + case SEC_SETPRI: +enter_pri: + if (Mem_examine_word(0, 0, &word)) + break; + dte_proc_num = (word >> 24) & 037; + dte_base = dte_proc_num + 1; + dte_off = dte_base + (word & 0177777); + dte_dt10_off = 16; + dte_et10_off = dte_dt10_off + 16; + dte_et11_off = dte_base + 16; + uptr->STATUS &= ~DTE_SEC; + dte_in_ptr = dte_out_ptr = 0; + dte_in_cmd = dte_out_res = 0; + cty_done = 0; + /* Start input process */ + M[SEC_DTCMD + base] = 0; + M[SEC_DTFLG + base] = FMASK; + uptr->STATUS &= ~DTE_11DB; + return; + + case SEC_SETDDT: /* Read character from console */ + if (empty(&cty_in)) { + M[SEC_DTF11 + base] = 0; + M[SEC_DTMTI + base] = FMASK; + break; + } + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + M[SEC_DTF11 + base] = 0177 & ch; + M[SEC_DTMTI + base] = FMASK; + break; + + case SEC_CLRDDT: /* Clear DDT input mode */ + uptr->STATUS &= ~DTE_MON; + break; + + case SEC_MONON: + uptr->STATUS |= DTE_MON; + break; + + case SEC_RDSW: /* Read switch register */ + M[SEC_DTSWR + base] = SW; + M[SEC_DTF11 + base] = SW; + break; + + case SEC_PGMCTL: /* Program control: Used by KLDCP */ + switch(word) { + case SEC_ENDPASS: + case SEC_LOOKUP: + case SEC_RDWRD: + case SEC_RDBYT: + break; + case SEC_ESEC: + goto enter_pri; + case SEC_EPRI: + case SEC_ERTM: + break; + } + break; + + case SEC_CLKCTL: /* Clock control: Used by KLDCP */ + switch(word) { + case SEC_CLKOFF: + dte_unit[3].STATUS &= ~SEC_CLK; + break; + case SEC_CLKWT: + rtc_wait = (uint16)(M[SEC_DTT11 + base] & 0177777); + /* Fall Through */ + + case SEC_CLKON: + dte_unit[3].STATUS |= SEC_CLK; + rtc_tick = 0; + break; + case SEC_CLKRD: + M[SEC_DTF11+base] = rtc_tick; + break; + } + break; + } + /* Acknowledge command */ + M[SEC_DTCMD + base] = 0; + M[SEC_DTFLG + base] = FMASK; + uptr->STATUS &= ~DTE_11DB; + if (dte_dev.flags & TYPE_RSX20) { + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, dte_unit[0].STATUS); + } +} + +#if KL_ITS +/* Process ITS Ioeleven locations */ +void dte_its(UNIT *uptr) { + uint64 word; + char ch; + uint16 data; + int cnt; + int ln; + + /* Check for input Start */ + word = M[ITS_DTEINP]; + if ((word & SMASK) == 0) { + M[ITS_DTEINP] = FMASK; + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEINP = %012llo\n", word); + } + /* Check for output Start */ + word = M[ITS_DTEOUT]; + if ((word & SMASK) == 0) { + cnt = word & 017777; + ln = ((word >> 18) & 077) - 1; + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEOUT = %012llo\n", word); + while (cnt > 0) { + if (ln < 0) { + if (full(&cty_out)) + return; + if (!Mem_read_byte(0, &data, 1)) + return; + ch = data & 0177; + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY queue %x\n", ch); + cty_out.buff[cty_out.in_ptr] = ch; + inci(&cty_out); + cnt--; + if (! sim_is_active(&dte_unit[1])) + sim_activate(&dte_unit[1], 50); +#if (NUM_DEVS_TTY > 0) + } else { + struct _buffer *otty = &tty_out[ln]; + if (full(otty)) + return; + if (!Mem_read_byte(0, &data, 1)) + return; + ch = data & 0177; + sim_debug(DEBUG_DETAIL, &dte_dev, "TTY queue %x %d\n", ch, ln); + otty->buff[otty->in_ptr] = ch; + inci(otty); + cnt--; +#endif + } + } + M[ITS_DTEOUT] = FMASK; + uptr->STATUS |= DTE_11DN; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEOUT = %012llo\n", word); + } + /* Check for line speed */ + word = M[ITS_DTELSP]; + if ((word & SMASK) == 0) { /* Ready? */ + M[ITS_DTELSP] = FMASK; + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTELSP = %012llo %012llo\n", word, M[ITS_DTELPR]); + } + dte_input(); + /* Check for output Start */ + word = M[ITS_DTEOST]; + if ((word & SMASK) == 0) { + if (word == 0) + cty_done++; +#if (NUM_DEVS_TTY > 0) + else if (word > 0 && word < tty_desc.lines) { + tty_done[word-1] = 1; + } +#endif + M[ITS_DTEOST] = FMASK; + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEOST = %012llo\n", word); + } +} +#endif + +/* Handle primary protocol */ +void dte_primary(UNIT *uptr) { + uint64 word, iword; + int s; + int cnt; + struct _dte_queue *in; + uint16 data1, *dp; + + if ((uptr->STATUS & DTE_11DB) == 0) + return; + + /* Check if there is room for another packet */ + if (((dte_in_ptr + 1) & 0x1f) == dte_in_cmd) { + /* If not reschedule ourselves */ + sim_activate(uptr, 100); + return; + } + uptr->STATUS &= ~(DTE_11DB); + clr_interrupt(DTE_DEVNUM); + /* Check status word to see if valid */ + if (Mem_examine_word(0, dte_et11_off + PRI_CMTW_STS, &word)) { + uint32 base; +error: + base = 0; +#if KI_22BIT +#if KL_ITS + if (!QITS) +#endif + base = eb_ptr; +#endif + /* If we can't read it, go back to secondary */ + M[SEC_DTFLG + base] = FMASK; + uptr->STATUS |= DTE_SEC; + uptr->STATUS &= ~DTE_11DB; + if (dte_dev.flags & TYPE_RSX20) { + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, dte_unit[0].STATUS); + } + sim_debug(DEBUG_DETAIL, &dte_dev, "DTE: error %012llo\n", word); + return; + } + + if ((word & PRI_CMT_QP) == 0) { + goto error; + } + in = &dte_in[dte_in_ptr]; + /* Check if indirect */ + if ((word & PRI_CMT_IP) != 0) { + /* Transfer from 10 */ + if ((uptr->STATUS & DTE_IND) == 0) { + fprintf(stderr, "DTE out of sync\n\r"); + return; + } + /* Get size of transfer */ + if (Mem_examine_word(0, dte_et11_off + PRI_CMTW_CNT, &iword)) + goto error; + sim_debug(DEBUG_EXP, &dte_dev, "DTE: count: %012llo\n", iword); + in->dcnt = (uint16)(iword & 0177777); + /* Read in data */ + dp = &in->data[0]; + for (cnt = in->dcnt; cnt > 0; cnt --) { + /* Read in data */ + s = Mem_read_byte(0, dp, 0); + if (s == 0) + goto error; + in->sz = s; + sim_debug(DEBUG_DATA, &dte_dev, + "DTE: Read Idata: %06o %03o %03o %06o cnt=%o\n", + *dp, *dp >> 8, *dp & 0377, + ((*dp & 0377) << 8) | ((*dp >> 8) & 0377), cnt); + dp++; + if (s <= 8) + cnt--; + } + uptr->STATUS &= ~DTE_IND; + dte_in_ptr = (dte_in_ptr + 1) & 0x1f; + } else { + /* Transfer from 10 */ + in->dptr = 0; + in->dcnt = 0; + /* Read in count */ + if (!Mem_read_byte(0, &data1, 0)) + goto error; + in->cnt = data1; + cnt = in->cnt-2; + if (!Mem_read_byte(0, &data1, 0)) + goto error; + in->func = data1; + cnt -= 2; + if (!Mem_read_byte(0, &data1, 0)) + goto error; + in->dev = data1; + cnt -= 2; + if (!Mem_read_byte(0, &data1, 0)) + goto error; + in->spare = data1; + cnt -= 2; + sim_debug(DEBUG_DATA, &dte_dev, "DTE: Read CMD: %o c=%o f=%o %s d=%o\n", + dte_in_ptr, in->cnt, in->func, + ((in->func & 0377) > PRI_EMLDV)?"***": + pri_name[in->func & 0377], in->dev); + dp = &in->data[0]; + for (; cnt > 0; cnt -=2) { + /* Read in data */ + if (!Mem_read_byte(0, dp, 0)) + goto error; + sim_debug(DEBUG_DATA, &dte_dev, "DTE: Read data: %06o %03o %03o\n", + *dp, *dp >> 8, *dp & 0377); + dp++; + in->dcnt += 2; + } + if (in->func & PRI_IND_FLG) { + uptr->STATUS |= DTE_IND; + in->dcnt = in->data[0]; + in->sdev = (in->dcnt >> 8) & 0377; + in->dcnt &= 0377; + word |= PRI_CMT_TOT; + if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word)) + goto error; + } else { + dte_in_ptr = (dte_in_ptr + 1) & 0x1f; + } + } + word &= ~PRI_CMT_TOT; + if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word)) + goto error; + uptr->STATUS |= DTE_11DN; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + dte_function(uptr); +} + +/* Process primary protocol packets */ +void +dte_function(UNIT *uptr) +{ + uint16 data1[32]; + int32 ch; + struct _dte_queue *cmd; + int func; + int dev; + + /* Check if queue is empty */ + while (dte_in_cmd != dte_in_ptr) { + if (((dte_out_res + 1) & 0x1f) == dte_out_ptr) { + sim_debug(DEBUG_DATA, &dte_dev, "DTE: func out full %d %d\n", + dte_out_res, dte_out_ptr); + return; + } + cmd = &dte_in[dte_in_cmd]; + dev = cmd->dev & 0377; + func = cmd->func & 0377; + sim_debug(DEBUG_DATA, &dte_dev, + "DTE: func %o %02o %s dev %o cnt %d dcnt %d\n", + dte_in_cmd, func, (func > PRI_EMLDV) ? "***" : pri_name[func], + cmd->dev, cmd->dcnt, cmd->dptr ); + switch (func) { + case PRI_EM2EI: /* Initial message to 11 */ + data1[0] = PRI_CTYDV; + if (dte_queue(PRI_EM2TI, PRI_EMCTY, 1, data1) == 0) + return; +#if (NUM_DEVS_LP20 > 0) + data1[0] = 140; + if (dte_queue(PRI_EMHLA, PRI_EMLPT, 1, data1) == 0) + return; +#endif + data1[0] = 0; + if (dte_queue(PRI_EMAKA, PRI_EMCLK, 1, data1) == 0) + return; + break; + + case PRI_EM2TI: /* Replay to initial message. */ + case PRI_EMLBE: /* Acknowledge line */ + /* Should never get these */ + break; + + case PRI_EMHDR: /* Here is date and time */ + /* Ignore this function */ + break; + + case PRI_EMRDT: /* Request Date/Time */ + { + time_t t = time(NULL); + struct tm *tm = localtime(&t); + int yr = tm->tm_year + 1900; + int tim = (((tm->tm_hour * 60) + tm->tm_min) * 60) + + tm->tm_sec; + data1[0] = 0177777; + data1[1] = ((yr & 0377) << 8) | ((yr >> 8) & 0377); + data1[2] = (tm->tm_mon) + ((tm->tm_mday - 1) << 8); + data1[3] = (((tm->tm_wday + 6) % 7)) + + (tm->tm_isdst ? 0200 << 8 : 0); + tim >>= 1; + data1[4] = ((tim & 0377) << 8) | ((tim >> 8) & 0377); + if (dte_queue(PRI_EMHDR | PRI_IND_FLG, PRI_EMCLK, 6, data1) == 0) + return; + } + break; + + case PRI_EMSTR: /* String data */ + +#if (NUM_DEVS_LP20 > 0) + /* Handle printer data */ + if (dev == PRI_EMLPT) { + uptr->LPST &= ~(EOFFLG); + if (!sim_is_active(&lp20_unit)) + sim_activate(&lp20_unit, 1000); + while (cmd->dptr < cmd->dcnt) { + ch = (int32)(cmd->data[cmd->dptr >> 1]); + if ((cmd->dptr & 1) == 0) + ch >>= 8; + ch &= 0177; + if (full(&lp20_queue)) + return; + lp20_queue.buff[lp20_queue.in_ptr] = ch; + inci(&lp20_queue); + cmd->dptr++; + } + if (cmd->dptr != cmd->dcnt) + return; + sim_debug(DEBUG_DETAIL, &dte_dev, "LP20 done\n"); + break; + } +#endif + + /* Handle terminal data */ + if (dev == PRI_EMDLS) { + int ln = cmd->sdev; + struct _buffer *otty; + if (ln == PRI_CTYDV) + goto cty; +#if (NUM_DEVS_TTY > 0) + ln -= NUM_DLS; + if (ln < 0 || ln >= tty_desc.lines) + break; + otty = &tty_out[ln]; + if (cmd->sz > 8) + cmd->dcnt += cmd->dcnt; + while (cmd->dptr < cmd->dcnt) { + ch = (int32)(cmd->data[cmd->dptr >> 1]); + if ((cmd->dptr & 1) == 0) + ch >>= 8; + ch &= 0177; + if (ch != 0) { + if (full(otty)) + return; + otty->buff[otty->in_ptr] = ch; + inci(otty); + sim_debug(DEBUG_DATA, &dte_dev, "TTY queue %o %d\n", + ch, ln); + } + cmd->dptr++; + } + if (cmd->dptr != cmd->dcnt) + return; +#endif + break; + } + + if (dev == PRI_EMCTY) { +cty: + sim_activate(&dte_unit[1], 100); + data1[0] = 0; + if (cmd->sz > 8) + cmd->dcnt += cmd->dcnt; + while (cmd->dptr < cmd->dcnt) { + ch = (int32)(cmd->data[cmd->dptr >> 1]); + if ((cmd->dptr & 1) == 0) + ch >>= 8; + ch &= 0177; + if (ch != 0) { + ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags)); + if (full(&cty_out)) + return; + cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff); + inci(&cty_out); + sim_debug(DEBUG_DATA, &dte_dev,"CTY queue %o\n",ch); + } + cmd->dptr++; + } + if (cmd->dptr != cmd->dcnt) + return; + } + break; + + case PRI_EMSNA: /* Send all (ttys) */ + /* Handle terminal data */ + if (dev == PRI_EMDLS || dev == PRI_EMCTY) { + struct _buffer *otty; + int ln; + while (cmd->dptr < cmd->dcnt) { + ch = (int32)(cmd->data[cmd->dptr >> 1]); + if ((cmd->dptr & 1) == 0) + ch >>= 8; + ch &= 0177; + if (ch != 0) { + sim_debug(DEBUG_DATA, &dte_dev, "SNA queue %o\n", ch); + ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags)); + if (!(full(&cty_out))) { + cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff); + inci(&cty_out); + } +#if (NUM_DEVS_TTY > 0) + for(ln = 0; ln <= tty_desc.lines; ln++) { + otty = &tty_out[ln]; + if (!(full(otty))) { + otty->buff[otty->in_ptr] = ch; + inci(otty); + } + } +#endif + } + cmd->dptr++; + } + if (cmd->dptr != cmd->dcnt) + return; + data1[0] = 0; + } + break; + + case PRI_EMLNC: /* Line-Char */ + if (dev == PRI_EMDLS) { + sim_activate(&dte_unit[1], 100); + while (cmd->dptr < cmd->dcnt) { + int ln; + ch = (int32)(cmd->data[cmd->dptr >> 1]); + ln = (ch >> 8); + ch &= 0177; + if (ch != 0 && ln == PRI_CTYDV) { + ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags)); + cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff); + inci(&cty_out); + if (((cty_out.in_ptr + 1) & 0xff) == cty_out.out_ptr) + return; + sim_debug(DEBUG_DATA, &dte_dev, "CTY queue %o\n", ch); + } else + if (ch != 0 && ln >= NUM_DLS && ln <= tty_desc.lines) { + struct _buffer *otty; + ln -= NUM_DLS; + otty = &tty_out[ln]; + if (full(otty)) + return; + otty->buff[otty->in_ptr] = ch; + inci(otty); + sim_debug(DEBUG_DATA, &dte_dev, "TTY queue %o %d\n", ch, ln); + } + cmd->dptr+=2; + } + if (cmd->dptr != cmd->dcnt) + return; + } + break; + + case PRI_EMOPS: +#if (NUM_DEVS_LP20 > 0) + if (dev == PRI_EMLPT) { + lp20_unit.LINE = 0; + } +#endif + break; + + case PRI_EMRDS: /* Request device status */ + if (dev == PRI_EMLPT) { + if (cmd->data[0] != 0) { + data1[0] = 2 << 8; + data1[1] = 0; + data1[2] = 0; + if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, + 3, data1) == 0) + return; + } else { +#if (NUM_DEVS_LP20 > 0) + lp20_unit.LPST |= HDSFLG; + if (!sim_is_active(&lp20_unit)) + sim_activate(&lp20_unit, 1000); +#else + data1[0] = 2 << 8; + data1[1] = 0; + data1[2] = 0; + if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, + 3, data1) == 0) + return; +#endif + } + } + if (dev == PRI_EMCTY) { + data1[0] = 0; + data1[1] = 0; + if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMCTY, + 3, data1) == 0) + return; + } + if (dev == PRI_EMDH1) { + data1[0] = 0; + data1[1] = 0; + if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMDH1, + 3, data1) == 0) + return; + } + break; + + case PRI_EMHDS: /* Here is device status */ +#if (NUM_DEVS_LP20 > 0) + if (dev == PRI_EMLPT) { + sim_debug(DEBUG_DETAIL, &dte_dev, "LPT HDS %06o %06o %06o\n", + cmd->data[0], cmd->data[1], cmd->data[2]); + if (cmd->data[0] & 040) { + lp20_unit.LPST |= EOFFLG; + lp20_unit.LPCNT = 0; + } + lp20_unit.LPST |= HDSFLG; + sim_debug(DEBUG_DETAIL, &dte_dev, "LPT HDS %06o \n", + lp20_unit.LPST); + if (!sim_is_active(&lp20_unit)) + sim_activate(&lp20_unit, 1000); + } +#endif + break; + case PRI_EMLDV: /* Load LP VFU */ +#if (NUM_DEVS_LP20 > 0) + if (dev == PRI_EMLPT) { + int ln = lp20_unit.LPCNT; + while (cmd->dptr < cmd->dcnt) { + uint16 d = cmd->data[cmd->dptr++]; + if (d == (0357 << 8)) + lp20_vfu[ln++] = 010000; /* Signal end of page */ + else + lp20_vfu[ln++] = ((d >> 8) & 077)|((d <<6) & 07700); + } + lp20_unit.LPCNT = ln; + for (ln = 0; ln < 256; ln++) + sim_debug(DEBUG_DETAIL, &lp20_dev, + "LP20 VFU %02d => %04o\n", ln, lp20_vfu[ln]); + data1[0] = 0; + if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0) + sim_activate(uptr, 1000); + } +#endif + break; + + case PRI_EMLDR: /* Load LP RAM */ +#if (NUM_DEVS_LP20 > 0) + if (dev == PRI_EMLPT) { + int ln = lp20_unit.LPCNT; + for (;cmd->dptr < cmd->dcnt; cmd->dptr++, ln++) { + if (ln < 256) + lp20_ram[ln] = cmd->data[cmd->dptr]; + } + lp20_unit.LPCNT = ln; + for (ln = 0; ln < 256; ln++) + sim_debug(DEBUG_DETAIL, &lp20_dev, + "LP20 RAM %02x => %04x\n", ln, lp20_ram[ln]); + data1[0] = 0; + if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0) + sim_activate(uptr, 1000); + } +#endif + break; + + + case PRI_EMFLO: /* Flush output */ +#if (NUM_DEVS_TTY > 0) + if (dev == PRI_EMDLS) { + int ln = cmd->data[0] - NUM_DLS; + + sim_debug(DEBUG_DETAIL, &dte_dev, "Flush out %d %o\n", + ln, cmd->data[0]); + if (ln == (NUM_DLS - PRI_CTYDV)) + cty_out.in_ptr = cty_out.out_ptr = 0; + else + tty_out[ln].in_ptr = tty_out[ln].out_ptr = 0; + data1[0] = (ln + NUM_DLS) | (PRI_EMDLS << 8); + if (dte_queue(PRI_EMLBE, PRI_EMDLS, 1, data1) == 0) + return; + } +#endif +#if (NUM_DEVS_LP20 > 0) + if ((cmd->dev & 0377) == PRI_EMLPT) { + data1[0] = cmd->data[0]; + if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0) + return; + } +#endif + break; + + case PRI_EMDSC: /* Dataset connect */ + break; + + case PRI_EMHUD: /* Hang up dataset */ +#if (NUM_DEVS_TTY > 0) + if (dev == PRI_EMDLS) { + int ln = cmd->sdev - NUM_DLS; + if (ln >= 0) { + TMLN *lp = &tty_ldsc[ln]; + tmxr_linemsg (lp, "\r\nLine Hangup\r\n"); + tmxr_reset_ln(lp); + tty_connect[ln] = 0; + } + } + break; + + case PRI_EMXOF: /* XOFF line */ + if (dev == PRI_EMDLS) { + int ln = cmd->sdev - NUM_DLS; + if (ln >= 0) { + tty_ldsc[ln].rcve = 0; + } + } + break; + + case PRI_EMXON: /* XON line */ + if (dev == PRI_EMDLS) { + int ln = cmd->sdev - NUM_DLS; + if (ln >= 0) { + tty_ldsc[ln].rcve = 1; + } + } + break; + + case PRI_EMHLS: /* Here is line speeds */ + if (dev == PRI_EMDLS) { + int ln = cmd->sdev - NUM_DLS; + sim_debug(DEBUG_DETAIL, &tty_dev, "HDL %o o=%d i=%d %o\n", + ln, cmd->data[0], cmd->data[1], cmd->data[2]); + } + break; + + case PRI_EMHLA: /* Here is line allocation */ + case PRI_EMRBI: /* Reboot information */ + case PRI_EMAKA: /* Ack ALL */ + case PRI_EMTDO: /* Turn device On/Off */ + break; + + case PRI_EMEDR: /* Enable/Disable line */ + if (cmd->dev == PRI_EMDH1) { + /* Zero means enable, no-zero means disable */ + tty_enable = !((cmd->data[0] >> 8) & 0xff); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY enable %x\n", + tty_enable); + if (tty_enable) { + sim_activate(&tty_unit[0], 1000); + sim_activate(&tty_unit[1], 1000); + } else { + sim_cancel(&tty_unit[0]); + sim_cancel(&tty_unit[1]); + } + } + break; +#endif + default: + break; + } + /* Mark command as finished */ + cmd->cnt = 0; + dte_in_cmd = (dte_in_cmd + 1) & 0x1F; + } +} + +/* + * Handle primary protocol, + * Send to 10 when requested. + */ +void dte_transfer(UNIT *uptr) { + uint16 cnt; + uint16 scnt; + struct _dte_queue *out; + uint16 *dp; + + /* Check if Queue empty */ + if (dte_out_res == dte_out_ptr) + return; + + out = &dte_out[dte_out_ptr]; + uptr->STATUS &= ~DTE_TO11; + clr_interrupt(DTE_DEVNUM); + + /* Compute how much 10 wants us to send */ + scnt = ((uptr->CNT ^ DTE_TO10BC) + 1) & DTE_TO10BC; + /* Check if indirect */ + if ((uptr->STATUS & DTE_SIND) != 0) { + /* Transfer indirect */ + cnt = out->dcnt; + dp = &out->data[0]; + if (cnt > scnt) /* Only send as much as we are allowed */ + cnt = scnt; + for (; cnt > 0; cnt -= 2) { + sim_debug(DEBUG_DATA, &dte_dev, "DTE: Send Idata: %06o %03o %03o\n", + *dp, *dp >> 8, *dp & 0377); + if (Mem_write_byte(0, dp) == 0) + goto error; + dp++; + } + uptr->STATUS &= ~DTE_SIND; + } else { + sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d send CMD: [%o] %o %o %o\n", + dte_out_ptr, dte_out_res, scnt, out->cnt, out->func, out->dev); + /* Get size of packet */ + cnt = out->cnt; + if ((out->func & PRI_IND_FLG) == 0) + cnt += out->dcnt; + /* If it will not fit, request indirect */ + if (cnt > scnt) { /* If not enough space request indirect */ + out->func |= PRI_IND_FLG; + cnt = scnt; + } + /* Write out header */ + if (!Mem_write_byte(0, &cnt)) + goto error; + if (!Mem_write_byte(0, &out->func)) + goto error; + cnt -= 2; + if (!Mem_write_byte(0, &out->dev)) + goto error; + cnt -= 2; + if (!Mem_write_byte(0, &out->spare)) + goto error; + cnt -= 2; + if (out->func & PRI_IND_FLG) { + uint16 dwrd = out->dcnt; + sim_debug(DEBUG_DATA, &dte_dev, "DTE: Indirect %o %o\n", cnt, + out->dcnt); + dwrd |= (out->sdev << 8); + if (!Mem_write_byte(0, &dwrd)) + goto error; + uptr->STATUS |= DTE_SIND; + goto done; + } + cnt -= 2; + dp = &out->data[0]; + for (; cnt > 0; cnt -= 2) { + sim_debug(DEBUG_DATA, &dte_dev, "DTE: Send data: %06o %03o %03o\n", + *dp, *dp >> 8, *dp & 0377); + if (!Mem_write_byte(0, dp)) + goto error; + dp++; + } + } + out->cnt = 0; + dte_out_ptr = (dte_out_ptr + 1) & 0x1f; +done: + uptr->STATUS |= DTE_10DN; + set_interrupt(DTE_DEVNUM, uptr->STATUS); +error: + return; +} + +/* Process input from CTY and TTY's to 10. */ +void +dte_input() +{ + uint16 data1; + uint16 dataq[32]; + int n; + int ln; + int save_ptr; + char ch; + UNIT *uptr = &dte_unit[0]; + +#if KL_ITS + if (QITS && (uptr->STATUS & ITS_ON) != 0) { + uint64 word; + word = M[ITS_DTEODN]; + /* Check if ready for output done */ + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEODN = %012llo %d\n", word, + cty_done); + if ((word & SMASK) != 0) { + if (cty_done) { + word = 64LL; + cty_done--; +#if (NUM_DEVS_TTY > 0) + } else { + for (ln = 0; ln < tty_desc.lines; ln++) { + if (tty_done[ln]) { + word = (((uint64)ln + 1) << 18); + word |=(tty_connect[ln])? 64: 1; + tty_done[ln] = 0; + break; + } + } +#endif + } + if ((word & SMASK) == 0) { + M[ITS_DTEODN] = word; + /* Tell 10 something is ready */ + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEODN = %012llo\n", + word); + } + } + /* Check if ready for any input */ + word = M[ITS_DTETYI]; + if ((word & SMASK) != 0) { + /* CTY first. */ + if (not_empty(&cty_in)) { + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + word = (uint64)ch; +#if (NUM_DEVS_TTY > 0) + } else { + ln = uptr->CNT; + while ((word & SMASK) != 0) { + if (not_empty(&tty_in[ln])) { + ch = tty_in[ln].buff[tty_in[ln].out_ptr]; + inco(&tty_in[ln]); + word = ((uint64)(ln+1) << 18) | (uint64)ch; + } + ln++; + if (ln >= tty_desc.lines) + ln = 0; + if (ln == uptr->CNT) + break; + } + uptr->CNT = ln; +#endif + } + if ((word & SMASK) == 0) { + M[ITS_DTETYI] = word; + /* Tell 10 something is ready */ + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTETYI = %012llo\n", + word); + } + } +#if (NUM_DEVS_TTY > 0) + /* Check ready for hang up message */ + word = M[ITS_DTEHNG]; + if ((word & SMASK) != 0) { + for (ln = 0; ln < tty_desc.lines; ln++) { + if (tty_connect[ln] != tty_ldsc[ln].conn) { + if (tty_ldsc[ln].conn) + word = 015500 + ln + 1; + else + word = ln + 1; + tty_connect[ln] = tty_ldsc[ln].conn; + tty_done[ln] = tty_ldsc[ln].conn; + break; + } + } + /* Tell 10 something is ready */ + if ((word & SMASK) == 0) { + M[ITS_DTEHNG] = word; + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEHNG = %012llo\n", + word); + } + } +#endif + } else +#endif + if ((uptr->STATUS & DTE_SEC) == 0) { + /* Check if CTY done with input */ + if (cty_done) { + data1 = PRI_CTYDV; + if (dte_queue(PRI_EMLBE, PRI_EMDLS, 1, &data1) == 0) + return; + cty_done--; + } + /* Grab a chunck of input from CTY if any */ + n = 0; + save_ptr = cty_in.out_ptr; + while (not_empty(&cty_in) && n < 32) { + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY recieve %02x\n", ch); + dataq[n++] = (PRI_CTYDV << 8) | ch; + } + if (n > 0 && dte_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0) { + /* Restore the input pointer */ + cty_in.out_ptr = save_ptr; + return; + } +#if (NUM_DEVS_TTY > 0) + n = 0; + /* While we have room for one more packet, + * grab as much input as we can */ + for (ln = 0; ln < tty_desc.lines && + ((dte_out_res + 1) & 0x1f) != dte_out_ptr; ln++) { + struct _buffer *itty = &tty_in[ln]; + while (not_empty(itty)) { + ch = itty->buff[itty->out_ptr]; + inco(itty); + dataq[n++] = ((ln + NUM_DLS) << 8) | ch; + if (n == 32) { + if (dte_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0) + return; + n = 0; + continue; + } + } + } + if (n > 0 && dte_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0) + return; + n = 0; + for (ln = 0; ln < tty_desc.lines; ln++) { + data1 = (ln + NUM_DLS) | (PRI_EMDLS << 8); + if (tty_connect[ln] != tty_ldsc[ln].conn) { + if (tty_ldsc[ln].conn) + n = PRI_EMDSC; + else + n = PRI_EMHUD; + if (dte_queue(n, PRI_EMDLS, 1, &data1) == 0) + return; + tty_connect[ln] = tty_ldsc[ln].conn; + } + if (tty_done[ln]) { + if (dte_queue(PRI_EMLBE, PRI_EMDLS, 1, &data1) == 0) + return; + tty_done[ln] = 0; + } + } +#endif + } +} + +/* + * Queue up a packet to send to 10. + */ +int +dte_queue(int func, int dev, int dcnt, uint16 *data) +{ + uint16 *dp; + struct _dte_queue *out; + + /* Check if room in queue for this packet. */ + if (((dte_out_res + 1) & 0x1f) == dte_out_ptr) { + sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d out full\n", dte_out_res, dte_out_ptr); + return 0; + } + out = &dte_out[dte_out_res]; + out->cnt = 10; + out->func = func; + out->dev = dev; + out->dcnt = (dcnt-1)*2; + out->spare = 0; + sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d queue resp: %o (%o) f=%o %s d=%o\n", + dte_out_ptr, dte_out_res, out->cnt, out->dcnt, out->func, + (out->func > PRI_EMLDV)? "***":pri_name[out->func], out->dev); + for (dp = &out->data[0]; dcnt > 0; dcnt--) { + *dp++ = *data++; + } + /* Advance pointer to next function */ + dte_out_res = (dte_out_res + 1) & 0x1f; + return 1; +} + + +/* + * If anything in queue, start a transfer, if one is not already + * pending. + */ +int +dte_start(UNIT *uptr) +{ + uint64 word; + int dcnt; + + /* Check if queue empty */ + if (dte_out_ptr == dte_out_res) + return 1; + + /* If there is interrupt pending, just return */ + if ((uptr->STATUS & (DTE_IND|DTE_10DB|DTE_11DB)) != 0) + return 1; + if (Mem_examine_word(0, dte_et11_off + PRI_CMTW_STS, &word)) { +error: + /* If we can't read it, go back to secondary */ + uptr->STATUS |= DTE_SEC|DTE_10ER; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + return 0; + } + /* Bump count of messages sent */ + word = (word & ~(PRI_CMT_10IC|PRI_CMT_IP)) | ((word + 0400) & PRI_CMT_10IC); + word &= ~PRI_CMT_FWD; + if ((uptr->STATUS & DTE_SIND) != 0) + word |= PRI_CMT_IP; + if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word)) + goto error; + dcnt = dte_out[dte_out_ptr].cnt; + if ((dte_out[dte_out_ptr].func & PRI_IND_FLG) == 0) + dcnt += dte_out[dte_out_ptr].dcnt; + /* Tell 10 something is ready */ + if ((uptr->STATUS & DTE_SIND) != 0) { + dcnt = dte_out[dte_out_ptr].dcnt; + } + sim_debug(DEBUG_DATA, &dte_dev, "DTE: start: %012llo %o\n", word, dcnt); + word = (uint64)dcnt; + if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_CNT, &word)) + goto error; + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, uptr->STATUS); + return 1; +} + + +/* Check for input from CTY and put on queue. */ +t_stat dtei_svc (UNIT *uptr) +{ + int32 ch; + uint32 base = 0; + UNIT *optr = &dte_unit[0]; + +#if KI_22BIT +#if KL_ITS + if (!QITS) +#endif + base = eb_ptr; +#endif + sim_clock_coschedule (uptr, tmxr_poll); + dte_input(); + if ((optr->STATUS & (DTE_SEC)) == 0) { + dte_function(uptr); /* Process queue */ + dte_start(optr); + } + + + /* If we have room see if any new lines */ + while (!full(&cty_in)) { + ch = sim_poll_kbd (); + if (ch & SCPE_KFLAG) { + ch = 0177 & sim_tt_inpcvt(ch, TT_GET_MODE (uptr->flags)); + cty_in.buff[cty_in.in_ptr] =ch & 0377; + inci(&cty_in); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY char %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + } else + break; + } + + /* If Monitor input, place in buffer */ + if ((optr->STATUS & (DTE_SEC|DTE_MON)) == (DTE_SEC|DTE_MON) && + not_empty(&cty_in) && M[SEC_DTMTI + base] == 0) { + ch = cty_in.buff[cty_in.out_ptr]; + inco(&cty_in); + M[SEC_DTF11 + base] = ch; + M[SEC_DTMTI + base] = FMASK; + if (dte_dev.flags & TYPE_RSX20) { + uptr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, dte_unit[0].STATUS); + } + } + return SCPE_OK; +} + +/* Handle output of characters to CTY. Started whenever there is output pending */ +t_stat dteo_svc (UNIT *uptr) +{ + uint32 base = 0; + UNIT *optr = &dte_unit[0]; + +#if KI_22BIT +#if KL_ITS + if (!QITS) +#endif + base = eb_ptr; +#endif + /* Flush out any pending CTY output */ + while(not_empty(&cty_out)) { + char ch = cty_out.buff[cty_out.out_ptr]; + if (ch != 0) { + if (sim_putchar(ch) != SCPE_OK) { + sim_activate(uptr, 1000); + return SCPE_OK;; + } + } + inco(&cty_out); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY outch %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + } + cty_done++; + return SCPE_OK; +} + + +/* Handle FE timer interrupts. And keepalive counts */ +t_stat +dtertc_srv(UNIT * uptr) +{ + UNIT *optr = &dte_unit[0]; + + sim_activate_after(uptr, 1000000/rtc_tps); + /* Check if clock requested */ + if (uptr->STATUS & SEC_CLK) { + rtc_tick++; + if (rtc_wait != 0) { + rtc_wait--; + } else { + UNIT *optr = &dte_unit[0]; + uint32 base = 0; +#if KI_22BIT + base = eb_ptr; +#endif + /* Set timer flag */ + M[SEC_DTCLK + base] = FMASK; + optr->STATUS |= DTE_10DB; + set_interrupt(DTE_DEVNUM, optr->STATUS); + sim_debug(DEBUG_EXP, &dte_dev, "CTY tick %x %x %06o\n", + rtc_tick, rtc_wait, optr->STATUS); + } + } +#if KL_ITS + /* Check if Timesharing is running */ + if (QITS) { + uint64 word; + + word = (M[ITS_DTECHK] + 1) & FMASK; + if (word == 0) { + optr->STATUS |= ITS_ON; + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS ON\n"); + sim_activate(&tty_unit[0], 1000); + sim_activate(&tty_unit[1], 1000); + } else if (word >= (15 * 60)) { + optr->STATUS &= ~ITS_ON; + word = 15 * 60; + sim_cancel(&tty_unit[0]); + sim_cancel(&tty_unit[1]); + sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS OFF\n"); + } + M[ITS_DTECHK] = word; + } else +#endif + + /* Update out keep alive timer if in secondary protocol */ + if ((optr->STATUS & DTE_SEC) == 0) { + int addr = 0144 + eb_ptr; + uint64 word; + + (void)Mem_examine_word(0, dte_et11_off + PRI_CMTW_STS, &word); + addr = (M[addr+1] + dte_off + PRI_CMTW_KAC) & RMASK; + word = M[addr]; + word = (word + 1) & FMASK; + M[addr] = word; + sim_debug(DEBUG_EXP, &dte_dev, "CTY keepalive %06o %012llo %06o\n", + addr, word, optr->STATUS); + } + + return SCPE_OK; +} + + +t_stat dte_reset (DEVICE *dptr) +{ + dte_unit[0].STATUS = DTE_SEC; + dte_unit[1].STATUS = 0; + dte_unit[2].STATUS = 0; + dte_unit[3].STATUS = 0; + cty_done = 0; + sim_activate(&dte_unit[3], 1000); + sim_activate(&dte_unit[2], 1000); + return SCPE_OK; +} + + +t_stat +dte_set_type(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_M_OS; + dptr->flags |= val; + return SCPE_OK; +} + +t_stat +dte_show_type (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 & TYPE_RSX20) ? "RSX20" : "RSX10"); + return SCPE_OK; +} + + +/* Stop operating system */ + +t_stat dte_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) +{ + dte_unit[0].flags = (dte_unit[0].flags & ~TT_MODE) | val; + return SCPE_OK; +} + +t_stat dte_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, &dte_dev); +return SCPE_OK; +} + +const char *dte_description (DEVICE *dptr) +{ + return "Console TTY Line"; +} + + +#if (NUM_DEVS_LP20 > 0) + +void +lp20_printline(UNIT *uptr, int nl) { + int trim = 0; + uint16 data1 = 1; + /* Trim off trailing blanks */ + while (uptr->COL >= 0 && lp20_buffer[uptr->COL - 1] == ' ') { + uptr->COL--; + trim = 1; + } + lp20_buffer[uptr->COL] = '\0'; + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP output %d %d [%s]\n", uptr->COL, nl, + lp20_buffer); + /* Stick a carraige return and linefeed as needed */ + if (uptr->COL != 0 || trim) + lp20_buffer[uptr->COL++] = '\r'; + if (nl != 0) { + lp20_buffer[uptr->COL++] = '\n'; + uptr->LINE++; + } + if (nl > 0 && lp20_vfu[uptr->LINE] == 010000) { + lp20_buffer[uptr->COL++] = '\f'; + uptr->LINE = 1; + } else if (nl < 0 && uptr->LINE >= (int32)uptr->capac) { + uptr->LINE = 1; + } + + sim_fwrite(&lp20_buffer, 1, uptr->COL, uptr->fileref); + uptr->pos += uptr->COL; + uptr->COL = 0; + return; +} + + +/* Unit service */ +void +lp20_output(UNIT *uptr, char c) { + + if (c == 0) + return; + if (uptr->COL == 132) + lp20_printline(uptr, 1); + if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140) + c &= 0137; + else if (c >= 040 && c < 0177) { /* If printable */ + lp20_buffer[uptr->COL++] = c; + } if (c == 011) { /* Tab */ + lp20_buffer[uptr->COL++] = ' '; + while ((uptr->COL & 07) != 0) + lp20_buffer[uptr->COL++] = ' '; + } + return; +} + +t_stat lp20_svc (UNIT *uptr) +{ + char ch; + uint16 ram_ch; + uint16 data1[5]; + int l = uptr->LINE; + + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_OK; + if (dte_dev.flags & TYPE_RSX20 && uptr->LPST & HDSFLG) { + data1[0] = 0; + + data1[1] = (uptr->LINE == 1) ? 01<<8: 0; + sim_debug(DEBUG_DETAIL, &dte_dev, "LPT status %06o \n", uptr->LPST); + if (uptr->LPST & EOFFLG) { + data1[0] |= 040 << 8; + uptr->LPCNT = 0; + } + if (uptr->LPST & INTFLG) { + data1[1] |= 02 << 8; + uptr->LPCNT = 0; + } + data1[2] = 0110200; + if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, 4, data1) == 0) + sim_activate(uptr, 1000); + uptr->LPST &= ~(HDSFLG); + } + + if (empty(&lp20_queue)) + return SCPE_OK; + while (not_empty(&lp20_queue)) { + ch = lp20_queue.buff[lp20_queue.out_ptr]; + inco(&lp20_queue); + ram_ch = lp20_ram[ch]; + + /* If previous was delimiter or translation do it */ + if (uptr->LPST & DELFLG || (ram_ch &(LP20_RAM_DEL|LP20_RAM_TRN)) != 0) { + ch = ram_ch & LP20_RAM_CHR; + uptr->LPST &= ~DELFLG; + if (ram_ch & LP20_RAM_DEL) + uptr->LPST |= DELFLG; + } + /* Flag if interrupt set */ + if (ram_ch & LP20_RAM_INT) + uptr->LPST |= HDSFLG|INTFLG; + /* Check if paper motion */ + if (ram_ch & LP20_RAM_PI) { + int lines = 0; /* Number of new lines to output */ + /* Print any buffered line */ + lp20_printline(uptr, (ram_ch & 037) != 020); + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP deque %02x %04x\n", + ch, ram_ch); + if ((ram_ch & 020) == 0) { /* Find channel mark in output */ + while ((lp20_vfu[uptr->LINE] & (1 << (ram_ch & 017))) == 0) { + sim_debug(DEBUG_DETAIL, &lp20_dev, + "LP skip chan %04x %04x %d\n", + lp20_vfu[uptr->LINE], ram_ch, uptr->LINE); + if (lp20_vfu[uptr->LINE] & 010000) { /* Hit bottom of form */ + sim_fwrite("\014", 1, 1, uptr->fileref); + uptr->pos++; + lines = 0; + uptr->LINE = 1; + break; + } + lines++; + uptr->LINE++; + } + } else { + while ((ram_ch & 017) != 0) { + sim_debug(DEBUG_DETAIL, &lp20_dev, + "LP skip line %04x %04x %d\n", + lp20_vfu[uptr->LINE], ram_ch, uptr->LINE); + if (lp20_vfu[uptr->LINE] & 010000) { /* Hit bottom of form */ + sim_fwrite("\014", 1, 1, uptr->fileref); + uptr->pos++; + lines = 0; + uptr->LINE = 1; + } + lines++; + uptr->LINE++; + ram_ch--; + } + } + for(;lines > 0; lines--) { + sim_fwrite("\r\n", 1, 2, uptr->fileref); + uptr->pos+=2; + } + } else if (ch != 0) { + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP deque %02x '%c' %04x\n", + ch, ch, ram_ch); + lp20_output(uptr, ch); + } + } + if (empty(&lp20_queue)) { + data1[0] = 0; + if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0) + sim_activate(uptr, 1000); + if (dte_dev.flags & TYPE_RSX20) { + if (uptr->LINE == 0) { + uptr->LPST |= HDSFLG; + sim_activate(uptr, 1000); + } + } + } + return SCPE_OK; +} + +/* Reset routine */ + +t_stat lp20_reset (DEVICE *dptr) +{ + UNIT *uptr = &lp20_unit; + int i; + uptr->POS = 0; + uptr->COL = 0; + uptr->LINE = 1; + /* Clear RAM & VFU */ + for (i = 0; i < 256; i++) { + lp20_ram[i] = 0; + lp20_vfu[i] = 0; + } + + /* Load default VFU into VFU */ + memcpy(&lp20_vfu, lp20_dvfu, sizeof(lp20_dvfu)); + lp20_ram[012] = LP20_RAM_TRN|LP20_RAM_PI|7; /* Line feed, print line, space one line */ + lp20_ram[013] = LP20_RAM_TRN|LP20_RAM_PI|6; /* Vertical tab, Skip mod 20 */ + lp20_ram[014] = LP20_RAM_TRN|LP20_RAM_PI|0; /* Form feed, skip to top of page */ + lp20_ram[015] = LP20_RAM_TRN|LP20_RAM_PI|020; /* Carrage return */ + lp20_ram[020] = LP20_RAM_TRN|LP20_RAM_PI|1; /* Skip half page */ + lp20_ram[021] = LP20_RAM_TRN|LP20_RAM_PI|2; /* Skip even lines */ + lp20_ram[022] = LP20_RAM_TRN|LP20_RAM_PI|3; /* Skip triple lines */ + lp20_ram[023] = LP20_RAM_TRN|LP20_RAM_PI|4; /* Skip one line */ + lp20_ram[024] = LP20_RAM_TRN|LP20_RAM_PI|5; + sim_cancel (&lp20_unit); /* deactivate unit */ + return SCPE_OK; +} + +/* Attach routine */ + +t_stat lp20_attach (UNIT *uptr, CONST char *cptr) +{ + return attach_unit (uptr, cptr); +} + +/* Detach routine */ + +t_stat lp20_detach (UNIT *uptr) +{ + return detach_unit (uptr); +} + +/* + * Line printer routines + */ + +t_stat +lp20_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + t_value i; + t_stat r; + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + i = get_uint (cptr, 10, 100, &r); + if (r != SCPE_OK) + return SCPE_ARG; + uptr->capac = (t_addr)i; + uptr->LINE = 0; + return SCPE_OK; +} + +t_stat +lp20_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "linesperpage=%d", uptr->capac); + return SCPE_OK; +} + +t_stat lp20_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"); +fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n"); +fprintf (st, " sim> SET %s0 LINESPERPAGE=n\n\n", dptr->name); +fprintf (st, "The default is 66 lines per page.\n\n"); +fprintf (st, "The device address of the Line printer can be changed\n"); +fprintf (st, " sim> SET %s0 DEV=n\n\n", dptr->name); +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +fprint_reg_help (st, dptr); +return SCPE_OK; +} + +const char *lp20_description (DEVICE *dptr) +{ + return "LP20 line printer" ; +} + +#endif + +#if (NUM_DEVS_TTY > 0) + +/* Unit service */ +t_stat ttyi_svc (UNIT *uptr) +{ + int32 ln; + TMLN *lp; + int flg; + + if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return SCPE_OK; + + sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */ + + /* If we have room see if any new lines */ + ln = tmxr_poll_conn (&tty_desc); /* look for connect */ + if (ln >= 0) { + tty_ldsc[ln].rcve = 1; + sim_debug(DEBUG_DETAIL, &tty_dev, "TTY line connect %d\n", ln); + } + + tmxr_poll_tx(&tty_desc); + tmxr_poll_rx(&tty_desc); + + /* Scan each line for input */ + for (ln = 0; ln < tty_desc.lines; ln++) { + struct _buffer *iptr = &tty_in[ln]; + lp = &tty_ldsc[ln]; + if (lp->conn == 0) + continue; + flg = 1; + while (flg && !full(iptr)) { + /* Spool up as much as we have room for */ + int32 ch = tmxr_getc_ln(lp); + if ((ch & TMXR_VALID) != 0) { + ch = sim_tt_inpcvt (ch, + TT_GET_MODE(tty_unit[0].flags) | TTUF_KSR); + iptr->buff[iptr->in_ptr] = ch & 0377; + inci(iptr); + sim_debug(DEBUG_DETAIL, &tty_dev, "TTY recieve %d: %o\n", + ln, ch); + } else + flg = 0; + } + } + + return SCPE_OK; +} + +/* Output whatever we can */ +t_stat ttyo_svc (UNIT *uptr) +{ + t_stat r; + int32 ln; + int n = 0; + TMLN *lp; + + if ((tty_unit[0].flags & UNIT_ATT) == 0) /* attached? */ + return SCPE_OK; + + sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */ + + for (ln = 0; ln < tty_desc.lines; ln++) { + struct _buffer *optr = &tty_out[ln]; + lp = &tty_ldsc[ln]; + if (lp->conn == 0) { + if (not_empty(optr)) { + optr->out_ptr = optr->in_ptr = 0; + tty_done[ln] = 1; + } + continue; + } + if (empty(optr)) + continue; + while (not_empty(optr)) { + int32 ch = optr->buff[optr->out_ptr]; + ch = sim_tt_outcvt(ch, TT_GET_MODE (tty_unit[0].flags) | TTUF_KSR); + sim_debug(DEBUG_DATA, &tty_dev, "TTY: %d output %o\n", ln, ch); + r = tmxr_putc_ln (lp, ch); + if (r == SCPE_OK) + inco(optr); + else if (r == SCPE_LOST) { + optr->out_ptr = optr->in_ptr = 0; + continue; + } else + continue; + } + tty_done[ln] = 1; + } + return SCPE_OK; +} + +/* Reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ + return SCPE_OK; +} + + +/* SET LINES processor */ + +t_stat tty_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, NUM_LINES_TTY, &r); + if ((r != SCPE_OK) || (newln == tty_desc.lines)) + return r; + if ((newln == 0) || (newln >= NUM_LINES_TTY) || (newln % 16) != 0) + return SCPE_ARG; + if (newln < tty_desc.lines) { + for (i = newln, t = 0; i < tty_desc.lines; i++) + t = t | tty_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < tty_desc.lines; i++) { + if (tty_ldsc[i].conn) { + tmxr_linemsg (&tty_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_send_buffered_data (&tty_ldsc[i]); + } + tmxr_detach_ln (&tty_ldsc[i]); /* completely reset line */ + } + } + if (tty_desc.lines < newln) + memset (tty_ldsc + tty_desc.lines, 0, + sizeof(*tty_ldsc)*(newln-tty_desc.lines)); + tty_desc.lines = newln; + return tty_reset (&tty_dev); /* setup lines and auto config */ +} + +/* SET LOG processor */ + +t_stat tty_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, tty_desc.lines, &r); + if ((r != SCPE_OK) || (ln >= tty_desc.lines)) + return SCPE_ARG; + return tmxr_set_log (NULL, ln, cptr, desc); +} + +/* SET NOLOG processor */ + +t_stat tty_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, tty_desc.lines, &r); + if ((r != SCPE_OK) || (ln >= tty_desc.lines)) + return SCPE_ARG; + return tmxr_set_nolog (NULL, ln, NULL, desc); +} + +/* SHOW LOG processor */ + +t_stat tty_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int32 i; + + for (i = 0; i < tty_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 tty_attach (UNIT *uptr, CONST char *cptr) +{ +t_stat reason; + +reason = tmxr_attach (&tty_desc, uptr, cptr); +if (reason != SCPE_OK) + return reason; +sim_activate (uptr, tmxr_poll); +return SCPE_OK; +} + +/* Detach routine */ + +t_stat tty_detach (UNIT *uptr) +{ + int32 i; + t_stat reason; +sim_cancel (uptr); +reason = tmxr_detach (&tty_desc, uptr); +for (i = 0; i < tty_desc.lines; i++) + tty_ldsc[i].rcve = 0; +return reason; +} + +t_stat tty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "FE Terminal Interfaces\n\n"); +fprintf (st, "The FE terminal could support up to 256 lines, in groups of 16\n"); +fprintf (st, "lines. The number of lines is specified with a SET command:\n\n"); +fprintf (st, " sim> SET TTY 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 TTYn LOG command enables\n"); +fprintf (st, "logging on a line:\n\n"); +fprintf (st, " sim> SET TTYn LOG=filename log output of line n to filename\n\n"); +fprintf (st, "The SET TTYn NOLOG command disables logging and closes the open log file,\n"); +fprintf (st, "if any.\n\n"); +fprintf (st, "Once TTY 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 TTY DISCONNECT command, or a DETACH TTY command.\n\n"); +fprintf (st, "Other special commands:\n\n"); +fprintf (st, " sim> SHOW TTY CONNECTIONS show current connections\n"); +fprintf (st, " sim> SHOW TTY STATISTICS show statistics for active connections\n"); +fprintf (st, " sim> SET TTYn DISCONNECT disconnects the specified line.\n"); +fprint_reg_help (st, &tty_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 TTY is detached.\n"); +return SCPE_OK; +} + +const char *tty_description (DEVICE *dptr) +{ +return "FE asynchronous line interface"; +} + +#endif +#endif diff --git a/PDP10/kl10_nia.c b/PDP10/kl10_nia.c new file mode 100644 index 00000000..1c3a2e34 --- /dev/null +++ b/PDP10/kl10_nia.c @@ -0,0 +1,1674 @@ +/* kl10_NIA.c: NIA 20 Network interface. + + Copyright (c) 2020, 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 emulates the MIT-AI/ML/MC Host/IMP interface. +*/ + + +#include "kx10_defs.h" +#include "sim_ether.h" + +#if NUM_DEVS_NIA > 0 +#define NIA_DEVNUM (0540 + (5 * 4)) + +/* NIA Bits */ + +/* CONI */ +#define NIA_PPT 0400000000000LL /* Port present */ +#define NIA_DCC 0100000000000LL /* Diag CSR */ +#define NIA_CPE 0004000000000LL /* CRAM Parity error */ +#define NIA_MBE 0002000000000LL /* MBUS error */ +#define NIA_ILD 0000100000000LL /* Idle */ +#define NIA_DCP 0000040000000LL /* Disable complete */ +#define NIA_ECP 0000020000000LL /* Enable complete */ +#define NIA_PID 0000007000000LL /* Port ID */ + +/* CONO/ CONI */ +#define NIA_CPT 0000000400000LL /* Clear Port */ +#define NIA_SEB 0000000200000LL /* Diag Select EBUF */ +#define NIA_GEB 0000000100000LL /* Diag Gen Ebus PE */ +#define NIA_LAR 0000000040000LL /* Diag select LAR */ +#define NIA_SSC 0000000020000LL /* Diag Single Cycle */ +#define NIA_EPE 0000000004000LL /* Ebus parity error */ +#define NIA_FQE 0000000002000LL /* Free Queue Error */ +#define NIA_DME 0000000001000LL /* Data mover error */ +#define NIA_CQA 0000000000400LL /* Command Queue Available */ +#define NIA_RQA 0000000000200LL /* Response Queue Available */ +#define NIA_DIS 0000000000040LL /* Disable */ +#define NIA_ENB 0000000000020LL /* Enable */ +#define NIA_MRN 0000000000010LL /* RUN */ +#define NIA_PIA 0000000000007LL /* PIA */ + +#define NIA_LRA 0400000000000LL /* Load Ram address */ +#define NIA_RAR 0377760000000LL /* Microcode address mask */ +#define NIA_MSB 0000020000000LL /* Half word select */ + +/* PCB Offsets */ +#define PCB_CQI 0 /* Command queue interlock */ +#define PCB_CQF 1 /* Command queue flink */ +#define PCB_CQB 2 /* Command queue blink */ +#define PCB_RS0 3 /* Reserved */ +#define PCB_RSI 4 /* Response queue interlock */ +#define PCB_RSF 5 /* Response queue flink */ +#define PCB_RSB 6 /* Response queue blink */ +#define PCB_RS1 7 /* Reserved */ +#define PCB_UPI 010 /* Unknown protocol queue interlock */ +#define PCB_UPF 011 /* Unknown protocol queue flink */ +#define PCB_UPB 012 /* Unknown protocol queue blink */ +#define PCB_UPL 013 /* Unknown protocol queue length */ +#define PCB_RS2 014 /* Reserved */ +#define PCB_PTT 015 /* Protocol Type Table */ +#define PCB_MCT 016 /* Multicast Table */ +#define PCB_RS3 017 /* Reserved */ +#define PCB_ER0 020 /* Error Log out 0 */ +#define PCB_ER1 021 /* Error Log out 1 */ +#define PCB_EPA 022 /* EPT Channel logout word 1 address */ +#define PCB_EPW 023 /* EPT Channel logout word 1 contents */ +#define PCB_PCB 024 /* PCB Base Address */ +#define PCB_PIA 025 /* PIA */ +#define PCB_RS4 026 /* Reserved */ +#define PCB_CCW 027 /* Channel command word */ +#define PCB_RCB 030 /* Counters base address */ + +#define CHNERR 07762 +#define SLFTST 07751 +#define INTERR 07750 +#define EBSERR 07752 + +/* 12 Bit Shift */ +#define NIA_CMD_SND 0001 /* Send a datagram */ +#define NIA_CMD_LMAC 0002 /* Load Multicast table */ +#define NIA_CMD_LPTT 0003 /* Load Protocol Type table */ +#define NIA_CMD_RCNT 0004 /* Read counts */ +#define NIA_CMD_RCV 0005 /* Received datagram */ +#define NIA_CMD_WPLI 0006 /* Write PLI */ +#define NIA_CMD_RPLI 0007 /* Read PLI */ +#define NIA_CMD_RNSA 0010 /* Read Station Address */ +#define NIA_CMD_WNSA 0011 /* Write Station Address */ + +/* 20 Bit shift */ +#define NIA_FLG_RESP 0001 /* Command wants a response */ +#define NIA_FLG_CLRC 0002 /* Clear counters (Read counters) */ +#define NIA_FLG_BSD 0010 /* Send BSD packet */ +#define NIA_FLG_PAD 0040 /* Send pad */ +#define NIA_FLG_ICRC 0100 /* Send use host CRC */ +#define NIA_FLG_PACK 0200 /* Send Pack */ +#define NIA_STS_CPE 0200 /* CRAM PE */ +#define NIA_STS_SR 0100 /* Send receive */ +#define NIA_STS_ERR 0001 /* Error bits valid */ + +/* 28 bit shift ERR +1 */ +#define NIA_ERR_ECL 000 /* Excessive collisions */ +#define NIA_ERR_CAR 001 /* Carrier check failed */ +#define NIA_ERR_COL 002 /* Collision detect failed */ +#define NIA_ERR_SHT 003 /* Short circuit */ +#define NIA_ERR_OPN 004 /* Open circuit */ +#define NIA_ERR_LNG 005 /* Frame to long */ +#define NIA_ERR_RMT 006 /* Remote failure */ +#define NIA_ERR_BLK 007 /* Block check error */ +#define NIA_ERR_FRM 010 /* Framing error */ +#define NIA_ERR_OVR 011 /* Data Overrun */ +#define NIA_ERR_PRO 012 /* Unrecongized protocol */ +#define NIA_ERR_RUN 013 /* Frame too short */ +#define NIA_ERR_WCZ 030 /* Word count not zero */ +#define NIA_ERR_QLV 031 /* Queue length violation */ +#define NIA_ERR_PLI 032 /* Illegal PLI function */ +#define NIA_ERR_UNK 033 /* Unknown command */ +#define NIA_ERR_BLV 034 /* Buffer length violation */ +#define NIA_ERR_PAR 036 /* Parity error */ +#define NIA_ERR_INT 037 /* Internal error */ + +/* Counters */ +#define NIA_CNT_BR 000 /* Bytes received */ +#define NIA_CNT_BX 001 /* Bytes transmitted */ +#define NIA_CNT_FR 002 /* Frames received */ +#define NIA_CNT_FX 003 /* Frames transmitted */ +#define NIA_CNT_MCB 004 /* Multicast bytes received */ +#define NIA_CNT_MCF 005 /* Multicast frames received */ +#define NIA_CNT_FXD 006 /* Frames xmitted, initially deferred */ +#define NIA_CNT_FXS 007 /* Frames xmitted, single collision */ +#define NIA_CNT_FXM 010 /* Frames xmitted, multiple collisions */ +#define NIA_CNT_XF 011 /* Transmit failures */ +#define NIA_CNT_XFM 012 /* Transmit failure bit mask */ +#define NIA_XFM_LOC 04000 /* B24 - Loss of carrier */ +#define NIA_XFM_XBP 02000 /* B25 - Xmit buffer parity error */ +#define NIA_XFM_RFD 01000 /* B26 - Remote failure to defer */ +#define NIA_XFM_XFL 00400 /* B27 - Xmitted frame too long */ +#define NIA_XFM_OC 00200 /* B28 - Open circuit */ +#define NIA_XFM_SC 00100 /* B29 - Short circuit */ +#define NIA_XFM_CCF 00040 /* B30 - Collision detect check failed */ +#define NIA_XFM_EXC 00020 /* B31 - Excessive collisions */ + +#define NIA_CNT_CDF 013 /* Carrier detect check failed */ +#define NIA_CNT_RF 014 /* Receive failures */ +#define NIA_CNT_RFM 015 /* Receive failure bit mask */ +#define NIA_RFM_FLE 0400 /* B27 - free list parity error */ +#define NIA_RFM_NFB 0200 /* B28 - no free buffers */ +#define NIA_RFM_FTL 0100 /* B29 - frame too long */ +#define NIA_RFM_FER 0040 /* B30 - framing error */ +#define NIA_RFM_BCE 0020 /* B31 - block check error */ + +#define NIA_CNT_DUN 016 /* Discarded unknown */ +#define NIA_CNT_D01 017 /* Discarded position 1 */ +#define NIA_CNT_D02 020 /* Discarded position 2 */ +#define NIA_CNT_D03 021 /* Discarded position 3 */ +#define NIA_CNT_D04 022 /* Discarded position 4 */ +#define NIA_CNT_D05 023 /* Discarded position 5 */ +#define NIA_CNT_D06 024 /* Discarded position 6 */ +#define NIA_CNT_D07 025 /* Discarded position 7 */ +#define NIA_CNT_D08 026 /* Discarded position 8 */ +#define NIA_CNT_D09 027 /* Discarded position 9 */ +#define NIA_CNT_D10 030 /* Discarded position 10 */ +#define NIA_CNT_D11 031 /* Discarded position 11 */ +#define NIA_CNT_D12 032 /* Discarded position 12 */ +#define NIA_CNT_D13 033 /* Discarded position 13 */ +#define NIA_CNT_D14 034 /* Discarded position 14 */ +#define NIA_CNT_D15 035 /* Discarded position 15 */ +#define NIA_CNT_D16 036 /* Discarded position 16 */ +#define NIA_CNT_UFD 037 /* Unrecognized frame dest */ +#define NIA_CNT_DOV 040 /* Data overrun */ +#define NIA_CNT_SBU 041 /* System buffer unavailable */ +#define NIA_CNT_UBU 042 /* User buffer unavailable */ +#define NIA_CNT_RS0 043 /* Pli reg rd par error,,pli parity error */ +#define NIA_CNT_RS1 044 /* Mover parity error,,cbus parity error */ +#define NIA_CNT_RS2 045 /* Ebus parity error,,ebus que parity error */ +#define NIA_CNT_RS3 046 /* Channel error,,spur channel error */ +#define NIA_CNT_RS4 047 /* Spur xmit attn error,,cbus req timout error */ +#define NIA_CNT_RS5 050 /* Ebus req timeout error,,csr grnt timeout error */ +#define NIA_CNT_RS6 051 /* Used buff parity error,,xmit buff parity error */ +#define NIA_CNT_RS7 052 /* Reserved for ucode */ +#define NIA_CNT_RS8 053 /* Reserved for ucode */ +#define NIA_CNT_LEN 054 /* # of counters */ + +#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 + +typedef uint32 in_addr_T; + +#define ETHTYPE_ARP 0x0806 +#define ETHTYPE_IP 0x0800 + +PACKED_BEGIN +struct nia_eth_hdr { + ETH_MAC dest; + ETH_MAC src; + uint16 type; +} PACKED_END; + +/* + * 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 */ +#define TCP_FL_FIN 0x01 +#define TCP_FL_SYN 0x02 +#define TCP_FL_RST 0x04 +#define TCP_FL_PSH 0x08 +#define TCP_FL_ACK 0x10 +#define TCP_FL_URG 0x20 + 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 nia_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 nia_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 nia_device { + ETH_PCALLBACK rcallback; /* read callback routine */ + ETH_PCALLBACK wcallback; /* write callback routine */ + ETH_MAC mac; /* Hardware MAC addresses */ + ETH_DEV etherface; + ETH_QUE ReadQ; + ETH_PACK rec_buff; /* Buffer for recieved packet */ + ETH_PACK snd_buff; /* Buffer for sending packet */ + t_addr cmd_entry; /* Pointer to current command entry */ + t_addr cmd_rply; /* Pointer to reply entry */ + uint8 cmd_status; /* Status feild of current command */ + t_addr rec_entry; /* Pointer to current recieve entry */ + t_addr pcb; /* Address of PCB */ + t_addr rcb; /* Read count buffer address */ +#define cmd_hdr pcb /* Command queue is at PCB */ + t_addr resp_hdr; /* Head of response queue */ + t_addr unk_hdr; /* Unknown protocol free queue */ + int unk_len; /* Length of Unknown entries */ + t_addr ptt_addr; /* Address of Protocol table */ + t_addr mcast_addr; /* Address of Multicast table */ + int pia; /* Interrupt channel */ + t_addr cnt_addr; /* Address of counters */ + uint64 pcnt[NIA_CNT_LEN]; /* Counters */ + + int ptt_n; /* Number of Protocol entries */ + uint16 ptt_proto[17]; /* Protocol for entry */ + t_addr ptt_head[17]; /* Head of protocol queue */ + int macs_n; /* Number of multi-cast addresses */ + ETH_MAC macs[20]; /* Watched Multi-cast addresses */ + int amc; /* Recieve all multicast packets */ + int prmsc; /* Recieve all packets */ + int h4000; /* Heart beat detection */ + int rar; + uint64 ebuf; + uint64 status; /* Status of device. */ + uint32 uver[4]; /* Version information */ + int r_pkt; /* Packet pending */ +} nia_data; + + +extern int32 tmxr_poll; + +static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff}; + +t_stat nia_devio(uint32 dev, uint64 *data); +void nia_start(); +void nia_stop(); +void nia_enable(); +void nia_disable(); +void nia_load_ptt(); +void nia_load_mcast(); +void nia_error(int); +t_stat nia_eth_srv(UNIT *); +t_stat nia_rec_srv(UNIT *); +t_stat nia_cmd_srv(UNIT *); +t_stat nia_reset (DEVICE *dptr); +t_stat nia_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat nia_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat nia_attach (UNIT * uptr, CONST char * cptr); +t_stat nia_detach (UNIT * uptr); +t_stat nia_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +const char *nia_description (DEVICE *dptr); + +struct rh_if nia_rh = { NULL, NULL, NULL}; + +UNIT nia_unit[] = { + {UDATA(nia_eth_srv, UNIT_IDLE+UNIT_ATTABLE, 0)}, /* 0 */ + {UDATA(nia_rec_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 1 */ + {UDATA(nia_cmd_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 2 */ +}; + +REG nia_reg[] = { + {SAVEDATA(DATA, nia_data) }, + {0} +}; + + +#define nia_cmd_uptr (&nia_unit[2]) /* Unit for processing commands */ +#define nia_recv_uptr (&nia_unit[0]) /* Unit doing receive digestion */ +#define nia_proc_uptr (&nia_unit[1]) /* Unit doing receive dispatching */ + +DIB nia_dib = {NIA_DEVNUM | RH20_DEV, 1, &nia_devio, NULL, &nia_rh }; + +MTAB nia_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", + &nia_set_mac, &nia_show_mac, NULL, "MAC address" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "ETH", NULL, NULL, + ð_show, NULL, "Display attachedable devices" }, + { 0 } + }; + +/* Simulator debug controls */ +DEBTAB nia_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"}, + {"IRQ", DEBUG_IRQ, "Show IRQ requests"}, +#define DEBUG_ARP (DEBUG_IRQ<<1) + {"ARP", DEBUG_ARP, "Show ARP activities"}, +#define DEBUG_TCP (DEBUG_ARP<<1) + {"TCP", DEBUG_TCP, "Show TCP packet activities"}, +#define DEBUG_UDP (DEBUG_TCP<<1) + {"UDP", DEBUG_UDP, "Show UDP packet activities"}, +#define DEBUG_ICMP (DEBUG_UDP<<1) + {"ICMP", DEBUG_ICMP, "Show ICMP packet activities"}, +#define DEBUG_ETHER (DEBUG_ICMP<<1) + {"ETHER", DEBUG_ETHER, "Show ETHER activities"}, + {0, 0} +}; + +DEVICE nia_dev = { + "NI", nia_unit, nia_reg, nia_mod, + 3, 8, 0, 1, 8, 36, + NULL, NULL, &nia_reset, NULL, &nia_attach, &nia_detach, + &nia_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, nia_debug, + NULL, NULL, &nia_help, NULL, NULL, &nia_description +}; + +t_stat nia_devio(uint32 dev, uint64 *data) +{ + DEVICE *dptr = &nia_dev; + UNIT *uptr = nia_cmd_uptr; + + switch(dev & 03) { + case CONO: + if (*data & NIA_CPT) + nia_reset(dptr); + + nia_data.status &= ~(NIA_SEB|NIA_LAR|NIA_SSC|NIA_DIS|NIA_ENB|NIA_PIA); + nia_data.status |= *data & (NIA_SEB|NIA_LAR|NIA_SSC|NIA_DIS|NIA_ENB|NIA_PIA); + nia_data.status &= ~(*data & (NIA_EPE|NIA_FQE|NIA_DME|NIA_RQA)); + clr_interrupt(NIA_DEVNUM); + if (*data & NIA_MRN) { + if ((nia_data.status & NIA_MRN) == 0) + nia_start(); + } else { + if ((nia_data.status & NIA_MRN) != 0) + nia_stop(); + } + if (*data & NIA_ENB) { + if ((nia_data.status & NIA_MRN) != 0 && + (nia_data.status & NIA_ECP) == 0) + nia_enable(); + else + nia_data.status |= NIA_ECP; + } else + nia_data.status &= ~NIA_ECP; + if (*data & NIA_DIS) { + if ((nia_data.status & NIA_MRN) != 0 && + (nia_data.status & NIA_DCP) == 0) + nia_disable(); + else + nia_data.status |= NIA_DCP; + } else + nia_data.status &= ~NIA_DCP; + if (*data & NIA_CQA && + (nia_data.status & NIA_CQA) == 0 && + (nia_data.status & NIA_MRN) != 0) { + nia_data.status |= NIA_CQA; + sim_activate(uptr, 200); + } + if (nia_data.status & (NIA_CPE|NIA_RQA)) + set_interrupt(NIA_DEVNUM, nia_data.status & NIA_PIA); + sim_debug(DEBUG_CONO, dptr, "NIA %03o CONO %06o PC=%06o %012llo\n", dev, + (uint32)(*data & RMASK), PC, nia_data.status); + break; + case CONI: + *data = nia_data.status|NIA_PPT|NIA_PID; + sim_debug(DEBUG_CONI, dptr, "NIA %03o CONI %012llo PC=%o\n", dev, + *data, PC); + break; + case DATAO: + if (nia_data.status & NIA_SEB) { + nia_data.ebuf = *data; + } else { + if (*data & NIA_LRA) { + nia_data.rar = (uint32)((*data & NIA_RAR) >> 22); + sim_debug(DEBUG_DETAIL, dptr, "NIA %03o set RAR=%o\n", + dev, nia_data.rar); + } else { + if (nia_data.rar >= 0274 && nia_data.rar <= 0277) + nia_data.uver[nia_data.rar - 0274] = (uint32)(*data & RMASK); + sim_debug(DEBUG_DETAIL, dptr, "NIA %03o set data=%o %06o\n", + dev, nia_data.rar, (uint32)(*data & RMASK)); + } + } + sim_debug(DEBUG_DATAIO, dptr, "NIA %03o DATO %012llo PC=%o\n", + dev, *data, PC); + break; + case DATAI: + if (nia_data.status & NIA_SEB) { + *data = nia_data.ebuf; + } else { + if (nia_data.status & NIA_LAR) { + *data = ((uint64)nia_data.rar) << 20; + *data &= ~NIA_MSB; + *data |= NIA_LRA; + } else if (nia_data.rar >= 0274 && nia_data.rar <= 0277) { + *data = (uint64)nia_data.uver[nia_data.rar - 0274]; + } + } + sim_debug(DEBUG_DATAIO, dptr, "NIA %03o DATI %012llo PC=%o\n", + dev, *data, PC); + break; + } + + return SCPE_OK; +} + +static char * +ipv4_inet_ntoa(struct in_addr ip) +{ + static char str[20]; + + if (sim_end) + 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); + else + sprintf (str, "%d.%d.%d.%d", (ip.s_addr >> 24) & 0xFF, + (ip.s_addr >> 16) & 0xFF, + (ip.s_addr >> 8) & 0xFF, + ip.s_addr & 0xFF); + return str; +} + +/* + * Set error code and stop. + */ +void nia_error(int err) +{ + nia_data.rar = err; + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA error %03o\n", err); + nia_data.status |= NIA_CPE; + set_interrupt(NIA_DEVNUM, nia_data.status & NIA_PIA); + nia_stop(); +} + +/* + * Start NIA device, load in 2 words using RH20 mode. + */ +void nia_start() +{ + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA start\n"); + /* Set up RH20 to read 2 words */ + nia_rh.stcr = BIT7; + nia_rh.imode = 2; + rh20_setup(&nia_rh); + /* Read PCB address */ + if (!rh_read(&nia_rh)) { + nia_error(CHNERR); + return; + } + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA PCB %012llo %o\n", nia_rh.buf, + nia_rh.wcr); + nia_data.pcb = (t_addr)(nia_rh.buf & AMASK); + nia_data.resp_hdr = (t_addr)((nia_rh.buf + 4) & AMASK); + nia_data.unk_hdr = (t_addr)((nia_rh.buf + 8) & AMASK); + /* Read PIA value */ + if (!rh_read(&nia_rh)) { + nia_error(CHNERR); + return; + } + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA PIA %012llo %o\n", nia_rh.buf, + nia_rh.wcr); + nia_data.pia = (int)(nia_rh.buf & 7); + nia_data.status |= NIA_MRN; + memcpy(&nia_data.macs[0], &nia_data.mac, sizeof (ETH_MAC)); + memcpy(&nia_data.macs[1], &broadcast_ethaddr, sizeof(ETH_MAC)); +} + +void nia_stop() +{ + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA stop\n"); + nia_data.status &= ~NIA_MRN; +} + +/* + * Enable NIA 20. + * + * Read in PTT and MACS table. + */ +void nia_enable() +{ + uint64 word; + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA enable\n"); + /* Load pointers to various table */ + if (Mem_read_word(nia_data.unk_hdr + PCB_UPL, &word, 0)) { + nia_error(EBSERR); + return; + } + nia_data.unk_len = (int)(word & AMASK); + /* Load PTT */ + if (Mem_read_word(nia_data.pcb + PCB_PTT, &word, 0)) { + nia_error(EBSERR); + return; + } + nia_data.ptt_addr = (t_addr)(word & AMASK); + nia_load_ptt(); + /* Load MCT */ + if (Mem_read_word(nia_data.pcb + PCB_MCT, &word, 0)) { + nia_error(EBSERR); + return; + } + nia_data.mcast_addr = (t_addr)(word & AMASK); + nia_load_mcast(); + /* Load read count buffer address */ + if (Mem_read_word(nia_data.pcb + PCB_RCB, &word, 0)) { + nia_error(EBSERR); + return; + } + nia_data.rcb = (t_addr)(word & AMASK); + nia_data.status |= NIA_ECP; + nia_data.status &= ~NIA_DCP; +} + +/* + * Disable NIA 20. + */ +void nia_disable() +{ + nia_data.status |= NIA_DCP; + nia_data.status &= ~NIA_ECP; +} + +/* + * Copy a MAC address from string to memory word. + */ +void nia_cpy_mac(uint64 word1, uint64 word2, ETH_MAC *mac) +{ + ETH_MAC m; + m[0] = (unsigned char)((word1 >> 28) & 0xff); + m[1] = (unsigned char)((word1 >> 20) & 0xff); + m[2] = (unsigned char)((word1 >> 12) & 0xff); + m[3] = (unsigned char)((word1 >> 4) & 0xff); + m[4] = (unsigned char)((word2 >> 28) & 0xff); + m[5] = (unsigned char)((word2 >> 20) & 0xff); + memcpy(mac, &m, sizeof(ETH_MAC)); +} + +/* + * Copy memory to a packet. + */ +uint8 *nia_cpy_to(t_addr addr, uint8 *data, int len) +{ + uint64 word; + /* Copy full words */ + while (len > 3) { + word = M[addr++]; + *data++ = (uint8)((word >> 28) & 0xff); + *data++ = (uint8)((word >> 20) & 0xff); + *data++ = (uint8)((word >> 12) & 0xff); + *data++ = (uint8)((word >> 4) & 0xff); + len -= 4; + } + /* Grab last partial word */ + if (len) { + word = M[addr++]; + switch (len) { + case 3: + *data++ = (uint8)((word >> 28) & 0xff); + *data++ = (uint8)((word >> 20) & 0xff); + *data++ = (uint8)((word >> 12) & 0xff); + break; + case 2: + *data++ = (uint8)((word >> 28) & 0xff); + *data++ = (uint8)((word >> 20) & 0xff); + break; + case 1: + *data++ = (uint8)((word >> 28) & 0xff); + break; + } + } + return data; +} + +/* + * Copy a packet to memory. + */ +uint8 *nia_cpy_from(t_addr addr, uint8 *data, int len) +{ + uint64 word; + + /* Copy full words */ + while (len > 3) { + word = (uint64)(*data++) << 28; + word |= (uint64)(*data++) << 20; + word |= (uint64)(*data++) << 12; + word |= (uint64)(*data++) << 4; + M[addr++] = word; + len -= 4; + } + /* Copy last partial word */ + if (len) { + switch (len) { + case 3: + word = (uint64)(*data++) << 28; + word |= (uint64)(*data++) << 20; + word |= (uint64)(*data++) << 12; + break; + case 2: + word = (uint64)(*data++) << 28; + word |= (uint64)(*data++) << 20; + break; + case 1: + word = (uint64)(*data++) << 28; + break; + } + } + M[addr++] = word; + return data; +} + +/* + * Get next entry off a queue. + * Return entry in *entry and 1 if successfull. + * Returns 0 if fail, queue locked or memory out of bounds. + */ +int nia_getq(t_addr head, t_addr *entry) +{ + uint64 temp; + t_addr flink; + t_addr nlink; + *entry = 0; /* For safty */ + + /* Try and get lock */ + if (Mem_read_word(head, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + /* Check if entry locked */ + if ((temp & SMASK) == 0) + return 0; + + /* Increment lock here */ + + /* Get head of queue */ + if (Mem_read_word(head + 1, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + flink = (t_addr)(temp & AMASK); + /* Check if queue empty */ + if (flink == (head+1)) { + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA empty %08o\n", head); + /* Decrement lock here */ + return 1; + } + /* Get link to next entry */ + if (Mem_read_word(flink + 1, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + nlink = (t_addr)(temp & AMASK); + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA head: q=%08o f=%08o n=%08o\n", + head, flink, nlink); + /* Set Head Flink to point to next */ + temp = (uint64)nlink; + if (Mem_write_word(head+1, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + /* Set Next Blink to head */ + temp = (uint64)(head + 1); + if (Mem_write_word(nlink+1, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + /* Return entry */ + *entry = flink; + + /* Decrement lock here */ + return 1; +} + + +/* + * Put entry on head of queue. + * Return entry in *entry and 1 if successfull. + * Returns 0 if fail, queue locked or memory out of bounds. + */ +int nia_putq(t_addr head, t_addr *entry) +{ + uint64 temp; + t_addr blink; + + /* Try and get lock */ + if (Mem_read_word(head, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + /* Check if entry locked */ + if ((temp & SMASK) == 0) + return 0; + + /* Increment lock here */ + + /* Hook entry into tail of queue */ + if (Mem_read_word(head + 2, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + + blink = (t_addr)(temp & AMASK); /* Get back link */ + /* Get link to previous entry */ + temp = (uint64)*entry; + if (Mem_write_word(blink, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + /* Old forward is new */ + if (Mem_write_word(head+2, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + + /* Flink is head of queue */ + temp = (uint64)(head+1); + if (Mem_write_word(*entry, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + + /* Back link points to previous */ + temp = (uint64)blink; + if (Mem_write_word(*entry+1, &temp, 0)) { + nia_error(EBSERR); + return 0; + } + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA put: q=%08o i=%08o b=%08o\n", + head, *entry, blink); + *entry = 0; + /* Decement lock here */ + + /* Check if Queue was empty, and response queue */ + if (blink == (head+1) && head == nia_data.resp_hdr) { + nia_data.status |= NIA_RQA; + set_interrupt(NIA_DEVNUM, nia_data.pia); + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA set response\n"); + } + return 1; +} + + +/* + * Load in the protocol type table + */ +void nia_load_ptt() +{ + int i; + int n = 0; + t_addr addr = nia_data.ptt_addr; + + for (i = 0; i < 17; i++) { + uint64 word1, word2; + + /* Get entry */ + if (Mem_read_word(addr++, &word1, 0)) { + nia_error(EBSERR); + return; + } + if (Mem_read_word(addr++, &word2, 0)) { + nia_error(EBSERR); + return; + } + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %012llo %012llo\n\r", + n, word1, word2); + if (word1 & SMASK) { + uint16 type; + type = (uint16)(word1 >> 12) & 0xff; + type |= (uint16)(word1 << 4) & 0xff00; + nia_data.ptt_proto[n] = type; + nia_data.ptt_head[n] = (t_addr)(word2 &AMASK) - 1; + n++; + } + addr++; + } + for (i = 0; i < n; i++) + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %04x %010o\n\r", + n, nia_data.ptt_proto[i], nia_data.ptt_head[i]); + nia_data.ptt_n = n; +} + +/* + * Load in the multi-cast table + */ +void nia_load_mcast() +{ + int i; + int n = 0; + char buffer[20]; + t_addr addr = nia_data.mcast_addr; + + /* Start with our own address. */ + memcpy(&nia_data.macs[n], &nia_data.mac, sizeof (ETH_MAC)); + n++; + memcpy(&nia_data.macs[n], &broadcast_ethaddr, sizeof (ETH_MAC)); + n++; + for (i = 0; i < 17; i++) { + uint64 word1, word2; + + if (Mem_read_word(addr++, &word1, 0)) { + nia_error(EBSERR); + return; + } + if (Mem_read_word(addr++, &word2, 0)) { + nia_error(EBSERR); + return; + } + if (word2 & 1) { + nia_cpy_mac(word1, word2, &nia_data.macs[n]); + n++; + } + } + for(i = 0; i< n; i++) { + eth_mac_fmt(&nia_data.macs[i], buffer); + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load mcast%d: %s\n\r",i,buffer); + } + nia_data.macs_n = n - 2; + if (nia_recv_uptr->flags & UNIT_ATT) + eth_filter (&nia_data.etherface, n, nia_data.macs, nia_data.amc, + nia_data.prmsc); +} + +/* + * Pretty print a packet for debugging. + */ +void nia_packet_debug(struct nia_device *nia, const char *action, + ETH_PACK *packet) { + struct nia_eth_hdr *eth = (struct nia_eth_hdr *)&packet->msg[0]; + struct arp_hdr *arp = (struct arp_hdr *)eth; + struct ip *ip = (struct ip *)&packet->msg[sizeof(struct nia_eth_hdr)]; + struct udp *udp; + struct tcp *tcp; + struct icmp *icmp; + uint8 *payload; + struct in_addr ipaddr; + size_t len; + int flag; + char src_ip[20]; + char dst_ip[20]; + char src_port[8]; + char dst_port[8]; + char flags[64]; + static struct tcp_flag_bits { + const char *name; + uint16 bitmask; + } bits[] = { + {"FIN", TCP_FL_FIN}, + {"SYN", TCP_FL_SYN}, + {"RST", TCP_FL_RST}, + {"PSH", TCP_FL_PSH}, + {"ACK", TCP_FL_ACK}, + {"URG", TCP_FL_URG}, + {NULL, 0} + }; + static const char *icmp_types[] = { + "Echo Reply", // Type 0 + "Type 1 - Unassigned", + "Type 2 - Unassigned", + "Destination Unreachable", // Type 3 + "Source Quench (Deprecated)", // Type 4 + "Redirect", // Type 5 + "Type 6 - Alternate Host Address (Deprecated)", + "Type 7 - Unassigned", + "Echo Request", // Type 8 + "Router Advertisement", // Type 9 + "Router Selection", // Type 10 + "Time Exceeded", // Type 11 + "Type 12 - Parameter Problem", + "Type 13 - Timestamp", + "Type 14 - Timestamp Reply", + "Type 15 - Information Request (Deprecated)", + "Type 16 - Information Reply (Deprecated)", + "Type 17 - Address Mask Request (Deprecated)", + "Type 18 - Address Mask Reply (Deprecated)", + "Type 19 - Reserved (for Security)", + "Type 20 - Reserved (for Robustness Experiment)", + "Type 21 - Reserved (for Robustness Experiment)", + "Type 22 - Reserved (for Robustness Experiment)", + "Type 23 - Reserved (for Robustness Experiment)", + "Type 24 - Reserved (for Robustness Experiment)", + "Type 25 - Reserved (for Robustness Experiment)", + "Type 26 - Reserved (for Robustness Experiment)", + "Type 27 - Reserved (for Robustness Experiment)", + "Type 28 - Reserved (for Robustness Experiment)", + "Type 29 - Reserved (for Robustness Experiment)", + "Type 30 - Traceroute (Deprecated)", + "Type 31 - Datagram Conversion Error (Deprecated)", + "Type 32 - Mobile Host Redirect (Deprecated)", + "Type 33 - IPv6 Where-Are-You (Deprecated)", + "Type 34 - IPv6 I-Am-Here (Deprecated)", + "Type 35 - Mobile Registration Request (Deprecated)", + "Type 36 - Mobile Registration Reply (Deprecated)", + "Type 37 - Domain Name Request (Deprecated)", + "Type 38 - Domain Name Reply (Deprecated)", + "Type 39 - SKIP (Deprecated)", + "Type 40 - Photuris", + "Type 41 - ICMP messages utilized by experimental mobility protocols such as Seamoby", + "Type 42 - Extended Echo Request", + "Type 43 - Extended Echo Reply" + }; + + if (ntohs(eth->type) == ETHTYPE_ARP) { + struct in_addr in_addr; + const char *arp_op = (ARP_REQUEST == ntohs(arp->opcode)) ? "REQUEST" : ((ARP_REPLY == ntohs(arp->opcode)) ? "REPLY" : "Unknown"); + char eth_src[20], eth_dst[20]; + char arp_shwaddr[20], arp_dhwaddr[20]; + char arp_sipaddr[20], arp_dipaddr[20]; + + if (!(nia_dev.dctrl & DEBUG_ARP)) + return; + eth_mac_fmt(&arp->ethhdr.src, eth_src); + eth_mac_fmt(&arp->ethhdr.dest, eth_dst); + eth_mac_fmt(&arp->shwaddr, arp_shwaddr); + memcpy(&in_addr, &arp->sipaddr, sizeof(in_addr)); + strlcpy(arp_sipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_sipaddr)); + eth_mac_fmt(&arp->dhwaddr, arp_dhwaddr); + memcpy(&in_addr, &arp->dipaddr, sizeof(in_addr)); + strlcpy(arp_dipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_dipaddr)); + sim_debug(DEBUG_ARP, &nia_dev, + "%s %s EthDst=%s EthSrc=%s shwaddr=%s sipaddr=%s dhwaddr=%s dipaddr=%s\n", + action, arp_op, eth_dst, eth_src, arp_shwaddr, arp_sipaddr, arp_dhwaddr, arp_dipaddr); + return; + } + if (ntohs(eth->type) != ETHTYPE_IP) { + payload = (uint8 *)&packet->msg[sizeof(struct nia_eth_hdr)]; + len = packet->len - sizeof(struct nia_eth_hdr); + sim_data_trace(&nia_dev, nia_unit, payload, "", len, "", DEBUG_DATA); + return; + } + if (!(nia_dev.dctrl & (DEBUG_TCP|DEBUG_UDP|DEBUG_ICMP))) + return; + memcpy(&ipaddr, &ip->ip_src, sizeof(ipaddr)); + strlcpy(src_ip, ipv4_inet_ntoa(ipaddr), sizeof(src_ip)); + memcpy(&ipaddr, &ip->ip_dst, sizeof(ipaddr)); + strlcpy(dst_ip, ipv4_inet_ntoa(ipaddr), sizeof(dst_ip)); + payload = (uint8 *)&packet->msg[sizeof(struct nia_eth_hdr) + (ip->ip_v_hl & 0xf) * 4]; + switch (ip->ip_p) { + case UDP_PROTO: + udp = (struct udp *)payload; + snprintf(src_port, sizeof(src_port), "%d", ntohs(udp->udp_sport)); + snprintf(dst_port, sizeof(dst_port), "%d", ntohs(udp->udp_dport)); + sim_debug(DEBUG_UDP, &nia_dev, "%s %d byte packet from %s:%s to %s:%s\n", action, + ntohs(udp->len), src_ip, src_port, dst_ip, dst_port); + if (udp->len && (nia_dev.dctrl & DEBUG_UDP)) + sim_data_trace(&nia_dev, nia_unit, payload + sizeof(struct udp), "", + ntohs(udp->len), "", DEBUG_DATA); + break; + case TCP_PROTO: + tcp = (struct tcp *)payload; + snprintf(src_port, sizeof(src_port), "%d", ntohs(tcp->tcp_sport)); + snprintf(dst_port, sizeof(dst_port), "%d", ntohs(tcp->tcp_dport)); + strlcpy(flags, "", sizeof(flags)); + for (flag=0; bits[flag].name; flag++) { + if (ntohs(tcp->flags) & bits[flag].bitmask) { + if (*flags) + strlcat(flags, ",", sizeof(flags)); + strlcat(flags, bits[flag].name, sizeof(flags)); + } + + } + len = ntohs(ip->ip_len) - ((ip->ip_v_hl & 0xf) * 4 + (ntohs(tcp->flags) >> 12) * 4); + sim_debug(DEBUG_TCP, &nia_dev, "%s %s%s %d byte packet from %s:%s to %s:%s\n", action, + flags, *flags ? ":" : "", (int)len, src_ip, src_port, dst_ip, dst_port); + if (len && (nia_dev.dctrl & DEBUG_TCP)) + sim_data_trace(&nia_dev, nia_unit, payload + 4 * (ntohs(tcp->flags) >> 12), "", len, "", DEBUG_DATA); + break; + case ICMP_PROTO: + icmp = (struct icmp *)payload; + len = ntohs(ip->ip_len) - (ip->ip_v_hl & 0xf) * 4; + sim_debug(DEBUG_ICMP, &nia_dev, "%s %s %d byte packet from %s to %s\n", action, + (icmp->type < sizeof(icmp_types)/sizeof(icmp_types[0])) ? icmp_types[icmp->type] : "", (int)len, src_ip, dst_ip); + if (len && (nia_dev.dctrl & DEBUG_ICMP)) + sim_data_trace(&nia_dev, nia_unit, payload + sizeof(struct icmp), "", len, "", DEBUG_DATA); + break; + } +} + + +/* + * Send out a packet. + */ +int nia_send_pkt(uint64 cmd) +{ + uint64 word1, word2; + struct nia_eth_hdr *hdr = (struct nia_eth_hdr *)(&nia_data.snd_buff.msg[0]); + uint8 *data = &nia_data.snd_buff.msg[sizeof(struct nia_eth_hdr)]; + ETH_MAC dest; + uint16 type; + int len; + int blen; + + /* Read packet length */ + if (Mem_read_word(nia_data.cmd_entry + 4, &word1, 0)) { + nia_error(EBSERR); + return 0; + } + len = (int)(word1 & 0177777); + blen = len + sizeof(struct nia_eth_hdr); + /* Check for runt packet */ + if (blen < ETH_MIN_PACKET && (cmd & (NIA_FLG_PAD << 8)) == 0) { + return NIA_ERR_RUN; + } + /* Check for too long of a packet */ + if (blen > ETH_MAX_PACKET) { + nia_data.pcnt[NIA_CNT_XF]++; + nia_data.pcnt[NIA_CNT_XFM] |= NIA_XFM_XFL; + return NIA_ERR_LNG; + } + /* If unit not attached, nothing more we can do */ + if ((nia_recv_uptr->flags & UNIT_ATT) == 0) + return 0; + /* Read protocol type */ + if (Mem_read_word(nia_data.cmd_entry + 5, &word1, 0)) { + nia_error(EBSERR); + return 0; + } + type = (uint16)(word1 >> 12) & 0xff; + type |= (uint16)(word1 << 4) & 0xff00; + hdr->type = htons(type); + + /* Load destination address */ + if (Mem_read_word(nia_data.cmd_entry + 7, &word1, 0)) { + nia_error(EBSERR); + return 0; + } + if (Mem_read_word(nia_data.cmd_entry + 8, &word2, 0)) { + nia_error(EBSERR); + return 0; + } + nia_cpy_mac(word1, word2, &dest); + memcpy(hdr->dest, dest, sizeof(ETH_MAC)); + /* Copy our address over */ + memcpy(hdr->src, nia_data.mac, sizeof(ETH_MAC)); + /* Set packet length */ + nia_data.snd_buff.len = len + sizeof(struct nia_eth_hdr); + /* Copy over rest of packet */ + if (cmd & (NIA_FLG_BSD << 8)) { + if (Mem_read_word(nia_data.cmd_entry + 9, &word1, 0)) { + nia_error(EBSERR); + return 0; + } + while (len > 0) { + uint64 tlen; + /* Read pointer to buffer */ + if (Mem_read_word((t_addr)(word1 & AMASK), &word2, 0)) { + nia_error(EBSERR); + return 0; + } + /* Read length */ + if (Mem_read_word((t_addr)((word1+2) & AMASK), &tlen, 0)) { + nia_error(EBSERR); + return 0; + } + blen = (int)(tlen & 0177777); + data = nia_cpy_to((t_addr)(word2 & AMASK), data, blen); + len -= blen; + if (Mem_read_word((t_addr)((word1 + 1) & AMASK), &word1, 0)) { + nia_error(EBSERR); + return 0; + } + } + } else { + data = nia_cpy_to(nia_data.cmd_entry + 9, data, len); + } + if (((cmd & (NIA_FLG_PAD << 8)) != 0) && + nia_data.snd_buff.len < ETH_MIN_PACKET) { + while (nia_data.snd_buff.len < ETH_MIN_PACKET) { + *data = 0; + nia_data.snd_buff.len++; + } + } + nia_packet_debug(&nia_data, "send", &nia_data.snd_buff); + if (eth_write(&nia_data.etherface, &nia_data.snd_buff, NULL) != SCPE_OK) { + nia_data.pcnt[NIA_CNT_XF]++; + nia_data.pcnt[NIA_CNT_XFM] |= NIA_XFM_LOC; + } + nia_data.pcnt[NIA_CNT_BX] += nia_data.snd_buff.len; + nia_data.pcnt[NIA_CNT_FX] ++; + return 0; +} + +/* + * Process commands. + */ +t_stat nia_cmd_srv(UNIT * uptr) +{ + t_addr free_q = nia_data.unk_hdr; + uint64 word1, word2; + uint32 cmd; + int len, i; + int err; + + /* See if we have command that we could not respond too */ + if (nia_data.cmd_entry != 0) { + /* Have to put this either on response queue or free queue */ + if (nia_putq(nia_data.cmd_rply, &nia_data.cmd_entry) == 0){ + sim_activate(uptr, 200); /* Reschedule ourselves to deal with it */ + return SCPE_OK; + } + nia_data.cmd_rply = 0; + } + + /* Check if we are running */ + if ((nia_data.status & NIA_MRN) == 0 || (nia_data.status & NIA_CQA) == 0) { + return SCPE_OK; + } + + /* or no commands pending, just idle out */ + /* Try to get command off queue */ + if (nia_getq(nia_data.cmd_hdr, &nia_data.cmd_entry) == 0) { + sim_activate(uptr, 200); /* Reschedule ourselves to deal with it */ + return SCPE_OK; + } + + /* Check if we got one */ + if (nia_data.cmd_entry == 0) { + /* Nothing to do */ + nia_data.status &= ~NIA_CQA; + return SCPE_OK; + } + + /* Get command */ + if (Mem_read_word(nia_data.cmd_entry + 3, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + cmd = (uint32)(word1 >> 12); + /* Save initial status */ + nia_data.cmd_status = ((uint8)(cmd >> 16)) & 0xff; + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA cmd: %08x\n", cmd); + cmd &= 0xffff; + len = 5; + /* Preform function of command */ + switch(cmd & 0xff) { + case NIA_CMD_SND: /* Send a datagram */ + err = nia_send_pkt(cmd); + if (err != 0) + cmd |= ((err<<1)|1) << 16; + cmd |= NIA_STS_SR << 16; + len = 10; + break; + case NIA_CMD_LPTT: /* Load Protocol Type table */ + nia_load_ptt(); + break; + case NIA_CMD_LMAC: /* Load Multicast table */ + nia_load_mcast(); + break; + case NIA_CMD_RCNT: /* Read counts */ + for (i = 0; i < NIA_CNT_LEN; i++) { + word1 = nia_data.pcnt[i]; + if (Mem_write_word(nia_data.cnt_addr + i, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + if ((cmd & (NIA_FLG_CLRC << 20)) != 0) + nia_data.pcnt[i] = 0; + } + break; + case NIA_CMD_WPLI: /* Write PLI */ + break; + case NIA_CMD_RPLI: /* Read PLI */ + break; + case NIA_CMD_RNSA: /* Read Station Address */ + len = 8; + word1 = ((uint64)nia_data.mac[0]) << 28; + word1 |= ((uint64)nia_data.mac[1]) << 20; + word1 |= ((uint64)nia_data.mac[2]) << 12; + word1 |= ((uint64)nia_data.mac[3]) << 4; + word2 = ((uint64)nia_data.mac[4]) << 28; + word2 |= ((uint64)nia_data.mac[5]) << 20; + if (Mem_write_word(nia_data.cmd_entry + 4, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + if (Mem_write_word(nia_data.cmd_entry + 5, &word2, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + word1 = (uint64)((nia_data.amc << 2)| (nia_data.h4000 << 1) + | nia_data.prmsc); + /* Micro code version, PTT len MACS len */ + word2 = (nia_data.uver[3] << 12) |(0xF << 6)|0xF; + if (Mem_write_word(nia_data.cmd_entry + 6, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + if (Mem_write_word(nia_data.cmd_entry + 7, &word2, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + break; + case NIA_CMD_WNSA: /* Write Station Address */ + len = 8; + if (Mem_read_word(nia_data.cmd_entry + 4, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + if (Mem_read_word(nia_data.cmd_entry + 5, &word2, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + nia_cpy_mac(word1, word2, &nia_data.mac); + if (Mem_read_word(nia_data.cmd_entry + 6, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + if (Mem_read_word(nia_data.cmd_entry + 7, &word2, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + nia_data.prmsc = (int)(word1 & 1); + nia_data.h4000 = (int)((word1 & 2) != 0); + nia_data.amc = (int)((word1 & 4) != 0); + memcpy(&nia_data.macs[0], &nia_data.mac, sizeof (ETH_MAC)); + if (nia_recv_uptr->flags & UNIT_ATT) + eth_filter (&nia_data.etherface, nia_data.macs_n + 2, + nia_data.macs, 0, 0); + break; + + case NIA_CMD_RCV: /* Received datagram */ + default: /* Invalid command */ + cmd |= ((NIA_ERR_UNK<<1)|1) << 16; + break; + } + + nia_data.cmd_rply = nia_data.unk_hdr; + word1 = ((uint64)cmd) << 12; + if (Mem_write_word(nia_data.cmd_entry + 3, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + if (((cmd >> 16) & 1) != 0 || (cmd & (NIA_FLG_RESP << 8)) != 0) { + nia_data.cmd_rply = nia_data.resp_hdr; + } else if ((cmd & 0xff) == NIA_CMD_SND) { + if (Mem_read_word(nia_data.cmd_entry + 5, &word1, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + nia_data.cmd_rply = (t_addr)(word1 & AMASK); + } + for(i = 0; i < len; i++) + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA rcmd: %d %09llx %012llo\n", + i, M[nia_data.cmd_entry + i], M[nia_data.cmd_entry + i]); + (void)nia_putq(nia_data.cmd_rply, &nia_data.cmd_entry); + sim_activate(uptr, 500); + return SCPE_OK; +} + + +int +nia_rec_pkt() +{ + struct nia_eth_hdr *hdr; + uint16 type; + int i; + int len; + t_addr queue; + t_addr bsd; + uint64 word; + uint8 *data; + + /* See if we have received packet to process */ + if (nia_data.rec_entry != 0) { + /* Have to put this response queue */ + if (nia_putq(nia_data.resp_hdr, &nia_data.rec_entry) == 0) + return 0; + } + + /* If no pending packet, return success */ + if (nia_data.r_pkt == 0) + return 1; + + /* Determine which queue to get free packet from */ + hdr = (struct nia_eth_hdr *)(&nia_data.rec_buff.msg[0]); + type = ntohs(hdr->type); + + queue = nia_data.unk_hdr; + for (i = 0; i < nia_data.ptt_n; i++) { + if (nia_data.ptt_proto[i] == type) { + queue = nia_data.ptt_head[i]; + break; + } + } + + /* Try to grab place to save packet */ + if (nia_getq(queue, &nia_data.rec_entry) == 0) + return 0; /* Indicate packet not processed */ + + /* If we queue empty, just drop packet */ + if (nia_data.rec_entry == 0) { + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA drop packet\n"); + nia_data.r_pkt = 0; /* Drop packet it queue empty */ + if (queue == nia_data.unk_hdr) + nia_data.pcnt[NIA_CNT_DUN]++; + else + nia_data.pcnt[NIA_CNT_D01 + i]++; + nia_data.pcnt[NIA_CNT_UBU] += nia_data.rec_buff.len; + nia_data.status |= NIA_FQE; + set_interrupt(NIA_DEVNUM, nia_data.status & NIA_PIA); + return 1; /* We did what we could with it. */ + } + + /* Get some information about packet */ + len = nia_data.rec_buff.len - sizeof(struct nia_eth_hdr); + data = &nia_data.rec_buff.msg[sizeof(struct nia_eth_hdr)]; + + /* Got one, now fill in data */ + word = (uint64)(NIA_CMD_RCV << 12); + if (Mem_write_word(nia_data.rec_entry + 3, &word, 0)) { + nia_error(EBSERR); + return SCPE_OK; + } + word = (uint64)len; + if (Mem_write_word(nia_data.rec_entry + 4, &word, 0)) { + nia_error(EBSERR); + return 0; + } + (void)nia_cpy_from(nia_data.rec_entry + 5, + (uint8 *)&hdr->dest, sizeof(ETH_MAC)); + (void)nia_cpy_from(nia_data.rec_entry + 7, + (uint8 *)&hdr->src, sizeof(ETH_MAC)); + word = (uint64)(((type & 0xff00) >> 4) | + ((type & 0xff) << 12)); + if (Mem_write_word(nia_data.rec_entry + 9, &word, 0)) { + nia_error(EBSERR); + return 0; + } + if (Mem_read_word(nia_data.rec_entry + 10, &word, 0)) { + nia_error(EBSERR); + return 0; + } + bsd = (t_addr)(word & AMASK); + while (len > 0) { + int blen; + /* Get length of segment */ + if (Mem_read_word(bsd+2, &word, 0)) { + nia_error(EBSERR); + return 0; + } + blen = (int)(word & 0177777); + if (blen > len) + blen = len; + /* Get address of where to put data */ + if (Mem_read_word(bsd, &word, 0)) { + nia_error(EBSERR); + return 0; + } + data = nia_cpy_from((t_addr)(word & AMASK), data, blen); + len -= blen; + /* Get pointer to next segment */ + if (Mem_read_word(bsd+1, &word, 0)) { + nia_error(EBSERR); + return 0; + } + bsd = (t_addr)(word & AMASK); + } + + for(i = 0; i < 10; i++) + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA recv: %d %09llx %012llo\n", + i, M[nia_data.rec_entry + i], M[nia_data.rec_entry + i]); + /* All done with packet */ + nia_data.r_pkt = 0; + /* Put on response queue */ + return nia_putq(nia_data.resp_hdr, &nia_data.rec_entry); +} + +/* + * Receive ether net packets. + */ +t_stat nia_eth_srv(UNIT * uptr) +{ + struct nia_eth_hdr *hdr; + uint16 type; + + /* Check if we need to get a packet */ + while (nia_data.r_pkt == 0) { + if (eth_read(&nia_data.etherface, &nia_data.rec_buff, NULL) <= 0) + return SCPE_OK; + + nia_packet_debug(&nia_data, "recv", &nia_data.rec_buff); + hdr = (struct nia_eth_hdr *)(&nia_data.rec_buff.msg[0]); + type = ntohs(hdr->type); + /* Check if we are running */ + if ((nia_data.status & NIA_MRN) == 0) { + sim_debug(DEBUG_DETAIL, &nia_dev, + "NIA read packet - not running: %d %04x\n", + nia_data.rec_buff.len, type); + return SCPE_OK; + } + + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA read packet: %d %04x\n", + nia_data.rec_buff.len, type); + nia_data.r_pkt = 1; /* Mark packet buffer full */ + nia_data.pcnt[NIA_CNT_BR] += nia_data.rec_buff.len; + nia_data.pcnt[NIA_CNT_FR] ++; + if (hdr->dest[0] & 1) { + nia_data.pcnt[NIA_CNT_MCB] += nia_data.rec_buff.len; + nia_data.pcnt[NIA_CNT_MCF] ++; + } + + /* Try to process the packet */ + if (nia_rec_pkt() == 0) { + sim_activate(nia_proc_uptr, 100); + return SCPE_OK; + } + } + return SCPE_OK; +} + +/* + * Handle delayed packets. + */ +t_stat nia_rec_srv(UNIT * uptr) +{ + + /* Process what we have */ + if (nia_rec_pkt() == 0) { + sim_activate(uptr, 100); + return SCPE_OK; + } + /* See if we can pick up any more packets */ + return nia_eth_srv(nia_recv_uptr); +} + + + +t_stat nia_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ + char buffer[20]; + eth_mac_fmt(&nia_data.mac, buffer); + fprintf(st, "MAC=%s", buffer); + return SCPE_OK; +} + +t_stat nia_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(&nia_data.mac, cptr, uptr); + if (status != SCPE_OK) + return status; + + return SCPE_OK; +} + +t_stat nia_reset (DEVICE *dptr) +{ + int i; + + for (i = 0; i < 6; i++) { + if (nia_data.mac[i] != 0) + break; + } + if (i == 6) { /* First call to reset? */ + /* Set a default MAC address in a BBN assigned OID range no longer in use */ + nia_set_mac (dptr->units, 0, "00:00:02:00:00:00/24", NULL); + } + return SCPE_OK; +} + +/* attach device: */ +t_stat nia_attach(UNIT* uptr, CONST char* cptr) +{ + t_stat status; + char* tptr; + char buf[32]; + + tptr = (char *) malloc(strlen(cptr) + 1); + if (tptr == NULL) return SCPE_MEM; + strcpy(tptr, cptr); + + memcpy(&nia_data.macs[0], &nia_data.mac, sizeof (ETH_MAC)); + memcpy(&nia_data.macs[1], &broadcast_ethaddr, 6); + status = eth_open(&nia_data.etherface, cptr, &nia_dev, DEBUG_ETHER); + if (status != SCPE_OK) { + free(tptr); + return status; + } + eth_mac_fmt(&nia_data.mac, buf); /* format ethernet mac address */ + if (SCPE_OK != eth_check_address_conflict (&nia_data.etherface, + &nia_data.mac)) { + eth_close(&nia_data.etherface); + free(tptr); + return sim_messagef (SCPE_NOATT, + "%s: MAC Address Conflict on LAN for address %s\n", + nia_dev.name, buf); + } + if (SCPE_OK != eth_filter(&nia_data.etherface, 2, nia_data.macs, 0, 0)) { + eth_close(&nia_data.etherface); + free(tptr); + return sim_messagef (SCPE_NOATT, + "%s: Can't set packet filter for MAC Address %s\n", + nia_dev.name, buf); + } + + uptr->filename = tptr; + uptr->flags |= UNIT_ATT; + eth_setcrc(&nia_data.etherface, 1); /* Enable CRC */ + + /* init read queue (first time only) */ + status = ethq_init(&nia_data.ReadQ, 8); + if (status != SCPE_OK) { + eth_close(&nia_data.etherface); + uptr->filename = NULL; + free(tptr); + return sim_messagef (status, "%s: Can't initialize receive queue\n", + nia_dev.name); + } + + + /* Allow Asynchronous inbound packets */ + eth_set_async (&nia_data.etherface, 0); + return SCPE_OK; +} + +/* detach device: */ + +t_stat nia_detach(UNIT* uptr) +{ + + if (uptr->flags & UNIT_ATT) { + sim_cancel(&nia_unit[1]); + sim_cancel(&nia_unit[2]); + eth_close (&nia_data.etherface); + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + } + return SCPE_OK; +} + +t_stat nia_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "NIA interface\n\n"); +fprintf (st, "The NIA interfaces to the network. Setting MAC defines default MAC address\n"); +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +eth_attach_help(st, dptr, uptr, flag, cptr); +return SCPE_OK; +} + +const char *nia_description (DEVICE *dptr) +{ + return "KL NIA interface"; +} +#endif + diff --git a/PDP10/kx10_cp.c b/PDP10/kx10_cp.c index ff8ebdea..b379c60b 100644 --- a/PDP10/kx10_cp.c +++ b/PDP10/kx10_cp.c @@ -1,6 +1,6 @@ -/* ka10_cp.c: PDP10 Card Punch +/* kx10_cp.c: PDP10 Card Punch - Copyright (c) 2016-2017, Richard Cornwell + Copyright (c) 2016-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP10/kx10_cpu.c b/PDP10/kx10_cpu.c index dae9f7a6..5199bbab 100644 --- a/PDP10/kx10_cpu.c +++ b/PDP10/kx10_cpu.c @@ -1,6 +1,6 @@ -/* ka10_cpu.c: PDP-10 CPU simulator +/* kx10_cpu.c: PDP-10 CPU simulator - Copyright (c) 2011-2017, Richard Cornwell + Copyright (c) 2011-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,7 +23,7 @@ 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 + cpu KA10/KI10/KL10 central processor The 36b system family had six different implementions: PDP-6, KA10, KI10, @@ -100,14 +100,17 @@ #define HIST_PC 0x40000000 #define HIST_PC2 0x80000000 +#define HIST_PCE 0x20000000 #define HIST_MIN 64 -#define HIST_MAX 500000 +#define HIST_MAX 5000000 #define TMR_RTC 0 #define TMR_QUA 1 uint64 M[MAXMEMSIZE]; /* Memory */ -#if KI +#if KL +uint64 FM[128]; /* Fast memory register */ +#elif KI uint64 FM[64]; /* Fast memory register */ #else uint64 FM[16]; /* Fast memory register */ @@ -117,8 +120,8 @@ 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 */ +t_addr AB; /* Memory address buffer */ +t_addr PC; /* Program counter */ uint32 IR; /* Instruction register */ uint64 MI; /* Monitor lights */ uint32 FLAGS; /* Flags */ @@ -126,12 +129,11 @@ 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 */ +t_addr Pl, Ph, Rl, Rh, Pflag; /* Protection registers */ int push_ovf; /* Push stack overflow */ int mem_prot; /* Memory protection flag */ #endif @@ -148,6 +150,7 @@ int ex_uuo_sync; /* Execute a UUO op */ uint8 PIR; /* Current priority level */ uint8 PIH; /* Highest priority */ uint8 PIE; /* Priority enable mask */ +int pi_cycle; /* Executing an interrupt */ int pi_enable; /* Interrupts enabled */ int parity_irq; /* Parity interupt */ int pi_pending; /* Interrupt pending. */ @@ -159,19 +162,43 @@ int pi_restore; /* Restore previous level */ int pi_hold; /* Hold onto interrupt */ int modify; /* Modify cycle */ int xct_flag; /* XCT flags */ -#if KI +#if KI | KL 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 */ +t_addr ub_ptr; /* User base pointer */ +t_addr 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 */ +#if KL +int pi_vect; /* Last pi location used for IRQ */ +int ext_ac; /* Extended instruction AC */ +uint8 prev_ctx; /* Previous AC context */ +uint16 irq_enable; /* Apr IRQ enable bits */ +uint16 irq_flags; /* Apr IRQ bits */ +int mtr_irq; /* Timer IRQ */ +int mtr_enable; /* Enable Timer */ +int mtr_flags; /* Flags for accounting */ +int tim_per; /* Timer period */ +int tim_val; /* Current timer value */ +int rtc_tim; /* Time till next 60hz clock */ +uint32 brk_addr; /* Address break */ +int brk_flags; /* Break flags */ +int t20_page; /* Tops 20 paging selected */ +int ptr_flg; /* Access to pointer value */ +int extend = 0; /* Process extended instruction */ +int sect; /* Actual resolved section */ +int cur_sect; /* Current section */ +int prev_sect; /* Previous section */ +int pc_sect; /* Program counter section */ +int glb_sect; /* Global section access */ +#else int small_user; /* Small user flag */ +#endif int user_addr_cmp; /* User address compare flag */ #endif -#if KI | ITS | BBN +#if KI | KL | ITS | BBN uint32 e_tlb[512]; /* Executive TLB */ uint32 u_tlb[546]; /* User TLB */ int page_enable; /* Enable paging */ @@ -203,27 +230,43 @@ uint32 fault_addr; /* Fault address */ uint64 opc; /* Saved PC and Flags */ uint64 mar; /* Memory address compare */ uint32 qua_time; /* Quantum clock value */ +#if MAGIC_SWITCH +int MAGIC = 1; /* Magic switch. */ +#endif /* MAGIC_SWITCH */ +#endif /* ITS */ +#if KL_ITS +#define dbr1 FM[(6<<4)|1] +#define dbr2 FM[(6<<4)|2] +#define dbr3 FM[(6<<4)|3] +#define dbr4 FM[(6<<4)|4] +#define jpc FM[(6<<4)|15] +#define mar brk_addr; #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_addr (*dev_irqv[128])(uint32 dev, t_addr addr); t_stat rtc_srv(UNIT * uptr); int32 rtc_tps = 60; #if ITS t_stat qua_srv(UNIT * uptr); int32 qua_tps = 125000; #endif +#if KL +t_stat tim_srv(UNIT * uptr); +#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. */ -extern int auxcpu_base; #define AUXCPURANGE(addr) ((addr) >= auxcpu_base && (addr) < (auxcpu_base + 040000)) + +/* List of RH10 & RH20 devices */ DEVICE *rh_devs[] = { #if (NUM_DEVS_RS > 0) &rsa_dev, @@ -242,19 +285,16 @@ DEVICE *rh_devs[] = { #endif #if (NUM_DEVS_TU > 0) &tua_dev, +#endif +#if (NUM_DEVS_NIA > 0) + &nia_dev, #endif NULL, }; - -struct rh_dev rh[] = { - { 0270, NULL, }, - { 0274, NULL, }, - { 0360, NULL, }, - { 0364, NULL, }, - { 0370, NULL, }, - { 0374, NULL, }, - { 0, NULL, }, -}; +/* RH10 device numbers */ +int rh_nums[] = { 0270, 0274, 0360, 0364, 0370, 0374, 0}; +/* Maps RH10 & RH20 device number to DEVICE structure */ +struct rh_dev rh[8]; typedef struct { uint32 pc; @@ -264,6 +304,7 @@ typedef struct { uint32 flags; uint64 mb; uint64 fmb; + uint16 prev_sect; } InstHistory; int32 hst_p = 0; /* history pointer */ @@ -272,13 +313,16 @@ InstHistory *hst = NULL; /* instruction history */ /* Forward and external declarations */ +#if KL +int do_extend(uint32 IA); +#endif 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 +#if KI | KL 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 @@ -286,6 +330,13 @@ 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); +#if KA +int (*Mem_read)(int flag, int cur_context, int fetch); +int (*Mem_write)(int flag, int cur_context); +#else +int Mem_read(int flag, int cur_context, int fetch); +int Mem_write(int flag, int cur_context); +#endif t_bool build_dev_tab (void); @@ -300,6 +351,9 @@ t_bool build_dev_tab (void); 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 +#if KL + { UDATA (&tim_srv, UNIT_IDLE|UNIT_DIS, 0) } #endif }; @@ -322,7 +376,9 @@ REG cpu_reg[] = { { ORDATA (FM15, FM[015], 36) }, { ORDATA (FM16, FM[016], 36) }, { ORDATA (FM17, FM[017], 36) }, -#if KI +#if KL + { BRDATA (FM, FM, 8, 36, 128)}, +#elif KI { BRDATA (FM, FM, 8, 36, 64)}, #else { BRDATA (FM, FM, 8, 36, 16)}, @@ -354,35 +410,48 @@ REG cpu_reg[] = { #else { FLDATAD (FOV, fov_irq, 0, "Floating overflow enable") }, #endif - { FLDATA (PI_PEND, pi_pending, 0), REG_HRO}, + { FLDATA (PIPEND, 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}, + { BRDATA (IRQV, dev_irq, 8, 16, 128 ), REG_HRO}, + { ORDATA (PIEN, pi_enc, 8), REG_HRO}, + { FLDATA (PIHOLD, pi_hold, 0), REG_HRO}, + { FLDATA (PIREST, pi_restore, 0), REG_HRO}, + { FLDATA (PICYC, pi_cycle, 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") }, +#endif +#if KL + { ORDATAD (UB, ub_ptr, 22, "User Base Pointer") }, + { ORDATAD (EB, eb_ptr, 22, "Executive Base Pointer") }, +#endif +#if KI | KL { ORDATAD (FMSEL, fm_sel, 8, "Register set select") }, { ORDATAD (SERIAL, apr_serial, 10, "System Serial Number") }, { FLDATA (INOUT, inout_fail, 0), REG_RO}, +#if KI { FLDATA (SMALL, small_user, 0), REG_RO}, +#endif { FLDATA (ADRCMP, user_addr_cmp, 0), REG_HRO}, #endif -#if KI | ITS | BBN +#if KL | 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}, +#if !KL { ORDATAD (LST_PAGE, last_page, 9, "Last page"), REG_HRO}, #endif +#endif #if BBN { FLDATAD (EXEC_MAP, exec_map, 0, "Executive mapping"), REG_RO}, { FLDATAD (NXT_WR, next_write, 0, "Map next write"), REG_RO}, @@ -401,6 +470,35 @@ REG cpu_reg[] = { { ORDATAD (OPC, opc, 36, "Saved PC and flags")}, { ORDATAD (MAR, mar, 18, "Memory address register")}, { ORDATAD (QUA_TIME, qua_time, 32, "Quantum timer"), REG_RO}, +#if MAGIC_SWITCH + { ORDATAD (MAGIC, MAGIC, 1, "Magic switch"), REG_FIT}, +#endif /* MAGIC_SWITCH */ +#endif /* ITS */ +#if KL + { ORDATAD (EXT_AC, ext_ac, 4, "Extended Instruction AC"), REG_HRO}, + { ORDATAD (PREV_CTX, prev_ctx, 5, "Previous context"), REG_HRO}, + { ORDATAD (ITQ_EN, irq_enable, 16, "Interrupt enable"), REG_HRO}, + { ORDATAD (ITQ_FLGS, irq_flags, 16, "Interrupt Flags"), REG_HRO}, + { ORDATAD (MTR_IRQ, mtr_irq, 1, "Timer IRQ"), REG_HRO}, + { ORDATAD (MTR_EN, mtr_enable, 1, "Timer Enable"), REG_HRO}, + { ORDATAD (MTR_FLGS, mtr_flags, 3, "Timer Flags"), REG_HRO}, + { ORDATAD (TIM_PER, tim_per, 12, "Timer period"), REG_HRO}, + { ORDATAD (TIM_VAl, tim_val, 12, "Timer period"), REG_HRO}, + { ORDATAD (RTC_TIM, rtc_tim, 12, "RTC timer"), REG_HRO}, + { ORDATAD (BRK_ADDR, brk_addr, 18, "Break address"), REG_HRO}, + { ORDATAD (BRK_FLGS, brk_flags, 18, "Break address"), REG_HRO}, + { ORDATAD (T20_PAGE, t20_page, 1, "TOPS20 paging"), REG_HRO}, + { ORDATAD (PTR_FLG, ptr_flg, 1, "Accessing pointer"), REG_HRO}, + { ORDATAD (EXTEND, extend, 1, "Execute Extend"), REG_HRO}, + { ORDATAD (SECT, sect, 12, "access section"), REG_HRO}, + { ORDATAD (CUR_SECT, cur_sect, 12, "Current section"), REG_HRO}, + { ORDATAD (PREV_SECT, prev_sect, 12, "Previous section"), REG_HRO}, + { ORDATAD (PC_SECT, pc_sect, 12, "PC section"), REG_HRO}, + { ORDATAD (GLB_SECT, glb_sect, 1, "Global section"), REG_HRO}, +#endif +#if !PDP6 + { BRDATA (ETLB, e_tlb, 8, 32, 512), REG_HRO}, + { BRDATA (UTLB, u_tlb, 8, 32, 546), REG_HRO}, #endif { NULL } }; @@ -428,13 +526,20 @@ MTAB cpu_mod[] = { #if KI|KL { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "SERIAL", "SERIAL", &cpu_set_serial, &cpu_show_serial, NULL, "CPU Serial Number" }, +#if KL + { UNIT_M_PAGE, 0, "KL10A", "KL10A", NULL, NULL, NULL, + "Base KL10"}, + { UNIT_M_PAGE, UNIT_KL10B, "KL10B", "KL10B", NULL, NULL, NULL, + "Extended addressing support for KL10"}, +#endif #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 +#endif +#if ITS | KL_ITS { UNIT_M_PAGE, UNIT_ITSPAGE, "ITS", "ITS", NULL, NULL, NULL, "Paging hardware for ITS"}, #endif @@ -454,6 +559,7 @@ MTAB cpu_mod[] = { { UNIT_M_MPX, 0, NULL, "NOMPX", NULL, NULL, NULL, "Disables the MPX device"}, #endif +#if PDP6 | KA | KI { UNIT_MAOFF, UNIT_MAOFF, "MAOFF", "MAOFF", NULL, NULL, NULL, "Interrupts relocated to 140"}, { UNIT_MAOFF, 0, NULL, "NOMAOFF", NULL, NULL, NULL, @@ -475,7 +581,7 @@ DEBTAB cpu_debug[] = { DEVICE cpu_dev = { "CPU", &cpu_unit[0], cpu_reg, cpu_mod, - 1, 8, 22, 1, 8, 36, + 1+ITS+KL, 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 @@ -539,7 +645,7 @@ int opflags[] = { /* Double precsision math */ /* UJEN */ /* UUO101 */ /* GFAD */ /* GFSB */ 0, 0, 0, 0, - /* JSYS */ /* ADJSP */ /* GFMP */ /*GFDV */ + /* JSYS */ /* ADJSP */ /* GFMP */ /* GFDV */ 0, 0, 0, 0, /* DFAD */ /* DFSB */ /* DFMP */ /* DFDV */ 0, 0, 0, 0, @@ -781,7 +887,7 @@ int opflags[] = { #define SMEAR_SIGN(x) x = ((x) & SMASK) ? (x) | EXPO : (x) & MANT #define GET_EXPO(x) ((((x) & SMASK) ? 0377 : 0 ) \ ^ (((x) >> 27) & 0377)) -#if KI +#if KI | KL #define AOB(x) ((x + 1) & RMASK) | ((x + 01000000LL) & (C1|LMASK)) #define SOB(x) ((x + RMASK) & RMASK) | ((x + LMASK) & (C1|LMASK)); #else @@ -793,8 +899,12 @@ int opflags[] = { #define QTEN11 (ten11_unit[0].flags & UNIT_ATT) #define QAUXCPU (auxcpu_unit[0].flags & UNIT_ATT) #else +#if KL_ITS +#define QITS (cpu_unit[0].flags & UNIT_ITSPAGE) +#else #define QITS 0 #endif +#endif #if BBN #define QBBN (cpu_unit[0].flags & UNIT_BBNPAGE) #else @@ -805,6 +915,54 @@ int opflags[] = { #else #define QWAITS 0 #endif +#if KL +#define QKLB (cpu_unit[0].flags & UNIT_KL10B) +#else +#define QKLB 0 +#endif +#if PDP6 +#define QSLAVE (slave_unit[0].flags & UNIT_ATT) +#else +#define QSLAVE 0 +#endif + +#if KL +struct _byte { + int p; + int s; +} _byte_adj[] = { + { /* 37 */ 36, 6 }, /* 45 */ + { /* 38 */ 30, 6 }, /* 46 */ + { /* 39 */ 24, 6 }, /* 47 */ + { /* 40 */ 18, 6 }, /* 50 */ + { /* 41 */ 12, 6 }, /* 51 */ + { /* 42 */ 6, 6 }, /* 52 */ + { /* 43 */ 0, 6 }, /* 53 */ + + { /* 44 */ 36, 8 }, /* 54 */ + { /* 45 */ 28, 8 }, /* 55 */ + { /* 46 */ 20, 8 }, /* 56 */ + { /* 47 */ 12, 8 }, /* 57 */ + { /* 48 */ 4, 8 }, /* 60 */ + + { /* 49 */ 36, 7 }, /* 61 */ + { /* 50 */ 29, 7 }, /* 62 */ + { /* 51 */ 22, 7 }, /* 63 */ + { /* 52 */ 15, 7 }, /* 64 */ + { /* 53 */ 8, 7 }, /* 65 */ + { /* 54 */ 1, 7 }, /* 66 */ + + { /* 55 */ 36, 9 }, /* 67 */ + { /* 56 */ 27, 9 }, /* 70 */ + { /* 57 */ 18, 9 }, /* 71 */ + { /* 58 */ 9, 9 }, /* 72 */ + { /* 59 */ 0, 9 }, /* 73 */ + + { /* 60 */ 36,18 }, /* 74 */ + { /* 61 */ 18,18 }, /* 75 */ + { /* 62 */ 0,18 } /* 76 */ +}; +#endif #if ITS /* @@ -1013,8 +1171,9 @@ t_stat dev_pi(uint32 dev, uint64 *data) { } if (res & 0400) /* Bit 27 */ pi_enable = 0; - if (res & 01000) /* Bit 26 */ + if (res & 01000) { /* Bit 26 */ PIE &= ~(*data & 0177); + } if (res & 02000) /* Bit 25 */ PIE |= (*data & 0177); if (res & 04000) { /* Bit 24 */ @@ -1025,15 +1184,17 @@ t_stat dev_pi(uint32 dev, uint64 *data) { if (res & 020000 && cpu_unit[0].flags & UNIT_MPX) mpx_enable = 1; #endif -#if KI +#if KI | KL if (res & 020000) { /* Bit 22 */ PIR &= ~(*data & 0177); } #endif +#if !KL if (res & 040000) /* Bit 21 */ parity_irq = 1; if (res & 0100000) /* Bit 20 */ parity_irq = 0; +#endif check_apr_irq(); sim_debug(DEBUG_CONO, &cpu_dev, "CONO PI %012llo\n", *data); break; @@ -1042,19 +1203,51 @@ t_stat dev_pi(uint32 dev, uint64 *data) { res = PIE; res |= (pi_enable << 7); res |= (PIH << 8); -#if KI +#if KI | KL res |= ((uint64)(PIR) << 18); #endif +#if !KL res |= (parity_irq << 15); +#endif *data = res; sim_debug(DEBUG_CONI, &cpu_dev, "CONI PI %012llo\n", *data); break; case DATAO: +#if KL + if (dev & 040) { /* SBDIAG */ + AB = (AB + 1) & RMASK; + res = 0; + if (((*data >> 31) & 030) == 010) { + int mc = MEMSIZE / 262144; + int c = (*data >> 31) & 07; + int s = 0; + if (c < mc) { + switch(*data & 037) { + case 0: res = 06000000000LL; break; + case 1: res = 00500000000LL; break; + case 2: res = 0; break; + case 012: + res = 0; + s = (int)(0176000 & *data) << 6; + s /= 262144; + if (s != c) + res = 010000000LL; + break; + default: res = 0; break; + } + } + } + MB = res; + (void)Mem_write(0, 0); + break; + } +#else MI = *data; #ifdef PANDA_LIGHTS /* Set lights */ ka10_lights_main (*data); +#endif #endif break; @@ -1081,6 +1274,425 @@ t_stat null_dev(uint32 dev, uint64 *data) { return SCPE_OK; } +#if KL +static int timer_irq, timer_flg; + +void +update_times(int tim) +{ + uint64 temp; + if (page_enable) { + temp = (M[eb_ptr + 0511] & CMASK) + (tim << 12); + if (temp & SMASK) + M[eb_ptr + 0510] = (M[eb_ptr+0510] + 1) & FMASK; + M[eb_ptr + 0511] = temp & CMASK; + if (FLAGS & USER) { + temp = (M[ub_ptr + 0506] & CMASK) + (tim << 12); + if (temp & SMASK) + M[ub_ptr + 0505] = (M[ub_ptr+0505] + 1) & FMASK; + M[ub_ptr + 0506] = temp & CMASK; + } + } +} + +/* + * Page device for KL10. + */ +t_stat dev_pag(uint32 dev, uint64 *data) { + uint64 res = 0; + int i; + switch(dev & 03) { + case CONI: + res = (eb_ptr >> 9); + if (page_enable) + res |= 020000; + if (t20_page) + res |= 040000; + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI PAG %012llo\n", *data); + break; + + case CONO: + eb_ptr = (*data & 017777) << 9; + for (i = 0; i < 512; i++) { + e_tlb[i] = 0; + u_tlb[i] = 0; + } + for (;i < 546; i++) + u_tlb[i] = 0; + page_enable = (*data & 020000) != 0; + t20_page = (*data & 040000) != 0; + sim_debug(DEBUG_CONO, &cpu_dev, "CONO PAG %012llo\n", *data); + break; + + case DATAO: + if (dev & 040) { /* CLRPT */ + int page = (RMASK & AB) >> 9; + int i; + + page &= ~7; + /* Map the page */ + for(i = 0; i < 8; i++) { + u_tlb[page+i] = 0; + e_tlb[page+i] = 0; + } + /* If not user do exec mappping */ + if (!t20_page && (page & 0740) == 0340) { + /* Pages 340-377 via UBT */ + page += 01000 - 0340; + for(i = 0; i < 8; i++) + u_tlb[page+i] = 0; + } + } else { + res = *data; + if (res & SMASK) { + fm_sel = (uint8)(res >> 23) & 0160; + prev_ctx = (res >> 20) & 0160; + } + if (QKLB && (res & BIT1) != 0) { + /* Load previous section */ + prev_sect = (res >> 18) & 037; + } + if (res & BIT2) { + if ((res & RSIGN) == 0) { + int t; + double us = sim_activate_time_usecs (&cpu_unit[0]); + t = rtc_tim - ((int)us); + update_times(t); + rtc_tim = ((int)us); + } + ub_ptr = (res & 017777) << 9; + for (i = 0; i < 512; i++) { + u_tlb[i] = 0; + e_tlb[i] = 0; + } + for (;i < 546; i++) + u_tlb[i] = 0; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, + "DATAO PAG %012llo ebr=%06o ubr=%06o\n", + *data, eb_ptr, ub_ptr); + } + break; + + case DATAI: + if (dev & 040) { + /* Convert to MMU */ + } + res = (ub_ptr >> 9); + /* Set previous section */ + res |= ((uint64)(prev_ctx & 0160)) << 20; + res |= ((uint64)(fm_sel & 0160)) << 23; + res |= SMASK|BIT1|BIT2; + if (QKLB) + res |= ((uint64)prev_sect & 037) << 18; + *data = res; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI PAG %012llo\n", *data); + break; + } + return SCPE_OK; +} + +/* + * Cache control. + * All operations set sweep done. + */ +t_stat dev_cca(uint32 dev, uint64 *data) { + irq_flags |= 020; + *data = 0; + check_apr_irq(); + 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 = irq_enable & irq_flags; + if (flg) + set_interrupt(0, apr_irq); + } +} + + +/* + * APR device for KL10. + */ +t_stat dev_apr(uint32 dev, uint64 *data) { + uint64 res = 0; + + switch(dev & 03) { + case CONI: + /* Read trap conditions */ + res = irq_flags | apr_irq; + res |= ((uint64)irq_enable) << 18; + if (irq_flags & irq_enable) + res |= 010; + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI APR %012llo\n", *data); + break; + + case CONO: + /* Set trap conditions */ + res = *data; + apr_irq = res & 07; + clr_interrupt(0); + if (res & 0200000) + reset_all(1); + if (res & 0100000) { /* Enable interrupts */ + irq_enable |= 07760 & res; + } + if (res & 0040000) { /* Disable interrupts */ + irq_enable &= ~(07760 & res); + } + if (res & 0020000) { /* Clear interrupt */ + irq_flags &= ~(07760 & res); + } + if (res & 0010000) { /* Set interrupt */ + irq_flags |= (07760 & res); + } + check_apr_irq(); + sim_debug(DEBUG_CONO, &cpu_dev, "CONO APR %012llo\n", *data); + break; + + case DATAO: + brk_addr = *data & RMASK; + brk_flags = 017 & (*data >> 23); + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAO APR %012llo\n", *data); + break; + + case DATAI: + if (dev & 040) { + /* APRID */ + AR = SMASK| (500LL << 18); /* MC level 500 */ + /* Bit 0 for TOPS-20 paging */ + /* Bit 1 for extended addressing */ + /* Bit 2 Exotic microcode */ + /* Bit 3 KL10B */ + /* Bit 4 PMOVE/PMOVEM or ITS Style Paging */ + /* Bit 5 Tops-20 R5 microcode */ +#if KL_ITS + if (QITS) + AR |= 00020000000000LL; +#endif + /* Bit 18 50hz */ + /* Bit 19 Cache */ + /* Bit 20 Channel? */ + /* Bit 21 Extended KL10 */ + /* Bit 22 Master Osc */ + if (QKLB) + AR |= BIT1|BIT4|040000; + AR |= (uint64)((apr_serial == -1) ? DEF_SERIAL : apr_serial); + sim_debug(DEBUG_DATAIO, &cpu_dev, "APRID BLKI %012llo\n", MB); + } else { + *data = ((uint64)brk_flags) << 23; + *data |= (uint64)brk_addr; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI APR %012llo\n", *data); + } + break; + } + return SCPE_OK; +} + +/* + * MTR device for KL10. + */ +t_stat dev_mtr(uint32 dev, uint64 *data) { + uint64 res = 0; + + switch(dev & 03) { + case CONI: + /* Reader meters */ + *data = mtr_irq; + if (mtr_enable) + *data |= 02000; + *data |= (uint64)(mtr_flags << 12); + sim_debug(DEBUG_CONI, &cpu_dev, "CONI MTR %012llo\n", *data); + break; + + case CONO: + /* WRTIME */ + mtr_irq = *data & 07; + if (*data & 02000) + mtr_enable = 1; + if (*data & 04000) + mtr_enable = 0; + if (*data & RSIGN) + mtr_flags = (*data >> 12) & 07; + clr_interrupt(4 << 2); + if (tim_val & 030000) + set_interrupt(4 << 2, mtr_irq); + sim_debug(DEBUG_CONO, &cpu_dev, "CONO MTR %012llo\n", *data); + break; + + case DATAO: + /* MUUO */ + if (dev & 040) { + sim_debug(DEBUG_DATAIO, &cpu_dev, "BLKO MTR %012llo\n", *data); + } else { + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAO MTR %012llo\n", *data); + } + break; + + case DATAI: + if (dev & 040) { + /* RDMACT */ + /* Read memory accounting */ + if (page_enable) { + sim_interval--; + res = M[ub_ptr + 0507]; + sim_interval--; + BR = (M[ub_ptr + 0506] & CMASK); + } else { + res = 0 << 12; + BR = 0; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "BLKI MTR %012llo\n", *data); + } else { + /* RDEACT */ + /* Read executive accounting */ + int t; + double us = sim_activate_time_usecs (&cpu_unit[0]); + t = rtc_tim - ((int)us); + update_times(t); + rtc_tim = ((int)us); + if (page_enable) { + sim_interval--; + res = M[ub_ptr + 0505]; + sim_interval--; + BR = (M[ub_ptr + 0504] & CMASK); + } else { + res = 0; + BR = t << 12; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI MTR %012llo\n", *data); + } + *data = res; + break; + } + return SCPE_OK; +} + +/* + * TIM device for KL10. + */ +t_stat dev_tim(uint32 dev, uint64 *data) { + uint64 res; + double us; + UNIT *uptr = &cpu_unit[1 + ITS]; + + /* Update current timer count */ + if (sim_is_active(uptr)) { + us = sim_activate_time_usecs (uptr) / 10; + /* Check if going to period or overflow */ + if (tim_val & 0100000) + tim_val = (tim_val & 0070000) + tim_per - (int)us; + else + tim_val = (tim_val & 0070000) + 010000 - (int)us; + } + clr_interrupt(4 << 2); + switch(dev & 03) { + case CONI: + /* Interval counter */ + res = tim_per; + res |= tim_val & 070000; + res |= ((uint64)(tim_val & 07777)) << 18; + *data = res; + sim_debug(DEBUG_CONI, &cpu_dev, "CONI TIM %012llo\n", *data); + return SCPE_OK; + + case CONO: + /* Interval counter */ + sim_cancel(uptr); + tim_val &= 037777; /* Clear run bit */ + tim_per = *data & 07777; + if (*data & 020000) /* Clear overflow and done */ + tim_val &= 07777; + if (*data & 0400000) /* Clear counter */ + tim_val = 0; + if (*data & 040000) /* Enable counter */ + tim_val |= 040000; + sim_debug(DEBUG_CONO, &cpu_dev, "CONO TIM %012llo\n", *data); + break; + + case DATAO: + if (dev & 040) { + /* WRPAE */ + /* Write performance enables */ + sim_debug(DEBUG_DATAIO, &cpu_dev, "BLKO TIM %012llo\n", *data); + } else { + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAO TIM %012llo\n", *data); + } + return SCPE_OK; + + case DATAI: + if (dev & 040) { + /* RDPERF */ + /* Read process execution time */ + int t; + us = sim_activate_time_usecs (&cpu_unit[0]); + t = rtc_tim - ((int)us); + update_times(t); + rtc_tim = ((int)us); + if (page_enable) { + sim_interval--; + res = (M[ub_ptr + 0505]); + sim_interval--; + BR = M[ub_ptr + 0504]; + } else { + res = 0 << 12; + BR = t; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "BLKI TIM %012llo\n", *data); + } else { + /* RDTIME */ + int t; + us = sim_activate_time_usecs (&cpu_unit[0]); + t = rtc_tim - ((int)us); + update_times(t); + rtc_tim = ((int)us); + if (page_enable) { + sim_interval--; + res = (M[eb_ptr + 0510]); + sim_interval--; + BR = M[eb_ptr + 0511]; + } else { + res = 0; + BR = t << 12; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI TIM %012llo\n", *data); + } + *data = res; + return SCPE_OK; + } + /* If timer is on, figure out when it will go off */ + if (tim_val & 040000) { + /* If we have already passed time, schedule to overflow */ + if ((tim_val & 07777) >= tim_per) { + us = (float)((010000 - (tim_val & 07777)) * 10); + tim_val &= 0077777; + } else { + us = (float)((tim_per - (tim_val & 07777)) * 10); + tim_val |= 0100000; + } + (void)sim_activate_after_d(uptr, us); + } + if (tim_val & 030000) + set_interrupt(4 << 2, mtr_irq); + return SCPE_OK; +} + +t_addr +tim_irq(uint32 dev, t_addr addr) +{ + return 0514; +} + +#endif #if KI static int timer_irq, timer_flg; @@ -1123,8 +1735,8 @@ t_stat dev_pag(uint32 dev, uint64 *data) { 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; + user_addr_cmp = (res & BIT4) != 0; + small_user = (res & BIT3) != 0; fm_sel = (uint8)(res >> 29) & 060; } pag_reload = 0; @@ -1139,9 +1751,9 @@ t_stat dev_pag(uint32 dev, uint64 *data) { res |= 020000; res |= ((uint64)(ub_ptr)) << 9; if (user_addr_cmp) - res |= 00020000000000LL; + res |= BIT4; if (small_user) - res |= 00040000000000LL; + res |= BIT3; res |= ((uint64)(fm_sel)) << 29; *data = res; sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI PAG %012llo\n", *data); @@ -1247,11 +1859,13 @@ t_stat dev_pag(uint32 dev, uint64 *data) { case 0: /* Clear page tables, reload from 71 & 72 */ for (i = 0; i < 512; i++) e_tlb[i] = u_tlb[i] = 0; + sim_interval--; 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]; + sim_interval--; pur = M[072]; break; @@ -1417,10 +2031,268 @@ sim_debug(DEBUG_DATAIO, &cpu_dev, "Rl=%06o Pl=%06o, Rh=%06o, Ph=%06o\n", Rl, Pl, } #endif +#if KL +int +load_tlb(int uf, int page, int wr) +{ + uint64 data; + +#if KL_ITS + if (QITS && t20_page) { + uint64 dbr; + int pg; + + dbr = (uf)? ((page & 0400) ? dbr2 : dbr1) : + ((page & 0400) ? dbr3 : dbr4) ; + pg = (page & 0377) >> 2; /* 2 1024 word page entries */ + sim_interval--; + data = M[dbr + pg]; + if ((page & 02) == 0) + data &= ~0160000000000LL; + else + data &= ~0160000LL; + M[dbr + pg] = data; + if ((page & 02) == 0) + data >>= 18; + data &= RMASK; + pg = 0; + switch(data >> 16) { + case 0: + fault_data = 033LL << 30; + page_fault = 1; + return 0; /* No access */ + case 1: /* Read Only */ + case 2: /* R/W First */ + if (wr) { + fault_data = 024LL << 30; + page_fault = 1; + return 0; + } + pg = KL_PAG_A; + break; + case 3: pg = KL_PAG_A|KL_PAG_W; break; /* R/W */ + } + pg |= (data & 017777) << 1; + /* Create 2 page table entries. */ + if (uf) { + u_tlb[page & 0776] = pg; + u_tlb[(page & 0776)|1] = pg|1; + data = u_tlb[page]; + } else { + e_tlb[page & 0776] = pg; + e_tlb[(page & 0776)|1] = pg|1; + data = e_tlb[page]; + } + } else +#endif +#define PG_PUB 0040000 +#define PG_WRT 0020000 +#define PG_KEP 0010000 +#define PG_CAC 0004000 +#define PG_STG (0000077LL << 18) +#define PG_IDX 0000777 + +#define PG_MASK 0000003777777LL +#define PG_AGE 0770000000000LL +#define PG_PAG 0017777 + if (t20_page) { /* Start with full access */ + int acc_bits = PG_PUB|PG_WRT|PG_KEP|PG_CAC; + uint64 spt = FM[(06<<4)|3] & PG_MASK; + uint64 cst = FM[(06<<4)|2] & PG_MASK; + uint64 cst_msk = FM[(06<<4)|0]; + uint64 cst_dat = FM[(06<<4)|1]; + uint64 cst_val = 0; + int index; + int pg; +#if EPT440 + int base = 0440; +#else + int base = 0540; +#endif + + /* Get segment pointer */ + /* And save it */ + if (QKLB) + base = 0540 + (sect & 037); + sim_interval--; + if (uf) + data = M[ub_ptr + base]; + else + data = M[eb_ptr + base]; + /* Find correct page table */ +sect_loop: + switch ((data >> 33) & 07) { + default: /* Invalid page */ + fault_data = 0; + page_fault = 1; + return 0; + case 1: /* Direct page */ + /* Bit 4 = execute */ + /* Bit 3 = Write */ + /* Bit 2 = Read */ + acc_bits &= (data >> 18) & RMASK; + break; + + case 2: /* Shared page */ + acc_bits &= (data >> 18) & RMASK; + sim_interval--; + index = data & RMASK; + data = M[index + spt]; + break; + + case 3: /* Indirect page */ + acc_bits &= (data >> 18) & RMASK; + index = (data >> 18) & PG_IDX; + sim_interval--; + data = M[(data & RMASK) + spt]; + if ((data & PG_STG) != 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + pg = data & PG_PAG; + sim_interval--; + data = M[(pg << 9) | index]; + goto sect_loop; + } + if ((data & PG_STG) != 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + pg = data & PG_PAG; + + /* Update CST entry if needed */ + if (cst) { + sim_interval--; + cst_val = M[cst + pg]; + if ((cst_val & PG_AGE) == 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + M[cst + pg] = (cst_val & cst_msk) | cst_dat; + } + + /* Get address of page */ + sim_interval--; + data = M[(pg << 9) | page]; +pg_loop: + + /* Decode map pointer */ + switch ((data >> 33) & 07) { + default: /* Invalid page */ + fault_data = 0; + page_fault = 1; + return 0; + case 1: /* Direct page */ + /* Bit 4 = execute */ + /* Bit 3 = Write */ + /* Bit 2 = Read */ + acc_bits &= (data >> 18) & RMASK; + break; + + case 2: /* Shared page */ + acc_bits &= (data >> 18) & RMASK; + sim_interval--; + index = data & RMASK; + data = M[index + spt]; + break; + + case 3: /* Indirect page */ + acc_bits &= (data >> 18) & RMASK; + index = (data >> 18) & PG_IDX; + sim_interval--; + data = M[(data & RMASK) + spt]; + if ((data & PG_STG) != 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + pg = data & RMASK; + sim_interval--; + data = M[(pg << 9) | index]; + goto pg_loop; + } + + /* Now have final page */ + if ((data & PG_STG) != 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + pg = data & PG_PAG; + /* Check outside of memory */ + /* Update CST entry if needed */ + if (cst) { + sim_interval--; + cst_val = M[cst + pg]; + if ((cst_val & PG_AGE) == 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + if (acc_bits & PG_WRT) { + if (wr) + cst_val |= 1; + } else if (wr) { /* Trying to write and not writable */ + fault_data = 0 /* Write fault */; + page_fault = 1; + return 0; + } + M[cst + pg] = (cst_val & cst_msk) | cst_dat; + } else { + if (acc_bits & PG_WRT) { + cst_val = 1; + } + } + /* Now construct a TBL entry */ + /* A = accessable */ + /* P = public */ + /* W = writable */ + /* S = user */ + /* C = cache */ + data = pg | KL_PAG_A; + if (acc_bits & PG_PUB) + data |= KL_PAG_P; /* P */ + if (acc_bits & PG_WRT) { + if (cst_val & 1) + data |= KL_PAG_W; /* Set Modified page */ + data |= KL_PAG_S; /* Set Writeable bit */ + } + if (acc_bits & PG_CAC) + data |= KL_PAG_C; + if (QKLB) + data |= (sect & 037) << 18; + /* And save it */ + if (uf) + u_tlb[page] = data & (SECTM|RMASK); + else + e_tlb[page] = data & (SECTM|RMASK); + } else { + + /* Map the page */ + sim_interval--; + if (uf) { + data = M[ub_ptr + (page >> 1)]; + u_tlb[page & 01776] = (uint32)(RMASK & (data >> 18)); + u_tlb[page | 1] = (uint32)(RMASK & data); + data = u_tlb[page]; + } else { + if (page & 0400) + data = M[eb_ptr + (page >> 1)]; + else + data = M[eb_ptr + (page >> 1) + 0600]; + e_tlb[page & 01776] = (uint32)(RMASK & (data >> 18)); + e_tlb[page | 1] = (uint32)(RMASK & data); + data = e_tlb[page]; + } + } + return (int)(data); +} -#if KI /* - * Handle page lookup on KI10 + * Handle page lookup on KL10 * * addr is address to look up. * flag is set for pi cycle and user overide. @@ -1429,47 +2301,452 @@ sim_debug(DEBUG_DATAIO, &cpu_dev, "Rl=%06o Pl=%06o, Rh=%06o, Ph=%06o\n", Rl, Pl, * 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 page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { + int data; int base = 0; int page = (RMASK & addr) >> 9; int uf = (FLAGS & USER) != 0; + int pub = (FLAGS & PUBLIC) != 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; + /* Handle address breaks */ + if (addr == brk_addr && uf == (brk_flags & 1) && (FLAGS & ADRFLT) == 0) { + if ((fetch && (brk_flags & 010) != 0) || + (!fetch && !wr && (brk_flags & 04) != 0) || + (wr && (brk_flags & 02) != 0)) { + fault_data = ((uint64)addr) | 023LL << 30 |((uf)?SMASK:0); + page_fault = 1; + return 0; + } + } /* If this is modify instruction use write access */ wr |= modify; /* Figure out if this is a user space access */ - if (flag) + + /* AC = 1 use BYF5 */ + /* AC = 2 use ptr_flg */ + /* AC = 4 all general access */ + /* AC = 8 only in cur_context EA calculations */ + 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; - } + sect = 0; + } else if (xct_flag != 0 && !fetch) { + if (((xct_flag & 8) != 0 && cur_context && !ptr_flg) || + ((xct_flag & 4) != 0 && !cur_context && !BYF5 && !ptr_flg) || + ((xct_flag & 2) != 0 && !cur_context && ptr_flg) || + ((xct_flag & 1) != 0 && !cur_context && BYF5 )) { + uf = (FLAGS & USERIO) != 0; + pub = (FLAGS & PRV_PUB) != 0; + + if ((xct_flag & 014) == 04 && !ptr_flg && glb_sect == 0) + sect = prev_sect; + if ((xct_flag & 03) == 01 && BYF5 && glb_sect == 0) + sect = prev_sect; + } } - /* If user, check if small user enabled */ - if (uf) { - if (small_user && (page & 0340) != 0) { - fault_data = (((uint64)(page))<<18) | ((uint64)(uf) << 27) | 020LL; + /* Check if invalid section */ + if (QKLB && t20_page && (sect & 07740) != 0) { + fault_data = (027LL << 30) | (((uint64)sect) << 18) | (uint64)addr; + if (uf) /* U */ + fault_data |= SMASK; /* BIT0 */ + page_fault = 1; + return 0; + } + + /* Handle KI paging odditiy */ + if (!uf && !t20_page && (page & 0740) == 0340) { + /* Pages 340-377 via UBT */ + page += 01000 - 0340; + upmp = 1; + } + + /* Map the page */ + if (uf || upmp) + data = u_tlb[page]; + else + data = e_tlb[page]; + + if (QKLB && t20_page && ((data >> 18) & 037) != sect) + data = 0; + /* If not valid, go refill it */ + if (data == 0) { + data = load_tlb(uf | upmp, page, wr); + if (data == 0 && page_fault) { + fault_data |= ((uint64)addr); + if (uf) /* U */ + fault_data |= SMASK; +#if KL_ITS + if (QITS) + return 0; +#endif + fault_data |= BIT8; + if (QKLB && t20_page) + fault_data |= (((uint64)sect) << 18); + if (fault_data & BIT1) + return 0; + if (wr) /* T */ + fault_data |= BIT5; /* BIT5 */ + return 0; + } + } + + /* Check if we need to modify TLB entry for TOPS 20 */ + if (t20_page && (data & KL_PAG_A) && (wr & ((data & KL_PAG_W) == 0)) && (data & KL_PAG_S)) { + uint64 cst = FM[(06<<4)|2] & PG_MASK; + uint64 cst_msk = FM[(06<<4)|0]; + uint64 cst_dat = FM[(06<<4)|1]; + /* Update CST entry if needed */ + if (cst) { + uint64 cst_val; + int pg = data & 017777; + sim_interval--; + cst_val = M[cst + pg]; + M[cst + pg] = (cst_msk & cst_val) | cst_dat | 1; + } + data |= KL_PAG_W; + /* Map the page */ + if (uf || upmp) + u_tlb[page] = data; + else + e_tlb[page] = data; + } + + /* create location. */ + *loc = ((data & 017777) << 9) + (addr & 0777); + + /* If PUBLIC and private page, make sure we are fetching a Portal */ + if ((data & KL_PAG_A) && !flag && pub && ((data & KL_PAG_P) == 0) && + (!fetch || !OP_PORTAL(M[*loc]))) { + /* Handle public violation */ + fault_data = ((uint64)addr) | 021LL << 30 | BIT8 |((uf)?SMASK:0); + if (QKLB && t20_page) + fault_data |= (((uint64)sect) << 18); + page_fault = 1; + return 0; + } + + + /* Check for access error */ + if ((data & KL_PAG_A) == 0 || (wr & ((data & KL_PAG_W) == 0))) { +#if KL_ITS + if (QITS) { + /* Remap the flag bits */ + if (uf) { /* U */ + u_tlb[page] = 0; + } else { + e_tlb[page] = 0; + } + if ((data & KL_PAG_A) == 0) { + fault_data = ((uint64)addr) | 033LL << 30 |((uf)?SMASK:0); + } else { + fault_data = ((uint64)addr) | 024LL << 30 |((uf)?SMASK:0); + } page_fault = 1; return 0; } +#endif + fault_data = BIT8 | (uint64)addr; + if (QKLB && t20_page) + fault_data |= (((uint64)sect) << 18); + /* Remap the flag bits */ + if (uf) { /* U */ + fault_data |= SMASK; /* BIT0 */ + u_tlb[page] = 0; + } else { + e_tlb[page] = 0; + } + if (data & KL_PAG_C) /* C */ + fault_data |= BIT7; /* BIT7 */ + if (data & KL_PAG_P) /* P */ + fault_data |= BIT6; /* BIT6 */ + if (wr) /* T */ + fault_data |= BIT5; /* BIT5 */ + if (data & KL_PAG_S) /* S */ + fault_data |= BIT4; /* BIT4 */ + if (data & KL_PAG_W) /* W */ + fault_data |= BIT3; /* BIT3 */ + if (data & KL_PAG_A) /* A */ + fault_data |= BIT2; /* BIT2 */ + page_fault = 1; + return 0; + } + + + /* If fetching from public page, set public flag */ + if (fetch && ((data & KL_PAG_P) != 0)) + FLAGS |= PUBLIC; + return 1; +} + +/* + * Register access on KL 10 + */ +uint64 get_reg(int reg) { + return FM[fm_sel|(reg & 017)]; +} + +void set_reg(int reg, uint64 value) { + FM[fm_sel|(reg & 017)] = value; +} + +int Mem_read(int flag, int cur_context, int fetch) { + t_addr addr; + + if (AB < 020 && ((QKLB && (glb_sect == 0 || sect == 0 || + (glb_sect && sect == 1))) || !QKLB)) { + if (xct_flag != 0 && !fetch) { + if (((xct_flag & 8) != 0 && cur_context && !ptr_flg) || + ((xct_flag & 4) != 0 && !cur_context && !BYF5 && !ptr_flg) || + ((xct_flag & 2) != 0 && !cur_context && ptr_flg) || + ((xct_flag & 1) != 0 && !cur_context && BYF5 )) { + MB = FM[prev_ctx|AB]; + return 0; + } + } + /* Check if invalid section */ + if (QKLB && t20_page && !flag && (sect & 07740) != 0) { + fault_data = (027LL << 30) | (uint64)AB | (((uint64)sect) << 18); + if (USER==0) /* U */ + fault_data |= SMASK; /* BIT0 */ + page_fault = 1; + return 0; + } + MB = get_reg(AB); } else { + if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch)) + return 1; + if (addr >= (int)MEMSIZE) { + irq_flags |= 02000; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) + watch_stop = 1; + sim_interval--; + MB = M[addr]; + } + return 0; +} + +int Mem_write(int flag, int cur_context) { + t_addr addr; + + if (AB < 020 && ((QKLB && (glb_sect == 0 || sect == 0 || + (glb_sect && sect == 1))) || !QKLB)) { + if (xct_flag != 0) { + if (((xct_flag & 8) != 0 && cur_context && !ptr_flg) || + ((xct_flag & 4) != 0 && !cur_context && !BYF5 && !ptr_flg) || + ((xct_flag & 2) != 0 && !cur_context && ptr_flg) || + ((xct_flag & 1) != 0 && !cur_context && BYF5 )) { + FM[prev_ctx|AB] = MB; + return 0; + } + } + /* Check if invalid section */ + if (QKLB && t20_page && !flag && (sect & 07740) != 0) { + fault_data = (027LL << 30) | (uint64)AB | (((uint64)sect) << 18); + if (USER==0) /* U */ + fault_data |= SMASK; /* BIT0 */ + page_fault = 1; + return 0; + } + set_reg(AB, MB); + } else { + if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr >= (int)MEMSIZE) { + irq_flags |= 02000; + return 1; + } + if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) + watch_stop = 1; + sim_interval--; + M[addr] = MB; + } + return 0; +} + +/* executive page table lookup */ +int exec_page_lookup(t_addr addr, int wr, t_addr *loc) +{ + int data; + int page = (RMASK & addr) >> 9; + int upmp = 0; + int sav_sect = sect; + + /* If paging is not enabled, address is direct */ + if (!page_enable) { + *loc = addr; + return 0; + } + + /* Handle KI paging odditiy */ + if (!t20_page && (page & 0740) == 0340) { + /* Pages 340-377 via UBT */ + page += 01000 - 0340; + upmp = 1; + } + + /* Map the page */ + if (upmp) + data = u_tlb[page]; + else + data = e_tlb[page]; + + /* If not valid, go refill it */ + if (data == 0 || (data & 037) != 0) { + sect = 0; + data = load_tlb(upmp, page, wr); + if (data == 0) { + page_fault = 0; + return 1; + } + sect = sav_sect; + } + *loc = ((data & 017777) << 9) + (addr & 0777); + return 0; +} + +int Mem_examine_word(int n, int wrd, uint64 *data) { + t_addr addr = 0144 + (8 * n) + eb_ptr; + int base = 0; + + if (addr >= MEMSIZE) + return 1; + if (M[addr] == 0 || wrd > M[addr]) + return 1; + addr = (M[addr+1] + wrd) & RMASK; + if (exec_page_lookup(addr, 0, &addr)) + return 1; + *data = M[addr]; + return 0; +} + +int Mem_deposit_word(int n, int wrd, uint64 *data) { + t_addr addr = 0146 + (8 * n) + eb_ptr; + int base = 0; + + if (addr >= MEMSIZE) + return 1; + if (M[addr] == 0 || wrd > M[addr]) + return 1; + addr = (M[addr+1] + wrd) & RMASK; + if (exec_page_lookup(addr, 1, &addr)) + return 1; + M[addr] = *data; + return 0; +} + +/* + * Read in 16 bits of data from a byte pointer. + */ +int Mem_read_byte(int n, uint16 *data, int byte) { + t_addr addr; + uint64 val; + uint64 msk; + int p, s, np; + int need = byte? 8: 16; + + *data = 0; + while (need > 0) { + addr = 0140 + (8 * n) + eb_ptr; + if (addr >= MEMSIZE) + return 0; + val = M[addr]; + s = (val >> 24) & 077; + p = (((val >> 30) & 077) + (0777 ^ s) + 1) & 0777; + if (p & 0400) { + p = np = (36 + (0777 ^ s) + 1) & 0777; + val = (val & LMASK) | ((val + 1) & RMASK); + } else + np = p; + np &= 077; + val &= PMASK; + val |= (uint64)(np) << 30; + M[addr] = val; + addr = val & RMASK; + if (exec_page_lookup((int)(val & RMASK), 0, &addr)) + return 0; + /* Generate mask for given size */ + msk = (uint64)(1) << s; + msk--; + val = M[addr]; + val = (val >> p) & msk; + if (s > 8) + need -= 16; + else + need -= 8; + *data |= val << need; + } + return s; +} + +int Mem_write_byte(int n, uint16 *data) { + t_addr addr; + uint64 val; + uint64 msk; + int p, s, np; + int need = 16; + uint16 dat = *data; + + dat = ((dat >> 8) & 0377) | ((dat & 0377) << 8); + while (need > 0) { + addr = 0141 + (8 * n) + eb_ptr; + if (addr >= MEMSIZE) + return 0; + val = M[addr]; + if (val == 0) + return 1; + s = (val >> 24) & 077; + p = (((val >> 30) & 077) + (0777 ^ s) + 1) & 0777; + if (p & 0400) { + p = np = (36 + (0777 ^ s) + 1) & 0777; + val = (val & LMASK) | ((val + 1) & RMASK); + } else + np = p; + np &= 077; + val &= PMASK; + val |= (uint64)(np) << 30; + M[addr] = val; + addr = val & RMASK; + if (exec_page_lookup((int)(val & RMASK), 1, &addr)) + return 0; + /* Generate mask for given size */ + msk = (uint64)(1) << s; + msk--; + msk <<= p; + val = M[addr]; + val &= CM(msk); + val |= msk & (((uint64)(dat >> (need - s))) << p); + M[addr] = val; + need -= s; + } + return s; +} + +#endif + +#if KI +/* + * Load the TLB entry, used for both page_lookup and MAP. + * Do not call this for direct map executive pages. + */ +int +load_tlb(int uf, int page) +{ + uint64 data; + int base = 0; + int upmp = 0; + + if (!uf) { /* Handle system mapping */ /* Pages 340-377 via UBR */ if ((page & 0740) == 0340) { @@ -1480,21 +2757,12 @@ int page_lookup(int addr, int flag, int *loc, int wr, int cur_context, int fetch 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; + /* Return what MAP wants to see */ + return (KI_PAG_A | KI_PAG_X | page); } } /* Map the page */ + sim_interval--; if (base) { data = e_tlb[page]; if (data == 0) { @@ -1519,23 +2787,94 @@ int page_lookup(int addr, int flag, int *loc, int wr, int cur_context, int fetch else last_page = ((page ^ 0777) << 1); } + return (int)(data & RMASK); +} + +/* + * 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(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { + int data; + int base = 0; + int page = (RMASK & addr) >> 9; + int uf = (FLAGS & USER) != 0; + int pub = (FLAGS & PUBLIC) != 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) { + if (((xct_flag & 2) != 0 && wr != 0) || + ((xct_flag & 1) != 0 && (wr == 0 || modify))) { + uf = (FLAGS & USERIO) != 0; + pub = (FLAGS & PRV_PUB) != 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; + } + } + + /* Handle direct pages */ + if (!uf && page < 0340) { + /* Check if supervisory mode */ + *loc = addr; + /* If PUBLIC and private page, make sure we are fetching a Portal */ + if (!flag && pub && + (!fetch || (M[addr] & 00777040000000LL) != 0254040000000LL)) { + /* Handle public violation */ + fault_data = (((uint64)(page))<<18) | ((uint64)(uf) << 27) + | 021LL; + page_fault = 1; + return !wr; + } + return 1; + } + data = load_tlb(uf, page); *loc = ((data & 017777) << 9) + (addr & 0777); /* Check for access error */ - if ((data & RSIGN) == 0 || (wr & ((data & 0100000) == 0))) { + if ((data & KI_PAG_A) == 0 || (wr & ((data & KI_PAG_W) == 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 |= (data & KI_PAG_A) ? 010LL : 0LL; /* A */ + fault_data |= (data & KI_PAG_W) ? 004LL : 0LL; /* W */ + fault_data |= (data & KI_PAG_S) ? 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)) { + if (!flag && pub && ((data & KI_PAG_P) == 0) && (!fetch || !OP_PORTAL(M[*loc]))) { /* Handle public violation */ fault_data = (((uint64)(page))<<18) | ((uint64)(uf) << 27) | 021LL; page_fault = 1; @@ -1543,7 +2882,7 @@ int page_lookup(int addr, int flag, int *loc, int wr, int cur_context, int fetch } /* If fetching from public page, set public flag */ - if (fetch && ((data & 0200000) != 0)) + if (fetch && ((data & KI_PAG_P) != 0)) FLAGS |= PUBLIC; return 1; } @@ -1565,47 +2904,8 @@ void set_reg(int reg, uint64 value) { 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; + t_addr addr; if (AB < 020) { if (FLAGS & USER) { @@ -1626,7 +2926,6 @@ int Mem_read(int flag, int cur_context, int fetch) { MB = get_reg(AB); } else { read: - sim_interval--; if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch)) return 1; if (addr >= (int)MEMSIZE) { @@ -1635,13 +2934,14 @@ read: } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; + sim_interval--; MB = M[addr]; } return 0; } int Mem_write(int flag, int cur_context) { - int addr; + t_addr addr; if (AB < 020) { if (FLAGS & USER) { @@ -1665,7 +2965,6 @@ int Mem_write(int flag, int cur_context) { set_reg(AB, MB); } else { write: - sim_interval--; if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) return 1; if (addr >= (int)MEMSIZE) { @@ -1674,6 +2973,7 @@ write: } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) watch_stop = 1; + sim_interval--; M[addr] = MB; } return 0; @@ -1698,11 +2998,12 @@ int its_load_tlb(uint32 reg, int page, uint32 *tlb) { fault_data |= 0200; return 1; } - if (entry > (int)MEMSIZE) { + if (entry >= (int)MEMSIZE) { nxm_flag = 1; fault_data |= 0400; return 1; } + sim_interval--; data = M[entry]; if (page & 1) { data &= ~036000LL; @@ -1724,7 +3025,7 @@ int its_load_tlb(uint32 reg, int page, uint32 *tlb) { * Translation logic for KA10 */ -int page_lookup_its(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { +int page_lookup_its(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { uint64 data; int base = 0; int page = (RMASK & addr) >> 10; @@ -1748,7 +3049,7 @@ int page_lookup_its(int addr, int flag, int *loc, int wr, int cur_context, int f /* Figure out if this is a user space access */ if (flag) uf = 0; - else if (xct_flag != 0 && !cur_context && !uf) { + else if (xct_flag != 0 && !cur_context) { if (((xct_flag & 2) != 0 && wr != 0) || ((xct_flag & 1) != 0 && (wr == 0 || modify))) { uf = 1; @@ -1856,16 +3157,15 @@ fault: * Return of 0 if successful, 1 if there was an error. */ int Mem_read_its(int flag, int cur_context, int fetch) { - int addr; + t_addr addr; if (AB < 020) { - if ((xct_flag & 1) != 0 && !cur_context && (FLAGS & USER) == 0) { + if ((xct_flag & 1) != 0 && !cur_context) { 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 @@ -1883,7 +3183,6 @@ int Mem_read_its(int flag, int cur_context, int fetch) { nxm_flag = 1; return 1; } - return 0; } #endif if (addr >= (int)MEMSIZE) { @@ -1892,6 +3191,7 @@ int Mem_read_its(int flag, int cur_context, int fetch) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; + sim_interval--; MB = M[addr]; } return 0; @@ -1903,16 +3203,15 @@ int Mem_read_its(int flag, int cur_context, int fetch) { * Return of 0 if successful, 1 if there was an error. */ int Mem_write_its(int flag, int cur_context) { - int addr; + t_addr addr; if (AB < 020) { - if ((xct_flag & 2) != 0 && !cur_context && (FLAGS & USER) == 0) { + if ((xct_flag & 2) != 0 && !cur_context) { 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 @@ -1930,7 +3229,6 @@ int Mem_write_its(int flag, int cur_context) { nxm_flag = 1; return 1; } - return 0; } #endif if (addr >= (int)MEMSIZE) { @@ -1939,6 +3237,7 @@ int Mem_write_its(int flag, int cur_context) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) watch_stop = 1; + sim_interval--; M[addr] = MB; } return 0; @@ -1946,7 +3245,7 @@ int Mem_write_its(int flag, int cur_context) { #endif #if BBN -int page_lookup_bbn(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { +int page_lookup_bbn(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { /* Group 0, 01 = 00 bit 2 = Age 00x 0100000 bit 3 = Age 02x 0040000 @@ -2008,7 +3307,7 @@ int page_lookup_bbn(int addr, int flag, int *loc, int wr, int cur_context, int f if (flag) uf = 0; else { - if (QWAITS && xct_flag != 0 && !fetch && !uf) { + if (QWAITS && xct_flag != 0 && !fetch) { if (xct_flag & 010 && cur_context) /* Indirect */ uf = 1; if (xct_flag & 004 && wr == 0) /* XR */ @@ -2016,7 +3315,7 @@ int page_lookup_bbn(int addr, int flag, int *loc, int wr, int cur_context, int f 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 (!QWAITS && (FLAGS & EXJSYS) == 0 && xct_flag != 0 && !fetch) { if (xct_flag & 010 && cur_context) uf = 1; if (xct_flag & 004 && wr == 0) @@ -2050,6 +3349,7 @@ lookup: goto fault_bbn; } base = user_base_reg; + sim_interval--; tlb_data = u_tlb[page]; } else { /* 000 - 077 resident map */ @@ -2064,6 +3364,7 @@ lookup: base = mon_base_reg; else base = 03000; + sim_interval--; tlb_data = e_tlb[page]; } if (tlb_data != 0) { @@ -2086,6 +3387,7 @@ access: /* Map the page */ match = 0; while (!match) { + sim_interval--; data = M[base + map]; switch ((data >> 34) & 03) { @@ -2150,6 +3452,7 @@ access: goto fault_bbn; } /* Update CST */ + sim_interval--; data = M[04000 + (tlb_data & 03777)]; if ((data & 00700000000000LL) == 0) { fault_data = 0100000 >> ((data >> 31) & 03); @@ -2204,14 +3507,13 @@ fault_bbn: * Return of 0 if successful, 1 if there was an error. */ int Mem_read_bbn(int flag, int cur_context, int fetch) { - int addr; + t_addr 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) { @@ -2224,6 +3526,7 @@ int Mem_read_bbn(int flag, int cur_context, int fetch) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; + sim_interval--; MB = M[addr]; return 0; } @@ -2234,14 +3537,13 @@ int Mem_read_bbn(int flag, int cur_context, int fetch) { * Return of 0 if successful, 1 if there was an error. */ int Mem_write_bbn(int flag, int cur_context) { - int addr; + t_addr 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) { @@ -2254,13 +3556,14 @@ int Mem_write_bbn(int flag, int cur_context) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) watch_stop = 1; + sim_interval--; 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 page_lookup_waits(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { int uf = (FLAGS & USER) != 0; /* If this is modify instruction use write access */ @@ -2269,7 +3572,7 @@ int page_lookup_waits(int addr, int flag, int *loc, int wr, int cur_context, int /* Figure out if this is a user space access */ if (flag) uf = 0; - else if (xct_flag != 0 && !fetch && !uf) { + else if (xct_flag != 0 && !fetch) { if (xct_flag & 010 && cur_context) /* Indirect */ uf = 1; if (xct_flag & 004 && wr == 0) /* XR */ @@ -2298,13 +3601,12 @@ int page_lookup_waits(int addr, int flag, int *loc, int wr, int cur_context, int } int Mem_read_waits(int flag, int cur_context, int fetch) { - int addr; + t_addr 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) { @@ -2313,6 +3615,7 @@ int Mem_read_waits(int flag, int cur_context, int fetch) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; + sim_interval--; MB = M[addr]; return 0; } @@ -2324,15 +3627,13 @@ int Mem_read_waits(int flag, int cur_context, int fetch) { */ int Mem_write_waits(int flag, int cur_context) { - int addr; - + t_addr 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) { @@ -2341,12 +3642,13 @@ int Mem_write_waits(int flag, int cur_context) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) watch_stop = 1; + sim_interval--; M[addr] = MB; return 0; } #endif -int page_lookup_ka(int addr, int flag, int *loc, int wr, int cur_context, int fetch) { +int page_lookup_ka(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { if (!flag && (FLAGS & USER) != 0) { if (addr <= Pl) { *loc = (addr + Rl) & RMASK; @@ -2368,12 +3670,11 @@ int page_lookup_ka(int addr, int flag, int *loc, int wr, int cur_context, int fe } int Mem_read_ka(int flag, int cur_context, int fetch) { - int addr; + t_addr 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) { @@ -2382,6 +3683,7 @@ int Mem_read_ka(int flag, int cur_context, int fetch) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; + sim_interval--; MB = M[addr]; } return 0; @@ -2394,12 +3696,11 @@ int Mem_read_ka(int flag, int cur_context, int fetch) { */ int Mem_write_ka(int flag, int cur_context) { - int addr; + t_addr 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) { @@ -2408,13 +3709,12 @@ int Mem_write_ka(int flag, int cur_context) { } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) watch_stop = 1; + sim_interval--; 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 @@ -2519,7 +3819,7 @@ t_stat dev_apr(uint32 dev, uint64 *data) { #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) { +int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch) { if (!flag && (FLAGS & USER) != 0) { if (addr <= Pl) { *loc = (addr + Rl) & RMASK; @@ -2534,12 +3834,12 @@ int page_lookup(int addr, int flag, int *loc, int wr, int cur_context, int fetch } int Mem_read(int flag, int cur_context, int fetch) { - int addr; + t_addr addr; + sim_interval--; 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) { @@ -2560,12 +3860,12 @@ int Mem_read(int flag, int cur_context, int fetch) { */ int Mem_write(int flag, int cur_context) { - int addr; + t_addr addr; + sim_interval--; 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) { @@ -2580,6 +3880,81 @@ int Mem_write(int flag, int cur_context) { } #endif +/* + * 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 = get_reg(AB); + } else { + if (AB >= (int)MEMSIZE) { +#if KL + irq_flags |= 02000; +#else + nxm_flag = 1; +#endif + return 1; + } + sim_interval--; + 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) { + set_reg(AB, MB); + } else { + if (AB >= (int)MEMSIZE) { +#if KL + irq_flags |= 02000; +#else + nxm_flag = 1; +#endif + return 1; + } + sim_interval--; + M[AB] = MB; + } + return 0; +} + +/* + * Access main memory. Returns 0 if access ok, 1 if out of memory range. + * On KI10 and KL10, optional EPT flag indicates address relative to ept. + */ +int Mem_read_word(t_addr addr, uint64 *data, int ept) +{ +#if KL | KI + if (ept) + addr += eb_ptr; +#endif + if (addr >= MEMSIZE) + return 1; + *data = M[addr]; + return 0; +} + +int Mem_write_word(t_addr addr, uint64 *data, int ept) +{ +#if KL | KI + if (ept) + addr += eb_ptr; +#endif + if (addr >= MEMSIZE) + return 1; + M[addr] = *data; + return 0; +} + + /* * Function to determine number of leading zero bits in a work */ @@ -2601,8 +3976,8 @@ 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 ix; /* Index register */ 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 */ @@ -2613,8 +3988,8 @@ int f; /* Temporary variables */ int flag1; int flag3; int instr_count = 0; /* Number of instructions to execute */ -uint32 IA; -#if ITS +uint32 IA; /* Initial address of first fetch */ +#if ITS | KL_ITS char one_p_arm = 0; /* One proceed arm */ #endif @@ -2639,6 +4014,9 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ BYF5 = 0; #if KI | KL page_fault = 0; +#if KL + ptr_flg = 0; +#endif #endif #if ITS if (QITS) { @@ -2646,11 +4024,15 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ set_quantum(); } #endif - watch_stop = 0; +#if KL_ITS + if (QITS) + one_p_arm = 0; +#endif + watch_stop = 0; - while ( reason == 0) { /* loop until ABORT */ - AIO_CHECK_EVENT; /* queue async events */ - if (sim_interval <= 0) { /* check clock queue */ + while ( reason == 0) { /* loop until ABORT */ + AIO_CHECK_EVENT; /* queue async events */ + if (sim_interval <= 0) { /* check clock queue */ if ((reason = sim_process_event()) != SCPE_OK) {/* error? stop sim */ #if ITS if (QITS) @@ -2670,6 +4052,13 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ break; } +#if MAGIC_SWITCH + if (!MAGIC) { + reason = STOP_MAGIC; + break; + } +#endif /* MAGIC_SWITCH */ + check_apr_irq(); /* Normal instruction */ if (f_load_pc) { @@ -2677,6 +4066,12 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ xct_flag = 0; #if KI | KL trap_flag = 0; +#if KL + sect = cur_sect = pc_sect; + glb_sect = 0; + extend = 0; + ptr_flg = 0; +#endif #endif AB = PC; uuo_cycle = 0; @@ -2696,11 +4091,16 @@ fetch: } } #endif - if (Mem_read(pi_cycle | uuo_cycle, 1, 1)) { +#if KA | PDP6 pi_rq = check_irq_level(); if (pi_rq) goto st_pi; +#endif +#if KL + if (((fault_data >> 30) & 037) == 021) + PC = (PC + 1) & RMASK; +#endif goto last; } @@ -2709,6 +4109,9 @@ no_fetch: AC = (MB >> 23) & 017; AD = MB; /* Save for historical sake */ IA = AB; +#if KL + glb_sect = 0; +#endif i_flags = opflags[IR]; BYF5 = 0; } @@ -2716,17 +4119,46 @@ no_fetch: #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; + if (FLAGS & ADRFLT) { +#if KL_ITS + if (QITS && (FLAGS & (TRP1|TRP2|ADRFLT)) == (TRP1|TRP2|ADRFLT)) + one_p_arm = 1; +#endif + FLAGS &= ~ADRFLT; + } else { + 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 +#if KL + /* If we are doing a PXCT with E1 or E2 set, change section */ + if (QKLB && t20_page) { + if (xct_flag != 0) { + if (((xct_flag & 8) != 0 && !ptr_flg) || + ((xct_flag & 2) != 0 && ptr_flg) +/* The following two lines are needed for Tops20 V3 */ +#if 1 + || ((xct_flag & 014) == 04 && !ptr_flg && prev_sect == 0) || + ((xct_flag & 03) == 01 && ptr_flg && prev_sect == 0) +#endif + ) + sect = cur_sect = prev_sect; + } + /* Short cut for extended pointer address */ + if (ptr_flg && (glb_sect || cur_sect != 0) && (AR & BIT12) != 0) { /* Full pointer */ + ind = 1; /* Allow us to read word, xDB has already bumped AB */ + goto in_loop; + } + } +#endif /* Handle indirection repeat until no longer indirect */ do { if ((!pi_cycle) & pi_pending @@ -2736,31 +4168,121 @@ no_fetch: ) { pi_rq = check_irq_level(); } - ind = (MB & 020000000) != 0; + ind = TST_IND(MB) != 0; AR = MB; AB = MB & RMASK; - if (MB & 017000000) { - AR = MB = (AB + get_reg((MB >> 18) & 017)) & FMASK; + ix = GET_XR(MB); + if (ix) { +#if KL + if (((xct_flag & 8) != 0 && !ptr_flg) || + ((xct_flag & 2) != 0 && ptr_flg)) + AR = FM[prev_ctx|ix]; + else + AR = get_reg(ix); + /* Check if extended indexing */ + if (QKLB && t20_page && cur_sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { + AR = (AR + ((AB & RSIGN) ? SECTM|((uint64)AB): (uint64)AB)) & (SECTM|RMASK); + sect = cur_sect = (AR >> 18) & 07777; + glb_sect = 1; + AB = 0; + } else + glb_sect = 0; + /* For KL */ + AR = MB = (AB + AR) & FMASK; +#else + /* For KA & KI */ + AR = MB = (AB + get_reg(ix)) & FMASK; +#endif AB = MB & RMASK; } - if (IR != 0254) - AR &= RMASK; - if (ind & !pi_rq) - if (Mem_read(pi_cycle | uuo_cycle, 1, 0)) +#if KL +in_loop: +#endif + if (ind & !pi_rq) { + if (Mem_read(pi_cycle | uuo_cycle, 1, 0)) goto last; +#if KL + /* Check if extended indexing */ + if (QKLB && t20_page && (cur_sect != 0 || glb_sect)) { + if (MB & SMASK || cur_sect == 0) { /* Instruction format IFIW */ + if (MB & BIT1 && cur_sect != 0) { /* Illegal index word */ + fault_data = 024LL << 30 | (((FLAGS & USER) != 0)?SMASK:0) | + (AR & RMASK) | ((uint64)cur_sect << 18); + page_fault = 1; + goto last; + } + ind = TST_IND(MB) != 0; + ix = GET_XR(MB); + AB = MB & RMASK; + if (ix) { + if (((xct_flag & 8) != 0 && !ptr_flg) || + ((xct_flag & 2) != 0 && ptr_flg)) + AR = FM[prev_ctx|ix]; + else + AR = get_reg(ix); + /* Check if extended indexing */ + if (cur_sect == 0 || (AR & SMASK) != 0 || (AR & SECTM) == 0) { + /* Local index word */ + AR = (AR + AB) & RMASK; + glb_sect = 0; + } else { + AR = (AR + AB) & FMASK; + glb_sect = 1; + sect = cur_sect = (AR >> 18) & 07777; + } + MB = AR; + } else { + glb_sect = 0; + if ((MB & RMASK) < 020) + sect = cur_sect = 1; + AR = MB; + } + AB = AR & RMASK; + } else { /* Extended index EFIW */ + ind = (MB & BIT1) != 0; + ix = (MB >> 30) & 017; + AB = MB & (SECTM|RMASK); + if (ix) { + if (((xct_flag & 8) != 0 && !ptr_flg) || + ((xct_flag & 2) != 0 && ptr_flg)) + AR = FM[prev_ctx|ix]; + else + AR = get_reg(ix); + if ((AR & SMASK) != 0 || (AR & SECTM) == 0) { /* Local index word */ + AR = AB + (((AR & RSIGN) ? 0: 0)|(AR & RMASK)); + } else + AR = AR + AB; + AR &= FMASK; + MB = AR; + } else + AR = MB; + sect = cur_sect = (AR >> 18) & 07777; + AB = AR & RMASK; + glb_sect = 1; + } + if (ind) + goto in_loop; + } +#endif + } /* Handle events during a indirect loop */ - AIO_CHECK_EVENT; /* queue async events */ + AIO_CHECK_EVENT; /* queue async events */ if (sim_interval-- <= 0) { if ((reason = sim_process_event()) != SCPE_OK) { return reason; } } } while (ind & !pi_rq); + if (IR != 0254) { + AR &= RMASK; + } /* If there is a interrupt handle it. */ if (pi_rq) { +#if KA | PDP6 st_pi: +#endif sim_debug(DEBUG_IRQ, &cpu_dev, "trap irq %o %03o %03o \n", pi_enc, PIR, PIH); pi_cycle = 1; @@ -2768,8 +4290,12 @@ st_pi: pi_hold = 0; pi_ov = 0; AB = 040 | (pi_enc << 1) | maoff; -#if KI | KL xct_flag = 0; +#if KI | KL +#if KL + sect = cur_sect = 0; + extend = 0; +#endif /* * Scan through the devices and allow KI devices to have first * hit at a given level. @@ -2777,10 +4303,19 @@ st_pi: for (f = 0; f < 128; f++) { if (dev_irqv[f] != 0 && dev_irq[f] & (0200 >> pi_enc)) { AB = dev_irqv[f](f << 2, AB); + if (dev_irqv[f] != 0) + sim_debug(DEBUG_IRQ, &cpu_dev, "vect irq %o %03o %06o\n", + pi_enc, dev_irq[f], AB); break; } } - AB |= eb_ptr; + if (AB & RSIGN) + AB &= 0777; + else + AB |= eb_ptr; +#if KL + pi_vect = AB; +#endif Mem_read_nopage(); goto no_fetch; #else @@ -2798,29 +4333,45 @@ st_pi: #endif /* Check if possible idle loop */ - if (sim_idle_enab && + if (sim_idle_enab && (((FLAGS & USER) != 0 && PC < 020 && AB < 020 && (IR & 0760) == 0340) || (uuo_cycle && (IR & 0740) == 0 && IA == 041))) { sim_idle (TMR_RTC, FALSE); } /* Update history */ - if (hst_lnt) { + if (hst_lnt && PC > 017) { 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; +#if KL + if (extend) + hst[hst_p].pc |= HIST_PCE; + hst[hst_p].pc |= (pc_sect << 18); + hst[hst_p].ea |= (sect << 18); +#endif hst[hst_p].ir = AD; - hst[hst_p].flags = (FLAGS << 5) |(clk_flg << 2) | (nxm_flag << 1) + hst[hst_p].flags = (FLAGS << 5) +#if KA | KI | PDP6 + |(clk_flg << 2) | (nxm_flag << 1) #if KA | PDP6 | (mem_prot << 4) | (push_ovf << 3) #endif #if PDP6 | ill_op #endif +#endif +#if KL + | (fm_sel >> 4) +#endif + ; +#if KL + hst[hst_p].prev_sect = prev_sect; +#endif hst[hst_p].ac = get_reg(AC); } @@ -2833,7 +4384,16 @@ st_pi: sac_inh = 0; modify = 0; f_pc_inh = 0; - +#if KL + if (extend) { + if (IR == 0 || IR > 031 || AC != 0 || do_extend(IA)) { + IR = 0123; + AC = ext_ac; + goto muuo; + } + goto last; + } +#endif /* Load pseudo registers based on flags */ if (i_flags & (FCEPSE|FCE)) { if (i_flags & FCEPSE) @@ -2852,7 +4412,7 @@ st_pi: BR = get_reg(AC); } - if (hst_lnt) { + if (hst_lnt && PC >= 020) { hst[hst_p].mb = AR; } @@ -2868,35 +4428,124 @@ st_pi: /* Process the instruction */ switch (IR) { +#if KL + case 0052: /* PMOVE */ + case 0053: /* PMOVEM */ + if (QKLB && t20_page && (FLAGS & USER) == 0) { + if (Mem_read(0, 0, 0)) + goto last; + AB = MB & (SECTM|RMASK); + if (IR & 1) { + MB = get_reg(AC); + if (Mem_write_nopage()) + goto last; + } else { + if (Mem_read_nopage()) + goto last; + set_reg(AC, MB); + } + break; + } + /* Fall through */ +#else + case 0052: case 0053: + /* Fall through */ +#endif 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 0050: case 0051: 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: +#if !KL_ITS case 0074: case 0075: case 0076: case 0077: +#endif /* MUUO */ #if KI | KL - case 0100: case 0101: case 0102: case 0103: - case 0104: case 0105: case 0106: case 0107: - case 0123: + case 0100: /* UJEN */ + case 0101: case 0102: case 0103: + case 0104: /* JSYS */ + case 0106: + case 0107: +#if !KL_ITS case 0247: /* UUO */ +#endif unasign: + /* Save Opcode */ +#if KL + if (QKLB && t20_page) { + AR = (uint64)AB; /* Save address */ + if (pc_sect != 0) { + if (glb_sect == 0 && AB < 020) + AR |= BIT17; + else + AR |= ((uint64)cur_sect) << 18; + } + MB = (((uint64)((IR << 9) | (AC << 5))) | ((uint64)(FLAGS) << 23)) & FMASK; + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + MB |= (uint64)(prev_sect); + } + } else +#endif MB = ((uint64)(IR) << 27) | ((uint64)(AC) << 23) | (uint64)(AB); AB = ub_ptr | 0424; +#if KL + /* If single sections KL10 UUO starts at 425 */ + if (!QKLB && !QITS && t20_page) + AB = AB + 1; +#endif Mem_write_nopage(); - AB |= 1; + /* Save flags */ + AB++; +#if KL + if (QKLB && t20_page) + MB = ((uint64)(pc_sect) << 18) | ((PC + (trap_flag == 0)) & RMASK); + else { + MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + (trap_flag == 0)) & RMASK); + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + } + } +#else MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + (trap_flag == 0)) & RMASK); if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; } +#endif Mem_write_nopage(); +#if KL + extend = 0; + if (QKLB && t20_page) { /* Save address */ + if (pc_sect != 0 && glb_sect == 0 && AR < 020) + AR |= BIT17; + else + AR |= ((uint64)cur_sect) << 18; + MB = AR; + AB ++; + Mem_write_nopage(); + } + /* Save context */ + AB ++; + MB = SMASK| + ((uint64)(fm_sel & 0160) << 23) | + ((uint64)(prev_ctx & 0160) << 20) | + (ub_ptr >> 9); + if (QKLB && t20_page) { + MB |= BIT1|((uint64)(prev_sect & 037) << 18); + prev_sect = pc_sect & 037; + } + Mem_write_nopage(); +#endif + /* Read in new PC and flags */ FLAGS &= ~ (PRV_PUB|BYTI|ADRFLT|TRP1|TRP2); AB = ub_ptr | 0430; if (trap_flag != 0) @@ -2906,12 +4555,21 @@ unasign: if (FLAGS & USER) AB |= 4; Mem_read_nopage(); + +#if KL + if (QKLB && t20_page) { + pc_sect = (MB >> 18) & 00037; + FLAGS = 0; + } else +#endif 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; + if ((FLAGS & USER) == 0) { + if ((AB & 4) != 0) + FLAGS |= USERIO; + if ((AB & 2 || (FLAGS & OVR) != 0)) + FLAGS |= PRV_PUB|OVR; + } PC = MB & RMASK; f_pc_inh = 1; break; @@ -2928,12 +4586,48 @@ unasign: 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 KL + /* LUUO's in non-zero section are different */ + if (QKLB && t20_page && pc_sect != 0) { + /* Save Effective address */ + if (pc_sect != 0 && glb_sect == 0 && AR < 020) + AR = BIT17; + else + AR = ((uint64)cur_sect) << 18; + AR |= AB; /* Save address */ + /* Grab address of LUUO block from user base 420 */ + AB = ((FLAGS & USER) ? ub_ptr : eb_ptr) + 0420; + Mem_read_nopage(); + /* Now save like MUUO */ + AB = MB & (SECTM|RMASK); + MB = (((uint64)((IR << 9) | (AC << 5))) | ((uint64)(FLAGS) << 23)) & FMASK; + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + } + Mem_write_nopage(); + /* Save PC */ + AB++; + MB = ((uint64)(pc_sect) << 18) | ((PC + (trap_flag == 0)) & RMASK); + Mem_write_nopage(); + MB = AR; + AB ++; + Mem_write_nopage(); + AB ++; + /* Read PC */ + Mem_read_nopage(); + pc_sect = (MB >> 18) & 07777; + PC = MB & RMASK; + f_pc_inh = 1; + break; + } +#endif #if PDP6 ill_op = 1; ex_uuo_sync = 1; #endif MB = ((uint64)(IR) << 27) | ((uint64)(AC) << 23) | (uint64)(AB); -#if KI | KL +#if KI if ((FLAGS & USER) == 0) { AB = eb_ptr + 040; Mem_write_nopage(); @@ -2957,8 +4651,87 @@ unasign: #endif f_pc_inh = 1; break; +#if KL_ITS + case 0074: /* XCTR */ + case 0075: /* XCTRI */ + if (QITS && (FLAGS & USER) == 0) { + f_load_pc = 0; + f_pc_inh = 1; + xct_flag = AC; + break; + } + goto unasign; + + case 0076: /* LMR */ + if (QITS && (FLAGS & USER) == 0) { + /* Load store ITS pager info */ + if ((AB + 8) >= MEMSIZE) { + break; + } + MB = M[AB]; /* WD 0 */ + jpc = (MB & RMASK); + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 1 */ + brk_addr = MB & RMASK; + brk_flags = 017 & (MB >> 23); + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 2 */ + FM[(6<<4)|0] = MB; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 3 */ + dbr1 = MB; + AB = (AB + 1) & RMASK; + MB = M[AB]; /* WD 4 */ + dbr2 = MB; + for (f = 0; f < 512; f++) + u_tlb[f] = 0; + break; + } + goto unasign; + case 0077: /* SPM */ + if (QITS && (FLAGS & USER) == 0) { + if ((AB + 8) >= MEMSIZE) { + break; + } + MB = (uint64)jpc; + M[AB] = MB; /* WD 0 */ + AB = (AB + 1) & RMASK; + MB = (uint64)brk_addr; + MB |= ((uint64)brk_flags) << 23; + M[AB] = MB; /* WD 1 */ + AB = (AB + 1) & RMASK; + MB = FM[(6<<4)|0]; + M[AB] = MB; /* WD 2 */ + AB = (AB + 1) & RMASK; + MB = dbr1; + M[AB] = MB; /* WD 3 */ + AB = (AB + 1) & RMASK; + MB = dbr2; + M[AB] = MB; /* WD 4 */ + break; + } + goto unasign; +#endif + #if KI | KL +#if KL + case 0105: /* ADJSP */ + BR = get_reg(AC); + if (QKLB && t20_page && pc_sect != 0 && (BR & SMASK) == 0 && (BR & SECTM) != 0) { + AD = (((AR & RSIGN)?(LMASK|AR):AR) + BR) & (SECTM|RMASK); + AD |= BR & ~(SECTM|RMASK); + } else { + AD = (BR + AR) & RMASK; + AD |= (BR & LMASK) + ((AR << 18) & LMASK); + if (QKLB && pc_sect == 0 && ((BR ^ AD) & SMASK) != 0) + FLAGS |= TRP2; + } + i_flags = SAC; + AR = AD & FMASK; + break; +#endif + case 0110: /* DFAD */ case 0111: /* DFSB */ /* On Load AR,MQ has memory operand */ @@ -3155,8 +4928,13 @@ dpnorm: if ((AR & 0777) == 0777) AR &= (FPFMASK << 8); } +#if KL + while (((AR & (FPSBIT|FPNBIT)) == (FPSBIT|FPNBIT)) || + ((AR & (FPSBIT|FPNBIT)) == 0)) { +#else if (((AR & (FPSBIT|FPNBIT)) == (FPSBIT|FPNBIT)) || ((AR & (FPSBIT|FPNBIT)) == 0)) { +#endif SC --; AR <<= 1; if (MQ & BIT1) @@ -3165,6 +4943,16 @@ dpnorm: MQ &= FMASK; nrf = 1; } +#if KL + /* Handle special minus case */ + if (AR == (FPHBIT|FPSBIT)) { + SC += 1; + if (AR & 1) + MQ |= SMASK; + MQ >>= 1; + AR = (AR & FPHBIT) | (AR >> 1); + } +#endif } else { AR = MQ = 0; SC = 0; @@ -3216,13 +5004,18 @@ dpnorm: SMEAR_SIGN(BR); BR <<= 35; BR |= MB & CMASK; +#if KL + /* One extra bit for KL */ + AR <<= 1; + BR <<= 1; +#endif /* Make both positive */ flag1 = 0; - if (AR & FPSBIT) { + if (AR & FPHBIT) { AR = (FPFMASK ^ AR) + 1; flag1 = 1; } - if (BR & FPSBIT) { + if (BR & FPHBIT) { BR = (FPFMASK ^ BR) + 1; flag1 = !flag1; } @@ -3250,7 +5043,7 @@ dpnorm: FLAGS |= FLTUND|OVR|FLTOVR|TRP1; /* Do divide */ AD = 0; - for (FE = 0; FE < 62; FE++) { + for (FE = 0; FE < (62 + KL); FE++) { AD <<= 1; if (AR >= BR) { AR = AR - BR; @@ -3259,15 +5052,24 @@ dpnorm: AR <<= 1; } AR = AD; + /* Fix sign of result */ if (flag1) { AR = (AR ^ FPFMASK) + 1; } +#if KL + else + AR++; /* Round on KL */ + AR = (AR & FPHBIT) | (AR >> 1); /* Remove extra bit */ +#endif + /* Check potential overflow */ if (((SC & 0400) != 0) ^ ((SC & 0200) != 0) || SC == 0600) fxu_hold_set = 1; + /* Normalize */ while (((AR & FPHBIT) != 0) != ((AR & FPSBIT) != 0)) { SC += 1; AR = (AR & FPHBIT) | (AR >> 1); } + /* Extract halfs from 64bit word */ MQ = (AR & CMASK); AR >>= 35; AR &= MMASK; @@ -3279,6 +5081,7 @@ dpnorm: FLAGS |= FLTUND; } } + /* Add in exponent */ SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); AR &= SMASK|MMASK; if (AR != 0 || MQ != 0) @@ -3288,11 +5091,275 @@ dpnorm: set_reg(AC+1, MQ); break; +#if KL + case 0114: /* DADD */ + flag1 = flag3 = 0; + /* AR,ARX = AC BR,BX = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + BR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + BRX = MB; + AR = get_reg(AC); + ARX = get_reg(AC + 1); + /* Add numbers */ + ARX = (ARX & CMASK) + (BRX & CMASK); + f = (ARX & SMASK) != 0; + if (((AR & CMASK) + (BR & CMASK) + f) & SMASK) { + FLAGS |= CRY1; + flag1 = 1; + } + AR = AR + BR + f; + if (AR & C1) { + if (!pi_cycle) + FLAGS |= CRY0; + flag3 = 1; + } + AR &= FMASK; + if (flag1 != flag3) { + if (!pi_cycle) + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + ARX &= CMASK; + ARX |= AR & SMASK; + set_reg(AC, AR); + set_reg(AC+1, ARX); + break; + + case 0115: /* DSUB */ + flag1 = flag3 = 0; + /* AR,AX = AC BR,BX = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + BR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + BRX = MB; + AR = get_reg(AC); + ARX = get_reg(AC + 1); + /* Add numbers */ + ARX = (ARX & CMASK) + CCM(BRX) + 1; + f = (ARX & SMASK) != 0; + if (((AR & CMASK) + CCM(BR) + f) & SMASK) { + FLAGS |= CRY1; + flag1 = 1; + } + AR = AR + CM(BR) + f; + if (AR & C1) { + if (!pi_cycle) + FLAGS |= CRY0; + flag3 = 1; + } + AR &= FMASK; + if (flag1 != flag3) { + if (!pi_cycle) + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + ARX &= CMASK; + ARX |= AR & SMASK; + set_reg(AC, AR); + set_reg(AC+1, ARX); + break; + + case 0116: /* DMUL */ + flag1 = flag3 = 0; + /* AR,ARX = AC BR,BRX = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + BR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + BRX = MB; + AR = get_reg(AC); + ARX = get_reg(AC + 1); + /* Make BR,BRX positive */ + if (BR & SMASK) { + /* Low */ + BRX = CCM(BRX) + 1; /* Low */ + /* High */ + BR = (CM(BR) + ((BRX & SMASK) != 0)) & FMASK; + flag1 = 1; + /* Can only occur if 2**-70 */ + if (BR & SMASK) + FLAGS |= OVR|TRP1; + } + /* Make AR,ARX positive */ + if (AR & SMASK) { + /* Low */ + ARX = CCM(ARX) + 1; /* Low */ + /* High */ + AR = (CM(AR) + ((ARX & SMASK) != 0)) & FMASK; + flag1 ^= 1; + /* Can only occur if 2**-70 */ + if (AR & SMASK) + FLAGS |= OVR|TRP1; + } + /* Form product in AD,ADX,BR,BX */ + AD = ADX = 0; + BRX &= CMASK; /* Clear sign of BX */ + ARX &= CMASK; + /* Compute product */ + for (SC = 70; SC >= 0; SC--) { + /* Shift MQ,MB,BR,BX right one */ + f = (BRX & 1); + if (BR & 1) + BRX |= SMASK; + if (ADX & 1) + BR |= SMASK; + if (AD & 1) + ADX |= SMASK; + BRX >>= 1; + BR >>= 1; + ADX >>= 1; + AD >>= 1; + if (f) { /* Add AR,ARX to AD,ADX */ + ADX = ADX + ARX; + AD = AD + AR + ((ADX & SMASK) != 0); + ADX &= CMASK; + } + } + /* If minus, negate whole thing */ + if (flag1) { + BRX = CCM(BRX) + 1; /* Low */ + BR = CCM(BR) + ((BRX & SMASK) != 0); + ADX = CCM(ADX) + ((BR & SMASK) != 0); + AD = CM(AD) + ((ADX & SMASK) != 0); + } + /* Copy signs */ + BRX &= CMASK; + BR &= CMASK; + ADX &= CMASK; + AD &= FMASK; + BRX |= AD & SMASK; + BR |= AD & SMASK; + ADX |= AD & SMASK; + /* Save results */ + set_reg(AC, AD); + set_reg(AC+1, ADX); + set_reg(AC+2, BR); + set_reg(AC+3, BRX); + break; + + case 0117: /* DDIV */ + flag1 = flag3 = 0; + /* AR,ARX = AC BR,BRX = mem */ + /* AR High */ + if (Mem_read(0, 0, 0)) + goto last; + BR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + BRX = MB; + /* Make BR,BX positive */ + if (BR & SMASK) { + /* Low */ + BRX = CCM(BRX) + 1; /* Low */ + /* High */ + BR = (CM(BR) + ((BRX & SMASK) != 0)) & FMASK; + flag1 = 1; + /* Can only occur if 2**-70 */ + if (BR & SMASK) { + FLAGS |= OVR|TRP1; + } + } + if ((BR | BRX) == 0) { + FLAGS |= NODIV; + break; + } + /* Get dividend */ + AR = get_reg(AC); + ARX = get_reg(AC + 1); + MB = get_reg(AC + 2); + MQ = get_reg(AC + 3); + + /* Make MQ,MB,AR,ARX positive */ + if (AR & SMASK) { + /* Low */ + MQ = CCM(MQ) + 1; + MB = CCM(MB) + ((MQ & SMASK) != 0); + ARX = CCM(ARX) + ((MB & SMASK) != 0); + AR = (CM(AR) + ((ARX & SMASK) != 0)) & FMASK; + flag1 ^= 1; + flag3 = 1; + if (AR & SMASK) { + FLAGS |= OVR|TRP1; + } + } + MQ &= CMASK; + MB &= CMASK; + ARX &= CMASK; + /* Precheck divide ok */ + ADX = ARX + CCM(BRX) + 1; + AD = AR + CM(BR) + ((ADX & SMASK) != 0); + if ((AD & C1) != 0) { + FLAGS |= OVR|TRP1|NODIV; + break; + } + /* Do divide */ + for (SC = 70; SC > 0; SC--) { + AR <<= 1; + ARX <<= 1; + MB <<= 1; + MQ <<= 1; + if (ARX & SMASK) + AR |= 1; + if (MB & SMASK) + ARX |= 1; + if (MQ & SMASK) + MB |= 1; + ARX &= CMASK; + MB &= CMASK; + MQ &= CMASK; + ADX = ARX + CCM(BRX) + 1; + AD = AR + CM(BR) + ((ADX & SMASK) != 0); + if ((AD & SMASK) == 0) { + ARX = ADX; + AR = AD & CMASK; + MQ |= 1; + } + } + BRX &= CMASK; /* Clear sign of BX */ + ARX &= CMASK; + /* Set sign of quotent */ + if (flag1) { + MQ = CCM(MQ) + 1; + MB = CM(MB) + ((MQ & SMASK) != 0); + MQ &= CMASK; + MB &= FMASK; + } + /* Set sign or remainder */ + if (flag3) { + ARX = CCM(ARX) + 1; /* Low */ + AR = CM(AR) + ((ARX & SMASK) != 0); + ARX &= CMASK; + AR &= FMASK; + } + MQ |= MB & SMASK; + ARX |= AR & SMASK; + /* Save results */ + set_reg(AC, MB); + set_reg(AC+1, MQ); + set_reg(AC+2, AR); + set_reg(AC+3, ARX); + break; + +#else case 0114: /* DADD */ case 0115: /* DSUB */ case 0116: /* DMUL */ case 0117: /* DDIV */ goto unasign; +#endif case 0120: /* DMOVE */ if (Mem_read(0, 0, 0)) @@ -3313,16 +5380,53 @@ dpnorm: AB = (AB + 1) & RMASK; if (Mem_read(0, 0, 0)) goto last; - MQ = ((MB & CMASK) ^ CMASK) + 1; /* Low */ + MQ = CCM(MB) + 1; /* Low */ /* High */ - AR = (CM(AR) + ((MQ & SMASK) != 0)) & FMASK; +#if KL + if ((CCM(AR) + ((MQ & SMASK) != 0)) & SMASK) { + FLAGS |= CRY1; + flag1 = 1; + } +#endif + AR = (CM(AR) + ((MQ & SMASK) != 0)); MQ &= CMASK; +#if KL + if (AR & C1) { + FLAGS |= CRY0; + flag3 = 1; + } + if (flag1 != flag3 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + if ((AR == SMASK && MQ == 0) && !pi_cycle) + FLAGS |= TRP1; +#endif + AR &= FMASK; set_reg(AC, AR); set_reg(AC+1, MQ); break; + case 0123: /* Extend */ +#if KL + /* Handle like xct */ + f_load_pc = 0; + f_pc_inh = 1; + extend = 1; + ext_ac = AC; + BR = AB; /* Save address of instruction */ + if (Mem_read(0, 1, 0)) + goto last; + goto no_fetch; +#else + goto unasign; +#endif + case 0124: /* DMOVEM */ AR = get_reg(AC); +#if KL + MQ = get_reg(AC + 1); +#endif /* Handle each half as seperate instruction */ if ((FLAGS & BYTI) == 0) { MB = AR; @@ -3330,13 +5434,15 @@ dpnorm: goto last; FLAGS |= BYTI; } +#if !KL MQ = get_reg(AC + 1); +#endif if ((FLAGS & BYTI)) { AB = (AB + 1) & RMASK; MB = MQ; + FLAGS &= ~BYTI; if (Mem_write(0, 0)) goto last; - FLAGS &= ~BYTI; } break; @@ -3346,15 +5452,42 @@ dpnorm: /* 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) + BR = BR + 1; + MQ = CCM(MQ) + 1; + if (MQ & SMASK) { +#if KL + if ((CCM(get_reg(AC)) + 1) & SMASK) { + FLAGS |= CRY1; + flag1 = 1; + } +#endif AR = BR; +#if KL + if (AR & C1) { + FLAGS |= CRY0; + flag3 = 1; + } + if (flag1 != flag3 && !pi_cycle) { + FLAGS |= OVR|TRP1; + check_apr_irq(); + } + if ((AR == SMASK && MQ == 0) && !pi_cycle) + FLAGS |= TRP1; +#endif + } AR &= FMASK; MB = AR; if (Mem_write(0, 0)) goto last; FLAGS |= BYTI; +#if KL + AB = (AB + 1) & RMASK; + MB = MQ & CMASK; + if (Mem_write(0, 0)) + goto last; + FLAGS &= ~BYTI; + break; +#endif } if ((FLAGS & BYTI)) { MQ = get_reg(AC + 1); @@ -3446,7 +5579,7 @@ dpnorm: /* Load store ITS pager info */ /* AC & 1 = Store */ if (AC & 1) { - if ((AB + 8) > MEMSIZE) { + if ((AB + 8) >= MEMSIZE) { fault_data |= 0400; mem_prot = 1; break; @@ -3479,7 +5612,7 @@ dpnorm: MB = (uint64)ac_stack; M[AB] = MB; } else { - if ((AB + 8) > MEMSIZE) { + if ((AB + 8) >= MEMSIZE) { fault_data |= 0400; mem_prot = 1; break; @@ -3498,10 +5631,9 @@ dpnorm: AB = (AB + 1) & RMASK; MB = M[AB]; /* WD 3 */ /* Store Quantum */ - qua_time = MB & (RMASK|BIT17); + qua_time = MB & RMASK; set_quantum(); fault_data = (MB >> 18) & RMASK; - fault_data &= ~1; /* Clear high quantum bit */ mem_prot = 0; if ((fault_data & 0777772) != 0) mem_prot = 1; @@ -3611,7 +5743,7 @@ dpnorm: goto unasign; case 0247: /* UUO or ITS CIRC instruction */ -#if ITS +#if ITS | KL_ITS if (QITS) { BR = AR; AR = get_reg(AC); @@ -3639,9 +5771,6 @@ dpnorm: #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; @@ -3651,9 +5780,9 @@ dpnorm: goto ufa; } #endif - + /* UUO */ - case 0105: case 0106: case 0107: + 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: @@ -3681,35 +5810,172 @@ unasign: #endif case 0133: /* IBP/ADJBP */ +#if KL + if (AC != 0) { /* ADJBP */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; + SC = (AR >> 24) & 077; /* S */ + if (SC) { + int bpw, left, newb, adjw, adjb; + + FE = (AR >> 30) & 077; /* P */ + f = 0; + if (QKLB && t20_page && pc_sect != 0 && FE > 36) { + if (FE == 077) + goto muuo; + f = 1; + SC = _byte_adj[(FE - 37)].s; + FE = _byte_adj[(FE - 37)].p; + } + left = (36 - FE) / SC; /* Number bytes left (36 - P)/S */ + bpw = left + (FE / SC); /* Bytes per word */ + if (bpw == 0) { + FLAGS |= OVR|NODIV|TRP1; + break; + } + BR = get_reg(AC); /* Grab amount to move */ + /* Make BR into native integer */ + if (BR & RSIGN) + adjw = -((int)(CM(BR) + 1) & RMASK); + else + adjw = (int)(BR & RMASK); + newb = adjw + left; /* Adjust to word boundry */ + adjw = newb / bpw; /* Number of words to move */ + adjb = (newb >= 0)? newb % bpw: -((-newb) % bpw); + if (adjb <= 0) { + adjb += bpw; /* Move forward */ + adjw--; + } + FE = 36 - (adjb * SC) - ((36 - FE) % SC); /* New P */ + if (f) { + /* Short pointer */ + for (f = 0; f < 28; f++) { + if (_byte_adj[f].s == SC && _byte_adj[f].p == FE) { + FE = f + 37; + break; + } + } + AR = (((uint64)(FE & 077)) << 30) | /* Make new BP */ + ((AR + adjw) & (SECTM|RMASK)); + set_reg(AC, AR); + break; + } else if (QKLB && t20_page && pc_sect != 0 && (AR & BIT12) != 0) { + /* Full pointer */ + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + AR = (((uint64)(FE & 077)) << 30) | /* Make new BP */ + (AR & PMASK); /* S and below */ + if (MB & SMASK) { + if (MB & BIT1) { + fault_data = 024LL << 30 | (((FLAGS & USER) != 0)?SMASK:0) | + (AB & RMASK) | ((uint64)cur_sect << 18); + page_fault = 1; + goto last; + } + BR = ((MB + adjw) & RMASK) | (MB & LMASK); + } else + BR = ((MB + adjw) & (SECTM|RMASK)) | (MB & ~(SECTM|RMASK)); + set_reg(AC, AR); + set_reg(AC+1, BR); + break; + } + AR = (((uint64)(FE & 077)) << 30) | /* Make new BP */ + (AR & PMASK & LMASK) | /* S,IX,I */ + ((AR + adjw) & RMASK); + } + set_reg(AC, AR); + break; + } +#endif case 0134: /* ILDB */ case 0136: /* IDPB */ if ((FLAGS & BYTI) == 0) { /* BYF6 */ +#if KL + if (Mem_read(0, 0, 0)) { +#elif KI + modify = 1; + if (Mem_read(0, 1, 0)) { +#else modify = 1; if (Mem_read(0, !QITS, 0)) { +#endif #if PDP6 FLAGS |= BYTI; #endif goto last; } AR = MB; + SCAD = (AR >> 30) & 077; +#if KL + if (QKLB && t20_page && pc_sect != 0 && SCAD > 36) { /* Extended pointer */ + f = SCAD - 37; + if (SCAD == 077) + goto muuo; + SC = _byte_adj[f].s; + SCAD = (_byte_adj[f].p + (0777 ^ SC) + 1) & 0777; + f++; + if (SCAD & 0400) { + SCAD = ((0777 ^ SC) + 044 + 1) & 0777; + AR++; + for(f = 0; f < 28; f++) { + if (_byte_adj[f].s == SC && _byte_adj[f].p == SCAD) + break; + } + } + AR &= (SECTM|RMASK); + AR |= ((uint64)(f + 37)) << 30; + MB = AR; + if (Mem_write(0, 0)) + goto last; + goto ld_ptr; + } +#endif SC = (AR >> 24) & 077; - SCAD = (((AR >> 30) & 077) + (0777 ^ SC) + 1) & 0777; + SCAD = (SCAD + (0777 ^ SC) + 1) & 0777; if (SCAD & 0400) { - SC = ((0777 ^ ((AR >> 24) & 077)) + 044 + 1) & 0777; -#if KI | KL + SCAD = ((0777 ^ SC) + 044 + 1) & 0777; +#if KL + if (QKLB && t20_page && pc_sect != 0 && (AR & BIT12) != 0) { /* Full pointer */ + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + if (MB & SMASK) { + if (MB & BIT1) { + fault_data = 024LL << 30 | (((FLAGS & USER) != 0)?SMASK:0) | + (AB & RMASK) | ((uint64)cur_sect << 18); + page_fault = 1; + goto last; + } + MB = ((MB + 1) & RMASK) | (MB & LMASK); + } else + MB = ((MB + 1) & (SECTM|RMASK)) | (MB & ~(SECTM|RMASK)); + if (Mem_write(0,0)) + goto last; + AB = (AB - 1) & RMASK; + } else + AR = (AR & LMASK) | ((AR + 1) & RMASK); +#elif KI AR = (AR & LMASK) | ((AR + 1) & RMASK); #else AR = (AR + 1) & FMASK; #endif - } else - SC = SCAD; + } AR &= PMASK; - AR |= (uint64)(SC & 077) << 30; + AR |= (uint64)(SCAD & 077) << 30; MB = AR; +#if KL + if (Mem_write(0, 0)) +#elif KI + if (Mem_write(0, 1)) +#else if (Mem_write(0, !QITS)) +#endif goto last; if ((IR & 04) == 0) break; + modify = 0; goto ldb_ptr; } /* Fall through */ @@ -3717,42 +5983,86 @@ unasign: case 0135:/* LDB */ case 0137:/* DPB */ if ((FLAGS & BYTI) == 0 || !BYF5) { +#if KL + if (Mem_read(0, 0, 0)) +#elif KI + if (Mem_read(0, 1, 0)) +#else if (Mem_read(0, !QITS, 0)) +#endif goto last; AR = MB; + SC = (AR >> 24) & 077; + SCAD = (AR >> 30) & 077; +#if KL + if (QKLB && t20_page && pc_sect != 0 && SCAD > 36) { /* Extended pointer */ + f = SCAD - 37; + if (SCAD == 077) + goto muuo; + SC = _byte_adj[f].s; + SCAD = _byte_adj[f].p; +ld_ptr: + glb_sect = 1; + sect = (AR >> 18) & 07777; + FLAGS |= BYTI; + BYF5 = 1; + goto ld_exe; + } +#endif 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; +#if KL_ITS + if (QITS && one_p_arm) { + FLAGS |= ADRFLT; + one_p_arm = 0; + } +#endif FLAGS |= BYTI; BYF5 = 1; +#if KL + ptr_flg = 1; + if (QKLB && t20_page && (SC < 36) && + pc_sect != 0 && (glb_sect || cur_sect != 0) && + (AR & BIT12) != 0) { + /* Full pointer */ + AB = (AB + 1) & RMASK; + } else + glb_sect = 0; +#endif #if ITS if (QITS && pi_cycle == 0 && mem_prot == 0) { opc = PC | (FLAGS << 18); } #endif } else { - AB = AR & RMASK; +#if KL + ptr_flg = 0; +ld_exe: +#else if ((IR & 06) == 6) modify = 1; +#endif + AB = AR & RMASK; + MQ = (uint64)(1) << SC; + MQ -= 1; if (Mem_read(0, 0, 0)) goto last; AR = MB; if ((IR & 06) == 4) { - AR = AR >> SC; + AR = AR >> SCAD; AR &= MQ; set_reg(AC, AR); } else { BR = get_reg(AC); - BR = BR << SC; - MQ = MQ << SC; + BR = BR << SCAD; + MQ = MQ << SCAD; AR &= CM(MQ); AR |= BR & MQ; MB = AR & FMASK; - Mem_write(0, 0); + if (Mem_write(0, 0)) + goto last; } FLAGS &= ~BYTI; BYF5 = 0; @@ -3780,16 +6090,18 @@ ldb_ptr: case 0132:/* FSC */ SC = ((AB & RSIGN) ? 0400 : 0) | (AB & 0377); SCAD = GET_EXPO(AR); +#if KL + SC |= (SC & 0400) ? 0777000 : 0; + SCAD |= (SC & 0400) ? 0777000 : 0; + SC = SCAD + SC; +#else SC = (SCAD + SC) & 0777; +#endif flag1 = 0; if (AR & SMASK) flag1 = 1; -#if !PDP6 - SMEAR_SIGN(AR); - AR <<= 34; - goto fnorm; -#else +#if PDP6 if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) fxu_hold_set = 1; if ((SC & 0400) != 0 && !pi_cycle) { @@ -3805,6 +6117,10 @@ ldb_ptr: AR &= SMASK|MMASK; AR |= ((uint64)((SC) & 0377)) << 27; break; +#else + SMEAR_SIGN(AR); + AR <<= 34; + goto fnorm; #endif @@ -3860,6 +6176,9 @@ ufa: /* Get exponent */ SC = GET_EXPO(AR); +#if KL + SC |= (SC & 0400) ? 0777000 : 0; /* Extend sign */ +#endif /* Smear the signs */ SMEAR_SIGN(AR); SMEAR_SIGN(BR); @@ -3898,8 +6217,10 @@ fnorm: #if !PDP6 AR &= ~077; /* Save one extra bit */ #endif +#if !KL if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) fxu_hold_set = 1; +#endif if (IR != 0130) { /* !UFA */ fnormx: while (AR != 0 && ((AR & FPSBIT) != 0) == ((AR & FPNBIT) != 0) && @@ -3913,14 +6234,14 @@ fnormx: } /* Handle edge case of a - and overflow bit */ if ((AR & 000777777777600000000000LL) == (FPSBIT|FPNBIT)) { - SC += 1; + SC ++; 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 */ + /* FADR & FSBR do not round if negative and equal round */ /* FMPR does not round if result negative and equal round */ if (((IR & 070) != 070 && (AR & FPSBIT) != 0 && @@ -3952,6 +6273,13 @@ fnormx: AR = MQ = 0; SC = 0; } +#if KL + if (!pi_cycle && (SC & 0400) != 0) { + FLAGS |= OVR|FLTOVR|TRP1; + if ((SC & RSIGN) != 0) + FLAGS |= FLTUND; + } +#else if (((SC & 0400) != 0) && !pi_cycle) { FLAGS |= OVR|FLTOVR|TRP1; #if !PDP6 @@ -3962,6 +6290,7 @@ fnormx: #endif check_apr_irq(); } +#endif SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); AR &= SMASK|MMASK; AR |= ((uint64)(SCAD & 0377)) << 27; @@ -4008,7 +6337,11 @@ fnormx: SC = (((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777; SC += (((AR & SMASK) ? 0777 : 0) ^ (AR >> 27)) & 0777; SC += 0600; +#if KL + SC |= (SC & 0400) ? 0777000 : 0; /* Extend sign */ +#else SC &= 0777; +#endif /* Make positive and compute result sign */ flag1 = 0; flag3 = 0; @@ -4067,7 +6400,6 @@ fnormx: if (BR & SMASK) { BR = CM(BR) + 1; flag1 = 1; - flag3 = 1; } if (AR & SMASK) { if ((AR & MMASK) == 0) { @@ -4077,7 +6409,11 @@ fnormx: AR = CM(AR) + 1; flag1 = !flag1; } +#if KL + SC = (SC + ((0777 ^ SCAD) + 1) + 0201); +#else SC = (SC + ((0777 ^ SCAD) + 1) + 0201) & 0777; +#endif /* Clear exponents */ AR &= MMASK; BR &= MMASK; @@ -4094,6 +6430,32 @@ fnormx: AR = BR / AR; if (AR != 0) { #if !PDP6 +#if KL + if (flag1) { + AR = ((AR ^ FMASK) + 1) & FMASK; + } + AR = (AR >> 1) | (AR & SMASK); + if (IR & 04) { + AR++; + flag3 = AR & 1; + } + AR = (AR >> 1) | (AR & SMASK); + while (AR != 0 && ((AR & SMASK) != 0) == ((AR & BIT8) != 0) && + ((AR & BIT8) != 0) == ((AR & BIT9) != 0)) { + AR <<= 1; + AR |= flag3; + flag3 = 0; + SC--; + } + AR &= FMASK; + if ((SC & 01600) != 1600) + fxu_hold_set = 1; + if (AR == (SMASK|EXPO)) { + AR = (AR >> 1) | (AR & SMASK); + SC ++; + } + AR &= SMASK|MMASK; +#else if ((AR & BIT7) != 0) { AR >>= 1; } else { @@ -4109,6 +6471,7 @@ fnormx: AR <<= 1; SC--; } +#endif #else if (flag1) { AR = ((AR ^ FMASK) + 1) & FMASK; @@ -4152,7 +6515,7 @@ fnormx: } check_apr_irq(); } -#if !PDP6 +#if !PDP6 & !KL if (flag1) { AR = ((AR ^ MMASK) + 1) & MMASK; AR |= SMASK; @@ -4398,7 +6761,7 @@ left: case 0213: /* MOVNS */ flag1 = flag3 = 0; AD = CM(AR) + 1; - if ((((AR & CMASK) ^ CMASK) + 1) & SMASK) { + if ((CCM(AR) + 1) & SMASK) { #if !PDP6 FLAGS |= CRY1; #endif @@ -4505,7 +6868,7 @@ left: break; /* Done */ } -#if !PDP6 +#if !PDP6 if (AR == SMASK && BR == 1) { FLAGS |= OVR|NODIV; /* Overflow and No Divide */ sac_inh=1; /* Don't touch AC */ @@ -4654,8 +7017,8 @@ left: #if !PDP6 SC = 0; if (AR != 0) { -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -4768,9 +7131,15 @@ left: case 0251: /* BLT */ BR = AB; do { - AIO_CHECK_EVENT; /* queue async events */ + AIO_CHECK_EVENT; /* queue async events */ if (sim_interval <= 0) { - sim_process_event(); + if ((reason = sim_process_event()) != SCPE_OK) { + f_pc_inh = 1; + f_load_pc = 0; + f_inst_fetch = 0; + set_reg(AC, AR); + break; + } } /* Allow for interrupt */ if (pi_pending) { @@ -4784,7 +7153,13 @@ left: } } AB = (AR >> 18) & RMASK; +#if KL + BYF5 = 1; +#endif if (Mem_read(0, 0, 0)) { +#if KL + BYF5 = 0; +#endif #if ITS /* On ITS if access error, allow for skip */ if (QITS && (xct_flag & 04) != 0) @@ -4795,15 +7170,21 @@ left: AR = AOB(AR) & FMASK; #endif f_pc_inh = 1; -#if KA & ITS +#if KA | PDP6 +#if ITS if (QITS) set_reg(AC, AR); +#endif #else set_reg(AC, AR); #endif goto last; } AB = (AR & RMASK); +#if KL + BYF5 = 0; + set_reg(AC, AOB(AR)); +#endif if (Mem_write(0, 0)) { #if ITS /* On ITS if access error, allow for skip */ @@ -4815,9 +7196,11 @@ left: AR = AOB(AR) & FMASK; #endif f_pc_inh = 1; -#if KA & ITS +#if KA | PDP6 +#if ITS if (QITS) set_reg(AC, AR); +#endif #else set_reg(AC, AR); #endif @@ -4831,8 +7214,8 @@ left: case 0252: /* AOBJP */ AR = AOB(AR); if ((AR & SMASK) == 0) { -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -4845,8 +7228,8 @@ left: case 0253: /* AOBJN */ AR = AOB(AR); if ((AR & SMASK) != 0) { -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -4857,6 +7240,166 @@ left: break; case 0254: /* JRST */ /* AR Frm PC */ +#if KL +#if KL_ITS + if (uuo_cycle | pi_cycle) { + if (QITS && one_p_arm) { + FLAGS |= ADRFLT; + one_p_arm = 0; + } + } +#endif + switch (AC) { + case 000: /* JRST */ + break; + case 001: /* PORTAL */ + FLAGS &= ~(PUBLIC|PRV_PUB); + break; + case 005: /* XJRSTF */ +xjrstf: + if (Mem_read(0, 0, 0)) + goto last; + BR = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; /* Get PC. */ + if (QKLB && t20_page) { + pc_sect = (AR >> 18) & 07777; + if (AC != 07 && (FLAGS & USER) == 0 && ((BR >> 23) & USER) == 0) + prev_sect = BR & 037; + } + BR = BR >> 23; /* Move flags into position */ + goto jrstf; + + case 006: /* XJEN */ + case 012: /* JEN */ + /* Restore interrupt level. */ + if ((FLAGS & (USER|USERIO)) == USER || + (FLAGS & (USER|PUBLIC)) == PUBLIC) { + goto muuo; + } else { + pi_restore = 1; + } + if (AC == 06) + goto xjrstf; + /* Fall through */ + + case 002: /* JRSTF */ + BR = AR >> 23; /* Move into position */ +jrstf: +#if KL_ITS + if (QITS) + f = FLAGS & (TRP1|TRP2); +#endif + FLAGS &= ~(OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|TRP1|TRP2|PCHNG|ADRFLT); + /* If executive mode, copy USER and UIO */ + if ((FLAGS & (PUBLIC|USER)) == 0) + FLAGS |= BR & (USER|USERIO|PUBLIC); + /* Can always clear UIO */ + if ((BR & USERIO) == 0) + FLAGS &= ~USERIO; + FLAGS |= BR & (OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|\ + TRP1|TRP2|PUBLIC|PCHNG|ADRFLT); + FLAGS &= ~PRV_PUB; + if ((FLAGS & USER) == 0) { + FLAGS |= (BR & OVR) ? PRV_PUB : 0; + } +#if KL_ITS + if (QITS) + FLAGS |= f; +#endif + check_apr_irq(); + break; + + case 017: /* Invalid */ +#if KL_ITS + if (QITS) { + BR = AR >> 23; /* Move into position */ + pi_enable = 1; + goto jrstf; + } +#endif + goto muuo; + + case 007: /* XPCW */ + MB = (((uint64)FLAGS) << 23) & FMASK; + /* Save Previous Public context */ + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + if (QKLB && t20_page) + MB |= (uint64)(prev_sect & 037); + } + if (uuo_cycle | pi_cycle) { + FLAGS &= ~(USER|PUBLIC); /* Clear USER */ + sect = 0; /* Force section zero on IRQ */ + } + if (Mem_write(0, 0)) + goto last; + AB = (AB + 1) & RMASK; + if (QKLB && t20_page) + MB = (((((uint64)pc_sect) << 18) | PC) + !pi_cycle) & (SECTM|RMASK); + else + MB = (PC + !pi_cycle) & (RMASK); + if (Mem_write(0, 0)) + goto last; + AB = (AB + 1) & RMASK; + goto xjrstf; + + case 015: /* XJRST */ + if (Mem_read(0, 0, 0)) + goto last; + AR = MB; /* Get PC. */ + if (QKLB && t20_page) { + pc_sect = (AR >> 18) & 07777; + } + break; + + case 014: /* SFM */ + MB = (((uint64)FLAGS) << 23) & FMASK; + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + if (QKLB && t20_page) + MB |= (uint64)(prev_sect & 037); + } + (void)Mem_write(0, 0); + goto last; + + case 003: /* Invalid */ + case 011: /* Invalid */ + case 013: /* Invalid */ + case 016: /* Invalid */ + goto muuo; + + case 004: /* HALT */ + if ((FLAGS & (USER|USERIO)) == USER || + (FLAGS & (USER|PUBLIC)) == PUBLIC) { + goto muuo; + } else { + reason = STOP_HALT; + } + break; + case 010: /* JEN */ + /* Restore interrupt level. */ + if ((FLAGS & (USER|USERIO)) == USER || + (FLAGS & (USER|PUBLIC)) == PUBLIC) { + goto muuo; + } else { + pi_restore = 1; + } + break; + } +#if KL_ITS + if (QITS && (FLAGS & USER)) { + jpc = PC; + } +#endif + PC = AR & RMASK; + if (QKLB && t20_page && glb_sect) + pc_sect = (AR >> 18) & 07777; +#else if (uuo_cycle | pi_cycle) { FLAGS &= ~USER; /* Clear USER */ #if ITS @@ -4868,7 +7411,7 @@ left: } /* JEN */ if (AC & 010) { /* Restore interrupt level. */ -#if KI | KL +#if KI if ((FLAGS & (USER|USERIO)) == USER || (FLAGS & (USER|PUBLIC)) == PUBLIC) { #else @@ -4881,7 +7424,7 @@ left: } /* HALT */ if (AC & 04) { -#if KI | KL +#if KI if ((FLAGS & (USER|USERIO)) == USER || (FLAGS & (USER|PUBLIC)) == PUBLIC) { #else @@ -4893,7 +7436,7 @@ left: } } #if ITS - if ((FLAGS & USER) && QITS) { + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -4901,7 +7444,7 @@ left: PC_CHANGE /* JRSTF */ if (AC & 02) { - FLAGS &= ~(OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|TRP1|TRP2|PCHNG); + FLAGS &= ~(OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|TRP1|TRP2|PCHNG|ADRFLT); AR >>= 23; /* Move into position */ /* If executive mode, copy USER and UIO */ if ((FLAGS & (PUBLIC|USER)) == 0) @@ -4914,7 +7457,7 @@ left: user_io = (FLAGS & USERIO) != 0; #endif FLAGS |= AR & (OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|\ - TRP1|TRP2|PUBLIC|PCHNG); + TRP1|TRP2|PUBLIC|PCHNG|ADRFLT); #if ITS if (QITS) FLAGS |= AR & (PURE|ONEP); @@ -4928,19 +7471,20 @@ left: } if (AC & 01) { /* Enter User Mode */ -#if KI | KL +#if KI FLAGS &= ~(PUBLIC|PRV_PUB); #else FLAGS |= USER; #endif } +#endif f_pc_inh = 1; break; case 0255: /* JFCL */ if ((FLAGS >> 9) & AC) { -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -4971,20 +7515,93 @@ left: FLAGS |= ONEP; one_p_arm = 0; } +#endif +#if KL_ITS + if (QITS && one_p_arm) { + FLAGS |= ADRFLT; + one_p_arm = 0; + } #endif break; case 0257: /* MAP */ #if KI | KL f = AB >> 9; + flag1 = (FLAGS & USER) != 0; + flag3 = 0; +#if KL + /* Invalid in user unless USERIO set, or not in supervisor mode */ + if ((FLAGS & (USER|USERIO)) == USER || (FLAGS & (USER|PUBLIC)) == PUBLIC) + goto muuo; + + /* Figure out if this is a user space access */ + if (xct_flag & 4) { + flag1 = (FLAGS & USERIO) != 0; + sect = prev_sect; + } + + /* Check if Paging Enabled */ + if (!page_enable) { + AR = AB; /* direct map */ + if (flag1) /* U */ + AR |= SMASK; /* BIT0 */ + AR |= BIT2|BIT3|BIT4|BIT8; + set_reg(AC, AR); + break; + } + + /* Check if access to register */ + if (AB < 020 && ((QKLB && + (glb_sect == 0 || sect == 0 || (glb_sect && sect == 1))) || !QKLB)) { + AR = AB; /* direct map */ + if (flag1) /* U */ + AR |= SMASK; /* BIT0 */ + AR |= BIT2|BIT3|BIT4|BIT8; + set_reg(AC, AR); + break; + } + + /* Handle KI paging odditiy */ + if (!flag1 && !t20_page && (f & 0740) == 0340) { + /* Pages 340-377 via UBT */ + f += 01000 - 0340; + flag3 = 1; + } + + AR = load_tlb(flag1 | flag3, f, 0); + if (page_fault) { + page_fault = 0; + AR |= fault_data; + if (flag1) /* U */ + AR |= SMASK; + set_reg(AC, AR); + break; + } + BR = AR; + /* Remap the flag bits */ + if (BR & KL_PAG_A) { /* A */ + AR = ((AR & 017777LL) << 9) + (AB & 0777); + if (flag1) /* U */ + AR |= SMASK; /* BIT0 */ + AR |= BIT2; /* BIT2 */ + if (BR & KL_PAG_P) /* P */ + AR |= BIT6; /* BIT6 */ + if (BR & KL_PAG_W) /* W */ + AR |= BIT3; /* BIT3 */ + if (BR & KL_PAG_S) /* S */ + AR |= BIT4; /* BIT4 */ + if (BR & KL_PAG_C) /* C */ + AR |= BIT7; /* BIT7 */ + } else + AR = (f & 01740) ? 0 : 0377777LL; + AR |= BIT8; +#else /* 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) { @@ -4992,7 +7609,6 @@ left: } } - flag3 = 0; /* If user, check if small user enabled */ if (flag1) { if (small_user && (f & 0340) != 0) { @@ -5000,78 +7616,52 @@ left: 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; + + /* Get translation */ + AR = load_tlb(flag1, f); + if (AR == 0) { + AR = 0437777LL; } 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. */ } - if ((AR & 0400000LL) == 0) - AR &= 0437777LL; /* Return valid entry for page */ - AR ^= 0400000LL; /* Flip access status. */ +#endif set_reg(AC, AR); #endif break; /* Stack, JUMP */ case 0260: /* PUSHJ */ /* AR Frm PC */ +#if KL + if (QKLB && t20_page && pc_sect != 0) + MB = ((uint64)pc_sect << 18) + (PC + !pi_cycle); + else { +#endif MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + !pi_cycle) & RMASK); -#if KI +#if KI | KL if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + MB &= FMASK; + } +#if KL } #endif - BR = AB; +#endif +#if KL + BYF5 = 1; + f = glb_sect; + if (QKLB && t20_page && pc_sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { + AR = (AR + 1) & FMASK; + sect = (AR >> 18) & 07777; + glb_sect = 1; + } else { + sect = pc_sect; + glb_sect = 0; +#endif 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 @@ -5082,6 +7672,14 @@ left: check_apr_irq(); #endif } +#if KL + } +#endif + AB = AR & RMASK; + if (hst_lnt) + hst[hst_p].mb = MB; + if (Mem_write(uuo_cycle | pi_cycle, 0)) + goto last; #if !PDP6 if (uuo_cycle | pi_cycle) { FLAGS &= ~(USER|PUBLIC); /* Clear USER */ @@ -5093,10 +7691,14 @@ left: #endif } #endif -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } +#endif +#if KL + if (QKLB && t20_page && f) + pc_sect = cur_sect; #endif PC = BR & RMASK; PC_CHANGE @@ -5104,6 +7706,14 @@ left: break; case 0261: /* PUSH */ +#if KL + BYF5 = 1; + if (QKLB && t20_page &&pc_sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { + AR = (AR + 1) & FMASK; + sect = (AR >> 18) & 07777; + } else { + sect = pc_sect; +#endif AR = AOB(AR); AB = AR & RMASK; if (AR & C1) { @@ -5115,19 +7725,57 @@ left: check_apr_irq(); #endif } +#if KL + } +#endif MB = BR; + if (hst_lnt) + hst[hst_p].mb = MB; if (Mem_write(0, 0)) goto last; break; case 0262: /* POP */ +#if KL + BYF5 = 1; /* Tell PXCT that this is stack */ + flag1 = glb_sect; + glb_sect = 0; + sect = pc_sect; + if (QKLB && t20_page) { + if ((xct_flag & 1) != 0) + sect = prev_sect; + if (sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { + sect = (AR >> 18) & 07777; + glb_sect = 1; + } + } +#endif AB = AR & RMASK; if (Mem_read(0, 0, 0)) goto last; - AR = SOB(AR); + if (hst_lnt) + hst[hst_p].mb = MB; AB = BR & RMASK; +#if KL + BYF5 = 0; /* Now back to data */ + if (QKLB && t20_page) { + sect = cur_sect; + glb_sect = flag1; + } +#endif if (Mem_write(0, 0)) goto last; +#if KL + if (QKLB && t20_page) { + if ((xct_flag & 1) != 0) + sect = prev_sect; + if (sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { + AR = (AR - 1) & FMASK; + break; + } + } +#endif + AR = SOB(AR); if ((AR & C1) == 0) { #if KI | KL if (!pi_cycle) @@ -5141,16 +7789,46 @@ left: case 0263: /* POPJ */ AB = AR & RMASK; +#if KL + BYF5 = 1; /* Tell PXCT that this is stack */ + glb_sect = 0; + sect = pc_sect; + if (QKLB && t20_page && (xct_flag & 1) != 0) + sect = prev_sect; + if (QKLB && t20_page && sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { + sect = (AR >> 18) & 07777; + glb_sect = 1; + AR = (AR - 1) & FMASK; + } else +#endif + AR = SOB(AR); + if (Mem_read(0, 0, 0)) goto last; -#if ITS - if ((FLAGS & USER) && QITS) { + if (hst_lnt) { +#if KL + hst[hst_p].ea = AB | (sect << 18); +#else + hst[hst_p].ea = AB; +#endif + hst[hst_p].mb = MB; + } +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif + f_pc_inh = 1; PC_CHANGE PC = MB & RMASK; - AR = SOB(AR); +#if KL + BYF5 = 0; /* Tell PXCT that this is stack */ + if (QKLB && t20_page && pc_sect != 0) { + pc_sect = (MB >> 18) & 07777; + if ((AR & SMASK) == 0 && (AR & SECTM) != 0) + break; + } +#endif if ((AR & C1) == 0) { #if KI | KL if (!pi_cycle) @@ -5160,16 +7838,23 @@ left: check_apr_irq(); #endif } - f_pc_inh = 1; break; case 0264: /* JSR */ /* AR Frm PC */ +#if KL + if (QKLB && t20_page && pc_sect != 0) + MB = ((uint64)pc_sect << 18) + (PC + !pi_cycle); + else { +#endif MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + !pi_cycle) & RMASK); -#if KI +#if KI | KL if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; } +#if KL + } +#endif #endif #if PDP6 if (ill_op | uuo_cycle | pi_cycle | ex_uuo_sync) { @@ -5183,37 +7868,59 @@ left: if (Mem_write(0, 0)) goto last; FLAGS &= ~ (BYTI|ADRFLT|TRP1|TRP2); -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif PC_CHANGE +#if KL + if (QKLB && t20_page) { + AR = AR + 1; + if (AR & BIT17) + cur_sect++; + if (glb_sect) + pc_sect = cur_sect; + PC = AR & RMASK; + } else +#endif PC = (AR + 1) & RMASK; f_pc_inh = 1; break; case 0265: /* JSP */ /* AR Frm PC */ +#if KL + if (QKLB && t20_page && pc_sect != 0) + AD = ((uint64)pc_sect << 18) + (PC + !pi_cycle); + else { +#endif AD = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + !pi_cycle) & RMASK); FLAGS &= ~ (BYTI|ADRFLT|TRP1|TRP2); -#if KI +#if KI | KL if ((FLAGS & USER) == 0) { AD &= ~SMASK; AD |= (FLAGS & PRV_PUB) ? SMASK : 0; } +#if KL + } +#endif #endif #if !PDP6 if (uuo_cycle | pi_cycle) { FLAGS &= ~(USER|PUBLIC); /* Clear USER */ } #endif -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif PC_CHANGE +#if KL + if (QKLB && t20_page && glb_sect) + pc_sect = cur_sect; +#endif PC = AR & RMASK; AR = AD; f_pc_inh = 1; @@ -5226,12 +7933,16 @@ left: FLAGS &= ~(USER|PUBLIC); /* Clear USER */ } #endif -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif PC_CHANGE +#if KL + if (QKLB && t20_page && glb_sect) + pc_sect = cur_sect; +#endif PC = AR & RMASK; AR = BR; break; @@ -5242,8 +7953,8 @@ left: if (Mem_read(uuo_cycle | pi_cycle, 0, 0)) goto last; set_reg(AC, MB); -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -5279,7 +7990,7 @@ left: case 0276: /* SUBM */ case 0277: /* SUBB */ flag1 = flag3 = 0; - if ((((AR & CMASK) ^ CMASK) + (BR & CMASK) + 1) & SMASK) { + if ((CCM(AR) + (BR & CMASK) + 1) & SMASK) { FLAGS |= CRY1; flag1 = 1; } @@ -5397,8 +8108,8 @@ jump_op: f |= ((AD == 0) << 1); f = f & IR; if (((IR & 04) != 0) == (f == 0)) { -#if ITS - if ((FLAGS & USER) && QITS) { +#if ITS | KL_ITS + if (QITS && (FLAGS & USER)) { jpc = PC; } #endif @@ -5455,7 +8166,7 @@ skip_op: PC_CHANGE PC = (PC + 1) & RMASK; #if KI | KL - } else if (pi_cycle) { + } else if (trap_flag == 0 && pi_cycle) { pi_ov = pi_hold = 1; #endif } @@ -5483,8 +8194,17 @@ skip_op: AR = AR & CM(BR); /* ANDCA */ break; - case 0414: /* SETM */ case 0415: /* SETMI */ +#if KL + /* XMOVEI for extended addressing */ + if (QKLB && t20_page && pc_sect != 0) { + if (glb_sect == 0 && AR < 020) + AR |= BIT17; + else + AR |= ((uint64)cur_sect) << 18; + } +#endif + case 0414: /* SETM */ case 0416: /* SETMM */ case 0417: /* SETMB */ /* SETM */ @@ -5579,8 +8299,19 @@ skip_op: BR = SWAP_AR; /* Fall Through */ - case 0500: /* HLL */ case 0501: /* HLLI */ +#if KL + /* XHLLI for extended addressing */ + if (QKLB && t20_page && IR == 0501 && pc_sect != 0) { + if (glb_sect == 0 && AR < 020) + AR = BIT17; + else + AR = ((uint64)cur_sect) << 18; + } + /* Fall Through */ +#endif + + case 0500: /* HLL */ case 0502: /* HLLM */ case 0504: /* HRL */ case 0505: /* HRLI */ @@ -5770,7 +8501,7 @@ test_op: 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 KI | KL if (!pi_cycle && ((((FLAGS & (USER|USERIO)) == USER) && (IR & 040) == 0) || ((FLAGS & (USER|PUBLIC)) == PUBLIC))) { @@ -5783,10 +8514,41 @@ test_op: goto muuo; } else { int d = ((IR & 077) << 1) | ((AC & 010) != 0); +#if KL + if (d == 3) { + irq_flags |= 020; + goto last; + } +#endif fetch_opr: switch(AC & 07) { case 0: /* 00 BLKI */ case 2: /* 10 BLKO */ +#if KL + /* For KL10 special devices treat like DATAI/DATAO */ + if (d <= 05) { + if (AC & 02) { + if (d == 1 || d == 4) { + if (Mem_read(pi_cycle, 0, 0)) + goto last; + AR = MB; + } + dev_tab[d](040|DATAO|(d<<2), &AR); + } else { + dev_tab[d](040|DATAI|(d<<2), &AR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + if (d == 4 || d == 5) { + AB = (AB + 1) & RMASK; + MB = BR; + if (Mem_write(pi_cycle, 0)) + goto last; + } + } + break; + } +#endif if (Mem_read(pi_cycle, 0, 0)) goto last; AR = MB; @@ -5812,6 +8574,14 @@ fetch_opr: MB = AR; if (Mem_write(pi_cycle, 0)) goto last; +#if KL + if (d == 4 || d == 5) { /* DATAI TIM, MTR is two words */ + AB = (AB + 1) & RMASK; + MB = BR; + if (Mem_write(pi_cycle, 0)) + goto last; + } +#endif break; case 3: /* 14 DATAO */ if (Mem_read(pi_cycle, 0, 0)) @@ -5860,8 +8630,8 @@ fetch_opr: set_reg(AC+1, MQ); } - if (hst_lnt) { - hst[hst_p].fmb = AR; + if (hst_lnt && PC >= 020) { + hst[hst_p].fmb = (i_flags & SAC) ? AR: MB; } last: @@ -5874,9 +8644,82 @@ last: goto fetch; } #endif -#if KI | KL +#if KL /* Handle page fault and traps */ if (page_enable && page_fault) { + page_fault = 0; + BYF5 = 0; +#if KL_ITS + if (QITS) { + AB = eb_ptr | 0500; + FM[(6<<4)|0] = fault_data; + } else +#endif + AB = ub_ptr | 0500; + if (!QKLB && !QITS && t20_page) + AB++; + MB = fault_data; + Mem_write_nopage(); + AB++; + /* If fault on trap, kill the pi_cycle flag */ + if (trap_flag) + pi_cycle = 0; + FLAGS |= trap_flag & (TRP1|TRP2); + trap_flag = (TRP1|TRP2); + MB = (((uint64)(FLAGS) << 23) & LMASK); + if ((FLAGS & USER) == 0) { + MB &= ~SMASK; + MB |= (FLAGS & PRV_PUB) ? SMASK : 0; + } + if (QKLB && t20_page) { + if ((FLAGS & USER) == 0) + MB |= (uint64)(prev_sect & 037); + } else + MB |= (PC & RMASK); + Mem_write_nopage(); + AB++; + if (QKLB && t20_page) { + MB = (((uint64)pc_sect) << 18) | (PC & RMASK); + Mem_write_nopage(); + AB++; + } + flag1 = flag3 = 0; + if (FLAGS & PUBLIC) + flag3 = 1; + if (FLAGS & USER) + flag1 = 1; + Mem_read_nopage(); + if (QKLB && t20_page) + FLAGS = 0; + else + FLAGS = (MB >> 23) & 017777; + /* If transistioning from user to executive adjust flags */ + if ((FLAGS & USER) == 0) { + if (flag1) + FLAGS |= USERIO; + if (flag3) + FLAGS |= PRV_PUB; + } + PC = MB & RMASK; + if (QKLB && t20_page) + pc_sect = (MB >> 18) & 07777; + xct_flag = 0; + f_load_pc = 1; + f_pc_inh = 1; + if (pi_cycle) { + pi_cycle = 0; + irq_flags |= 01000; + FM[(7 << 4) | 2] = fault_data; + pi_enable = 0; + } + } +#endif +#if KI + /* Handle page fault and traps */ + if (page_enable && page_fault) { + if (pi_cycle) { + inout_fail = 1; + } page_fault = 0; AB = ub_ptr + ((FLAGS & USER) ? 0427 : 0426); MB = fault_data; @@ -5893,6 +8736,7 @@ last: #if KI | KL if (!f_pc_inh && (trap_flag == 0) && !pi_cycle) { + FLAGS &= ~ADRFLT; #else if (!f_pc_inh && !pi_cycle) { #endif @@ -5910,20 +8754,23 @@ last: /* Dismiss an interrupt */ if (pi_cycle) { #if KI | KL - if (page_enable && page_fault) { - page_fault = 0; - inout_fail = 1; - } + if (trap_flag != 0) { + pi_hold = pi_ov = 0; + f_pc_inh = 0; + trap_flag = 0; + } #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 KL + AB = pi_vect | pi_ov; +#else + AB = 040 | (pi_enc << 1) | maoff | pi_ov; +#endif #if KI | KL - AB |= eb_ptr; Mem_read_nopage(); #else Mem_read(1, 0, 1); @@ -5934,11 +8781,14 @@ last: if ((IR & 0700) == 0700) { (void)check_irq_level(); } - AB = 040 | (pi_enc << 1) | pi_ov | maoff; +#if KL + AB = pi_vect | pi_ov; +#else + AB = 040 | (pi_enc << 1) | maoff | pi_ov; +#endif pi_ov = 0; pi_hold = 0; #if KI | KL - AB |= eb_ptr; Mem_read_nopage(); #else Mem_read(1, 0, 1); @@ -5985,6 +8835,1157 @@ if (QITS) return reason; } +#if KL + +/* Handle indirection for extended byte instructions */ +int +do_byte_setup(int n, int wr, int *pos, int *sz) +{ + uint64 val1; + uint64 val2; + uint64 temp; + int s; + int p; + int np; + int ix; + int ind; + + /* Get pointer */ + val1 = get_reg(n+1); + val2 = get_reg(n+2); + /* Extract index */ + *sz = s = (val1 >> 24) & 077; + p = (val1 >> 30) & 077; + np = (p + (0777 ^ s) + 1) & 0777; + /* Advance pointer */ + if (QKLB && t20_page && pc_sect != 0) { + if (p > 36) { /* Extended pointer */ + int i = p - 37; + *sz = s = _byte_adj[i].s; + p = _byte_adj[i].p; + np = p = (p + (0777 ^ s) + 1) & 0777; + val2 = val1 & (SECTM|RMASK); /* Convert to long pointer */ + val1 = ((uint64)s << 24) | BIT12; + if (p & 0400) { + np = p = ((0777 ^ s) + 044 + 1) & 0777; + val2 = (val2 & ~(SECTM|RMASK)) | ((val2 + 1) & (SECTM|RMASK)); + } + ind = 0; + ix = 0; + MB = val2 & (SECTM|RMASK); + sect = (MB >> 18) & 07777; + glb_sect = 1; + } else if ((val1 & BIT12) != 0) { /* Full pointer */ + if (np & 0400) { + np = p = ((0777 ^ s) + 044 + 1) & 0777; + if (val2 & SMASK) + val2 = (val2 & LMASK) | ((val2 + 1) & RMASK); + else + val2 = (val2 & ~(SECTM|RMASK)) | ((val2 + 1) & (SECTM|RMASK)); + } + if (val2 & SMASK) { + if (val2 & BIT1) { + fault_data = 024LL << 30 | (((FLAGS & USER) != 0)?SMASK:0) | + (val2 & RMASK) | ((uint64)sect << 18); + page_fault = 1; + return 1; + } + ind = TST_IND(val2) != 0; + ix = GET_XR(val2); + MB = (val2 & RMASK) | ((val2 & RSIGN)? LMASK:0); + sect = cur_sect; + glb_sect = 0; + } else { + ind = (val2 & BIT1) != 0; + ix = (val2 >> 30) & 017; + MB = val2 & (SECTM|RMASK); + sect = (MB >> 18) & 07777; + glb_sect = 1; + } + } else { + if (np & 0400) { + np = p = ((0777 ^ s) + 044 + 1) & 0777; + val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); + } + ix = GET_XR(val1); + ind = TST_IND(val1) != 0; + MB = (val1 & RMASK) | ((val1 & RSIGN)? LMASK:0); + sect = cur_sect; + glb_sect = 0; + } + } else { + if (np & 0400) { + np = p = ((0777 ^ s) + 044 + 1) & 0777; + val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); + } + ix = GET_XR(val1); + ind = TST_IND(val1) != 0; + MB = (val1 & RMASK) | ((val1 & RSIGN)? LMASK:0); + sect = cur_sect; + glb_sect = 0; + } + *pos = np & 077; + + AB = MB & RMASK; + if (ix) { + temp = get_reg(ix); + /* Check if extended indexing */ + if (QKLB && t20_page && glb_sect != 0 && (temp & SMASK) == 0 && (temp & SECTM) != 0) { + temp = (temp + MB) & (SECTM|RMASK); + sect = (temp >> 18) & 07777; + MB = 0; + glb_sect = 1; + } else + glb_sect = 0; + temp = MB = (MB + temp) & FMASK; + AB = MB & RMASK; + } + while (ind & !check_irq_level()) { + if (Mem_read(0, 1, 0)) { + return 1; + } + /* Check if extended indexing */ + if (QKLB && sect != 0) { + if (MB & SMASK) { /* Instruction format IFIW */ + if (MB & BIT1) { /* Illegal index word */ + fault_data = 024LL << 30 | (((FLAGS & USER) != 0)?SMASK:0) | + (temp & RMASK) | ((uint64)sect << 18); + page_fault = 1; + return 1; + } + glb_sect = 0; + ix = GET_XR(MB); + ind = TST_IND(MB) != 0; + AB = MB & RMASK; + if (ix) { + temp = get_reg(ix); + /* Check if extended indexing */ + if ((temp & SMASK) != 0 || (temp & SECTM) == 0) { /* Local index word */ + temp = (temp + AB) & RMASK; + } else { + temp = (temp + AB) & FMASK; + glb_sect = 1; + sect = cur_sect = (temp >> 18) & 07777; + } + MB = temp; + } else + temp = MB; + AB = temp & RMASK; + } else { /* Extended index EFIW */ + ind = (MB & BIT1) != 0; + ix = (MB >> 30) & 017; + AB = MB & (SECTM|RMASK); + if (ix) { + temp = get_reg(ix); + if ((temp & SMASK) != 0 || (temp & SECTM) == 0) { /* Local index word */ + temp = AB + (((temp & RSIGN) ? 0: 0)|(temp & RMASK)); + } else + temp = temp + AB; + temp &= FMASK; + MB = temp; + } else + temp = MB; + sect = cur_sect = (temp >> 18) & 07777; + AB = temp & RMASK; + glb_sect = 1; + } + } else { + ix = GET_XR(MB); + ind = TST_IND(MB) != 0; + AB = MB & RMASK; + if (ix) { + temp = get_reg(ix); + /* Check if extended indexing */ + if (QKLB && sect != 0 && (temp & SMASK) == 0 && (temp & SECTM) != 0) { + temp = (temp + ((AB & RSIGN) ? + SECTM|((uint64)AB): (uint64)AB)) & (SECTM|RMASK); + sect = (temp >> 18) & 07777; + MB = 0; + glb_sect = 1; + AB = 0; + } else + glb_sect = 0; + temp = MB = (MB + temp) & FMASK; + AB = MB & RMASK; + } + } +#if 0 + /* Handle events during a indirect loop */ + if (sim_interval-- <= 0) { + if (sim_process_event() != SCPE_OK) { + return -1; + } + } +#endif + }; + /* Update pointer */ + val1 &= PMASK; + val1 |= (uint64)(np) << 30; + + /* Save pointer */ + set_reg(n+1, val1); + set_reg(n+2, val2); + + modify = wr; + /* Read final value */ + if (Mem_read(0, 0, 0)) { + modify = ptr_flg = BYF5 = 0; + return 1; + } + modify = 0; + return 0; +} + +/* Get data from pointer */ +int +load_byte(int n, uint64 *data, uint64 fill, int cnt) +{ + uint64 val1, msk; + int s, p; + + /* Check if should return fill */ + val1 = get_reg(n); + if (cnt && (val1 & MANT) == 0) { + *data = fill; + return 1; + } + + /* Fetch Pointer word */ + ptr_flg = 1; + if (do_byte_setup(n, 0, &p, &s)) + goto back; + + ptr_flg = 0; + /* Generate mask for given size */ + msk = (uint64)(1) << s; + msk--; + *data = (MB >> p) & msk; + if (cnt) { + /* Decrement count */ + val1 = get_reg(n); + val1--; + set_reg(n, val1); + } + + return 1; + +back: + ptr_flg = 0; + val1 = get_reg(n+1); + val1 &= PMASK; + val1 |= (uint64)(p + s) << 30; + set_reg(n+1, val1); + return 0; +} + +/* Store data into pointer */ +int +store_byte(int n, uint64 data, int cnt) +{ + uint64 val1, msk; + int s, p; + + /* Fetch Pointer word */ + BYF5 = 1; + if (do_byte_setup(n, 1, &p, &s)) + goto back; + + /* Generate mask for given size */ + msk = (uint64)(1) << s; + msk--; + msk <<= p; + MB &= CM(msk); + MB |= msk & ((uint64)(data) << p); + if (Mem_write(0, 0)) + goto back; + + BYF5 = 0; + if (cnt) { + /* Decrement count */ + val1 = get_reg(n); + val1--; + set_reg(n, val1); + } + + return 1; + +back: + BYF5 = 0; + val1 = get_reg(n+1); + val1 &= PMASK; + val1 |= (uint64)(p + s) << 30; + set_reg(n+1, val1); + return 0; +} + +void +get_mask(int n, uint64 *msk) +{ + uint64 val; + int s; + /* Get pointer */ + val = get_reg(n+1); + /* Extract index */ + s = (val >> 24) & 077; + + /* Generate mask for given size */ + *msk = ((uint64)(1) << s) - 1; +} + +/* Adjust a pointer to be valid */ +void +adj_byte(int n) +{ + uint64 val1, val2; + int s, p, np; + + /* Get pointer */ + val1 = get_reg(n+1); + val2 = get_reg(n+2); + /* Extract index */ + s = (val1 >> 24) & 077; + p = (val1 >> 30) & 077; + /* Advance pointer */ + np = (p + (0777 ^ s) + 1) & 0777; + if (QKLB && t20_page && pc_sect != 0) { + if (p > 36) { /* Extended pointer */ + int i = p - 37; + s = _byte_adj[i].s; + p = _byte_adj[i].p; + val2 = val1 & (SECTM|RMASK); /* Convert to long pointer */ + val1 = ((uint64)s << 24) | BIT12; + /* Save pointer */ + set_reg(n+1, val1); + set_reg(n+2, val2); + return; + } else if ((val1 & BIT12) != 0) { /* Full pointer */ + if (np & 0400) + val2 = (val2 & ~(SECTM|RMASK)) | ((val2 + 1) & (SECTM|RMASK)); + } else { + if (np & 0400) + val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); + } + } else { + if (np & 0400) + val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); + } + if ((np & 0400) == 0) + return; + /* Update pointer */ + val1 &= PMASK; + val1 |= (uint64)(044) << 30; + + /* Save pointer */ + set_reg(n+1, val1); + set_reg(n+2, val2); +} + + +/* Advance a pointer by 1 */ +void +adv_byte(int n) +{ + uint64 val1, val2; + int s, p, np; + + /* Check if should return fill */ + val1 = get_reg(n); + if ((val1 & MANT) == 0) + return; + /* Decrement count */ + val1--; + set_reg(n, val1); + + /* Get pointer */ + val1 = get_reg(n+1); + val2 = get_reg(n+2); + /* Extract index */ + s = (val1 >> 24) & 077; + p = (val1 >> 30) & 077; + /* Advance pointer */ + np = (p + (0777 ^ s) + 1) & 0777; + if (QKLB && t20_page && pc_sect != 0) { + if (p > 36) { /* Extended pointer */ + int i = p - 37; + s = _byte_adj[i].s; + p = _byte_adj[i].p; + np = (p + (0777 ^ s) + 1) & 0777; + val2 = val1 & (SECTM|RMASK); /* Convert to long pointer */ + val1 = ((uint64)s << 24) | BIT12; + if (np & 0400) { + np = ((0777 ^ s) + 044 + 1) & 0777; + val2 = (val2 & ~(SECTM|RMASK)) | ((val2 + 1) & (SECTM|RMASK)); + } + } else if ((val1 & BIT12) != 0) { /* Full pointer */ + if (np & 0400) { + np = ((0777 ^ s) + 044 + 1) & 0777; + val2 = (val2 & ~(SECTM|RMASK)) | ((val2 + 1) & (SECTM|RMASK)); + } + } else { + if (np & 0400) { + np = ((0777 ^ s) + 044 + 1) & 0777; + val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); + } + } + } else { + if (np & 0400) { + np = ((0777 ^ s) + 044 + 1) & 0777; + val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); + } + } + np &= 077; + /* Update pointer */ + val1 &= PMASK; + val1 |= (uint64)(np) << 30; + + /* Save pointer */ + set_reg(n+1, val1); + set_reg(n+2, val2); +} + +/* back a pointer by 1 */ +void +bak_byte(int n, int cnt) +{ + uint64 val; + int s, p; + + /* Increment count */ + if (cnt) { + val = get_reg(n); + val++; + set_reg(n, val); + } + + /* Get pointer */ + val = get_reg(n+1); + /* Extract index */ + s = (val >> 24) & 077; + p = (((val >> 30) & 077) + (s)) & 0777; + /* Advance pointer */ + /* Update pointer */ + val &= PMASK; + val |= (uint64)(p) << 30; + MB = val; + + /* Save pointer */ + set_reg(n+1, val); +} + +/* Preform a table lookup operation */ +int +do_xlate(uint32 tbl, uint64 val, int mask) +{ + uint64 reg; + int f; + + AB = (tbl + (val >> 1)) & RMASK; + if (Mem_read(0, 0, 0)) { + /* Backup ext_ac */ + return -2; + } + if ((val & 1) == 0) + MB >>= 18; + val = MB & mask; + reg = get_reg(ext_ac); + f = 1; + switch ((MB >> 15) & 07) { + case 0: + if ((reg & SMASK) == 0) /* If S */ + f = 0; + break; + case 1: f = -1; /* Terminate */ + break; + case 2: + if ((reg & SMASK) == 0) /* If S, clear M */ + f = 0; + reg &= ~BIT2; + break; + case 3: + if ((reg & SMASK) == 0) /* If S, set M */ + f = 0; + reg |= BIT2; + break; + case 4: + reg |= SMASK|BIT1; /* Set S & N */ + break; + case 5: + f = -1; /* Terminate, set N */ + reg |= BIT1; + break; + case 6: + reg |= SMASK|BIT1; /* Set S, N, Clear M */ + reg &= ~BIT2; + break; + case 7: + reg |= SMASK|BIT1|BIT2; /* Set S, N, M */ + break; + } + set_reg(ext_ac, reg); + return f; +} + +/* Table of powers of 10 for CVTBD opcodes */ +uint64 pow10_tab[22][2] = { + /* 0: */ 0000000000000LL, 0000000000001LL, + /* 1: */ 0000000000000LL, 0000000000012LL, + /* 2: */ 0000000000000LL, 0000000000144LL, + /* 3: */ 0000000000000LL, 0000000001750LL, + /* 4: */ 0000000000000LL, 0000000023420LL, + /* 5: */ 0000000000000LL, 0000000303240LL, + /* 6: */ 0000000000000LL, 0000003641100LL, + /* 7: */ 0000000000000LL, 0000046113200LL, + /* 8: */ 0000000000000LL, 0000575360400LL, + /* 9: */ 0000000000000LL, 0007346545000LL, + /* 10: */ 0000000000000LL, 0112402762000LL, + /* 11: */ 0000000000002LL, 0351035564000LL, + /* 12: */ 0000000000035LL, 0032451210000LL, + /* 13: */ 0000000000443LL, 0011634520000LL, + /* 14: */ 0000000005536LL, 0142036440000LL, + /* 15: */ 0000000070657LL, 0324461500000LL, + /* 16: */ 0000001070336LL, 0115760200000LL, + /* 17: */ 0000013064257LL, 0013542400000LL, + /* 18: */ 0000157013326LL, 0164731000000LL, + /* 19: */ 0002126162140LL, 0221172000000LL, + /* 20: */ 0025536165705LL, 0254304000000LL, + /* 21: */ 0330656232670LL, 0273650000000LL +}; + +/* + * Process extended instruction. + * + * On entry BR = address of instruction. + * AB = value of E0. + * IR = opcode. + */ +int +do_extend(uint32 ia) +{ + uint64 fill1, fill2; + uint64 val1, val2; + uint64 msk; + uint64 reg; + int xlat_sect; + int f, i; + + + switch(IR) { + case 001: /* CMPSL */ + case 002: /* CMPSE */ + case 003: /* CMPSLE */ + case 005: /* CMPSGE */ + case 006: /* CMPSN */ + case 007: /* CMPSG */ + if (((get_reg(ext_ac) | get_reg(ext_ac+3)) & EMASK) != 0) + return 1; + /* Fetch filler values */ + AB = (ia + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill1 = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill2 = MB; + + /* Compare the strings */ + f = 2; + while (((get_reg(ext_ac) | get_reg(ext_ac+3)) & MANT) != 0) { + if (!load_byte(ext_ac, &val1, fill1, 1)) { + return 0; + } + if (!load_byte(ext_ac+3, &val2, fill2, 1)) { + /* Backup ext_ac */ + bak_byte(ext_ac, 1); + return 0; + } + if (val1 != val2) { + f = (val1 < val2) ? 1: 0; + break; + } + } + /* Check if we should skip */ + switch (IR & 7) { + case 1: f = (f == 1); break; + case 2: f = (f == 2); break; + case 3: f = (f != 0); break; + case 5: f = (f != 1); break; + case 6: f = (f != 2); break; + case 7: f = (f == 0); break; + default: f = 0; break; + } + /* Skip if conditions match */ + if (f) + PC = (PC + 1) & RMASK; + return 0; + + case 004: /* EDIT */ + val2 = MB; /* Save address of translate table */ + if (QKLB && pc_sect != 0 && glb_sect) + xlat_sect = (val2 >> 18) & 07777; + else + xlat_sect = cur_sect; + /* Fetch filler values */ + AB = (ia + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill1 = MB; + /* Get floating character */ + AB = (AB + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill2 = MB; + f = 1; + while (f) { + int a; + + /* Read in pattern control */ + reg = get_reg(ext_ac); + AB = reg & RMASK; + if (QKLB && pc_sect != 0) { + sect = (reg >> 18) & 07777; + glb_sect = 1; + } else { + sect = cur_sect; + glb_sect = 0; + } + if (Mem_read(0, 0, 0)) + return 0; + i = (reg >> 30) & 03; + reg &= ~(3LL << 30); /* Clear byte number */ + val1 = (MB >> ((3 - i) * 9)) & 0777; + i++; + if (i > 3) { + if (QKLB && pc_sect != 0) + reg = (reg & ~(SECTM|RMASK)) | ((reg + 1) & (SECTM|RMASK)); + else + reg = (reg & LMASK) | ((reg+1) & RMASK); + i = 0; + } + reg |= ((uint64)i) << 30; + i = 0; + a = 0; + switch ((val1 >> 6) & 07) { + case 0: /* Individual options */ + switch (val1 & 077) { + case 0: /* Stop */ + f = 0; + break; + case 1: /* SELECT */ + if (!load_byte(ext_ac, &val1, 0, 0)) + return 0; + a = 1; + AB = (val2 + (val1 >> 1)) & RMASK; + sect = xlat_sect; + if (Mem_read(0, 0, 0)) + return 0; + if ((val1 & 1) == 0) + MB >>= 18; + val1 = MB & 07777; + switch ((MB >> 15) & 07) { + case 0: + func0: + if ((reg & SMASK) != 0) { /* If S */ + i = 1; + } else if (fill1 != 0) { + val1 = fill1; + i = 1; + } + break; + case 1: + set_reg(ext_ac, reg); + return 0; /* Next */ + case 2: + reg &= ~BIT2; /* If S, clear M */ + goto func0; + case 3: + reg |= BIT2; /* If S, set M */ + goto func0; + case 4: + func4: + if ((reg & SMASK) == 0) { + adj_byte(ext_ac+3); + reg |= SMASK; + AR = get_reg(ext_ac+3); + if (QKLB && pc_sect != 0) { + sect = (AR >> 18) & 07777; + glb_sect = 1; + } else { + sect = cur_sect; + glb_sect = 0; + } + AB = AR & RMASK; + MB = get_reg(ext_ac+4); + if (Mem_write(0, 0)) + return 0; + if (QKLB && pc_sect != 0 && (MB & BIT12) != 0) { + AB = (++AR) & RMASK; + sect = (AR >> 18) & 07777; + MB = get_reg(ext_ac+5); + if (Mem_write(0,0)) + return 0; + } + if (fill2 != 0) { + if (!store_byte(ext_ac+3, fill1, 0)) { + return 0; + } + } + } + i = 1; + reg |= SMASK|BIT1; /* Set S & N */ + break; + case 5: + reg |= BIT1; + break; + case 6: + reg &= ~BIT2; /* Clear M */ + goto func4; + case 7: + reg |= BIT2; /* Set M */ + goto func4; + } + break; + case 2: /* Set signifigance */ + if ((reg & SMASK) == 0) { + AR = get_reg(ext_ac+3); + if (QKLB && pc_sect != 0) { + sect = (AR >> 18) & 07777; + glb_sect = 1; + } else { + sect = cur_sect; + glb_sect = 0; + } + AB = AR & RMASK; + MB = get_reg(ext_ac+4); + if (Mem_write(0, 0)) + return 0; + if (QKLB && pc_sect != 0 && (MB & BIT12) != 0) { + AB = (++AR) & RMASK; + sect = (AR >> 18) & 07777; + MB = get_reg(ext_ac+5); + if (Mem_write(0,0)) + return 0; + } + if (fill2 != 0) { + val1 = fill2; + i = 1; + } + } + reg |= SMASK; + break; + case 3: /* Field separater */ + reg &= ~(SMASK|BIT1|BIT2); /* Clear S & N */ + break; + case 4: /* Exchange Mark */ + AR = get_reg(ext_ac+3); + if (QKLB && pc_sect != 0) { + sect = (AR >> 18) & 07777; + glb_sect = 1; + } else { + sect = cur_sect; + glb_sect = 0; + } + AB = AR & RMASK; + if (Mem_read(0, 0, 0)) + return 0; + BR = MB; + MB = get_reg(ext_ac+4); + /* Make sure byte pointers are same size */ + if (QKLB && (MB & BIT12) != (BR & BIT12)) + return 0; + if (Mem_write(0, 0)) + return 0; + if (QKLB && pc_sect != 0 && (BR & BIT12) != 0) { + AB = (AR + 1) & RMASK; + sect = ((AR + 1)>> 18) & 07777; + if (Mem_read(0, 0, 0)) { + AB = AR & RMASK; /* Restore lower pointer */ + sect = (AR >> 18) & 07777; + MB = BR; + (void)Mem_write(0, 0); + return 0; + } + AD = MB; + MB = get_reg(ext_ac+5); + if (Mem_write(0, 0)) { + AB = AR & RMASK; /* Restore lower pointer */ + sect = (AR >> 18) & 07777; + MB = BR; + (void)Mem_write(0, 0); + return 0; + } + set_reg(ext_ac+5, AD); + } + set_reg(ext_ac+4, BR); + break; + case 5: + i = 0; + break; + } + break; + case 1: /* Insert Message char */ + if ((reg & SMASK) != 0) { + AB = (ia + (val1 & 077) + 1) & RMASK; + sect = cur_sect; + if (Mem_read(0, 0, 0)) + return 0; + i = 1; + val1 = MB; + } else if (fill1 != 0) { + i = 1; + val1 = fill1; + } + break; + case 5: /* Skip on M */ + if ((reg & BIT2) != 0) + goto skipa; + break; + case 6: /* Skip on N */ + if ((reg & BIT1) == 0) + break; + case 7: /* Skip allways */ + skipa: + /* Compute new byte number */ + val1 = (val1 & 077) + 1; + val2 = ((reg >> 30) & 03) + val1; + reg &= ~(3LL << 30); /* Clear byte number */ + reg += (val2 >> 2); + reg |= (val2 & 3) << 30; + i = 0; + default: + break; + } + if (i) { + if (!store_byte(ext_ac+3, val1, 0)) { + if (a) + bak_byte(ext_ac, 0); + return 0; + } + } + set_reg(ext_ac, reg); + } + PC = (PC + 1) & RMASK; + break; + + case 010: /* CVTDBO */ + case 011: /* CVTDBT */ + if (QKLB && pc_sect != 0 && glb_sect) + xlat_sect = (AR >> 18) & 07777; + else + xlat_sect = cur_sect; + val2 = ((AR & RSIGN) ? LMASK : 0) | (AR & RMASK); + /* Check if conversion started */ + if ((get_reg(ext_ac) & SMASK) == 0) { + set_reg(ext_ac+3, 0); + set_reg(ext_ac+4, 0); + } + AR = get_reg(ext_ac + 3); + ARX = get_reg(ext_ac + 4); + if (IR == 010) { + fill2 = get_reg(ext_ac); + fill2 |= SMASK; + set_reg(ext_ac, fill2); + } + while ((get_reg(ext_ac) & MANT) != 0) { + if (!load_byte(ext_ac, &val1, 0, 1)) { + set_reg(ext_ac+3, AR); + set_reg(ext_ac+4, ARX); + return 0; + } + if (IR == 010) { + val1 = (val1 + val2) & FMASK; + } else { + sect = xlat_sect; + f = do_xlate((uint32)(val2 & RMASK), val1, 017); + if (f < 0) + break; + if (f) + val1 = MB & 017; + } + if ((val1 & RSIGN) != 0 || val1 > 9) { + ARX = (ARX & CMASK) | (AR & SMASK); + set_reg(ext_ac+3, AR); + set_reg(ext_ac+4, ARX); + return 0; + } + /* Multiply by 2 */ + AR <<= 1; + ARX <<= 1; + if (ARX & SMASK) + AR |= 1; + ARX &= CMASK; + /* Compute times 4 */ + BR = (AR << 2) | ((ARX >> 33) & 03); + BRX = (ARX << 2) & CMASK; + ARX = (ARX & CMASK) + (BRX & CMASK) + val1; + f = (ARX >> 35); + AR = AR + BR + f; + ARX &= CMASK; + AR &= FMASK; + } + ARX &= CMASK; + if ((get_reg(ext_ac) & MANT) == 0) { + PC = (PC + 1) & RMASK; + if (get_reg(ext_ac) & BIT2) { + ARX = CCM(ARX) + 1; + AR = CM(AR) + ((ARX & SMASK) != 0); + } + } + ARX = (ARX & CMASK) | (AR & SMASK); + AR &= FMASK; + set_reg(ext_ac+3, AR); + set_reg(ext_ac+4, ARX); + break; + case 012: /* CVTBDO */ + case 013: /* CVTBDT */ + /* Save E1 */ + if (IR == 012) + val2 = ((AR & RSIGN) ? LMASK : 0) | (AR & RMASK); + else { + val2 = AB; + if (QKLB && pc_sect != 0 && glb_sect) + xlat_sect = (AR >> 18) & 07777; + else + xlat_sect = cur_sect; + } + /* Get fill */ + AB = (ia + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill1 = MB; + AR = get_reg(ext_ac); + ARX = get_reg(ext_ac + 1); + reg = get_reg(ext_ac + 3); + /* Set M bit if minus */ + if ((AR & SMASK) != 0 && (reg & BIT2) == 0) { + reg |= BIT2; + ARX = CCM(ARX) + 1; + AR = CM(AR) + ((ARX & SMASK) != 0); + } + ARX &= CMASK; + /* Set N bit if non-zero number */ + if ((AR | ARX) != 0) + reg |= BIT1; + set_reg(ext_ac+3, reg); + /* Compute number of digits needed for value */ + for (f = 0; f < 22; f++) { + BRX = ARX + CCM(pow10_tab[f][1]) + 1; + BR = AR + CM(pow10_tab[f][0]) + ((BRX & SMASK) != 0); + if ((BR & C1) == 0) + break; + } + if (f == 0) + f = 1; + /* Check if room to save it */ + if (f > (reg & MANT)) + return 0; + /* Fill out left justify */ + /* If L, fill leading zeros with fill char */ + while ((reg & SMASK) != 0 && (reg & MANT) > f) { + if (!store_byte(ext_ac + 3, fill1, 1)) + return 0; + reg = get_reg(ext_ac + 3); + } + /* Insert correct digit */ + for (f--; f >= 0; f--) { + /* Subtract closest power of 10 */ + for (i = 0; i < 10; i++) { + BRX = ARX + CCM(pow10_tab[f][1]) + 1; + BR = AR + CM(pow10_tab[f][0]) + ((BRX & SMASK) != 0); + if ((BR & C1) == 0) + break; + ARX = BRX & CMASK; + AR = BR & FMASK; + } + val1 = (uint64)i; + if (IR == 013) { + /* Read first translation entry */ + AB = (val1 + val2) & RMASK; + sect = xlat_sect; + if (Mem_read(0, 0, 0)) { + set_reg(ext_ac + 3, (reg & (SMASK|EXPO)) | (f+1)); + return 0; + } + val1 = MB; + if (f == 0 && (get_reg(ext_ac + 3) & BIT2) != 0) + val1 >>= 12; + val1 &= 07777; + } else + val1 += val2; + if (!store_byte(ext_ac + 3, val1, 1)) { + set_reg(ext_ac + 3, (reg & (SMASK|EXPO)) | (f+1)); + return 0; + } + set_reg(ext_ac, AR); + set_reg(ext_ac+1, ARX); + } + reg = get_reg(ext_ac+3); + reg &= SMASK|EXPO; + set_reg(ext_ac+3, reg); + set_reg(ext_ac, 0); + set_reg(ext_ac+1, 0); + PC = (PC + 1) & RMASK; + break; + case 014: /* MOVSO */ + case 015: /* MOVST */ + case 016: /* MOVSLJ */ + get_mask(ext_ac+3, &msk); + if ((((get_reg(ext_ac) & (077LL << 26))| get_reg(ext_ac+3)) & EMASK) != 0) + return 1; + if (IR == 014) { + val2 = ((AR & RSIGN) ? LMASK : 0) | (AR & RMASK); + } else if (IR == 015) { + AB = ia; + if (QKLB) { + if (pc_sect != 0 && glb_sect) + xlat_sect = (AR >> 18) & 07777; + else + xlat_sect = cur_sect; + } else + xlat_sect = 0; + if (Mem_read(0, 1, 0)) + return 0; + val2 = MB; + } else { + val2 = AB; + } + /* Fetch filler values */ + AB = (ia + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill1 = MB; + while ((get_reg(ext_ac) & MANT) != 0) { + if ((get_reg(ext_ac+3) & MANT) == 0) + return 0; + if (!load_byte(ext_ac, &val1, fill1, 1)) + return 0; + if (IR == 014) { + val1 = (val1 + val2) & FMASK; + /* Check if in range */ + if ((val1 & ~msk) != 0) + return 0; + } else if (IR == 015) { + sect = xlat_sect; + f = do_xlate((uint32)(val2), val1, 07777); + if (f < 0) + return 0; + if (f) + val1 = MB & 07777; + } + if (!store_byte(ext_ac+3, val1, 1)) { + bak_byte(ext_ac, 1); + return 0; + } + } + while ((get_reg(ext_ac+3) & MANT) != 0) { + if (!store_byte(ext_ac+3, fill1, 1)) + return 0; + } + PC = (PC + 1) & RMASK; + break; + + case 017: /* MOVSRJ */ + /* Fetch filler values */ + if (((get_reg(ext_ac) | get_reg(ext_ac+3)) & EMASK) != 0) + return 1; + AB = (ia + 1) & RMASK; + if (Mem_read(0, 1, 0)) + return 0; + fill1 = MB; + /* While source is larger, skip source */ + val2 = get_reg(ext_ac+3); + while (val2 != 0 && get_reg(ext_ac) > val2) + adv_byte(ext_ac); + + /* While destination is larger, fill destination */ + while (val2 != 0 && get_reg(ext_ac) < val2) { + if (!store_byte(ext_ac+3, fill1, 1)) { + return 0; + } + val2 = get_reg(ext_ac+3); + } + /* Copy rest of string */ + while (get_reg(ext_ac+3)) { + if (!load_byte(ext_ac, &val1, fill1, 1)) + return 0; + if (!store_byte(ext_ac+3, val1, 1)) { + /* Backup ext_ac */ + bak_byte(ext_ac, 1); + return 0; + } + } + PC = (PC + 1) & RMASK; + break; + + case 020: /* XBLT */ + if (QKLB) { + glb_sect = 1; + reg = get_reg(ext_ac); + val1 = get_reg(ext_ac + 1); + val2 = get_reg(ext_ac + 2); + while (reg != 0) { + if (reg & SMASK) { + val1 = (val1 - 1) & (SECTM|RMASK); + sect = (val1 >> 18) & 00037; + AB = val1 & RMASK; + ptr_flg = 1; + if (Mem_read(0, 0, 0)) { + val1 = (val1 + 1) & (SECTM|RMASK); + goto xblt_done; + } + val2 = (val2 - 1) & (SECTM|RMASK); + sect = (val2 >> 18) & 00037; + AB = val2 & RMASK; + ptr_flg = 0; + BYF5 = 1; + if (Mem_write(0, 0)) { + val1 = (val1 + 1) & (SECTM|RMASK); + val2 = (val2 + 1) & (SECTM|RMASK); + goto xblt_done; + } + BYF5 = 0; + reg = (reg + 1) & FMASK; + } else { + sect = (val1 >> 18) & 00037; + AB = val1 & RMASK; + ptr_flg = 1; + if (Mem_read(0, 0, 0)) + goto xblt_done; + sect = (val2 >> 18) & 00037; + AB = val2 & RMASK; + ptr_flg = 0; + BYF5 = 1; + if (Mem_write(0, 0)) + goto xblt_done; + val1 = (val1 + 1) & (SECTM|RMASK); + val2 = (val2 + 1) & (SECTM|RMASK); + reg = (reg - 1) & FMASK; + BYF5 = 0; + } + } +xblt_done: + ptr_flg = BYF5 = 0; + set_reg(ext_ac, reg); + set_reg(ext_ac + 1, val1); + set_reg(ext_ac + 2, val2); + return 0; + } + case 021: /* GSNGL */ + case 022: /* GDBLE */ + case 023: /* GDFIX */ + case 024: /* GFIX */ + case 025: /* GDFIXR */ + case 026: /* GFIXR */ + case 027: /* DGFLTR */ + case 030: /* GFLTR */ + case 031: /* GFSC */ + default: + return 1; + } + return 0; +} +#endif + t_stat rtc_srv(UNIT * uptr) { @@ -5992,11 +9993,17 @@ rtc_srv(UNIT * uptr) t = sim_rtcn_calb (rtc_tps, TMR_RTC); sim_activate_after(uptr, 1000000/rtc_tps); tmxr_poll = t/2; +#if PDP6 | KA | KI clk_flg = 1; if (clk_en) { sim_debug(DEBUG_CONO, &cpu_dev, "CONO timmer\n"); set_interrupt(4, clk_irq); } +#endif +#if KL + update_times(rtc_tim); + rtc_tim = (1000000/rtc_tps); +#endif return SCPE_OK; } @@ -6012,10 +10019,29 @@ qua_srv(UNIT * uptr) } #endif +#if KL +t_stat +tim_srv(UNIT * uptr) +{ + double us; + + /* See if we are counting to overflow or period */ + if (tim_val & 0100000) { + tim_val = 020000 | tim_per; + us = (double)((010000 - tim_per) * 10); + } else { + tim_val = 0130000; + us = (double)(tim_per * 10); + } + set_interrupt(4 << 2, mtr_irq); + (void)sim_activate_after_d(uptr, us); + return SCPE_OK; +} +#endif /* * This sequence of instructions is a mix that hopefully - * represents a resonable instruction set that is a close + * represents a resonable instruction set that is a close * estimate to the normal calibrated result. */ @@ -6031,6 +10057,7 @@ static const char *pdp10_clock_precalibrate_commands[] = { t_stat cpu_reset (DEVICE *dptr) { int i; +sim_debug(DEBUG_CONO, dptr, "CPU reset\n"); BYF5 = uuo_cycle = 0; #if KA | PDP6 Pl = Ph = 01777; @@ -6048,10 +10075,16 @@ 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; +FLAGS = 0; #if KI | KL ub_ptr = eb_ptr = 0; pag_reload = ac_stack = 0; +#if KI fm_sel = small_user = user_addr_cmp = page_enable = 0; +#else +fm_sel = prev_ctx = user_addr_cmp = page_enable = t20_page = 0; +sect = cur_sect = pc_sect = 0; +#endif #endif #if BBN exec_map = 0; @@ -6069,6 +10102,8 @@ mpx_enable = 0; #ifdef PANDA_LIGHTS ka10_lights_init (); #endif +sim_vm_interval_units = "cycles"; +sim_vm_step_unit = "instruction"; return SCPE_OK; } @@ -6081,14 +10116,34 @@ if (vptr == NULL) if (ea < 020) *vptr = FM[ea] & FMASK; else { +#if KL | KI if (sw & SWMASK ('V')) { - if (ea >= MAXMEMSIZE) - return SCPE_REL; + int uf = ((sw & SWMASK('U')) != 0); + int page = ea >> 9; + uint32 tlb; +#if KL + if (!uf && !t20_page && (page & 0740) == 0340) { +#else + if (!uf && (page & 0740) == 0340) { +#endif + /* Pages 340-377 via UBT */ + page += 01000 - 0340; + uf = 1; } + if (uf) + tlb = u_tlb[page]; + else + tlb = e_tlb[page]; + if ((tlb & RSIGN) == 0) + return 4; + ea = ((tlb & 017777) << 9) + (ea & 0777); + } +#endif + if (ea >= MEMSIZE) return SCPE_NXM; *vptr = M[ea] & FMASK; - } +} return SCPE_OK; } @@ -6099,10 +10154,29 @@ t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) if (ea < 020) FM[ea] = val & FMASK; else { +#if KL | KI if (sw & SWMASK ('V')) { - if (ea >= MAXMEMSIZE) - return SCPE_REL; + int uf = ((sw & SWMASK('U')) != 0); + int page = ea >> 9; + uint32 tlb; +#if KL + if (!uf && !t20_page && (page & 0740) == 0340) { +#else + if (!uf && (page & 0740) == 0340) { +#endif + /* Pages 340-377 via UBT */ + page += 01000 - 0340; + uf = 1; } + if (uf) + tlb = u_tlb[page]; + else + tlb = e_tlb[page]; + if ((tlb & RSIGN) == 0) + return 4; + ea = ((tlb & 017777) << 9) + (ea & 0777); + } +#endif if (ea >= MEMSIZE) return SCPE_NXM; M[ea] = val & FMASK; @@ -6139,6 +10213,8 @@ t_bool build_dev_tab (void) DEVICE *dptr; DIB *dibp; uint32 i, j, d; +int rh20; +int rh_idx; /* Set trap offset based on MAOFF flag */ maoff = (cpu_unit[0].flags & UNIT_MAOFF)? 0100 : 0; @@ -6180,32 +10256,58 @@ dev_tab[0] = &dev_apr; dev_tab[1] = &dev_pi; #if KI | KL dev_tab[2] = &dev_pag; +#if KL +dev_tab[3] = &dev_cca; +dev_tab[4] = &dev_tim; +dev_irqv[4] = &tim_irq; +dev_tab[5] = &dev_mtr; +#endif #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++) { + +/* Assign all RH10 & RH20 devices */ +rh20 = 0540; +rh_idx = 0; +for (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; + d = dibp->dev_num; /* Check type */ + if (d & RH10_DEV) { /* Skip RH10 devices */ + d = rh_nums[rh_idx]; + if (d == 0) { + sim_printf ("To many RH10 devices %s\n", sim_dname (dptr)); + return TRUE; + } +#if KL + } else if (d & RH20_DEV) { /* RH20, grab next device */ +#if NUM_DEVS_NIA > 0 + /* If NIA20 installed, skip this slot */ + if ((nia_dev.flags & DEV_DIS) == 0 && dptr != &nia_dev && + rh20 == (((DIB *)nia_dev.ctxt)->dev_num & 0777)) + rh20 += 4; + /* If NIA20, then assign it to it's requested address */ + if ((nia_dev.flags & DEV_DIS) == 0 && dptr == &nia_dev) + d = dibp->dev_num & 0777; + else +#endif + d = rh20; + rh20 += 4; +#endif + } dev_tab[(d >> 2)] = dibp->io; dev_irqv[(d >> 2)] = dibp->irq; - rh[j].dev = dptr; - j++; + rh[rh_idx].dev_num = d; + rh[rh_idx].dev = dptr; + rh[rh_idx].rh = dibp->rh; + dibp->rh->devnum = d; + rh_idx++; } } -/* 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; @@ -6213,7 +10315,7 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { 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 */ + if (d & (RH10_DEV|RH20_DEV)) /* Skip RH10 & RH20 devices */ continue; if (dev_tab[(d >> 2) + j] != &null_dev) { /* already filled? */ @@ -6242,10 +10344,14 @@ if (cptr == NULL) { apr_serial = -1; return SCPE_OK; } +#if KI lnt = (int32) get_uint (cptr, 10, 001777, &r); +#else +lnt = (int32) get_uint (cptr, 10, 007777, &r); +#endif if ((r != SCPE_OK) || (lnt <= 0)) return SCPE_ARG; -apr_serial = lnt & 01777; +apr_serial = lnt; return SCPE_OK; } @@ -6312,14 +10418,24 @@ 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"); +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); +#if KL + if (QKLB) + fprintf(st, "%08o ", h->pc & 0777777777); + else +#endif + fprintf (st, "%06o ", h->pc & 0777777); fprint_val (st, h->ac, 8, 36, PV_RZRO); fputs (" ", st); - fprintf (st, "%06o ", h->ea); +#if KL + if (QKLB) + fprintf(st, "%08o ", h->ea & 0777777777); + else +#endif + fprintf (st, "%06o ", h->ea); fputs (" ", st); fprint_val (st, h->mb, 8, 36, PV_RZRO); fputs (" ", st); @@ -6327,10 +10443,14 @@ for (k = 0; k < lnt; k++) { /* print specified */ fputs (" ", st); #if KI | KL fprintf (st, "%c%06o ", ((h->flags & (PRV_PUB << 5))? 'p':' '), h->flags & 0777777); + fprintf (st, "%02o ", h->prev_sect); #else fprintf (st, "%06o ", h->flags); #endif - if ((h->pc & HIST_PC2) == 0) { + if ((h->pc & HIST_PCE) != 0) { + sim_eval = h->ir; + fprint_val (st, sim_eval, 8, 36, PV_RZRO); + } else if ((h->pc & HIST_PC2) == 0) { sim_eval = h->ir; fprint_val (st, sim_eval, 8, 36, PV_RZRO); fputs (" ", st); @@ -6361,7 +10481,7 @@ const char * cpu_description (DEVICE *dptr) { #if KL - return "KL10A CPU"; + return "KL10 CPU"; #endif #if KI return "KI10 CPU"; diff --git a/PDP10/kx10_cr.c b/PDP10/kx10_cr.c index 66a9820f..de1d3620 100644 --- a/PDP10/kx10_cr.c +++ b/PDP10/kx10_cr.c @@ -1,6 +1,6 @@ -/* ka10_cr.c: PDP10 Card reader. +/* kx10_cr.c: PDP10 Card reader. - Copyright (c) 2016-2017, Richard Cornwell + Copyright (c) 2016-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP10/kx10_cty.c b/PDP10/kx10_cty.c index eb06805b..c79af5c6 100644 --- a/PDP10/kx10_cty.c +++ b/PDP10/kx10_cty.c @@ -1,6 +1,6 @@ -/* ka10_cty.c: KA-10 front end (console terminal) simulator +/* kx10_cty.c: PDP6, KA-10 and KI-10 front end (console terminal) simulator - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2013-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -74,8 +74,13 @@ MTAB cty_mod[] = { { 0 } }; +REG cty_reg[] = { + { HRDATAD (WRU, sim_int_char, 8, "interrupt character") }, + { 0 } + }; + DEVICE cty_dev = { - "CTY", cty_unit, NULL, cty_mod, + "CTY", cty_unit, cty_reg, cty_mod, 2, 10, 31, 1, 8, 8, NULL, NULL, &cty_reset, NULL, NULL, NULL, &cty_dib, DEV_DEBUG, 0, dev_debug, @@ -136,10 +141,10 @@ t_stat ctyo_svc (UNIT *uptr) 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 */ + 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; @@ -154,6 +159,8 @@ t_stat ctyi_svc (UNIT *uptr) sim_clock_coschedule (uptr, tmxr_poll); /* continue poll */ + if (uptr->STATUS & KEY_RDY) + return SCPE_OK; if ((ch = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return ch; if (ch & SCPE_BREAK) /* ignore break */ diff --git a/PDP10/kx10_dc.c b/PDP10/kx10_dc.c index f54ce793..3adfe3db 100644 --- a/PDP10/kx10_dc.c +++ b/PDP10/kx10_dc.c @@ -1,6 +1,6 @@ -/* ka10_dc.c: PDP-10 DC10 communication server simulator +/* kx10_dc.c: PDP-10 DC10 communication server simulator - Copyright (c) 2011-2017, Richard Cornwell + Copyright (c) 2011-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index 13c453cb..e7fadd3a 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -1,6 +1,6 @@ -/* ka10_defs.h: PDP-10 simulator definitions +/* kx10_defs.h: PDP-10 simulator definitions - Copyright (c) 2011-2017, Richard Cornwell + Copyright (c) 2011-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -46,16 +46,12 @@ #define KI 0 #endif -#ifndef KLA -#define KLA 0 +#ifndef KL +#define KL 0 #endif -#ifndef KLB -#define KLB 0 -#endif - -#ifndef KL /* Either KL10A or KL10B */ -#define KL (KLA+KLB) +#if KL +#define EPT440 0 /* Force KL10 to use as 440 section address */ #endif #if (PDP6 + KA + KI + KL) != 1 @@ -81,10 +77,19 @@ #define WAITS KA #endif +/* Support for ITS on KL */ +#ifndef KL_ITS +#define KL_ITS KL +#endif + #ifndef PDP6_DEV /* Include PDP6 devices */ #define PDP6_DEV PDP6|WAITS #endif +#ifndef MAGIC_SWITCH /* Infamous MIT magic switch. */ +#define MAGIC_SWITCH 0 +#endif + /* MPX interrupt multiplexer for ITS systems */ #define MPX_DEV ITS @@ -131,6 +136,10 @@ typedef t_uint64 uint64; #define STOP_HALT 1 /* halted */ #define STOP_IBKPT 2 /* breakpoint */ +#define STOP_ACCESS 3 /* invalid access */ +#if MAGIC_SWITCH +#define STOP_MAGIC 4 /* low on magic */ +#endif /* Debuging controls */ #define DEBUG_CMD 0x0000001 /* Show device commands */ @@ -161,12 +170,19 @@ extern DEBTAB crd_debug[]; #define XMASK 03777777777777LL #define EMASK 00777000000000LL #define MMASK 00000777777777LL +#define SECTM 00007777000000LL #define BIT1 00200000000000LL +#define BIT2 00100000000000LL +#define BIT3 00040000000000LL +#define BIT4 00020000000000LL +#define BIT5 00010000000000LL +#define BIT6 00004000000000LL #define BIT7 00002000000000LL #define BIT8 00001000000000LL #define BIT9 00000400000000LL #define BIT10 00000200000000LL #define BIT10_35 00000377777777LL +#define BIT12 00000040000000LL #define BIT17 00000001000000LL #define MANT 00000777777777LL #define EXPO 00377000000000LL @@ -181,6 +197,7 @@ extern DEBTAB crd_debug[]; #define FPRBIT1 00000000000200000000000LL #define CM(x) (FMASK ^ (x)) +#define CCM(x) ((CMASK ^ (x)) & CMASK) #define INST_V_OP 27 /* opcode */ #define INST_M_OP 0777 @@ -205,6 +222,8 @@ extern DEBTAB crd_debug[]; #define LRZ(x) (((x) >> 18) & RMASK) #define JRST1 (((uint64)OP_JRST << 27) + 1) +#define OP_PORTAL(x) (((x) & 00777740000000LL) == 0254040000000LL) + #if PDP6 #define NODIV 000000 #define FLTUND 000000 @@ -275,6 +294,12 @@ extern DEBTAB crd_debug[]; #define AMASK 00000017777777LL #define WMASK 0037777LL #define CSHIFT 22 +#if KL +#define RH20_WMASK 003777LL +#define RH20_XFER SMASK +#define RH20_HALT BIT1 +#define RH20_REV BIT2 +#endif #else #define AMASK RMASK #define WMASK RMASK @@ -285,11 +310,19 @@ extern DEBTAB crd_debug[]; #define PI_ENABLE 0000000010 /* Clear DONE */ #define BUSY 0000000020 /* STOP */ #define CCW_COMP 0000000040 /* Write Final CCW */ +/* RH10 / RH20 interrupt */ +#define IADR_ATTN 0000000000040LL /* Interrupt on attention */ +#define IARD_RAE 0000000000100LL /* Interrupt on register access error */ +#define CCW_COMP_1 0000000040000LL /* Control word written. */ #if KI #define DEF_SERIAL 514 /* Default DEC test machine */ #endif +#if KL +#define DEF_SERIAL 1025 /* Default DEC test machine */ +#endif + #if BBN #define BBN_PAGE 0000017777777LL #define BBN_TRPPG 0000017000000LL @@ -306,13 +339,36 @@ extern DEBTAB crd_debug[]; #define BBN_MERGE 0161740000000LL #endif +#if KL +/* KL10 TLB paging bits */ +#define KL_PAG_A 0400000 /* Access */ +#define KL_PAG_P 0200000 /* Public */ +#define KL_PAG_W 0100000 /* Writable (M Tops 20) */ +#define KL_PAG_S 0040000 /* Software (W Writable Tops 20) */ +#define KL_PAG_C 0020000 /* Cacheable */ +#endif + +#if KI +/* KI10 TLB paging bits */ +#define KI_PAG_A 0400000 /* Access */ +#define KI_PAG_P 0200000 /* Public */ +#define KI_PAG_W 0100000 /* Writable */ +#define KI_PAG_S 0040000 /* Software */ +#define KI_PAG_X 0020000 /* Reserved */ +#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) +#if KL +#define UNIT_KL10B (1 << UNIT_V_PAGE) +#define UNIT_TWOSEG (0) +#else #define UNIT_TWOSEG (1 << UNIT_V_PAGE) +#endif #define UNIT_ITSPAGE (2 << UNIT_V_PAGE) #define UNIT_BBNPAGE (4 << UNIT_V_PAGE) #define UNIT_M_PAGE (007 << UNIT_V_PAGE) @@ -322,7 +378,29 @@ extern DEBTAB crd_debug[]; #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 */ +#define CNTRL_V_RH (UNIT_V_UF + 4) +#define CNTRL_M_RH 7 +#define GET_CNTRL_RH(x) (((x) >> CNTRL_V_RH) & CNTRL_M_RH) +#define CNTRL_RH(x) (((x) & CNTRL_M_RH) << CNTRL_V_RH) +#define DEV_V_RH (DEV_V_UF + 1) /* Type RH20 */ +#define DEV_M_RH (1 << DEV_V_RH) +#define TYPE_RH10 (0 << DEV_V_RH) +#define TYPE_RH20 (1 << DEV_V_RH) +#if KL +/* DTE memory access functions, n = DTE# */ +extern int Mem_examine_word(int n, int wrd, uint64 *data); +extern int Mem_deposit_word(int n, int wrd, uint64 *data); +extern int Mem_read_byte(int n, uint16 *data, int byte); +extern int Mem_write_byte(int n, uint16 *data); +#endif + +/* + * Access main memory. Returns 0 if access ok, 1 if out of memory range. + * On KI10 and KL10, optional EPT flag indicates address relative to ept. + */ +extern int Mem_read_word(t_addr addr, uint64 *data, int ept); +extern int Mem_write_word(t_addr addr, uint64 *data, int ept); #if MPX_DEV extern void set_interrupt_mpx(int dev, int lvl, int mpx); @@ -337,9 +415,15 @@ 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; +#if KL +extern DEVICE dte_dev; +extern DEVICE lp20_dev; +extern DEVICE tty_dev; +extern DEVICE nia_dev; +#else extern DEVICE cty_dev; +#endif +extern DEVICE cpu_dev; extern DEVICE mt_dev; extern DEVICE dpa_dev; extern DEVICE dpb_dev; @@ -365,6 +449,7 @@ extern DEVICE pmp_dev; extern DEVICE dk_dev; extern DEVICE pd_dev; extern DEVICE dpy_dev; +extern DEVICE iii_dev; extern DEVICE imx_dev; extern DEVICE imp_dev; extern DEVICE ch10_dev; @@ -374,8 +459,10 @@ extern DEVICE mty_dev; extern DEVICE ten11_dev; extern DEVICE dkb_dev; extern DEVICE auxcpu_dev; +extern DEVICE slave_dev; extern DEVICE dpk_dev; extern DEVICE wcnsls_dev; /* MIT Spacewar Consoles */ +extern DEVICE ocnsls_dev; /* Old MIT Spacewar Consoles */ extern DEVICE ai_dev; extern DEVICE dct_dev; /* PDP6 devices. */ extern DEVICE dtc_dev; @@ -387,41 +474,67 @@ extern t_stat (*dev_tab[128])(uint32 dev, t_uint64 *data); #define VEC_DEVMAX 8 /* max device vec */ +/* DF10 Interface */ +struct df10 { + uint32 status; /* DF10 status word */ + uint32 cia; /* Initial transfer address */ + uint32 ccw; /* Next control word address */ + uint32 wcr; /* CUrrent word count */ + uint32 cda; /* Current transfer address */ + uint32 devnum; /* Device number */ + t_uint64 buf; /* Data buffer */ + uint8 nxmerr; /* Bit to set for NXM */ + uint8 ccw_comp; /* Have we written out CCW */ +} ; + +/* RH10/RH20 Interface */ +struct rh_if { + void (*dev_write)(DEVICE *dptr, struct rh_if *rh, int reg, uint32 data); + uint32 (*dev_read)(DEVICE *dptr, struct rh_if *rh, int reg); + void (*dev_reset)(DEVICE *dptr); + t_uint64 buf; /* Data buffer */ + uint32 status; /* DF10 status word */ + uint32 cia; /* Initial transfer address */ + uint32 ccw; /* Current word count */ + uint32 wcr; + uint32 cda; /* Current transfer address */ + uint32 devnum; /* Device number */ + int ivect; /* Interrupt vector */ + uint8 imode; /* Mode of vector */ + int cop; /* RH20 Channel operator */ + uint32 sbar; /* RH20 Starting address */ + uint32 stcr; /* RH20 Count */ + uint32 pbar; + uint32 ptcr; + int reg; /* Last register selected */ + int drive; /* Last drive selected */ + int rae; /* Access register error */ + int attn; /* Attention bits */ + int xfer_drive; /* Current transfering drive */ +}; + /* 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); + t_addr (*irq)(uint32 dev, t_addr addr); + struct rh_if *rh; }; - + #define RH10_DEV 01000 +#define RH20_DEV 02000 struct rh_dev { uint32 dev_num; DEVICE *dev; + struct rh_if *rh; }; - 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_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); @@ -432,8 +545,27 @@ 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); +/* Define RH10/RH20 functions */ +t_stat rh_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat rh_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat rh_devio(uint32 dev, t_uint64 *data); +t_addr rh_devirq(uint32 dev, t_addr addr); +#if KL +void rh20_setup(struct rh_if *rhc); +#endif +void rh_setup(struct rh_if *rh, uint32 addr); +void rh_setattn(struct rh_if *rh, int unit); +void rh_error(struct rh_if *rh); +int rh_blkend(struct rh_if *rh); +void rh_setirq(struct rh_if *rh); +void rh_writecw(struct rh_if *rh, int nxm); +void rh_finish_op(struct rh_if *rh, int flags); +int rh_read(struct rh_if *rh); +int rh_write(struct rh_if *rh); + + +int ten11_read (t_addr addr, t_uint64 *data); +int ten11_write (t_addr addr, t_uint64 data); /* Console lights. */ extern void ka10_lights_init (void); @@ -441,36 +573,57 @@ 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 +#if KL +#define NUM_DEVS_PT 0 +#define NUM_DEVS_CR 0 +#define NUM_DEVS_CP 0 +#else #define NUM_DEVS_PT 1 #define NUM_DEVS_CR 1 #define NUM_DEVS_CP 1 +#endif #define NUM_DEVS_DPY USE_DISPLAY #define NUM_DEVS_WCNSLS USE_DISPLAY +#define NUM_DEVS_OCNSLS 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 +#define NUM_DEVS_SLAVE PDP6 #endif #if !PDP6 #define NUM_DEVS_DC 1 #define NUM_DEVS_MT 1 +#if KL +#define NUM_DEVS_RC 0 +#define NUM_DEVS_DT 0 +#define NUM_DEVS_DK 0 +#define NUM_DEVS_DP 0 +#define NUM_DEVS_LP20 1 +#define NUM_DEVS_TTY 1 +#define NUM_LINES_TTY 40 +#define NUM_DEVS_NIA 1 +#else #define NUM_DEVS_RC 1 #define NUM_DEVS_DT 1 #define NUM_DEVS_DK 1 #define NUM_DEVS_DP 2 +#define NUM_DEVS_LP20 0 +#define NUM_DEVS_TTY 0 +#define NUM_DEVS_NIA 0 +#endif #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_DKB (WAITS * USE_DISPLAY) +#define NUM_DEVS_III (WAITS * USE_DISPLAY) +#define NUM_DEVS_PD ITS | KL_ITS #define NUM_DEVS_IMX ITS #define NUM_DEVS_STK ITS #define NUM_DEVS_TK10 ITS @@ -478,10 +631,14 @@ int auxcpu_write (int addr, t_uint64); #define NUM_DEVS_TEN11 ITS #define NUM_DEVS_AUXCPU ITS #define NUM_DEVS_IMP 1 -#define NUM_DEVS_CH10 ITS +#define NUM_DEVS_CH10 ITS | KL_ITS #define NUM_DEVS_DPK ITS #define NUM_DEVS_AI ITS #endif +#if MAGIC_SWITCH && !KA && !ITS +#error "Magic switch only valid on KA10 with ITS mods" +#endif + /* Global data */ @@ -492,4 +649,16 @@ extern t_uint64 FM[]; extern uint32 PC; extern uint32 FLAGS; +#if NUM_DEVS_AUXCPU +extern t_addr auxcpu_base; +int auxcpu_read (t_addr addr, uint64 *); +int auxcpu_write (t_addr addr, uint64); +extern UNIT auxcpu_unit[]; +#endif +#if NUM_DEVS_SLAVE +//int slave_read (t_addr addr); +//int slave_write (t_addr addr, uint64); +//extern UNIT slave_unit[]; +#endif + #endif diff --git a/PDP10/kx10_df.c b/PDP10/kx10_df.c index ceeb46ea..a796d12f 100644 --- a/PDP10/kx10_df.c +++ b/PDP10/kx10_df.c @@ -1,6 +1,6 @@ -/* ka10_df.c: DF10 common routines. +/* kx10_df.c: DF10 common routines. - Copyright (c) 2015-2017, Richard Cornwell + Copyright (c) 2015-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -23,18 +23,24 @@ #include "kx10_defs.h" + +/* Set an IRQ for a DF10 device */ void df10_setirq(struct df10 *df) { df->status |= PI_ENABLE; set_interrupt(df->devnum, df->status); } +/* Generate the DF10 complete word */ void df10_writecw(struct df10 *df) { + uint64 wrd; 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); + wrd = ((uint64)(df->ccw & WMASK) << CSHIFT) | ((uint64)(df->cda) & AMASK); + (void)Mem_write_word(df->cia|1, &wrd, 0); } +/* Finish off a DF10 transfer */ void df10_finish_op(struct df10 *df, int flags) { df->status &= ~BUSY; df->status |= flags; @@ -42,6 +48,7 @@ void df10_finish_op(struct df10 *df, int flags) { df10_setirq(df); } +/* Setup for a DF10 transfer */ void df10_setup(struct df10 *df, uint32 addr) { df->cia = addr & ICWA; df->ccw = df->cia; @@ -50,24 +57,23 @@ void df10_setup(struct df10 *df, uint32 addr) { df->status &= ~(1 << df->ccw_comp); } +/* Fetch the next IO control word */ int df10_fetch(struct df10 *df) { uint64 data; - if (df->ccw > MEMSIZE) { + if (Mem_read_word(df->ccw, &data, 0)) { 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) { + if (Mem_read_word(df->ccw, &data, 0)) { df10_finish_op(df, 1<nxmerr); return 0; } - data = M[df->ccw]; } #if KA & ITS if (cpu_unit[0].flags & UNIT_ITSPAGE) { @@ -84,6 +90,7 @@ int df10_fetch(struct df10 *df) { return 1; } +/* Read next word */ int df10_read(struct df10 *df) { uint64 data; if (df->wcr == 0) { @@ -102,7 +109,10 @@ int df10_read(struct df10 *df) { else #endif df->cda = (uint32)((df->cda + 1) & AMASK); - data = M[df->cda]; + if (Mem_read_word(df->cda, &data, 0)) { + df10_finish_op(df, 1<nxmerr); + return 0; + } } else { data = 0; } @@ -113,6 +123,7 @@ int df10_read(struct df10 *df) { return 1; } +/* Write next word */ int df10_write(struct df10 *df) { if (df->wcr == 0) { if (!df10_fetch(df)) @@ -130,7 +141,10 @@ int df10_write(struct df10 *df) { else #endif df->cda = (uint32)((df->cda + 1) & AMASK); - M[df->cda] = df->buf; + if (Mem_write_word(df->cda, &df->buf, 0)) { + df10_finish_op(df, 1<nxmerr); + return 0; + } } if (df->wcr == 0) { return df10_fetch(df); diff --git a/PDP10/kx10_disk.c b/PDP10/kx10_disk.c new file mode 100644 index 00000000..0d404057 --- /dev/null +++ b/PDP10/kx10_disk.c @@ -0,0 +1,289 @@ +/* kx10_disk.c: Disk translator. + + Copyright (c) 2020, 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" +#include "kx10_disk.h" + +/* + * SIMH format is number words per sector stored as a 64 bit word. + * + * DBD9 format is: 9 character per pair of words. + * + * 0 - B0 1 2 3 4 5 6 7 + * 0 - 8 9 10 11 12 13 14 15 + * 0 - 16 17 18 19 20 21 22 23 + * 0 - 24 25 26 27 28 29 30 31 + * 0 - 32 33 34 35 B0 1 2 3 + * 1 - 4 5 6 7 8 9 10 11 + * 1 - 12 13 14 15 16 17 18 19 + * 1 - 20 21 22 23 24 25 26 27 + * 1 - 28 29 30 31 32 33 34 35 + * + * + * DLD9 format is: 9 character per pair of words. + * + * 0 - 28 29 30 31 32 33 34 35 + * 0 - 20 21 22 23 24 25 26 27 + * 0 - 12 13 14 15 16 17 18 19 + * 0 - 4 5 6 7 8 9 10 11 + * 0 - 32 33 34 35 B0 1 2 3 + * 1 - 24 25 26 27 28 29 30 31 + * 1 - 16 17 18 19 20 21 22 23 + * 1 - 8 9 10 11 12 13 14 15 + * 1 - B0 1 2 3 4 5 6 7 + */ + + +struct disk_formats { + uint32 mode; + const char *name; +}; + +static struct disk_formats fmts[] = { + {SIMH, "SIMH"}, + {DBD9, "DBD9"}, + {DLD9, "DLD9"}, + {0, 0}, +}; + +t_stat +disk_read(UNIT *uptr, uint64 *buffer, int sector, int wps) +{ + int da; + int wc; + int bc; + int wp; + uint64 temp; + uint8 conv_buff[2048]; + switch(GET_FMT(uptr->flags)) { + case SIMH: + da = sector * wps; + (void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET); + wc = sim_fread (buffer, sizeof(uint64), wps, uptr->fileref); + while (wc < wps) + buffer[wc++] = 0; + break; + case DBD9: + bc = (wps / 2) * 9; + da = sector * bc; + (void)sim_fseek(uptr->fileref, da, SEEK_SET); + wc = sim_fread (&conv_buff, 1, bc, uptr->fileref); + while (wc < bc) + conv_buff[wc++] = 0; + for (wp = wc = 0; wp < wps;) { + temp = ((uint64)conv_buff[wc++]) << 28; + temp |= ((uint64)conv_buff[wc++]) << 20; + temp |= ((uint64)conv_buff[wc++]) << 12; + temp |= ((uint64)conv_buff[wc++]) << 4; + temp |= ((uint64)conv_buff[wc]) >> 4; + buffer[wp++] = temp; + temp = ((uint64)conv_buff[wc++] & 0xf) << 32; + temp |= ((uint64)conv_buff[wc++]) << 24; + temp |= ((uint64)conv_buff[wc++]) << 16; + temp |= ((uint64)conv_buff[wc++]) << 8; + temp |= ((uint64)conv_buff[wc++]); + buffer[wp++] = temp; + } + break; + + case DLD9: + bc = (wps / 2) * 9; + da = sector * bc; + (void)sim_fseek(uptr->fileref, da, SEEK_SET); + wc = sim_fread (&conv_buff, 1, bc, uptr->fileref); + while (wc < bc) + conv_buff[wc++] = 0; + for (wp = wc = 0; wp < wps;) { + temp = ((uint64)conv_buff[wc++]); + temp |= ((uint64)conv_buff[wc++]) << 8; + temp |= ((uint64)conv_buff[wc++]) << 16; + temp |= ((uint64)conv_buff[wc++]) << 24; + temp |= ((uint64)conv_buff[wc] & 0xf) << 32; + buffer[wp++] = temp; + temp = ((uint64)conv_buff[wc++] & 0xf0) >> 4; + temp |= ((uint64)conv_buff[wc++]) << 4; + temp |= ((uint64)conv_buff[wc++]) << 12; + temp |= ((uint64)conv_buff[wc++]) << 20; + temp |= ((uint64)conv_buff[wc++]) << 28; + buffer[wp++] = temp; + } + break; + } + return SCPE_OK; +} + +t_stat +disk_write(UNIT *uptr, uint64 *buffer, int sector, int wps) +{ + int da; + int wc; + int bc; + int wp; + uint64 temp; + uint8 conv_buff[2048]; + switch(GET_FMT(uptr->flags)) { + case SIMH: + da = sector * wps; + (void)sim_fseek(uptr->fileref, da * sizeof(uint64), SEEK_SET); + wc = sim_fwrite (buffer, sizeof(uint64), wps, uptr->fileref); + break; + case DBD9: + bc = (wps / 2) * 9; + for (wp = wc = 0; wp < wps;) { + temp = buffer[wp++]; + conv_buff[wc++] = (uint8)((temp >> 28) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 20) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 12) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 4) & 0xff); + conv_buff[wc] = (uint8)((temp & 0xf) << 4); + temp = buffer[wp++]; + conv_buff[wc++] |= (uint8)((temp >> 32) & 0xf); + conv_buff[wc++] = (uint8)((temp >> 24) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 16) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 8) & 0xff); + conv_buff[wc++] = (uint8)(temp & 0xff); + } + da = sector * bc; + (void)sim_fseek(uptr->fileref, da, SEEK_SET); + wc = sim_fwrite (&conv_buff, 1, bc, uptr->fileref); + return SCPE_OK; + case DLD9: + bc = (wps / 2) * 9; + for (wp = wc = 0; wp < wps;) { + temp = buffer[wp++]; + conv_buff[wc++] = (uint8)(temp & 0xff); + conv_buff[wc++] = (uint8)((temp >> 8) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 16) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 24) & 0xff); + conv_buff[wc] = (uint8)((temp >> 32) & 0xf); + temp = buffer[wp++]; + conv_buff[wc++] |= (uint8)((temp << 4) & 0xf0); + conv_buff[wc++] = (uint8)((temp >> 4) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 12) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 20) & 0xff); + conv_buff[wc++] = (uint8)((temp >> 28) & 0xff); + } + da = sector * bc; + (void)sim_fseek(uptr->fileref, da, SEEK_SET); + wc = sim_fwrite (&conv_buff, 1, bc, uptr->fileref); + return SCPE_OK; + } + return SCPE_OK; +} + + +/* Set disk format */ +t_stat disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int f; + + if (uptr == NULL) return SCPE_IERR; + if (cptr == NULL) return SCPE_ARG; + for (f = 0; fmts[f].name != 0; f++) { + if (strcmp (cptr, fmts[f].name) == 0) { + uptr->flags &= ~UNIT_FMT; + uptr->flags |= SET_FMT(fmts[f].mode); + return SCPE_OK; + } + } + return SCPE_ARG; +} + +/* Show disk format */ + +t_stat disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int fmt = GET_FMT(uptr->flags); + int f; + + for (f = 0; fmts[f].name != 0; f++) { + if (fmt == fmts[f].mode) { + fprintf (st, "%s format", fmts[f].name); + return SCPE_OK; + } + } + fprintf (st, "invalid format"); + return SCPE_OK; +} + + +/* Device attach */ +t_stat disk_attach (UNIT *uptr, CONST char *cptr) +{ + t_stat r; + char gbuf[30]; + + /* Reset to SIMH format on attach */ + uptr->flags &= ~UNIT_FMT; + /* Pickup optional format specifier during RESTORE */ + cptr = get_sim_sw (cptr); + if (sim_switches & SWMASK ('F')) { /* format spec? */ + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) return SCPE_2FARG; /* must be more */ + if (disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } + + r = attach_unit (uptr, cptr); + if (r != SCPE_OK) + return r; + return SCPE_OK; +} + +/* Device detach */ + +t_stat disk_detach (UNIT *uptr) +{ + return detach_unit (uptr); +} + +t_stat disk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "%s Disk Attach Help\n\n", dptr->name); + + fprintf (st, "Disk container files can be one of 3 different types:\n\n"); + fprintf (st, " SIMH A disk is an unstructured binary file of 64bit integers\n"); + fprintf (st, " DBD9 Compatible with KLH10 is a packed big endian word\n"); + fprintf (st, " DLD9 Compatible with KLH10 is a packed little endian word\n"); + + if (dptr->numunits > 1) { + uint32 i; + + for (i=0; (i < dptr->numunits); ++i) + if ((dptr->units[i].flags & UNIT_ATTABLE) && + !(dptr->units[i].flags & UNIT_DIS)) { + fprintf (st, " sim> ATTACH {switches} %s%d diskfile\n", dptr->name, i); + } + } + else + fprintf (st, " sim> ATTACH {switches} %s diskfile\n", dptr->name); + fprintf (st, "\n%s attach command switches\n", dptr->name); + fprintf (st, " -R Attach Read Only.\n"); + fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n"); + fprintf (st, " disk container will be attempted).\n"); + fprintf (st, " -F Open the indicated disk container in a specific format (default\n"); + fprintf (st, " is SIMH), other options are DBD9 and DLD9\n"); + fprintf (st, " -Y Answer Yes to prompt to overwrite last track (on disk create)\n"); + fprintf (st, " -N Answer No to prompt to overwrite last track (on disk create)\n"); + return SCPE_OK; +} diff --git a/PDP10/kx10_disk.h b/PDP10/kx10_disk.h new file mode 100644 index 00000000..21bf9bc8 --- /dev/null +++ b/PDP10/kx10_disk.h @@ -0,0 +1,81 @@ +/* kx10_disk.h: Disk translator. + + Copyright (c) 2020, 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. + +*/ + + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ +#define UNIT_V_FMT (UNIT_V_UF + 8) +#define UNIT_M_FMT 7 +#define GET_FMT(x) (((x) >> UNIT_V_FMT) & UNIT_M_FMT) +#define SET_FMT(x) (((x) & UNIT_M_FMT) << UNIT_V_FMT) +#define UNIT_FMT (UNIT_M_FMT << UNIT_V_FMT) + +#define SIMH 0 /* Default raw uint64 word format */ +#define DBD9 1 /* KLH10 Disb Big End Double */ +#define DLD9 2 /* KLH10 Disb Little End Double */ + +/* + * SIMH format is number words per sector stored as a 64 bit word. + * + * DBD9 format is: 9 character per pair of words. + * + * 0 - B0 1 2 3 4 5 6 7 + * 0 - 8 9 10 11 12 13 14 15 + * 0 - 16 17 18 19 20 21 22 23 + * 0 - 24 25 26 27 28 29 30 31 + * 0 - 32 33 34 35 B0 1 2 3 + * 1 - 4 5 6 7 8 9 10 11 + * 1 - 12 13 14 15 16 17 18 19 + * 1 - 20 21 22 23 24 25 26 27 + * 1 - 28 29 30 31 32 33 34 35 + * + * + * DLD9 format is: 9 character per pair of words. + * + * 0 - 28 29 30 31 32 33 34 35 + * 0 - 20 21 22 23 24 25 26 27 + * 0 - 12 13 14 15 16 17 18 19 + * 0 - 4 5 6 7 8 9 10 11 + * 0 - 32 33 34 35 B0 1 2 3 + * 1 - 24 25 26 27 28 29 30 31 + * 1 - 16 17 18 19 20 21 22 23 + * 1 - 8 9 10 11 12 13 14 15 + * 1 - B0 1 2 3 4 5 6 7 + */ + + +t_stat disk_read(UNIT *uptr, uint64 *buffer, int sector, int wps); +t_stat disk_write(UNIT *uptr, uint64 *buffer, int sector, int wps); +/* Set disk format */ +t_stat disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +/* Show disk format */ +t_stat disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +/* Device attach */ +t_stat disk_attach (UNIT *uptr, CONST char *cptr); +/* Device detach */ +t_stat disk_detach (UNIT *uptr); +/* Print attach help */ +t_stat disk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); diff --git a/PDP10/kx10_dk.c b/PDP10/kx10_dk.c index ca05e979..83e2cbfd 100644 --- a/PDP10/kx10_dk.c +++ b/PDP10/kx10_dk.c @@ -1,6 +1,6 @@ -/* ka10_dk.c: PDP-10 DK subsystem simulator +/* kx10_dk.c: PDP-10 DK subsystem simulator - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2013-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP10/kx10_dp.c b/PDP10/kx10_dp.c index 6810203e..40ac232f 100644 --- a/PDP10/kx10_dp.c +++ b/PDP10/kx10_dp.c @@ -1,6 +1,6 @@ -/* ka10_dp.c: Dec Data Products Disk Drive. +/* kx10_dp.c: Dec Data Products Disk Drive. - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2013-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -22,6 +22,7 @@ */ #include "kx10_defs.h" +#include "kx10_disk.h" #ifndef NUM_DEVS_DP #define NUM_DEVS_DP 0 @@ -39,13 +40,10 @@ /* 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 */ @@ -271,6 +269,7 @@ MTAB dp_mod[] = { {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 }, + {MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", NULL, &disk_show_fmt }, {0}, }; @@ -301,7 +300,7 @@ DEVICE dpa_dev = { #if (NUM_DEVS_DP > 1) REG dpb_reg[] = { {BRDATA(BUFF, dp_buf[1], 16, 64, RP_NUMWD), REG_HRO}, - {HRDATA(UNIT, dp_cur_unit[1], 32), REG_HRO}, + {HRDATA(DF10, dp_cur_unit[1], 32), 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)}, @@ -325,7 +324,7 @@ DEVICE dpb_dev = { #if (NUM_DEVS_DP > 2) REG dpc_reg[] = { {BRDATA(BUFF, dp_buf[2], 16, 64, RP_NUMWD), REG_HRO}, - {BRDATA(DF10, &dp_cur_unit[2], 16, 8, 1), REG_HRO}, + {HRDATA(DF10, dp_cur_unit[2], 32), 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)}, @@ -348,8 +347,8 @@ DEVICE dpc_dev = { #if (NUM_DEVS_DP > 3) REG dpd_reg[] = { - {BRDATA(BUFF, dp_buf[3], 16, 64, RP_NUMWD), REG_HRO}, - {BRDATA(DF10, &dp_cur_unit[3], 16, 8, 1), REG_HRO}, + {BRDATA(BUFF, &dp_buf[3][0], 16, 64, RP_NUMWD), REG_HRO}, + {BRDATA(DF10, dp_cur_unit[3], 32), 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)}, @@ -423,7 +422,6 @@ t_stat dp_devio(uint32 dev, uint64 *data) { if (*data & BUSY) { /* Stop controller */ sim_cancel(uptr); - uptr->STATUS &= ~BUSY; df10_finish_op(df10, 0); } /* Clear flags */ @@ -603,7 +601,7 @@ t_stat dp_svc (UNIT *uptr) int cyl = (uptr->UFLAGS >> 20) & 0777; DEVICE *dptr = dp_devs[ctlr]; struct df10 *df10 = &dp_df10[ctlr]; - int diff, diffs, wc; + int diff, diffs; int r; sect &= 017; @@ -647,12 +645,8 @@ t_stat dp_svc (UNIT *uptr) 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; + * dp_drv_tab[dtype].sect + sect); + (void)disk_read(uptr, &dp_buf[ctlr][0], da, RP_NUMWD); uptr->hwmark = RP_NUMWD; uptr->DATAPTR = 0; sect = sect + 1; @@ -695,13 +689,11 @@ t_stat dp_svc (UNIT *uptr) 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; + * dp_drv_tab[dtype].sect + sect); /* 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); + (void)disk_write(uptr, &dp_buf[ctlr][0], da, RP_NUMWD); uptr->STATUS |= SRC_DONE; sect = sect + 1; if (sect >= dp_drv_tab[dtype].sect) { @@ -722,7 +714,7 @@ t_stat dp_svc (UNIT *uptr) CLR_BUF(uptr); } if (r) - sim_activate(uptr, 25); + sim_activate(uptr, 40); else { uptr->STATUS &= ~(SRC_DONE|END_CYL|BUSY); uptr->UFLAGS |= DONE; @@ -780,13 +772,11 @@ t_stat dp_svc (UNIT *uptr) 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; + * dp_drv_tab[dtype].sect + sect); /* 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); + (void)disk_write(uptr, &dp_buf[ctlr][0], da, RP_NUMWD); uptr->STATUS |= SRC_DONE; sect = sect + 1; if (sect >= dp_drv_tab[dtype].sect) { @@ -931,8 +921,7 @@ dp_boot(int32 unit_num, DEVICE * dptr) 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); + (void)disk_read(uptr, &dp_buf[0][0], sect, RP_NUMWD); ptr = 0; for(wc = RP_NUMWD; wc > 0; wc--) M[addr++] = dp_buf[0][ptr++]; @@ -950,10 +939,10 @@ t_stat dp_attach (UNIT *uptr, CONST char *cptr) DIB *dib; int ctlr; - uptr->capac = dp_drv_tab[GET_DTYPE (uptr->flags)].size; - r = attach_unit (uptr, cptr); - if (r != SCPE_OK) + r = disk_attach (uptr, cptr); + if (r != SCPE_OK || (sim_switches & SIM_SW_REST) != 0) return r; + uptr->capac = dp_drv_tab[GET_DTYPE (uptr->flags)].size; dptr = find_dev_from_unit(uptr); if (dptr == 0) return SCPE_OK; @@ -962,7 +951,7 @@ t_stat dp_attach (UNIT *uptr, CONST char *cptr) 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); + set_interrupt(DP_DEVNUM + (ctlr), dp_df10[ctlr].status); return SCPE_OK; } @@ -974,7 +963,7 @@ t_stat dp_detach (UNIT *uptr) return SCPE_OK; if (sim_is_active (uptr)) /* unit active? */ sim_cancel (uptr); /* cancel operation */ - return detach_unit (uptr); + return disk_detach (uptr); } t_stat dp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) @@ -983,6 +972,7 @@ 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"); +disk_attach_help(st, dptr, uptr, flag, cptr); 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"); diff --git a/PDP10/kx10_dpy.c b/PDP10/kx10_dpy.c index 54e2fd6f..4fb94b40 100644 --- a/PDP10/kx10_dpy.c +++ b/PDP10/kx10_dpy.c @@ -85,6 +85,7 @@ */ #include "kx10_defs.h" +#include "sim_video.h" #include #ifndef NUM_DEVS_DPY @@ -296,13 +297,40 @@ t_stat dpy_svc (UNIT *uptr) return SCPE_OK; } +#define JOY_MAX_UNITS 4 +#define JOY_MAX_AXES 4 +#define JOY_MAX_BUTTONS 4 + +static int joy_axes[JOY_MAX_UNITS * JOY_MAX_AXES]; +static int joy_buttons[JOY_MAX_UNITS * JOY_MAX_BUTTONS]; + +static void dpy_joy_motion(int which, int axis, int value) +{ + int result = FALSE; + if (which < JOY_MAX_UNITS && axis < JOY_MAX_AXES) { + joy_axes[which * JOY_MAX_AXES + axis] = value; + } +} + +static void dpy_joy_button(int which, int button, int state) +{ + int result = FALSE; + if (which < JOY_MAX_UNITS && button < JOY_MAX_BUTTONS) { + joy_buttons[which * JOY_MAX_UNITS + button] = state; + } +} + /* Reset routine */ t_stat dpy_reset (DEVICE *dptr) { - if (!(dptr->flags & DEV_DIS)) { + if (dptr->flags & DEV_DIS) { + display_close(dptr); + } else { display_reset(); ty340_reset(dptr); + vid_register_gamepad_motion_callback (dpy_joy_motion); + vid_register_gamepad_button_callback (dpy_joy_button); } sim_cancel (&dpy_unit[0]); /* deactivate unit */ return SCPE_OK; @@ -367,17 +395,25 @@ cpu_set_switches(unsigned long w1, unsigned long w2) { #if NUM_DEVS_WCNSLS > 0 #define WCNSLS_DEVNUM 0420 +#define UNIT_JOY (1 << DEV_V_UF) + t_stat wcnsls_devio(uint32 dev, uint64 *data); const char *wcnsls_description (DEVICE *dptr); DIB wcnsls_dib[] = { { WCNSLS_DEVNUM, 1, &wcnsls_devio, NULL }}; +MTAB wcnsls_mod[] = { + { UNIT_JOY, UNIT_JOY, "JOYSTICK", "JOYSTICK", NULL, NULL, NULL, + "Use USB joysticks"}, + { 0 } + }; + UNIT wcnsls_unit[] = { { UDATA (NULL, UNIT_IDLE, 0) }}; DEVICE wcnsls_dev = { - "WCNSLS", wcnsls_unit, NULL, NULL, + "WCNSLS", wcnsls_unit, NULL, wcnsls_mod, NUM_DEVS_WCNSLS, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, @@ -390,27 +426,16 @@ 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 +#define CCW 0400LL /* counter clockwise (L) */ +#define CW 0200LL /* clockwise (R) */ +#define THRUST 0100LL +#define HYPER 040LL +#define FIRE 020LL /* shift values for the players' bytes */ #define UR 0 /* upper right: enterprise "top plug" */ @@ -418,6 +443,71 @@ t_stat wcnsls_devio(uint32 dev, uint64 *data) { #define LL 18 /* lower left: thin ship "third plug" */ #define UL 27 /* upper left: fat ship "bottom plug" */ +#define JOY_TRIG 5000 +#define JOY0 (JOY_MAX_AXES*0) +#define JOY1 (JOY_MAX_AXES*1) +#define JOY2 (JOY_MAX_AXES*2) +#define JOY3 (JOY_MAX_AXES*3) +#define BUT0 (JOY_MAX_BUTTONS*0) +#define BUT1 (JOY_MAX_BUTTONS*1) +#define BUT2 (JOY_MAX_BUTTONS*2) +#define BUT3 (JOY_MAX_BUTTONS*3) + +static uint64 joystick_switches (void) +{ + uint64 switches = 0777777777777LL; + + if (joy_axes[JOY0] > JOY_TRIG) + switches &= ~(CCW << UR); + else if (joy_axes[JOY0] < -JOY_TRIG) + switches &= ~(CW << UR); + if (joy_axes[JOY0+1] < -JOY_TRIG) + switches &= ~(THRUST << UR); + if (joy_buttons[BUT0]) + switches &= ~(FIRE << UR); + if (joy_buttons[BUT0+1]) + switches &= ~(HYPER << UR); + + if (joy_axes[JOY1] > JOY_TRIG) + switches &= ~(CCW << LR); + else if (joy_axes[JOY1] < -JOY_TRIG) + switches &= ~(CW << LR); + if (joy_axes[JOY1+1] < -JOY_TRIG) + switches &= ~(THRUST << LR); + if (joy_buttons[BUT1]) + switches &= ~(FIRE << LR); + if (joy_buttons[BUT1+1]) + switches &= ~(HYPER << LR); + + if (joy_axes[JOY2] > JOY_TRIG) + switches &= ~(CCW << LL); + else if (joy_axes[JOY2] < -JOY_TRIG) + switches &= ~(CW << LL); + if (joy_axes[JOY2+1] < -JOY_TRIG) + switches &= ~(THRUST << LL); + if (joy_buttons[BUT2]) + switches &= ~(FIRE << LL); + if (joy_buttons[BUT2+1]) + switches &= ~(HYPER << LL); + + if (joy_axes[JOY3] > JOY_TRIG) + switches &= ~((uint64)CCW << UL); + else if (joy_axes[JOY3] < -JOY_TRIG) + switches &= ~((uint64)CW << UL); + if (joy_axes[JOY3+1] < -JOY_TRIG) + switches &= ~((uint64)THRUST << UL); + if (joy_buttons[BUT3]) + switches &= ~((uint64)FIRE << UL); + if (joy_buttons[BUT3+1]) + switches &= ~(HYPER << UL); + + return switches; +} + +static uint64 keyboard_switches (void) +{ + uint64 switches = 0777777777777LL; /* 1 is off */ + #if 1 #define DEBUGSW(X) (void)0 #else @@ -433,15 +523,118 @@ t_stat wcnsls_devio(uint32 dev, uint64 *data) { SPACEWAR_SWITCHES; #undef SWSW - if (spacewar_switches) - DEBUGSW(("in %#lo out %#llo\r\n", spacewar_switches, switches)); + if (spacewar_switches) + DEBUGSW(("in %#lo out %#llo\r\n", spacewar_switches, switches)); + + + return switches; +} + +t_stat wcnsls_devio(uint32 dev, uint64 *data) { + switch (dev & 3) { + case CONO: + /* CONO WCNSLS,40 ;enable spacewar consoles */ + break; + + case DATAI: + if (wcnsls_unit->flags & UNIT_JOY) { + *data = joystick_switches (); + } else { + *data = keyboard_switches (); + } - *data = switches; sim_debug(DEBUG_DATAIO, &wcnsls_dev, "WCNSLS %03o DATI %012llo PC=%06o\n", - dev, switches, PC); + dev, *data, PC); + break; + } + return SCPE_OK; +} + +/* + * Old MIT Spacewar console switches + */ +#if NUM_DEVS_OCNSLS > 0 +#define OCNSLS_DEVNUM 0724 + +t_stat ocnsls_devio(uint32 dev, uint64 *data); +const char *ocnsls_description (DEVICE *dptr); + +DIB ocnsls_dib[] = { + { OCNSLS_DEVNUM, 1, &ocnsls_devio, NULL }}; + +UNIT ocnsls_unit[] = { + { UDATA (NULL, UNIT_IDLE, 0) }}; + +DEVICE ocnsls_dev = { + "OCNSLS", ocnsls_unit, NULL, NULL, + NUM_DEVS_OCNSLS, 0, 0, 0, 0, 0, + NULL, NULL, NULL, + NULL, NULL, NULL, + &ocnsls_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL, + NULL, NULL, NULL, NULL, NULL, &ocnsls_description + }; + +const char *ocnsls_description (DEVICE *dptr) +{ + return "Old MIT Spacewar Consoles"; +} + +#define OHYPER 0004LL /* Hyperspace. */ +#define OFIRE 0010LL /* Fire torpedo. */ +#define OCW 0020LL /* Turn clockwise. */ +#define OCCW 0040LL /* Turn counter clockwise. */ +#define SLOW 0100LL /* Weak thrust. */ +#define FAST 0200LL /* Strong thrust. */ +#define BEACON 020000LL /* Aiming beacon. */ + +static uint64 old_switches (void) +{ + uint64 switches = 0; + + if (joy_axes[JOY0] > JOY_TRIG) + switches |= OCCW; + else if (joy_axes[JOY0] < -JOY_TRIG) + switches |= OCW; + if (joy_axes[JOY0+1] < -JOY_TRIG) + switches |= FAST; + if (joy_axes[JOY0+1] > JOY_TRIG) + switches |= SLOW; + if (joy_buttons[BUT0]) + switches |= OFIRE; + if (joy_buttons[BUT0+1]) + switches |= OHYPER; + if (joy_buttons[BUT0+2]) + switches |= BEACON; + + if (joy_axes[JOY1] > JOY_TRIG) + switches |= OCCW << 18; + else if (joy_axes[JOY1] < -JOY_TRIG) + switches |= OCW << 18; + if (joy_axes[JOY1+1] < -JOY_TRIG) + switches |= FAST << 18; + if (joy_axes[JOY1+1] > JOY_TRIG) + switches |= SLOW << 18; + if (joy_buttons[BUT1]) + switches |= OFIRE << 18; + if (joy_buttons[BUT1+1]) + switches |= OHYPER << 18; + if (joy_buttons[BUT1+2]) + switches |= BEACON << 18; + + return switches; +} + +t_stat ocnsls_devio(uint32 dev, uint64 *data) { + switch (dev & 3) { + case DATAI: + *data = old_switches (); + break; + case CONI: + *data = 0; break; } return SCPE_OK; } #endif #endif +#endif diff --git a/PDP10/kx10_dt.c b/PDP10/kx10_dt.c index 15e46438..449e7889 100644 --- a/PDP10/kx10_dt.c +++ b/PDP10/kx10_dt.c @@ -1,6 +1,9 @@ -/* pdp10_dt.c: 18b DECtape simulator +/* kx10_dt.c: 18b DECtape simulator - Copyright (c) 2017 Richard Cornwell + Copyright (c) 2017-2020 Richard Cornwell based on work by Bob Supnik + + Based on PDP18B/pdp18b_dt.c by: + Copyright (c) 1993-2017, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -19,6 +22,10 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + 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. @@ -169,6 +176,7 @@ #define DTC_FWDRV 0200000 /* Move unit forward */ #define DTC_STSTOP 0400000 /* Stop unit */ +#define CMD u3 /* Flags in lower bits of u3 */ #define DTC_FNC_STOP 001 /* Unit stopping */ #define DTC_FNC_START 002 /* Start unit motion */ @@ -250,7 +258,9 @@ #define ABS(x) (((x) < 0)? (-(x)): (x)) -#define DT_WRDTIM 15000 +#define DT_WRDTIM 10000 + +#define WRITTEN u6 /* Set when tape modified */ int32 dtsa = 0; /* status A */ uint64 dtsb = 0; /* status B */ @@ -262,6 +272,7 @@ 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); +void dt_flush (UNIT *uptr); t_stat dt_detach (UNIT *uptr); #if MPX_DEV t_stat dt_set_mpx (UNIT *uptr, int32 val, CONST char *cptr, void *desc) ; @@ -369,7 +380,7 @@ t_stat dt_devio(uint32 dev, uint64 *data) { /* Stop all drives and clear drive unit */ dtsa &= 0770777; for (i = 0; i < DT_NUMDR; i++) { - dt_unit[i].u3 &= ~0700; + dt_unit[i].CMD &= ~0700; } if ((*data & DTC_SEL) == 0) break; @@ -426,14 +437,14 @@ t_stat dt_devio(uint32 dev, uint64 *data) { } if (*data & DTC_STSTOP) { if ((dt_unit[i].DSTATE & (DTC_MOT)) != 0) { - dt_unit[i].u3 |= DTC_FNC_STOP; + dt_unit[i].CMD |= DTC_FNC_STOP; } dtsa &=~ (DTC_FWDRV|DTC_RVDRV); } else { /* Start the unit if not already running */ - dt_unit[i].u3 &= ~DTC_FNC_STOP; + dt_unit[i].CMD &= ~DTC_FNC_STOP; if ((dt_unit[i].DSTATE & (DTC_MOT)) == 0) { - dt_unit[i].u3 |= DTC_FNC_START; + dt_unit[i].CMD |= DTC_FNC_START; dtsb |= DTB_DLY; if (!sim_is_active(&dt_unit[i])) sim_activate(&dt_unit[i], 10000); @@ -442,20 +453,20 @@ t_stat dt_devio(uint32 dev, uint64 *data) { switch(*data & (DTC_FWDRV|DTC_RVDRV)) { case DTC_FWDRV: if (dt_unit[i].DSTATE & DTC_REV) { - dt_unit[i].u3 |= DTC_FNC_REV; + dt_unit[i].CMD |= 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; + dt_unit[i].CMD |= DTC_FNC_REV; dtsa |= (DTC_RVDRV); } else dtsa |= (DTC_FWDRV); break; case DTC_FWDRV|DTC_RVDRV: - dt_unit[i].u3 |= DTC_FNC_REV; + dt_unit[i].CMD |= DTC_FNC_REV; if ((dt_unit[i].DSTATE & DTC_REV) == 0) dtsa |= (DTC_RVDRV); else @@ -500,7 +511,7 @@ t_stat dt_devio(uint32 dev, uint64 *data) { 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; + dt_unit[i].CMD |= DTC_FNC_STOP; } } dtsb = (uint64)((*data & (DTS_PAR_ERR|DTS_DATA_MISS|DTS_JOB_DONE| \ @@ -569,11 +580,11 @@ t_stat dt_svc (UNIT *uptr) */ if (uptr->DSTATE & DTC_MOT) { /* Check if stoping */ - if (uptr->u3 & DTC_FNC_STOP) { + if (uptr->CMD & 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->CMD &= ~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); @@ -591,10 +602,10 @@ if (uptr->DSTATE & DTC_MOT) { uptr->DSTATE |= (blk << DTC_V_BLK); return SCPE_OK; } - if (uptr->u3 & DTC_FNC_REV) { + if (uptr->CMD & 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->CMD &= ~DTC_FNC_REV; uptr->DSTATE ^= DTC_REV; return SCPE_OK; } @@ -614,7 +625,7 @@ if (uptr->DSTATE & DTC_MOT) { 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->CMD |= DTC_FNC_STOP; uptr->u6 = 0; dtsb |= DTB_END; dtsb &= ~DTB_IDL; @@ -636,7 +647,7 @@ if (uptr->DSTATE & DTC_MOT) { 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)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_MOVE: case FNC_SRCH: case FNC_WBLK: @@ -647,7 +658,7 @@ if (uptr->DSTATE & DTC_MOT) { case FNC_RALL: case FNC_WRIT: case FNC_READ: - uptr->u3 &= 077077; + uptr->CMD &= 077077; dtsb |= DTB_DONE; if (dtsb & DTB_JOBENB) set_interrupt(DT_DEVNUM, dtsa); @@ -661,11 +672,11 @@ if (uptr->DSTATE & DTC_MOT) { break; } if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { - uptr->u3 |= DTC_FNC_STOP; + uptr->CMD |= DTC_FNC_STOP; } if (DTC_GETUNI(dtsa) == u) { - uptr->u3 &= 077077; - uptr->u3 |= dtsa & 0700; /* Copy command */ + uptr->CMD &= 077077; + uptr->CMD |= dtsa & 0700; /* Copy command */ } if (word <= 0) { uptr->DSTATE = DTC_FEND | (DTC_MOTMASK & uptr->DSTATE); @@ -698,7 +709,7 @@ if (uptr->DSTATE & DTC_MOT) { uptr->DSTATE |= (word - 1) << DTC_V_WORD; } uptr->u6-=2; - switch (DTC_GETFNC(uptr->u3)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_MOVE: case FNC_SRCH: case FNC_WBLK: @@ -724,6 +735,7 @@ if (uptr->DSTATE & DTC_MOT) { data = dtdb; fbuf[off] = (data >> 18) & RMASK; fbuf[off+1] = data & RMASK; + uptr->WRITTEN = 1; uptr->hwmark = uptr->capac; break; } @@ -740,15 +752,16 @@ if (uptr->DSTATE & DTC_MOT) { 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); + 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 */ + uptr->CMD &= 077077; + uptr->CMD |= dtsa & 0700; /* Copy command */ } dtsb &= ~DTB_BLKRD; - switch (DTC_GETFNC(uptr->u3)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_WRIT: case FNC_WALL: dtsb |= DTB_DATREQ; @@ -772,7 +785,7 @@ if (uptr->DSTATE & DTC_MOT) { break; } if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { - uptr->u3 |= DTC_FNC_STOP; + uptr->CMD |= DTC_FNC_STOP; } break; @@ -780,15 +793,16 @@ if (uptr->DSTATE & DTC_MOT) { 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); + 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 */ + uptr->CMD &= 077077; + uptr->CMD |= dtsa & 0700; /* Copy command */ } - switch (DTC_GETFNC(uptr->u3)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_MOVE: case FNC_READ: case FNC_WMRK: @@ -825,7 +839,7 @@ if (uptr->DSTATE & DTC_MOT) { 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->DSTATE = DTC_FBLK | (DTC_MOTMASK & uptr->DSTATE); /* Move to first block */ uptr->u6 = 0; dtsb &= ~DTB_IDL; break; @@ -839,10 +853,10 @@ if (uptr->DSTATE & DTC_MOT) { 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 */ + uptr->CMD &= 077077; + uptr->CMD |= dtsa & 0700; /* Copy command */ } - switch (DTC_GETFNC(uptr->u3)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_RALL: case FNC_SRCH: dt_putword(&data); @@ -872,10 +886,10 @@ if (uptr->DSTATE & DTC_MOT) { if (dtsb & DTB_STOP) dtsa &= ~0700; /* Clear command */ if (DTC_GETUNI(dtsa) == u) { - uptr->u3 &= 077077; - uptr->u3 |= dtsa & 0700; /* Copy command */ + uptr->CMD &= 077077; + uptr->CMD |= dtsa & 0700; /* Copy command */ } - switch (DTC_GETFNC(uptr->u3)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_WRIT: case FNC_WALL: dtsb |= DTB_DATREQ; @@ -899,7 +913,7 @@ if (uptr->DSTATE & DTC_MOT) { break; } if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { - uptr->u3 |= DTC_FNC_STOP; + uptr->CMD |= DTC_FNC_STOP; } break; @@ -918,7 +932,7 @@ if (uptr->DSTATE & DTC_MOT) { uptr->DSTATE &= ~(DTC_M_WORD << DTC_V_WORD); uptr->DSTATE |= (word + 1) << DTC_V_WORD; } - switch (DTC_GETFNC(uptr->u3)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_MOVE: case FNC_SRCH: case FNC_WALL: @@ -931,17 +945,18 @@ if (uptr->DSTATE & DTC_MOT) { if ((dtsb & DTB_STOP) == 0) dt_putword(&data); else - uptr->u3 &= 077077; + uptr->CMD &= 077077; break; case FNC_WRIT: if ((dtsb & DTB_STOP) == 0) dt_getword(&data, (word != DTC_M_WORD)); else { - uptr->u3 &= 077077; + uptr->CMD &= 077077; data = dtdb; } fbuf[off] = (data >> 18) & RMASK; fbuf[off+1] = data & RMASK; + uptr->WRITTEN = 1; uptr->hwmark = uptr->capac; break; case FNC_WMRK: @@ -972,20 +987,21 @@ if (uptr->DSTATE & DTC_MOT) { dtsb &= ~(DTB_CHK); dtsb |= DTB_IDL; if (DTC_GETUNI(dtsa) == u) { - uptr->u3 &= 077077; - uptr->u3 |= dtsa & 0700; /* Copy command */ + uptr->CMD &= 077077; + uptr->CMD |= 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); + 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)) { + switch (DTC_GETFNC(uptr->CMD)) { case FNC_MOVE: case FNC_WBLK: case FNC_SRCH: @@ -997,7 +1013,7 @@ if (uptr->DSTATE & DTC_MOT) { case FNC_WRIT: case FNC_READ: case FNC_WMRK: - uptr->u3 &= 077077; + uptr->CMD &= 077077; dtsb |= DTB_DONE; if (dtsb & DTB_JOBENB) set_interrupt(DT_DEVNUM, dtsa); @@ -1006,13 +1022,13 @@ if (uptr->DSTATE & DTC_MOT) { break; } if (dtsb & (DTB_PAR|DTB_MIS|DTB_ILL|DTB_END|DTB_INCBLK|DTB_MRKERR)) { - uptr->u3 |= DTC_FNC_STOP; + uptr->CMD |= DTC_FNC_STOP; } break; case DTC_REND: /* In final endzone */ /* Set stop */ - uptr->u3 |= DTC_FNC_STOP; + uptr->CMD |= DTC_FNC_STOP; sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o reverse end\n", u); dtsb &= ~DTB_IDL; dtsb |= DTB_END; @@ -1023,18 +1039,18 @@ if (uptr->DSTATE & DTC_MOT) { } } /* Check if starting */ -} else if (uptr->u3 & DTC_FNC_START) { +} else if (uptr->CMD & DTC_FNC_START) { /* Start up delay */ sim_activate(uptr, DT_WRDTIM*10); - uptr->u3 &= ~(0700 | DTC_FNC_START); + uptr->CMD &= ~(0700 | DTC_FNC_START); if (DTC_GETUNI(dtsa) == u) - uptr->u3 |= dtsa & 0700; /* Copy command */ + uptr->CMD |= dtsa & 0700; /* Copy command */ uptr->DSTATE |= DTC_MOT; - if (uptr->u3 & DTC_FNC_REV) { - uptr->u3 &= ~DTC_FNC_REV; + if (uptr->CMD & DTC_FNC_REV) { + uptr->CMD &= ~DTC_FNC_REV; uptr->DSTATE ^= DTC_REV; } - sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o start %06o\n", u, uptr->u3); + sim_debug(DEBUG_DETAIL, &dt_dev, "DTA %o start %06o\n", u, uptr->CMD); return SCPE_OK; } return SCPE_OK; @@ -1111,7 +1127,7 @@ t_stat dt_reset (DEVICE *dptr) 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; + dt_unit[i].CMD |= DTC_FNC_STOP; } clr_interrupt(DT_DEVNUM); clr_interrupt(DT_DEVNUM|4); @@ -1129,77 +1145,126 @@ t_stat dt_reset (DEVICE *dptr) 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) + 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 (sz == D11_FILSIZ) + 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; + 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 */ + fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + sim_printf ("%s%d: ", sim_dname (&dt_dev), u); + if (uptr->flags & UNIT_8FMT) + sim_printf ("12b format"); + else if (uptr->flags & UNIT_11FMT) + sim_printf ("16b format"); + else sim_printf ("18b/36b format"); + sim_printf (", buffering file in memory\n"); + uptr->io_flush = dt_flush; + 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]; + } /* end file loop */ + uptr->hwmark = ba; + } 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; + } 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 */ + uptr->WRITTEN = 0; + return SCPE_OK; +} + +/* Flush tape image to disk + + 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 +*/ + +void dt_flush (UNIT* uptr) +{ + uint16 pdp8b[D8_NBSIZE]; + uint16 pdp11b[D18_BSIZE]; + uint32 ba, k, *fbuf; + int32 u = uptr - dt_dev.units; + + if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + rewind (uptr->fileref); /* start of file */ + fbuf = (uint32 *) uptr->filebuf; /* file buffer */ + 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 */ + } 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)) + sim_perror ("I/O error"); + } /* end if hwmark */ + uptr->WRITTEN = 0; } /* Detach routine @@ -1213,54 +1278,23 @@ return SCPE_OK; t_stat dt_detach (UNIT* uptr) { -uint16 pdp8b[D8_NBSIZE]; -uint16 pdp11b[D18_BSIZE]; -uint32 ba, k, *fbuf; -int32 u = uptr - dt_dev.units; + 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; + 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 (&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); + if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + sim_printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + dt_flush(uptr); + } /* 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 index a5f73c01..dd74f208 100644 --- a/PDP10/kx10_imp.c +++ b/PDP10/kx10_imp.c @@ -1,6 +1,6 @@ -/* ka10_imp.c: IMP, interface message processor. +/* kx10_imp.c: IMP, interface message processor. - Copyright (c) 2018, Richard Cornwell based on code provided by + Copyright (c) 2018-2020, Richard Cornwell based on code provided by Lars Brinkhoff and Danny Gasparovski. Permission is hereby granted, free of charge, to any person obtaining a @@ -451,6 +451,7 @@ static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff}; static CONST in_addr_T broadcast_ipaddr = {0xffffffff}; t_stat imp_devio(uint32 dev, uint64 *data); +t_addr imp_devirq(uint32 dev, t_addr addr); t_stat imp_srv(UNIT *); t_stat imp_eth_srv(UNIT *); t_stat imp_tim_srv(UNIT *); @@ -503,7 +504,13 @@ UNIT imp_unit[] = { {UDATA(imp_eth_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 0 */ {UDATA(imp_tim_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 0 */ }; -DIB imp_dib = {IMP_DEVNUM, 1, &imp_devio, NULL}; +DIB imp_dib = {IMP_DEVNUM, 1, &imp_devio, +#if KL + &imp_devirq, +#else + NULL +#endif +}; MTAB imp_mod[] = { { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", @@ -562,10 +569,13 @@ DEBTAB imp_debug[] = { {0, 0} }; - +REG imp_reg[] = { + {SAVEDATA(DATA, imp_data) }, + {0} +}; DEVICE imp_dev = { - "IMP", imp_unit, NULL, imp_mod, + "IMP", imp_unit, imp_reg, imp_mod, 3, 8, 0, 1, 8, 36, NULL, NULL, &imp_reset, NULL, &imp_attach, &imp_detach, &imp_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, imp_debug, @@ -635,6 +645,7 @@ t_stat imp_devio(uint32 dev, uint64 *data) uptr->STATUS |= IMPIHE; if (*data & IMPLHW) /* Last host word. */ uptr->STATUS |= IMPLHW; + check_interrupts(uptr); break; case TYPE_BBN: break; @@ -731,6 +742,20 @@ t_stat imp_devio(uint32 dev, uint64 *data) return SCPE_OK; } +#if KL +/* Handle KL style interrupt vectors for ITS */ +t_addr +imp_devirq(uint32 dev, t_addr addr) { + if ((cpu_unit[0].flags & UNIT_ITSPAGE) != 0 && (imp_data.pia & 7) == 1) { + if (imp_unit[0].STATUS & IMPID && (imp_unit[0].STATUS & IMPLW) == 0) + return 070|RSIGN; + if (imp_unit[0].STATUS & IMPOD) + return 072|RSIGN; + } + return addr; +} +#endif + t_stat imp_srv(UNIT * uptr) { DEVICE *dptr = find_dev_from_unit(uptr); @@ -2669,6 +2694,7 @@ t_stat imp_attach(UNIT* uptr, CONST char* cptr) if (tptr == NULL) return SCPE_MEM; strcpy(tptr, cptr); + memset(&imp_data.ReadQ, 0, sizeof(ETH_QUE)); status = eth_open(&imp_data.etherface, cptr, &imp_dev, DEBUG_ETHER); if (status != SCPE_OK) { free(tptr); @@ -2747,12 +2773,12 @@ t_stat imp_detach(UNIT* uptr) if (uptr->flags & UNIT_DHCP) { imp_dhcp_release(&imp_data); } + sim_cancel (uptr+1); /* stop the packet timing services */ + sim_cancel (uptr+2); /* stop the clock timer services */ eth_close (&imp_data.etherface); free(uptr->filename); uptr->filename = NULL; uptr->flags &= ~UNIT_ATT; - sim_cancel (uptr+1); /* stop the packet timing services */ - sim_cancel (uptr+2); /* stop the clock timer services */ } return SCPE_OK; } diff --git a/PDP10/kx10_lp.c b/PDP10/kx10_lp.c index 5f8a5201..ebb625ff 100644 --- a/PDP10/kx10_lp.c +++ b/PDP10/kx10_lp.c @@ -1,6 +1,6 @@ -/* ka10_lp.c: PDP-10 line printer simulator +/* kx10_lp.c: PDP-10 line printer simulator - Copyright (c) 2011-2017, Richard Cornwell + Copyright (c) 2011-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -394,6 +394,8 @@ t_stat lpt_attach (UNIT *uptr, CONST char *cptr) t_stat reason; reason = attach_unit (uptr, cptr); + if (sim_switches & SIM_SW_REST) + return reason; uptr->STATUS &= ~ERR_FLG; clr_interrupt(LP_DEVNUM); return reason; diff --git a/PDP10/kx10_mt.c b/PDP10/kx10_mt.c index 2d2151e0..01674227 100644 --- a/PDP10/kx10_mt.c +++ b/PDP10/kx10_mt.c @@ -1,6 +1,6 @@ -/* ka10_mt.c: TM10A/B Magnetic tape controller +/* kx10_mt.c: TM10A/B Magnetic tape controller - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2013-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP10/kx10_pt.c b/PDP10/kx10_pt.c index 5d9457ef..7f31db81 100644 --- a/PDP10/kx10_pt.c +++ b/PDP10/kx10_pt.c @@ -1,6 +1,6 @@ -/* ka10_pt.c: PDP-10 reader/punch simulator +/* kx10_pt.c: PDP-10 paper tape reader/punch simulator - Copyright (c) 2011-2017, Richard Cornwell + Copyright (c) 2011-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/PDP10/kx10_rc.c b/PDP10/kx10_rc.c index 36f3e4a3..08978ca1 100644 --- a/PDP10/kx10_rc.c +++ b/PDP10/kx10_rc.c @@ -1,6 +1,6 @@ -/* ka10_rc.c: RC10 Disk Controller. +/* kx10_rc.c: RC10 Disk Controller. - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2013-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -187,7 +187,7 @@ DEVICE rca_dev = { #if (NUM_DEVS_RC > 1) REG rcb_reg[] = { - {BRDATA(BUFF, rc_buf[1], 16, 64, RM10_WDS), REG_HRO}, + {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)}, @@ -533,7 +533,7 @@ t_stat r; uptr->capac = rc_drv_tab[GET_DTYPE (uptr->flags)].size; r = attach_unit (uptr, cptr); -if (r != SCPE_OK) +if (r != SCPE_OK || (sim_switches & SIM_SW_REST) != 0) return r; uptr->CUR_CYL = 0; uptr->UFLAGS = 0; diff --git a/PDP10/kx10_rh.c b/PDP10/kx10_rh.c new file mode 100644 index 00000000..8378eb3c --- /dev/null +++ b/PDP10/kx10_rh.c @@ -0,0 +1,801 @@ +/* kx10_rh.c: RH10/RH20 interace routines. + + Copyright (c) 2019-2020, 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" + + +/* 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) */ + +/* RH20 CONI Flags */ +#define RH20_PCR_FULL 0000000000020LL /* Primary command file full */ +#define RH20_ATTN_ENA 0000000000040LL /* Attention enable */ +#define RH20_SCR_FULL 0000000000100LL /* Secondary command full */ +#define RH20_ATTN 0000000000200LL /* Attention */ +#define RH20_MASS_ENA 0000000000400LL /* Mass bus enable */ +#define RH20_DATA_OVR 0000000001000LL /* Data overrun */ +#define RH20_CHAN_RDY 0000000002000LL /* Channel ready to start */ +#define RH20_RAE 0000000004000LL /* Register access error */ +#define RH20_DR_RESP 0000000010000LL /* Drive no response */ +#define RH20_CHAN_ERR 0000000020000LL /* Channel error */ +#define RH20_SHRT_WC 0000000040000LL /* Short word count */ +#define RH20_LONG_WC 0000000100000LL /* Long word count */ +#define RH20_DR_EXC 0000000200000LL /* Exception */ +#define RH20_DATA_PRI 0000000400000LL /* Data parity error */ +#define RH20_SBAR 0000001000000LL /* SBAR set */ +#define RH20_XEND 0000002000000LL /* Transfer ended */ + +/* 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 */ + +/* RH20 CONO Flags */ +#define RH20_DELETE_SCR 0000000000100LL /* Clear SCR */ +#define RH20_RCLP 0000000000200LL /* Reset command list */ +#define RH20_MASS_EN 0000000000400LL /* Mass bus enable */ +#define RH20_XFER_CLR 0000000001000LL /* Clear XFER error */ +#define RH20_CLR_MBC 0000000002000LL /* Clear MBC */ +#define RH20_CLR_RAE 0000000004000LL /* Clear RAE error */ + +/* 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 0000000000777LL /* Interupt vector */ +#define IRQ_KI10 0000002000000LL +#define IRQ_KA10 0000001000000LL +#define FNC_XFER 024 /* >=? data xfr */ + +/* Status register settings */ +#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 */ + +/* RH20 channel status flags */ +#define RH20_MEM_PAR 00200000000000LL /* Memory parity error */ +#define RH20_NADR_PAR 00100000000000LL /* Address parity error */ +#define RH20_NOT_WC0 00040000000000LL /* Word count not zero */ +#define RH20_NXM_ERR 00020000000000LL /* Non existent memory */ +#define RH20_LAST_ERR 00000400000000LL /* Last transfer error */ +#define RH20_ERROR 00000200000000LL /* RH20 error */ +#define RH20_LONG_STS 00000100000000LL /* Did not reach wc */ +#define RH20_SHRT_STS 00000040000000LL /* WC reached zero */ +#define RH20_OVER 00000020000000LL /* Overrun error */ + +/* 0-37 mass bus register. + 70 SBAR, block address. + 71 STCR, neg block count, func + 72 PBAR + 73 PTCR + 74 IVIR Interrupt vector address. + 75 Diags read. + 76 Diags write. + 77 Status (tra,cb test, bar test, ev par, r/w, exc,ebl, 0, attn, sclk +*/ + +/* + * CCW 000..... New channel comand list pointer HALT. + 010..... Next CCW Address JUMP + 1xycount-address. x=halt last xfer, y=reverse +*/ + +extern uint32 eb_ptr; + +t_stat +rh_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + dibp = (DIB *) dptr->ctxt; + dptr->flags &= ~DEV_M_RH; + dptr->flags |= val; + dibp->dev_num &= ~(RH10_DEV|RH20_DEV); + dibp->dev_num |= (val) ? RH20_DEV: RH10_DEV; + return SCPE_OK; +} + +t_stat rh_show_type (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 & TYPE_RH20) ? "RH20" : "RH10"); + return SCPE_OK; +} + + +t_stat rh_devio(uint32 dev, uint64 *data) { + DEVICE *dptr = NULL; + struct rh_if *rhc = NULL; + int drive; + + for (drive = 0; rh[drive].dev_num != 0; drive++) { + if (rh[drive].dev_num == (dev & 0774)) { + rhc = rh[drive].rh; + dptr = rh[drive].dev; + break; + } + } + if (rhc == NULL) + return SCPE_OK; +#if KL + if (dptr->flags & TYPE_RH20) { + switch(dev & 3) { + case CONI: + *data = rhc->status & RMASK; + if (rhc->attn != 0) + *data |= RH20_ATTN; + if (rhc->rae != 0) + *data |= RH20_RAE; + sim_debug(DEBUG_CONI, dptr, "%s %03o CONI %06o PC=%o %o\n", + dptr->name, dev, (uint32)*data, PC, rhc->attn); + return SCPE_OK; + + case CONO: + clr_interrupt(dev); + /* Clear flags */ + if (*data & RH20_CLR_MBC) { + if (rhc->dev_reset != NULL) + rhc->dev_reset(dptr); + rhc->attn = 0; + rhc->imode = 2; + } + rhc->status &= ~(07LL|IADR_ATTN|RH20_MASS_EN); + rhc->status |= *data & (07LL|IADR_ATTN|RH20_MASS_EN); + if (*data & RH20_DELETE_SCR) + rhc->status &= ~(RH20_SBAR|RH20_SCR_FULL); + if (*data & (RH20_RCLP|RH20_CLR_MBC)) + rhc->cia = eb_ptr | (rhc->devnum - 0540); + if (*data & (RH20_CLR_RAE|RH20_CLR_MBC)) + rhc->rae = 0; + if (*data & PI_ENABLE) + rhc->status &= ~PI_ENABLE; + if (((rhc->status & IADR_ATTN) != 0 && rhc->attn != 0) + || (rhc->status & PI_ENABLE)) + set_interrupt(rhc->devnum, rhc->status); + sim_debug(DEBUG_CONO, dptr, "%s %03o CONO %06o PC=%06o %06o\n", + dptr->name, dev, (uint32)*data, PC, rhc->status); + return SCPE_OK; + + case DATAI: + *data = 0; + if (rhc->status & BUSY && rhc->reg != 04) { + rhc->status |= CC_CHAN_ACT; + return SCPE_OK; + } + if (rhc->reg < 040) { + int parity; + *data = (uint64)(rhc->dev_read(dptr, rhc, rhc->reg) & 0177777); + parity = (int)((*data >> 8) ^ *data); + parity = (parity >> 4) ^ parity; + parity = (parity >> 2) ^ parity; + parity = ((parity >> 1) ^ parity) & 1; + *data |= ((uint64)(!parity)) << 16; + *data |= ((uint64)(rhc->drive)) << 18; + *data |= BIT10; + } else if ((rhc->reg & 070) != 070) { + rhc->rae = 1; + break; + } else { + switch(rhc->reg & 07) { + case 0: *data = rhc->sbar; break; + case 1: *data = rhc->stcr; break; + case 2: *data = rhc->pbar; break; + case 3: *data = rhc->ptcr; break; + case 4: *data = rhc->ivect; break; + case 5: + case 6: break; + case 7: *data = 0; break; + } + } + *data |= ((uint64)(rhc->reg)) << 30; + sim_debug(DEBUG_DATAIO, dptr, "%s %03o DATI %012llo %d PC=%06o\n", + dptr->name, dev, *data, rhc->drive, PC); + return SCPE_OK; + + case DATAO: + sim_debug(DEBUG_DATAIO, dptr, "%s %03o DATO %012llo PC=%06o %06o\n", + dptr->name, dev, *data, PC, rhc->status); + rhc->reg = ((int)(*data >> 30)) & 077; + rhc->imode |= 2; + if (rhc->reg < 040) + rhc->drive = (int)(*data >> 18) & 07; + if (*data & LOAD_REG) { + if (rhc->reg < 040) { + clr_interrupt(dev); + /* Check if access error */ + if (rhc->rae & (1 << rhc->drive) && (*data & BIT9) == 0) { + set_interrupt(rhc->devnum, rhc->status); + return SCPE_OK; + } + rhc->dev_write(dptr, rhc, rhc->reg & 037, (int)(*data & 0777777)); + if (((rhc->status & IADR_ATTN) != 0 && rhc->attn != 0) + || (rhc->status & PI_ENABLE)) + set_interrupt(rhc->devnum, rhc->status); + /* Check if access error */ + if (rhc->rae & (1 << rhc->drive) && (*data & BIT9) == 0) + set_interrupt(rhc->devnum, rhc->status); + else + rhc->rae &= ~(1 << rhc->drive); + } else if ((rhc->reg & 070) != 070) { + if ((*data & BIT9) == 0) { + rhc->rae = (1 << rhc->drive); + set_interrupt(rhc->devnum, rhc->status); + } + } else { + switch(rhc->reg & 07) { + case 0: + rhc->sbar = (*data) & (CR_DRIVE|RMASK); + rhc->status |= RH20_SBAR; + break; + case 1: + rhc->stcr = (*data) & (BIT10|BIT7|CR_DRIVE|RMASK); + rhc->status |= RH20_SCR_FULL; + break; + case 4: + rhc->ivect = (*data & IRQ_VECT); + break; + case 2: + case 3: + case 5: + case 6: + case 7: + break; + } + } + } + } + if ((rhc->status & (RH20_SCR_FULL|RH20_PCR_FULL)) == (RH20_SCR_FULL)) + rh20_setup(rhc); + return SCPE_OK; + } +#endif + switch(dev & 3) { + case CONI: + *data = rhc->status & ~(IADR_ATTN|IARD_RAE); + if (rhc->attn != 0 && (rhc->status & IADR_ATTN)) + *data |= IADR_ATTN; + if (rhc->rae != 0 && (rhc->status & IARD_RAE)) + *data |= IARD_RAE; +#if KI_22BIT + *data |= B22_FLAG; +#endif + sim_debug(DEBUG_CONI, dptr, "%s %03o CONI %06o PC=%o %o\n", + dptr->name, dev, (uint32)*data, PC, rhc->attn); + return SCPE_OK; + + case CONO: + clr_interrupt(dev); + rhc->status &= ~(07LL|IADR_ATTN|IARD_RAE); + rhc->status |= *data & (07LL|IADR_ATTN|IARD_RAE); + /* Clear flags */ + if (*data & CONT_RESET && rhc->dev_reset != NULL) + rhc->dev_reset(dptr); + if (*data & (DBPE_CLR|DR_EXC_CLR|CHN_CLR)) + rhc->status &= ~(*data & (DBPE_CLR|DR_EXC_CLR|CHN_CLR)); + if (*data & OVER_CLR) + rhc->status &= ~(DTC_OVER); + if (*data & CBOV_CLR) + rhc->status &= ~(DIB_CBOV); + if (*data & CXR_ILC) + rhc->status &= ~(CXR_ILFC|CXR_SD_RAE); + if (*data & WRT_CW) + rh_writecw(rhc, 0); + if (*data & PI_ENABLE) + rhc->status &= ~PI_ENABLE; + if (rhc->status & PI_ENABLE) + set_interrupt(dev, rhc->status); + if ((rhc->status & IADR_ATTN) != 0 && rhc->attn != 0) + set_interrupt(dev, rhc->status); + sim_debug(DEBUG_CONO, dptr, "%s %03o CONO %06o PC=%06o %06o\n", + dptr->name, dev, (uint32)*data, PC, rhc->status); + return SCPE_OK; + + case DATAI: + *data = 0; + if (rhc->status & BUSY && rhc->reg != 04) { + rhc->status |= CC_CHAN_ACT; + return SCPE_OK; + } + if (rhc->reg == 040) { + *data = (uint64)(rhc->dev_read(dptr, rhc, 0) & 077); + *data |= ((uint64)(rhc->cia)) << 6; + *data |= ((uint64)(rhc->xfer_drive)) << 18; + } else if (rhc->reg == 044) { + *data = (uint64)rhc->ivect; + if (rhc->imode) + *data |= IRQ_KI10; + else + *data |= IRQ_KA10; + } else if (rhc->reg == 054) { + *data = (uint64)(rhc->rae); + } else if ((rhc->reg & 040) == 0) { + int parity; + *data = (uint64)(rhc->dev_read(dptr, rhc, rhc->reg) & 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)(rhc->drive)) << 18; + } + *data |= ((uint64)(rhc->reg)) << 30; + sim_debug(DEBUG_DATAIO, dptr, "%s %03o DATI %012llo %d PC=%06o\n", + dptr->name, dev, *data, rhc->drive, PC); + return SCPE_OK; + + case DATAO: + sim_debug(DEBUG_DATAIO, dptr, "%s %03o DATO %012llo PC=%06o %06o\n", + dptr->name, dev, *data, PC, rhc->status); + rhc->reg = ((int)(*data >> 30)) & 077; + rhc->imode &= ~2; + if (rhc->reg < 040 && rhc->reg != 04) { + rhc->drive = (int)(*data >> 18) & 07; + } + if (*data & LOAD_REG) { + if (rhc->reg == 040) { + if ((*data & 1) == 0) { + return SCPE_OK; + } + + if (rhc->status & BUSY) { + rhc->status |= CC_CHAN_ACT; + return SCPE_OK; + } + + rhc->status &= ~(CCW_COMP_1|PI_ENABLE); + if (((*data >> 1) & 037) < FNC_XFER) { + rhc->status |= CXR_ILC; + rh_setirq(rhc); + sim_debug(DEBUG_DATAIO, dptr, + "%s %03o command abort %012llo, %d PC=%06o %06o\n", + dptr->name, dev, *data, rhc->drive, PC, rhc->status); + return SCPE_OK; + } + /* Check if access error */ + if (rhc->rae & (1 << rhc->drive)) + return SCPE_OK; + /* Start command */ + rh_setup(rhc, (uint32)(*data >> 6)); + rhc->xfer_drive = (int)(*data >> 18) & 07; + rhc->dev_write(dptr, rhc, 0, (uint32)(*data & 077)); + sim_debug(DEBUG_DATAIO, dptr, + "%s %03o command %012llo, %d PC=%06o %06o\n", + dptr->name, dev, *data, rhc->drive, PC, rhc->status); + } else if (rhc->reg == 044) { + /* Set KI10 Irq vector */ + rhc->ivect = (int)(*data & IRQ_VECT); + rhc->imode = (*data & IRQ_KI10) != 0; + } else if (rhc->reg == 050) { + ; /* Diagnostic access to mass bus. */ + } else if (rhc->reg == 054) { + /* clear flags */ + rhc->rae &= ~(*data & 0377); + if (rhc->rae == 0) + clr_interrupt(dev); + } else if ((rhc->reg & 040) == 0) { + rhc->drive = (int)(*data >> 18) & 07; + /* Check if access error */ + if (rhc->rae & (1 << rhc->drive)) { + return SCPE_OK; + } + rhc->dev_write(dptr, rhc, rhc->reg & 037, (int)(*data & 0777777)); + } + } + clr_interrupt(dev); + if (((rhc->status & (IADR_ATTN|BUSY)) == IADR_ATTN && rhc->attn != 0) + || (rhc->status & PI_ENABLE)) + set_interrupt(rhc->devnum, rhc->status); + return SCPE_OK; + } + return SCPE_OK; /* Unreached */ +} + +/* Handle KI and KL style interrupt vectors */ +t_addr +rh_devirq(uint32 dev, t_addr addr) { + DEVICE *dptr = NULL; + struct rh_if *rhc = NULL; + int drive; + + for (drive = 0; rh[drive].dev_num != 0; drive++) { + if (rh[drive].dev_num == (dev & 0774)) { + rhc = rh[drive].rh; + break; + } + } + if (rhc != NULL) { + if (rhc->imode == 1) /* KI10 Style */ + addr = RSIGN | rhc->ivect; + else if (rhc->imode == 2) /* RH20 style */ + addr = rhc->ivect; + } + return addr; +} + +/* Set the attention flag for a unit */ +void rh_setattn(struct rh_if *rhc, int unit) +{ + rhc->attn |= 1<status & BUSY) == 0 && (rhc->status & IADR_ATTN) != 0) + set_interrupt(rhc->devnum, rhc->status); +} + +void rh_error(struct rh_if *rhc) +{ + if (rhc->imode == 2) + rhc->status |= RH20_DR_EXC; +} + +/* Decrement block count for RH20, nop for RH10 */ +int rh_blkend(struct rh_if *rhc) +{ +#if KL + if (rhc->imode == 2) { +//fprintf(stderr, "RH blkend %o\n\r", rhc->cia); + rhc->cia = (rhc->cia + 1) & 01777; + if (rhc->cia == 0) { + rhc->status |= RH20_XEND; + return 1; + } + } +#endif + return 0; +} + +/* Set an IRQ for a DF10 device */ +void rh_setirq(struct rh_if *rhc) { + rhc->status |= PI_ENABLE; + set_interrupt(rhc->devnum, rhc->status); +} + +/* Generate the DF10 complete word */ +void rh_writecw(struct rh_if *rhc, int nxm) { + uint64 wrd1; +#if KL + if (rhc->imode == 2) { + uint32 chan = (rhc->devnum - 0540); + int wc = ((rhc->wcr ^ RH20_WMASK) + 1) & RH20_WMASK; + rhc->status |= RH20_CHAN_RDY; + rhc->status &= ~(RH20_PCR_FULL); + if (wc != 0 || (rhc->status & RH20_XEND) == 0 || + (rhc->ptcr & BIT10) != 0 || nxm) { + uint64 wrd2; + wrd1 = SMASK|(uint64)(rhc->ccw); + if ((rhc->ptcr & BIT10) == 0 || (rhc->status & RH20_DR_EXC) != 0) + return; + if (nxm) { + wrd1 |= RH20_NXM_ERR; + rhc->status |= RH20_CHAN_ERR; + } + if (wc != 0) { + wrd1 |= RH20_NOT_WC0; + if (rhc->status & RH20_XEND) { + wrd1 |= RH20_LONG_STS; + if ((rhc->ptcr & 070) == 060) { /* Write command */ + rhc->status |= RH20_LONG_WC|RH20_CHAN_ERR; + } + } + } else if ((rhc->status & RH20_XEND) == 0) { + wrd1 |= RH20_SHRT_STS; + if ((rhc->ptcr & 070) == 060) { /* Write command */ + rhc->status |= RH20_SHRT_WC|RH20_CHAN_ERR; + } + } + /* No error and not storing */ + if ((rhc->status & RH20_CHAN_ERR) == 0 && (rhc->ptcr & BIT10) == 0) + return; + wrd1 |= RH20_NADR_PAR; + wrd2 = ((uint64)rhc->cop << 33) | (((uint64)wc) << CSHIFT) | + ((uint64)(rhc->cda) & AMASK); + (void)Mem_write_word(chan+1, &wrd1, 1); + (void)Mem_write_word(chan+2, &wrd2, 1); +//fprintf(stderr, "RH20 final %012llo %012llo %06o\n\r", wrd1, wrd2, wc); + } + return; + } +#endif + if (nxm) + rhc->status |= CXR_NXM; + rhc->status |= CCW_COMP_1; + if (rhc->wcr != 0) + rhc->cda++; + wrd1 = ((uint64)(rhc->ccw & WMASK) << CSHIFT) | ((uint64)(rhc->cda) & AMASK); + (void)Mem_write_word(rhc->cia|1, &wrd1, 0); +} + +/* Finish off a DF10 transfer */ +void rh_finish_op(struct rh_if *rhc, int nxm) { +#if KL + if (rhc->imode != 2) +#endif + rhc->status &= ~BUSY; + rh_writecw(rhc, nxm); + rh_setirq(rhc); +#if KL + if (rhc->imode == 2 && + (rhc->status & (RH20_SCR_FULL|RH20_PCR_FULL)) == (RH20_SCR_FULL) && + (rhc->status & (RH20_DR_EXC|RH20_CHAN_ERR)) == 0) + rh20_setup(rhc); +#endif +} + +#if KL +/* Set up for a RH20 transfer */ +void rh20_setup(struct rh_if *rhc) +{ + DEVICE *dptr = NULL; + int reg; + + for (reg = 0; rh[reg].dev_num != 0; reg++) { + if (rh[reg].rh == rhc) { + dptr = rh[reg].dev; + break; + } + } + if (dptr == 0) + return; + rhc->pbar = rhc->sbar; + rhc->ptcr = rhc->stcr; + /* Read drive status */ + rhc->drive = (rhc->ptcr >> 18) & 07; + rhc->status &= ~(RH20_DATA_OVR|RH20_CHAN_RDY|RH20_DR_RESP|RH20_CHAN_ERR|RH20_SHRT_WC|\ + RH20_LONG_WC|RH20_DR_EXC|RH20_SCR_FULL|PI_ENABLE|RH20_XEND); + rhc->status |= RH20_PCR_FULL; + if (rhc->status & RH20_SBAR) { + rhc->drive = (rhc->pbar >> 18) & 07; + if (rhc->dev_write != NULL) + rhc->dev_write(dptr, rhc, 5, (rhc->pbar & 0177777)); + rhc->status &= ~RH20_SBAR; + } + if (rhc->ptcr & BIT7) { /* If RCPL reset I/O pointers */ + rhc->ccw = eb_ptr + (rhc->devnum - 0540); + rhc->wcr = 0; + } + /* Hold block count in cia */ + rhc->drive = (rhc->ptcr >> 18) & 07; + rhc->cia = (rhc->ptcr >> 6) & 01777; + if (rhc->dev_write != NULL) + rhc->dev_write(dptr, rhc, 0, (rhc->ptcr & 077)); + rhc->cop = 0; + rhc->wcr = 0; + rhc->status &= ~RH20_CHAN_RDY; +//fprintf(stderr, "RH setup %06o %06o %o\n\r", rhc->ptcr, rhc->ccw, rhc->cia); +} +#endif + +/* Setup for a DF10 transfer */ +void rh_setup(struct rh_if *rhc, uint32 addr) +{ + rhc->cia = addr & ICWA; + rhc->ccw = rhc->cia; + rhc->wcr = 0; + rhc->status |= BUSY; +} + + +/* Fetch the next IO control word */ +int rh_fetch(struct rh_if *rhc) { + uint64 data; + int reg; + DEVICE *dptr = NULL; + + for (reg = 0; rh[reg].dev_num != 0; reg++) { + if (rh[reg].rh == rhc) { + dptr = rh[reg].dev; + break; + } + } +#if KL + if (rhc->imode == 2 && (rhc->cop & 2) != 0) { + return 0; + } +#endif + if (Mem_read_word(rhc->ccw, &data, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + sim_debug(DEBUG_EXP, dptr, "%s fetch %06o %012llo\n\r", dptr->name, rhc->ccw, data); +#if KL + if (rhc->imode == 2) { + while((data & RH20_XFER) == 0) { + rhc->ccw = (uint32)(data & AMASK); + if ((data & (BIT1|BIT2)) == 0) { + return 0; + } + if (Mem_read_word(rhc->ccw, &data, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + sim_debug(DEBUG_EXP, dptr, "%s fetch2 %06o %012llo\n\r", dptr->name, rhc->ccw, data); +//fprintf(stderr, "RH20 fetch2 %06o %012llo\n\r", rhc->ccw, data); + } + rhc->wcr = (((data >> CSHIFT) & RH20_WMASK) ^ WMASK) + 1; + rhc->cda = (data & AMASK); + rhc->cop = (data >> 33) & 07; + rhc->ccw = (uint32)((rhc->ccw + 1) & AMASK); + return 1; + } +#endif + while((data & (WMASK << CSHIFT)) == 0) { + if ((data & AMASK) == 0 || (uint32)(data & AMASK) == rhc->ccw) { + rh_finish_op(rhc, 0); + return 0; + } + rhc->ccw = (uint32)(data & AMASK); + if (Mem_read_word(rhc->ccw, &data, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + sim_debug(DEBUG_EXP, dptr, "%s fetch2 %06o %012llo\n\r", dptr->name, rhc->ccw, data); + } + rhc->wcr = (uint32)((data >> CSHIFT) & WMASK); + rhc->cda = (uint32)(data & AMASK); + rhc->ccw = (uint32)((rhc->ccw + 1) & AMASK); + return 1; +} + +/* Read next word */ +int rh_read(struct rh_if *rhc) { + uint64 data; + if (rhc->wcr == 0) { + if (!rh_fetch(rhc)) + return 0; + } + rhc->wcr = (uint32)((rhc->wcr + 1) & WMASK); + if (rhc->cda != 0) { + if (rhc->cda > MEMSIZE) { + rh_finish_op(rhc, 1); + return 0; + } +#if KL + if (rhc->imode == 2) { + if (Mem_read_word(rhc->cda, &data, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + if (rhc->cop & 01) + rhc->cda = (uint32)((rhc->cda - 1) & AMASK); + else + rhc->cda = (uint32)((rhc->cda + 1) & AMASK); + } else { + rhc->cda = (uint32)((rhc->cda + 1) & AMASK); + if (Mem_read_word(rhc->cda, &data, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + } +#else + rhc->cda = (uint32)((rhc->cda + 1) & AMASK); + if (Mem_read_word(rhc->cda, &data, 0)) { + rh_finish_op(rhc, 1); + return 0; + } +#endif + } else { + data = 0; + } + rhc->buf = data; + if (rhc->wcr == 0) { + return rh_fetch(rhc); + } + return 1; +} + +/* Write next word */ +int rh_write(struct rh_if *rhc) { + if (rhc->wcr == 0) { + if (!rh_fetch(rhc)) + return 0; + } + rhc->wcr = (uint32)((rhc->wcr + 1) & WMASK); + if (rhc->cda != 0) { + if (rhc->cda > MEMSIZE) { + rh_finish_op(rhc, 1); + return 0; + } +#if KL + if (rhc->imode == 2) { + if (Mem_write_word(rhc->cda, &rhc->buf, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + if (rhc->cop & 01) + rhc->cda = (uint32)((rhc->cda - 1) & AMASK); + else + rhc->cda = (uint32)((rhc->cda + 1) & AMASK); + } else { + rhc->cda = (uint32)((rhc->cda + 1) & AMASK); + if (Mem_write_word(rhc->cda, &rhc->buf, 0)) { + rh_finish_op(rhc, 1); + return 0; + } + } +#else + rhc->cda = (uint32)((rhc->cda + 1) & AMASK); + if (Mem_write_word(rhc->cda, &rhc->buf, 0)) { + rh_finish_op(rhc, 1); + return 0; + } +#endif + } + if (rhc->wcr == 0) { + return rh_fetch(rhc); + } + return 1; +} + diff --git a/PDP10/kx10_rp.c b/PDP10/kx10_rp.c index fe6a77f0..1a101770 100644 --- a/PDP10/kx10_rp.c +++ b/PDP10/kx10_rp.c @@ -1,6 +1,6 @@ -/* ka10_rp.c: Dec RH10 RP04/5/6 +/* kx10_rp.c: DEC Massbus RP04/5/6 - Copyright (c) 2013-2017, Richard Cornwell + Copyright (c) 2013-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -22,6 +22,7 @@ */ #include "kx10_defs.h" +#include "kx10_disk.h" #ifndef NUM_DEVS_RP #define NUM_DEVS_RP 0 @@ -32,84 +33,22 @@ #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_GO 1 /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) @@ -191,7 +130,7 @@ /* RPDT - 06 - drive type */ /* RPLA - 07 - look ahead register */ - +#define LA_REG us9 #define LA_V_SC 6 /* sector pos */ /* RPER2 - 10 - error status 2 - drive unsafe conditions - unimplemented */ @@ -283,21 +222,11 @@ struct drvtyp rp_drv_tab[] = { }; -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); +void rp_write(DEVICE *dptr, struct rh_if *rh, int reg, uint32 data); +uint32 rp_read(DEVICE *dptr, struct rh_if *rh, int reg); +void rp_rst(DEVICE *dptr); t_stat rp_svc(UNIT *); t_stat rp_boot(int32, DEVICE *); void rp_ini(UNIT *, t_bool); @@ -308,118 +237,131 @@ 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); +uint64 rp_buf[NUM_DEVS_RP][RP_NUMWD]; UNIT rp_unit[] = { /* Controller 1 */ { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(0), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(0), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(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) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(1), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(1), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(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) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(2), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(2), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(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) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL(3), RP06_SIZE) }, + UNIT_ROABLE+DTYPE(RP06_DTYPE)+CNTRL_RH(3), RP06_SIZE) }, #endif #endif #endif }; +struct rh_if rp_rh[NUM_DEVS_RP] = { + { &rp_write, &rp_read, &rp_rst}, + { &rp_write, &rp_read, &rp_rst}, + { &rp_write, &rp_read, &rp_rst}, + { &rp_write, &rp_read, &rp_rst} +}; + 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}}; + {RH10_DEV, 1, &rh_devio, &rh_devirq, &rp_rh[0]}, + {RH10_DEV, 1, &rh_devio, &rh_devirq, &rp_rh[1]}, + {RH10_DEV, 1, &rh_devio, &rh_devirq, &rp_rh[2]}, + {RH10_DEV, 1, &rh_devio, &rh_devirq, &rp_rh[3]}}; + MTAB rp_mod[] = { +#if KL + {MTAB_XTD|MTAB_VDV, TYPE_RH10, NULL, "RH10", &rh_set_type, NULL, + NULL, "Sets controller to RH10" }, + {MTAB_XTD|MTAB_VDV, TYPE_RH20, "RH20", "RH20", &rh_set_type, &rh_show_type, + NULL, "Sets controller to RH20"}, +#endif {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 }, + {MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", NULL, &disk_show_fmt }, {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}, + {ORDATA(IVECT, rp_rh[0].ivect, 18)}, + {FLDATA(IMODE, rp_rh[0].imode, 0)}, + {ORDATA(XFER, rp_rh[0].xfer_drive, 3), REG_HRO}, + {ORDATA(DRIVE, rp_rh[0].drive, 3), REG_HRO}, + {ORDATA(REG, rp_rh[0].reg, 6), REG_RO}, + {ORDATA(RAE, rp_rh[0].rae, 8), REG_RO}, + {ORDATA(ATTN, rp_rh[0].attn, 8), REG_RO}, + {ORDATA(STATUS, rp_rh[0].status, 18), REG_RO}, + {ORDATA(CIA, rp_rh[0].cia, 18)}, + {ORDATA(CCW, rp_rh[0].ccw, 18)}, + {ORDATA(WCR, rp_rh[0].wcr, 18)}, + {ORDATA(CDA, rp_rh[0].cda, 18)}, + {ORDATA(DEVNUM, rp_rh[0].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_rh[0].buf, 36), REG_HRO}, {BRDATA(BUFF, rp_buf[0], 16, 64, RP_NUMWD), REG_HRO}, {0} }; @@ -434,22 +376,20 @@ DEVICE rpa_dev = { #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}, + {ORDATA(IVECT, rp_rh[1].ivect, 18)}, + {FLDATA(IMODE, rp_rh[1].imode, 0)}, + {ORDATA(XFER, rp_rh[1].xfer_drive, 3), REG_HRO}, + {ORDATA(DRIVE, rp_rh[1].drive, 3), REG_HRO}, + {ORDATA(REG, rp_rh[1].reg, 6), REG_RO}, + {ORDATA(RAE, rp_rh[1].rae, 8), REG_RO}, + {ORDATA(ATTN, rp_rh[1].attn, 8), REG_RO}, + {ORDATA(STATUS, rp_rh[1].status, 18), REG_RO}, + {ORDATA(CIA, rp_rh[1].cia, 18)}, + {ORDATA(CCW, rp_rh[1].ccw, 18)}, + {ORDATA(WCR, rp_rh[1].wcr, 18)}, + {ORDATA(CDA, rp_rh[1].cda, 18)}, + {ORDATA(DEVNUM, rp_rh[1].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_rh[1].buf, 36), REG_HRO}, {BRDATA(BUFF, rp_buf[1], 16, 64, RP_NUMWD), REG_HRO}, {0} }; @@ -464,22 +404,20 @@ DEVICE rpb_dev = { #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}, + {ORDATA(IVECT, rp_rh[2].ivect, 18)}, + {FLDATA(IMODE, rp_rh[2].imode, 0)}, + {ORDATA(XFER, rp_rh[2].xfer_drive, 3), REG_HRO}, + {ORDATA(DRIVE, rp_rh[2].drive, 3), REG_HRO}, + {ORDATA(REG, rp_rh[2].reg, 6), REG_RO}, + {ORDATA(RAE, rp_rh[2].rae, 8), REG_RO}, + {ORDATA(ATTN, rp_rh[2].attn, 8), REG_RO}, + {ORDATA(STATUS, rp_rh[2].status, 18), REG_RO}, + {ORDATA(CIA, rp_rh[2].cia, 18)}, + {ORDATA(CCW, rp_rh[2].ccw, 18)}, + {ORDATA(WCR, rp_rh[2].wcr, 18)}, + {ORDATA(CDA, rp_rh[2].cda, 18)}, + {ORDATA(DEVNUM, rp_rh[2].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_rh[2].buf, 36), REG_HRO}, {BRDATA(BUFF, rp_buf[2], 16, 64, RP_NUMWD), REG_HRO}, {0} }; @@ -494,22 +432,20 @@ DEVICE rpc_dev = { #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}, + {ORDATA(IVECT, rp_rh[3].ivect, 18)}, + {FLDATA(IMODE, rp_rh[3].imode, 0)}, + {ORDATA(XFER, rp_rh[3].xfer_drive, 3), REG_HRO}, + {ORDATA(DRIVE, rp_rh[3].drive, 3), REG_HRO}, + {ORDATA(REG, rp_rh[3].reg, 6), REG_RO}, + {ORDATA(RAE, rp_rh[3].rae, 8), REG_RO}, + {ORDATA(ATTN, rp_rh[3].attn, 8), REG_RO}, + {ORDATA(STATUS, rp_rh[3].status, 18), REG_RO}, + {ORDATA(CIA, rp_rh[3].cia, 18)}, + {ORDATA(CCW, rp_rh[3].ccw, 18)}, + {ORDATA(WCR, rp_rh[3].wcr, 18)}, + {ORDATA(CDA, rp_rh[3].cda, 18)}, + {ORDATA(DEVNUM, rp_rh[3].devnum, 9), REG_HRO}, + {ORDATA(BUF, rp_rh[3].buf, 36), REG_HRO}, {BRDATA(BUFF, rp_buf[3], 16, 64, RP_NUMWD), REG_HRO}, {0} }; @@ -540,214 +476,48 @@ DEVICE *rp_devs[] = { }; -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_rst(DEVICE *dptr) +{ + UNIT *uptr=dptr->units; + int drive; + 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; + } } void -rp_write(int ctlr, int unit, int reg, uint32 data) { +rp_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { int i; - DEVICE *dptr = rp_devs[ctlr]; + int unit = rhc->drive; UNIT *uptr = &dptr->units[unit]; - struct df10 *df10 = &rp_df10[ctlr]; int dtype = GET_DTYPE(uptr->flags); - if ((uptr->CMD & CR_GO) && reg != 04) { + if ((uptr->CMD & CS1_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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o Status=%06o\n", dptr->name, unit, 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o not ready\n", dptr->name, unit); 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o no go\n", dptr->name, unit); return; /* No, nop */ } uptr->CMD &= DS_ATA|DS_VV|DS_DPR|DS_MOL|DS_WRL; @@ -779,16 +549,13 @@ rp_write(int ctlr, int unit, int reg, uint32 data) { 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<attn &= ~(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; + uptr->CMD |= CS1_GO; CLR_BUF(uptr); uptr->DATAPTR = 0; break; @@ -796,14 +563,12 @@ rp_write(int ctlr, int unit, int reg, uint32 data) { case FNC_DCLR: /* drive clear */ uptr->CMD |= DS_DRY; - uptr->CMD &= ~(DS_ATA|CR_GO); + uptr->CMD &= ~(DS_ATA|CS1_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); + rhc->attn &= ~(1<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); + rhc->attn |= (1<CMD & CR_GO) + if (uptr->CMD & CS1_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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, uptr->CMD); return; case 001: /* status */ break; @@ -849,14 +607,11 @@ rp_write(int ctlr, int unit, int reg, uint32 data) { case 004: /* atten summary */ for (i = 0; i < 8; i++) { if (data & (1<units[i]; + u->CMD &= ~DS_ATA; + rhc->attn &= ~(1<devnum); break; case 005: /* sector/track */ uptr->DA &= 0177777; @@ -892,15 +647,14 @@ rp_write(int ctlr, int unit, int reg, uint32 data) { break; default: uptr->CMD |= (ER1_ILR<<16)|DS_ERR; - rp_rae[ctlr] &= ~(1<rae |= 1 << unit; } } uint32 -rp_read(int ctlr, int unit, int reg) { - DEVICE *dptr = rp_devs[ctlr]; +rp_read(DEVICE *dptr, struct rh_if *rhc, int reg) { + int unit = rhc->drive; UNIT *uptr = &dptr->units[unit]; - struct df10 *df10; uint32 temp = 0; int i; @@ -909,11 +663,10 @@ rp_read(int ctlr, int unit, int reg) { } 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) + if (uptr->CMD & CS1_GO) temp |= CS1_GO; break; case 001: /* status */ @@ -926,7 +679,8 @@ rp_read(int ctlr, int unit, int reg) { break; case 004: /* atten summary */ for (i = 0; i < 8; i++) { - if (rp_unit[(ctlr * 8) + i].CMD & DS_ATA) { + UNIT *u = &dptr->units[i]; + if (u->CMD & DS_ATA) { temp |= 1 << i; } } @@ -947,7 +701,8 @@ rp_read(int ctlr, int unit, int reg) { temp = uptr->CCYL & 0177777; break; case 010: /* serial no */ - temp = (020 * ctlr) + (unit + 1); + i = GET_CNTRL_RH(uptr->flags); + temp = (020 * i) + (unit + 1); break; case 014: /* error register 2 */ temp = uptr->ERR2; @@ -956,12 +711,16 @@ rp_read(int ctlr, int unit, int reg) { temp = uptr->ERR3; break; case 007: /* look ahead */ + uptr->LA_REG += 0100; + uptr->LA_REG &= 07700; + temp = uptr->LA_REG; + break; case 016: /* ecc position */ case 017: /* ecc pattern */ break; default: uptr->CMD |= (ER1_ILR<<16); - rp_rae[ctlr] &= ~(1<rae |= 1 << unit; } return temp; } @@ -969,34 +728,35 @@ rp_read(int ctlr, int unit, int reg) { t_stat rp_svc (UNIT *uptr) { - int dtype = GET_DTYPE(uptr->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; + int dtype = GET_DTYPE(uptr->flags); + int ctlr = GET_CNTRL_RH(uptr->flags); + int cyl = GET_CY(uptr->DA); + int unit; + DEVICE *dptr; + struct rh_if *rhc; + int diff, da; + int sts; - /* Find dptr, and df10 */ dptr = rp_devs[ctlr]; + rhc = &rp_rh[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); + rh_setirq(rhc); + } else { + rh_setattn(rhc, unit); } 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o seek %d %d\n", dptr->name, 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; + rh_setattn(rhc, unit); } diff = cyl - (uptr->CCYL & 01777); if (diff < 0) { @@ -1048,75 +808,65 @@ t_stat rp_svc (UNIT *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; - 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); + uptr->CMD &= ~CS1_GO; + rh_setattn(rhc, unit); + sim_debug(DEBUG_DETAIL, dptr, "%s%o seekdone %d %o\n", dptr->name, 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); + uptr->CMD &= ~CS1_GO; + rh_setattn(rhc, unit); + sim_debug(DEBUG_DETAIL, dptr, "%s%o searchdone %d %o\n", dptr->name, 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o read error\n", dptr->name, 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); + uptr->CMD &= ~CS1_GO; + rh_finish_op(rhc, 0); + sim_debug(DEBUG_DETAIL, dptr, "%s%o readx done\n", dptr->name, unit); return SCPE_OK; } - sim_debug(DEBUG_DETAIL, dptr, "RP%o read (%d,%d,%d)\n", unit, cyl, + sim_debug(DEBUG_DETAIL, dptr, "%s%o read (%d,%d,%d)\n", dptr->name, 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; + da = GET_DA(uptr->DA, dtype); + (void)disk_read(uptr, &rp_buf[ctlr][0], da, RP_NUMWD); 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) | + rhc->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) + sim_debug(DEBUG_DATA, dptr, "%s%o read word h1 %012llo %09o %06o\n", + dptr->name, unit, rhc->buf, rhc->cda, rhc->wcr); + if (rh_write(rhc) == 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) + rhc->buf = ((uint64)((020 * ctlr) + (unit + 1)) << 18) | (uint64)(unit); + sim_debug(DEBUG_DATA, dptr, "%s%o read word h2 %012llo %09o %06o\n", + dptr->name, unit, rhc->buf, rhc->cda, rhc->wcr); + if (rh_write(rhc) == 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)) { + rhc->buf = rp_buf[ctlr][uptr->DATAPTR++]; + sim_debug(DEBUG_DATA, dptr, "%s%o read word %d %012llo %09o %06o\n", + dptr->name, unit, uptr->DATAPTR, rhc->buf, rhc->cda, rhc->wcr); + if (rh_write(rhc)) { if (uptr->DATAPTR == RP_NUMWD) { /* Increment to next sector. Set Last Sector */ uptr->DATAPTR = 0; @@ -1131,14 +881,18 @@ t_stat rp_svc (UNIT *uptr) uptr->CMD |= DS_PIP; } } + if (rh_blkend(rhc)) + goto rd_end; } - sim_activate(uptr, 50); + sim_activate(uptr, 10); } else { rd_end: - sim_debug(DEBUG_DETAIL, dptr, "RP%o read done\n", unit); + sim_debug(DEBUG_DETAIL, dptr, "%s%o read done\n", dptr->name, unit); uptr->CMD |= DS_DRY; - uptr->CMD &= ~CR_GO; - df10_finish_op(df, 0); + uptr->CMD &= ~CS1_GO; + if (uptr->DATAPTR == RP_NUMWD) + (void)rh_blkend(rhc); + rh_finish_op(rhc, 0); return SCPE_OK; } break; @@ -1146,7 +900,7 @@ rd_end: 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o read error\n", dptr->name, unit); goto wr_end; } @@ -1154,41 +908,41 @@ rd_end: 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); + uptr->CMD &= ~CS1_GO; + rh_finish_op(rhc, 0); + sim_debug(DEBUG_DETAIL, dptr, "%s%o writex done\n", dptr->name, unit); return SCPE_OK; } /* On Write headers, transfer 2 words to start */ if (GET_FNC(uptr->CMD) == FNC_WRITEH) { - if (df10_read(df) == 0) + if (rh_read(rhc) == 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) + sim_debug(DEBUG_DATA, dptr, "%s%o write word h1 %012llo %06o\n", + dptr->name, unit, rhc->buf, rhc->wcr); + if (rh_read(rhc) == 0) goto wr_end; - sim_debug(DEBUG_DATA, dptr, "RP%o write word h2 %012llo %06o\n", - unit, df->buf, df->wcr); + sim_debug(DEBUG_DATA, dptr, "%s%o write word h2 %012llo %06o\n", + dptr->name, unit, rhc->buf, rhc->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) { + sts = rh_read(rhc); + sim_debug(DEBUG_DATA, dptr, "%s%o write word %d %012llo %06o %06o\n", + dptr->name, unit, uptr->DATAPTR, rhc->buf, rhc->cda, rhc->wcr); + rp_buf[ctlr][uptr->DATAPTR++] = rhc->buf; + if (sts == 0) { 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); + } + if (uptr->DATAPTR == RP_NUMWD) { + sim_debug(DEBUG_DETAIL, dptr, "%s%o write (%d,%d,%d)\n", dptr->name, + unit, cyl, GET_SF(uptr->DA), GET_SC(uptr->DA)); + da = GET_DA(uptr->DA, dtype); + (void)disk_write(uptr, &rp_buf[ctlr][0], da, RP_NUMWD); uptr->DATAPTR = 0; CLR_BUF(uptr); - if (r) { + if (sts) { 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); @@ -1199,16 +953,18 @@ rd_end: uptr->CMD |= DS_PIP; } } - } + } + if (rh_blkend(rhc)) + goto wr_end; } - if (r) { - sim_activate(uptr, 50); + if (sts) { + sim_activate(uptr, 10); } 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); + uptr->CMD &= ~CS1_GO; + rh_finish_op(rhc, 0); return SCPE_OK; } break; @@ -1236,12 +992,9 @@ 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; + rp_rh[ctlr].status = 0; + rp_rh[ctlr].attn = 0; + rp_rh[ctlr].rae = 0; } return SCPE_OK; } @@ -1251,19 +1004,29 @@ 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; + int ctlr = GET_CNTRL_RH(uptr->flags); + struct rh_if *rhc = &rp_rh[ctlr]; + DEVICE *dptr = rp_devs[ctlr]; 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; + uint64 word; +#if KL + int sect; + /* KL does not support readin, so fake it by reading in sectors 4 to 7 */ + /* Possible in future find boot loader in FE file system */ + addr = (MEMSIZE - 512) & RMASK; + for (sect = 4; sect <= 7; sect++) { + disk_read(uptr, &rp_buf[0][0], sect, RP_NUMWD); + ptr = 0; + for(wc = RP_NUMWD; wc > 0; wc--) { + word = rp_buf[0][ptr++]; + M[addr++] = word; + } + } + word = (MEMSIZE - 512) & RMASK; +#else + disk_read(uptr, &rp_buf[0][0], 0, RP_NUMWD); addr = rp_buf[0][ptr] & RMASK; wc = (rp_buf[0][ptr++] >> 18) & RMASK; while (wc != 0) { @@ -1278,11 +1041,12 @@ rp_boot(int32 unit_num, DEVICE * rptr) 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; +#endif PC = word & RMASK; + uptr->CMD |= DS_VV; + rhc->reg = 040; + rhc->drive = uptr - dptr->units; + rhc->status |= CCW_COMP_1|PI_ENABLE; return SCPE_OK; } @@ -1296,21 +1060,26 @@ t_stat rp_attach (UNIT *uptr, CONST char *cptr) int ctlr; uptr->capac = rp_drv_tab[GET_DTYPE (uptr->flags)].size; - r = attach_unit (uptr, cptr); + r = disk_attach (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; + for (ctlr = 0; rh[ctlr].dev_num != 0; ctlr++) { + if (rh[ctlr].dev == rptr) + break; + } + if (uptr->flags & UNIT_WLK) + uptr->CMD |= DS_WRL; + if (sim_switches & SIM_SW_REST) + return SCPE_OK; 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); + rp_rh[ctlr].status |= PI_ENABLE; + set_interrupt(dib->dev_num, rp_rh[ctlr].status); return SCPE_OK; } @@ -1323,7 +1092,7 @@ t_stat rp_detach (UNIT *uptr) 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); + return disk_detach (uptr); } t_stat rp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) @@ -1333,6 +1102,7 @@ fprintf (st, "The RP controller implements the Massbus family of large disk driv 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"); +disk_attach_help(st, dptr, uptr, flag, cptr); 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"); diff --git a/PDP10/kx10_rs.c b/PDP10/kx10_rs.c index 0b589546..760c3f87 100644 --- a/PDP10/kx10_rs.c +++ b/PDP10/kx10_rs.c @@ -1,6 +1,6 @@ -/* ka10_rs.c: Dec RH10 RS04 +/* kx10_rs.c: DEC Massbus RS04 - Copyright (c) 2017, Richard Cornwell + Copyright (c) 2017-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -28,6 +28,8 @@ #endif #if (NUM_DEVS_RS > 0) +#define BUF_EMPTY(u) (u->hwmark == 0xFFFFFFFF) +#define CLR_BUF(u) u->hwmark = 0xFFFFFFFF #define RS_NUMWD 128 /* 36bit words/sec */ #define NUM_UNITS_RS 8 @@ -42,72 +44,14 @@ #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_GO 1 /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) @@ -228,21 +172,10 @@ struct drvtyp rs_drv_tab[] = { }; -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); +void rs_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data); +uint32 rs_read(DEVICE *dptr, struct rh_if *rhc, int reg); +void rs_rst(DEVICE *dptr); t_stat rs_svc(UNIT *); t_stat rs_boot(int32, DEVICE *); void rs_ini(UNIT *, t_bool); @@ -258,28 +191,38 @@ 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) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, { UDATA (&rs_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ - UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL(0), RS04_SIZE) }, + UNIT_ROABLE+DTYPE(RS04_DTYPE)+CNTRL_RH(0), RS04_SIZE) }, +}; + +struct rh_if rs_rh[] = { + { &rs_write, &rs_read, &rs_rst}, }; DIB rs_dib[] = { - {RH10_DEV, 1, &rs_devio, &rs_devirq} + {RH10_DEV, 1, &rh_devio, &rh_devirq, &rs_rh[0]} }; MTAB rs_mod[] = { +#if KL + {MTAB_XTD|MTAB_VDV, TYPE_RH10, NULL, "RH10", &rh_set_type, NULL, + NULL, "Sets controller to RH10" }, + {MTAB_XTD|MTAB_VDV, TYPE_RH20, "RH20", "RH20", &rh_set_type, &rh_show_type, + NULL, "Sets controller to RH20"}, +#endif {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 }, @@ -288,23 +231,20 @@ MTAB rs_mod[] = { }; 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}, + {ORDATA(IVECT, rs_rh[0].ivect, 18)}, + {FLDATA(IMODE, rs_rh[0].imode, 0)}, + {ORDATA(XFER, rs_rh[0].xfer_drive, 3), REG_HRO}, + {ORDATA(DRIVE, rs_rh[0].drive, 3), REG_HRO}, + {ORDATA(REG, rs_rh[0].reg, 6), REG_RO}, + {ORDATA(RAE, rs_rh[0].rae, 8), REG_RO}, + {ORDATA(ATTN, rs_rh[0].attn, 8), REG_RO}, + {ORDATA(STATUS, rs_rh[0].status, 18), REG_RO}, + {ORDATA(CIA, rs_rh[0].cia, 18)}, + {ORDATA(CCW, rs_rh[0].ccw, 18)}, + {ORDATA(WCR, rs_rh[0].wcr, 18)}, + {ORDATA(CDA, rs_rh[0].cda, 18)}, + {ORDATA(DEVNUM, rs_rh[0].devnum, 9), REG_HRO}, + {ORDATA(BUF, rs_rh[0].buf, 36), REG_HRO}, {BRDATA(BUFF, rs_buf[0], 16, 64, RS_NUMWD), REG_HRO}, {0} }; @@ -322,213 +262,44 @@ DEVICE *rs_devs[] = { }; -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_rst(DEVICE *dptr) +{ + UNIT *uptr=dptr->units; + int drive; + 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; + } } void -rs_write(int ctlr, int unit, int reg, uint32 data) { +rs_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { int i; - DEVICE *dptr = rs_devs[ctlr]; - struct df10 *df10 = &rs_df10[ctlr]; + int unit = rhc->drive; UNIT *uptr = &dptr->units[unit]; - if ((uptr->CMD & CR_GO) && reg != 04) { + if ((uptr->CMD & CS1_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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o Status=%06o\n", dptr->name, unit, 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o busy\n", dptr->name, unit); 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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o no go\n", dptr->name, unit); return; /* No, nop */ } uptr->CMD &= DS_ATA|DS_VV|DS_DPR|DS_MOL|DS_WRL; @@ -542,7 +313,8 @@ rs_write(int ctlr, int unit, int reg, uint32 data) { case FNC_WCHK: /* write check */ case FNC_WRITE: /* write */ case FNC_READ: /* read */ - uptr->CMD |= DS_PIP|CR_GO; + uptr->CMD |= DS_PIP|CS1_GO; + CLR_BUF(uptr); uptr->DATAPTR = 0; break; @@ -551,30 +323,26 @@ rs_write(int ctlr, int unit, int reg, uint32 data) { if ((uptr->flags & UNIT_ATT) != 0) uptr->CMD |= DS_VV; uptr->CMD |= DS_DRY; - df10_setirq(df10); + rh_setirq(rhc); break; case FNC_DCLR: /* drive clear */ uptr->CMD |= DS_DRY; - uptr->CMD &= ~(DS_ATA|CR_GO); - rs_attn[ctlr] = 0; - clr_interrupt(df10->devnum); + uptr->CMD &= ~(DS_ATA|CS1_GO); + rhc->attn = 0; + clr_interrupt(rhc->devnum); for (i = 0; i < 8; i++) { - if (rs_unit[(ctlr * 8) + i].CMD & DS_ATA) - rs_attn[ctlr] = 1; + if (dptr->units[i].CMD & DS_ATA) + rhc->attn |= 1 << i; } - 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) + if (uptr->CMD & CS1_GO) sim_activate(uptr, 100); - sim_debug(DEBUG_DETAIL, dptr, "RSA%o AStatus=%06o\n", unit, uptr->CMD); + sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, uptr->CMD); return; case 001: /* status */ break; @@ -587,17 +355,13 @@ rs_write(int ctlr, int unit, int reg, uint32 data) { case 003: /* maintenance */ break; case 004: /* atten summary */ - rs_attn[ctlr] = 0; + rhc->attn = 0; for (i = 0; i < 8; i++) { if (data & (1<units[i].CMD &= ~DS_ATA; + if (dptr->units[i].CMD & DS_ATA) + rhc->attn |= 1 << i; } - clr_interrupt(df10->devnum); - if (((df10->status & IADR_ATTN) != 0 && rs_attn[ctlr] != 0) || - (df10->status & PI_ENABLE)) - df10_setirq(df10); break; case 005: /* sector/track */ uptr->DA = data & 0177777; @@ -607,14 +371,13 @@ rs_write(int ctlr, int unit, int reg, uint32 data) { break; default: uptr->CMD |= (ER1_ILR<<16)|DS_ERR; - rs_rae[ctlr] &= ~(1<rae |= 1 << unit; } } uint32 -rs_read(int ctlr, int unit, int reg) { - DEVICE *dptr = rs_devs[ctlr]; - struct df10 *df10 = &rs_df10[ctlr]; +rs_read(DEVICE *dptr, struct rh_if *rhc, int reg) { + int unit = rhc->drive; UNIT *uptr = &dptr->units[unit]; uint32 temp = 0; int i; @@ -628,7 +391,7 @@ rs_read(int ctlr, int unit, int reg) { temp = uptr->CMD & 077; if (uptr->flags & UNIT_ATT) temp |= CS1_DVA; - if ((df10->status & BUSY) == 0 && (uptr->CMD & CR_GO) == 0) + if ((uptr->CMD & CS1_GO) == 0) temp |= CS1_GO; break; case 001: /* status */ @@ -639,7 +402,7 @@ rs_read(int ctlr, int unit, int reg) { break; case 004: /* atten summary */ for (i = 0; i < 8; i++) { - if (rs_unit[(ctlr * 8) + i].CMD & DS_ATA) { + if (dptr->units[i].CMD & DS_ATA) { temp |= 1 << i; } } @@ -655,7 +418,7 @@ rs_read(int ctlr, int unit, int reg) { break; default: uptr->CMD |= (ER1_ILR<<16); - rs_rae[ctlr] &= ~(1<rae |= 1 << unit; } return temp; } @@ -663,22 +426,21 @@ rs_read(int ctlr, int unit, int reg) { t_stat rs_svc (UNIT *uptr) { - int dtype = GET_DTYPE(uptr->flags); - int ctlr = GET_CNTRL(uptr->flags); - int unit; - DEVICE *dptr; - struct df10 *df; - int da; - t_stat r; + int dtype = GET_DTYPE(uptr->flags); + int ctlr = GET_CNTRL_RH(uptr->flags); + int unit; + DEVICE *dptr; + struct rh_if *rhc; + int da; + int sts; /* Find dptr, and df10 */ dptr = rs_devs[ctlr]; + rhc = &rs_rh[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); + rh_setirq(rhc); return (SCPE_OK); } @@ -693,42 +455,35 @@ t_stat rs_svc (UNIT *uptr) 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); + uptr->CMD &= ~CS1_GO; + rh_setattn(rhc, unit); + sim_debug(DEBUG_DETAIL, dptr, "%s%o seekdone\n", dptr->name, 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); + uptr->CMD &= ~CS1_GO; + rh_setattn(rhc, unit); + sim_debug(DEBUG_DETAIL, dptr, "%s%o searchdone\n", dptr->name, unit); break; case FNC_READ: /* read */ case FNC_WCHK: /* write check */ - if (uptr->DATAPTR == 0) { + if (BUF_EMPTY(uptr)) { 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); + uptr->CMD &= ~CS1_GO; + sim_debug(DEBUG_DETAIL, dptr, "%s%o readx done\n", dptr->name, unit); + rh_finish_op(rhc, 0); return SCPE_OK; } - sim_debug(DEBUG_DETAIL, dptr, "RSA%o read (%d,%d)\n", unit, + sim_debug(DEBUG_DETAIL, dptr, "%s%o read (%d,%d)\n", dptr->name, 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); @@ -737,15 +492,17 @@ t_stat rs_svc (UNIT *uptr) while (wc < RS_NUMWD) rs_buf[ctlr][wc++] = 0; uptr->hwmark = RS_NUMWD; + uptr->DATAPTR = 0; } - 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) { + rhc->buf = rs_buf[ctlr][uptr->DATAPTR++]; + sim_debug(DEBUG_DATA, dptr, "%s%o read word %d %012llo %09o %06o\n", + dptr->name, unit, uptr->DATAPTR, rhc->buf, rhc->cda, rhc->wcr); + if (rh_write(rhc)) { + if (uptr->DATAPTR == RS_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) >= rs_drv_tab[dtype].sect) { uptr->DA &= (DA_M_SF << DA_V_SF); @@ -753,43 +510,53 @@ t_stat rs_svc (UNIT *uptr) if (GET_SF(uptr->DA) >= rs_drv_tab[dtype].surf) uptr->CMD |= DS_LST; } + if (rh_blkend(rhc)) + goto rd_end; } - sim_activate(uptr, 20); + sim_activate(uptr, 10); } else { - sim_debug(DEBUG_DETAIL, dptr, "RSA%o read done\n", unit); +rd_end: + sim_debug(DEBUG_DETAIL, dptr, "%s%o read done\n", dptr->name, unit); uptr->CMD |= DS_DRY; - uptr->CMD &= ~CR_GO; - df10_finish_op(df, 0); + uptr->CMD &= ~CS1_GO; + if (uptr->DATAPTR == RS_NUMWD) + (void)rh_blkend(rhc); + rh_finish_op(rhc, 0); return SCPE_OK; } break; case FNC_WRITE: /* write */ - if (uptr->DATAPTR == 0) { + if (BUF_EMPTY(uptr)) { 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); + uptr->CMD &= ~CS1_GO; + sim_debug(DEBUG_DETAIL, dptr, "%s%o writex done\n", dptr->name, unit); + rh_finish_op(rhc, 0); return SCPE_OK; } + uptr->DATAPTR = 0; + uptr->hwmark = 0; } - 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) { + sts = rh_read(rhc); + rs_buf[ctlr][uptr->DATAPTR++] = rhc->buf; + sim_debug(DEBUG_DATA, dptr, "%s%o write word %d %012llo %09o %06o\n", + dptr->name, unit, uptr->DATAPTR, rhc->buf, rhc->cda, rhc->wcr); + if (sts == 0) { while (uptr->DATAPTR < RS_NUMWD) rs_buf[ctlr][uptr->DATAPTR++] = 0; - sim_debug(DEBUG_DETAIL, dptr, "RSA%o write (%d,%d)\n", unit, + } + if (uptr->DATAPTR == RS_NUMWD) { + sim_debug(DEBUG_DETAIL, dptr, "%s%o write (%d,%d)\n", dptr->name, 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) { + CLR_BUF(uptr); + if (sts) { uptr->DA += 1 << DA_V_SC; if (GET_SC(uptr->DA) >= rs_drv_tab[dtype].sect) { uptr->DA &= (DA_M_SF << DA_V_SF); @@ -798,14 +565,17 @@ t_stat rs_svc (UNIT *uptr) uptr->CMD |= DS_LST; } } + if (rh_blkend(rhc)) + goto wr_end; } - if (r) { - sim_activate(uptr, 20); + if (sts) { + sim_activate(uptr, 10); } else { - sim_debug(DEBUG_DETAIL, dptr, "RSA%o write done\n", unit); +wr_end: + sim_debug(DEBUG_DETAIL, dptr, "%s%o write done\n", dptr->name, unit); uptr->CMD |= DS_DRY; - uptr->CMD &= ~CR_GO; - df10_finish_op(df, 0); + uptr->CMD &= ~CS1_GO; + rh_finish_op(rhc, 0); return SCPE_OK; } break; @@ -833,12 +603,9 @@ 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; + rs_rh[ctlr].status = 0; + rs_rh[ctlr].attn = 0; + rs_rh[ctlr].rae = 0; } return SCPE_OK; } @@ -848,16 +615,16 @@ t_stat rs_boot(int32 unit_num, DEVICE * rptr) { UNIT *uptr = &rptr->units[unit_num]; - int ctlr = GET_CNTRL(uptr->flags); + int ctlr = GET_CNTRL_RH(uptr->flags); + struct rh_if *rhc; DEVICE *dptr; - struct df10 *df; uint32 addr; uint32 ptr = 0; uint64 word; int wc; - df = &rs_df10[ctlr]; dptr = rs_devs[ctlr]; + rhc = &rs_rh[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; @@ -875,9 +642,9 @@ rs_boot(int32 unit_num, DEVICE * rptr) 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; + rhc->reg = 040; + rhc->drive = uptr - dptr->units; + rhc->status |= CCW_COMP_1|PI_ENABLE; PC = word & RMASK; return SCPE_OK; @@ -901,14 +668,19 @@ t_stat rs_attach (UNIT *uptr, CONST char *cptr) if (rstr == 0) return SCPE_OK; dib = (DIB *) rstr->ctxt; - ctlr = dib->dev_num & 014; + for (ctlr = 0; rh[ctlr].dev_num != 0; ctlr++) { + if (rh[ctlr].dev == rstr) + break; + } + if (uptr->flags & UNIT_WLK) + uptr->CMD |= DS_WRL; + if (sim_switches & SIM_SW_REST) + return SCPE_OK; 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); + rs_rh[ctlr].status |= PI_ENABLE; + set_interrupt(dib->dev_num, rs_rh[ctlr].status); return SCPE_OK; } diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index d9e0faff..8b9e94af 100644 --- a/PDP10/kx10_sys.c +++ b/PDP10/kx10_sys.c @@ -1,9 +1,9 @@ -/* ka10_sys.c: PDP-10 simulator interface +/* kx10_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 + Copyright (c) 2011-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -42,11 +42,8 @@ sim_load binary loader */ -#if KLB -char sim_name[] = "KL-10B"; -#endif -#if KLA -char sim_name[] = "KL-10A"; +#if KL +char sim_name[] = "KL-10"; #endif #if KI char sim_name[] = "KI-10"; @@ -68,6 +65,9 @@ DEVICE *sim_devices[] = { #if PDP6 | KA | KI &cty_dev, #endif +#if KL + &dte_dev, +#endif #if (NUM_DEVS_PT > 0) &ptp_dev, &ptr_dev, @@ -75,6 +75,9 @@ DEVICE *sim_devices[] = { #if (NUM_DEVS_LP > 0) &lpt_dev, #endif +#if (NUM_DEVS_LP20 > 0) + &lp20_dev, +#endif #if (NUM_DEVS_CR > 0) &cr_dev, #endif @@ -141,6 +144,9 @@ DEVICE *sim_devices[] = { #if (NUM_DEVS_DC > 0) &dc_dev, #endif +#if (NUM_DEVS_TTY > 0) + &tty_dev, +#endif #if (NUM_DEVS_DCS > 0) &dcs_dev, #endif @@ -155,10 +161,19 @@ DEVICE *sim_devices[] = { #if (NUM_DEVS_WCNSLS > 0) &wcnsls_dev, #endif +#if (NUM_DEVS_OCNSLS > 0) + &ocnsls_dev, +#endif +#endif +#if (NUM_DEVS_III > 0) + &iii_dev, #endif #if NUM_DEVS_IMP > 0 &imp_dev, #endif +#if NUM_DEVS_NIA > 0 + &nia_dev, +#endif #if NUM_DEVS_CH10 > 0 &ch10_dev, #endif @@ -182,6 +197,9 @@ DEVICE *sim_devices[] = { #if NUM_DEVS_AUXCPU > 0 &auxcpu_dev, #endif +#if NUM_DEVS_SLAVE > 0 + &slave_dev, +#endif #if NUM_DEVS_DKB > 0 &dkb_dev, #endif @@ -197,7 +215,11 @@ DEVICE *sim_devices[] = { const char *sim_stop_messages[] = { "Unknown error", "HALT instruction", - "Breakpoint" + "Breakpoint", + "Invalid access", +#if MAGIC_SWITCH + "No magic" +#endif }; /* Simulator debug controls */ @@ -232,6 +254,7 @@ DEBTAB crd_debug[] = { #define FMT_E 3 /* EXE */ #define FMT_D 4 /* WAITS DMP */ #define FMT_I 5 /* ITS SBLK */ +#define FMT_B 6 /* EXB format */ #define EXE_DIR 01776 /* EXE directory */ #define EXE_VEC 01775 /* EXE entry vec */ @@ -510,19 +533,28 @@ return SCPE_OK; } -int get_word(FILE *fileref, uint64 *word) +int get_word(FILE *fileref, uint64 *word, int ftype) { - char cbuf[5]; + char cbuf[5]; - if (sim_fread(cbuf, 1, 5, fileref) != 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; + if (ftype) { + *word = ((uint64)(cbuf[0] & 0177) << 29) | + ((uint64)(cbuf[1] & 0177) << 22) | + ((uint64)(cbuf[2] & 0177) << 15) | + ((uint64)(cbuf[3] & 0177) << 8) | + ((uint64)(cbuf[4] & 0177) << 1); + if (cbuf[4] & 0200) + *word |= 1; + } else { + *word = ((uint64)(cbuf[0] & 0377) << 28) | + ((uint64)(cbuf[1] & 0377) << 20) | + ((uint64)(cbuf[2] & 0377) << 12) | + ((uint64)(cbuf[3] & 0377) << 4) | + (uint64)(cbuf[4] & 017); + } + return 0; } /* SAV file loader @@ -538,14 +570,14 @@ int get_word(FILE *fileref, uint64 *word) JRST start */ -t_stat load_sav (FILE *fileref) +t_stat load_sav (FILE *fileref, int ftype) { uint64 data; uint32 pa; int32 wc; for ( ;; ) { /* loop */ - if (get_word(fileref, &data)) + if (get_word(fileref, &data, ftype)) return SCPE_OK; wc = (int32)(data >> 18); pa = (uint32) (data & RMASK); @@ -559,7 +591,7 @@ t_stat load_sav (FILE *fileref) pa &= RMASK; wc++; wc &= RMASK; - if (get_word(fileref, &data)) + if (get_word(fileref, &data, ftype)) return SCPE_FMT; M[pa] = data; } /* end if count*/ @@ -594,7 +626,7 @@ t_stat load_sav (FILE *fileref) #define PAG_V_PN 9 #define DIRSIZ (2 * PAG_SIZE) -t_stat load_exe (FILE *fileref) +t_stat load_exe (FILE *fileref, int ftype) { uint64 data, dirbuf[DIRSIZ], pagbuf[PAG_SIZE], entbuf[2]; int32 ndir, entvec, i, j, k, cont, bsz, bty, rpt, wc; @@ -604,8 +636,9 @@ 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? */ + + wc = get_word(fileref, &data, ftype); + if (wc != 0) /* error? */ return SCPE_FMT; bsz = (int32) ((data & RMASK) - 1); /* get count */ if (bsz < 0) /* zero? */ @@ -616,9 +649,11 @@ do { 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; + for (i = 0; i < bsz; i++) { + if (get_word(fileref, &dirbuf[i], ftype)) + return SCPE_FMT; + } + ndir = bsz; break; case EXE_PDV: /* optional */ @@ -628,9 +663,11 @@ do { 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; + for (i = 0; i < bsz; i++) { + if (get_word(fileref, &entbuf[i], ftype)) + return SCPE_FMT; + } + entvec = bsz; cont = 0; /* stop */ break; @@ -651,10 +688,11 @@ for (i = 0; i < ndir; i = i + 2) { /* loop thru dir */ 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; + (void)sim_fseek (fileref, (fpage << PAG_V_PN) * 5, SEEK_SET); + for (k = 0; k < PAG_SIZE; k++) { + if (get_word(fileref, &pagbuf[k], ftype)) + break; + } fpage++; } ma = mpage << PAG_V_PN; /* mem addr */ @@ -667,18 +705,103 @@ for (i = 0; i < ndir; i = i + 2) { /* loop thru dir */ } /* end directory */ if (entvec && entbuf[1]) PC = (int32) (entbuf[1] & RMASK); /* start addr */ +else if (entvec == 0) + PC = (int32) (M[0120] & RMASK); return SCPE_OK; } +static int exb_pos = -1; +int get_exb_byte (FILE *fileref, int *byt, int ftype) +{ + static uint64 word; + + exb_pos++; + switch(exb_pos & 3) { + case 0: if (get_word(fileref, &word, ftype)) + return 1; + *byt = ((word >> 18) & 0377); + break; + case 1: *byt = ((word >> 26) & 0377); + break; + case 2: *byt = ((word >> 0) & 0377); + break; + case 3: *byt = ((word >> 8) & 0377); + break; + } + return 0; +} + +t_stat load_exb (FILE *fileref, int ftype) +{ + int odd = 0; + int pos = 0; + int wc; + int byt; + uint64 word; + t_addr addr; + + exb_pos = -1; + for ( ;; ) { + /* All records start on even byte */ + if (odd && get_exb_byte (fileref, &byt, ftype)) + return SCPE_FMT; + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + wc = byt; + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + wc |= byt << 8; + wc -= 4; + odd = wc & 1; + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + addr = byt; + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + addr |= byt << 8; + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + addr |= byt << 16; + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + addr |= byt << 24; + /* Empty record gives start address */ + if (wc == 0) { + PC = addr; + return SCPE_OK; + } + pos = 0; + for (; wc > 0; wc--, pos++) { + if (get_exb_byte(fileref, &byt, ftype)) + return SCPE_FMT; + switch(pos) { + case 0: word = ((uint64)byt); break; + case 1: word |= ((uint64)byt) << 8; break; + case 2: word |= ((uint64)byt) << 16; break; + case 3: word |= ((uint64)byt) << 24; break; + case 4: word |= ((uint64)(byt & 017)) << 32; + M[addr++] = word; + pos = -1; + break; + } + } + } + return SCPE_FMT; +} + /* Master loader */ t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) { uint64 data; int32 wc, fmt; +int ftype; extern int32 sim_switches; fmt = 0; /* no fmt */ +ftype = 0; +if (sim_switches & SWMASK ('C')) /* -c? core dump */ + ftype = 1; if (sim_switches & SWMASK ('R')) /* -r? */ fmt = FMT_R; else if (sim_switches & SWMASK ('S')) /* -s? */ @@ -689,12 +812,16 @@ else if (sim_switches & SWMASK ('D')) /* -d? */ fmt = FMT_D; else if (sim_switches & SWMASK ('I')) /* -i? */ fmt = FMT_I; +else if (sim_switches & SWMASK ('B')) /* -b? */ + fmt = FMT_B; 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, "EXB")) /* .EXB? */ + fmt = FMT_B; else if (match_ext (fnam, "DMP")) /* .DMP? */ fmt = FMT_D; else if (match_ext (fnam, "BIN")) /* .BIN? */ @@ -716,16 +843,19 @@ switch (fmt) { /* case fmt */ return load_rim (fileref); case FMT_S: /* SAV */ - return load_sav (fileref); + return load_sav (fileref, ftype); case FMT_E: /* EXE */ - return load_exe (fileref); + return load_exe (fileref, ftype); case FMT_D: /* DMP */ return load_dmp (fileref); case FMT_I: /* SBLK */ return load_sblk (fileref); + + case FMT_B: /* EXB */ + return load_exb (fileref, ftype); } printf ("Can't determine load file format\n"); @@ -759,13 +889,22 @@ static const char *opcode[] = { "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", +"CALL", "INITI", "MUUO42", "MUUO43", "MUUO44", "MUUO45", "MUUO46", "CALLI", +#if KL +"OPEN", "TTCALL", "PMOVE", "PMOVEM", "MUUO54", "RENAME", "IN", "OUT", +#else +"OPEN", "TTCALL", "MUUO52", "MUUO53", "MUUO54", "RENAME", "IN", "OUT", +#endif +"SETSTS", "STATO", "STATUS", "GETSTS", "INBUF", "OUTBUF", "INPUT", "OUTPUT", +"CLOSE", "RELEAS", "MTAPE", "UGETF", "USETI", "USETO", "LOOKUP", "ENTER", -"UJEN", "MUUO101", "MUUO102", "JSYS", "MUUO104", "MUUO105", "MUUO106", +#if KL +"UJEN", "GFAD", "GFSB", "JSYS", "ADJSP", "GFMP", "GFDV ", "DFAD", "DFSB", "DFMP", "DFDV", "DADD", "DSUB", "DMUL", "DDIV", +#else +"UJEN", "MUUO101", "MUUO102", "JSYS", "MUUO104", "MUUO105", "MUUO106", +"DFAD", "DFSB", "DFMP", "DFDV", "MUUO114", "MUUO115", "MUUO116", "MUUO117", +#endif "DMOVE", "DMOVN", "FIX", "EXTEND", "DMOVEM", "DMOVNM", "FIXR", "FLTR", "UFA", "DFN", "FSC", "ADJBP", "ILDB", "LDB", "IDPB", "DPB", "FAD", "FADL", "FADM", "FADB", "FADR", "FADRL", "FADRM", "FADRB", diff --git a/PDP10/kx10_tu.c b/PDP10/kx10_tu.c index 11da775a..0f386ac8 100644 --- a/PDP10/kx10_tu.c +++ b/PDP10/kx10_tu.c @@ -1,6 +1,6 @@ -/* ka10_tu.c: Dec RH10 TM03/TU10 tape controller +/* kx10_tu.c: DEC Massbus TM03/TU10 tape controller - Copyright (c) 2017, Richard Cornwell + Copyright (c) 2017-2020, Richard Cornwell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -39,69 +39,12 @@ /* 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_GO 1 /* go */ #define CS1_V_FNC 1 /* function pos */ #define CS1_M_FNC 037 /* function mask */ #define CS1_FNC (CS1_M_FNC << CS1_V_FNC) @@ -179,7 +122,7 @@ /* TUDT - 06 - drive type */ -/* TULA - 07 - look ahead register */ +/* TULA - 07 - Check Character */ /* TUSN - 10 - serial number */ @@ -213,24 +156,14 @@ #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); +void tu_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data); +uint32 tu_read(DEVICE *dptr, struct rh_if *rhc, int reg); +void tu_rst(DEVICE *dptr); t_stat tu_srv(UNIT *); t_stat tu_boot(int32, DEVICE *); void tu_ini(UNIT *, t_bool); @@ -244,21 +177,31 @@ 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) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, + { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, +}; + +struct rh_if tu_rh[NUM_DEVS_TU] = { + { &tu_write, &tu_read} }; DIB tu_dib[] = { - {RH10_DEV, 1, &tu_devio, &tu_devirq} + {RH10_DEV, 1, &rh_devio, &rh_devirq, &tu_rh[0]} }; MTAB tu_mod[] = { +#if KL + {MTAB_XTD|MTAB_VDV, TYPE_RH10, NULL, "RH10", &rh_set_type, NULL, + NULL, "Sets controller to RH10" }, + {MTAB_XTD|MTAB_VDV, TYPE_RH20, "RH20", "RH20", &rh_set_type, &rh_show_type, + NULL, "Sets controller to RH20"}, +#endif {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL}, {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL}, {MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", @@ -271,31 +214,28 @@ MTAB tu_mod[] = { }; REG tua_reg[] = { - {ORDATA(IVECT, tu_ivect[0], 18)}, - {FLDATA(IMODE, tu_imode[0], 0)}, + {ORDATA(IVECT, tu_rh[0].ivect, 18)}, + {FLDATA(IMODE, tu_rh[0].imode, 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}, + {ORDATA(XFER, tu_rh[0].xfer_drive, 3), REG_HRO}, + {ORDATA(DRIVE, tu_rh[0].drive, 3), REG_HRO}, + {ORDATA(REG, tu_rh[0].reg, 6), REG_RO}, + {ORDATA(RAE, tu_rh[0].rae, 8), REG_RO}, + {ORDATA(ATTN, tu_rh[0].attn, 8), REG_RO}, + {ORDATA(STATUS, tu_rh[0].status, 18), REG_RO}, + {ORDATA(CIA, tu_rh[0].cia, 18)}, + {ORDATA(CCW, tu_rh[0].ccw, 18)}, + {ORDATA(WCR, tu_rh[0].wcr, 18)}, + {ORDATA(CDA, tu_rh[0].cda, 18)}, + {ORDATA(DEVNUM, tu_rh[0].devnum, 9), REG_HRO}, + {ORDATA(BUF, tu_rh[0].buf, 36), REG_HRO}, {BRDATA(BUFF, tu_buf[0], 16, 8, TU_NUMFR), REG_HRO}, {0} -}; +}; DEVICE tua_dev = { - "TUA", tu_unit, NULL, tu_mod, + "TUA", tu_unit, tua_reg, 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, @@ -307,196 +247,25 @@ 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]; +tu_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { + int ctlr = GET_CNTRL_RH(dptr->units[0].flags); + int unit = tu_tcr[ctlr] & 07; + UNIT *uptr = &dptr->units[unit]; int i; - if (uptr->CMD & CR_GO) { + if (rhc->drive != 0) /* Only one unit at 0 */ + return; + + if (uptr->CMD & CS1_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); + sim_debug(DEBUG_DETAIL, dptr, "%s%o %d Status=%06o\n", + dptr->name, unit, ctlr, uptr->STATUS); if ((data & 01) != 0 && (uptr->flags & UNIT_ATT) != 0) { uptr->CMD = data & 076; switch (GET_FNC(data)) { @@ -507,45 +276,51 @@ tu_write(int ctlr, int unit, int reg, uint32 data) { case FNC_READ: /* read */ case FNC_READREV: /* read w/ headers */ tu_frame[ctlr] = 0; + tu_tcr[ctlr] |= TC_FCS; + /* Fall through */ + + case FNC_WRITE: /* write */ + case FNC_SPACEF: /* Space forward */ + case FNC_SPACEB: /* Space backward */ + if ((tu_tcr[ctlr] & TC_FCS) == 0) { + uptr->STATUS |= ER1_NEF; + break; + } /* 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; + uptr->CMD |= CS_PIP|CS1_GO; + rhc->attn = 0; + for (i = 0; i < 8; i++) { + if (dptr->units[i].CMD & CS_ATA) + rhc->attn = 1; + } 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->CMD &= ~(CS_ATA|CS1_GO|CS_TM); uptr->STATUS = 0; - tu_attn[ctlr] = 0; - clr_interrupt(df10->devnum); + rhc->status &= ~PI_ENABLE; + rhc->attn = 0; for (i = 0; i < 8; i++) { - if (tu_unit[(ctlr * 8) + i].CMD & CS_ATA) - tu_attn[ctlr] = 1; + if (dptr->units[i].CMD & CS_ATA) + rhc->attn = 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); + rhc->attn = 1; } - sim_debug(DEBUG_DETAIL, dptr, "TUA%o AStatus=%06o\n", unit, + sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, uptr->CMD); } return; @@ -558,20 +333,17 @@ tu_write(int ctlr, int unit, int reg, uint32 data) { case 003: /* maintenance */ break; case 004: /* atten summary */ - tu_attn[ctlr] = 0; + rhc->attn = 0; for (i = 0; i < 8; i++) { if (data & (1<units[i].CMD &= ~CS_ATA; + if (dptr->units[i].CMD & CS_ATA) + rhc->attn = 1; } - clr_interrupt(df10->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; + tu_tcr[ctlr] |= TC_FCS; break; case 006: /* drive type */ case 007: /* look ahead */ @@ -581,43 +353,45 @@ tu_write(int ctlr, int unit, int reg, uint32 data) { default: uptr->STATUS |= ER1_ILR; uptr->CMD |= CS_ATA; - tu_attn[ctlr] = 1; - tu_rae[ctlr] |= (1<status & IADR_ATTN) != 0) - df10_setirq(df10); + rhc->attn = 1; + rhc->rae = 1; } } 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; +tu_read(DEVICE *dptr, struct rh_if *rhc, int reg) { + int ctlr = GET_CNTRL_RH(dptr->units[0].flags); + int unit = tu_tcr[ctlr] & 07; + UNIT *uptr = &dptr->units[unit]; + uint32 temp = 0; + int i; + + if (rhc->drive != 0) /* Only one unit at 0 */ + return 0; 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) + if (uptr->CMD & CS1_GO) temp |= CS1_GO; break; case 001: /* status */ temp = DS_DPR; - if (tu_attn[ctlr] != 0) + if (uptr->CMD & CS_ATA) temp |= DS_ATA; if (uptr->CMD & CS_CHANGE) temp |= DS_SSC; if ((uptr->STATUS & 0177777) != 0) - temp |= DS_ERR|DS_ATA; + temp |= DS_ERR; 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) + if ((uptr->CMD & (CS_MOTION|CS_PIP|CS1_GO)) == 0) temp |= DS_DRY; if (sim_tape_bot(uptr)) temp |= DS_BOT; @@ -634,7 +408,7 @@ tu_read(int ctlr, int unit, int reg) { break; case 004: /* atten summary */ for (i = 0; i < 8; i++) { - if (tu_unit[(ctlr * 8) + i].CMD & CS_ATA) { + if (dptr->units[i].CMD & CS_ATA) { temp |= 1 << i; } } @@ -643,7 +417,7 @@ tu_read(int ctlr, int unit, int reg) { temp = tu_frame[ctlr]; break; case 006: /* drive type */ - temp = 040054; + temp = 042054; break; case 011: /* tape control register */ temp = tu_tcr[ctlr]; @@ -657,10 +431,8 @@ tu_read(int ctlr, int unit, int reg) { default: uptr->STATUS |= (ER1_ILR); uptr->CMD |= CS_ATA; - tu_attn[ctlr] = 1; - tu_rae[ctlr] |= (1<status & IADR_ATTN) != 0) - df10_setirq(df10); + rhc->attn = 1; + rhc->rae = 1; } return temp; } @@ -669,8 +441,9 @@ tu_read(int ctlr, int unit, int reg) { /* 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]; + int ctlr = GET_CNTRL_RH(uptr->flags); + DEVICE *dptr = tu_devs[ctlr]; + struct rh_if *rhc = &tu_rh[ctlr]; switch (r) { case MTSE_OK: /* no error */ @@ -707,211 +480,214 @@ void tu_error(UNIT * uptr, t_stat r) break; } - if (uptr->CMD & CS_ATA) { - tu_attn[ctlr] = 1; - } - uptr->CMD &= ~(CS_MOTION|CS_PIP|CR_GO); + if (uptr->CMD & CS_ATA) + rh_setattn(rhc, 0); + if (GET_FNC(uptr->CMD) >= FNC_XFER && uptr->CMD & (CS_ATA | CS_TM)) + rh_error(rhc); + uptr->CMD &= ~(CS_MOTION|CS_PIP|CS1_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; + int ctlr = GET_CNTRL_RH(uptr->flags); + int unit; + struct rh_if *rhc; + DEVICE *dptr; + t_stat r; + t_mtrlnt reclen; + uint8 ch; + int cc; + int cc_max; /* Find dptr, and df10 */ dptr = tu_devs[ctlr]; + rhc = &tu_rh[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); + rh_setirq(rhc); 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; + sim_debug(DEBUG_DETAIL, dptr, "%s%o nop\n", dptr->name, unit); + tu_error(uptr, MTSE_OK); /* Nop */ + rh_setirq(rhc); + 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 { + sim_debug(DEBUG_DETAIL, dptr, "%s%o rewind\n", dptr->name, unit); + if (uptr->CMD & CS1_GO) { + sim_activate(uptr,40000); + uptr->CMD |= CS_MOTION; + uptr->CMD &= ~(CS1_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; + rh_setattn(rhc, 0); + (void)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; + sim_debug(DEBUG_DETAIL, dptr, "%s%o unload\n", dptr->name, unit); + uptr->CMD |= CS_CHANGE|CS_ATA; + 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; + 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, "%s%o read error %d\n", dptr->name, unit, r); + if (r == MTSE_BOT) + uptr->STATUS |= ER1_NEF; + tu_error(uptr, r); + rh_finish_op(rhc, 0); + } else { + sim_debug(DEBUG_DETAIL, dptr, "%s%o read %d\n", dptr->name, unit, reclen); + uptr->CMD |= CS_MOTION; + uptr->hwmark = reclen; + uptr->DATAPTR = uptr->hwmark-1; + uptr->CPOS = cc_max; + rhc->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) + rhc->buf |= (uint64)(ch & 0x0f); + else + rhc->buf |= (uint64)(ch & 0xff) << cc; + uptr->DATAPTR--; + uptr->CPOS--; + if (uptr->CPOS == 0) { + uptr->CPOS = cc_max; + if (GET_FNC(uptr->CMD) == FNC_READREV && rh_write(rhc) == 0) { + tu_error(uptr, MTSE_OK); + rh_finish_op(rhc, 0); + return SCPE_OK; + } + sim_debug(DEBUG_DATA, dptr, "%s%o readrev %012llo\n", + dptr->name, unit, rhc->buf); + rhc->buf = 0; + } + } else { + if (uptr->CPOS != cc_max) + rh_write(rhc); + (void)rh_blkend(rhc); + tu_error(uptr, MTSE_OK); + rh_finish_op(rhc, 0); + 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; + 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, "%s%o read error %d\n", dptr->name, unit, r); + if (r == MTSE_TMK) + uptr->STATUS |= ER1_FCE; + tu_error(uptr, r); + rh_finish_op(rhc, 0); + } else { + sim_debug(DEBUG_DETAIL, dptr, "%s%o read %d %d\n", dptr->name, unit, reclen, uptr->pos); + uptr->hwmark = reclen; + uptr->DATAPTR = 0; + uptr->CPOS = 0; + rhc->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) + rhc->buf |= (uint64)(ch & 0x0f); + else + rhc->buf |= (uint64)(ch & 0xff) << cc; + uptr->DATAPTR++; + uptr->CPOS++; + if (uptr->CPOS == cc_max) { + uptr->CPOS = 0; + if (GET_FNC(uptr->CMD) == FNC_READ && rh_write(rhc) == 0) { + tu_error(uptr, MTSE_OK); + if (uptr->DATAPTR == uptr->hwmark) + (void)rh_blkend(rhc); + rh_finish_op(rhc, 0); + return SCPE_OK; + } + sim_debug(DEBUG_DATA, dptr, "%s%o read %012llo %d\n", + dptr->name, unit, rhc->buf, uptr->DATAPTR); + rhc->buf = 0; + } + } else { + if (uptr->CPOS != 0) { + sim_debug(DEBUG_DATA, dptr, "%s%o readf %012llo\n", + dptr->name, unit, rhc->buf); + rh_write(rhc); + } + if (tu_frame[ctlr] != 0) + uptr->STATUS |= ER1_FCE; + tu_error(uptr, MTSE_OK); + (void)rh_blkend(rhc); + rh_finish_op(rhc, 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; + rhc->attn = 1; tu_error(uptr, MTSE_OK); - df10_finish_op(df, 0); + rh_finish_op(rhc, 0); return SCPE_OK; } if ((uptr->flags & MTUF_WLK) != 0) { tu_error(uptr, MTSE_WRP); - df10_finish_op(df, 0); + rh_finish_op(rhc, 0); return SCPE_OK; } uptr->CMD |= CS_MOTION; - sim_debug(DEBUG_EXP, dptr, "TU%o Init write\n", unit); + sim_debug(DEBUG_EXP, dptr, "%s%o Init write\n", dptr->name, unit); uptr->hwmark = 0; uptr->CPOS = 0; uptr->DATAPTR = 0; - df->buf = 0; + rhc->buf = 0; } - if (tu_frame[ctlr] != 0 && uptr->CPOS == 0 && df10_read(df) == 0) + if (tu_frame[ctlr] != 0 && uptr->CPOS == 0 && rh_read(rhc) == 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); + sim_debug(DEBUG_DATA, dptr, "%s%o write %012llo\n", + dptr->name, unit, rhc->buf); /* Write next char out */ cc = (8 * (3 - (uptr->CPOS & 07))) + 4; if (cc < 0) - ch = df->buf & 0x0f; + ch = rhc->buf & 0x0f; else - ch = (df->buf >> cc) & 0xff; + ch = (rhc->buf >> cc) & 0xff; tu_buf[ctlr][uptr->DATAPTR] = ch; uptr->DATAPTR++; uptr->hwmark = uptr->DATAPTR; @@ -920,99 +696,95 @@ t_stat tu_srv(UNIT * uptr) uptr->CPOS &= 010; } tu_frame[ctlr] = 0177777 & (tu_frame[ctlr] + 1); - if (tu_frame[ctlr] == 0) + if (tu_frame[ctlr] == 0) { uptr->CPOS = 010; + tu_tcr[ctlr] &= ~(TC_FCS); + } } 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; + /* Write out the block */ + reclen = uptr->hwmark; + r = sim_tape_wrrecf(uptr, &tu_buf[ctlr][0], reclen); + sim_debug(DEBUG_DETAIL, dptr, "%s%o Write %d %d\n", + dptr->name, unit, reclen, uptr->CPOS); + uptr->DATAPTR = 0; + uptr->hwmark = 0; + (void)rh_blkend(rhc); + tu_error(uptr, r); /* Record errors */ + rh_finish_op(rhc,0 ); + 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; + uptr->CMD &= ~CS_PIP; + uptr->CMD |= CS_ATA; + if ((uptr->flags & MTUF_WLK) != 0) { + tu_error(uptr, MTSE_WRP); + } else { + tu_error(uptr, sim_tape_wrtmk(uptr)); + } + sim_debug(DEBUG_DETAIL, dptr, "%s%o WTM\n", dptr->name, unit); + 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; + uptr->CMD &= ~CS_PIP; + uptr->CMD |= CS_ATA; + if ((uptr->flags & MTUF_WLK) != 0) { + tu_error(uptr, MTSE_WRP); + } else { + tu_error(uptr, sim_tape_wrgap(uptr, 35)); + } + sim_debug(DEBUG_DETAIL, dptr, "%s%o ERG\n", dptr->name, unit); + 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; + sim_debug(DEBUG_DETAIL, dptr, "%s%o space %o\n", dptr->name, unit, GET_FNC(uptr->CMD)); + uptr->CMD &= ~CS_PIP; + if (tu_frame[ctlr] == 0) { + uptr->STATUS |= ER1_NEF; + uptr->CMD |= CS_ATA; + tu_error(uptr, MTSE_OK); + 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); + tu_frame[ctlr] = 0177777 & (tu_frame[ctlr] + 1); + switch (r) { + case MTSE_OK: /* no error */ + break; - case MTSE_BOT: /* beginning of tape */ - uptr->STATUS |= ER1_NEF; - /* Fall Through */ + 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; + case MTSE_TMK: /* tape mark */ + case MTSE_EOM: /* end of medium */ + if (tu_frame[ctlr] != 0) + uptr->STATUS |= ER1_FCE; + else + tu_tcr[ctlr] &= ~(TC_FCS); + uptr->CMD |= CS_ATA; + /* Stop motion if we recieve any of these */ + tu_error(uptr, r); + return SCPE_OK; + } + if (tu_frame[ctlr] == 0) { + uptr->CMD |= CS_ATA; + tu_error(uptr, MTSE_OK); + return SCPE_OK; + } else { + tu_tcr[ctlr] &= ~(TC_FCS); + sim_activate(uptr, reclen * 50); + } + return SCPE_OK; } - sim_activate(uptr, 200); + sim_activate(uptr, 20); return SCPE_OK; } @@ -1024,11 +796,8 @@ 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; + tu_rh[ctlr].attn = 0; + tu_rh[ctlr].rae = 0; } return SCPE_OK; } @@ -1102,20 +871,15 @@ tu_boot(int32 unit_num, DEVICE * dptr) 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]; + int ctlr = GET_CNTRL_RH(uptr->flags); + struct rh_if *rhc = &tu_rh[ctlr]; uptr->CMD = 0; uptr->STATUS = 0; r = sim_tape_attach_ex(uptr, file, 0, 0); - if (r == SCPE_OK) { + if (r == SCPE_OK && (sim_switches & SIM_SW_REST) == 0) { uptr->CMD = CS_ATA|CS_CHANGE; - tu_attn[ctlr] = 1; - if ((df->status & IADR_ATTN) != 0) - df10_setirq(df); + rh_setattn(rhc, 0); } return r; } @@ -1123,16 +887,13 @@ tu_attach(UNIT * uptr, CONST char *file) t_stat tu_detach(UNIT * uptr) { - int ctlr = GET_CNTRL(uptr->flags); - struct df10 *df; + int ctlr = GET_CNTRL_RH(uptr->flags); + struct rh_if *rhc = &tu_rh[ctlr]; /* 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); + rh_setattn(rhc, 0); return sim_tape_detach(uptr); } diff --git a/PDP10/pdp6_dtc.c b/PDP10/pdp6_dtc.c index 5663bfce..c4e1567a 100644 --- a/PDP10/pdp6_dtc.c +++ b/PDP10/pdp6_dtc.c @@ -2,6 +2,9 @@ Copyright (c) 2017 Richard Cornwell + Based on PDP18B/pdp18b_dt.c by: + Copyright (c) 1993-2017, Robert M Supnik + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation @@ -19,6 +22,10 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + 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. @@ -228,6 +235,8 @@ #define DT_WRDTIM 15000 +#define WRITTEN u6 + int32 dtc_dtsa = 0; /* status A */ int32 dtc_dtsb = 0; /* status B */ int dtc_dct = 0; @@ -239,6 +248,7 @@ 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); +void dtc_flush (UNIT *uptr); t_stat dtc_detach (UNIT *uptr); /* DT data structures @@ -495,7 +505,6 @@ dtc_svc (UNIT *uptr) 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) @@ -607,7 +616,6 @@ dtc_svc (UNIT *uptr) 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)) { @@ -636,6 +644,7 @@ dtc_svc (UNIT *uptr) } fbuf[off] = (data >> 18) & RMASK; fbuf[off+1] = data & RMASK; + uptr->WRITTEN = 1; uptr->hwmark = uptr->capac; break; } @@ -749,7 +758,6 @@ dtc_svc (UNIT *uptr) 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 */ @@ -887,6 +895,7 @@ dtc_svc (UNIT *uptr) dtc_dtsb |= DTB_DONE; fbuf[off] = (data >> 18) & RMASK; fbuf[off+1] = data & RMASK; + uptr->WRITTEN = 1; uptr->hwmark = uptr->capac; break; case FNC_WMRK: @@ -1169,22 +1178,24 @@ dtc_attach (UNIT *uptr, CONST char *cptr) 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); + sim_printf ("%s%d: ", sim_dname (&dtc_dev), u); if (uptr->flags & UNIT_8FMT) - printf ("12b format"); + sim_printf ("12b format"); else if (uptr->flags & UNIT_11FMT) - printf ("16b format"); - else printf ("18b/36b format"); - printf (", buffering file in memory\n"); + sim_printf ("16b format"); + else sim_printf ("18b/36b format"); + sim_printf (", buffering file in memory\n"); + uptr->WRITTEN = 0; + uptr->io_flush = dtc_flush; if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); @@ -1198,11 +1209,10 @@ dtc_attach (UNIT *uptr, CONST char *cptr) fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | ((uint32) pdp8b[k + 2] & 07777); ba = ba + 2; /* end blk loop */ - } - } /* end file loop */ + } + } /* end file loop */ uptr->hwmark = ba; - } /* end if */ - else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + } 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) @@ -1211,10 +1221,9 @@ dtc_attach (UNIT *uptr, CONST char *cptr) 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), + } 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 */ @@ -1230,23 +1239,15 @@ dtc_attach (UNIT *uptr, CONST char *cptr) Deallocate buffer */ -t_stat -dtc_detach (UNIT* uptr) +void +dtc_flush (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); + if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + fbuf = (uint32 *) uptr->filebuf; /* file buffer */ rewind (uptr->fileref); /* start of file */ if (uptr->flags & UNIT_8FMT) { /* 12b? */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ @@ -1256,26 +1257,42 @@ dtc_detach (UNIT* uptr) ((fbuf[ba + 1] >> 12) & 077); pdp8b[k + 2] = fbuf[ba + 1] & 07777; ba = ba + 2; - } /* end loop blk */ + } /* 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? */ + } /* end loop file */ + } 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 */ + } /* end loop file */ + } else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ uptr->hwmark, uptr->fileref); if (ferror (uptr->fileref)) - perror ("I/O error"); - } /* end if hwmark */ + sim_perror ("I/O error"); + uptr->WRITTEN = 0; + } /* end if hwmark */ +} + +t_stat +dtc_detach (UNIT* uptr) +{ + 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; + } + if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + sim_printf ("%s%d: writing buffer to file\n", sim_dname (&dtc_dev), u); + dtc_flush (uptr); + } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ diff --git a/PDP10/pdp6_slave.c b/PDP10/pdp6_slave.c new file mode 100644 index 00000000..acd73dbb --- /dev/null +++ b/PDP10/pdp6_slave.c @@ -0,0 +1,329 @@ +/* pdp6_slave.c: Slaved 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 LARS BRINKHOFF 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 master processor through + shared memory and inter-processor interrupts. +*/ + +#include "kx10_defs.h" +#include "sim_tmxr.h" + +#ifndef NUM_DEVS_SLAVE +#define NUM_DEVS_SLAVE 0 +#endif + +#if NUM_DEVS_SLAVE > 0 + +/* 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 SLAVE_MEM_CYCLE 100 + +/* Interprocessor interrupt device. */ +#define SLAVE_DEVNUM 020 + +#define SLAVE_POLL 1000 + +#define PIA u3 +#define STATUS u4 + +static t_stat slave_devio(uint32 dev, uint64 *data); +static t_stat slave_svc (UNIT *uptr); +static t_stat slave_reset (DEVICE *dptr); +static t_stat slave_attach (UNIT *uptr, CONST char *ptr); +static t_stat slave_detach (UNIT *uptr); +static t_stat slave_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char *slave_description (DEVICE *dptr); +static uint8 slave_valid[040000]; + +UNIT slave_unit[1] = { + { UDATA (&slave_svc, UNIT_IDLE|UNIT_ATTABLE, 0), 1000 }, +}; + +static REG slave_reg[] = { + { DRDATAD (POLL, slave_unit[0].wait, 24, "poll interval"), PV_LEFT }, + {BRDATA(BUFF, slave_valid, 8, 8, sizeof(slave_valid)), REG_HRO}, + { NULL } +}; + +static MTAB slave_mod[] = { + { 0 } +}; + +#define DEBUG_TRC 0x0000400 + +static DEBTAB slave_debug[] = { + {"TRACE", DEBUG_TRC, "Routine trace"}, + {"CMD", DEBUG_CMD, "Command Processing"}, + {"CONO", DEBUG_CONO, "CONO instructions"}, + {"CONI", DEBUG_CONI, "CONI instructions"}, + {"DATAIO", DEBUG_DATAIO, "DATAI/O instructions"}, + {0}, +}; + +DEVICE slave_dev = { + "SLAVE", slave_unit, slave_reg, slave_mod, + 1, 8, 16, 2, 8, 16, + NULL, /* examine */ + NULL, /* deposit */ + &slave_reset, /* reset */ + NULL, /* boot */ + slave_attach, /* attach */ + slave_detach, /* detach */ + NULL, /* context */ + DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX, + DEBUG_CMD, /* debug control */ + slave_debug, /* debug flags */ + NULL, /* memory size chage */ + NULL, /* logical name */ + NULL, /* help */ + &slave_attach_help, /* attach help */ + NULL, /* help context */ + &slave_description, /* description */ +}; + +static TMLN slave_ldsc; /* line descriptor */ +static TMXR slave_desc = { 1, 0, 0, &slave_ldsc }; /* mux descriptor */ + +static t_stat slave_reset (DEVICE *dptr) +{ + sim_debug(DEBUG_TRC, dptr, "slave_reset()\n"); + + slave_unit[0].flags |= UNIT_ATTABLE | UNIT_IDLE; + slave_desc.packet = TRUE; + slave_desc.notelnet = TRUE; + slave_desc.buffered = 2048; + + if (slave_unit[0].flags & UNIT_ATT) + sim_activate (&slave_unit[0], 1000); + else + sim_cancel (&slave_unit[0]); + + return SCPE_OK; +} + +static t_stat slave_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 (&slave_desc, uptr, cptr, FALSE); + if (r != SCPE_OK) /* error? */ + return r; + sim_debug(DEBUG_TRC, &slave_dev, "activate connection\n"); + sim_activate (uptr, 10); /* start poll */ + return SCPE_OK; +} + +static t_stat slave_detach (UNIT *uptr) +{ + t_stat r; + + if (!(uptr->flags & UNIT_ATT)) + return SCPE_OK; + sim_cancel (uptr); + r = tmxr_detach (&slave_desc, uptr); + uptr->filename = NULL; + return r; +} + +static int error (const char *message) +{ + sim_debug (DEBUG_TRC, &slave_dev, "%s\r\n", message); + sim_debug (DEBUG_TRC, &slave_dev, "CLOSE\r\n"); + slave_ldsc.rcve = 0; + tmxr_reset_ln (&slave_ldsc); + return -1; +} + +static void build (uint8 *request, uint8 octet) +{ + request[0]++; + request[request[0]] = octet & 0377; +} + +static t_stat process_request (UNIT *uptr, const uint8 *request, size_t size) +{ + uint8 response[12]; + t_addr address; + uint64 data; + t_stat stat; + + if (size == 0) + return SCPE_OK; + if (size > 9) + return error ("Malformed transaction"); + + sim_debug(DEBUG_CMD, &slave_dev, "got packet\n"); + + memset (response, 0, sizeof response); + + switch (request[0]) { + case DATI: + address = request[1] + (request[2] << 8) + (request[3] << 16); + if (address < MEMSIZE) { + data = M[address]; + build (response, ACK); + build (response, (uint8)(data & 0xff)); + build (response, (uint8)((data >> 8)) & 0xff); + build (response, (uint8)((data >> 16) & 0xff)); + build (response, (uint8)((data >> 24) & 0xff)); + build (response, (uint8)((data >> 32) & 0xff)); + sim_debug(DEBUG_DATAIO, &slave_dev, "DATI %06o -> %012llo\n", + address, data); + } else { + build (response, ERR); + sim_debug(DEBUG_DATAIO, &slave_dev, "DATI %06o -> NXM\n", address); + } + break; + case DATO: + address = request[1] + (request[2] << 8) + (request[3] << 16); + if (address < MEMSIZE) { + data = request[4]; + data |= ((uint64)request[5]) << 8; + data |= ((uint64)request[6]) << 16; + data |= ((uint64)request[7]) << 24; + data |= ((uint64)request[8]) << 32; + M[address] = data; + build (response, ACK); + sim_debug(DEBUG_DATAIO, &slave_dev, "DATO %06o <- %012llo\n", + address, data); + } else { + build (response, ERR); + sim_debug(DEBUG_DATAIO, &slave_dev, "DATO %06o -> NXM\n", address); + } + break; + case ACK: + break; + case IRQ: + uptr->STATUS |= 010; + set_interrupt(SLAVE_DEVNUM, uptr->PIA); + build (response, ACK); + sim_debug(DEBUG_DATAIO, &slave_dev, "IRQ\n"); + break; + default: + return error ("Malformed transaction"); + } + + stat = tmxr_put_packet_ln (&slave_ldsc, response + 1, (size_t)response[0]); + if (stat != SCPE_OK) + return error ("Write error in transaction"); + return stat; +} + +static t_stat slave_svc (UNIT *uptr) +{ + const uint8 *slave_request; + size_t size; + + if (tmxr_poll_conn(&slave_desc) >= 0) { + sim_debug(DEBUG_CMD, &slave_dev, "got connection\n"); + slave_ldsc.rcve = 1; + memset(&slave_valid[0], 0, sizeof(slave_valid)); + uptr->wait = SLAVE_POLL; + } + + tmxr_poll_rx (&slave_desc); + if (slave_ldsc.rcve && !slave_ldsc.conn) { + slave_ldsc.rcve = 0; + tmxr_reset_ln (&slave_ldsc); + sim_debug(DEBUG_CMD, &slave_dev, "reset\n"); + } + + if (tmxr_get_packet_ln (&slave_ldsc, &slave_request, &size) == SCPE_OK) + process_request (uptr, slave_request, size); + + sim_clock_coschedule (uptr, uptr->wait); + return SCPE_OK; +} + +static t_stat slave_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 *slave_description (DEVICE *dptr) +{ + return "Auxiliary processor"; +} + +t_stat slave_devio(uint32 dev, uint64 *data) +{ + DEVICE *dptr = &slave_dev; + UNIT *uptr = &slave_unit[0]; + + switch(dev & 03) { + case CONO: + sim_debug(DEBUG_CONO, &slave_dev, "CONO %012llo\n", *data); + uptr->PIA = *data & 7; + if (*data & 010) + { + // Clear interrupt from the PDP-10. + uptr->STATUS &= ~010; + clr_interrupt(SLAVE_DEVNUM); + } +#if 0 + if (*data & 020) + slave_interrupt (); +#endif + break; + case CONI: + *data = (uptr->STATUS & 010) | uptr->PIA; + sim_debug(DEBUG_CONI, &slave_dev, "CONI %012llo\n", *data); + break; + case DATAI: + *data = 0; + sim_debug(DEBUG_CONI, &slave_dev, "DATAI %012llo\n", *data); + break; + case DATAO: + sim_debug(DEBUG_CONI, &slave_dev, "DATAO %012llo\n", *data); + break; + } + + return SCPE_OK; +} + +#endif + diff --git a/README.md b/README.md index 46980cbd..e332a198 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ #### Richard Cornwell has implemented the IBM 701, IBM 704, IBM 7010/1410, IBM 7070/7074, IBM 7080/702/705/7053 and IBM 7090/7094/709/704 simulators. -#### Richard Cornwell has implemented the PDP6, PDP10-KA, and PDP10-KI simulators. +#### Richard Cornwell has implemented the PDP6, PDP10-KA, PDP10-KI and PDP10-KL simulators. #### Dave Bryan has implemented an HP-3000 Series III simulator. @@ -229,7 +229,7 @@ Host platforms which have libSDL2 available can leverage this functionality. RAW Disk Access (including CDROM) Virtual Disk Container files, including differencing disks File System type detection to accurately autosize disks. - Recognized file systems are: DEC ODS1, DEC ODS2, DEC RT11, Ultrix Partitions + Recognized file systems are: DEC ODS1, DEC ODS2, DEC RT11, DEC RSX11, Ultrix Partitions #### Tape Extensions AWS format tape support @@ -304,6 +304,9 @@ The following extensions to the SCP command language without affecting prior beh Restores the default CTRL+C behavior for the currently running command procedure. + DO + Invokes a nested DO command with input from the + running console. Error traps can be taken for any command which returns a status other than SCPE_STEP, SCPE_OK, and SCPE_EXIT. @@ -541,6 +544,7 @@ Different Linux distributions have different package management systems: Ubuntu: # apt-get install libpcap-dev + # apt-get install libpcre3-dev # apt-get install vde2 # apt-get install libsdl2 # apt-get install libsdl2_ttf diff --git a/Visual Studio Projects/PDP10-KA.vcproj b/Visual Studio Projects/PDP10-KA.vcproj index 2d0abdc9..7b28a814 100644 --- a/Visual Studio Projects/PDP10-KA.vcproj +++ b/Visual Studio Projects/PDP10-KA.vcproj @@ -213,6 +213,10 @@ RelativePath="..\PDP10\ka10_dpk.c" > + + @@ -265,6 +269,10 @@ RelativePath="..\PDP10\kx10_df.c" > + + @@ -301,6 +309,10 @@ RelativePath="..\PDP10\kx10_rc.c" > + + @@ -419,6 +431,10 @@ RelativePath="..\display\display.h" > + + @@ -601,10 +617,18 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl;inc" > + + + + diff --git a/Visual Studio Projects/PDP10-KI.vcproj b/Visual Studio Projects/PDP10-KI.vcproj index 11ba1bb5..42bcafca 100644 --- a/Visual Studio Projects/PDP10-KI.vcproj +++ b/Visual Studio Projects/PDP10-KI.vcproj @@ -217,6 +217,10 @@ RelativePath="..\PDP10\kx10_df.c" > + + @@ -253,6 +257,10 @@ RelativePath="..\PDP10\kx10_rc.c" > + + @@ -537,6 +545,10 @@ RelativePath="..\PDP10\kx10_defs.h" > + + diff --git a/Visual Studio Projects/PDP10-KL.vcproj b/Visual Studio Projects/PDP10-KL.vcproj new file mode 100644 index 00000000..29d3a493 --- /dev/null +++ b/Visual Studio Projects/PDP10-KL.vcproj @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Visual Studio Projects/PDP6.vcproj b/Visual Studio Projects/PDP6.vcproj index 83566227..e0041f01 100644 --- a/Visual Studio Projects/PDP6.vcproj +++ b/Visual Studio Projects/PDP6.vcproj @@ -245,6 +245,10 @@ RelativePath="..\PDP10\pdp6_mtc.c" > + + diff --git a/Visual Studio Projects/Simh.sln b/Visual Studio Projects/Simh.sln index 10436a0c..89272922 100644 --- a/Visual Studio Projects/Simh.sln +++ b/Visual Studio Projects/Simh.sln @@ -398,6 +398,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imds-810", "imds-810.vcproj {D40F3AF1-EEE7-4432-9807-2AD287B490F8} = {D40F3AF1-EEE7-4432-9807-2AD287B490F8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PDP10-KL", "PDP10-KL.vcproj", "{DA2AA7A0-B679-456B-B152-DEF40FAE5A7A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -724,6 +726,10 @@ Global {0720F164-692E-4C07-BE69-41C4302062E5}.Debug|Win32.Build.0 = Debug|Win32 {0720F164-692E-4C07-BE69-41C4302062E5}.Release|Win32.ActiveCfg = Release|Win32 {0720F164-692E-4C07-BE69-41C4302062E5}.Release|Win32.Build.0 = Release|Win32 + {DA2AA7A0-B679-456B-B152-DEF40FAE5A7A}.Debug|Win32.ActiveCfg = Debug|Win32 + {DA2AA7A0-B679-456B-B152-DEF40FAE5A7A}.Debug|Win32.Build.0 = Debug|Win32 + {DA2AA7A0-B679-456B-B152-DEF40FAE5A7A}.Release|Win32.ActiveCfg = Release|Win32 + {DA2AA7A0-B679-456B-B152-DEF40FAE5A7A}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/doc/ka10_doc.doc b/doc/ka10_doc.doc index 58e8d19bc07a9789ca4d4db73a1f999ebf598d23..8110df02322ade20d1c405a73112e71e517f4446 100644 GIT binary patch literal 230400 zcmeF43xHl#b??t414($q0D&S>8Dk8QLdc5{5NIBgkc2#j%4 zq9T`CYOPgDEv3{VrIb=iDW#N4y_QnTRqCaPTrRbg8@WiSRdWBo^Z(ZDbIy0ZnaRt} z?RCPR?{&`JYp>T{d+o=0>TeEs{!3py>^oWI?|s?O?5*u1vb_u6x9k1GZH+HRW!VtD z<>Oo1w{LHJn{CybYlLeBef`tFLC%36H|Rr#e=1?_CAlHgs_jWudtu6zc5mG zn;;d)4iF9$4iZKS2MdP?KO-C}yj?gR;W%NMaJ(>GI6*j3c%N{RaI$cU@bkh9VWu!k zm@RY)bA-9Vslq&AzOXD?-xEGoGW}# zI8Rt6oG&aFRtPJFRl)_rYT-iRBH?1;65&!|jj&c&C#)AX2pFP5q?qlsIXKet=F9$YWX_7Zp`n{4+H!=L>kjjq5Neaw-x=xK(6oa znxEF6IM5%j_`S&gw#H|^YuEdt{qr66&+AYB{&RAmP0}4I2T$wv9x+7H&ay}H|7UaY z--|9@xpDob^{Y2`oV9-As$)9WUwYZv3)XGU`-DZ^eAl^tW!@9;eu6&azfYK+y?(;I zIH!L8q<^lopX*xv{ML42kme%L>*qiA8?XJ_l8Z+D?q`N)KX>FGe2e~KlzyNO<9GZg zcj7093fBqPz|8`7@Ofcx;ZA}6@IGO<@PvR}d_xfHvgZZ*$5({?gf|81Xm2#`pMLB< zk2?9I|J%{`l)HHS#B{eKACmV+d#krwuU=uTdMf%K+Bs=#cGTFm znPcf)XB{4Ua@V|0qtYj=^aO?uf0GutWtlsPJLfT-mf1ix@L^a_8FJ$JGU)f&bv;XsBpe; z&>h>d(RWCEBUSC#?6~1=%k^^Z@V577L;5J2c;QI3cE-52ea5x9?EIx%_7kb>4wv2G zvWNCjHgU+2(xw^XhU_zLh|A7j%4Oe_%0Ap>A5Pi2x#n+7_SO-vZU25ACyh{Kah4*R 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∾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 Ue-8*#*BWgdY1cMmU(h7*f98?<9RL6T diff --git a/doc/ki10_doc.doc b/doc/ki10_doc.doc index 42c1f6bd94847f45ad5842c7a29afa059499a339..d202f9ea2e899555deb5c938301a0483301b1722 100644 GIT binary patch delta 19351 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 J<8jyD|6hnO650R& delta 14597 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 diff --git a/doc/kl10_doc.doc b/doc/kl10_doc.doc new file mode 100644 index 0000000000000000000000000000000000000000..9fe1eb6990bf4ca12c8e985b4d057b4d8b5a6be0 GIT binary patch literal 164352 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%0Av`dQN& zvQt5}*Mw<9vsu#`Icv=3b4;3k<-cXk`bAeKsLmt6NU#Q64{iWAf}6lQU<=4ToMk70 zlffzAR4@-b1Re&Dfb92JO8^={6KDqKfW=@5cmS*i4}yol!(hlj^a>0ESA%Q7wct9i z25bgzfw#f5k05g}3(N+s;9hVaxF5U>UInj#jbIa)y)Ws&T(AnP23LS9!ByZjun}wm z1NUQ10vH4agJ1pfjW>SDii%fD-!GQFpDleqRr-Fc^!-rj`~K4R-T8Mew-;$XZE`~! z{-Esqrk?5bUpJ-abIZ=ZjnL!#`jwrZ-xyst?UA-n@)NOMW|LLVA>aydCAbRQ1MUO& zg9pHR(9!~5z+f;N%mpWblffxqJ$Mj21p0oI_67!ky}>?UCAbt^2A&1agBQSy;3Y6| zP?k*wQ@~Z=8gMPR4y*y2!CT;MFyv$C3pfA_2P43>;5x7dYzA+Gcfb~q4bHOD!9uVI zJOQ2pPlFBM8F0kMu}9!&a2>cF{5JdLZ{K+1w|u`^`hKzW{cP#`snYjjrSFGI-}jfk z@8&y~|JV9P@<0l2?w+2{DLcQbPkMcivh&5erPu!%lN{^AkodgKdVHZ*luG)LO;$Z` z05^i0z!s1Vp&f&;ZmR)R~xZQu^D9y|y(gLgpR ziS)f-Avg!D0+)jq!7Jcx@D4b75_$&af|I~Ha3^>WJOo|=8vzR@vU)HUOa>=`lfi0m z6}TC!1slL~U?bQB`X7T00wcjFFb^yOE5W7U9&kT+5Z-aNhu<7IlMuJh`bg&Sd1^Uk5cQ6W!2D89&a1mGmt^@ai`@sF+ zY48?!8@vPdo=Kkpjs!=8lff!*Iam$Wf=9rk;4$zPD>sYq-~3*AA$SSA3SI-lm%@86 z7K{f|z%;NNTm-HFSAy%n8t@v}2(|#iQ`tbUFK7W1mmzB~7n}r62MfU>a2;3!ZUrY_ zKpO_vfV;tZ@FLg<`Y$IxFd57Sw}Exw4sa*fbP;3Di)l;XH>|>zq&6FVo}a$XvhIKX zFaEu*Yd_DgrvH_W3w-_SW)8#O>&ndSe#yw!WqZ^&f3D%2Gy8jIuRgs({%w9lo796& zoFH1~fz!cdU=?^8EL*`bxEx#wt^&`2=fOs>32X*$foYe}e}GfKsbDc!0`3O)fQP{& z;Cb)@7_<^y0mHyzaPpfmPtC%b){n01scz{ota{)0coNz*S)H)uaI%!N4z| zyI?rD5!?itub>YHE5J(dEO-tac_lQ1>0l<91!jX*Fb|v#R)NdGbzlv+9^3$K1h;?( z!9$?$7twFf0tSJ>pd|H;x$G~iP;dY^5*!T{fwRClUEV+zTH5BkVuu{}pT~I2w!v=YVx<$P+vZo&)cI zEnwwWX;a`%&~!a=1V?})!L{Hza4)zIya^_L4SK;@;2f|LTncUhw}Lys17JOP3A_x3 z-GFTYBf%=L?~TY190865M}u3zZQvpBFn9!v{Zn`VrhrqxrQo`o&=GJaxC`73M*JCi z@C#hOjT?D(Lv3DvFP8rQ<`=kh`u8kymaI$oMgCjq*BAM{En)Uz+i8Ur2oiT4DMf`` z%DSVtPnat@M}slootu%<*Xf(VXmA6#5!?(0{RO-Mr+`zzL*QZX9C#bN1N7idJ-8NJ z2W|(Cg2zDrzr_B75nv{m1=fOFzyn}CxMLkW1doF!K+88N7aR#zfy=?b+mRI*4aR_5 z!EN9n@G#ituh2&@5{v@(g9pGf;8`&G4(dVUbk+_2Hd)W$Mhs4&97(ZlUppl``fq*o8y!JiBA%F{Vcwcu=_{xmB-_ge~eGCzv1(pa7Y&%pU`E5 z&n@B5o=9ywU(wfa_x;F`~HXWCrtsmU+S$bc3w!OJ*?|n(>(y@Mu z&npJ6IJ;u$ikB-Uu6VFwzKZKAcB}ZTVz5^M#b6bG9XlMo7)_sdI6m`H*oByPug51z zyZw*&O496p9A8QF;D_;*$KsQJh)?LW!{`2RNWUJR;0J_Hq9J%8;qw>a&>m2Z&(q_& zakQ@ped@B_L$ckN)H`)`mVE}y194j})2Bo-={2_p+pTl$XiI;|?e?Rw^J9@OSO*kG zR}5Y8bH&b+_Whf<;)!*yZnhKST>T`@xputm9B4Ww+k0@`)J1i=6>a31?E)oC^t4&> zy$5J+MDrs3#%Da07JcSK$E+iXKFLY+`%XAaYDTl9njiFl{9By1*M_EyzBa4tzkyCJ(_`V5wBl%KM+rtLtGWhW#H)k)2Hvf!gGT?Q znS+K=*`iU|K^u0jtE-Q%)i=b~>Ko!*>KoFO&7Mov zD0%k>nhVoB*myA8Ii&At@yW!hC-H^tIZhH(QlIc zPL%i|osoD`I}*2RE^$Ar&n4$)CRICg`TJR%(&g{hBKb?+6M^PFHSc*jsLB7`xAv6RfckHB^{m-p`H}U@@@9{wEueJ92c2Mj8yHEcw zdEWuF-u@x*DyYf7`^aDNehujPiu%de0#K8G_mRKkJrU@68a+#M1*plt`^aDNz7pv9 zDLp&&45-P!`^aDNeirC?IXx>k;Fy~HbLn>z|6lUn8|eASQQ&lNW{Cm$S8e|1{T=^D z-V4DZpl4z41!vaeFAn(Xk@vOyCGY!yo`2S}&)Jlk{7dTJE#xnG*8`S`XH7uQy$=C; z?p@EkpIKr?{&hxtvLHS=DLy$iKA9SyFv(E(X_}F)T-2*(3YIRie%9ACvO;}zSXi6Y zp}InC{=X?-Q|9ac{H!rrH&)-!>}v|Lo|3WlG`I(7PXp~|kgV#xI2KrPB{_*xb>ZXB zm9GyOk=6fA&+qh|#^Ve$geogXw z?~C?)smZ^4$zL+R3}~Mp?eSBSfA^BVWPS(GzE0ZHsV4vKC4b5MRiJ&mw1?N1VsGvX zRZrpz?~~)iH@u+Tae`{>V}iK${+qtvi*Z8i@AGt-d-$vr_qE|X6PBN)*V@JcJQ|M7 zhXC!{rajwg?Z11qf606?&^~?Iqfh(uX>UI5%hyR;P~A!-SK@}!6_2}R`)`t*HvsLw zsJ$0EDf6xyQn7r?jwJ_tLh4%_f`43s%rA@KJwT5za9sA2bjPA zt0w>MBY(Z`Z2-_a-~4@VHTicR`Ro0DCjq_V&)@G?lYjS-zuxD#7U-Rh{yxW={JW3* zUq+PVE@=qee?+k37 zr2K2~e-t-=`^YXF|JVDN-voLmv%inICjahZ|MmXuBY@t)?eE{N$-n!^U+-JL8t9$t z{=W5^{JW3*Hz4_>Hfykb)4`sla#+mAhMevyBE?%%yOG;3!L+nr-52`YB+F8Xch zhE6Aak9P5hpVjlXdf9V)zN?F$#VH#a`L*rca1D*|vw;nbY0BvNKgoO=m=0!w#o$-Y zVSWEkd~!g1G9*6HaWYPNC>4K^?Dfta?YI)Xi>GbRrEU04dA#Sc-%;=PD!sL@$)3|J zy|vHJOlo%K^7S*5ub-tUgM7={XL)7foKoJ*)?4{|Hs8R?t7cX@A@E@V9<>$kw$ zpuUxO1{ey4fkX$`A0T{^=3f2e_ro1!M`d;?(Uh7kz|fW63>w?@x^{bq?UR3#Bz&r$B#IDj=*&?~9|~6IR(%_AXRJf#^r44jeI-~2?f}n$=fRP& z)Og2x_*6ehdIsIlo>Xi70gi8}m7A=J+k9;0+qVF5O85W0cZlssLu2p_dPvqUfOkN4 z0?&Sc(V!-O`0*~rhP%l4poe5V222D~z(rsMsL8*3$X~Kv308xvz|CMSsL8*3$X~L) z1>6np1CN6zKu!MLL;jNWlVAgQ4rFr~2ZEaXyNCQG>qgKU^aG>87_d|1pND{^F2YeI z;8cqTbp`)lvOWTg1(U&2unhc;$N#aW)Z#%qFCNrGvR)2Wf>q!Sa3`p>|L$S`CF{Gu zdhif<8B~}32w86bjP`@}zT?O)!H6)gUbFvQA^y`SdA|Z)1@D0D#Im^ms93s5gl}kk z#gA+9&hljf%?*cT4WqI}qqP5SRu{+Svbx`0yss=Gn>9+_L%>il3>*QHDcfY4ciZG& z^YK`Z@w<=lmdQM^!i|#m(O@xH0+xamAkl&3q`E>R$z%{}Hohy$KU&iSd`W|3v3m! zu|m6M5vJ{XHlT-Oy$-Ag4}+J$o1iBD?je84dNXJ`nKb}lUoafhasbG?)!e1~vJ25BW>hr+{VPB5*ZW18VZ`9`cv0uLtYEo!~L>6sXC+ zd&pn1ei}RvUIP1^!h9g8$-jHZU$Pzq4ge#;BCrJ1_%_LNqkG`IGNR}tqtu8YeRcT*8RbU!MBue zn6@Jhl9563sA!x@$90pyxPV&!-zEBg$@)R?DtHa_pGO=Bi~=WtYrwCw=<|!{IJ+f2 zsh%teCR9%rXBHjvb`l;N|KQ*{+c~6p!-%Y&4J&s0!;eIX+CA*Gq0x2)A3(B-UBQ=b zDEzs6x9@h|qTb~xj7Q#0lcJ~anC&ZBsot5)4}}<*b8VOvIozBH^9g<%JkrlSa!F^Ctr_G*2E{tTOqEFuUrwI z{H~9ywi5cS-5RpqL+W>%T$i1?I?Fx-=7HVn`L(Y6SL^5K@4CMHndjzTn%mk zFM=-I{$FbN-+O$grLzk2ZtOqo+0ew64GpD(Mn7ogz;<}3sy9XQ?^2X!O_K9V;B7E| z5#sw;DW!fuk}PowI;jj%&byZUV~=W=($=h5^1U1kJD2tkt^ikp zwcv5^W+asCjrioX_~hmIltrh$vVZ-cZL7{n+46`%ZPeDd${$xq^wE8~;u$>LP+)$y!?Xu~ev%Bzi1@UMDF zwkyCIun}wmEuX~)T(*5}zq)&{-2$Ao%SxonQ=Tda1W?1_oS7xdrB8cM5A}Y2pU!6b9{ON~n%kwQ&6*_Vd%=3}5_lPG1Ot}WSVi^fTK2!EWV{M&0MCGD!Lk+11%T(l3m^&X z{UVmbd4$v-h&??-CSeO_KA=;1zJ>m5lqqj%~zMr|Nbf#V{(#M{Nze)(7lln@~6o>{1k_ zmTDaHzmjclFbXUK%fU6^F;KnjCw4y(W^yGtNrkx`XS;+&O*q{3HMU7|ejL0E27Q@$ z4;TWbfIDI#B`rHSN&LSl@jXYyC*{Y*RzLqW+ifztkgtBS-q%_08og;E`&~j?nQ%wW zM-y8)72FDL15bhhe}u;kMu5d2S^tw5>Et9)jO0pkl9-I-N^+8_Oi^oE*D!vME;IHo zwEta-!bC$A-RdRTE&*49hruJD&$al3U*QoeaAz!o#GWT7C&tO9$0yU`ljNTGLVB$A zip5V>ba@>}INar_&3a14i@_2w>NPq71Hnp=MC=n0Ce8iUScuiVhuo)!sPbPMfF<|v%$49kmC$~6NbX>&Is~YazYj{`DgF;R1QWRF7|B-y( z0u#T+dmzDj@E~{@9B@OK{jcs2uKoY*oquRnRT#(5ec!!5+Dyi_Oq6WdqCw`5B{C`_ zYZ~=WRwE($BW)WsoZ4En!e9lJR*0n3vKf`jj6%lf4>Mb<#;T<@0z;%=NDLFP6+{I! z`#kr)clVsvdtcvoOu2LK8K2$r+;iT0?z!LRo_p@O?>(=km%3Xwj3&a!rsoQZ`)S`F zZ7*#Mi}ry>HmC&)m0zfYXgnVlLK~Ftpg#z^VGkJBe?$Lwbj>FI`yc($noB2z%KrB; zfg@L;@?Mn?jr-xYH)#W41#E=v4B&P{)y}Njf8*rllH!GJUNqCz^jsl}OOMp|4d4tq zPX_-@ioT0s2|NH@@GqUWC7jVaAj|WgD~K12C;Hq=dSR0n4QVV>d+zT60e31vBiEs7 z*Hl6@?ts5x?t6>_Kp*r&glo5<|1MoLqy4AF@Pcvf8}EM^*}M-(%|yI^$cjeEb~zQ57k56%{>q?r^2SF%U*8@7*O%eH{uha+Nw^DMgLc>f ztv$2>FrGHR82>dM_XK?Z%VHKap81<|tJ&Y8vyj;DC>BK2WphG*XBhfDR|(Ph;OG42 z6SVg7ybszyeZl=d{6};w{5cjQ@|$kFr5nazkdbYA3DGzSH^VJZ124cP*aAo4Fa3D`t{ZRb22T&XU}U?)Td$On zVi@Gg`v7koI7ch>>Otn&X>;tIp5+5c(f1e(LFJds0|xWpAqZ~!jm!HgU0tK?H?rxu zvW}YU>M$NCuV4>_3vWKoPJBxp4Hs7BDQ6} z8ytT9i$%Y+&m-M)Uelx$|E%R|mQ0 zgK38t|AAWg=rCjcF!VjY4FoR@rnL>5^^VVry0K9=9@CBWx?!X=1*X*LDH-C)WvZmE zoX&7*C%u`@SLmTFv+@6aR#Cv1v-6oLbN&~KuHV1_48d{u1%}}SoP<+w8jSA(8H0j< z=)#QX#%bL+r5h)8&DNzk&kOfz{lfBmAk-n3hq(S3Tt%*ceJF&R&b7taE`R{ z%EwKraG`Z&*WiNkuA!w@b*(Fy3zx&yTR%6=U{zMB z=?k@e$ww#Y5g28l!jj)pOAE3la^%pFj`_2Tx4oWJ*UkH9Zw>2WY3m+j-Ar&@u2?ZzReY=(;2lO)O03h{yXC5|ukI$K%cw=u>` zRa?sCEOk5OH>nN0HMB-G`eo0!t^{wBO~oMf#3W;WH_6$x=hYr=*qE(B#0~4^!wS2w)6Ht84^$%0|{{xviA%Fk? literal 0 HcmV?d00001 diff --git a/makefile b/makefile index c994c84c..9a556171 100644 --- a/makefile +++ b/makefile @@ -587,6 +587,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) DISPLAYVT = ${DISPLAYD}/vt11.c DISPLAY340 = ${DISPLAYD}/type340.c DISPLAYNG = ${DISPLAYD}/ng.c + DISPLAYIII = ${DISPLAYD}/iii.c DISPLAY_OPT += -DUSE_DISPLAY $(VIDEO_CCDEFS) $(VIDEO_LDFLAGS) $(info using libSDL2: $(call find_include,SDL2/SDL)) ifeq (Darwin,$(OSTYPE)) @@ -1994,7 +1995,7 @@ PDP6 = ${PDP6D}/kx10_cpu.c ${PDP6D}/kx10_sys.c ${PDP6D}/kx10_cty.c \ ${PDP6D}/kx10_lp.c ${PDP6D}/kx10_pt.c ${PDP6D}/kx10_cr.c \ ${PDP6D}/kx10_cp.c ${PDP6D}/pdp6_dct.c ${PDP6D}/pdp6_dtc.c \ ${PDP6D}/pdp6_mtc.c ${PDP6D}/pdp6_dsk.c ${PDP6D}/pdp6_dcs.c \ - ${PDP6D}/kx10_dpy.c ${DISPLAYL} ${DISPLAY340} + ${PDP6D}/kx10_dpy.c ${PDP6D}/pdp6_slave.c ${DISPLAYL} ${DISPLAY340} PDP6_OPT = -DPDP6=1 -DUSE_INT64 -I ${PDP6D} -DUSE_SIM_CARD ${DISPLAY_OPT} ${PDP6_DISPLAY_OPT} KA10D = ${SIMHD}/PDP10 @@ -2007,13 +2008,14 @@ KA10 = ${KA10D}/kx10_cpu.c ${KA10D}/kx10_sys.c ${KA10D}/kx10_df.c \ ${KA10D}/kx10_rp.c ${KA10D}/kx10_rc.c ${KA10D}/kx10_dt.c \ ${KA10D}/kx10_dk.c ${KA10D}/kx10_cr.c ${KA10D}/kx10_cp.c \ ${KA10D}/kx10_tu.c ${KA10D}/kx10_rs.c ${KA10D}/ka10_pd.c \ - ${KA10D}/kx10_imp.c ${KA10D}/ka10_tk10.c ${KA10D}/ka10_mty.c \ - ${KA10D}/ka10_imx.c ${KA10D}/ka10_ch10.c ${KA10D}/ka10_stk.c \ - ${KA10D}/ka10_ten11.c ${KA10D}/ka10_auxcpu.c ${KA10D}/ka10_pmp.c \ - ${KA10D}/ka10_dkb.c ${KA10D}/pdp6_dct.c ${KA10D}/pdp6_dtc.c \ - ${KA10D}/pdp6_mtc.c ${KA10D}/pdp6_dsk.c ${KA10D}/pdp6_dcs.c \ - ${KA10D}/ka10_dpk.c ${KA10D}/kx10_dpy.c ${PDP10D}/ka10_ai.c \ - ${DISPLAYL} ${DISPLAY340} + ${KA10D}/kx10_rh.c ${KA10D}/kx10_imp.c ${KA10D}/ka10_tk10.c \ + ${KA10D}/ka10_mty.c ${KA10D}/ka10_imx.c ${KA10D}/ka10_ch10.c \ + ${KA10D}/ka10_stk.c ${KA10D}/ka10_ten11.c ${KA10D}/ka10_auxcpu.c \ + $(KA10D)/ka10_pmp.c ${KA10D}/ka10_dkb.c ${KA10D}/pdp6_dct.c \ + ${KA10D}/pdp6_dtc.c ${KA10D}/pdp6_mtc.c ${KA10D}/pdp6_dsk.c \ + ${KA10D}/pdp6_dcs.c ${KA10D}/ka10_dpk.c ${KA10D}/kx10_dpy.c \ + ${PDP10D}/ka10_ai.c ${KA10D}/ka10_iii.c ${KA10D}/kx10_disk.c \ + ${DISPLAYL} ${DISPLAY340} ${DISPLAYIII} KA10_OPT = -DKA=1 -DUSE_INT64 -I ${KA10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KA10_DISPLAY_OPT} ifneq (${PANDA_LIGHTS},) # ONLY for Panda display. @@ -2029,10 +2031,11 @@ endif KI10 = ${KI10D}/kx10_cpu.c ${KI10D}/kx10_sys.c ${KI10D}/kx10_df.c \ ${KI10D}/kx10_dp.c ${KI10D}/kx10_mt.c ${KI10D}/kx10_cty.c \ ${KI10D}/kx10_lp.c ${KI10D}/kx10_pt.c ${KI10D}/kx10_dc.c \ - ${KI10D}/kx10_rp.c ${KI10D}/kx10_rc.c ${KI10D}/kx10_dt.c \ - ${KI10D}/kx10_dk.c ${KI10D}/kx10_cr.c ${KI10D}/kx10_cp.c \ - ${KI10D}/kx10_tu.c ${KI10D}/kx10_rs.c ${KI10D}/kx10_imp.c \ - ${KI10D}/kx10_dpy.c ${DISPLAYL} ${DISPLAY340} + ${KI10D}/kx10_rh.c ${KI10D}/kx10_rp.c ${KI10D}/kx10_rc.c \ + ${KI10D}/kx10_dt.c ${KI10D}/kx10_dk.c ${KI10D}/kx10_cr.c \ + ${KI10D}/kx10_cp.c ${KI10D}/kx10_tu.c ${KI10D}/kx10_rs.c \ + ${KI10D}/kx10_imp.c ${KI10D}/kx10_dpy.c ${KI10D}/kx10_disk.c \ + ${DISPLAYL} ${DISPLAY340} KI10_OPT = -DKI=1 -DUSE_INT64 -I ${KI10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KI10_DISPLAY_OPT} ifneq (${PANDA_LIGHTS},) # ONLY for Panda display. @@ -2041,12 +2044,13 @@ KI10 += ${KA10D}/ka10_lights.c KI10_LDFLAGS = -lusb-1.0 endif -KL10D = PDP10 +KL10D = ${SIMHD}/PDP10 KL10 = ${KL10D}/kx10_cpu.c ${KL10D}/kx10_sys.c ${KL10D}/kx10_df.c \ ${KL10D}/kx10_mt.c ${KL10D}/kx10_dc.c ${KL10D}/kx10_rh.c \ ${KL10D}/kx10_rp.c ${KL10D}/kx10_tu.c ${KL10D}/kx10_rs.c \ ${KL10D}/kx10_imp.c ${KL10D}/kl10_fe.c ${KL10D}/ka10_pd.c \ - ${KL10D}/ka10_ch10.c ${KL10D}/kx10_lp.c + ${KL10D}/ka10_ch10.c ${KL10D}/kx10_lp.c ${KL10D}/kl10_nia.c \ + ${KL10D}/kx10_disk.c KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) -DUSE_SIM_CARD ${NETWORK_OPT} ATT3B2D = ${SIMHD}/3B2 @@ -2121,7 +2125,7 @@ ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ swtp6800mp-a swtp6800mp-a2 tx-0 ssem b5500 isys8010 isys8020 \ isys8030 isys8024 imds-210 imds-220 imds-225 imds-230 imds-800 imds-810 \ scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 \ - sigma uc15 pdp10-ka pdp10-ki pdp6 + sigma uc15 pdp10-ka pdp10-ki pdp10-kl pdp6 all : ${ALL} @@ -2921,7 +2925,6 @@ endif pdp10-kl : ${BIN}pdp10-kl${EXE} ${BIN}pdp10-kl${EXE} : ${KL10} ${SIM} - #cmake:ignore-target ${MKDIRBIN} ${CC} ${KL10} ${SIM} ${KL10_OPT} ${CC_OUTSPEC} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},kl10))