diff --git a/PDP10/ka10_dkb.c b/PDP10/ka10_dkb.c index 00c80eed..944311ba 100644 --- a/PDP10/ka10_dkb.c +++ b/PDP10/ka10_dkb.c @@ -54,7 +54,6 @@ 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); @@ -65,7 +64,7 @@ int dkb_kmod = 0; DIB dkb_dib = { DKB_DEVNUM, 1, dkb_devio, NULL}; UNIT dkb_unit[] = { - {UDATA (&dkb_svc, UNIT_IDLE, 0) }, + {UDATA (NULL, UNIT_IDLE, 0) }, { 0 } }; @@ -417,11 +416,6 @@ int dkb_keyboard (SIM_KEY_EVENT *kev) } -t_stat dkb_svc( UNIT *uptr) -{ - return SCPE_OK; -} - t_stat dkb_reset( DEVICE *dptr) { if ((dkb_dev.flags & DEV_DIS) == 0) diff --git a/PDP10/ka10_iii.c b/PDP10/ka10_iii.c index 89b29cec..167b57be 100644 --- a/PDP10/ka10_iii.c +++ b/PDP10/ka10_iii.c @@ -1,4 +1,4 @@ -/* ka10_iii.c: Triple III display processor. +/* ka10_iii.c: Triple-I display processor. Copyright (c) 2019-2020, Richard Cornwell @@ -32,7 +32,6 @@ #if NUM_DEVS_III > 0 #include "display/display.h" -#include "display/iii.h" #define III_DEVNUM 0430 @@ -98,6 +97,11 @@ #define CBRT_V 3 #define CSIZE_V 0 +#define MIN_X -512 +#define MAX_X 512 +#define MIN_Y -501 +#define MAX_Y 522 + /* * Character map. * M(x,y) moves pointer to x,y. @@ -380,7 +384,8 @@ t_stat iii_devio(uint32 dev, uint64 *data) { uptr->STATUS |= DATA_FLG; else { iii_instr = *data; - sim_activate(uptr, 10); + /* Process instruction right away to ensure MAR is updated. */ + iii_svc(iii_unit); } sim_debug(DEBUG_DATAIO, &iii_dev, "III %03o DATAO %06o\n", dev, (uint32)*data); break; @@ -398,7 +403,7 @@ iii_svc (UNIT *uptr) float ch_sz; if (uptr->CYCLE > 20) { - iii_cycle(300, 0); + display_age(300, 0); uptr->CYCLE = 0; } else { uptr->CYCLE++; @@ -448,7 +453,7 @@ iii_svc (UNIT *uptr) if (ch == '\t' || ch == 0) continue; if (ch == '\r') { - ox = -512; + ox = MIN_X; continue; } if (ch == '\n') { @@ -493,7 +498,7 @@ iii_svc (UNIT *uptr) nx, ny, sz, br); nx += ox; ny += oy; - if (nx < -512 || nx > 512 || ny < -512 || ny > 512) + if (nx < MIN_X || nx > MAX_X || ny < MIN_Y || ny > MAX_Y) uptr->STATUS |= EDG_FBIT; i = (int)((iii_instr >> 18) & 3); if ((i & 02) == 0 && (iii_sel & 04000) != 0) { /* Check if visible */ @@ -516,7 +521,7 @@ iii_svc (UNIT *uptr) /* Compute relative position. */ nx += ox; ny += oy; - if (nx < -512 || nx > 512 || ny < -512 || ny > 512) + if (nx < MIN_X || nx > MAX_X || ny < MIN_Y || ny > MAX_Y) uptr->STATUS |= EDG_FBIT; /* Check if visible */ if ((iii_instr & 040) == 0 && (iii_sel & 04000) != 0) { @@ -561,7 +566,7 @@ iii_svc (UNIT *uptr) if ((iii_instr & 0100) == 0) { /* Relative mode */ nx += ox; ny += oy; - if (nx < -512 || nx > 512 || ny < -512 || ny > 512) + if (nx < MIN_X || nx > MAX_X || ny < MIN_Y || ny > MAX_Y) uptr->STATUS |= EDG_FBIT; } /* Check if visible */ @@ -637,7 +642,7 @@ t_stat iii_reset (DEVICE *dptr) } else { display_reset(); dptr->units[0].POS = 0; - iii_init(dptr, 1); + display_init(DIS_III, 1, dptr); } return SCPE_OK; } @@ -647,20 +652,20 @@ t_stat iii_reset (DEVICE *dptr) static void draw_point(int x, int y, int b, UNIT *uptr) { - if (x < -512 || x > 512 || y < -512 || y > 512) + if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_X) uptr->STATUS |= WRP_FBIT; - iii_point(x, y, b); + display_point(x - MIN_X, y - MIN_Y, b, 0); } /* 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) + if (x1 < MIN_X || x1 > MAX_X || y1 < MIN_Y || y1 > MAX_Y) uptr->STATUS |= WRP_FBIT; - if (x2 < -512 || x2 > 512 || y2 < -512 || y2 > 512) + if (x2 < MIN_X || x2 > MAX_X || y2 < MIN_Y || y2 > MAX_Y) uptr->STATUS |= WRP_FBIT; - iii_draw_line(x1, y1, x2, y2, b); + display_line(x1 - MIN_X, y1 - MIN_Y, x2 - MIN_X, y2 - MIN_Y, b); } t_stat iii_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) @@ -670,6 +675,6 @@ return SCPE_OK; const char *iii_description (DEVICE *dptr) { - return "Triple III Display"; + return "Triple-I Display"; } #endif diff --git a/PDP10/ka10_imx.c b/PDP10/ka10_imx.c index 8d144b8f..3ea10809 100644 --- a/PDP10/ka10_imx.c +++ b/PDP10/ka10_imx.c @@ -291,7 +291,7 @@ t_stat imx_set_channel (UNIT *uptr, int32 val, CONST char *cptr, void *desc) return r; if (*tptr != 0) { - if (strcasecmp (tptr, "negate") != 0) + if (MATCH_CMD (tptr, "NEGATE")) return SCPE_ARG; negate = 1; } diff --git a/PDP10/ka10_mty.c b/PDP10/ka10_mty.c index 335b76cd..cd97ef60 100644 --- a/PDP10/ka10_mty.c +++ b/PDP10/ka10_mty.c @@ -171,6 +171,9 @@ static t_stat mty_input_svc (UNIT *uptr) tmxr_poll_rx (&mty_desc); + if (status & MTY_DONE) + return SCPE_OK; + for (i = 0; i < MTY_LINES; i++) { /* Round robin scan 32 lines. */ scan = (scan + 1) & 037; @@ -200,6 +203,9 @@ static t_stat mty_output_svc (UNIT *uptr) int i, ch; int32 txdone; + if (status & MTY_DONE) + return SCPE_OK; + for (i = 0; i < MTY_LINES; i++) { /* Round robin scan 32 lines. */ scan = (scan + 1) & 037; @@ -232,9 +238,11 @@ static t_stat mty_output_svc (UNIT *uptr) tmxr_poll_tx (&mty_desc); - /* SIMH will actually schedule this UNIT when output is due - according to the line speed. */ - sim_activate_after (uptr, 1000000); + if ((status & MTY_ODONE) == 0) { + /* SIMH will actually schedule this UNIT when output is due + according to the line speed. */ + sim_activate_after (uptr, 1000000); + } return SCPE_OK; } @@ -245,7 +253,7 @@ static t_stat mty_reset (DEVICE *dptr) sim_debug(DEBUG_CMD, &mty_dev, "Reset\n"); if (mty_unit->flags & UNIT_ATT) { - sim_activate (mty_unit, tmxr_poll); + sim_activate (&mty_unit[0], tmxr_poll); sim_activate_after (&mty_unit[1], 100); } else { sim_cancel (&mty_unit[0]); diff --git a/PDP10/ka10_pmp.c b/PDP10/ka10_pmp.c index 828decaf..659d2583 100644 --- a/PDP10/ka10_pmp.c +++ b/PDP10/ka10_pmp.c @@ -409,7 +409,7 @@ DEVICE pmp_dev = { "PMP", pmp_unit, NULL, pmp_mod, NUM_UNITS_PMP, 8, 15, 1, 8, 8, NULL, NULL, &pmp_reset, NULL, &pmp_attach, &pmp_detach, - &pmp_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_DISK, 0, dev_debug, + &pmp_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug, NULL, NULL, &pmp_help, NULL, NULL, &pmp_description }; @@ -2212,8 +2212,8 @@ pmp_format(UNIT * uptr, int flag) { uptr->CMD |= DK_ATTN; pmp_statusb |= REQ_CH; sim_activate(uptr, 100); - fputc('\n', stderr); fputc('\r', stderr); + fputc('\n', stderr); return 0; } else return 1; @@ -2243,7 +2243,7 @@ pmp_attach(UNIT * uptr, CONST char *file) return SCPE_OK; } - sim_messagef(SCPE_OK, "Drive %03x=%d %d %02x %d\n\r", addr, + sim_messagef(SCPE_OK, "Drive %03x=%d %d %02x %d\r\n", addr, hdr.heads, hdr.tracksize, hdr.devtype, hdr.highcyl); for (i = 0; disk_type[i].name != 0; i++) { tsize = (uint32)((disk_type[i].bpt | 0x1ff) + 1); @@ -2251,7 +2251,7 @@ pmp_attach(UNIT * uptr, CONST char *file) hdr.heads == disk_type[i].heads && hdr.highcyl == disk_type[i].cyl) { if (GET_TYPE(uptr->flags) != i) { /* Ask if we should change */ - fprintf(stderr, "Wrong type %s\n\r", disk_type[i].name); + fprintf(stderr, "Wrong type %s\r\n", disk_type[i].name); if (!get_yn("Update dasd type? [N] ", FALSE)) { detach_unit(uptr); return SCPE_FMT; @@ -2367,7 +2367,7 @@ pmp_set_dev_addr(UNIT * uptr, int32 val, CONST char *cptr, void *desc) /* Update device entry */ uptr->flags &= ~UNIT_ADDR(0xff); uptr->flags |= UNIT_ADDR(newdev); - fprintf(stderr, "Set dev %x\n\r", GET_UADDR(uptr->flags)); + fprintf(stderr, "Set dev %x\r\n", GET_UADDR(uptr->flags)); return r; } diff --git a/PDP10/ka10_stk.c b/PDP10/ka10_stk.c index 4802d901..4c965ec0 100644 --- a/PDP10/ka10_stk.c +++ b/PDP10/ka10_stk.c @@ -148,6 +148,11 @@ static int stk_modifiers (SIM_KEY_EVENT *kev) static int stk_keys (SIM_KEY_EVENT *kev) { + if (kev->state == SIM_KEYPRESS_UP && kev->key == SIM_KEY_F11) { + vid_set_fullscreen (!vid_is_fullscreen ()); + return 1; + } + if (kev->state == SIM_KEYPRESS_UP) return 0; diff --git a/PDP10/ka10_ten11.c b/PDP10/ka10_ten11.c index df87ab9c..bacad4e4 100644 --- a/PDP10/ka10_ten11.c +++ b/PDP10/ka10_ten11.c @@ -37,8 +37,12 @@ /* Rubin 10-11 pager. */ static uint64 ten11_pager[256]; +/* Physical address range of TEN11 moby. */ +t_addr ten11_base = 03000000; +t_addr ten11_end = 04000000; + /* Physical address of 10-11 control page. */ -#define T11CPA 03776000 +#define T11CPA 0776000 //Offset inside TEN11 moby. /* Bits in a 10-11 page table entry. */ #define T11VALID (0400000000000LL) @@ -64,6 +68,8 @@ static t_stat ten11_svc (UNIT *uptr); static t_stat ten11_reset (DEVICE *dptr); static t_stat ten11_attach (UNIT *uptr, CONST char *ptr); static t_stat ten11_detach (UNIT *uptr); +static t_stat ten11_set_base (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat ten11_show_base (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat ten11_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); static const char *ten11_description (DEVICE *dptr); @@ -77,6 +83,8 @@ static REG ten11_reg[] = { }; static MTAB ten11_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "base address", "BASE", + &ten11_set_base, &ten11_show_base }, { 0 } }; @@ -290,6 +298,8 @@ int ten11_read (t_addr addr, uint64 *data) int offset = addr & 01777; int word1, word2; + addr &= RMASK; //Address offset inside moby. + if (addr >= T11CPA) { /* Accessing the control page. */ if (offset >= 0400) { @@ -370,6 +380,8 @@ int ten11_write (t_addr addr, uint64 data) { int offset = addr & 01777; + addr &= RMASK; //Address offset inside moby. + if (addr >= T11CPA) { /* Accessing the control page. */ if (offset >= 0400) { @@ -413,4 +425,28 @@ int ten11_write (t_addr addr, uint64 data) } return 0; } + +static t_stat ten11_set_base (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + t_stat r; + t_value x; + + if (cptr == NULL || *cptr == 0) + return SCPE_ARG; + + x = get_uint (cptr, 8, 03777777, &r); + if (r != SCPE_OK) + return SCPE_ARG; + + ten11_base = (t_addr)(x&03777777); + /* The end of the TEN11 range is the start of the next moby. */ + ten11_end = (ten11_base + 01000000) & LMASK; + return SCPE_OK; +} + +static t_stat ten11_show_base (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fprintf (st, "Base: %011o", ten11_base); + return SCPE_OK; +} #endif diff --git a/PDP10/ka10_tv.c b/PDP10/ka10_tv.c new file mode 100644 index 00000000..719d55a4 --- /dev/null +++ b/PDP10/ka10_tv.c @@ -0,0 +1,130 @@ +/* ka10_tv.c: Stanford TV camera and Spacewar buttons. + + Copyright (c) 2021, 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. + +*/ + +#include "kx10_defs.h" +#include "sim_video.h" + +#if NUM_DEVS_TV > 0 + +#define TV_DEVNUM 0404 + +#define JOY_MAX_UNITS 5 +#define JOY_MAX_AXES 4 +#define JOY_MAX_BUTTONS 4 + +#define JOY_TRIG 5000 + +#define ROTATE_AXIS 0 +#define THRUSTER_AXIS 1 +#define TORPEDO_BUTTON 0 +#define HYPER_BUTTON 1 + +/* CONI bits. */ +#define TPBIT 001LL /* Fire torpedo. */ +#define THRUBT 002LL /* Thruster. */ +#define ROTRBT 004LL /* Rotate right. */ +#define ROTLBT 010LL /* Rotate left. */ +#define HYPRBT 014LL /* Hyperspace = right + left. */ + +static t_stat tv_devio(uint32 dev, uint64 *data); +static t_stat tv_reset (DEVICE *dptr); +static const char *tv_description (DEVICE *dptr); + +static int joy_axes[JOY_MAX_UNITS][JOY_MAX_AXES]; +static int joy_buttons[JOY_MAX_UNITS][JOY_MAX_BUTTONS]; + +DIB tv_dib = { TV_DEVNUM, 1, &tv_devio, NULL }; + +DEVICE tv_dev = { + "TV", NULL, NULL, NULL, + 0, 8, 0, 1, 8, 36, + NULL, NULL, &tv_reset, NULL, NULL, NULL, + &tv_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug, + NULL, NULL, NULL, NULL, NULL, &tv_description +}; + +static void tv_joy_motion(int which, int axis, int value) +{ + if (which < JOY_MAX_UNITS && axis < JOY_MAX_AXES) { + joy_axes[which][axis] = value; + sim_debug (DEBUG_DETAIL, &tv_dev, "Joystick %d axid %d: value %d\n", + which, axis, value); + } +} + +static void tv_joy_button(int which, int button, int state) +{ + if (which < JOY_MAX_UNITS && button < JOY_MAX_BUTTONS) { + joy_buttons[which][button] = state; + sim_debug (DEBUG_DETAIL, &tv_dev, "Joystick %d button %d: state %d\n", + which, button, state); + } +} + +static uint64 tv_buttons (void) +{ + uint64 buttons = 2; /* Needed for unknown reason! */ + int i; + + for (i = 0; i < JOY_MAX_UNITS; i++) { + if (joy_axes[i][ROTATE_AXIS] > JOY_TRIG) + buttons ^= ROTRBT << (4 * i); + else if (joy_axes[i][ROTATE_AXIS] < -JOY_TRIG) + buttons ^= ROTLBT << (4 * i); + if (joy_axes[i][THRUSTER_AXIS] < -JOY_TRIG) + buttons ^= THRUBT << (4 * i); + if (joy_buttons[i][TORPEDO_BUTTON]) + buttons ^= TPBIT << (4 * i); + if (joy_buttons[i][HYPER_BUTTON]) + buttons ^= HYPRBT << (4 * i); + } + + return buttons; +} + +t_stat tv_devio(uint32 dev, uint64 *data) +{ + switch(dev & 07) { + case CONI|4: + *data = tv_buttons (); + sim_debug (DEBUG_CONI, &tv_dev, "%07llo\n", *data); + break; + } + + return SCPE_OK; +} + +static t_stat tv_reset (DEVICE *dptr) +{ + memset (joy_axes, 0, sizeof joy_axes); + memset (joy_buttons, 0, sizeof joy_buttons); + vid_register_gamepad_motion_callback (tv_joy_motion); + vid_register_gamepad_button_callback (tv_joy_button); + return SCPE_OK; +} + +const char *tv_description (DEVICE *dptr) +{ + return "Stanford TV camera and Spacewar buttons"; +} +#endif diff --git a/PDP10/kl10_fe.c b/PDP10/kl10_fe.c index a4915cdf..74e4c42d 100644 --- a/PDP10/kl10_fe.c +++ b/PDP10/kl10_fe.c @@ -236,7 +236,7 @@ struct _dte_queue { uint16 data[258]; /* Data packet */ uint16 sdev; /* Secondary device code */ uint16 sz; /* Byte size */ -} dte_in[32], dte_out[32]; +} dte_in[16], dte_out[16]; int32 dte_in_ptr; int32 dte_in_cmd; @@ -589,7 +589,7 @@ t_stat dte_devio(uint32 dev, uint64 *data) { 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); + sim_activate(&dte_unit[0], 100); } if (dte_unit[0].STATUS & (DTE_10DB|DTE_11DN|DTE_10DN|DTE_11ER|DTE_10ER)) set_interrupt(dev, dte_unit[0].STATUS); @@ -660,6 +660,7 @@ void dte_second(UNIT *uptr) { return; } #endif + /* Do it */ sim_debug(DEBUG_DETAIL, &dte_dev, "CTY secondary %012llo\n", word); switch(word & SEC_CMDMSK) { @@ -673,7 +674,8 @@ void dte_second(UNIT *uptr) { if (ch != 0) { cty_out.buff[cty_out.in_ptr] = ch & 0x7f; inci(&cty_out); - sim_activate(&dte_unit[1], 200); + if (!sim_is_active(&dte_unit[1])) + sim_activate(&dte_unit[1], 200); } M[SEC_DTCHR + base] = ch; M[SEC_DTMTD + base] = FMASK; @@ -683,6 +685,7 @@ void dte_second(UNIT *uptr) { 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); @@ -697,6 +700,7 @@ enter_pri: M[SEC_DTCMD + base] = 0; M[SEC_DTFLG + base] = FMASK; uptr->STATUS &= ~DTE_11DB; + tty_reset(&tty_dev); return; case SEC_SETDDT: /* Read character from console */ @@ -857,7 +861,7 @@ void dte_primary(UNIT *uptr) { return; /* Check if there is room for another packet */ - if (((dte_in_ptr + 1) & 0x1f) == dte_in_cmd) { + if (((dte_in_ptr + 1) & 0xf) == dte_in_cmd) { /* If not reschedule ourselves */ sim_activate(uptr, 100); return; @@ -886,6 +890,7 @@ error: sim_debug(DEBUG_DETAIL, &dte_dev, "DTE: error %012llo\n", word); return; } + sim_debug(DEBUG_DETAIL, &dte_dev, "DTE: status word %012llo\n", word); if ((word & PRI_CMT_QP) == 0) { goto error; @@ -895,7 +900,7 @@ error: if ((word & PRI_CMT_IP) != 0) { /* Transfer from 10 */ if ((uptr->STATUS & DTE_IND) == 0) { - fprintf(stderr, "DTE out of sync\n\r"); + fprintf(stderr, "DTE out of sync\r\n"); return; } /* Get size of transfer */ @@ -920,7 +925,7 @@ error: cnt--; } uptr->STATUS &= ~DTE_IND; - dte_in_ptr = (dte_in_ptr + 1) & 0x1f; + dte_in_ptr = (dte_in_ptr + 1) & 0xf; } else { /* Transfer from 10 */ in->dptr = 0; @@ -965,7 +970,7 @@ error: if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word)) goto error; } else { - dte_in_ptr = (dte_in_ptr + 1) & 0x1f; + dte_in_ptr = (dte_in_ptr + 1) & 0xf; } } word &= ~PRI_CMT_TOT; @@ -988,7 +993,7 @@ dte_function(UNIT *uptr) /* Check if queue is empty */ while (dte_in_cmd != dte_in_ptr) { - if (((dte_out_res + 1) & 0x1f) == dte_out_ptr) { + if (((dte_out_res + 1) & 0xf) == dte_out_ptr) { sim_debug(DEBUG_DATA, &dte_dev, "DTE: func out full %d %d\n", dte_out_res, dte_out_ptr); return; @@ -1016,10 +1021,15 @@ dte_function(UNIT *uptr) break; case PRI_EM2TI: /* Replay to initial message. */ - case PRI_EMLBE: /* Acknowledge line */ /* Should never get these */ break; + case PRI_EMLBE: /* Acknowledge line */ + data1[0] = 0; + if (dte_queue(PRI_EMLBE, dev, 1, data1) == 0) + return; + break; + case PRI_EMHDR: /* Here is date and time */ /* Ignore this function */ break; @@ -1109,8 +1119,10 @@ cty: data1[0] = 0; if (cmd->sz > 8) cmd->dcnt += cmd->dcnt; + while (cmd->dptr < cmd->dcnt) { ch = (int32)(cmd->data[cmd->dptr >> 1]); + sim_debug(DEBUG_DATA, &dte_dev,"CTY data %o\n", ch); if ((cmd->dptr & 1) == 0) ch >>= 8; ch &= 0177; @@ -1127,6 +1139,7 @@ cty: if (cmd->dptr != cmd->dcnt) return; } + break; case PRI_EMSNA: /* Send all (ttys) */ @@ -1398,7 +1411,7 @@ cty: } /* Mark command as finished */ cmd->cnt = 0; - dte_in_cmd = (dte_in_cmd + 1) & 0x1F; + dte_in_cmd = (dte_in_cmd + 1) & 0xf; } } @@ -1482,7 +1495,7 @@ void dte_transfer(UNIT *uptr) { } } out->cnt = 0; - dte_out_ptr = (dte_out_ptr + 1) & 0x1f; + dte_out_ptr = (dte_out_ptr + 1) & 0xf; done: uptr->STATUS |= DTE_10DN; set_interrupt(DTE_DEVNUM, uptr->STATUS); @@ -1623,7 +1636,7 @@ dte_input() /* 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++) { + ((dte_out_res + 1) & 0xf) != dte_out_ptr; ln++) { struct _buffer *itty = &tty_in[ln]; while (not_empty(itty)) { ch = itty->buff[itty->out_ptr]; @@ -1671,7 +1684,7 @@ dte_queue(int func, int dev, int dcnt, uint16 *data) struct _dte_queue *out; /* Check if room in queue for this packet. */ - if (((dte_out_res + 1) & 0x1f) == dte_out_ptr) { + if (((dte_out_res + 1) & 0xf) == dte_out_ptr) { sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d out full\n", dte_out_res, dte_out_ptr); return 0; } @@ -1688,7 +1701,7 @@ dte_queue(int func, int dev, int dcnt, uint16 *data) *dp++ = *data++; } /* Advance pointer to next function */ - dte_out_res = (dte_out_res + 1) & 0x1f; + dte_out_res = (dte_out_res + 1) & 0xf; return 1; } @@ -1965,7 +1978,7 @@ lp20_printline(UNIT *uptr, int nl) { int trim = 0; /* Trim off trailing blanks */ - while (uptr->COL >= 0 && lp20_buffer[uptr->COL - 1] == ' ') { + while (uptr->COL > 0 && lp20_buffer[uptr->COL - 1] == ' ') { uptr->COL--; trim = 1; } @@ -2197,15 +2210,11 @@ lp20_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc) 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, "Line Printer (LP20)\n\n"); +fprintf (st, "The line printer (LP20) writes data to a disk file.\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, " sim> SET %s 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); @@ -2274,16 +2283,15 @@ t_stat ttyo_svc (UNIT *uptr) t_stat r; int32 ln; TMLN *lp; + int f; - 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 (lp->conn == 0 || (tty_unit[0].flags & UNIT_ATT) == 0) { if (not_empty(optr)) { optr->out_ptr = optr->in_ptr = 0; tty_done[ln] = 1; @@ -2292,7 +2300,8 @@ t_stat ttyo_svc (UNIT *uptr) } if (empty(optr)) continue; - while (not_empty(optr)) { + f = 1; + while (f && 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); @@ -2301,11 +2310,16 @@ t_stat ttyo_svc (UNIT *uptr) inco(optr); else if (r == SCPE_LOST) { optr->out_ptr = optr->in_ptr = 0; - continue; - } else - continue; + f = 0; + } else if (r == SCPE_STALL) { + f = 0; + } else { + break; + } + } + if (empty(optr)) { + tty_done[ln] = 1; } - tty_done[ln] = 1; } return SCPE_OK; } @@ -2314,6 +2328,12 @@ t_stat ttyo_svc (UNIT *uptr) t_stat tty_reset (DEVICE *dptr) { + int i; + for (i = 0; i < tty_desc.lines; i++) { + tty_done[i] = 0; + tty_out[i].out_ptr = tty_out[i].in_ptr = 0; + tty_in[i].out_ptr = tty_in[i].in_ptr = 0; + } return SCPE_OK; } diff --git a/PDP10/kl10_nia.c b/PDP10/kl10_nia.c index b1972e67..82d9765a 100644 --- a/PDP10/kl10_nia.c +++ b/PDP10/kl10_nia.c @@ -874,7 +874,7 @@ void nia_load_ptt() nia_error(EBSERR); return; } - sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %012llo %012llo\n\r", + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %012llo %012llo\n", n, word1, word2); if (word1 & SMASK) { uint16 type; @@ -887,7 +887,7 @@ void nia_load_ptt() addr++; } for (i = 0; i < n; i++) - sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %04x %010o\n\r", + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %04x %010o\n", n, nia_data.ptt_proto[i], nia_data.ptt_head[i]); nia_data.ptt_n = n; } @@ -925,7 +925,7 @@ void nia_load_mcast() } 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); + sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load mcast%d: %s\n",i,buffer); } nia_data.macs_n = n - 2; if (nia_recv_uptr->flags & UNIT_ATT) diff --git a/PDP10/ks10_ch11.c b/PDP10/ks10_ch11.c new file mode 100644 index 00000000..3e763328 --- /dev/null +++ b/PDP10/ks10_ch11.c @@ -0,0 +1,562 @@ +/* ks10_ch11.c: CH11 Chaosnet interface. + ------------------------------------------------------------------------------ + + Copyright (c) 2022, Richard Cornwell, original by Lars Brinkhoff. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + +*/ + +#include "kx10_defs.h" +#include "sim_tmxr.h" + +#ifndef NUM_DEVS_CH11 +#define NUM_DEVS_CH11 0 +#endif + +#if (NUM_DEVS_CH11 > 0) + +/* CSR 076410 */ + +#define CSR_BSY 0000001 /* Xmit busy RO */ +#define CSR_LUP 0000002 /* Loop back R/W */ +#define CSR_SPY 0000004 /* Receive msgs from any destination R/W */ +#define CSR_RCL 0000010 /* Clear receiver WO */ +#define CSR_REN 0000020 /* Receiver int enable R/W */ +#define CSR_TEN 0000040 /* Transmitter int enable R/W */ +#define CSR_TAB 0000100 /* Transmit abort by conflict RO */ +#define CSR_TDN 0000200 /* Transmit Done */ +#define CSR_TCL 0000400 /* Clear transmitter WO */ +#define CSR_LOS 0017000 /* Lost count RO */ +#define CSR_RST 0020000 /* I/O Reset WO */ +#define CSR_ERR 0040000 /* CRC Error RO */ +#define CSR_RDN 0100000 /* Receive done */ + + +/* MY # 764142 Host number RO */ + +/* WBF 764142 Write bufffer WO */ + +/* RBF 764144 Read buffer RO */ + +/* RBC 764146 Receive bit counter RO */ + +/* XMT 764152 Read initiates transmission */ + + +#define CHUDP_HEADER 4 +#define IOLN_CH 020 +#define DBG_TRC 0x0001 +#define DBG_REG 0x0002 +#define DBG_PKT 0x0004 +#define DBG_DAT 0x0008 +#define DBG_INT 0x0010 +#define DBG_ERR 0x0020 + +int ch11_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +int ch11_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +uint16 ch11_checksum (const uint8 *p, int count); +void ch11_validate (const uint8 *p, int count); +t_stat ch11_transmit (struct pdp_dib *dibp); +void ch11_receive (struct pdp_dib *dibp); +void ch11_clear (struct pdp_dib *dibp); +t_stat ch11_svc(UNIT *); +t_stat ch11_reset (DEVICE *); +t_stat ch11_attach (UNIT *, CONST char *); +t_stat ch11_detach (UNIT *); +t_stat ch11_show_peer (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat ch11_set_peer (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat ch11_show_node (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +t_stat ch11_set_node (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +t_stat ch11_help (FILE *, DEVICE *, UNIT *, int32, const char *); +t_stat ch11_help_attach (FILE *, DEVICE *, UNIT *, int32, const char *); +const char *ch11_description (DEVICE *); + +static char peer[256]; +static int address; +static uint16 ch11_csr; +static int rx_count; +static int tx_count; +static uint8 rx_buffer[512+100]; +static uint8 tx_buffer[512+100]; + +TMLN ch11_lines[1] = { {0} }; +TMXR ch11_tmxr = { 1, NULL, 0, ch11_lines}; + +UNIT ch11_unit[] = { + {UDATA (&ch11_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, +}; + +REG ch11_reg[] = { + { ORDATA(CSR, ch11_csr, 16)}, + { GRDATAD(RXCNT, rx_count, 16, 16, 0, "Receive word count"), REG_FIT|REG_RO}, + { GRDATAD(TXCNT, tx_count, 16, 16, 0, "Transmit word count"), REG_FIT|REG_RO}, + { BRDATAD(RXBUF, rx_buffer, 16, 8, sizeof rx_buffer, "Receive packet buffer"), REG_FIT}, + { BRDATAD(TXBUF, tx_buffer, 16, 8, sizeof tx_buffer, "Transmit packet buffer"), REG_FIT}, + { BRDATAD(PEER, peer, 16, 8, sizeof peer, "Network peer"), REG_HRO}, + { GRDATAD(NODE, address, 16, 16, 0, "Node address"), REG_HRO}, + { NULL } }; + +MTAB ch11_mod[] = { + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of CH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of CH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of CH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of CH11" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PEER", "PEER", + &ch11_set_peer, &ch11_show_peer, NULL, "Remote host name and port" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "NODE", "NODE", + &ch11_set_node, &ch11_show_node, NULL, "Chaosnet node address" }, + { 0 }, +}; + +DIB ch11_dib = { 0764140, 017, 0270, 6, 3, &ch11_read, &ch11_write, NULL, 0, 0 }; + +DEBTAB ch11_debug[] = { + { "DETAIL", DEBUG_DETAIL,"I/O operations"}, + { "TRC", DBG_TRC, "Detailed trace" }, + { "REG", DBG_REG, "Hardware registers" }, + { "PKT", DBG_PKT, "Packets" }, + { "DAT", DBG_DAT, "Packet data" }, + { "INT", DBG_INT, "Interrupts" }, + { "ERR", DBG_ERR, "Error conditions" }, + { 0 } +}; + +DEVICE ch11_dev = { + "CH", ch11_unit, ch11_reg, ch11_mod, + 1, 8, 16, 1, 8, 16, + NULL, NULL, &ch11_reset, + NULL, &ch11_attach, &ch11_detach, + &ch11_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX, + 0, ch11_debug, NULL, NULL, &ch11_help, &ch11_help_attach, NULL, + &ch11_description + }; + +int +ch11_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + int i; + + addr &= dibp->uba_mask; + sim_debug(DEBUG_DETAIL, dptr, "CH11 write %06o %06o %o\n", + addr, data, access); + + switch (addr & 016) { + case 000: /* CSR */ + if (data & CSR_RST) { + sim_debug (DBG_REG, &ch11_dev, "Reset\n"); + ch11_clear (dibp); + } + ch11_csr &= ~(CSR_REN|CSR_TEN|CSR_SPY); + ch11_csr |= data & (CSR_REN|CSR_TEN|CSR_SPY); + if (data & CSR_RCL) { + sim_debug (DBG_REG, &ch11_dev, "Clear RX\n"); + ch11_csr &= ~CSR_RDN; + rx_count = 0; + ch11_lines[0].rcve = TRUE; + uba_clr_irq(dibp, dibp->uba_vect); + } + if (data & CSR_TCL) { + sim_debug (DBG_REG, &ch11_dev, "Clear TX\n"); + tx_count = 0; + ch11_csr |= CSR_TDN; + if (ch11_csr & CSR_TEN) + uba_set_irq(dibp, dibp->uba_vect); + } + break; + + case 002: /* Write buffer */ + ch11_csr &= ~CSR_TDN; + if (tx_count < 512) { + i = CHUDP_HEADER + tx_count; + tx_buffer[i] = (data >> 8) & 0xff; + tx_buffer[i+1] = data & 0xff; + tx_count+=2; + sim_debug (DBG_DAT, &ch11_dev, "Write buffer word %d:%02x %02x %06o %06o\n", + tx_count, tx_buffer[i], tx_buffer[i+1], data, ch11_csr); + } else { + sim_debug (DBG_ERR, &ch11_dev, "Write buffer overflow\n"); + } + break; + case 004: /* Read buffer */ + case 006: /* Bit count */ + case 012: /* Start transmission */ + case 010: /* Empty */ + case 014: /* Empty */ + case 016: /* Empty */ + break; + } + return 0; +} + +int +ch11_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + int i; + + addr &= dibp->uba_mask; + *data = 0; + switch (addr & 016) { + case 000: /* CSR */ + *data = ch11_csr; + break; + + case 002: /* Host number */ + *data = address; + break; + + case 004: /* Read buffer */ + if (rx_count == 0) { + *data = 0; + sim_debug (DBG_ERR, &ch11_dev, "Read empty buffer\n"); + } else { + i = 512-rx_count; + ch11_csr &= ~CSR_RDN; + uba_clr_irq(dibp, dibp->uba_vect); + *data = ((uint64)(rx_buffer[i]) & 0xff) << 8; + *data |= ((uint64)(rx_buffer[i+1]) & 0xff); + rx_count-=2; + sim_debug (DBG_DAT, &ch11_dev, "Read buffer word %d:%02x %02x %06o %06o\n", + rx_count, rx_buffer[i], rx_buffer[i+1], *data, ch11_csr); + } + break; + + case 006: /* Bit count */ + *data = ((512 - rx_count) - 1) & 07777; + break; + case 012: /* Start transmission */ + sim_debug (DBG_REG, &ch11_dev, "XMIT TX\n"); + ch11_transmit(dibp); + break; + + case 010: /* Empty */ + case 014: /* Empty */ + case 016: /* Empty */ + break; + } + sim_debug(DEBUG_DETAIL, dptr, "CH11 read %06o %06o %o\n", + addr, *data, access); + return 0; +} + +uint16 +ch11_checksum (const uint8 *p, int count) +{ + int32 sum = 0; + + while (count > 1) { + sum += (p[0]<<8) | p[1]; + p += 2; + count -= 2; + } + + if ( count > 0) + sum += p[0]; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return (~sum) & 0xffff; +} + +void +ch11_validate (const uint8 *p, int count) +{ + uint16 chksum; + int size; + + sim_debug (DBG_TRC, &ch11_dev, "Packet opcode: %02x\n", p[0]); + sim_debug (DBG_TRC, &ch11_dev, "MBZ: %02x\n", p[1]); + sim_debug (DBG_TRC, &ch11_dev, "Forwarding count: %02x\n", p[2] >> 4); + size = ((p[2] & 0xF) << 8) + p[3]; + sim_debug (DBG_TRC, &ch11_dev, "Packet size: %03x\n", size); + sim_debug (DBG_TRC, &ch11_dev, "Destination address: %o\n", (p[4] << 8) + p[5]); + sim_debug (DBG_TRC, &ch11_dev, "Destination index: %02x\n", (p[6] << 8) + p[7]); + sim_debug (DBG_TRC, &ch11_dev, "Source address: %o\n", (p[8] << 8) + p[9]); + sim_debug (DBG_TRC, &ch11_dev, "Source index: %02x\n", (p[10] << 8) + p[11]); + sim_debug (DBG_TRC, &ch11_dev, "Packet number: %02x\n", (p[12] << 8) + p[13]); + sim_debug (DBG_TRC, &ch11_dev, "Acknowledgement: %02x\n", (p[14] << 8) + p[15]); + + if (p[1] != 0) + sim_debug (DBG_ERR, &ch11_dev, "Bad packet\n"); + + chksum = ch11_checksum (p, count); + if (chksum != 0) { + sim_debug (DBG_ERR, &ch11_dev, "Checksum error: %04x\n", chksum); + ch11_csr |= CSR_ERR; + } else + sim_debug (DBG_TRC, &ch11_dev, "Checksum: %05o\n", chksum); +} + +t_stat +ch11_transmit (struct pdp_dib *dibp) +{ + size_t len; + t_stat r; + int i = CHUDP_HEADER + tx_count; + uint16 chk; + + if (tx_count > (512 - CHUDP_HEADER)) { + sim_debug (DBG_PKT, &ch11_dev, "Pack size failed, %d bytes.\n", (int)tx_count); + ch11_csr |= CSR_ERR; + return SCPE_INCOMP; + } + tx_buffer[i] = tx_buffer[8+CHUDP_HEADER]; + tx_buffer[i+1] = tx_buffer[9+CHUDP_HEADER]; + tx_count += 2; + chk = ch11_checksum(tx_buffer + CHUDP_HEADER, tx_count); + tx_buffer[i+2] = (chk >> 8) & 0xff; + tx_buffer[i+3] = chk & 0xff; + tx_count += 2; + + tmxr_poll_tx (&ch11_tmxr); + len = CHUDP_HEADER + (size_t)tx_count; + r = tmxr_put_packet_ln (&ch11_lines[0], (const uint8 *)&tx_buffer, len); + if (r == SCPE_OK) { + sim_debug (DBG_PKT, &ch11_dev, "Sent UDP packet, %d bytes.\n", (int)len); + tmxr_poll_tx (&ch11_tmxr); + } else { + sim_debug (DBG_ERR, &ch11_dev, "Sending UDP failed: %d.\n", r); + ch11_csr |= CSR_TAB; + } + tx_count = 0; + return SCPE_OK; +} + +void +ch11_receive (struct pdp_dib *dibp) +{ + size_t count; + const uint8 *p; + uint16 dest; + + tmxr_poll_rx (&ch11_tmxr); + if (tmxr_get_packet_ln (&ch11_lines[0], &p, &count) != SCPE_OK) { + sim_debug (DBG_ERR, &ch11_dev, "TMXR error receiving packet\n"); + return; + } + if (p == NULL) + return; + dest = ((p[4+CHUDP_HEADER] & 0xff) << 8) + (p[5+CHUDP_HEADER] & 0xff); + + sim_debug (DBG_PKT, &ch11_dev, "Received UDP packet, %d bytes for: %o\n", (int)count, dest); + /* Check if packet for us. */ + if (dest != address && dest != 0 && (ch11_csr & CSR_SPY) == 0) + return; + + if ((CSR_RDN & ch11_csr) == 0) { + count = (count + 1) & 0776; + memcpy (rx_buffer + (512 - count), p, count); + rx_count = count; + sim_debug (DBG_TRC, &ch11_dev, "Rx count, %d\n", rx_count); + ch11_validate (p + CHUDP_HEADER, count - CHUDP_HEADER); + ch11_csr |= CSR_RDN; + if (ch11_csr & CSR_REN) { + sim_debug (DBG_INT, &ch11_dev, "RX Interrupt\n"); + uba_set_irq(dibp, dibp->uba_vect); + } + ch11_lines[0].rcve = FALSE; + sim_debug (DBG_TRC, &ch11_dev, "Rx off\n"); + } else { + sim_debug (DBG_ERR, &ch11_dev, "Lost packet\n"); + if ((ch11_csr & CSR_LOS) != CSR_LOS) + ch11_csr = (ch11_csr & ~CSR_LOS) | (CSR_LOS & (ch11_csr + 01000)); + } +} + +void +ch11_clear (struct pdp_dib *dibp) +{ + ch11_csr = CSR_TDN; + rx_count = 0; + tx_count = 0; + + tx_buffer[0] = 1; /* CHUDP header */ + tx_buffer[1] = 1; + tx_buffer[2] = 0; + tx_buffer[3] = 0; + ch11_lines[0].rcve = TRUE; + + uba_clr_irq(dibp, dibp->uba_vect); +} + +t_stat +ch11_svc(UNIT *uptr) +{ + DEVICE *dptr = find_dev_from_unit (uptr); + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + + sim_clock_coschedule (uptr, 1000); + (void)tmxr_poll_conn (&ch11_tmxr); + if (ch11_lines[0].conn) { + ch11_receive (dibp); + } + if (tx_count == 0) { + ch11_csr |= CSR_TDN; + if (ch11_csr & CSR_TEN) { + sim_debug (DBG_INT, &ch11_dev, "RX Interrupt\n"); + uba_set_irq(dibp, dibp->uba_vect); + } + } + return SCPE_OK; +} + +t_stat ch11_attach (UNIT *uptr, CONST char *cptr) +{ + char linkinfo[256]; + t_stat r; + + ch11_dev.dctrl |= 0xF77F0000; + if (address == -1) + return sim_messagef (SCPE_2FARG, "Must set Chaosnet NODE address first \"SET CH NODE=val\"\n"); + if (peer[0] == '\0') + return sim_messagef (SCPE_2FARG, "Must set Chaosnet PEER \"SET CH PEER=host:port\"\n"); + + snprintf (linkinfo, sizeof(linkinfo), "Buffer=%d,UDP,%s,PACKET,Connect=%.*s,Line=0", + (int)sizeof tx_buffer, cptr, (int)(sizeof(linkinfo) - (45 + strlen(cptr))), peer); + r = tmxr_attach (&ch11_tmxr, uptr, linkinfo); + if (r != SCPE_OK) { + sim_debug (DBG_ERR, &ch11_dev, "TMXR error opening master\n"); + return sim_messagef (r, "Error Opening: %s\n", peer); + } + + uptr->filename = (char *)realloc (uptr->filename, 1 + strlen (cptr)); + strcpy (uptr->filename, cptr); + sim_activate (uptr, 1000); + return SCPE_OK; +} + +t_stat ch11_detach (UNIT *uptr) +{ + sim_cancel (uptr); + tmxr_detach (&ch11_tmxr, uptr); + return SCPE_OK; +} + +t_stat ch11_reset (DEVICE *dptr) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + + ch11_clear (dibp); + if (ch11_unit[0].flags & UNIT_ATT) + sim_activate (&ch11_unit[0], 100); + return SCPE_OK; +} + +t_stat ch11_show_peer (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ + fprintf (st, "peer=%s", peer[0] ? peer : "unspecified"); + return SCPE_OK; +} + +t_stat ch11_set_peer (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + char host[256], port[256]; + + if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + if (sim_parse_addr (cptr, host, sizeof host, NULL, port, sizeof port, NULL, NULL)) + return SCPE_ARG; + if (host[0] == '\0') + return SCPE_ARG; + + strncpy (peer, cptr, sizeof(peer) - 1); + return SCPE_OK; +} + +t_stat ch11_show_node (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ + if (address == -1) + fprintf (st, "node=unspecified"); + else + fprintf (st, "node=%o", address); + return SCPE_OK; +} + +t_stat ch11_set_node (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ + t_stat r; + int x; + + if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; + if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; + + x = (int)get_uint (cptr, 8, 0177777, &r); + if (r != SCPE_OK) + return SCPE_ARG; + + address = x; + return SCPE_OK; +} + +const char *ch11_description (DEVICE *dptr) +{ + return "CH11 Chaosnet interface"; +} + +t_stat ch11_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "CH11 Chaosnet interface\n\n"); + fprintf (st, "It's a network interface for MIT's Chaosnet. Options allow\n"); + fprintf (st, "control of the node address and network peer. The node address must\n"); + fprintf (st, "be a 16-bit octal number.\n"); + fprint_set_help (st, dptr); + fprintf (st, "\nConfigured options and controller state can be displayed with:\n"); + fprint_show_help (st, dptr); + fprintf (st, "\nThe CH11 simulation will encapsulate Chaosnet packets in UDP or TCP.\n"); + fprintf (st, "To access the network, the simulated Chaosnet interface must be attached\n"); + fprintf (st, "to a network peer.\n\n"); + ch11_help_attach (st, dptr, uptr, flag, cptr); + fprintf (st, "Software that runs on SIMH that supports this device include:\n"); + fprintf (st, " - ITS, the PDP-10 Incompatible Timesharing System\n"); + fprintf (st, "Outside SIMH, there's KLH10 and Lisp machine simulators. Various\n"); + fprintf (st, "encapsulating transport mechanisms exist: UDP, IP, Ethernet.\n\n"); + fprintf (st, "Documentation:\n"); + fprintf (st, "https://lm-3.github.io/amber.html#Hardware-Programming-Documentation\n\n"); + return SCPE_OK; +} + +t_stat ch11_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + fprintf (st, "To configure CH11, first set the local Chaosnet node address, and\n"); + fprintf (st, "the peer:\n\n"); + fprintf (st, " sim> SET CH NODE=\n"); + fprintf (st, " sim> SET CH PEER=:\n\n"); + fprintf (st, "Then, attach a local port. By default UDP is used:\n\n"); + fprintf (st, " sim> ATTACH CH \n\n"); + fprintf (st, "If TCP is desired, add \"TCP\":\n\n"); + fprintf (st, " sim> ATTACH CH ,TCP\n\n"); + return SCPE_OK; +} +#endif diff --git a/PDP10/ks10_cty.c b/PDP10/ks10_cty.c new file mode 100644 index 00000000..ceba154c --- /dev/null +++ b/PDP10/ks10_cty.c @@ -0,0 +1,282 @@ +/* ks10_cty.c: KS-10 front end (console terminal) simulator + + Copyright (c) 2021, 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 KS +#define UNIT_DUMMY (1 << UNIT_V_UF) + +#define STATUS 031 +#define CTY_IN 032 +#define CTY_OUT 033 +#define KLINK_IN 034 +#define KLINK_OUT 035 +#define BOOT_ADDR 036 +#define BOOT_DRIVE 037 +#define MAG_FMT 040 + +#define KA_FAIL 0000000000001LL /* Keep Alive failed to change */ +#define FORCE_RELOAD 0000000000002LL /* Force reload */ +#define PWR_FAIL1 0000000000004LL /* Power failure */ +#define BOOT_SW 0000000000010LL /* Boot switch */ +#define KEEP_ALIVE 0000000177400LL /* Keep alive */ +#define TRAPS_ENB 0000040000000LL /* Traps enabled */ +#define ONE_MS 0000100000000LL /* 1ms enabled */ +#define CACHE_ENB 0000200000000LL /* Cache enable */ +#define DP_PAR_ENB 0000400000000LL /* DP parity error enable */ +#define CRAM_PAR_ENB 0001000000000LL /* CRAM parity error enable */ +#define PAR_ENB 0002000000000LL /* Parity error detect enable */ +#define KLINK_ENB 0004000000000LL /* Klink active */ +#define EX_KEEP_ALV 0010000000000LL /* Examine Keep Alive */ +#define RELOAD 0020000000000LL /* Reload */ + +#define CTY_CHAR 0000000000400LL /* Character pending */ +#define KLINK_CHAR 0000000000400LL /* Character pending */ +#define KLINK_ACT 0000000001000LL /* KLINK ACTIVE */ +#define KLINK_HANG 0000000001400LL /* KLINK HANGUP */ + +extern int32 tmxr_poll; +t_stat ctyi_svc (UNIT *uptr); +t_stat ctyo_svc (UNIT *uptr); +t_stat ctyrtc_srv(UNIT * uptr); +t_stat cty_reset (DEVICE *dptr); +t_stat cty_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +const char *cty_description (DEVICE *dptr); +uint64 keep_alive = 0; +int keep_num = 0; + +extern DEVICE *rh_boot_dev; +extern int rh_boot_unit; + +static int32 rtc_tps = 1; + +MTAB cty_mod[] = { + { UNIT_DUMMY, 0, NULL, "STOP", &cty_stop_os }, + { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, + { 0 } + }; + +UNIT cty_unit[] = { + { UDATA (&ctyo_svc, TT_MODE_7B, 0), 20000}, + { UDATA (&ctyi_svc, TT_MODE_7B|UNIT_DIS, 0), 4000 }, + { UDATA (&ctyrtc_srv, UNIT_IDLE|UNIT_DIS, 0), 1000 } + }; + +REG cty_reg[] = { + {HRDATAD(WRU, sim_int_char, 8, "interrupt character") }, + { 0 }, + }; + + +DEVICE cty_dev = { + "CTY", cty_unit, cty_reg, cty_mod, + 3, 10, 31, 1, 8, 8, + NULL, NULL, &cty_reset, + NULL, NULL, NULL, NULL, DEV_DEBUG, 0, dev_debug, + NULL, NULL, &cty_help, NULL, NULL, &cty_description + }; + +void +cty_wakeup() +{ + sim_debug(DEBUG_EXP, &cty_dev, "CTY wakeup\n"); + sim_activate(&cty_unit[0], cty_unit[0].wait); +} + + +/* Check for input from CTY and put on queue. */ +t_stat ctyi_svc (UNIT *uptr) +{ + uint64 buffer; + int32 ch; + + sim_clock_coschedule (uptr, tmxr_poll * 3); + + if (Mem_read_word(CTY_IN, &buffer, 0)) + return SCPE_OK; + sim_debug(DEBUG_DETAIL, &cty_dev, "CTY Read %012llo\n", buffer); + if (buffer & CTY_CHAR) + return SCPE_OK; + ch = sim_poll_kbd (); + if (ch & SCPE_KFLAG) { + ch = 0177 & sim_tt_inpcvt(ch, TT_GET_MODE (cty_unit[0].flags)); + sim_debug(DEBUG_DETAIL, &cty_dev, "CTY char %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + buffer = (uint64)(ch) | CTY_CHAR; + if (Mem_write_word(CTY_IN, &buffer, 0) == 0) { + cty_interrupt(); + } else { + sim_debug(DEBUG_DETAIL, &cty_dev, "CTY write failed %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + } + } + return SCPE_OK; +} + +/* Handle output of characters to CTY. Started whenever there is output pending */ +t_stat ctyo_svc (UNIT *uptr) +{ + uint64 buffer; + /* Check if any input pending? */ + if (Mem_read_word(CTY_OUT, &buffer, 0)) + return SCPE_OK; + sim_debug(DEBUG_DETAIL, &cty_dev, "CTY Write %012llo\n", buffer); + if (buffer & CTY_CHAR) { + int32 ch; + ch = buffer & 0377; + ch = sim_tt_outcvt ( ch, TT_GET_MODE (uptr->flags)); + if (sim_putchar_s(ch) != SCPE_OK) { + sim_activate(uptr, 2000); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, &cty_dev, "CTY write %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + + buffer = 0; + if (Mem_write_word(CTY_OUT, &buffer, 0) == 0) { + cty_interrupt(); + } else { + sim_debug(DEBUG_DETAIL, &cty_dev, "CTY write failed %o '%c'\n", ch, + ((ch > 040 && ch < 0177)? ch: '.')); + } + } + + if (Mem_read_word(KLINK_OUT, &buffer, 0)) + return SCPE_OK; + if (buffer != 0) { + buffer = 0; + if (Mem_write_word(CTY_OUT, &buffer, 0) == 0) { + cty_interrupt(); + } + } + + return SCPE_OK; +} + + +/* Handle FE timer interrupts. And keepalive counts */ +t_stat +ctyrtc_srv(UNIT * uptr) +{ + uint64 buffer; + + sim_activate_after(uptr, 1000000/rtc_tps); + if (Mem_read_word(STATUS, &buffer, 0)) + return SCPE_OK; + if (buffer & ONE_MS) { + fprintf(stderr, "1MS\n\r"); + } + if (buffer & RELOAD && rh_boot_dev != NULL) { + reset_all(1); /* Reset everybody */ + if (rh_boot_dev->boot(rh_boot_unit, rh_boot_dev) != SCPE_OK) + return SCPE_STOP; + } + /* Check if clock requested */ + if (buffer & EX_KEEP_ALV) { + if (keep_alive != (buffer & KEEP_ALIVE)) { + keep_alive = buffer; + keep_num = 0; + } else { + if (++keep_num >= 15) { + keep_num = 0; + buffer &= ~0377LL; + buffer |= 1; + cty_execute(071); + M[STATUS] = buffer; + M[CTY_IN] = 0; + M[CTY_OUT] = 0; + M[KLINK_IN] = 0; + M[KLINK_OUT] = 0; + } + } + } + return SCPE_OK; +} + + +t_stat cty_reset (DEVICE *dptr) +{ + sim_activate(&cty_unit[1], cty_unit[1].wait); + sim_activate(&cty_unit[2], cty_unit[2].wait); + M[STATUS] = 0; + M[CTY_IN] = 0; + M[CTY_OUT] = 0; + M[KLINK_IN] = 0; + M[KLINK_OUT] = 0; + M[CTY_SWITCH] = 0; + return SCPE_OK; +} + +t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + cty_unit[0].flags = (cty_unit[0].flags & ~TT_MODE) | val; + return SCPE_OK; +} + +/* Stop operating system */ + +t_stat cty_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + M[CTY_SWITCH] = 1; /* tell OS to stop */ + return SCPE_OK; +} + + +t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "To stop the cpu use the command:\n\n"); +fprintf (st, " sim> SET CTY STOP\n\n"); +fprintf (st, "This will write a 1 to location %03o, causing TOPS10 to stop\n\n", CTY_SWITCH); +fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n"); +fprintf (st, " mode input characters output characters\n\n"); +fprintf (st, " UC lower case converted lower case converted to upper case,\n"); +fprintf (st, " to upper case, high-order bit cleared,\n"); +fprintf (st, " high-order bit cleared non-printing characters suppressed\n"); +fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n"); +fprintf (st, " non-printing characters suppressed\n"); +fprintf (st, " 7B high-order bit cleared high-order bit cleared\n"); +fprintf (st, " 8B no changes no changes\n\n"); +fprintf (st, "The default mode is 7P. In addition, each line can be configured to\n"); +fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n"); +fprint_reg_help (st, &cty_dev); +return SCPE_OK; +} + +const char *cty_description (DEVICE *dptr) +{ + return "Console TTY Line"; +} + +#endif diff --git a/PDP10/ks10_dup.c b/PDP10/ks10_dup.c new file mode 100644 index 00000000..b1954c81 --- /dev/null +++ b/PDP10/ks10_dup.c @@ -0,0 +1,1604 @@ +/* ks10_dup.c: PDP-11 DUP11/DPV11 bit synchronous interface + + Copyright (c) 2013, Mark Pizzolato + Modified for use with KA10 by Richard Cornwell 2022 + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dup DUP11 Unibus/DPV11 Qbus bit synchronous interface + + This module implements a bit synchronous interface to support DDCMP. Other + synchronous protocols which may have been supported on the DUP11/DPV11 bit + synchronous interface are explicitly not supported. + + Connections are modeled with a tcp session with connection management and + I/O provided by the tmxr library. + + The wire protocol implemented is native DDCMP WITHOUT the DDCMP SYNC + characters both initially and between DDCMP packets. + + 15-May-13 MP Initial implementation +*/ + +#include "kx10_defs.h" + +#include "sim_tmxr.h" +#include "ks10_dup.h" +#include "pdp11_ddcmp.h" + +#if NUM_DEVS_DUP > 0 + +#define DUP_WAIT 50 /* Minimum character time */ +#define DUP_CONNECT_POLL 2 /* Seconds */ + +extern int32 tmxr_poll; /* calibrated delay */ + +static uint16 dup_rxcsr[NUM_DEVS_DUP]; +static uint16 dup_rxdbuf[NUM_DEVS_DUP]; +static uint16 dup_parcsr[NUM_DEVS_DUP]; +static uint16 dup_txcsr[NUM_DEVS_DUP]; +static uint16 dup_txdbuf[NUM_DEVS_DUP]; +static t_bool dup_W3[NUM_DEVS_DUP]; +static t_bool dup_W5[NUM_DEVS_DUP]; +static t_bool dup_W6[NUM_DEVS_DUP]; +static uint32 dup_rxi = 0; /* rcv interrupts */ +static uint32 dup_txi = 0; /* xmt interrupts */ +static uint32 dup_wait[NUM_DEVS_DUP]; /* rcv/xmt byte delay */ +static uint32 dup_speed[NUM_DEVS_DUP]; /* line speed (bits/sec) */ +static uint8 *dup_rcvpacket[NUM_DEVS_DUP]; /* rcv buffer */ +static uint16 dup_rcvpksize[NUM_DEVS_DUP]; /* rcv buffer size */ +static uint16 dup_rcvpkbytes[NUM_DEVS_DUP]; /* rcv buffer size of packet */ +static uint16 dup_rcvpkinoff[NUM_DEVS_DUP]; /* rcv packet in offset */ +static uint8 *dup_xmtpacket[NUM_DEVS_DUP]; /* xmt buffer */ +static uint16 dup_xmtpksize[NUM_DEVS_DUP]; /* xmt buffer size */ +static uint16 dup_xmtpkoffset[NUM_DEVS_DUP]; /* xmt buffer offset */ +static uint32 dup_xmtpkstart[NUM_DEVS_DUP]; /* xmt packet start time */ +static uint16 dup_xmtpkbytes[NUM_DEVS_DUP]; /* xmt packet size of packet */ +static uint16 dup_xmtpkdelaying[NUM_DEVS_DUP]; /* xmt packet speed delaying completion */ +static int32 dup_corruption[NUM_DEVS_DUP]; /* data corrupting troll hunger value */ + +static PACKET_DATA_AVAILABLE_CALLBACK dup_rcv_packet_data_callback[NUM_DEVS_DUP]; +static PACKET_TRANSMIT_COMPLETE_CALLBACK dup_xmt_complete_callback[NUM_DEVS_DUP]; +static MODEM_CHANGE_CALLBACK dup_modem_change_callback[NUM_DEVS_DUP]; + +static int dup_rd (DEVICE *dptr, t_addr PA, uint16 *data, int32 access); +static int dup_wr (DEVICE *dptr, t_addr PA, uint16 data, int32 access); +static t_stat dup_set_modem (int32 dup, int32 rxcsr_bits); +static t_stat dup_get_modem (int32 dup); +static t_stat dup_svc (UNIT *uptr); +static t_stat dup_poll_svc (UNIT *uptr); +static t_stat dup_rcv_byte (int32 dup); +static t_stat dup_reset (DEVICE *dptr); +static t_stat dup_attach (UNIT *uptr, CONST char *ptr); +static t_stat dup_detach (UNIT *uptr); +static t_stat dup_clear (int32 dup, t_bool flag); +static void dup_update_rcvi (void); +static void dup_update_xmti (void); +static void dup_clr_rxint (int32 dup); +static void dup_set_rxint (int32 dup); +static void dup_clr_txint (int32 dup); +static void dup_set_txint (int32 dup); +static t_stat dup_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat dup_setspeed (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +static t_stat dup_setcorrupt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat dup_showcorrupt (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat dup_set_W3 (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +static t_stat dup_set_W5 (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +static t_stat dup_show_W5 (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +static t_stat dup_set_W6 (UNIT* uptr, int32 val, CONST char* cptr, void* desc); +static t_stat dup_show_W6 (FILE* st, UNIT* uptr, int32 val, CONST void* desc); +static t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char *dup_description (DEVICE *dptr); + +/* RXCSR - 16XXX0 - receiver control/status register */ + +static BITFIELD dup_rxcsr_bits[] = { + BIT(BDATSET), /* Data Set Change B */ +#define RXCSR_V_BDATSET 0 +#define RXCSR_M_BDATSET (1<> TXCSR_V_MAISEL) + BIT(MAISSCLK), /* Maintenance Single Step Clock */ +#define TXCSR_V_MAISSCLK 13 +#define TXCSR_M_MAISSCLK (1<ctxt; +int32 dup = ((PA - dup_dib.uba_addr) >> 3); /* get line num */ +int32 orig_val; + +if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + +if (dup >= dup_desc.lines) /* validate line number */ + return 1; + +orig_val = regs[(PA >> 1) & 03][dup]; +switch ((PA >> 1) & 03) { /* case on PA<2:1> */ + + case 00: /* RXCSR */ + dup_get_modem (dup); + *data = dup_rxcsr[dup]; + dup_rxcsr[dup] &= ~(RXCSR_M_DSCHNG|RXCSR_M_BDATSET); + break; + + case 01: /* RXDBUF */ + *data = dup_rxdbuf[dup]; + dup_rxcsr[dup] &= ~RXCSR_M_RXDONE; + if (dup_rxcsr[dup] & RXCSR_M_RXACT) + sim_activate (dup_units+dup, dup_wait[dup]); + break; + + case 02: /* TXCSR */ + *data = dup_txcsr[dup]; + break; + + case 03: /* TXDBUF */ + *data = dup_txdbuf[dup]; + break; + } + +sim_debug(DEBUG_DETAIL, DUPDPTR, "dup_rd(PA=%010o [%s], data=0x%X) ", PA, dup_rd_regs[(PA >> 1) & 03], *data); +sim_debug_bits(DEBUG_DETAIL, DUPDPTR, bitdefs[(PA >> 1) & 03], (uint32)(orig_val), (uint32)(regs[(PA >> 1) & 03][dup]), TRUE); + +return 0; +} + +static int dup_wr (DEVICE *dptr, t_addr PA, uint16 data, int32 access) +{ +static BITFIELD* bitdefs[] = {dup_rxcsr_bits, dup_parcsr_bits, dup_txcsr_bits, dup_txdbuf_bits}; +static uint16 *regs[] = {dup_rxcsr, dup_parcsr, dup_txcsr, dup_txdbuf}; +struct pdp_dib *dibp = (DIB *)dptr->ctxt; +int32 dup = ((PA - dup_dib.uba_addr) >> 3); /* get line num */ +int32 orig_val; + +sim_debug(DEBUG_DETAIL, DUPDPTR, "dup_wr(PA=%010o [%s], data=0x%X) ", PA, dup_wr_regs[(PA >> 1) & 03], data); +if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + +if (dup >= dup_desc.lines) /* validate line number */ + return 1; + +orig_val = regs[(PA >> 1) & 03][dup]; +if (access == BYTE) { /* byte access? */ + if (PA & 1) /* unaligned byte access? */ + data = (data | (orig_val & 0xFF)) & 0xFFFF; /* Merge with original word */ + else + data = (orig_val & 0xFF00) | (data & 0xFF); /* Merge with original high word */ +} + +switch ((PA >> 1) & 03) { /* case on PA<2:1> */ + + case 00: /* RXCSR */ + dup_set_modem (dup, data); + dup_rxcsr[dup] &= ~RXCSR_WRITEABLE; + dup_rxcsr[dup] |= (data & RXCSR_WRITEABLE); + if ((dup_rxcsr[dup] & RXCSR_M_DTR) && /* Upward transition of DTR */ + (!(orig_val & RXCSR_M_DTR))) /* Enables Receive on the line */ + dup_desc.ldsc[dup].rcve = TRUE; + if ((dup_rxcsr[dup] & RXCSR_M_RTS) && /* Upward transition of RTS */ + (!(orig_val & RXCSR_M_RTS)) && /* while receiver is enabled and */ + (dup_rxcsr[dup] & RXCSR_M_RCVEN) && /* not stripping sync characters */ + (!(dup_rxcsr[dup] & RXCSR_M_STRSYN)) ) { /* Receive a SYNC character */ + dup_rxcsr[dup] |= RXCSR_M_RXDONE; + dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF; + dup_rxdbuf[dup] |= (dup_parcsr[dup] & PARCSR_M_ADSYNC); + if (dup_rxcsr[dup] & RXCSR_M_RXIE) + dup_set_rxint (dup); + } + if ((dup_rxcsr[dup] & RXCSR_M_RCVEN) && + (!(orig_val & RXCSR_M_RCVEN))) { /* Upward transition of receiver enable */ + dup_rcv_byte (dup); /* start any pending receive */ + } + if ((!(dup_rxcsr[dup] & RXCSR_M_RCVEN)) && + (orig_val & RXCSR_M_RCVEN)) { /* Downward transition of receiver enable */ + dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF; + dup_rxcsr[dup] &= ~RXCSR_M_RXACT; + if ((dup_rcvpkinoff[dup] != 0) || + (dup_rcvpkbytes[dup] != 0)) + dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0; + } + if ((!(dup_rxcsr[dup] & RXCSR_M_RXIE)) && + (orig_val & RXCSR_M_RXIE)) /* Downward transition of receiver interrupt enable */ + dup_clr_rxint (dup); + if ((dup_rxcsr[dup] & RXCSR_M_RXIE) && (dup_rxcsr[dup] & RXCSR_M_RXDONE)) + dup_set_rxint (dup); + break; + + case 01: /* PARCSR */ + dup_parcsr[dup] &= ~PARCSR_WRITEABLE; + dup_parcsr[dup] |= (data & PARCSR_WRITEABLE); + break; + + case 02: /* TXCSR */ + dup_txcsr[dup] &= ~TXCSR_WRITEABLE; + dup_txcsr[dup] |= (data & TXCSR_WRITEABLE); + if (dup_txcsr[dup] & TXCSR_M_DRESET) { + dup_clear(dup, dup_W3[dup]); + break; + } + if (TXCSR_GETMAISEL(dup_txcsr[dup]) != TXCSR_GETMAISEL(orig_val)) { /* Maint Select Changed */ + switch (TXCSR_GETMAISEL(dup_txcsr[dup])) { + case 0: /* User/Normal Mode */ + tmxr_set_line_loopback (&dup_desc.ldsc[dup], FALSE); + break; + case 1: /* External Loopback Mode */ + case 2: /* Internal Loopback Mode */ + tmxr_set_line_loopback (&dup_desc.ldsc[dup], TRUE); + break; + case 3: /* System Test Mode */ + break; + } + } + if ((dup_txcsr[dup] & TXCSR_M_TXACT) && + (!(orig_val & TXCSR_M_TXACT)) && + (orig_val & TXCSR_M_TXDONE)) { + dup_txcsr[dup] &= ~TXCSR_M_TXDONE; + } + if ((!(dup_txcsr[dup] & TXCSR_M_SEND)) && + (orig_val & TXCSR_M_SEND)) { + dup_txcsr[dup] &= ~TXCSR_M_TXACT; + dup_put_msg_bytes (dup, NULL, 0, FALSE, TRUE); + } + if ((dup_txcsr[dup] & TXCSR_M_HALFDUP) ^ (orig_val & TXCSR_M_HALFDUP)) + tmxr_set_line_halfduplex (dup_desc.ldsc+dup, dup_txcsr[dup] & TXCSR_M_HALFDUP); + if ((dup_txcsr[dup] & TXCSR_M_TXIE) && + (!(orig_val & TXCSR_M_TXIE)) && + (dup_txcsr[dup] & TXCSR_M_TXDONE)) { + dup_set_txint (dup); + } + break; + + case 03: /* TXDBUF */ + dup_txdbuf[dup] &= ~TXDBUF_WRITEABLE; + dup_txdbuf[dup] |= (data & TXDBUF_WRITEABLE); + dup_txcsr[dup] &= ~TXCSR_M_TXDONE; + if (dup_txcsr[dup] & TXCSR_M_SEND) { + dup_txcsr[dup] |= TXCSR_M_TXACT; + sim_activate (dup_units+dup, dup_wait[dup]); + } + break; + } + +dup_get_modem (dup); +return 0; +} + +static t_stat dup_set_modem (int32 dup, int32 rxcsr_bits) +{ +int32 bits_to_set, bits_to_clear; + +if ((rxcsr_bits & (RXCSR_M_DTR | RXCSR_M_RTS)) == (dup_rxcsr[dup] & (RXCSR_M_DTR | RXCSR_M_RTS))) + return SCPE_OK; +bits_to_set = ((rxcsr_bits & RXCSR_M_DTR) ? TMXR_MDM_DTR : 0) | ((rxcsr_bits & RXCSR_M_RTS) ? TMXR_MDM_RTS : 0); +bits_to_clear = (~bits_to_set) & (TMXR_MDM_DTR | TMXR_MDM_RTS); +tmxr_set_get_modem_bits (dup_desc.ldsc+dup, bits_to_set, bits_to_clear, NULL); +return SCPE_OK; +} + +static t_stat dup_get_modem (int32 dup) +{ +int32 modem_bits; +uint16 old_rxcsr = dup_rxcsr[dup]; +int32 old_rxcsr_a_modem_bits, new_rxcsr_a_modem_bits, old_rxcsr_b_modem_bits, new_rxcsr_b_modem_bits; +TMLN *lp = &dup_desc.ldsc[dup]; +t_bool new_modem_change = FALSE; + +if (dup_W5[dup]) + old_rxcsr_a_modem_bits = dup_rxcsr[dup] & (RXCSR_M_RING | RXCSR_M_CTS | RXCSR_M_DSR | RXCSR_M_DCD); +else + old_rxcsr_a_modem_bits = dup_rxcsr[dup] & (RXCSR_M_RING | RXCSR_M_CTS); +if (dup_W6[dup]) + old_rxcsr_b_modem_bits = dup_rxcsr[dup] & RXCSR_B_MODEM_BITS; +else + old_rxcsr_b_modem_bits = 0; +tmxr_set_get_modem_bits (lp, 0, 0, &modem_bits); +if (dup_W5[dup]) + new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) | + ((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0) | + ((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0) | + ((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0)); +else + new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) | + ((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0)); +if (dup_W6[dup]) + new_rxcsr_b_modem_bits = (((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0) | + ((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0)); +else + new_rxcsr_b_modem_bits = 0; +dup_rxcsr[dup] &= ~(RXCSR_A_MODEM_BITS | RXCSR_B_MODEM_BITS); +dup_rxcsr[dup] |= new_rxcsr_a_modem_bits | new_rxcsr_b_modem_bits; +if (old_rxcsr_a_modem_bits != new_rxcsr_a_modem_bits) { + dup_rxcsr[dup] |= RXCSR_M_DSCHNG; + new_modem_change = TRUE; + } +if (old_rxcsr_b_modem_bits != new_rxcsr_b_modem_bits) { + dup_rxcsr[dup] |= RXCSR_M_BDATSET; + new_modem_change = TRUE; + } +if (new_modem_change) { + sim_debug(DBG_MDM, DUPDPTR, "dup_get_modem() - Modem Signal Change "); + sim_debug_bits(DBG_MDM, DUPDPTR, dup_rxcsr_bits, (uint32)old_rxcsr, (uint32)dup_rxcsr[dup], TRUE); + } +if (dup_modem_change_callback[dup] && new_modem_change) + dup_modem_change_callback[dup](dup); +if ((dup_rxcsr[dup] & RXCSR_M_DSCHNG) && + ((dup_rxcsr[dup] & RXCSR_M_DSCHNG) != (old_rxcsr & RXCSR_M_DSCHNG)) && + (dup_rxcsr[dup] & RXCSR_M_DSCIE)) + dup_set_rxint (dup); +return SCPE_OK; +} + +/* + * Public routines for use by other devices (i.e. KDP11) + */ +int32 dup_csr_to_linenum (int32 CSRPA) +{ +DEVICE *dptr = DUPDPTR; +DIB *dib = (DIB *)dptr->ctxt; + +if ((dib->uba_addr < (uint32)CSRPA) || + ((uint32)CSRPA > (dib->uba_addr + (IOLN_DUP * dup_desc.lines))) || + (DUPDPTR->flags & DEV_DIS)) + return -1; + +return ((uint32)CSRPA - dib->uba_addr)/IOLN_DUP; +} + + + +void dup_set_callback_mode (int32 dup, PACKET_DATA_AVAILABLE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit, MODEM_CHANGE_CALLBACK modem) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return; +dup_rcv_packet_data_callback[dup] = receive; +dup_xmt_complete_callback[dup] = transmit; +dup_modem_change_callback[dup] = modem; +} + +int32 dup_get_DCD (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_DCD) ? 1 : 0; +} + +int32 dup_get_DSR (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_DSR) ? 1 : 0; +} + +int32 dup_get_CTS (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_CTS) ? 1 : 0; +} + +int32 dup_get_RING (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_RING) ? 1 : 0; +} + +int32 dup_get_RCVEN (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_RCVEN) ? 1 : 0; +} + +t_stat dup_set_DTR (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_set_modem (dup, (state ? RXCSR_M_DTR : 0) | (dup_rxcsr[dup] & RXCSR_M_RTS)); +if (state) + dup_rxcsr[dup] |= RXCSR_M_DTR; +else + dup_rxcsr[dup] &= ~RXCSR_M_DTR; +dup_ldsc[dup].rcve = state; +dup_get_modem (dup); +return SCPE_OK; +} + +t_stat dup_set_RTS (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_set_modem (dup, (state ? RXCSR_M_RTS : 0) | (dup_rxcsr[dup] & RXCSR_M_DTR)); +if (state) + dup_rxcsr[dup] |= RXCSR_M_RTS; +else + dup_rxcsr[dup] &= ~RXCSR_M_RTS; +dup_get_modem (dup); +return SCPE_OK; +} + +t_stat dup_set_RCVEN (int32 dup, t_bool state) +{ +uint16 orig_val; + +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +orig_val = dup_rxcsr[dup]; +dup_rxcsr[dup] &= ~RXCSR_M_RCVEN; +dup_rxcsr[dup] |= (state ? RXCSR_M_RCVEN : 0); +if ((dup_rxcsr[dup] & RXCSR_M_RCVEN) && + (!(orig_val & RXCSR_M_RCVEN))) { /* Upward transition of receiver enable */ + UNIT *uptr = dup_units + dup; + + dup_poll_svc (uptr); /* start any pending receive */ + } +return SCPE_OK; +} + +t_stat dup_setup_dup (int32 dup, t_bool enable, t_bool protocol_DDCMP, t_bool crc_inhibit, t_bool halfduplex, uint8 station) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +if (!enable) { + dup_clear(dup, TRUE); + return SCPE_OK; + } +if (!protocol_DDCMP) { + return SCPE_NOFNC; /* only DDCMP for now */ + } +if (crc_inhibit) { + return SCPE_ARG; /* Must enable CRC for DDCMP */ + } +/* These settings reflect how RSX operates a bare DUP when used for + DECnet communications */ +dup_clear(dup, FALSE); +dup_rxcsr[dup] |= RXCSR_M_STRSYN | RXCSR_M_RCVEN; +dup_parcsr[dup] = PARCSR_M_DECMODE | (DDCMP_SYN << PARCSR_V_ADSYNC); +dup_txcsr[dup] &= TXCSR_M_HALFDUP; +dup_txcsr[dup] |= (halfduplex ? TXCSR_M_HALFDUP : 0); +tmxr_set_line_halfduplex (dup_desc.ldsc+dup, dup_txcsr[dup] & TXCSR_M_HALFDUP); +return dup_set_DTR (dup, TRUE); +} + +t_stat dup_reset_dup (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_clear(dup, dup_W3[dup]); +return SCPE_OK; +} + +t_stat dup_set_W3_option (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_W3[dup] = state; +return SCPE_OK; +} + +t_stat dup_set_W5_option (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_W5[dup] = state; +return SCPE_OK; +} + +t_stat dup_set_W6_option (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_W6[dup] = state; +return SCPE_OK; +} + + +t_bool dup_put_msg_bytes (int32 dup, uint8 *bytes, size_t len, t_bool start, t_bool end) +{ +t_bool breturn = FALSE; + +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return FALSE; + +if (!tmxr_tpbusyln(&dup_ldsc[dup])) { /* Not Busy sending? */ + if (start) { + dup_xmtpkoffset[dup] = 0; + dup_xmtpkdelaying[dup] = 0; + dup_xmtpkstart[dup] = sim_grtime(); + } + if (dup_xmtpkoffset[dup] + 2 + len > dup_xmtpksize[dup]) { + dup_xmtpksize[dup] += 2 + (uint16)len; + dup_xmtpacket[dup] = (uint8 *)realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]); + } + /* Strip sync bytes at the beginning of a message */ + while (len > 0 && (dup_xmtpkoffset[dup] == 0) && (bytes[0] == DDCMP_SYN)) { + --len; + ++bytes; + } + /* Insert remaining bytes into transmit buffer */ + if (len) { + memcpy (&dup_xmtpacket[dup][dup_xmtpkoffset[dup]], bytes, len); + dup_xmtpkoffset[dup] += (uint16)len; + } + dup_txcsr[dup] |= TXCSR_M_TXDONE; + if (dup_txcsr[dup] & TXCSR_M_TXIE) + dup_set_txint (dup); + /* On End of Message, insert CRC and flag delivery start */ + if (end) { + uint16 crc16 = ddcmp_crc16 (0, dup_xmtpacket[dup], dup_xmtpkoffset[dup]); + + dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 & 0xFF; + dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8; + if ((dup_xmtpkoffset[dup] > 8) || (dup_xmtpacket[dup][0] == DDCMP_ENQ)) { + dup_xmtpkbytes[dup] = dup_xmtpkoffset[dup]; + ddcmp_tmxr_put_packet_ln (&dup_ldsc[dup], dup_xmtpacket[dup], dup_xmtpkbytes[dup], dup_corruption[dup]); + } + } + breturn = TRUE; + } +sim_debug (DBG_TRC, DUPDPTR, "dup_put_msg_bytes(dup=%d, len=%d, start=%s, end=%s) %s\n", + dup, (int)len, start ? "TRUE" : "FALSE", end ? "TRUE" : "FALSE", breturn ? "Good" : "Busy"); +if (breturn && (tmxr_tpbusyln (&dup_ldsc[dup]) || dup_xmtpkbytes[dup])) { + if (dup_xmt_complete_callback[dup]) + dup_svc(dup_units+dup); + } +return breturn; +} + +t_stat dup_get_packet (int32 dup, const uint8 **pbuf, uint16 *psize) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; + +if (*pbuf == &dup_rcvpacket[dup][0]) { + *pbuf = NULL; + *psize = 0; + dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0; + dup_rxcsr[dup] &= ~RXCSR_M_RXACT; + } +if ((dup_rcvpkinoff[dup] == 0) && (dup_rcvpkbytes[dup] != 0)) { + *pbuf = &dup_rcvpacket[dup][0]; + *psize = dup_rcvpkbytes[dup]; + } +sim_debug (DBG_TRC, DUPDPTR, "dup_get_packet(dup=%d, psize=%d)\n", + dup, (int)*psize); +return SCPE_OK; +} + +static t_stat dup_rcv_byte (int32 dup) +{ +sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d) - %s, byte %d of %d\n", dup, + (dup_rxcsr[dup] & RXCSR_M_RCVEN) ? "enabled" : "disabled", + dup_rcvpkinoff[dup], dup_rcvpkbytes[dup]); +if (!(dup_rxcsr[dup] & RXCSR_M_RCVEN) || (dup_rcvpkbytes[dup] == 0) || (dup_rxcsr[dup] & RXCSR_M_RXDONE)) + return SCPE_OK; +if (dup_rcv_packet_data_callback[dup]) { + sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d, psize=%d) - Invoking Receive Data callback\n", + dup, (int)dup_rcvpkbytes[dup]); + dup_rcv_packet_data_callback[dup](dup, dup_rcvpkbytes[dup]); + return SCPE_OK; + } +dup_rxcsr[dup] |= RXCSR_M_RXACT; +dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER; +dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF; +dup_rxdbuf[dup] |= dup_rcvpacket[dup][dup_rcvpkinoff[dup]++]; +dup_rxcsr[dup] |= RXCSR_M_RXDONE; +if (((dup_rcvpkinoff[dup] == 8) || + (dup_rcvpkinoff[dup] >= dup_rcvpkbytes[dup])) && + (0 == ddcmp_crc16 (0, dup_rcvpacket[dup], dup_rcvpkinoff[dup]))) + dup_rxdbuf[dup] |= RXDBUF_M_RCRCER; +else + dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER; +if (dup_rcvpkinoff[dup] >= dup_rcvpkbytes[dup]) { + dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0; + dup_rxcsr[dup] &= ~RXCSR_M_RXACT; + } +if (dup_rxcsr[dup] & RXCSR_M_RXIE) + dup_set_rxint (dup); +return SCPE_OK; +} + +/* service routine to delay device activity */ + +static t_stat dup_svc (UNIT *uptr) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); +TMLN *lp = &dup_desc.ldsc[dup]; + +sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d)\n", dup); +if (!(dup_txcsr[dup] & TXCSR_M_TXDONE) && (!tmxr_tpbusyln (lp))) { + uint8 data[1]; /* Make coverity happy */ + data[0] = dup_txdbuf[dup] & TXDBUF_M_TXDBUF; + + dup_put_msg_bytes (dup, &data[0], 0, (dup_txdbuf[dup] & TXDBUF_M_TSOM), + (dup_txdbuf[dup] & TXDBUF_M_TEOM)); + if (tmxr_tpbusyln (lp)) { /* Packet ready to send? */ + sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d) - Packet Done %d bytes\n", dup, dup_xmtpkoffset[dup]); + } + } +if ((tmxr_tpbusyln (lp) || dup_xmtpkbytes[dup]) && (lp->xmte || (!lp->conn))) { + int32 start = tmxr_tpbusyln (lp) ? tmxr_tpqln (lp) + tmxr_tqln (lp) : dup_xmtpkbytes[dup]; + int32 remain = tmxr_send_buffered_data (lp);/* send any buffered data */ + if (remain) { + sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - Packet Transmission Stalled with %d bytes remaining\n", dup, remain); + } + else { + if (!lp->conn) { + if (dup_xmtpkoffset[dup]) { + sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission with link down (dropped)\n", dup, dup_xmtpkoffset[dup]); + } + dup_get_modem (dup); + } + else { + sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission complete\n", dup, dup_xmtpkbytes[dup]); + } + dup_xmtpkoffset[dup] = 0; + } + if (!tmxr_tpbusyln (lp)) { /* Done transmitting? */ + if (((start - remain) > 0) && dup_speed[dup] && dup_xmt_complete_callback[dup] && !dup_xmtpkdelaying[dup]) { /* just done, and speed limited using packet interface? */ + dup_xmtpkdelaying[dup] = 1; + sim_activate_notbefore (uptr, dup_xmtpkstart[dup] + (uint32)((tmxr_poll)*((double)dup_xmtpkbytes[dup]*8)/dup_speed[dup])); + } + else { + dup_txcsr[dup] &= ~TXCSR_M_TXACT; /* Set idle */ + dup_xmtpkbytes[dup] = 0; + dup_xmtpkdelaying[dup] = 0; + if (dup_xmt_complete_callback[dup]) + dup_xmt_complete_callback[dup](dup, (dup_rxcsr[dup] & RXCSR_M_DCD) ? 0 : 1); + } + } + } +if (dup_rxcsr[dup] & RXCSR_M_RXACT) + dup_rcv_byte (dup); +return SCPE_OK; +} + +static t_stat dup_poll_svc (UNIT *uptr) +{ +int32 dup, active, attached; + +sim_debug(DBG_TRC, DUPDPTR, "dup_poll_svc()\n"); + +(void)tmxr_poll_conn(&dup_desc); +tmxr_poll_rx (&dup_desc); +tmxr_poll_tx (&dup_desc); +for (dup=active=attached=0; dup < dup_desc.lines; dup++) { + TMLN *lp = &dup_desc.ldsc[dup]; + + if (dup_units[dup].flags & UNIT_ATT) + ++attached; + if (dup_ldsc[dup].conn) + ++active; + dup_get_modem (dup); + if (lp->xmte && tmxr_tpbusyln(lp)) { + sim_debug(DBG_PKT, DUPDPTR, "dup_poll_svc(dup=%d) - Packet Transmission of remaining %d bytes restarting...\n", dup, tmxr_tpqln (lp)); + dup_svc (&dup_units[dup]); /* Flush pending output */ + } + if (!(dup_rxcsr[dup] & RXCSR_M_RXACT)) { + const uint8 *buf; + uint16 size; + t_stat r; + + if (dup_parcsr[dup] & PARCSR_M_DECMODE) + r = ddcmp_tmxr_get_packet_ln (lp, &buf, &size, dup_corruption[dup]); + else { + size_t size_t_size; + + r = tmxr_get_packet_ln (lp, &buf, &size_t_size); + size = (uint16)size_t_size; + } + if ((r == SCPE_OK) && (buf)) { + if (dup_rcvpksize[dup] < size) { + dup_rcvpksize[dup] = size; + dup_rcvpacket[dup] = (uint8 *)realloc (dup_rcvpacket[dup], dup_rcvpksize[dup]); + } + memcpy (dup_rcvpacket[dup], buf, size); + dup_rcvpkbytes[dup] = size; + dup_rcvpkinoff[dup] = 0; + dup_rxcsr[dup] |= RXCSR_M_RXACT; + dup_rcv_byte (dup); + } + } + } +if (active) + sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */ +else { + for (dup=0; dup < dup_desc.lines; dup++) { + if (dup_speed[dup]/8) { + dup_wait[dup] = (tmxr_poll*2)/(dup_speed[dup]/8); + if (dup_wait[dup] < DUP_WAIT) + dup_wait[dup] = DUP_WAIT; + } + else + dup_wait[dup] = DUP_WAIT; /* set minimum byte delay */ + } + if (attached) + sim_activate_after (uptr, DUP_CONNECT_POLL*1000000);/* periodic check for connections */ + } +return SCPE_OK; +} + +/* Interrupt routines */ + +static void dup_clr_rxint (int32 dup) +{ + int vect; + vect = dup_dib.uba_vect + (dup * 010); /* return vector */ + uba_clr_irq(&dup_dib, vect); + return; +} + +static void dup_set_rxint (int32 dup) +{ + int vect; + vect = dup_dib.uba_vect + (dup * 010); /* return vector */ + uba_set_irq(&dup_dib, vect); + sim_debug(DEBUG_IRQ, DUPDPTR, "dup_set_rxint(dup=%d)\n", dup); + return; +} + +static void dup_clr_txint (int32 dup) +{ + int vect; + vect = dup_dib.uba_vect + 4 + (dup * 010); /* return vector */ + uba_clr_irq(&dup_dib, vect); + return; +} + +static void dup_set_txint (int32 dup) +{ + int vect; + vect = dup_dib.uba_vect + 4 + (dup * 010); /* return vector */ + uba_set_irq(&dup_dib, vect); + sim_debug(DEBUG_IRQ, DUPDPTR, "dup_set_txint(dup=%d)\n", dup); + return; +} + +/* Device reset */ + +static t_stat dup_clear (int32 dup, t_bool flag) +{ +sim_debug(DBG_TRC, DUPDPTR, "dup_clear(dup=%d,flag=%d)\n", dup, flag); + +dup_rxdbuf[dup] = 0; /* silo empty */ +dup_txdbuf[dup] = 0; +dup_parcsr[dup] = 0; /* no params */ +dup_txcsr[dup] = TXCSR_M_TXDONE; /* clear CSR */ +dup_wait[dup] = DUP_WAIT; /* initial/default byte delay */ +if (flag) { /* INIT? clr all */ + dup_rxcsr[dup] = 0; + dup_set_modem (dup, dup_rxcsr[dup]); /* push change out to line */ + } +else + dup_rxcsr[dup] &= ~(RXCSR_M_DTR|RXCSR_M_RTS); /* else save dtr & rts */ +dup_clr_rxint (dup); /* clear int */ +dup_clr_txint (dup); +if (!dup_ldsc[dup].conn) /* set xmt enb */ + dup_ldsc[dup].xmte = 1; +dup_ldsc[dup].rcve = 0; /* clr rcv enb */ +return SCPE_OK; +} + +static t_stat dup_reset (DEVICE *dptr) +{ +int32 i, ndev, attached = 0; + +sim_debug(DBG_TRC, dptr, "dup_reset()\n"); + +dup_desc.packet = TRUE; +dup_desc.buffered = 16384; + +if (dup_ldsc == NULL) { /* First time startup */ + dup_desc.ldsc = dup_ldsc = (TMLN *)calloc (dup_desc.lines, sizeof(*dup_ldsc)); + for (i = 0; i < dup_desc.lines; i++) { /* init each line */ + dup_units[i] = dup_unit_template; + if (dup_units[i].flags & UNIT_ATT) + ++attached; + } + dup_units[dup_desc.lines] = dup_poll_unit_template; + /* Initialize to standard factory Option Jumper Settings */ + for (i = 0; i < NUM_DEVS_DUP; i++) { + dup_W3[i] = TRUE; + dup_W5[i] = FALSE; + dup_W6[i] = TRUE; + } + } +for (i = 0; i < dup_desc.lines; i++) { /* init each line */ + dup_clear (i, TRUE); + if (dup_units[i].flags & UNIT_ATT) + ++attached; + } +for (i = 0; i < dup_desc.lines; i++) { /* Clear irq's */ + int vect = dup_dib.uba_vect + (i * 010); + uba_clr_irq(&dup_dib, vect); + vect += 4; + uba_clr_irq(&dup_dib, vect); +} +tmxr_set_modem_control_passthru (&dup_desc); /* We always want Modem Control */ +dup_desc.notelnet = TRUE; /* We always want raw tcp socket */ +dup_desc.dptr = DUPDPTR; /* Connect appropriate device */ +dup_desc.uptr = dup_units+dup_desc.lines; /* Identify polling unit */ +sim_cancel (dup_units+dup_desc.lines); /* stop poll */ +ndev = ((dptr->flags & DEV_DIS)? 0: dup_desc.lines ); +if (attached) + sim_activate_after (dup_units+dup_desc.lines, DUP_CONNECT_POLL*1000000);/* start poll */ +return SCPE_OK; +} + +static t_stat dup_attach (UNIT *uptr, CONST char *cptr) +{ +t_stat r; +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); +char attach_string[512]; + +if (!cptr || !*cptr) + return SCPE_ARG; +if (!(uptr->flags & UNIT_ATTABLE)) + return SCPE_NOATT; +sprintf (attach_string, "Line=%d,%s", dup, cptr); +r = tmxr_open_master (&dup_desc, attach_string); /* open master socket */ +free (uptr->filename); +uptr->filename = tmxr_line_attach_string(&dup_desc.ldsc[dup]); +if (r != SCPE_OK) /* error? */ + return r; +uptr->flags |= UNIT_ATT; +sim_activate_after (dup_units+dup_desc.lines, DUP_CONNECT_POLL*1000000);/* start poll */ +return r; +} + +static t_stat dup_detach (UNIT *uptr) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); +TMLN *lp = &dup_ldsc[dup]; +int32 i, attached; +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; +sim_cancel (uptr); +uptr->flags &= ~UNIT_ATT; +for (i=attached=0; ifilename); +uptr->filename = NULL; +free (dup_rcvpacket[dup]); +dup_rcvpacket[dup] = NULL; +dup_rcvpksize[dup] = 0; +dup_rcvpkbytes[dup] = 0; +free (dup_xmtpacket[dup]); +dup_xmtpacket[dup] = NULL; +dup_xmtpksize[dup] = 0; +dup_xmtpkoffset[dup] = 0; +return r; +} + +/* SET/SHOW SPEED processor */ + +static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_speed[dup]) + fprintf(st, "speed=%d bits/sec", dup_speed[dup]); +else + fprintf(st, "speed=0 (unrestricted)"); +return SCPE_OK; +} + +static t_stat dup_setspeed (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); +t_stat r; +int32 newspeed; + +if (cptr == NULL) + return SCPE_ARG; +newspeed = (int32) get_uint (cptr, 10, 100000000, &r); +if (r != SCPE_OK) + return r; +dup_speed[dup] = newspeed; +return SCPE_OK; +} + +/* SET/SHOW CORRUPTION processor */ + +static t_stat dup_showcorrupt (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_corruption[dup]) + fprintf(st, "Corruption=%d milligulps (%.1f%% of messages processed)", dup_corruption[dup], ((double)dup_corruption[dup])/10.0); +else + fprintf(st, "No Corruption"); +return SCPE_OK; +} + +static t_stat dup_setcorrupt (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); +t_stat r; +int32 appetite; + +if (cptr == NULL) + return SCPE_ARG; +appetite = (int32) get_uint (cptr, 10, 999, &r); +if (r != SCPE_OK) + return r; +dup_corruption[dup] = appetite; +return SCPE_OK; +} + +/* SET/SHOW W3 processor */ + +static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_W3[dup]) + fprintf(st, "W3 Jumper Installed"); +else + fprintf(st, "W3 Jumper Removed"); +return SCPE_OK; +} + +static t_stat dup_set_W3 (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +dup_W3[dup] = val; +return SCPE_OK; +} + +/* SET/SHOW W5 processor */ + +static t_stat dup_show_W5 (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_W5[dup]) + fprintf(st, "W5 Jumper Installed"); +else + fprintf(st, "W5 Jumper Removed"); +return SCPE_OK; +} + +static t_stat dup_set_W5 (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +dup_W5[dup] = val; +return SCPE_OK; +} + +/* SET/SHOW W6 processor */ + +static t_stat dup_show_W6 (FILE* st, UNIT* uptr, int32 val, CONST void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_W6[dup]) + fprintf(st, "W6 Jumper Installed"); +else + fprintf(st, "W6 Jumper Removed"); +return SCPE_OK; +} + +static t_stat dup_set_W6 (UNIT* uptr, int32 val, CONST char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +dup_W6[dup] = val; +return SCPE_OK; +} + +/* SET LINES processor */ + +static t_stat dup_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 newln, l; +uint32 i; +t_stat r; +DEVICE *dptr = DUPDPTR; + +for (i=0; inumunits; i++) + if (dptr->units[i].flags&UNIT_ATT) + return SCPE_ALATT; +if (cptr == NULL) + return SCPE_ARG; +newln = (int32) get_uint (cptr, 10, NUM_DEVS_DUP, &r); +if ((r != SCPE_OK) || (newln == dup_desc.lines)) + return r; +if (newln == 0) + return SCPE_ARG; +sim_cancel (dup_units + dup_desc.lines); +dup_desc.ldsc = dup_ldsc = (TMLN *)realloc(dup_ldsc, newln*sizeof(*dup_ldsc)); +for (l=dup_desc.lines; l < newln; l++) { + memset (&dup_ldsc[l], 0, sizeof(*dup_ldsc)); + dup_units[l] = dup_unit_template; + } +dup_units[newln] = dup_poll_unit_template; +dup_desc.lines = newln; +dup_desc.uptr = dptr->units + newln; /* Identify polling unit */ +dptr->numunits = newln + 1; +return dup_reset (dptr); /* setup lines and auto config */ +} + +static t_stat dup_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 %D11 is a single-line, program controlled, double buffered\n" + " communications device designed to interface the %1s system to a\n" + " serial synchronous line. The original hardware is capable of handling\n" + " a wide variety of protocols, including byte oriented protocols, such\n" + " as DDCMP and BISYNC and bit-oriented protocols such as SDLC, HDLC\n" + " and ADCCP. The emulated device currently only supports connections\n" + " using the DDCMP protocol.\n\n" + " The %D11 is ideally suited for interfacing the %1s system\n" + " to medium-speed synchronous lines for remote batch, remote data\n" + " collection, remote concentration and network applications. Multiple\n" + " %D11's on a %1s allow its use in applications requiring several\n" + " synchronous lines.\n\n" + " The %D11 is capable of transmitting data at the maximum speed of\n" + " 9600 baud. The emulated device can move data at significantly faster\n" + " data rates. The maximum emulated rate is dependent on the host CPU's\n" + " available cycles.\n" + "1 Hardware Description\n" + " The %1s %D11 consists of a microprocessor module and a synchronous line\n" + " unit module.\n" + "2 $Registers\n" + "\n" + " These registers contain the emulated state of the device. These values\n" + " don't necessarily relate to any detail of the original device being\n" + " emulated but are merely internal details of the emulation.\n" + "1 Configuration\n" + " A %D device is configured with various simh SET and ATTACH commands\n" + "2 $Set commands\n" + "3 Lines\n" + " A maximum of %2s %D11 devices can be emulated concurrently in the %S\n" + " simulator. The number of simulated %D devices or lines can be\n" + " specified with command:\n" + "\n" + "+sim> SET %D LINES=n\n" + "3 Peer\n" + " To set the host and port to which data is to be transmitted use the\n" + " following command:\n" + "\n" + "+sim> SET %U PEER=host:port\n" + "3 Connectpoll\n" + " The minimum interval between attempts to connect to the other side is set\n" + " using the following command:\n" + "\n" + "+sim> SET %U CONNECTPOLL=n\n" + "\n" + " Where n is the number of seconds. The default is %3s seconds.\n" + "3 Speed\n" + " If you want to experience the actual data rates of the physical hardware\n" + " you can set the bit rate of the simulated line can be set using the\n" + " following command:\n" + "\n" + "+sim> SET %U SPEED=n\n" + "\n" + " Where n is the number of data bits per second that the simulated line\n" + " runs at. In practice this is implemented as a delay while transmitting\n" + " bytes to the socket. Use a value of zero to run at full speed with no\n" + " artificial throttling.\n" + "3 Corruption\n" + " Corruption Troll - the DDCMP emulation includes the ability to enable a\n" + " process that will intentionally drop or corrupt some messages. This\n" + " emulates the less-than-perfect communications lines encountered in the\n" + " real world, and enables network monitoring software to see non-zero error\n" + " counters.\n" + "\n" + " The troll selects messages with a probablility selected by the SET %U\n" + " CORRUPT command. The units are 0.1%%; that is, a value of 1 means that\n" + " every message has a 1/1000 chance of being selected to be corrupted\n" + " or discarded.\n" + /****************************************************************************/ +#define DUP_HLP_ATTACH "Configuration Attach" + "2 Attach\n" + " The communication line performs input and output through a TCP session\n" + " (or UDP session) connected to a user-specified port. The ATTACH command\n" + " specifies the port to be used as well as the peer address:\n" + "\n" + "+sim> ATTACH %U {interface:}port{,UDP},Connect=peerhost:port\n" + "\n" + " where port is a decimal number between 1 and 65535 that is not being\n" + " used for other TCP/IP activities.\n" + "\n" + " Specifying symmetric attach configuration (with both a listen port and\n" + " a peer address) will cause the side receiving an incoming\n" + " connection to validate that the connection actually comes from the\n" + " connecction destination system.\n" + " A symmetric attach configuration is required when using UDP packet\n" + " transport.\n" + "\n" + " The default connection uses TCP transport between the local system and\n" + " the peer. Alternatively, UDP can be used by specifying UDP on the\n" + " ATTACH command.\n" + "\n" + " Communication may alternately use the DDCMP synchronous framer device.\n" + " The DDCMP synchronous device is a USB device that can send and\n" + " receive DDCMP frames over either RS-232 or coax synchronous lines.\n" + " Refer to https://github.com/pkoning2/ddcmp for documentation.\n" + "\n" + "+sim> ATTACH %U SYNC=ifname:mode:speed\n" + "\n" + " Communicate via the synchronous DDCMP framer interface \"ifname\", \n" + " and framer mode \"mode\" -- one of INTEGRAL, RS232_DTE, or\n" + " RS232_DCE. The \"speed\" argument is the bit rate for the line.\n" + " You can use \"SHOW SYNC\" to see the list of synchronous DDCMP devices.\n" + "2 Examples\n" + " To configure two simulators to talk to each other use the following\n" + " example:\n" + " \n" + " Machine 1\n" + "+sim> SET %D ENABLE\n" + "+sim> ATTACH %U 1111,connect=LOCALHOST:2222\n" + " \n" + " Machine 2\n" + "+sim> SET %D ENABLE\n" + "+sim> ATTACH %U 2222,connect=LOCALHOST:1111\n" + "\n" + " To communicate with an \"integral modem\" DMC or similar, at 56 kbps:\n" + "+sim> ATTACH %U SYNC=sync0:INTEGRAL:56000\n" + "1 Monitoring\n" + " The %D device and %U line configuration and state can be displayed with\n" + " one of the available show commands.\n" + "2 $Show commands\n" + "1 Diagnostics\n" + " Corruption Troll - the DDCMP emulation includes a process that will\n" + " intentionally drop or corrupt some messages. This emulates the\n" + " less-than-perfect communications lines encountered in the real world,\n" + " and enables network monitoring software to see non-zero error counters.\n" + "\n" + " The troll selects messages with a probablility selected by the SET %U\n" + " CORRUPT command. The units are 0.1%%; that is, a value of 1 means that\n" + " every message has a 1/1000 chance of being selected to be corrupted\n" + " or discarded.\n" + "1 Restrictions\n" + " Real hardware synchronous connections could operate in Multi-Point mode.\n" + " Multi-Point mode was a way of sharing a single wire with multiple\n" + " destination systems or devices. Multi-Point mode is not currently\n" + " emulated by this or other simulated synchronous devices.\n" + "\n" + "1 Implementation\n" + " A real %D11 transports host generated protocol implemented data via a\n" + " synchronous connection, the emulated device makes a TCP (or UDP)\n" + " connection to another emulated device which either speaks DDCMP over the\n" + " TCP connection directly, or interfaces to a simulated computer where the\n" + " operating system speaks the DDCMP protocol on the wire.\n" + "\n" + " The %D11 can be used for point-to-point DDCMP connections carrying\n" + " DECnet and other types of networking, e.g. from ULTRIX or DSM.\n" + "1 Debugging\n" + " The simulator has a number of debug options, these are:\n" + "\n" + "++REG Shows whenever a CSR is programatically read or written\n" + "++++and the current value.\n" + "++INT Shows Interrupt activity.\n" + "++PKT Shows Packet activity.\n" + "++XMT Shows Transmitted data.\n" + "++RCV Shows Received data.\n" + "++MDM Shows Modem Signal Transitions.\n" + "++CON Shows connection activities.\n" + "++TRC Shows routine call traces.\n" + "++ASY Shows Asynchronous activities.\n" + "\n" + " To get a full trace use\n" + "\n" + "+sim> SET %D DEBUG\n" + "\n" + " However it is recommended to use the following when sending traces:\n" + "\n" + "+sim> SET %D DEBUG=REG;PKT;XMT;RCV;CON\n" + "\n" + "1 Related Devices\n" + " The %D11 can facilitate communication with other simh simulators which\n" + " have emulated synchronous network devices available. These include\n" + " the following:\n" + "\n" + "++DUP11* KS10 PDP10 simulators\n" + "\n" + "++* Indicates systems which have OS provided DDCMP implementations.\n" + ; +char devcount[16]; +char connectpoll[16]; + +sprintf (devcount, "%d", NUM_DEVS_DUP); +sprintf (connectpoll, "%d", DUP_CONNECT_POLL); + +return scp_help (st, dptr, uptr, flag, helpString, cptr, "Unibus", devcount, connectpoll); +} + +static t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +return dup_help (st, dptr, uptr, flag, DUP_HLP_ATTACH); +} + +static const char *dup_description (DEVICE *dptr) +{ +return "DUP11 bit synchronous interface" ; +} +#endif diff --git a/PDP10/ks10_dup.h b/PDP10/ks10_dup.h new file mode 100644 index 00000000..2a624c1f --- /dev/null +++ b/PDP10/ks10_dup.h @@ -0,0 +1,64 @@ +/* ks10_dup.h: PDP-11 DUP11 bit synchronous shared device packet interface interface + + Copyright (c) 2013, Mark Pizzolato + Modified for KA10 simulator by Richard Cornwell, 2022 + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dup DUP11 Unibus/DPV11 Qbus bit synchronous interface + + This module describes the interfaces exposed by the dup device for use by + a packet delivery devices (i.e. KMC11). + + 31-May-13 MP Initial implementation +*/ + +#ifndef PDP11_DUP_H_ +#define PDP11_DUP_H_ 0 + +typedef void (*PACKET_DATA_AVAILABLE_CALLBACK)(int32 dup, int len); +typedef void (*PACKET_TRANSMIT_COMPLETE_CALLBACK)(int32 dup, int status); +typedef void (*MODEM_CHANGE_CALLBACK)(int32 dup); + +int32 dup_get_DSR (int32 dup); +int32 dup_get_DCD (int32 dup); +int32 dup_get_CTS (int32 dup); +int32 dup_get_RING (int32 dup); +int32 dup_get_RCVEN (int32 dup); +t_stat dup_set_DTR (int32 dup, t_bool state); +t_stat dup_set_RTS (int32 dup, t_bool state); +t_stat dup_set_W3_option (int32 dup, t_bool state); +t_stat dup_set_W5_option (int32 dup, t_bool state); +t_stat dup_set_W6_option (int32 dup, t_bool state); +t_stat dup_set_RCVEN (int32 dup, t_bool state); +t_stat dup_setup_dup (int32 dup, t_bool enable, t_bool protocol_DDCMP, t_bool crc_inhibit, t_bool halfduplex, uint8 station); +t_stat dup_reset_dup (int32 dup); + +int32 dup_csr_to_linenum (int32 CSRPA); + +void dup_set_callback_mode (int32 dup, PACKET_DATA_AVAILABLE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit, MODEM_CHANGE_CALLBACK modem); + +t_bool dup_put_msg_bytes (int32 dup, uint8 *bytes, size_t len, t_bool start, t_bool end); + +t_stat dup_get_packet (int32 dup, const uint8 **pbuf, uint16 *psize); + +#endif /* PDP11_DUP_H_ */ diff --git a/PDP10/ks10_dz.c b/PDP10/ks10_dz.c new file mode 100644 index 00000000..6945e055 --- /dev/null +++ b/PDP10/ks10_dz.c @@ -0,0 +1,667 @@ +/* ks10_dz.c: PDP-10 DZ11 communication server simulator + + Copyright (c) 2021, Richard Cornwell + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell. + +*/ + +#include "kx10_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#ifndef NUM_DEVS_DZ +#define NUM_DEVS_DZ 0 +#endif + +#if (NUM_DEVS_DZ > 0) + + +#define DZ11_LINES (8 * NUM_DEVS_DZ) + +/* 0 CSR */ + +#define MAINT 0000010 /* Maintance mode */ +#define CLR 0000020 +#define MSE 0000040 +#define RIE 0000100 +#define RDONE 0000200 +#define TLINE 0003400 +#define TLINE_V 8 +#define SAE 0010000 +#define SA 0020000 +#define TIE 0040000 +#define TRDY 0100000 + +/* 2 RBUF */ +#define RBUF 0000377 +#define RXLINE 0003400 +#define RXLINE_V 8 +#define PAR_ERR 0010000 +#define FRM_ERR 0020000 +#define OVRN 0040000 +#define VALID 0100000 + +/* 2 LPR */ +#define LINE 0000007 +#define CHAR_LEN 0000030 +#define STOP 0000040 +#define PAR_ENB 0000100 +#define ODD_PAR 0000200 +#define FREQ 0007400 +#define RXON 0010000 + +/* 4 TCR */ +#define LINE_ENB 0000001 +#define DTR 0000400 + +/* 6 MSR */ +#define RO 0000001 +#define CO 0000400 + +/* 6 TDR */ +#define TBUF 0000377 +#define BRK 0000400 + +struct _buffer { + int in_ptr; /* Insert pointer */ + int out_ptr; /* Remove pointer */ + uint16 buff[64]; /* Buffer */ + int len; /* Length */ +}; + +#define full(q) ((((q)->in_ptr + 1) & 0x3f) == (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) & 0x3f +#define inci(q) (q)->in_ptr = ((q)->in_ptr + 1) & 0x3f + +#define LINE_EN 01 +#define DTR_FLAG 02 + +uint16 dz_csr[NUM_DEVS_DZ]; +uint16 dz_xmit[DZ11_LINES]; +uint8 dz_flags[DZ11_LINES]; +uint8 dz_ring[NUM_DEVS_DZ]; +struct _buffer dz_recv[NUM_DEVS_DZ]; +TMLN dz_ldsc[DZ11_LINES] = { 0 }; /* Line descriptors */ +TMXR dz_desc = { DZ11_LINES, 0, 0, dz_ldsc }; +extern int32 tmxr_poll; + +int dz_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +int dz_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +t_stat dz_svc (UNIT *uptr); +t_stat dz_reset (DEVICE *dptr); +void dz_checkirq(struct pdp_dib *dibp); +uint16 dz_vect(struct pdp_dib *dibp); +t_stat dz_set_modem (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat dz_show_modem (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat dz_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat dz_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat dz_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat dz_attach (UNIT *uptr, CONST char *cptr); +t_stat dz_detach (UNIT *uptr); +t_stat dz_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *dz_description (DEVICE *dptr); +DIB dz_dib = { 0760000, 077, 0340, 5, 3, &dz_read, &dz_write, 0, 0, 0 }; + +UNIT dz_unit = { + UDATA (&dz_svc, TT_MODE_7B+UNIT_IDLE+UNIT_DISABLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT + }; + +REG dz_reg[] = { + { DRDATA (TIME, dz_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB dz_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, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of DZ11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of DZ11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of DZ11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of DZ11" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dz_desc, "Disconnect a specific line" }, + { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, + NULL, &tmxr_show_summ, (void *) &dz_desc, "Display a summary of line states" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tmxr_show_cstat, (void *) &dz_desc, "Display current connections" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tmxr_show_cstat, (void *) &dz_desc, "Display multiplexer statistics" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &dz_setnl, &tmxr_show_lines, (void *) &dz_desc, "Set number of lines" }, + { MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file", + &dz_set_log, NULL, (void *)&dz_desc }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG", + &dz_set_nolog, NULL, (void *)&dz_desc, "Disable logging on designated line" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL, + NULL, &dz_show_log, (void *)&dz_desc, "Display logging for all lines" }, + + { 0 } + }; + +DEVICE dz_dev = { + "DZ", &dz_unit, dz_reg, dz_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &dz_reset, + NULL, &dz_attach, &dz_detach, + &dz_dib, DEV_MUX | DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &dz_help, NULL, NULL, &dz_description + }; + + +int +dz_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + int base; + uint16 temp; + int ln; + TMLN *lp; + int i; + + if ((dptr->flags & DEV_DIS) != 0) + return 1; + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + addr &= dibp->uba_mask; + if (addr < 010 || addr > 047) + return 1; + base = ((addr & 070) - 010) >> 3; + if (((base + 1) * 8) > dz_desc.lines) + return 1; + sim_debug(DEBUG_DETAIL, dptr, "DZ%o write %06o %06o %o\n", base, + addr, data, access); + + switch (addr & 06) { + case 0: + if (access == BYTE) { + temp = dz_csr[base]; + if (addr & 1) + data = data | (temp & 0377); + else + data = (temp & 0177400) | data; + } + if (data & CLR) { + dz_csr[base] = 0; + dz_recv[base].in_ptr = dz_recv[base].out_ptr = 0; + dz_recv[base].len = 0; + /* Set up the current status */ + ln = base << 3; + for (i = 0; i < 8; i++) { + dz_flags[ln + i] &= ~LINE_EN; + } + return 0; + } + dz_csr[base] &= ~(TIE|SAE|RIE|MSE|CLR|MAINT); + dz_csr[base] |= data & (TIE|SAE|RIE|MSE|MAINT); + if (((dz_csr[base] & (RDONE|RIE)) == (RDONE|RIE)) || + (dz_csr[base] & (SA|SAE)) == (SA|SAE)) + uba_set_irq(dibp, dibp->uba_vect + (010 * base)); + else + uba_clr_irq(dibp, dibp->uba_vect + (010 * base)); + if ((dz_csr[base] & (TRDY|TIE)) == (TRDY|TIE)) + uba_set_irq(dibp, dibp->uba_vect + 4 + (010 * base)); + else + uba_clr_irq(dibp, dibp->uba_vect + 4 + (010 * base)); + break; + + case 2: + ln = (data & 07) + (base << 3); + dz_ldsc[ln].rcve = (data & RXON) != 0; + break; + + case 4: + temp = 0; + ln = base << 3; + /* Set up the current status */ + for (i = 0; i < 8; i++) { + if (dz_flags[ln + i] & LINE_EN) + temp |= LINE_ENB << i; + if (dz_flags[ln + i] & DTR_FLAG) + temp |= DTR << i; + dz_flags[ln + i] = 0; + } + if (access == BYTE) { + if (addr & 1) + data = data | (temp & 0377); + else + data = (temp & 0177400) | data; + } + dz_csr[base] &= ~(TRDY); + for (i = 0; i < 8; i++) { + lp = &dz_ldsc[ln + i]; + if ((data & (LINE_ENB << i)) != 0) + dz_flags[ln + i] |= LINE_EN; + if ((data & (DTR << i)) != 0) + dz_flags[ln + i] |= DTR_FLAG; + if (dz_flags[ln + i] & DTR_FLAG) + tmxr_set_get_modem_bits(lp, TMXR_MDM_OUTGOING, 0, NULL); + else + tmxr_set_get_modem_bits(lp, 0, TMXR_MDM_OUTGOING, NULL); + sim_debug(DEBUG_EXP, dptr, "DZ%o sstatus %07o %o %o\n", base, data, i, dz_flags[ln+i]); + } + uba_clr_irq(dibp, dibp->uba_vect + 4 + (010 * base)); + break; + + case 6: + if (access == BYTE && (addr & 1) != 0) { + break; + } + + if ((dz_csr[base] & TRDY) == 0) + break; + + ln = ((dz_csr[base] & TLINE) >> TLINE_V) + (base << 3); + lp = &dz_ldsc[ln]; + + if ((dz_flags[ln] & LINE_EN) != 0 && lp->conn) { + int32 ch = data & 0377; + t_stat r; + ch = sim_tt_outcvt(ch, TT_GET_MODE (dz_unit.flags) | TTUF_KSR); + /* Try and send character */ + r = tmxr_putc_ln(lp, ch); + /* If character did not send, queue it */ + if (r == SCPE_STALL) + dz_xmit[ln] = TRDY | ch; + } + dz_csr[base] &= ~TRDY; + uba_clr_irq(dibp, dibp->uba_vect + 4 + (010 * base)); + break; + } + + dz_checkirq(dibp); + return 0; +} + +int +dz_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + int base; + uint16 temp; + int ln; + TMLN *lp; + int i; + + if ((dptr->flags & DEV_DIS) != 0) + return 1; + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + addr &= dibp->uba_mask; + if (addr < 010 || addr > 047) + return 1; + base = ((addr & 070) - 010) >> 3; + if (((base + 1) * 8) > dz_desc.lines) + return 1; + switch (addr & 06) { + case 0: + *data = dz_csr[base]; + break; + + case 2: + *data = 0; + if ((dz_csr[base] & MSE) == 0) + return 0; + dz_csr[base] &= ~(SA|RDONE); + uba_clr_irq(dibp, dibp->uba_vect + (010 * base)); + if (!empty(&dz_recv[base])) { + *data = dz_recv[base].buff[dz_recv[base].out_ptr]; + inco(&dz_recv[base]); + dz_recv[base].len = 0; + } + if (!empty(&dz_recv[base])) { + dz_csr[base] |= RDONE; + if (dz_csr[base] & RIE) { + uba_set_irq(dibp, dibp->uba_vect + (010 * base)); + } + } + break; + + case 4: + temp = 0; + base <<= 3; + /* Set up the current status */ + for (ln = 0; ln < 8; ln++) { + if (dz_flags[base + ln] & LINE_EN) + temp |= LINE_ENB << ln; + if (dz_flags[base + ln] & DTR_FLAG) + temp |= DTR << ln; + } + *data = temp; + break; + + case 6: + temp = (uint16)dz_ring[base]; + ln = base << 3; + for (i = 0; i < 8; i++) { + lp = &dz_ldsc[ln + i]; + if (lp->conn) + temp |= CO << i; + } + dz_ring[base] = 0; + *data = temp; + break; + } + sim_debug(DEBUG_DETAIL, dptr, "DZ%o read %06o %06o %o\n", base, + addr, *data, access); + return 0; +} + +/* Unit service */ +t_stat dz_svc (UNIT *uptr) +{ + int32 ln; + int base; + uint16 temp; + DEVICE *dptr = find_dev_from_unit (uptr); + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + TMLN *lp; + + if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return SCPE_OK; + ln = tmxr_poll_conn (&dz_desc); /* look for connect */ + if (ln >= 0) { /* got one? rcv enb*/ + dz_ring[(ln & 030) >> 3] |= (1 << (ln & 7)); + sim_debug(DEBUG_DETAIL, &dz_dev, "DZ line connect %d\n", ln); + dz_xmit[ln] = 0; + } + tmxr_poll_tx(&dz_desc); + tmxr_poll_rx(&dz_desc); + for (ln = 0; ln < dz_desc.lines; ln++) { + lp = &dz_ldsc[ln]; + base = (ln >> 3) & 03; + if (dz_xmit[ln] != 0) { + /* Try and send character */ + t_stat r = tmxr_putc_ln(lp, dz_xmit[ln] & 0377); + /* If character did not send, queue it */ + if (r == SCPE_OK) + dz_xmit[ln] = 0; + } + /* If silo full, skip to next */ + while (!full(&dz_recv[base])) { + int32 ch = tmxr_getc_ln(lp); + if ((ch & TMXR_VALID) != 0) { + if (ch & SCPE_BREAK) { /* break? */ + temp = FRM_ERR; + } else { + ch = sim_tt_inpcvt (ch, TT_GET_MODE(dz_unit.flags) | TTUF_KSR); + temp = VALID | ((ln & 07) << RXLINE_V) | (uint16)(ch & RBUF); + } + dz_recv[base].buff[dz_recv[base].in_ptr] = temp; + inci(&dz_recv[base]); + dz_recv[base].len++; + dz_csr[base] |= RDONE; + if (dz_csr[base] & RIE) + uba_set_irq(dibp, dibp->uba_vect + (010 * base)); + if (dz_recv[base].len > 16) { + dz_csr[base] |= SA; + if (dz_csr[base] & SAE) { + uba_set_irq(dibp, dibp->uba_vect + (010 * base)); + } + } + sim_debug(DEBUG_DETAIL, dptr, "TTY recieve %d: %o\n", ln, ch); + } else + break; + } + } + + dz_checkirq(dibp); + sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */ + return SCPE_OK; +} + +/* Check if a device has an IRQ event ready */ +void +dz_checkirq(struct pdp_dib *dibp) +{ + int i; + int ln; + int stop; + TMLN *lp; + int irq = 0; + + for (i = 0; i < NUM_DEVS_DZ; i++) { + if ((dz_csr[i] & MSE) == 0) + continue; + ln = ((dz_csr[i] & TLINE) >> TLINE_V) + (i << 3); + stop = ln; + if ((dz_csr[i] & TRDY) == 0) { + /* See if there is another line ready */ + do { + ln = (ln & 070) | ((ln + 1) & 07); + lp = &dz_ldsc[ln]; + /* Connected and empty xmit_buffer */ + if ((dz_flags[ln] & LINE_EN) != 0 && dz_xmit[ln] == 0) { + sim_debug(DEBUG_DETAIL, &dz_dev, "DZ line ready %o\n", ln); + + dz_csr[i] &= ~(TRDY|TLINE); + dz_csr[i] |= TRDY | ((ln & 07) << TLINE_V); + if (dz_csr[i] & TIE) { + uba_set_irq(dibp, dibp->uba_vect + 4 + (010 * i)); + } + break; + } + } while (ln != stop); + } + } +} + +/* Reset routine */ + +t_stat +dz_reset (DEVICE *dptr) +{ + int i; + + if (dz_unit.flags & UNIT_ATT) /* if attached, */ + sim_activate (&dz_unit, tmxr_poll); /* activate */ + else + sim_cancel (&dz_unit); /* else stop */ + for (i = 0; i < NUM_DEVS_DZ; i++) { + dz_csr[i] = 0; + dz_recv[i].in_ptr = dz_recv[i].out_ptr = 0; + dz_recv[i].len = 0; + dz_ring[i] = 0; + dz_xmit[i] = 0; + } + for (i = 0; i < DZ11_LINES; i++) { + dz_flags[i] = 0; + } + return SCPE_OK; +} + + +/* SET LINES processor */ + +t_stat +dz_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, DZ11_LINES, &r); + if ((r != SCPE_OK) || (newln == dz_desc.lines)) + return r; + if ((newln == 0) || (newln > DZ11_LINES) || (newln % 8) != 0) + return SCPE_ARG; + if (newln < dz_desc.lines) { + for (i = newln - 1, t = 0; i < dz_desc.lines; i++) + t = t | dz_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln - 1; i < dz_desc.lines; i++) { + if (dz_ldsc[i].conn) { + tmxr_linemsg (&dz_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_send_buffered_data (&dz_ldsc[i]); + } + tmxr_detach_ln (&dz_ldsc[i]); /* completely reset line */ + } + } + if (dz_desc.lines < newln) + memset (dz_ldsc + dz_desc.lines, 0, sizeof(*dz_ldsc)*(newln-dz_desc.lines)); + dz_desc.lines = newln; + return dz_reset (&dz_dev); /* setup lines and auto config */ +} + +/* SET LOG processor */ + +t_stat +dz_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, dz_desc.lines, &r); + if ((r != SCPE_OK) || (ln > dz_desc.lines)) + return SCPE_ARG; + return tmxr_set_log (NULL, ln, cptr, desc); +} + +/* SET NOLOG processor */ + +t_stat +dz_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, dz_desc.lines, &r); + if ((r != SCPE_OK) || (ln > dz_desc.lines)) + return SCPE_ARG; + return tmxr_set_nolog (NULL, ln, NULL, desc); +} + +/* SHOW LOG processor */ + +t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + int32 i; + + for (i = 0; i < dz_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 +dz_attach (UNIT *uptr, CONST char *cptr) +{ + t_stat reason; + + reason = tmxr_attach (&dz_desc, uptr, cptr); + if (reason != SCPE_OK) + return reason; + sim_activate (uptr, tmxr_poll); + return SCPE_OK; +} + +/* Detach routine */ + +t_stat +dz_detach (UNIT *uptr) +{ + int32 i; + t_stat reason; + reason = tmxr_detach (&dz_desc, uptr); + for (i = 0; i < dz_desc.lines; i++) + dz_ldsc[i].rcve = 0; + sim_cancel (uptr); + return reason; +} + +t_stat dz_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "DZ11 Terminal Interfaces\n\n"); +fprintf (st, "Each DZ11 supports 8 serial lines. Up to 32 can be configured\n"); +fprintf (st, " sim> SET DZ 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 DZn LOG command enables\n"); +fprintf (st, "logging on a line:\n\n"); +fprintf (st, " sim> SET DZn LOG=filename log output of line n to filename\n\n"); +fprintf (st, "The SET DZn NOLOG command disables logging and closes the open log file,\n"); +fprintf (st, "if any.\n\n"); +fprintf (st, "Once DZ 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 DZ DISCONNECT command, or a DETACH DC command.\n\n"); +fprintf (st, "Other special commands:\n\n"); +fprintf (st, " sim> SHOW DZ CONNECTIONS show current connections\n"); +fprintf (st, " sim> SHOW DZ STATISTICS show statistics for active connections\n"); +fprintf (st, " sim> SET DZn DISCONNECT disconnects the specified line.\n\n"); +fprintf (st, "The DZ11 is a unibus device, various parameters can be changed on these devices\n"); +fprintf (st, "\n The address of the device can be set with: \n"); +fprintf (st, " sim> SET DZ ADDR=octal default address= 760000\n"); +fprintf (st, "\n The interrupt vector can be set with: \n"); +fprintf (st, " sim> SET DZ VECT=octal default 340\n"); +fprintf (st, "\n The interrupt level can be set with: \n"); +fprintf (st, " sim> SET DZ BR=# # should be between 4 and 7.\n"); +fprintf (st, "\n The unibus addaptor that the DZ is on can be set with:\n"); +fprintf (st, " sim> SET DZ CTL=# # can be either 1 or 3\n"); +fprint_reg_help (st, &dz_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 DZ is detached.\n"); +return SCPE_OK; +} + +const char *dz_description (DEVICE *dptr) +{ +return "DZ11 asynchronous line interface"; +} + +#endif diff --git a/PDP10/ks10_kmc.c b/PDP10/ks10_kmc.c new file mode 100644 index 00000000..ed879c78 --- /dev/null +++ b/PDP10/ks10_kmc.c @@ -0,0 +1,3080 @@ +/* pdp11_kmc.c: KMC11-A with COMM IOP-DUP microcode Emulation + ------------------------------------------------------------------------------ + + + Initial implementation 2002 by Johnny Eriksson + + Adapted to SIMH 3.? by Robert M. A. Jarratt in 2013 + + Adapted to PDP10 simulators by Richard Cornwell 2022 + + Completely rewritten by Timothe Litt for SimH 4.0 in 2013. + Implements all DDCMP functions, although the DUP11 doesn't currently support half-duplex. + + DUP separated into separate devices, the DUPs were created by Mark Pizzolato. + + This code is Copyright 2002, 2013 by the authors,all rights reserved. + It is licensed under the standard SimH license. + ------------------------------------------------------------------------------ + + Modification history: + + 05-Jun-13 TL Massive rewrite to split KMC/DUP, add missing functions, and + restructure so TOPS-10/20 & RSX are happy. Support multiple + KMCs. + 14-Apr-13 RJ Took original sources into latest source code. + 15-Feb-02 JE Massive changes/cleanups. + 23-Jan-02 JE Modify for version 2.9. + 17-Jan-02 JE First attempt. +------------------------------------------------------------------------------*/ +/* + * Loose ends, known problems etc: + * + * This implementation should do both full and half duplex DDCMP, but half duplex needs to be tested. + * + * DUP: Unexplained code to generate SYNC and RCVEN based on RTS transitions + * prevents RTS from working; using hacked version. + * Select final speed limit. + */ + +#include "kx10_defs.h" + + +#define KMC_RDX 8 + +#include "ks10_dup.h" +#include "pdp11_ddcmp.h" + +#define DIM(x) (sizeof(x)/sizeof((x)[0])) +#define UNUSED_ARG(x) (void)(x) + +/* Configuration macros + */ + +/* From host VM: DUP_LINES is the maximum number of DUP lines on the Unibus. + * The KMC may not control all of them, but the DUP API also uses the index in + * IO space to identify the line. Default to max KDP supported. + * + * Note: It is perfectly reasonable to have MANY more DUP_LINES than a single KMC + * can support. A configuration can have multiple KMCs, or DUPs that are controlled + * directly by the OS. DUP_LINES allocates space for the KMC to keep track of lines + * that some KMC instance can POTENTIALLY control. + */ +#if NUM_DEVS_KMC > 0 +#ifndef DUP_LINES +#define DUP_LINES (NUM_DEVS_DUP+1) +#endif + +/* Number of KMC devices possible. */ +#ifndef KMC_UNITS +#define KMC_UNITS 1 +#endif + +/* Number of KMC devices initially enabled. */ +#ifndef INITIAL_KMCS +#define INITIAL_KMCS 1 +#endif + +#if INITIAL_KMCS > KMC_UNITS +#undef INITIAL_KMCS +#define INITIAL_KMCS KMC_UNITS +#endif + +#if INITIAL_KMCS == 0 +#undef INITIAL_KMCS +#define INITIAL_KMCS 1 +#define KMC_DIS DEV_DIS +#else +#define KMC_DIS 0 +#endif + +/* Define DUP_RXRESYNC to disable/enable RCVEN at the expected times. + * This is used to resynch the receiver, but I'm not sure if it would + * cause the emulated DUP to lose data...especially with QSYNC +#define DUP_RXRESYNC 1 + */ + +/* End of configuration */ + +/* Architectural structures and macros */ + +/* Maximum line speed + * KDP limit was about 19,200 BPS. + * Used to pace BD consumption. If too fast, too many messages can + * be outstanding and the OS will reject ACKs. *TODO* Pick a final limit. + */ +#define MAX_SPEED (1*1000*1000) + +#define DFLT_SPEED (19200) + + +/* Transmission (or reception) time of a buffer of n + * characters at speed bits/sec. 8 bit chars (sync line) + * result in microseconds. + */ +#define XTIME(n,speed) (((n)*8*1000*1000)/(speed)) + +/* Queue elements + * A queue is a double-linked list of element headers. + * The head of the queue is an element without a body. + * A queue is empty when the only element in the queue is + * the head. + * Each queue has an asociated count of the elements in the + * queue; this simplifies knowing its state. + * Each queue also has a maximum length. + * + * Queues are manipulated with initqueue, insqueue, and remqueue. + */ +struct queuehdr { + struct queuehdr *next; + struct queuehdr *prev; +}; +typedef struct queuehdr QH; + +/* bits, SEL0 */ + +#define SEL0_RUN 0100000 /* Run bit. */ +#define SEL0_MRC 0040000 /* Master clear. */ +#define SEL0_CWR 0020000 /* CRAM write. */ +#define SEL0_SLU 0010000 /* Step Line Unit. */ +#define SEL0_LUL 0004000 /* Line Unit Loop. */ +#define SEL0_RMO 0002000 /* ROM output. */ +#define SEL0_RMI 0001000 /* ROM input. */ +#define SEL0_SUP 0000400 /* Step microprocessor. */ +#define SEL0_RQI 0000200 /* Request input. */ +#define SEL0_IEO 0000020 /* Interrupt enable output. */ +#define SEL0_IEI 0000001 /* Interrupt enable input. */ + +/* bits, SEL2 */ + +#define SEL2_OVR 0100000 /* Completion queue overrun. */ +#define SEL2_V_LINE 8 /* Line number assigned by host */ +#define SEL2_LINE (0177 << SEL2_V_LINE) +#define MAX_LINE 017 /* Maximum line number allowed in BASE_IN */ +#define MAX_ACTIVE (MAX_LINE+1) +#define UNASSIGNED_LINE (MAX_ACTIVE+1) +#define SEL2_RDO 0000200 /* Ready for output transaction. */ +#define SEL2_RDI 0000020 /* Ready for input transaction. */ +#define SEL2_IOT 0000004 /* I/O type, 1 = rx, 0 = tx. */ +#define SEL2_V_CMD 0 /* Command code */ +#define SEL2_CMD 0000003 /* Command code + * IN are commands TO the KMC + * OUT are command completions FROM the KMC. + */ +# define CMD_BUFFIN 0 /* BUFFER IN */ +# define CMD_CTRLIN 1 /* CONTROL IN */ +# define CMD_BASEIN 3 /* BASE IN */ +# define CMD_BUFFOUT 0 /* BUFFER OUT */ +# define CMD_CTRLOUT 1 /* CONTROL OUT */ + +#define SEL2_II_RESERVED (SEL2_OVR | 0354) /* Reserved: 15, 7:5, 3:2 */ +/* bits, SEL4 */ + +#define SEL4_CI_POLL 0377 /* DUP polling interval, 50 usec units */ + +#define SEL4_ADDR 0177777 /* Generic: Unibus address <15:0> */ + +/* bits, SEL6 */ + +#define SEL6_V_CO_XAD 14 /* Unibus extended address bits */ +#define SEL6_CO_XAD (3u << SEL6_V_CO_XAD) + +/* BASE IN */ +#define SEL6_II_DUPCSR 0017770 /* BASE IN: DUP CSR <12:3> */ + +/* BUFFER IN */ +#define SEL6_BI_ENABLE 0020000 /* BUFFER IN: Assign after KILL */ +#define SEL6_BI_KILL 0010000 /* BUFFER IN: Return all buffers */ + +/* BUFFER OUT */ +#define SEL6_BO_EOM 0010000 /* BUFFER OUT: End of message */ + +/* CONTROL OUT event codes */ +#define SEL6_CO_ABORT 006 /* Bit stuffing rx abort */ +#define SEL6_CO_HCRC 010 /* DDCMP Header CRC error */ +#define SEL6_CO_DCRC 012 /* DDCMP Data CRC/ BS frame CRC */ +#define SEL6_CO_NOBUF 014 /* No RX buffer available */ +#define SEL6_CO_DSRCHG 016 /* DSR changed (Initially OFF) */ +#define SEL6_CO_NXM 020 /* NXM */ +#define SEL6_CO_TXU 022 /* Transmitter underrun */ +#define SEL6_CO_RXO 024 /* Receiver overrun */ +#define SEL6_CO_KDONE 026 /* Kill complete */ + +/* CONTROL IN modifiers */ +#define SEL6_CI_V_DDCMP 15 /* Run DDCMP vs. bit-stuffing */ +#define SEL6_CI_DDCMP (1u << SEL6_CI_V_DDCMP) +#define SEL6_CI_V_HDX 13 /* Half-duplex */ +#define SEL6_CI_HDX (1u << SEL6_CI_V_HDX) +#define SEL6_CI_V_ENASS 12 /* Enable secondary station address filter */ +#define SEL6_CI_ENASS (1u << SEL6_CI_V_ENASS) +#define SEL6_CI_V_NOCRC 9 +#define SEL6_CI_NOCRC (1u << SEL6_CI_V_NOCRC) +#define SEL6_CI_V_ENABLE 8 +#define SEL6_CI_ENABLE (1u << SEL6_CI_V_ENABLE) +#define SEL6_CI_SADDR 0377 + +/* Buffer descriptor list bits */ + +#define BDL_LDS 0100000 /* Last descriptor in list. */ +#define BDL_RSY 0010000 /* Resync transmitter. */ +#define BDL_XAD 0006000 /* Buffer address bits 17 & 16. */ +#define BDL_S_XAD (16-10) /* Shift to position XAX in address */ +#define BDL_EOM 0001000 /* End of message. */ +#define BDL_SOM 0000400 /* Start of message. */ + +#define KMC_CRAMSIZE 1024 /* Size of CRAM (microcode control RAM). */ +#define KMC_DRAMSIZE 1024 +#define KMC_CYCLETIME 300 /* Microinstruction cycle time, nsec */ + +#define MAXQUEUE 2 /* Number of bdls that can be queued for tx and rx + * per line. (KDP ucode limits to 2) + */ + +struct buffer_list { /* BDL queue elements */ + QH hdr; + uint32 ba; +}; +typedef struct buffer_list BDL; + + struct workblock { + t_bool first; + uint32 bda; + uint16 bd[3]; + uint16 rcvc; + uint32 ba; + }; + typedef struct workblock WB; + +/* Each DUP in the system can potentially be assigned to a KMC. + * Since the total number of DUPs is relatively small, and in + * most configurations all DUPs will be assigned, a dup structure + * is allocated for all possible DUPs. This structure is common + * to ALL KMCs; a given DUP is assigned to at most one. + */ + +struct dupstate { + int32 kmc; /* Controlling KMC */ + uint8 line; /* OS-assigned line number */ + int32 dupidx; /* DUP API Number amongst all DUP11's on Unibus (-1 == unassigned) */ + int32 linkstate; /* Line Link Status (i.e. 1 when DCD/DSR is on, 0 otherwise */ + #define LINK_DSR 1 + #define LINK_SEL 2 + uint16 ctrlFlags; + uint32 dupcsr; + uint32 linespeed; /* Effective line speed (bps) */ + BDL bdq[MAXQUEUE*2]; /* Queued TX and RX buffer lists */ + QH bdqh; /* Free queue */ + int32 bdavail; + + QH rxqh; /* Receive queue from host */ + int32 rxavail; + WB rx; + uint32 rxstate; +/* States. Note that these are ordered; there are < comparisions */ +#define RXIDLE 0 +#define RXBDL 1 +#define RXBUF 2 +#define RXDAT 3 +#define RXLAST 4 +#define RXFULL 5 +#define RXNOBUF 6 + + uint8 *rxmsg; + uint16 rxmlen; + uint16 rxdlen; + uint16 rxused; + + QH txqh; /* Transmit queue from host */ + int32 txavail; + WB tx; + uint32 txstate; +/* States. Note that these are ordered; there are < comparisions */ +#define TXIDLE 0 +#define TXDONE 1 +#define TXRTS 2 +#define TXSOM 3 +#define TXHDR 4 +#define TXHDRX 5 +#define TXDATA 6 +#define TXDATAX 7 +#define TXMRDY 8 +#define TXRDY 9 +#define TXACT 10 +#define TXKILL 11 +#define TXKILR 12 + + uint8 *txmsg; + size_t txmsize, txslen, txmlen; + }; + +typedef struct dupstate dupstate; + +/* State for every DUP that MIGHT be controlled. + * A DUP can be controlled by at most one KMC. + */ +static dupstate dupState[DUP_LINES] = {{ 0 }}; + +/* Flags defining sim_debug conditions. */ + +#define DF_CMD 00001 /* Trace commands. */ +#define DF_BFO 00002 /* Trace buffers out */ +#define DF_CTO 00004 /* Trace control out */ +#define DF_QUE 00010 /* Trace internal queues */ +#define DF_RGR 00020 /* Register reads */ +#define DF_RGW 00040 /* Register writes. */ +#define DF_INF 00100 /* Info */ +#define DF_ERR 00200 /* Error halts */ +#define DF_PKT 00400 /* Errors in packet (use DUP pkt trace for pkt data */ +#define DF_INT 01000 /* Interrupt delivery */ +#define DF_BUF 02000 /* Buffer service */ + +static DEBTAB kmc_debug[] = { + {"CMD", DF_CMD}, + {"BFO", DF_BFO}, + {"CTO", DF_CTO}, + {"QUE", DF_QUE}, + {"RGR", DF_RGR}, + {"RGW", DF_RGW}, + {"INF", DF_INF}, + {"ERR", DF_ERR}, + {"PKT", DF_PKT}, + {"BUF", DF_BUF}, + {"INT", DF_INT}, + {0} +}; + +/* Per-KMC state */ + +/* To help make the code more readable, by convention the symbol 'k' + * is the number of the KMC that is the target of the current operation. + * The global state variables below have a #define of the short form + * of each name. Thus, instead of kmc_upc[kmcnum][j], write upc[j]. + * For this to work, k, a uint32 must be in scope and valid. + * + * k can be found in several ways: + * k is the offset into any of the tables. + * Given a UNIT pointer k = txup->unit_kmc. + * The KMC assigned to control a DUP is stored it its dupstate. + * k = dupState[dupno]->kmc; (-1 if not controlled by any KMC) + * The DUP associated with a line is stored in line2dup. + * k = line2dup[line]->kmc + * From the DEVICE pointer, dptr->units lists all the UNITs, and the + * number of units is dptr->numunits. + * From a CSR address: + * k = (PA - dib.ba) / IOLN_KMC + * + * Note that the UNIT arrays are indexed [line][kmc], so pointer + * math is not the best way to find k. (TX line 0 is the public + * UNIT for each KMC.) + * + */ + +/* Emulator error halt codes + * These are mostl error conditions that produce undefined + * results in the hardware. To help with debugging, unique + * codes are provided here. + */ + +#define HALT_STOP 0 /* Run bit cleared */ +#define HALT_MRC 1 /* Master clear */ +#define HALT_BADRES 2 /* Resume without initialization */ +#define HALT_LINE 3 /* Line number out of range */ +#define HALT_BADCMD 4 /* Undefined command received */ +#define HALT_BADCSR 5 /* BASE IN had non-zero MBZ */ +#define HALT_RCVOVF 6 /* Too many receive buffers assigned */ +#define HALT_MTRCV 7 /* Receive buffer descriptor has zero size */ +#define HALT_XMTOVF 8 /* Too many transmit buffers assigned */ +#define HALT_XSOM 9 /* Transmission didn't start with SOM */ +#define HALT_XSOM2 10 /* Data buffer didn't start with SOM */ +#define HALT_BADUC 11 /* No or unrecognized microcode loaded */ + +/* KMC event notifications are funneled through the small number of CSRs. + * Since the CSRs may not be available when an event happens, events are + * queued in these structures. An event is represented by the values to + * be exposed in BSEL2, BSEL4, and BSEL6. + * + * Queue overflow is signalled by setting the overflow bit in the entry + * at the tail of the completion queue at the time a new entry fails to + * be inserted. Note that the line number in that entry may not be + * the line whose event was lost. Effectively, that makes this a fatal + * error. + * + * The KMC microcode uses a queue depth of 29. + */ + +#define CQUEUE_MAX (29) + +struct cqueue { + QH hdr; + uint16 bsel2, bsel4, bsel6; +}; +typedef struct cqueue CQ; + +/* CSRs. These are known as SELn as words and BSELn as bytes */ +static uint16 kmc_sel0[KMC_UNITS]; /* CSR0 - BSEL 1,0 */ +#define sel0 kmc_sel0[k] +static uint16 kmc_sel2[KMC_UNITS]; /* CSR2 - BSEL 3,2 */ +#define sel2 kmc_sel2[k] +static uint16 kmc_sel4[KMC_UNITS]; /* CSR4 - BSEL 5,4 */ +#define sel4 kmc_sel4[k] +static uint16 kmc_sel6[KMC_UNITS]; /* CSR6 - BSEL 7,6 */ +#define sel6 kmc_sel6[k] + +/* Microprocessor state - subset exposed to the host */ +static uint16 kmc_upc[KMC_UNITS]; /* Micro PC */ +#define upc kmc_upc[k] +static uint16 kmc_mar[KMC_UNITS]; /* Micro Memory Address Register */ +#define mar kmc_mar[k] +static uint16 kmc_mna[KMC_UNITS]; /* Maintenance Address Register */ +#define mna kmc_mna[k] +static uint16 kmc_mni[KMC_UNITS]; /* Maintenance Instruction Register */ +#define mni kmc_mni[k] +static uint16 kmc_ucode[KMC_UNITS][KMC_CRAMSIZE]; +#define ucode kmc_ucode[k] +static uint16 kmc_dram[KMC_UNITS][KMC_DRAMSIZE]; +#define dram kmc_dram[k] + +static dupstate *kmc_line2dup[KMC_UNITS][MAX_ACTIVE]; +#define line2dup kmc_line2dup[k] + +/* General state booleans */ +static int kmc_gflags[KMC_UNITS]; /* Miscellaneous gflags */ +#define gflags kmc_gflags[k] +# define FLG_INIT 000001 /* Master clear has been done once. + * Data structures trustworthy. + */ +# define FLG_AINT 000002 /* Pending KMC "A" (INPUT) interrupt */ +# define FLG_BINT 000004 /* Pending KMC "B" (OUTPUT) interrupt */ +# define FLG_UCINI 000010 /* Ucode initialized, ucode structures and ints OK */ + +/* Completion queue elements, header and freelist */ +static CQ kmc_cqueue[KMC_UNITS][CQUEUE_MAX]; +#define cqueue kmc_cqueue[k] + +static QH kmc_cqueueHead[KMC_UNITS]; +#define cqueueHead kmc_cqueueHead[k] +static int32 kmc_cqueueCount[KMC_UNITS]; +#define cqueueCount kmc_cqueueCount[k] + +static QH kmc_freecqHead[KMC_UNITS]; +#define freecqHead kmc_freecqHead[k] +static int32 kmc_freecqCount[KMC_UNITS]; +#define freecqCount kmc_freecqCount[k] + +/* *** End of per-KMC state *** */ + +/* Forward declarations: simulator interface */ + + +static t_stat kmc_reset(DEVICE * dptr); +static t_stat kmc_readCsr(DEVICE *dptr, t_addr PA, uint16* data, int32 access); +static t_stat kmc_writeCsr(DEVICE *dptr, t_addr PA, uint16 data, int32 access); +static void kmc_doMicroinstruction (int32 k, uint16 instr); +static t_stat kmc_txService(UNIT * txup); +static t_stat kmc_rxService(UNIT * rxup); + +#if KMC_UNITS > 1 +static t_stat kmc_setDeviceCount (UNIT *txup, int32 val, CONST char *cptr, void *desc); +static t_stat kmc_showDeviceCount (FILE *st, UNIT *txup, int32 val, CONST void *desc); +#endif +static t_stat kmc_setLineSpeed (UNIT *txup, int32 val, CONST char *cptr, void *desc); +static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, CONST void *desc); +static t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, CONST void *dp); + +static t_stat kmc_help (FILE *st, DEVICE *dptr, + UNIT *uptr, int32 flag, const char *cptr); +static const char *kmc_description (DEVICE *dptr); + +/* Global data */ + +extern int32 tmxr_poll; /* calibrated delay */ + +#define IOLN_KMC 010 + +static DIB kmc_dib = { + 0760540, /* ba - Base address */ + 017, /* Masks */ + 0540, /* Interrupt vector */ + 5, /* Interrupt level */ + 3, /* UBA Adaptor number */ + + &kmc_readCsr, /* rd - read IO */ + &kmc_writeCsr, /* wr - write IO */ + NULL, +}; + +/* One UNIT for each (possible) active line for transmit, and another for receive */ +static UNIT tx_units[MAX_ACTIVE][KMC_UNITS]; /* Line 0 is primary unit. txup references */ +#define unit_kmc u3 +#define unit_line u4 +#define unit_htime u5 + +/* Timers - in usec */ + +#define RXPOLL_DELAY (1*1000) /* Poll for a new message */ +#define RXBDL_DELAY (10*1000) /* Grace period for host to supply a BDL */ +#define RXNEWBD_DELAY (10) /* Delay changing descriptors */ +#define RXSTART_DELAY (50) /* Host provided BDL to receive start */ + + +static UNIT rx_units[MAX_ACTIVE][KMC_UNITS]; /* Secondary unit, used for RX. rxup references */ + +DEVICE kmc_int_rxdev = { + "KDP-RX", rx_units[0], + NULL, /* Register decode tables */ + NULL, /* Modifier table */ + INITIAL_KMCS, /* Number of units */ + KMC_RDX, /* Address radix */ + 13, /* Address width: 18 - <17:13> are 1s, omits UBA */ + 1, /* Address increment */ + KMC_RDX, /* Data radix */ + 8, /* Data width */ + NULL, /* examine routine */ + NULL, /* Deposit routine */ + NULL, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + NULL, /* context */ + DEV_NOSAVE, /* Flags */ + 0, /* debug control */ + NULL, /* debug flag table */ + NULL, /* memory size routine */ + NULL, /* logical name */ + NULL, /* help routine */ + NULL, /* attach help routine */ + NULL, /* help context */ + &kmc_description /* Device description routine */ +}; + + +/* Timers - in usec */ + +#define TXSTART_DELAY (10) /* TX BUFFER IN to TX start */ +#define TXDONE_DELAY (10) /* Completion to to poll for next bd */ +#define TXCTS_DELAY (100*1000) /* Polling for CTS to appear */ +#define TXDUP_DELAY (1*1000*1000) /* Wait for DUP to accept data (SNH) */ + +static BITFIELD kmc_sel0_decoder[] = { + BIT (IEI), + BITNCF (3), + BIT (IEO), + BIT (RQI), + BITNCF (2), + BIT (SUP), + BIT (RMI), + BIT (RMO), + BIT (LUL), + BIT (SLU), + BIT (CWR), + BIT (MRC), + BIT (RUN), + ENDBITS +}; +static BITFIELD kmc_sel2_decoder[] = { + BITF (CMD,2), + BIT (IOT), + BITNCF (1), + BIT (RDI), + BITNCF (2), + BIT (RDO), + BITFFMT (LINE,7,"%u"), + BIT (CQOVF), + ENDBITS +}; +static REG kmc_reg[] = { + { BRDATADF (SEL0, kmc_sel0, KMC_RDX, 16, KMC_UNITS, "Initialization/control", kmc_sel0_decoder) }, + { BRDATADF (SEL2, kmc_sel2, KMC_RDX, 16, KMC_UNITS, "Command/line", kmc_sel2_decoder) }, + { ORDATA (SEL4, kmc_sel4, 16) }, + { ORDATA (SEL6, kmc_sel6, 16) }, + { NULL }, + }; + +MTAB kmc_mod[] = { + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of KDP" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of KDP" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of KDP" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of KDP" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NMO, 0, "SPEED", "SPEED=dup=bps", + &kmc_setLineSpeed, &kmc_showLineSpeed, NULL, "Line speed (bps)" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "STATUS", NULL, NULL, &kmc_showStatus, NULL, "Display KMC status" }, +#if KMC_UNITS > 1 + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DEVICES", "DEVICES=n", + &kmc_setDeviceCount, &kmc_showDeviceCount, NULL, "Display number of KMC devices enabled" }, +#endif + { 0 }, + }; + +DEVICE kmc_dev = { + "KDP", + tx_units[0], + kmc_reg, /* Register decode tables */ + kmc_mod, /* Modifier table */ + INITIAL_KMCS, /* Number of units */ + KMC_RDX, /* Address radix */ + 13, /* Address width: 18 - <17:13> are 1s, omits UBA */ + 1, /* Address increment */ + KMC_RDX, /* Data radix */ + 8, /* Data width */ + NULL, /* examine routine */ + NULL, /* Deposit routine */ + &kmc_reset, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + &kmc_dib, /* context */ + KMC_DIS /* Flags */ + | DEV_DISABLE + | DEV_DEBUG, + 0, /* debug control */ + kmc_debug, /* debug flag table */ + NULL, /* memory size routine */ + NULL, /* logical name */ + &kmc_help, /* help routine */ + NULL, /* attach help routine */ + NULL, /* help context */ + &kmc_description /* Device description routine */ +}; + +/* Forward declarations: not referenced in simulator data */ + +static void kmc_masterClear(int32 k); +static void kmc_startUcode (int32 k); +static void kmc_dispatchInputCmd(int32 k); + +/* Control functions */ +static void kmc_baseIn (int32 k, dupstate *d, uint16 cmdsel2, uint8 line); +static void kmc_ctrlIn (int32 k, dupstate *d, int line); + +/* Receive functions */ +void kmc_rxBufferIn(dupstate *d, int32 ba, uint16 sel6v); +static void kdp_receive(int32 dupidx, int count); + +/* Transmit functions */ +static void kmc_txBufferIn(dupstate *d, int32 ba, uint16 sel6v); +static void kmc_txComplete (int32 dupidx, int status); +static t_bool kmc_txNewBdl(dupstate *d); +static t_bool kmc_txNewBd(dupstate *d); +static t_bool kmc_txAppendBuffer(dupstate *d); + +/* Completions */ +static void kmc_processCompletions (int32 k); + +static void kmc_ctrlOut (int32 k, uint8 code, uint16 rx, uint8 line, uint32 bda); +static void kmc_modemChange (int32 dupidx); +static t_bool kmc_updateDSR (dupstate *d); + +static t_bool kmc_bufferAddressOut (int32 k, uint16 flags, uint16 rx, uint8 line, uint32 bda); + +/* Buffer descriptor list utilities */ +static int32 kmc_updateBDCount(uint32 bda, uint16 *bd); + +/* Errors */ +static void kmc_halt (int32 k, int error); + +/* Interrupt management */ +static void kmc_updints(int32 k); +static int32 kmc_AintAck (void); +static int32 kmc_BintAck (void); + +/* DUP access */ + +/* Debug support */ +static t_bool kmc_printBufferIn (int32 k, DEVICE *dev, uint8 line, t_bool rx, + int32 count, int32 ba, uint16 sel6v); +static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32 ba, int prbuf); + +/* Environment */ +static const char *kmc_verifyUcode (int32 k); + +/* Queue management */ +static void initqueue (QH *head, int32 *count, int32 max, void *list, size_t size); +/* Convenience for initqueue() calls */ +# define MAX_LIST_SIZE(q) DIM(q), (q), sizeof(q[0]) +# define INIT_HDR_ONLY 0, NULL, 0 + +static t_bool insqueue (QH *entry, QH *pred, int32 *count, int32 max); +static void *remqueue (QH *entry, int32 *count); + + +/* + * Reset KMC device. This resets ALL the KMCs: + */ + +static t_stat kmc_reset(DEVICE* dptr) { + int32 k; + size_t i; + + if (sim_switches & SWMASK ('P')) { + for (i = 0; i < DIM (dupState); i++) { + dupstate *d = &dupState[i]; + d->kmc = -1; + d->dupidx = -1; + d->linespeed = DFLT_SPEED; + } + } + + for (k = 0; ((uint32)k) < kmc_dev.numunits; k++) { + sim_debug (DF_INF, dptr, "KMC%d: Reset\n", k); + + /* One-time initialization of UNITs, one/direction/line */ + for (i = 0; i < MAX_ACTIVE; i++) { + if (!tx_units[i][k].action) { + memset (&tx_units[i][k], 0, sizeof tx_units[0][0]); + memset (&rx_units[i][k], 0, sizeof tx_units[0][0]); + + tx_units[i][k].action = &kmc_txService; + tx_units[i][k].flags = UNIT_IDLE; + tx_units[i][k].capac = 0; + tx_units[i][k].unit_kmc = k; + tx_units[i][k].unit_line = i; + + rx_units[i][k].action = &kmc_rxService; + rx_units[i][k].flags = UNIT_IDLE; + rx_units[i][k].capac = 0; + rx_units[i][k].unit_kmc = k; + rx_units[i][k].unit_line = i; + } + } + kmc_masterClear (k); /* If previously running, halt */ + + if (sim_switches & SWMASK ('P')) + gflags &= ~FLG_INIT; + + if (!(gflags & FLG_INIT)) { /* Power-up reset */ + sel0 = 0x00aa; + sel2 = 0xa5a5; + sel4 = 0xdead; + sel6 = 0x5a5a; + memset ((uint8 *)&ucode[0], 0xcc, sizeof ucode); + memset ((uint8 *)&dram[0], 0xdd, sizeof dram); + gflags |= FLG_INIT; + gflags &= ~FLG_UCINI; + sim_register_internal_device (&kmc_int_rxdev); + } + } + + kmc_int_rxdev.flags &= ~DEV_DIS; /* Make internal RX device */ + kmc_int_rxdev.flags |= (kmc_dev.flags & DEV_DIS); /* enable/disable track KDP device */ + + return SCPE_OK; +} + +/* + * Read registers: + */ + +static t_stat kmc_readCsr(DEVICE *dptr, t_addr PA, uint16* data, int32 access) { + int32 k; + + k = ((PA-((DIB *)kmc_dev.ctxt)->uba_addr) / IOLN_KMC); + + switch ((PA >> 1) & 03) { + case 00: + *data = sel0; + break; + case 01: + *data = sel2; + break; + case 02: + if ((sel0 & SEL0_RMO) && (sel0 & SEL0_RMI)) { + *data = mni; + } else { + *data = sel4; + } + break; + case 03: + if (sel0 & SEL0_RMO) { + if (sel0 & SEL0_RMI) { + *data = mni; + } else { + *data = ucode[mna]; + } + } else { + *data = sel6; + } + break; + } + + sim_debug (DF_RGR, &kmc_dev, "KMC%u CSR rd: addr=0%06o SEL%d, data=%06o 0x%04x access=%d\n", + k, PA, PA & 07, *data, *data, access); + return SCPE_OK; +} + +/* + * Write registers: + */ +static t_stat kmc_writeCsr(DEVICE *dptr, t_addr PA, uint16 data, int32 access) { + uint32 changed; + int reg = PA & 07; + int sel = (PA >> 1) & 03; + int32 k; + + k = ((PA-((DIB *)kmc_dev.ctxt)->uba_addr) / IOLN_KMC); + + if (access != BYTE) { + sim_debug (DF_RGW, &kmc_dev, "KMC%u CSR wr: addr=0%06o SEL%d, data=%06o 0x%04x\n", + k, PA, reg, data, data); + } else { + sim_debug (DF_RGW, &kmc_dev, "KMC%u CSR wr: addr=0%06o BSEL%d, data=%06o 0x%04x\n", + k, PA, reg, data, data); + } + + switch (sel) { + case 00: /* SEL0 */ + if (access == BYTE) { + data = (PA & 1) + ? (((data & 0377) << 8) | (sel0 & 0377)) + : ((data & 0377) | (sel0 & 0177400)); + } + changed = sel0 ^ data; + sel0 = (uint16)data; + if (sel0 & SEL0_MRC) { + if (((sel0 & SEL0_RUN) == 0) && (changed & SEL0_RUN)) { + kmc_halt (k, HALT_MRC); + } + kmc_masterClear(k); + break; + } + if (!(data & SEL0_RUN)) { + if (data & SEL0_RMO) { + if ((changed & SEL0_CWR) && (data & SEL0_CWR)) { /* CWR rising */ + ucode[mna] = sel6; + sel4 = ucode[mna]; /* Copy contents to sel 4 */ + } + } else { + if (changed & SEL0_RMO) { /* RMO falling */ + sel4 = mna; + } + } + if ((data & SEL0_RMI) && (changed & SEL0_RMI)) { + mni = sel6; + } + if ((data & SEL0_SUP) && (changed & SEL0_SUP)) { + if (data & SEL0_RMI) { + kmc_doMicroinstruction(k, mni); + } else { + kmc_doMicroinstruction(k, ucode[upc++]); + } + } + } + if (changed & SEL0_RUN) { /* Changing the run bit? */ + if (sel0 & SEL0_RUN) { + kmc_startUcode (k); + } else { + kmc_halt (k, HALT_STOP); + } + } + if (changed & (SEL0_IEI | SEL0_IEO)) + kmc_updints (k); + + if ((sel0 & SEL0_RUN)) { + if ((sel0 & SEL0_RQI) && !(sel2 & SEL2_RDO)) + sel2 = (sel2 & 0xFF00) | SEL2_RDI; /* Clear command bits too */ + kmc_updints(k); + } + break; + case 01: /* SEL2 */ + if (access == BYTE) { + data = (PA & 1) + ? (((data & 0377) << 8) | (sel2 & 0377)) + : ((data & 0377) | (sel2 & 0177400)); + } + if (sel0 & SEL0_RUN) { + /* Handle commands in and out. + * Output takes priority, but after servicing an + * output, an input request must be serviced even + * if another output command is ready. + */ + if ((sel2 & SEL2_RDO) && (!(data & SEL2_RDO))) { + sel2 = (uint16)data; /* RDO clearing, RDI can't be set */ + if (sel0 & SEL0_RQI) { + sel2 = (sel2 & 0xFF00) | SEL2_RDI; + kmc_updints(k); + } else + kmc_processCompletions(k); + } else { + if ((sel2 & SEL2_RDI) && (!(data & SEL2_RDI))) { + sel2 = (uint16)data; /* RDI clearing, RDO can't be set */ + kmc_dispatchInputCmd(k); /* Can set RDO */ + if ((sel0 & SEL0_RQI) && !(sel2 & SEL2_RDO)) + sel2 = (sel2 & 0xFF00) | SEL2_RDI; + kmc_updints(k); + } else { + sel2 = (uint16)data; + } + } + } else { + sel2 = (uint16)data; + } + break; + case 02: /* SEL4 */ + mna = data & (KMC_CRAMSIZE -1); + sel4 = (uint16)data; + break; + case 03: /* SEL6 */ + if (sel0 & SEL0_RMI) { + mni = (uint16)data; + } + sel6 = (uint16)data; + break; + } + + return SCPE_OK; +} + + +/* Microengine simulator + * + * This simulates a small subset of the KMC11-A's instruction set. + * This is necessary because the operating systems force microprocessor + * to execute these instructions in order to load (and extract) state. + * These are implemented here so that the OS tools are happy, and to + * give their error logging tools something to do. + */ + +static void kmc_doMicroinstruction (int32 k, uint16 instr) { + switch (instr) { + case 0041222: /* MOVE */ + sel2 = (sel2 & ~0xFF) | (dram[mar%KMC_DRAMSIZE] & 0xFF); + break; + case 0055222: /* MOVE */ + sel2 = (sel2 & ~0xFF) | (dram[mar%KMC_DRAMSIZE] & 0xFF); + mar = (mar +1)%KMC_DRAMSIZE; + break; + case 0122440: /* MOVE */ + dram[mar%KMC_DRAMSIZE] = sel2 & 0xFF; + break; + case 0136440: /* MOVE */ + dram[mar%KMC_DRAMSIZE] = sel2 & 0xFF; + mar = (mar +1)%KMC_DRAMSIZE; + break; + case 0121202: /* MOVE */ + case 0021002: /* MOVE */ + sel2 = (sel2 & ~0xFF) | 0; + break; + default: + if ((instr & 0160000) == 0000000) { /* MVI */ + switch (instr & 0174000) { + case 0010000: /* Load MAR LOW */ + mar = (mar & 0xFF00) | (instr & 0xFF); + break; + case 0004000: /* Load MAR HIGH */ + mar = (mar & 0x00FF) | ((instr & 0xFF)<<8); + break; + default: /* MVI NOP / MVI INC */ + break; + } + break; + } + if ((instr & 0163400) == 0100400) { + upc = ((instr & 0014000) >> 3) | (instr & 0377); + sim_debug (DF_INF, &kmc_dev, "KMC%u microcode start uPC %04o\n", k, upc); + break; + } + } + return; +} + +/* Transmitter service. + * + * Each line has a TX unit. This thread handles message transmission. + * + * A host-supplied buffer descriptor list is walked and the data handed off + * to the line. Because the DUP emulator can't abort a transmission in progress, + * and wants to receive formatted messages, the data from host memory is + * accumulated into an intermediate buffer. + * + * TX BUFFER OUT completions are delivered as the data is retrieved, which happens + * at approximately the 'line speed'; this does not indicate that the data is on + * the wire. Only a DDCMP ACK confirms receipt. + * + * TX CONTROL OUT completions are generated for errors. + * + * The buffer descriptor flags for resync, start and end of message are obeyed. + * + * The transmitter asserts RTS at the beginning of a message, and clears it when + * idle. + * + * No more than one message is on the wire at any time. + * + * The DUP may be speed limited, delivering transmit completions at line speed. + * In that case, the KDP and DUP limits will interact. + * + * This thread wakes: + * o When a new buffer descriptor is delivered by the host + * o When a state machine timeout expires + * o When a DUP transmit completion occurs. + * + */ + +static t_stat kmc_txService (UNIT *txup) { + int32 k = txup->unit_kmc; + dupstate *d = line2dup[txup->unit_line]; + t_bool more; + + ASSURE ((k >= 0) && (k < (int32) kmc_dev.numunits) && (d->kmc == k) && + (d->line == txup->unit_line)); + + /* Provide the illusion of progress. */ + upc = 1 + ((upc + 1) % (KMC_CRAMSIZE -1)); + + /* Process one buffer descriptor per cycle + * CAUTION: this switch statement uses fall-through cases. + */ + +/* State control macros. + * + * All exit the current statement (think "break" or "continue") + * + * Change to newstate and process immediately + */ + +#define TXSTATE(newstate) { \ + d->txstate = newstate; \ + more = FALSE; \ + continue; } + +/* Change to newstate after a delay of time. */ +#define TXDELAY(newstate,time) { \ + txup->wait = time; \ + d->txstate = newstate; \ + more = FALSE; \ + break; } + +/* Stop processing an return to IDLE. + * This will NOT check for more work - it is used + * primarily in error conditions. + */ +#define TXSTOP { \ + d->txstate = TXIDLE; \ + more = FALSE; \ + break; } + + do { + more = TRUE; + + if (d->txstate > TXRTS) { + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: transmit service %s state = %u\n", + k, txup->unit_line, (more? "continued": "activated"), d->txstate); + } + + switch (d->txstate) { + case TXDONE: /* Resume from completions */ + d->txstate = TXIDLE; + /* Fall through */ + + case TXIDLE: /* Check for new BD */ + if (!kmc_txNewBdl(d)) { + TXSTOP; + } + d->txmlen = + d->txslen = 0; + d->txstate = TXRTS; + + if (dup_set_RTS (d->dupidx, TRUE) != SCPE_OK) { + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: dup: %d DUP CSR NXM\n", + k, d->line, d->dupidx); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, 0); + } + /* Fall through */ + + case TXRTS: /* Wait for CTS */ + if (dup_get_CTS (d->dupidx) <= 0) { + TXDELAY (TXRTS, TXCTS_DELAY); + } + + d->txstate = TXSOM; + + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: transmitting bdl=%06o\n", + k, txup->unit_line, d->tx.bda); + /* Fall through */ + + case TXSOM: /* Start assembling a message */ + if (!(d->tx.bd[2] & BDL_SOM)) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: TX BDL not SOM\n", k, d->line); + kmc_halt (k, HALT_XSOM); + TXSTOP; + } + + if (d->tx.bd[2] & BDL_RSY) { + static const uint8 resync[8] = { DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, + DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, }; + if (!d->txmsg || (d->txmsize < sizeof (resync))) { + d->txmsize = 8 + sizeof (resync); + d->txmsg = (uint8 *)realloc (d->txmsg, 8 + sizeof (resync)); + } + memcpy (d->txmsg, resync, sizeof (resync)); + d->txmlen = + d->txslen = sizeof (resync); + } + + d->txstate = TXHDR; + /* Fall through */ + + case TXHDR: /* Assemble the header */ + if (!kmc_txAppendBuffer(d)) { /* NXM - Try next list */ + TXDELAY (TXDONE, TXDONE_DELAY); + } + TXDELAY (TXHDRX, XTIME (d->tx.bd[1], d->linespeed)); + /* Fall through */ + + case TXHDRX: /* Report header descriptor done */ + if (!kmc_bufferAddressOut (k, 0, 0, d->line, d->tx.bda)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + if (!(d->tx.bd[2] & BDL_EOM)) { + if (kmc_txNewBd(d)) { + TXSTATE (TXHDR); + } + /* Not EOM, no more BDs - underrun or NXM. In theory + * this should have waited on char time before declaring + * underrun, but no sensible OS would live that dangerously. + */ + TXDELAY (TXDONE, TXDONE_DELAY); + } + + /* EOM. Control messages are always complete */ + if (d->txmsg[d->txslen+0] == DDCMP_ENQ) { + TXSTATE (TXRDY); + } + + /* EOM expecting data to follow. + * However, if the OS computes and includes HRC in a data/MOP message, this can + * be the last descriptor. In that case, this is EOM. + */ + if (d->tx.bd[2] & BDL_LDS) { + TXSTATE (TXMRDY); + break; + } + + /* Data sent in a separate descriptor */ + + if (!kmc_txNewBd(d)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + + if (!(d->tx.bd[2] & BDL_SOM)) { + kmc_halt (k, HALT_XSOM2); + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: TX BDL not SOM\n", k, d->line); + TXSTOP; + } + d->txstate = TXDATA; + /* Fall through */ + + case TXDATA: /* Assemble data/maint payload */ + if (!kmc_txAppendBuffer(d)) { /* NXM */ + TXDELAY (TXDONE, TXDONE_DELAY); + } + TXDELAY (TXDATAX, XTIME (d->tx.bd[1], d->linespeed)); + /* Fall through */ + + case TXDATAX: /* Report BD completion */ + if (!kmc_bufferAddressOut (k, 0, 0, d->line, d->tx.bda)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + if (d->tx.bd[2] & BDL_EOM) { + TXSTATE (TXRDY); + } + if (!kmc_txNewBd(d)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + TXSTATE (TXDATA); + /* Fall through */ + + /* These states hand-off the message to the DUP. + * txService suspends until transmit complete. + * Note that txComplete can happen within the calls to the DUP. + */ + case TXMRDY: /* Data with OS-embedded HCRC */ + d->txstate = TXACT; + ASSURE (d->txmsg[d->txslen + 0] != DDCMP_ENQ); + ASSURE (((d->txmlen - d->txslen) > 8) && /* Data, length should match count */ + (((size_t)(((d->txmsg[d->txslen + 2] & 077) << 8) | d->txmsg[d->txslen + 1])) == + (d->txmlen - (d->txslen + 8)))); + if (!dup_put_msg_bytes (d->dupidx, d->txmsg + d->txslen, d->txmlen - d->txslen, TRUE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXMRDY, TXDUP_DELAY); + } + more = FALSE; + break; + + case TXRDY: /* Control or DATA with KDP-CRCH */ + d->txstate = TXACT; /* Note that DUP can complete before returning */ + if (d->txmsg[d->txslen + 0] == DDCMP_ENQ) { /* Control message */ + ASSURE ((d->txmlen - d->txslen) == 6); + if (!dup_put_msg_bytes (d->dupidx, d->txmsg, d->txslen + 6, TRUE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXRDY, TXDUP_DELAY); + } + more = FALSE; + break; + } + + ASSURE (((d->txmlen - d->txslen) > 6) && /* Data, length should match count */ + (((size_t)(((d->txmsg[d->txslen + 2] & 077) << 8) | d->txmsg[d->txslen + 1])) == + (d->txmlen - (d->txslen + 6)))); + if (!dup_put_msg_bytes (d->dupidx, d->txmsg, d->txslen + 6, TRUE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXRDY, TXDUP_DELAY); + } + if (!dup_put_msg_bytes (d->dupidx, d->txmsg + d->txslen + 6, d->txmlen - (d->txslen + 6), FALSE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXRDY, TXDUP_DELAY); + } + more = FALSE; + break; + + /* Active should never be reached as txService is not + * scheduled while transmission is in progress. + * txComplete will reset txstate based on the final descriptor. + */ + default: + case TXACT: + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: kmc_txService called while active\n", k, d->line); + TXSTOP; + } + } while (more); +#undef TXSTATE +#undef TXDELAY +#undef TXSTOP + + if (d->txstate == TXIDLE) { + ASSURE (!d->txavail); + if (dup_set_RTS (d->dupidx, FALSE) != SCPE_OK) { + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: dup: %d DUP CSR NXM\n", + k, d->line, d->dupidx); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, 0); + } + } else { + if (d->txstate != TXACT) + sim_activate_after(txup, txup->wait); + } + return SCPE_OK; +} + +/* Receiver service + * + * Each line has an RX unit. This service thread accepts incoming + * messages from the DUP, and delivers the data into buffer descriptors + * provided by the host. + * + * Polling is not necessary if the DUP provides input notification; otherwise + * it's at a rate of a few character times at 19,200 - the maximum line speed. + * + * Once a message has been accepted, the thread looks for a host-provided + * buffer descriptor. If one doesn't appear in a reasonable time (in hardware, + * it's one character time - this code is more generous), the message is dropped + * and a RX CONTROL OUT is issued. + * + * Then the message is parsed, validated and delivered to the host buffer. + * + * Any CRC errors generate RX CONTROL OUTs. RX BUFFER OUTs are generated for each + * descriptor filled, and at of message for the last descriptor used. + * + * If the line is configured for Secondary Station address matching, the + * message is discarded if the address does not match (and the CRC validated). + * + * After generating a RX BUFFER OUT, the thread suspends prior to filling the + * next descriptor. + * + * If reception is killed, the RX state machine is reset by the RX BUFFER IN. + * + * This thread wakes: + * o When a RX BUFFER IN delivers a buffer + * o When timeouts between states expire. + * o When the DUP notifies it of a new message received. + */ + +static t_stat kmc_rxService (UNIT *rxup) { + int32 k = rxup->unit_kmc; + dupstate *d = line2dup[rxup->unit_line]; + BDL *bdl; + t_stat r; + uint16 seglen; + int i; + t_addr ba; + + + ASSURE ((k >= 0) && (k < (int32) kmc_dev.numunits) && (d->kmc == k) && + (d->line == rxup->unit_line)); + + if (d->rxstate > RXBDL) { + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receive service activated state = %u\n", + k, rxup->unit_line, d->rxstate); + } + + /* Provide the illusion of progress. */ + upc = 1 + ((upc + 1) % (KMC_CRAMSIZE -1)); + + rxup->wait = RXPOLL_DELAY; + + /* CAUTION: This switch statement uses fall-through cases + * + * The KILL logic in kmc_rxBufferIn tracks these states. + */ + + switch (d->rxstate) { + case RXIDLE: + rxup->wait = RXPOLL_DELAY; + + r = dup_get_packet (d->dupidx, (const uint8 **)&d->rxmsg, &d->rxmlen); + if (r == SCPE_LOST) { + kmc_updateDSR (d); + break; + } + if ((r != SCPE_OK) || (d->rxmsg == NULL)) { /* No packet? */ + rxup->wait = tmxr_poll; + break; + } + + if (!(d->ctrlFlags & SEL6_CI_ENABLE)) { + break; + } + + while (d->rxmlen && (d->rxmsg[0] == DDCMP_SYN)) { + d->rxmsg++; + d->rxmlen--; + } + if (d->rxmlen < 8) { + break; + } + + if (!((d->rxmsg[0] == DDCMP_SOH) || + (d->rxmsg[0] == DDCMP_ENQ) || + (d->rxmsg[0] == DDCMP_DLE))) { + + /* Toggling RCVEN causes the DUP receiver to resync. + */ +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + break; + } + + d->rxstate = RXBDL; + d->rxused = 0; + + if (DEBUG_PRS (kmc_dev)) { + if (d->rxmsg[0] == DDCMP_ENQ) { + static const char *const ctlnames [] = { + "00", "ACK", "NAK", "REP", "04", "05", "STRT", "STACK" }; + uint8 type = d->rxmsg[1]; + + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving %s\n", + k, rxup->unit_line, + ((type >= DIM (ctlnames))? "UNKNOWN" : ctlnames[type])); + } else { + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving %s len=%u\n", + k, rxup->unit_line, + ((d->rxmsg[0] == DDCMP_SOH)? "DATA" : "MAINT"), d->rxmlen); + } + } + /* Fall through */ + + case RXBDL: + if (!(bdl = (BDL *)remqueue(d->rxqh.next, &d->rxavail))) { + rxup->wait = RXBDL_DELAY; + d->rxstate = RXNOBUF; + break; + } + d->rx.bda = bdl->ba; + ASSURE (insqueue (&bdl->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving bdl=%06o\n", + k, rxup->unit_line, d->rx.bda); + + ba = d->rx.bda; + for (i = 0; i < 3; i++) { + if (uba_read_npr_word(ba, kmc_dib.uba_ctl, &d->rx.bd[i]) == 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + goto done; + } + ba += 2; + } + d->rxstate = RXBUF; + /* Fall through */ + + case RXBUF: + d->rx.ba = ((d->rx.bd[2] & BDL_XAD) << BDL_S_XAD) | d->rx.bd[0]; + if (d->rx.bd[1] == 0) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: RX buffer descriptor size is zero\n", k, d->line); + kmc_halt (k, HALT_MTRCV); + d->rxstate = RXIDLE; + break; + } + d->rx.rcvc = 0; + d->rxdlen = 0; + d->rxstate = RXDAT; + /* Fall through */ + + case RXDAT: + more: + if (d->rxused < 8) { + seglen = 6 - d->rxused; + } else { + seglen = d->rxmlen - (d->rxused +2); + } + if (seglen > d->rx.bd[1]) { + seglen = d->rx.bd[1]; + } + ASSURE (seglen > 0); + + ba = d->rx.ba; + sim_debug (DF_PKT, &kmc_dev, "Send to: "); + for (i = 0; i < seglen; i++) { + sim_debug (DF_PKT, &kmc_dev, "%02x ", d->rxmsg[d->rxused + i]); + if (uba_write_npr_byte(ba, kmc_dib.uba_ctl, d->rxmsg[d->rxused + i]) == 0) { + uint16 bd[3]; + memcpy(bd, &d->rx.bd, sizeof bd); + d->rx.rcvc += i; + bd[1] = d->rx.rcvc; + kmc_updateBDCount (d->rx.bda, bd); /* Unchecked because already reporting NXM */ + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + goto done; + } + ba++; + } + sim_debug (DF_PKT, &kmc_dev, "\n"); + d->rx.ba += seglen; + d->rx.rcvc += seglen; + d->rxused += seglen; + + if (d->rxused == 6) { + if (0 != ddcmp_crc16 (0, d->rxmsg, 8)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: HCRC Error for %d byte packet\n", + k, d->line, d->rxmlen); +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + kmc_ctrlOut (k, SEL6_CO_HCRC, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + break; + } + d->rxused += 2; + d->linkstate &= ~LINK_SEL; + if (d->rxmsg[2] & 0x80) { + d->linkstate |= LINK_SEL; + } + if (d->ctrlFlags & SEL6_CI_ENASS) { /* Note that spec requires first bd >= 6 if SS match enabled */ + if (!(d->rxmsg[5] == (d->ctrlFlags & SEL6_CI_SADDR))) { /* Also include SELECT? */ + ASSURE ((bdl = (BDL *)remqueue(d->bdqh.prev, &d->bdavail)) != NULL); + ASSURE (bdl->ba == d->rx.bda); + ASSURE (insqueue (&bdl->hdr, &d->rxqh, &d->rxavail, MAXQUEUE)); + d->rxstate = RXIDLE; + break; + } + } + d->rxdlen = ((d->rxmsg[2] &~ 0300) << 8) | d->rxmsg[1]; + } + if (((d->rxused == 8) && (d->rxmsg[0] == DDCMP_ENQ)) || + (((d->rxused - 8) == d->rxdlen) && (d->rxmsg[0] != DDCMP_ENQ))) { /* End of message */ + + /* Issue completion after the nominal reception delay */ + + rxup->wait = XTIME (d->rx.rcvc+2, d->linespeed); + d->rxstate = RXLAST; + break; + } + if (d->rx.rcvc < d->rx.bd[1]) { + goto more; + } + + /* This descriptor is full. No need to update its bc. + * Issue the completion after the nominal reception delay. + */ + d->rxstate = RXFULL; + rxup->wait = XTIME (d->rx.bd[1], d->linespeed); + break; + + case RXLAST: + /* End of message. Update final BD count, check data CRC & report either + * BUFFER OUT (with EOM) or error CONTROL OUT (NXM or DCRC). + */ + d->rx.bd[1] = d->rx.rcvc; + if (kmc_updateBDCount (d->rx.bda, d->rx.bd)) { + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + } else { + if ((d->rxmsg[0] != DDCMP_ENQ) && + (0 != ddcmp_crc16 (0, d->rxmsg +8, d->rxdlen+2))) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DCRC Error for %d byte packet\n", + k, d->line, d->rxmlen); +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + kmc_ctrlOut (k, SEL6_CO_DCRC, SEL2_IOT, d->line, d->rx.bda); + } else { + kmc_bufferAddressOut (k, SEL6_BO_EOM, SEL2_IOT, d->line, d->rx.bda); +#ifdef DUP_RXRESYNC + if (d->rxmsg[2] & 0x40) { /* QSYNC? (Next message uses short sync) */ + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); + } +#endif + } + } + rxup->wait = RXNEWBD_DELAY; + d->rxstate = RXIDLE; + break; + + case RXFULL: + kmc_bufferAddressOut (k, 0, SEL2_IOT, d->line, d->rx.bda); + + /* Advance to next descriptor */ + + if (d->rx.bd[2] & BDL_LDS) { + d->rxstate = RXBDL; + } else { + d->rx.bda += 3*2; + ba = d->rx.bda; + for (i = 0; i < 3; i++) { + if (uba_read_npr_word(ba, kmc_dib.uba_ctl, &d->rx.bd[i]) == 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + goto done; + } + ba += 2; + } + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving bd=%06o\n", + k, rxup->unit_line, d->rx.bda); + d->rx.rcvc = 0; /* Set here in case of kill */ + d->rxstate = RXBUF; + } + rxup->wait = RXNEWBD_DELAY; + break; + + case RXNOBUF: + kmc_ctrlOut (k, SEL6_CO_NOBUF, SEL2_IOT, d->line, 0); + d->rxstate = RXIDLE; + break; + + default: + ASSURE (FALSE); + } + +done: + + if ((d->rxstate != RXIDLE) || d->rxavail) { + if (rxup->wait == tmxr_poll) + sim_clock_coschedule(rxup, tmxr_poll); + else + sim_activate_after(rxup, rxup->wait); + } + + return SCPE_OK; +} + +/* + * master clear a KMC + * + * Master clear initializes the hardware, but does not clear any RAM. + * This includes the CSRs, which are a dual-ported RAM structure. + * + * There is no guarantee that any data structures are initialized. + */ + +static void kmc_masterClear(int32 k) { + + if (sim_deb) { + DEVICE *dptr = find_dev_from_unit (&tx_units[0][k]); + + sim_debug (DF_INF, dptr, "KMC%d: Master clear\n", k); + } + + if (sel0 & SEL0_RUN) { + kmc_halt (k, HALT_MRC); + } + + /* Clear SEL1 (maint reg) - done by HW reset. + * Clear IE (HW doesn't, but it simplifies + * things & every user to date writes zeroes with MRC. + */ + sel0 &= SEL0_MRC | (0x00FF & ~(SEL0_IEO | SEL0_IEI)); + upc = 0; + mar = 0; + mna = 0; + mni = 0; + + kmc_updints (k); +} + +/* Initialize the KMC state that is done by microcode */ + +static void kmc_startUcode (int32 k) { + int i; + const char *uname; + + if ((uname = kmc_verifyUcode (k)) == NULL) { + sim_debug (DF_INF, &kmc_dev, "KMC%u: microcode not loaded, won't run\n", k); + kmc_halt (k, HALT_BADUC); + return; + } + + sim_debug (DF_INF, &kmc_dev, "KMC%u started %s microcode at uPC %04o\n", + k, uname, upc); + + if (upc != 0) { /* Resume from cleared RUN */ + if (gflags & FLG_UCINI) { + for (i = 0; i < MAX_ACTIVE; i++) { + UNIT *up = &tx_units[i][k]; + + if (up->unit_htime) { + sim_activate (up, up->unit_htime); + } + up = &rx_units[i][k]; + if (up->unit_htime) { + sim_activate (up, up->unit_htime); + } + } + return; + } + kmc_halt (k, HALT_BADRES); + return; + } + + /* upc == 0: microcode initialization */ + + upc = 1; + + /* CSRs */ + + sel0 &= 0xFF00; + sel2 = 0; + sel4 = 0; + sel6 = 0; + + /* Line data */ + + /* Initialize OS mapping to least likely device. (To avoid validating everywhere.) */ + + for (i = 0; i < MAX_ACTIVE; i++) { + line2dup[i] = &dupState[DUP_LINES-1]; + } + + /* Initialize all the DUP structures, releasing any assigned to this KMC. + * + * Only touch the devices if they have previously been assigned to this KMC. + */ + + for (i = 0; i < DUP_LINES; i++) { + dupstate *d = dupState + i; + + if ((d->kmc == k) && (d->dupidx != -1)) { + /* Make sure no callbacks are issued. + * Hardware initialization is done by BASE IN. + */ + dup_set_callback_mode (i, NULL, NULL, NULL); + } + /* Initialize DUP state if dup is unassigned or previously assigned + * to this KMC. This releases the DUP, so restarting ucode will + * release devices, allowing another KMC to assign them if desired. + * This is a level of cooperation that the real devices don't have, + * but it helps catch configuration errors and keeps these data + * structures consistent. Don't deassign a device currently owned + * by another kmc! + */ + if ((d->kmc == k) || (d->kmc == -1)) { + d->dupidx = -1; + d->kmc = -1; + d->line = UNASSIGNED_LINE; + + initqueue (&d->rxqh, &d->rxavail, INIT_HDR_ONLY); + initqueue (&d->txqh, &d->txavail, INIT_HDR_ONLY); + initqueue (&d->bdqh, &d->bdavail, MAX_LIST_SIZE(d->bdq)); + + d->rxstate = RXIDLE; + d->txstate = TXIDLE; + } + } + + /* Completion queue */ + + initqueue(&cqueueHead, &cqueueCount, INIT_HDR_ONLY); + initqueue(&freecqHead, &freecqCount, MAX_LIST_SIZE(cqueue)); + + gflags |= FLG_UCINI; + kmc_updints (k); + return; +} + +/* + * Perform an input command + * + * The host must request ownership of the CSRs by setting RQI. + * If enabled, it gets an interrupt when RDI sets, allowing it + * to write the command. RDI and RDO are mutually exclusive. + * + * The microcode sets RDI by writing the entire BSEL2 with just RDI. + * This works because RDO can not be set at the same time as RDI. + * + * Mark P found that VMS drivers rely on this and BIS the command code. + * The COM IOP-DUP manual does say that all bits of BSEL2 are + * cleared on completion of a command. However, an output command could + * leave other bits on. + * + * Input commands are processed by the KMC when the host + * clears RDI. Upon completion of a command, 'all bits of bsel2 + * are cleared by the KMC'. This is not implemented literally, since + * the processing of a command can result in an immediate completion, + * setting RDO and the other registers. + * Thus, although all bits are cleared before dispatching, RDO + * and the other other bits of BSEL2 may be set for a output command + * due to a completion if the host has cleared RQI. + */ + +static void kmc_dispatchInputCmd(int32 k) { + uint8 line; + int32 ba; + int16 cmdsel2 = sel2; + dupstate* d; + + line = (cmdsel2 & SEL2_LINE) >> SEL2_V_LINE; + + sel2 &= ~0xFF; /* Clear BSEL2. */ + if (sel0 & SEL0_RQI) /* If RQI was left on, grant the input request */ + sel2 |= SEL2_RDI; /* here so any generated completions will block. */ + + if (line > MAX_LINE) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: Line number is out of range\n", k, line); + kmc_halt (k, HALT_LINE); + return; + } + d = line2dup[line]; + ba = ((sel6 & SEL6_CO_XAD) << (16-SEL6_V_CO_XAD)) | sel4; + + sim_debug (DF_CMD, &kmc_dev, "KMC%u line %u: INPUT COMMAND sel2=%06o sel4=%06o sel6=%06o ba=%06o\n", k, line, + cmdsel2, sel4, sel6, ba); + + switch (cmdsel2 & (SEL2_IOT | SEL2_CMD)) { + case CMD_BUFFIN: /* TX BUFFER IN */ + kmc_txBufferIn(d, ba, sel6); + break; + case CMD_CTRLIN: /* CONTROL IN. */ + case SEL2_IOT | CMD_CTRLIN: + kmc_ctrlIn (k, d, line); + break; + + case CMD_BASEIN: /* BASE IN. */ + kmc_baseIn (k, d, cmdsel2, line); + break; + + case (SEL2_IOT | CMD_BUFFIN): /* Buffer in, receive buffer for us... */ + kmc_rxBufferIn(d, ba ,sel6); + break; + default: + kmc_halt (k, HALT_BADCMD); + break; + } + + return; +} +/* Process BASE IN command + * + * BASE IN assigns a line number to a DUP device, and marks it + * assigned by a KMC. The CSR address is expressed as bits <12:3> + * only. <17:13> are all set for IO page addresses and added by ucode. + * The DUP has 8 registers, so <2:1> must be zero. The other bits are + * reserved and must be zero. + * + * There is no way to release a line, short of re-starting the microcode. + * + */ +static void kmc_baseIn (int32 k, dupstate *d, uint16 cmdsel2, uint8 line) { + uint32 csraddress; + int32 dupidx; + + /* Verify DUP is enabled and at specified address */ + + /* Ucode clears low three bits of CSR address, ors 1s into the high 3. + * CSR address here may include bits >17 (e.g. UBA number) + * + * The check for reserved bits is not done by the ucode, and can be + * removed. It's here for debugging any cases where this code is at fault. + */ + + csraddress = sel6 & SEL6_II_DUPCSR; + + if ((sel4 != 0) || (cmdsel2 & SEL2_II_RESERVED)) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u: BASE IN reserved bits set\n", k); + kmc_halt (k, HALT_BADCSR); + return; + } + csraddress |= 0760000; + + dupidx = dup_csr_to_linenum (csraddress); + if ((dupidx < 0) || (((size_t) dupidx) >= DIM(dupState))) { /* Call this a NXM so OS can recover */ + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: BASE IN %06o 0x%05x is not an enabled DUP %d\n", + k, line, csraddress, csraddress, dupidx); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, 0); + return; + } + d = &dupState[dupidx]; + if ((d->kmc != -1) && (d->kmc != k)) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: BASE IN %06o 0x%05x is already assigned to KMC%u\n", + k, line, csraddress, csraddress, + d->kmc); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, 0); + return; + } + + /* OK to take ownership. + * Master clear the DUP. NXM will cause a control-out. + * + * The microcode takes no special action to clear DTR. + */ + + d->dupcsr = csraddress; + d->kmc = k; + line2dup[line] = d; + d->line = line; + + /* + * Jumper W3 Installed causes RTS,DTR, and SecTxD to be cleared on Device Reset or bus init. + * Jumper W3 is installed in factory DUPs, and in the KS10 config. + * Make sure the DUP emulation enables this option. + */ + dup_set_W3_option (dupidx, 1); + /* + * Reset the DUP device. This will clear DTR and RTS. + */ + if (dup_reset_dup (dupidx) != SCPE_OK) { + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: BASE IN dup %d DUP TXCSR NXM\n", + k, line, dupidx); + d->kmc = -1; + return; + } + + /* Hardware requires a 2 usec delay before next access to DUP. + */ + + d->dupidx = dupidx; + + sim_debug (DF_INF, &kmc_dev, "KMC%u line %u: BASE IN DUP%u address=%06o 0x%05x assigned\n", + k, line, d->dupidx,csraddress, csraddress); + return; +} + +/* Process CONTROL IN command + * + * CONTROL IN establishes the characteristics of each communication line + * controlled by the KMC. At least one CONTROL IN must be issued for each + * DUP that is to communicate. + * + * CONTROL IN writes the DUP CSRs to configure it for the selected mode. + * + * Not implemented: + * o Polling count (no mapping to emulator) (SEL4_CI_POLL) + */ + +static void kmc_ctrlIn (int32 k, dupstate *d, int line) { + t_stat r; + + if (DEBUG_PRS (&kmc_dev)) { + sim_debug (DF_CMD, &kmc_dev, "KMC%u line %u: CONTROL IN ", k, line); + if (!(sel6 & SEL6_CI_ENABLE)) { + sim_debug (DF_CMD, &kmc_dev, "line disabled\n"); + } else { + sim_debug (DF_CMD, &kmc_dev, "enabled for %s in %s duplex", + (sel6 & SEL6_CI_DDCMP)? "DDCMP":"Bit-stuffing", + (sel6 & SEL6_CI_HDX)? "half" : "full"); + if (sel6 & SEL6_CI_ENASS) { + sim_debug (DF_CMD, &kmc_dev, " SS:%u-%d", + (sel6 & SEL6_CI_SADDR), line); + } + sim_debug (DF_CMD, &kmc_dev, "\n"); + } + } + + /* BSEL4 has the polling count, which isn't needed for emulation */ + + d->linkstate &= ~(LINK_DSR|LINK_SEL);/* Initialize modem state reporting. */ + + d->ctrlFlags = sel6; + + /* Initialize DUP interface in the appropriate mode + * DTR will be turned on. + */ + r = dup_setup_dup (d->dupidx, (sel6 & SEL6_CI_ENABLE), + (sel6 & SEL6_CI_DDCMP), + (sel6 & SEL6_CI_NOCRC), + (sel6 & SEL6_CI_HDX), + (sel6 & SEL6_CI_ENASS) ? (sel6 & SEL6_CI_SADDR) : 0); + + /* If setup succeeded, enable packet callbacks. + * + * If setup failed, generate a CONTROL OUT. + */ + if (r == SCPE_OK) { + dup_set_callback_mode (d->dupidx, kdp_receive, kmc_txComplete, kmc_modemChange); + } else { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, 0); + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: CONTROL IN dup %d DUP CSR NXM\n", + k, line, d->dupidx); + } + + return; +} + +/* Process RX BUFFER IN command + * + * RX BUFFER IN delivers a buffer descriptor list from the host to + * the KMC. It may also deassign a buffer descriptor list. + * + * A buffer descriptor list is a sequential list of three word blocks in + * host memory space. Each 3-word block points to and describes the + * boundaries of a single buffer, which is also in host CPU memory. + * + * A buffer descriptor list is defined by its starting address. The + * end of a buffer descriptor list is marked in the list. A maximum of + * MAXQUEUE transmit and MAXQUEUE receive lists can be assigned to each + * COMM IOP-DUP communications line. + * + * The starting address of a buffer descriptor list must be word aligned. + * + * The buffers in this list will be filled with data received from the + * line and returned to the host by RX BUFFER OUT completions. + * + * Buffer descriptor lists are deassigned through use of the Kill bit + * and also reassigned when this bit is used in conjuntion with the + * Buffer Enable bit. When Kill is set, all buffer descriptor lists for + * the specified direction (RX or TX) are released. If the enable bit is + * also set, a new buffer descriptor list is assigned after the old lists + * are deassigned. In any case, RX kill places the associated DUP in sync + * search mode. TX kill brings the line to a mark hold state. + * + * Note that Buffer Enable is ignored unless Kill is also set. + * + */ + +void kmc_rxBufferIn(dupstate *d, int32 ba, uint16 sel6v) { + int32 k = d->kmc; + BDL *qe; + uint32 bda = 0; + UNIT *rxup; + + if (d->line == UNASSIGNED_LINE) + return; + + ASSURE ((k >= 0) && (((unsigned int)k) < kmc_dev.numunits) && (d->dupidx != -1)); + + rxup = &rx_units[d->line][k]; + + if (!kmc_printBufferIn (k, &kmc_dev, d->line, TRUE, d->rxavail, ba, sel6v)) + return; + + if (sel6v & SEL6_BI_KILL) { + /* Kill all current RX buffers. + * Resync the DUP receiver. + */ +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + if ((d->rxstate >= RXBUF) && (d->rxstate < RXFULL)) { + /* A bd is open (active). + * Updating bytes received should be done before the kill. TOPS-10 clears the UBA map + * before requesting it. But it doesn't look at bd. So don't report NXM. + * In these states, the bd is always cached. + */ + d->rx.bd[1] = d->rx.rcvc; + kmc_updateBDCount (d->rx.bda, d->rx.bd); + bda = d->rx.bda; + } else { + bda = 0; + } + d->rxstate = RXIDLE; + sim_cancel (rxup); + while ((qe = (BDL *)remqueue (d->rxqh.next, &d->rxavail)) != NULL) { + ASSURE (insqueue (&qe->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + } + if (!(sel6v & SEL6_BI_ENABLE)) { + kmc_ctrlOut (k, SEL6_CO_KDONE, SEL2_IOT, d->line, bda); + return; + } + } + + /* Add new buffer to available for RX queue */ + + if ((qe = (BDL *)remqueue (d->bdqh.next, &d->bdavail)) == NULL) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: Too many receive buffers from hostd\n", k, d->line); + kmc_halt (k, HALT_RCVOVF); + return; + } + qe->ba = ba; + ASSURE (insqueue (&qe->hdr, d->rxqh.prev, &d->rxavail, MAXQUEUE)); + + if (sel6v & SEL6_BI_KILL) { /* KILL & Replace - ENABLE is set too */ + kmc_ctrlOut (k, SEL6_CO_KDONE, SEL2_IOT, d->line, bda); + } + + /* Start receiver if necessary */ + + if ((d->rxstate == RXIDLE) && !sim_is_active (rxup)) { + sim_activate_after (rxup, RXSTART_DELAY); + } + return; +} + +/* Message available callback + * + * The DUP calls this routine when a new message is available + * to be read. + * + * If the line's receive thread is idle, it is called to start the + * receive process. If the thread is busy, the message will be + * retrieved when the current receive process completes. + * + * This notification avoids the need for the receive thread to + * periodically poll for an incoming message when idle. + * + * The data and count arguments are unused - the function signature + * requires them for other modes. + */ + +static void kdp_receive(int32 dupidx, int count) { + int32 k; + dupstate* d; + UNIT *rxup; + UNUSED_ARG (count); + + ASSURE ((dupidx >= 0) && (((size_t)dupidx) < DIM(dupState))); + d = &dupState[dupidx]; + ASSURE (dupidx == d->dupidx); + k = d->kmc; + rxup = &rx_units[d->line][k]; + + if (d->rxstate == RXIDLE){ + sim_cancel (rxup); + sim_activate_after (rxup, RXNEWBD_DELAY); + } + return; +} + +/* Process TX BUFFER IN command + * + * TX BUFFER IN delivers a buffer descriptor list from the host to + * the KMC. It may also deassign a buffer descriptor list. + * + * For a complete description of buffer descriptor list, see + * RX BUFFER IN. + * + * The buffers in this list contain data to be transmitted to the + * line, and are returned to the host by TX BUFFER OUT completions. + * + * TX buffer descriptors include control flags that indicate start + * and end of message. These determine when the DUP is told start/ + * stop accumulating data for and to insert CRCs. A resync flag + * indicates when sync characters must be inserted (after half-duplex + * line turn-around or CRC errors.) + * + */ + +void kmc_txBufferIn(dupstate *d, int32 ba, uint16 sel6v) { + int32 k = d->kmc; + BDL *qe; + + if (d->line == UNASSIGNED_LINE) + return; + + ASSURE ((k >= 0) && (((unsigned int)k) < kmc_dev.numunits) && (d->dupidx != -1)); + + if (!kmc_printBufferIn (k, &kmc_dev, d->line, FALSE, d->txavail, ba, sel6v)) + return; + + if (sel6v & SEL6_BI_KILL) { + /* Kill all current TX buffers. The DUP can't abort transmission in simulation, so + * anything pending will stop when complete. The queue is reset here because + * the kill & replace option has to be able to enqueue the replacement BDL. + * If a tx is active, the DUP will issue a completion, which will report + * completion of the kill. A partial message that has been buffered, but + * not handed to the DUP can be stopped here. + */ + while ((qe = (BDL *)remqueue (d->txqh.next, &d->txavail)) != NULL) { + ASSURE (insqueue (&qe->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + } + if (d->txstate < TXACT) { /* DUP is idle */ + sim_cancel (&tx_units[d->line][k]); /* Stop tx bdl walker */ + d->txstate = TXIDLE; + + if (!(sel6v & SEL6_BI_ENABLE)) { + kmc_ctrlOut (k, SEL6_CO_KDONE, 0, d->line, 0); + return; + } + /* Continue to replace buffer */ + } else { /* DUP is transmitting, defer */ + if (sel6v & SEL6_BI_ENABLE) /* Replace now, kill done later */ + d->txstate = TXKILR; + else { + d->txstate = TXKILL; + return; + } + } + } + + if (!(qe = (BDL *)remqueue (d->bdqh.next, &d->bdavail))) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: Too many transmit buffers from host\n", k, d->line); + kmc_halt (k, HALT_XMTOVF); + return; + } + qe->ba = ba; + ASSURE (insqueue (&qe->hdr, d->txqh.prev, &d->txavail, MAXQUEUE)); + if (d->txstate == TXIDLE) { + UNIT *txup = &tx_units[d->line][k]; + if (!sim_is_active (txup)) { + txup->wait = TXSTART_DELAY; + sim_activate_after (txup, txup->wait); + } + } + + return; +} + +/* Transmit complete callback from the DUP + * + * Called when the last byte of a packet has been transmitted. + * + * Handle any deferred kill and schedule the next message. + * + * Note that this may be called from within the txService when the + * DUP is not speed limited. The unconditional activation ensures + * that txService will not be called recursively. + */ + +static void kmc_txComplete (int32 dupidx, int status) { + dupstate *d; + UNIT *txup; + int32 k; + + ASSURE ((dupidx >= 0) && (((size_t)dupidx) < DIM(dupState))); + + d = &dupState[dupidx]; + k = d->kmc; + txup = &tx_units[d->line][k]; + + if (status) { /* Failure is probably due to modem state change */ + kmc_updateDSR(d); /* Change does not stop transmission or release buffer */ + } + + if (d->txstate < TXACT) { /* DUP shouldn't call when KMC is not sending */ + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: tx completion while inactive\n", + k, d->line); + return; + } + + d->txmlen = + d->txslen = 0; + if ((d->txstate == TXKILL) || (d->txstate == TXKILR)) { + /* If DUP could kill a partial transmission, would update bd here */ + d->txstate = TXDONE; + kmc_ctrlOut (k, SEL6_CO_KDONE, 0, d->line, d->tx.bda); + } else { + if (d->tx.bd[2] & BDL_LDS) + d->txstate = TXDONE; + else + d->txstate = TXSOM; + } + sim_cancel (txup); + sim_activate_after (txup, TXDONE_DELAY); + + return; +} + +/* Obtain a new buffer descriptor list from those queued by the host */ + +static t_bool kmc_txNewBdl(dupstate *d) { + BDL *qe; + + if (!(qe = (BDL *)remqueue (d->txqh.next, &d->txavail))) { + return FALSE; + } + d->tx.bda = qe->ba; + ASSURE (insqueue (&qe->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + + d->tx.first = TRUE; + d->tx.bd[1] = 0; + return kmc_txNewBd(d); +} + +/* Obtain a new TX buffer descriptor. + * + * If the current descriptor is last, obtain a new list. + * (The new list case will recurse.) + * + */ + +static t_bool kmc_txNewBd(dupstate *d) { + int32 k = d->kmc; + int i; + t_addr ba; + + if (d->tx.first) + d->tx.first = FALSE; + else { + if (d->tx.bd[2] & BDL_LDS) { + if (!kmc_txNewBdl(d)) { + kmc_ctrlOut (k, SEL6_CO_TXU, 0, d->line, d->tx.bda); + return FALSE; + } + return TRUE; + } + d->tx.bda += 6; + } + ba = d->tx.bda; + for (i = 0; i < 3; i++) { + if (uba_read_npr_word(ba, kmc_dib.uba_ctl, &d->tx.bd[i]) == 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, d->tx.bda); + return FALSE; + } + ba += 2; + } + + d->tx.ba = ((d->tx.bd[2] & BDL_XAD) << BDL_S_XAD) | d->tx.bd[0]; + return TRUE; +} + +/* Append data from a host buffer to the current message, as + * the DUP prefers to get the entire message in one swell foop. + */ + +static t_bool kmc_txAppendBuffer(dupstate *d) { + int32 k = d->kmc; + t_addr ba; + int i; + + if (!d->txmsg || (d->txmsize < d->txmlen+d->tx.bd[1])) { + d->txmsize = d->txmlen+d->tx.bd[1]; + d->txmsg = (uint8 *)realloc(d->txmsg, d->txmsize); + ASSURE (d->txmsg); + } + + ba = d->tx.ba; + for (i = 0; i < d->tx.bd[1]; i++) { + if (uba_read_npr_byte(ba, kmc_dib.uba_ctl, &d->txmsg[d->txmlen+i]) == 0) { + d->tx.bd[1] -= i; + kmc_updateBDCount (d->tx.bda, d->tx.bd); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, ba); + return FALSE; + } + ba++; + } + kmc_updateBDCount (d->tx.bda, d->tx.bd); + d->txmlen += d->tx.bd[1]; + return TRUE; +} + +/* Try to deliver a completion (OUTPUT command) + * + * Because the same CSRs are used for delivering commands to the KMC, + * the RDO and RDI bits, along with RQI arbitrate access to the CSRs. + * + * The KMC prioritizes completions over taking new commands. + * + * Thus, if RDO is set, the host has not taken the previous completion + * data from the CSRs. Output is not possible until the host clears RDO. + * + * If RDO is clear, RDI indicates that the host owns the CSRs, and + * should be writing a command to them. Output is not possible. + * + * If neither is set, the KMC takes ownership of the CSRs and updates + * them from the queue before setting RDO. + * + * There is aditional prioitization of RDI/RDO in the logic that detects + * RDO clearing. If RQI has been set by the host before clearing RDO, + * the KMC guarantees that RDI will set even if more completions are + * pending. + */ + +static void kmc_processCompletions (int32 k) { + CQ *qe; + + if (sel2 & (SEL2_RDO | SEL2_RDI)) /* CSRs available? */ + return; + + if (!(qe = (CQ *)remqueue (cqueueHead.next, &cqueueCount))) { + return; + } + + ASSURE (insqueue (&qe->hdr, freecqHead.prev, &freecqCount, CQUEUE_MAX)); + sel2 = qe->bsel2; + sel4 = qe->bsel4; + sel6 = qe->bsel6; + + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: %s %s delivered: sel2=%06o sel4=%06o sel6=%06o\n", + k, ((sel2 & SEL2_LINE)>>SEL2_V_LINE), + (sel2 & SEL2_IOT)? "RX":"TX", + ((sel2 & SEL2_CMD) == CMD_BUFFOUT)? "BUFFER OUT":"CONTROL OUT", + sel2, sel4, sel6); + + sel2 |= SEL2_RDO; + + kmc_updints (k); + + return; +} + +/* Queue a CONTROL OUT command to the host. + * + * All but one of these release one or more buffers to the host. + * + * code is the event (usually error) + * rx is TRUE for a receive buffer, false for transmit. + * line is the line number assigned by BASE IN + * bda is the address of the buffer descriptor that has been processed. + * + * Returns FALSE if the completion queue is full (a fatal error) + */ + +static void kmc_ctrlOut (int32 k, uint8 code, uint16 rx, uint8 line, uint32 bda) +{ + CQ *qe; + + if (DEBUG_PRS (kmc_dev)) { + static const char *const codenames[] = { + "Undef", "Abort", "HCRC", "DCRC", "NoBfr", "DSR", "NXM", "TXU", "RXO", "KillDun" }; + unsigned int idx = code; + idx = ((code < 06) || (code > 026))? 0: ((code/2)-2); + + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: %s CONTROL OUT Code=%02o (%s) Address=%06o\n", + k, line, rx? "RX":"TX", code, codenames[idx], bda); + } + + if (!(qe = (CQ *)remqueue (freecqHead.next, &freecqCount))) { + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: Completion queue overflow\n", k, line); + /* Set overflow status in last entry of queue */ + qe = (CQ *)cqueueHead.prev; + qe->bsel2 |= SEL2_OVR; + return; + } + qe->bsel2 = ((line << SEL2_V_LINE) & SEL2_LINE) | rx | CMD_CTRLOUT; + qe->bsel4 = bda & 0177777; + qe->bsel6 = ((bda >> (16-SEL6_V_CO_XAD)) & SEL6_CO_XAD) | code; + ASSURE (insqueue (&qe->hdr, cqueueHead.prev, &cqueueCount, CQUEUE_MAX)); + kmc_processCompletions(k); + return; +} + +/* DUP device callback for modem state change. + * The DUP device provides this callback whenever + * any modem control signal changes state. + * + * The timing is not exact with respect to the data + * stream. + + * This can be used for HDX as well as DSR CHANGE> + * + */ +static void kmc_modemChange (int32 dupidx) { + dupstate *d; + + ASSURE ((dupidx >= 0) && (((size_t)dupidx) < DIM(dupState))); + d = &dupState[dupidx]; + + if (d->dupidx != -1) { + kmc_updateDSR (d); + } + return; +} + +/* Check for and report DSR changes to the host. + * DSR is assumed false initially by the host. + * DSR Change Control-Out reports each change. + * No value is provided; the report simply toggles + * the host's view of the state. + * + * This is the ONLY CONTROL OUT that does not release + * a buffer. + * + * Returns TRUE if a change occurred. + */ +static t_bool kmc_updateDSR (dupstate *d) { + int32 k = d->kmc; + int32 status; + + status = dup_get_DSR(d->dupidx); + status = status? LINK_DSR : 0; + if (status ^ (d->linkstate & LINK_DSR)) { + d->linkstate = (d->linkstate &~LINK_DSR) | status; + kmc_ctrlOut (k, SEL6_CO_DSRCHG, 0, d->line, 0); + return TRUE; + } + return FALSE; +} + +/* Queue a BUFFER ADDRESS OUT command to the host. + * + * flags are applied to BSEL6 (e.g. receive EOM). + * rx is TRUE for a receive buffer, false for transmit. + * line is the line number assigned by BASE IN + * bda is the address of the buffer descriptor that has been processed. + * + * Returns FALSE if the completion queue is full (a fatal error) + */ + +static t_bool kmc_bufferAddressOut (int32 k, uint16 flags, uint16 rx, uint8 line, uint32 bda) { + CQ *qe; + + sim_debug (DF_BFO, &kmc_dev, "KMC%u line %u: %s BUFFER OUT Flags=%06o Address=%06o\n", + k, line, rx? "RX": "TX", flags, bda); + + if (!kmc_printBDL(k, DF_BFO, &kmc_dev, line, bda, rx? 6: 2)) + return FALSE; + if (!(qe = (CQ *)remqueue (freecqHead.next, &freecqCount))) { + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: Completion queue overflow\n", k, line); + /* Set overflow status in last entry of queue */ + qe = (CQ *)cqueueHead.prev; + qe->bsel2 |= SEL2_OVR; + return FALSE; + } + qe->bsel2 = ((line << SEL2_V_LINE) & SEL2_LINE) | rx | CMD_BUFFOUT; + qe->bsel4 = bda & 0177777; + qe->bsel6 = ((bda >> (16-SEL6_V_CO_XAD)) & SEL6_CO_XAD) | flags; + ASSURE (insqueue (&qe->hdr, cqueueHead.prev, &cqueueCount, CQUEUE_MAX)); + + kmc_processCompletions(k); + return TRUE; +} + +/* The UBA does not do a RPW cycle when byte 0 (of 4) + * on a -10 is written. (It can, but the OS doesn't program it that + * way. Thus, if the count word is in the left half-word, updating + * it will trash the 3rd word of that buffer descriptor. The hardware + * works. The KMC microcode does a word NPR write. At this writing + * how the hardware works is a mystery. The UBA documentation is quite + * clear that a write is done; the prints show zeros muxed into the bits. + * The OS is NOT setting 'read reverse'. The KMC ucode is not doing + * any magic. And the OS will kill the KMC if the 3rd word of the BD + * is trashed. Unless there is an undiscovered ECO to the KS10, there + * is no explanation for the fact that the KDP does work on that machine. + * Certainly, if the UBA always did RPW cycles, this would work, and + * probably no one would notice the performance loss. + * + * For now, this code writes the count, and also the 3rd word if the + * count is in byte 0. It's not the right solution, but it works for now. + * This does an extra write if the UBA trashed the following word, and not + * otherwise. Note that any BDL with two buffers is guaranteed to + * run into this issue. If the first BD is in byte 0, its count is OK, but + * the following BD will start in byte 2, putting its count in byte 0 of + * the following word, causing the write to step on that bd's flags. + */ + +static int32 kmc_updateBDCount(uint32 bda, uint16 *bd) { + bda+=2; + if (uba_write_npr_word(bda, kmc_dib.uba_ctl, bd[1]) == 0) { + return 1; + } + if ((bda & 2) != 0) { + if (uba_write_npr_word(bda + 2, kmc_dib.uba_ctl, bd[2]) == 0) { + return 1; + } + } + return 0; +} + +/* Halt a KMC. This happens for some errors that the real KMC + * may not detect, as well as when RUN is cleared. + * The kmc is halted & interrupts are disabled. + */ + +static void kmc_halt (int32 k, int error) { + int line; + + if (error){ + sel0 &= ~(SEL0_IEO|SEL0_IEI); + } + sel0 &= ~SEL0_RUN; + + kmc_updints (k); + for (line = 0; line < MAX_ACTIVE; line++) { + UNIT *up = &tx_units[line][k]; + + if (sim_is_active (up)) { + up->unit_htime = sim_activate_time (up); + sim_cancel (up); + } else { + up->unit_htime = 0; + } + up = &rx_units[line][k]; + if (sim_is_active (up)) { + up->unit_htime = sim_activate_time (up); + sim_cancel (up); + } else { + up->unit_htime = 0; + } + } + sim_debug (DF_INF, &kmc_dev, "KMC%u: Halted at uPC %04o reason=%d\n", k, upc, error); + return; +} +/* + * Update interrupts pending. + * + * Since the interrupt request is shared across all KMCs + * (a simplification), pending flags are kept per KMC, + * and a global request count across KMCs for the UBA. + * The UBA will clear the global request flag when it grants + * an interrupt; thus for set the global flag is always set + * if this KMC has a request. This doesn't quite match "with IEI + * set, only one interrupt is generated for each setting of RDI." + * An extra interrupt, however, should be harmless. + * + * Since interrupts are generated by microcode, do not touch the interrupt + * system unless microcode initialization has run. + */ + +static void kmc_updints(int32 k) { + uint16 vect = kmc_dib.uba_vect; + if (!(gflags & FLG_UCINI)) { + return; + } + + if ((sel0 & SEL0_IEI) && (sel2 & SEL2_RDI)) { + sim_debug (DF_INT, &kmc_dev, "KMC%u: set input interrupt pending\n", k); + uba_set_irq(&kmc_dib, vect + (k*10)); + } else { + uba_clr_irq(&kmc_dib, vect + (k*10)); + sim_debug (DF_INT, &kmc_dev, "KMC%u: cleared pending input interrupt\n", k); + } + + if ((sel0 & SEL0_IEO) && (sel2 & SEL2_RDO)) { + uba_set_irq(&kmc_dib, vect + 4 + (k*10)); + sim_debug (DF_INT, &kmc_dev, "KMC%u: set output interrupt\n", k); + } else { + uba_clr_irq(&kmc_dib, vect + 4 + (k*10)); + sim_debug (DF_INT, &kmc_dev, "KKMC%u: clear output interrupt\n", k); + } + + return; +} + +/* Debug: Log a BUFFER IN or BUFFER OUT command. + * returns FALSE if print encounters a NXM as (a) it's fatal and + * (b) only one completion per bdl. + */ + +static t_bool kmc_printBufferIn (int32 k, DEVICE *dev, uint8 line, t_bool rx, + int32 count, int32 ba, uint16 sel6v) { + t_bool kill = ((sel6v & (SEL6_BI_KILL|SEL6_BI_ENABLE)) == SEL6_BI_KILL); + const char *dir = rx? "RX": "TX"; + + sim_debug (DF_CMD, &kmc_dev, "KMC%u line %u: %s BUFFER IN%s %d, bdl=%06o 0x%04x\n", k, line, dir, + (kill? "(Buffer kill)": ((sel6v & SEL6_BI_KILL)? "(Kill & replace)": "")), + count, ba, ba); + + if (kill) /* Just kill doesn't use BDL, may NXM if attempt to dump */ + return TRUE; + + /* Kill and replace supplies new BDL */ + + if (!kmc_printBDL(k, DF_CMD, dev, line, ba, rx? 5: 1)) + return FALSE; + + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: %s BUFFER IN %d, bdl=%06o 0x%04X\n", k, line, dir, count, ba, ba); + + return TRUE; +} +/* + * Debug: Dump a BDL and a sample of its buffer. + * + * prbuf: non-zero to access buffer + * Bit 1 set to print just one descriptor (BFO), clear to do entire list (BFI) + * Bit 2 set if rx (rx bfi doesn't print data) + */ + +static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32 ba, int prbuf) { + uint16 bd[3]; + t_addr dp; + uint16 i; + + if (!DEBUG_PRJ(dev,dbits)) + return TRUE; + + for (;;) { + for (i = 0; i < 3; i++) { + if (uba_read_npr_word (ba, kmc_dib.uba_ctl, &bd[i]) == 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, ba); + sim_debug (dbits,dev, "KMC%u line %u: NXM reading descriptor addr=%06o\n", k, line, ba); + return FALSE; + } + ba += 2; + } + + dp = bd[0] | ((bd[2] & BDL_XAD) << BDL_S_XAD); + sim_debug (dbits, dev, " bd[0] = %06o 0x%04X Adr=%06o\n", bd[0], bd[0], dp); + sim_debug (dbits, dev, " bd[1] = %06o 0x%04X Len=%u.\n", bd[1], bd[1], bd[1]); + sim_debug (dbits, dev, " bd[2] = %06o 0x%04X", bd[2], bd[2]); + if (bd[2] & BDL_LDS) { + sim_debug (dbits, dev, " Last"); + } + if (bd[2] & BDL_RSY) { + sim_debug (dbits, dev, " Rsync"); + } + if (bd[2] & BDL_EOM) { + sim_debug (dbits, dev, " XEOM"); + } + if (bd[2] & BDL_SOM) { + sim_debug (dbits, dev, " XSOM"); + } + sim_debug (dbits, dev, "\n"); + + if (prbuf) { + uint8 buf[20]; + if (bd[1] > sizeof buf) + bd[1] = sizeof buf; + + for (i = 0; i < bd[1]; i++) { + if (uba_read_npr_byte(dp + i, kmc_dib.uba_ctl, + &buf[i]) == 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, dp); + sim_debug (dbits, dev, "KMC%u line %u: NXM reading buffer %06o\n", k, line, dp); + return FALSE; + } + } + + if (prbuf != 5) { /* Don't print RX buffer in */ + for (i = 0; i < bd[1] ; i++) { + sim_debug (dbits, dev, " %02x", buf[i]); + } + sim_debug (dbits, dev, "\r\n"); + } + } + if ((bd[2] & BDL_LDS) || (prbuf & 2)) /* Last, or 1 only */ + break; + } + return TRUE; +} + +/* Verify that the microcode image is one that this code knows how to emulate. + * As far as I know, there was COMM IOP-DUP V1.0 and one patch, V1.0A. + * This is the patched version, which I have verified by rebuilding the + * V1.0A microcode from sources and computing its CRC from the binary. + * + * RSX DECnet has a different binary; the version is unknown. + * + * The reason for this check is that there ARE other KMC microcodes. + * If some software thinks it's loading one of those, the results + * could be catastrophic - and hard to diagnose. (COMM IOP-DZ has + * a similar function; but there are other, stranger microcodes. + */ + +static const char *kmc_verifyUcode (int32 k) { + size_t i, n; + uint16 crc = 'T' << 8 | 'L'; + uint8 w[2]; + static const struct { + uint16 crc; + const char *name; + } known[] = { + { 0xc3cd, "COMM IOP-DUP V1.0A" }, + { 0x1a38, "COMM IOP-DUP RSX" }, + }; + + for (i = 0, n = 0; i < DIM (ucode); i++) { + if (ucode[i] != 0) + n++; + w[0] = ucode[i] >> 8; + w[1] = ucode[i] & 0xFF; + crc = ddcmp_crc16 (crc, &w, sizeof w); + } + if (n < ((3 * DIM (ucode))/4)) { + sim_debug (DF_INF, &kmc_dev, "KMC%u: Microcode not loaded\n", k); + return NULL; + } + for (i = 0; i < DIM (known); i++) { + if (crc == known[i].crc) { + sim_debug (DF_INF, &kmc_dev, "KMC%u: %s microcode loaded\n", k, known[i].name); + return known[i].name; + } + } + sim_debug (DF_INF, &kmc_dev, "KMC%u: Unknown microcode loaded\n", k); + return NULL; +} + +/* Initialize a queue to empty. + * + * Optionally, adds a list of elements to the queue. + * max, list and size are only used if list is non-NULL. + * + * Convenience macros: + * MAX_LIST_SIZE(q) specifies max, list and size for an array of elements q + * INIT_HDR_ONLY provides placeholders for these arguments when only the + * header and count are to be initialized. + */ + +static void initqueue (QH *head, int32 *count, int32 max, void *list, size_t size) { + head->next = head->prev = head; + *count = 0; + if (list == NULL) + return; + while (insqueue ((QH *)list, head->prev, count, max)) + list = (QH *)(((char *)list)+size); + return; +} + +/* Insert entry on queue after pred, if count < max. + * Increment count. + * To insert at head of queue, specify &head for predecessor. + * To insert at tail, specify head.pred + * + * returns FALSE if queue is full. + */ + +static t_bool insqueue (QH *entry, QH *pred, int32 *count, int32 max) { + if (*count >= max) + return FALSE; + entry-> next = pred->next; + entry->prev = pred; + pred->next->prev = entry; + pred->next = entry; + ++*count; + return TRUE; +} + +/* Remove entry from queue. + * Decrement count. + * To remove from head of queue, specify head.next. + * To remove form tail of queue, specify head.pred. + * + * returns FALSE if queue is empty. + */ + +static void *remqueue (QH *entry, int32 *count) { + if (*count <= 0) + return NULL; + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + --*count; + return (void *)entry; +} + +/* Simulator UI functions */ + +/* SET KMC DEVICES processor + * + * Adjusts the size of I/O space and number of vectors to match the specified + * number of KMCs. + * The txup is that of unit zero. + */ + +#if KMC_UNITS > 1 +static t_stat kmc_setDeviceCount (UNIT *txup, int32 val, CONST char *cptr, void *desc) { + int32 newln; + uint32 dupidx; + t_stat r; + DEVICE *dptr = find_dev_from_unit(txup); + + if (cptr == NULL) + return SCPE_ARG; + + for (dupidx = 0; dupidx < DIM (dupState); dupidx++) { + dupstate *d = &dupState[dupidx]; + if ((d->kmc != -1) || (d->dupidx != -1)) { + return SCPE_ALATT; + } + } + newln = (int32) get_uint (cptr, 10, KMC_UNITS, &r); + if ((r != SCPE_OK) || (newln == (int32)dptr->numunits)) + return r; + if (newln == 0) + return SCPE_ARG; + kmc_dib.lnt = newln * IOLN_KMC; /* set length */ + kmc_dib.vnum = newln * 2; /* set vectors */ + dptr->numunits = newln; + return kmc_reset (dptr); /* setup devices and auto config */ +} +#endif + +/* Report number of configured KMCs */ + +#if KMC_UNITS > 1 +static t_stat kmc_showDeviceCount (FILE *st, UNIT *txup, int32 val, CONST void *desc) { + DEVICE *dev = find_dev_from_unit(txup); + + if (dev->flags & DEV_DIS) { + fprintf (st, "Disabled"); + return SCPE_OK; + } + + fprintf (st, "devices=%d", dev->numunits); + return SCPE_OK; +} +#endif + +/* Set line speed + * + * This is the speed at which the KMC processed data for/from a DUP. + * This limits the rate at which buffer descriptors are processed, even + * if the actual DUP speed is faster. If the DUP speed is slower, of + * course the DUP wins. This limit ensures that the infinite speed DUPs + * of simulation won't overrun the host's ability to process buffers. + * + * The speed range is expressed as line bits/sec for user convenience. + * The limits are 300 bps (a sync line won't realistically run slower than + * that), and 1,000,000 bps - the maximum speed of a DMR11. The KDP's + * practical limit was about 19,200 BPS. The higher limit is a rate that + * the typicaly host software could handle, even if the line couldn't. + * + * Note that the DUP line speed can also be set. + * + * To allow setting the speed before a DUP has been assigned to a KMC by + * the OS, the set (and show) commands reference the DUP and apply to any + * potential use of that DUP by a KMC. + */ + +static t_stat kmc_setLineSpeed (UNIT *txup, int32 val, CONST char *cptr, void *desc) { + dupstate *d; + int32 dupidx, newspeed; + char gbuf[CBUFSIZE]; + t_stat r; + + if (!cptr || !*cptr) + return SCPE_ARG; + + cptr = get_glyph (cptr, gbuf, '='); /* get next glyph */ + if (*cptr == 0) /* should be speed */ + return SCPE_2FARG; + dupidx = (int32) get_uint (gbuf, 10, DUP_LINES, &r); /* Parse dup # */ + if ((r != SCPE_OK) || (dupidx < 0)) /* error? */ + return SCPE_ARG; + + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* should be end */ + return SCPE_2MARG; + cptr = gbuf; + if (!strcmp (cptr, "DUP")) + cptr += 3; + newspeed = (int32) get_uint (cptr, 10, MAX_SPEED, &r); + if ((r != SCPE_OK) || (newspeed < 300)) /* error? */ + return SCPE_ARG; + + d = &dupState[dupidx]; + d->linespeed = newspeed; + return SCPE_OK; +} + +static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, CONST void *desc) { + int dupidx; + + fprintf (st, "DUP KMC Line Speed\n" + "--- --- ---- --------\n"); + + for (dupidx =0; dupidx < DUP_LINES; dupidx++) { + dupstate *d = &dupState[dupidx]; + + fprintf (st, "%3u ", dupidx); + + if (d->kmc == -1) { + fprintf (st, " - - "); + } else { + fprintf (st, "%3u %3u", d->kmc, d->line); + } + fprintf (st, " %8u\n", d->linespeed); + } + + return SCPE_OK; +} + +/* Show KMC status */ + +t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, CONST void *dp) { + int32 k = up->unit_kmc; + int32 line; + t_bool first = TRUE; + DEVICE *dev = find_dev_from_unit(up); + const char *ucname; + + if ((dev->flags & DEV_DIS) || (((uint32)k) >= dev->numunits)) { + fprintf (st, "KMC%u Disabled\n", k); + return SCPE_OK; + } + + ucname = kmc_verifyUcode (k); + + fprintf (st, "KMC%u ", k); + + if (!(sel0 & SEL0_RUN)) { + fprintf (st, "%s halted at uPC %04o\n", + (ucname?ucname: "(No or unknown microcode)"), upc); + return SCPE_OK; + } + + fprintf (st, "%s is running at uPC %04o\n", + (ucname?ucname: "(No or unknown microcode)"), upc); + + if (!(gflags & FLG_UCINI)) { + return SCPE_OK; + } + + for (line = 0; line <= MAX_LINE; line++) { + dupstate *d = line2dup[line]; + if (d->kmc == k) { + if (first) { + fprintf (st, " Line DUP CSR State\n"); + first = FALSE; + } + fprintf (st, " %3u %3u %06o %-8s %3s %s %s %s", + line, d->dupidx, d->dupcsr, + (d->ctrlFlags & SEL6_CI_ENABLE)? "enabled": "disabled", + (d->linkstate & LINK_DSR)? "DSR" : "OFF", + (d->ctrlFlags & SEL6_CI_DDCMP)? "DDCMP" : "Bit-Stuff", + (d->ctrlFlags & SEL6_CI_HDX)? "HDX " : "FDX", + (d->ctrlFlags & SEL6_CI_NOCRC)? "NOCRC": ""); + if (d->ctrlFlags & SEL6_CI_ENASS) + fprintf (st, " SS (%u) ", d->ctrlFlags & SEL6_CI_SADDR); + fprintf (st, "\n"); + } + } + if (first) + fprintf (st, " No DUPs assigned\n"); + + return SCPE_OK; +} + +/* Help for this device. + * + */ + +static t_stat kmc_help (FILE *st, DEVICE *dptr, + UNIT *uptr, int32 flag, const char *cptr) { + const char *const text = +" The KMC11-A is a general purpose microprocessor that is used in\n" +" several DEC products. The KDP is an emulation of one of those\n" +" products: COMM IOP-DUP.\n" +"\n" +" The COMM IOP-DUP microcode controls and supervises 1 - 16 DUP-11\n" +" synchronous communications line interfaces, providing scatter/gather\n" +" DMA, message framing, modem control, CRC validation, receiver\n" +" resynchronization, and address recognition.\n" +"\n" +" The DUP-11 lines are assigned to the KMC11 by the (emulated) operating\n" +" system, but SimH must be told how to connect them. See the DUP HELP\n" +" for details.\n" +"1 Hardware Description\n" +" The KMC11-A microprocessor is a 16-bit Harvard architecture machine\n" +" optimized for data movement, character processing, address arithmetic\n" +" and other functions necessary for controlling I/O devices. It resides\n" +" on the UNIBUS and operates in parallel with the host CPU with a cycle\n" +" time of 300 nsec. It contains a 1024 word writable control store that\n" +" is loaded by the host, 1024 words of data memory, 16 8-bit scratchpad\n" +" registers, and 8 bytes of RAM that are dual-ported between the KMC11\n" +" and UNIBUS I/O space. It also has a timer and various internal busses\n" +" and registers.\n" +"\n" +" Seven of the eight bytes of dual-ported RAM have no fixed function;\n" +" they are defined by the microcode. The eighth register allows the\n" +" host to control the KMC11: the host can start, stop, examine state and\n" +" load microcode using this register.\n" +"\n" +" The microprocessor is capable of initiating DMA (NPR) UNIBUS cycles to\n" +" any UNIBUS address (memory and I/O space). It can interrupt the host\n" +" via one of two interrupt vectors.\n" +"\n" +" The microcodes operate other UNIBUS devices by reading and writing\n" +" their CSRs with UNIBUS DMA transactions, typically on a\n" +" character-by-character basis. There is no direct connection between\n" +" the KMC11 and the peripherals that it controls. The controlled\n" +" devices do not generate interrupts; all interrupts are generated by\n" +" the KMC11, which monitors the devices by polling their CSRs.\n" +"\n" +" By presenting the character-oriented peripherals to the host as\n" +" message-oriented devices, the KMC11 reduces the host's overhead in\n" +" operating the peripherals, relaxes the required interrupt response\n" +" times and increases the potential I/O throughput of a system.\n" +"\n" +" The hardware also has a private bus that can be used to control\n" +" dedicated peripherals (such as a DMC11 synchronous line unit) without\n" +" UNIBUS transactions. This feature is not emulated.\n" +"\n" +" This emulation does not execute the KMC microcode, but rather provides\n" +" a functional emulation.\n" +"\n" +" However, some of the microcode operators are emulated because system\n" +" loaders and OS diagnostics execute single instructions to initialize\n" +" or diagnose the device.\n" +"2 $Registers\n" +"2 Related devices\n" +" Other versions of the KMC11 have ROM microcode, which are used in such\n" +" devices as the DMC11 and DMR11 communications devices. This emulation\n" +" does not support those versions.\n" +"\n" +" Microcodes, not supported by this emulation, exist which control other\n" +" UNIBUS peripherals in a similar manner. These include:\n" +"\n" +"+DMA for DZ11 asynchronous lines (COMM IOP-DZ)\n" +"+DMA for line printers\n" +"+Arpanet IMP interface (AN22 on the KS10/TOPS-20)\n" +"\n" +" The KMC11 was also embedded in other products, such as the DX20 Massbus\n" +" to IBM channel adapter.\n" +"\n" +" The KMC11-B is an enhanced version of the KMC11-A. Note that microcode\n" +" loading is handled differently in that version, which is NOT emulated.\n" +"1 Configuration\n" +" Most configuration of KDP lines is done by the host OS and by SimH\n" +" configuration of the DUP11 lines.\n" +"\n" +#if KMC_TROLL +" The KDP has two configurable parameters.\n" +#else +" The KDP has one configurable parameter.\n" +#endif +" Line speed - this is the speed at which each communication line\n" +" operates. The DUP11's line speed should be set to 'unlimited' to\n" +" avoid unpredictable interactions.\n" +#if KMC_TROLL +" Troll - the KDP emulation includes a process that will intentionally\n" +" drop or corrupt some messages. This emulates the less-than-perfect\n" +" communications lines encountered in the real world, and enables\n" +" network monitoring software to see non-zero error counters.\n" +"\n" +" The troll selects messages with a probablility selected by the SET\n" +" TROLL command. The units are 0.1%%; that is, a value of 1 means that\n" +" every message has a 1/1000 chance of being selected.\n" +#endif +"2 $Set commands\n" +#if KMC_UNITS > 1 +" SET KDP DEVICES=n enables emulation of up to %1s KMC11s.\n" +#endif +"2 $Show commands\n" +"1 Operation\n" +" A KDP device consists of one or more DUP11s controlled by a KMC11.\n" +" The association of DUP11s to KMC11s is determined by the host OS.\n" +"\n" +" For RSX DECnet, use NCP:\n" +" +SET LINE KDP-kdp-line CSR address\n" +" +SET LINE KDP-kdp-line UNIT CSR address\n" +" where 'kdp' is the KDP number and 'line' is the line number on\n" +" that kdp. 'address' is the I/O page offset of the CSR; e.g.\n" +" 760050 is entered as 160050.\n" +"\n" +" For TOPS-10/20, the addresses are fixed.\n" +"\n" +" For VMS...\n" +"\n" +" Although the microcode is not directly executed by the emulated KMC11,\n" +" the correct microcode must be loaded by the host operating system.\n" +; + char kmc_units[10]; + + sprintf (kmc_units, "%u", KMC_UNITS); + + return scp_help (st, dptr, uptr, flag, text, cptr, kmc_units); +} + +/* Description of this device. + * Conventionally last function in the file. + */ +static const char *kmc_description (DEVICE *dptr) { + if (dptr == &kmc_dev) + return "KMC11-A Synchronous line controller supporting only COMM IOP/DUP microcode"; + else + return "KMC pseudo device for receive units"; +} +#endif diff --git a/PDP10/ks10_lp.c b/PDP10/ks10_lp.c new file mode 100644 index 00000000..1ecbe289 --- /dev/null +++ b/PDP10/ks10_lp.c @@ -0,0 +1,896 @@ +/* ks10_lp.c: PDP-10 LP20 printer. + + Copyright (c) 2021, Richard Cornwell + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell. + +*/ + +#include "kx10_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#ifndef NUM_DEVS_LP20 +#define NUM_DEVS_LP20 0 +#endif + +#if (NUM_DEVS_LP20 > 0) + +#define UNIT_V_CT (UNIT_V_UF + 0) +#define UNIT_UC (1 << UNIT_V_CT) +#define UNIT_CT (1 << UNIT_V_CT) +#define UNIT_V_VFU (UNIT_V_CT + 1) +#define UNIT_OPT (1 << UNIT_V_VFU) + + +#define LINE u6 + +/* LPCSRA (765400) */ +#define CS1_GO 0000001 /* Go command */ +#define CS1_PAR 0000002 /* Enable Parity interrupt */ +#define CS1_V_FNC 2 /* Function shift */ +#define CS1_M_FNC 03 /* Function mask */ +#define FNC_PRINT 0 /* Print */ +#define FNC_TEST 1 /* Test */ +#define FNC_DVU 2 /* Load DAVFU */ +#define FNC_RAM 3 /* Load translation RAM */ +#define CS1_UBA 0000060 /* Upper Unibus address */ +#define CS1_IE 0000100 /* Interrupt enable */ +#define CS1_DONE 0000200 /* Done flag */ +#define CS1_INIT 0000400 /* Init */ +#define CS1_ECLR 0001000 /* Clear errors */ +#define CS1_DHOLD 0002000 /* Delimiter hold */ +#define CS1_ONL 0004000 /* Online */ +#define CS1_DVON 0010000 /* DAVFU online */ +#define CS1_UND 0020000 /* Undefined Character */ +#define CS1_PZERO 0040000 /* Page counter zero */ +#define CS1_ERR 0100000 /* Errors */ +#define CS1_MOD (CS1_DHOLD|CS1_IE|(CS1_M_FNC << CS1_V_FNC)|CS1_PAR|CS1_GO) + +/* LPCSRB (765402) */ +#define CS2_GOE 0000001 /* Go error */ +#define CS2_DTE 0000002 /* DEM timing error */ +#define CS2_MTE 0000004 /* MSYN error */ +#define CS2_RPE 0000010 /* RAM parity error */ +#define CS2_MPE 0000020 /* Memory parity error */ +#define CS2_LPE 0000040 /* LPT parity error */ +#define CS2_DVOF 0000100 /* DAVFU not ready */ +#define CS2_OFFL 0000200 /* Offline */ +#define CS2_TEST 0003400 /* Test mode */ +#define CS2_OVFU 0004000 /* Optical VFU */ +#define CS2_PBIT 0010000 /* data parity bit */ +#define CS2_NRDY 0020000 /* Printer error */ +#define CS2_LA180 0040000 /* LA180 printer */ +#define CS2_VLD 0100000 /* Valid data */ +#define CS2_ECLR (CS2_GOE|CS2_DTE|CS2_MTE|CS2_RPE|CS2_LPE) +#define CS2_ERR (CS2_ECLR|CS2_OFFL|CS2_DVOF) + +/* LPBA (765404) */ +/* Unibus address */ + +/* LPBC (765406) */ +/* byte count */ + +/* LPPAGC (765410) */ +/* Page count */ + +/* LPRDAT (765412) */ +/* RAM Data register */ + +/* LPCOLC/LPCBUF (765414) */ +/* Column counter / Character buffer */ + +/* LPCSUM/LPPDAT (765416) */ +/* Checksum / Printer data */ + + +#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 + + + +int lp20_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +int lp20_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +void lp20_printline(UNIT *uptr, int nl); +void lp20_output(UNIT *uptr, uint8 c); +void lp20_update_chkirq (UNIT *uptr, int done, int irq); +void lp20_update_ready(UNIT *uptr, uint16 setrdy, uint16 clrrdy); +t_stat lp20_svc (UNIT *uptr); +t_stat lp20_init (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]; +uint16 lp20_cs1; +uint16 lp20_cs2; +uint16 lp20_pagcnt; +uint32 lp20_ba; +uint16 lp20_wcnt; +uint8 lp20_col; +uint8 lp20_chksum; +uint8 lp20_buf; +uint8 lp20_data; +int lp20_odd = 0; +int lp20_duvfa_state = 0; +int lp20_index = 0; + +#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 */ +}; + + +/* LPT data structures + + lp20_dev LPT device descriptor + lp20_unit LPT unit descriptor + lp20_reg LPT register list +*/ + +DIB lp20_dib = { 0775400, 017, 0754, 5, 3, &lp20_read, &lp20_write, NULL, 0, 0 }; + + +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}, + {ORDATA(CS1, lp20_cs1, 16)}, + {ORDATA(CS2, lp20_cs2, 16)}, + {ORDATA(PAGCNT, lp20_pagcnt, 12)}, + {ORDATA(BA, lp20_ba, 18)}, + {ORDATA(BC, lp20_wcnt, 16)}, + {ORDATA(COL, lp20_col, 8)}, + {ORDATA(CHKSUM, lp20_chksum, 8)}, + {ORDATA(BUF, lp20_buf, 8)}, + { NULL } +}; + +MTAB lp20_mod[] = { + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of LP20" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of LP20" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of LP20" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of LP20" }, + {UNIT_CT, 0, "Lower case", "LC", NULL}, + {UNIT_CT, UNIT_UC, "Upper case", "UC", NULL}, + {UNIT_OPT, 0, "Normal VFU", "NORMAL", NULL}, + {UNIT_OPT, UNIT_OPT, "Optical VFU", "OPTICAL", 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, + &lp20_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &lp20_help, NULL, NULL, &lp20_description +}; + +int +lp20_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + uint16 temp; + + addr &= dibp->uba_mask; + sim_debug(DEBUG_DETAIL, dptr, "LP20 write %06o %06o %o\n", + addr, data, access); + + switch (addr & 016) { + case 000: /* LPCSA */ + if (access == BYTE) { + if (addr & 1) + data = data | (lp20_cs1 & 0377); + else + data = (lp20_cs1 & 0177400) | data; + } + lp20_ba = ((data & CS1_UBA) << 9) | (lp20_ba & 0177777); + if (data & CS1_INIT) { + /* Reset controller */ + lp20_init (&lp20_unit); + } + if (data & CS1_ECLR) { + /* Clear errors */ + lp20_cs2 &= ~(CS2_ECLR); + lp20_cs1 &= ~(CS1_GO); + lp20_cs1 |= CS1_DONE; + } + if (data & CS1_GO) { + if ((lp20_cs1 & CS1_GO) == 0) { + lp20_chksum = 0; + lp20_odd = 0; + lp20_duvfa_state = 0; + lp20_index = 0; + sim_activate(&lp20_unit, 100); + lp20_cs1 |= CS1_GO; + } + } else { + lp20_cs1 &= ~CS1_GO; + sim_cancel(&lp20_unit); + } + lp20_cs1 &= ~(CS1_MOD); + lp20_cs1 |= data & CS1_MOD; + if (lp20_duvfa_state && ((lp20_cs1 >> CS1_V_FNC) & CS1_M_FNC) != FNC_DVU) { + lp20_update_ready(&lp20_unit, 0, CS1_DVON); + lp20_duvfa_state = 0; + } + break; + + case 002: /* LPCSB */ + if (access == BYTE) { + if (addr & 1) { + lp20_cs2 &= ~(CS2_TEST); + lp20_cs2 |= data & CS2_TEST; + } else { + lp20_cs2 &= ~(CS2_GOE); + lp20_cs2 |= data & CS2_GOE; + } + } else { + lp20_cs2 &= ~(CS2_TEST|CS2_GOE); + lp20_cs2 |= data & (CS2_TEST|CS2_GOE); + } + break; + + case 004: /* LPBA */ + lp20_ba = (lp20_ba & 060000) | (data & 0177777); + break; + + case 006: /* LPBC */ + lp20_wcnt = (data & 0177777); + lp20_cs1 &= ~CS1_DONE; + break; + + case 010: /* LPPAGC */ + if (access == BYTE) { + if (addr & 1) + data = data | (lp20_pagcnt & 0377); + else + data = (lp20_pagcnt & 0177400) | data; + } + lp20_pagcnt = (data & 0177777); + lp20_cs1 &= ~CS1_PZERO; + break; + + case 012: /* LPRDAT */ + break; + + case 014: /* LPCOL/LPCBUF */ + if (access == BYTE) { + if (addr & 1) + lp20_col = (data >> 8) & 0377; + else + lp20_buf = data & 0377; + } else { + lp20_buf = data & 0377; + lp20_col = (data >> 8) & 0377; + } + break; + + case 016: /* LPCSUM/LPPDAT */ + if (access == BYTE) { + temp = lp20_ram[(int)lp20_buf]; + if (addr & 1) + data = data | (temp & 0377); + else + data = (temp & 0177400) | data; + } + lp20_ram[(int)lp20_buf] = data & 07777; + break; + } + lp20_update_chkirq(&lp20_unit, 0, 0); + return 0; +} + +int +lp20_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + uint16 temp; + int par; + + addr &= dibp->uba_mask; + switch (addr & 016) { + case 000: /* LPCSA */ + *data = lp20_cs1; + *data &= ~CS1_UBA; + *data |= (lp20_ba >> 9) & CS1_UBA; + if ((lp20_cs2 & CS2_ERR) != 0) + *data |= CS1_ERR; + break; + + case 002: /* LPCSB */ + *data = lp20_cs2; + break; + + case 004: /* LPBA */ + *data = lp20_ba & 0177777; + break; + + case 006: /* LPBC */ + *data = lp20_wcnt; + break; + + case 010: /* LPPAGC */ + *data = lp20_pagcnt; + break; + + case 012: /* LPRDAT */ + temp = lp20_ram[(int)lp20_buf] & 07777; + par = (temp >> 8) ^ (temp >> 4) ^ temp; + par = (par >> 2) ^ par; + par ^= par >> 1; + if ((par & 1) != 0) + temp |= LP20_RAM_RAP; + break; + + case 014: /* LPCOL/LPCBUF */ + *data = ((uint16)lp20_col) << 8; + *data |= (uint16)lp20_buf; + break; + + case 016: /* LPCSUM/LPPDAT */ + *data = ((uint16)lp20_chksum) << 8; + *data |= (uint16)lp20_data; + break; + } + sim_debug(DEBUG_DETAIL, dptr, "LP20 read %06o %06o %o\n", + addr, *data, access); + return 0; +} + +void +lp20_printline(UNIT *uptr, int nl) { + int trim = 0; + + /* Trim off trailing blanks */ + while (lp20_col != 0 && lp20_buffer[lp20_col - 1] == ' ') { + lp20_col--; + trim = 1; + } + lp20_buffer[lp20_col] = '\0'; + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP output %d %d [%s]\n", lp20_col, nl, + lp20_buffer); + /* Stick a carraige return and linefeed as needed */ + if (lp20_col != 0 || trim) + lp20_buffer[lp20_col++] = '\r'; + if (nl != 0) { + lp20_buffer[lp20_col++] = '\n'; + uptr->LINE++; + } + if (nl > 0 && lp20_vfu[uptr->LINE] == 010000) { + lp20_buffer[lp20_col++] = '\f'; + uptr->LINE = 0; + lp20_pagcnt = (lp20_pagcnt - 1) & 07777; + if (lp20_pagcnt == 0) + lp20_cs1 |= CS1_PZERO; + } else if (nl < 0 && uptr->LINE >= (int32)uptr->capac) { + uptr->LINE = 0; + lp20_pagcnt = (lp20_pagcnt - 1) & 07777; + if (lp20_pagcnt == 0) + lp20_cs1 |= CS1_PZERO; + } + + sim_fwrite(&lp20_buffer, 1, lp20_col, uptr->fileref); + uptr->pos += lp20_col; + lp20_col = 0; + return; +} + + +/* Unit service */ +void +lp20_output(UNIT *uptr, uint8 c) { + + if (c == 0) + return; + lp20_data = c & 0377; + if (lp20_col == 132) + lp20_printline(uptr, 1); + /* Map lower to upper case if uppercase only */ + if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140) + c &= 0137; + if (c >= 040 && c < 0177) { /* If printable */ + lp20_buffer[lp20_col++] = c; + return; + } + if (c == 012) { /* Line feed? */ + lp20_printline(uptr, 1); + } else if (c == 014) { /* Form feed, advance to top of form */ + if (lp20_col != 0) + lp20_printline(uptr, 1); + sim_fwrite("\f", 1, 1, uptr->fileref); + uptr->pos += 1; + lp20_col = 0; + uptr->LINE = 0; + } else if (c == 015) { /* Carrage return */ + lp20_col = 0; + } + if (c == 011) { /* Tab */ + lp20_buffer[lp20_col++] = ' '; + while ((lp20_col & 07) != 0) + lp20_buffer[lp20_col++] = ' '; + } + return; +} + +/* + * Check if interrupt should be sent. + * Done set CS1_DONE. + * Irq set interrupt + */ +void +lp20_update_chkirq (UNIT *uptr, int done, int irq) +{ + DEVICE *dptr = find_dev_from_unit (uptr); + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + if (done) + lp20_cs1 |= CS1_DONE; + + if (uptr->flags & UNIT_ATT) { + lp20_cs1 |= CS1_ONL; + lp20_cs2 &= ~(CS2_OFFL|CS2_NRDY); + } else { + lp20_cs1 &= ~(CS1_ONL|CS1_DONE); + lp20_cs2 |= CS2_NRDY|CS2_OFFL; + } + if ((lp20_cs1 & CS1_IE) && (irq || (lp20_cs1 & CS1_DONE))) + uba_set_irq(dibp, dibp->uba_vect); + else + uba_clr_irq(dibp, dibp->uba_vect); +} + +/* + * Update ready status of printer. + */ + +void +lp20_update_ready(UNIT *uptr, uint16 setrdy, uint16 clrrdy) +{ + DEVICE *dptr = find_dev_from_unit (uptr); + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + uint16 new_cs1 = (lp20_cs1 | setrdy) & ~clrrdy; + + if ((new_cs1 ^ lp20_cs1) & (CS1_ONL|CS1_DVON) && !sim_is_active(uptr)) { + if (new_cs1 & CS1_IE) + uba_set_irq(dibp, dibp->uba_vect); + } + if (new_cs1 & CS1_DVON) + lp20_cs2 &= ~CS2_DVOF; + if (new_cs1 & CS1_ONL) + lp20_cs2 &= ~CS2_OFFL; + else + lp20_cs2 |= CS2_OFFL; + lp20_cs1 = new_cs1; +} + +t_stat +lp20_svc (UNIT *uptr) +{ + DEVICE *dptr = find_dev_from_unit (uptr); + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + char ch; + int fnc = (lp20_cs1 >> CS1_V_FNC) & CS1_M_FNC; + uint16 ram_ch; + uint8 data; + + if (fnc == FNC_PRINT && (uptr->flags & UNIT_ATT) == 0) { + lp20_cs1 |= CS1_ERR; + /* Set error */ + lp20_cs1 &= ~CS1_GO; + lp20_update_chkirq (uptr, 0, 1); + return SCPE_OK; + } + + if (uba_read_npr_byte(lp20_ba, dibp->uba_ctl, &data) == 0) { + lp20_cs2 |= CS2_MTE; + lp20_cs1 &= ~CS1_GO; + lp20_update_chkirq (uptr, 0, 1); + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP npr failed\n"); + return SCPE_OK; + } + + lp20_buf = data; + lp20_ba = (lp20_ba + 1) & 0777777; + lp20_wcnt = (lp20_wcnt + 1) & 07777; + if (lp20_wcnt == 0) { + lp20_cs1 &= ~CS1_GO; + } + lp20_chksum += lp20_buf; + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP npr %08o %06o %03o %d\n", lp20_ba, lp20_wcnt, + lp20_buf, fnc); + switch(fnc) { + case FNC_PRINT: + ram_ch = lp20_ram[(int)lp20_buf]; + /* If previous was delimiter or translation do it */ + if (lp20_cs1 & CS1_DHOLD || (ram_ch &(LP20_RAM_DEL|LP20_RAM_TRN)) != 0) { + ch = ram_ch & LP20_RAM_CHR; + lp20_cs1 &= ~CS1_DHOLD; + if (ram_ch & LP20_RAM_DEL) + lp20_cs1 |= CS1_DHOLD; + } + /* Flag if interrupt set */ + if (ram_ch & LP20_RAM_INT) { + lp20_cs1 &= ~CS1_GO; + lp20_cs1 |= CS1_UND; + } + /* Check if translate flag set */ + if (ram_ch & LP20_RAM_TRN) { + lp20_buf = (uint8)(ram_ch & 0377); + } + /* Check if paper motion */ + if (ram_ch & LP20_RAM_PI) { + int lines = 0; /* Number of new lines to output */ + /* Print any buffered line */ + if (lp20_col != 0) + lp20_printline(uptr, 1); + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP Page Index %02x %04x\n", + lp20_buf, 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 = 0; + lp20_pagcnt = (lp20_pagcnt - 1) & 07777; + if (lp20_pagcnt == 0) + lp20_cs1 |= CS1_PZERO; + 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 = 0; + lp20_pagcnt = (lp20_pagcnt - 1) & 07777; + if (lp20_pagcnt == 0) + lp20_cs1 |= CS1_PZERO; + } + lines++; + uptr->LINE++; + ram_ch--; + } + } + for(;lines > 0; lines--) { + sim_fwrite("\r\n", 1, 2, uptr->fileref); + uptr->pos+=2; + } + } else if (lp20_buf != 0) { + sim_debug(DEBUG_DETAIL, &lp20_dev, "LP print %03o '%c' %04o\n", + lp20_buf, lp20_buf, ram_ch); + lp20_output(uptr, lp20_buf); + } + if (lp20_cs1 & CS1_GO) + sim_activate(uptr, 600); + else + lp20_update_chkirq (uptr, lp20_wcnt == 0, 1); + return SCPE_OK; + + case FNC_TEST: + break; + + case FNC_DVU: + if ((uptr->flags & UNIT_OPT) != 0) { + lp20_output(uptr, lp20_buf); + break; + } + if (lp20_buf >= 0354 && lp20_buf <= 0356) { /* Start of DVU load */ + lp20_duvfa_state = 1; + lp20_index = 0; + lp20_odd = 0; + lp20_cs2 &= ~CS2_DVOF; + } else if (lp20_buf == 0357) { /* Stop DVU load */ + lp20_duvfa_state = 0; + lp20_vfu[lp20_index] = 010000; + if (lp20_odd || lp20_index < 12) { + lp20_cs1 &= ~CS1_DVON; + } else { + lp20_cs1 |= CS1_DVON; + } + } else if (lp20_duvfa_state) { + if (lp20_odd) { + lp20_vfu[lp20_index] = (lp20_vfu[lp20_index] & 077) | ((lp20_buf & 077) << 6); + sim_debug(DEBUG_DETAIL, &lp20_dev, + "LP load DFU %d %04x\n", lp20_index, lp20_vfu[lp20_index]); + lp20_index++; + } else { + lp20_vfu[lp20_index] = (lp20_vfu[lp20_index] & 0007700) | (lp20_buf & 077); + } + lp20_odd = !lp20_odd; + } + break; + + case FNC_RAM: + if (lp20_odd) { + lp20_ram[lp20_index] = (lp20_ram[lp20_index] & 0377) | ((lp20_buf & 017) << 8); + lp20_index++; + } else { + lp20_ram[lp20_index] = (lp20_ram[lp20_index] & 07400) | (lp20_buf & 0377); + } + lp20_odd = !lp20_odd; + break; + } + if (lp20_cs1 & CS1_GO) { + sim_activate(uptr, 10); + } else { + lp20_update_chkirq (uptr, 1, 1); + } + return SCPE_OK; +} + +/* Init routine */ +t_stat lp20_init (UNIT *uptr) +{ + lp20_cs1 = (lp20_cs1 & CS1_DVON) | CS1_DONE; + lp20_cs2 = lp20_cs2 & (CS2_OFFL|CS2_DVOF); + lp20_col = 0; + lp20_ba = 0; + lp20_wcnt = 0; + lp20_chksum = 0; + sim_cancel (uptr); /* deactivate unit */ + lp20_update_chkirq (uptr, 1, 0); + return SCPE_OK; +} + + +/* Reset routine */ +t_stat lp20_reset (DEVICE *dptr) +{ + UNIT *uptr = &lp20_unit; + int i; + int par; + lp20_col = 0; + uptr->LINE = 0; + lp20_cs1 = 0; + lp20_cs2 = CS2_OFFL|CS2_DVOF; + lp20_ba = 0; + lp20_wcnt = 0; + /* Clear RAM & VFU */ + for (i = 0; i < 256; i++) { + lp20_ram[i] = 0; + lp20_vfu[i] = 0; + } + + if ((uptr->flags & UNIT_OPT) != 0) { + /* Load default VFU into VFU */ + memcpy(&lp20_vfu, lp20_dvfu, sizeof(lp20_dvfu)); + lp20_cs2 |= CS2_OVFU; + lp20_cs2 &= CS2_DVOF; + lp20_cs1 |= CS1_DVON; + } + 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; + /* Set parity of default RAM */ + for (i = 0; i < 256; i++) { + lp20_ram[i] &= ~(LP20_RAM_RAP); + par = (lp20_ram[i] >> 8) ^ (lp20_ram[i] >> 4) ^ (lp20_ram[i]); + par = (par >> 2) ^ par; + par ^= par >> 1; + if ((par & 1) == 0) + lp20_ram[i] |= LP20_RAM_RAP; + } + sim_cancel (uptr); /* deactivate unit */ + if (uptr->flags & UNIT_ATT) { + lp20_update_ready(uptr, CS1_ONL, 0); + lp20_update_chkirq (uptr, 1, 0); + } + return SCPE_OK; +} + +/* Attach routine */ + +t_stat lp20_attach (UNIT *uptr, CONST char *cptr) +{ + t_stat r; + sim_switches |= SWMASK ('A'); /* Position to EOF */ + r = attach_unit (uptr, cptr); + if (r == SCPE_OK) { + lp20_update_ready(uptr, CS1_ONL, 0); + lp20_update_chkirq (uptr, 1, 1); + } + return r; +} + +/* Detach routine */ + +t_stat lp20_detach (UNIT *uptr) +{ + sim_cancel (uptr); /* deactivate unit */ + lp20_update_ready(uptr, 0, CS1_ONL); + lp20_update_chkirq (uptr, 1, 1); + 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. \n"); +fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n"); +fprintf (st, " sim> SET %s LINESPERPAGE=n\n\n", dptr->name); +fprintf (st, "The default is 66 lines per page.\n\n"); +fprintf (st, "The LP20 is a unibus device, various parameters can be changed on these devices\n"); +fprintf (st, "\n The address of the device can be set with: \n"); +fprintf (st, " sim> SET LP20 ADDR=octal default address= 775400\n"); +fprintf (st, "\n The interrupt vector can be set with: \n"); +fprintf (st, " sim> SET LP20 VECT=octal default 754\n"); +fprintf (st, "\n The interrupt level can be set with: \n"); +fprintf (st, " sim> SET LP20 BR=# # should be between 4 and 7.\n"); +fprintf (st, "\n The unibus addaptor that the DZ is on can be set with:\n"); +fprintf (st, " sim> SET LP20 CTL=# # can be either 1 or 3\n"); + +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 diff --git a/PDP10/ks10_tcu.c b/PDP10/ks10_tcu.c new file mode 100644 index 00000000..c7a78535 --- /dev/null +++ b/PDP10/ks10_tcu.c @@ -0,0 +1,137 @@ +/* ks10_tcu.c: PDP-10 + + Copyright (c) 2021, Richard Cornwell + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Richard Cornwell shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Richard Cornwell. + +*/ + +#include "kx10_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#ifndef NUM_DEVS_TCU +#define NUM_DEVS_TCU 0 +#endif + +#if (NUM_DEVS_TCU > 0) + +#define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */ +#define UNIT_Y2K (1u << UNIT_V_Y2K) + + +int tcu_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +int tcu_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +t_stat tcu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +const char *tcu_description (DEVICE *dptr); +DIB tcu_dib = { 0760770, 07, 0, 0, 3, &tcu_read, &tcu_write, NULL, 0, 0 }; + +UNIT tcu_unit = {UDATA (NULL, UNIT_IDLE+UNIT_DISABLE, 0)}; + +MTAB tcu_mod[] = { + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of TCU" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of TCU" }, + { UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL }, + { UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL }, + { 0 } + }; + +DEVICE tcu_dev = { + "TIM", &tcu_unit, NULL, tcu_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, NULL, NULL, NULL, NULL, + &tcu_dib, DEV_DISABLE | DEV_DEBUG, 0, dev_debug, + NULL, NULL, &tcu_help, NULL, NULL, &tcu_description + }; + + +/* Time can't be set in device */ +int +tcu_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) +{ + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + + return 0; +} + +/* Read current time of day */ +int +tcu_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + time_t curtim; + struct tm *tptr; + + if ((dptr->units[0].flags & UNIT_DIS) != 0) + return 1; + + /* Get time */ + curtim = sim_get_time (NULL); + tptr = localtime (&curtim); + if (tptr == NULL) + return 0; + if ((tptr->tm_year > 99) && !(dptr->units[0].flags & UNIT_Y2K)) + tptr->tm_year = 99; /* Y2K prob? */ + + switch (addr & 06) { + + case 0: /* year/month/day */ + *data = (((tptr->tm_year) & 0177) << 9) | + (((tptr->tm_mon + 1) & 017) << 5) | + ((tptr->tm_mday) & 037); + break; + + case 2: /* hour/minute */ + *data = (((tptr->tm_hour) & 037) << 8) | + ((tptr->tm_min) & 077); + break; + + case 4: /* second */ + *data = (tptr->tm_sec) & 077; + break; + + case 6: /* status */ + *data = 0200; + break; + } + + sim_debug(DEBUG_DETAIL, dptr, "TCU read %06o %06o %o\n", + addr, *data, access); + return 0; +} + + +t_stat tcu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +return SCPE_OK; +} + +const char *tcu_description (DEVICE *dptr) +{ +return "TCU150 Time of day clock"; +} + +#endif diff --git a/PDP10/ks10_uba.c b/PDP10/ks10_uba.c new file mode 100644 index 00000000..c01cd531 --- /dev/null +++ b/PDP10/ks10_uba.c @@ -0,0 +1,586 @@ +/* ks10_uba.c: KS10 Unibus interface + + Copyright (c) 2021, 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" + +/* UBA Map as stored */ +#define PAGE_MASK 000003777000 /* Page mask bits, bits 25-36 on load */ +#define MAP_RPV 000400000000 /* Ram parity valid bit */ +#define MAP_VALID 001000000000 /* Page valid */ +#define MAP_FME 002000000000 /* Fast Mode enable */ +#define MAP_EN16 004000000000 /* Disable upper 2 bits UBA */ +#define MAP_RPW 010000000000 /* For Read Pause Write */ +#define MAP_RAMP 020000000000 /* Parity error in RAM */ + +/* UBA Stats register */ +#define UBST_PIL 000000000007 /* Low level PIA */ +#define UBST_PIH 000000000070 /* High level PIA */ +#define UBST_INIT 000000000100 /* Initialize UBA */ +#define UBST_DXFR 000000000200 /* Disable transfer on uncorrectable */ +#define UBST_PWRL 000000001000 /* Power low */ +#define UBST_INTL 000000002000 /* Interrupt on Br5 or Br4 */ +#define UBST_INTH 000000004000 /* INterrupt on Br7 or Br6 */ +#define UBST_NED 000000040000 /* Non-existant device */ +#define UBST_PAR 000000100000 /* Parity error */ +#define UBST_BAD 000000200000 /* Bad mem transfer */ +#define UBST_TIM 000000400000 /* UBA Timout */ + +#define VECT_L 0x10 +#define VECT_H 0x20 +#define VECT_CTR 0x0F + +uint32 uba_map[2][64]; +uint32 uba_status[2]; +int uba_device[16] = { -1, 0, -1, 1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; + +int uba_irq_ctlr[128]; + +int +uba_read(t_addr addr, int ctl, uint64 *data, int access) +{ + DEVICE *dptr; + int i; + int ubm = uba_device[ctl]; + + if (ctl == 0 && addr == 0100000) { + *data = 0; + return 0; + } + + if (ubm == -1) { + sim_debug(DEBUG_EXP, &cpu_dev, "No UBA adaptor %02o %08o\n", ctl, addr); + return 1; + } + + /* Check if in UBA map */ + if ((addr & 0777600) == 0763000) { + if ((addr & 0100) == 0) { + *data = (uint64)uba_map[ubm][addr & 077]; + return 0; + } else if ((addr & 077) == 0) { + int pih, pil; + int irqf = 0; + *data = (uint64)uba_status[ubm]; + pih = 0200 >> ((uba_status[ubm] >> 3) & 07); + pil = 0200 >> (uba_status[ubm] & 07); + for (i = 0; i < 128; i++) { + if ((uba_irq_ctlr[i] & VECT_CTR) == ctl) + irqf |= uba_irq_ctlr[i]; + } + *data |= (irqf & (VECT_L|VECT_H)) << 6; + return 0; + } else if ((addr & 077) == 1) { + *data = 0; + return 0; + } + *data = 0; + uba_status[ubm] |= UBST_TIM | UBST_NED; + return 1; + } + + /* Look for device */ + for(i = 0; (dptr = sim_devices[i]) != NULL; i++) { + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + continue; + if (ctl == dibp->uba_ctl && + dibp->uba_addr == (addr & (~dibp->uba_mask))) { + uint16 buf; + int r = dibp->rd_io(dptr, addr, &buf, access); + if (r) + break; + if (access == BYTE) { + if ((addr & 1) != 0) + buf >>= 8; + buf &= 0377; + } + *data = (uint64)buf; + return r; + } + } + sim_debug(DEBUG_EXP, &cpu_dev, "No UBA device %02o %08o\n", ctl, addr); + *data = 0; + uba_status[ubm] |= UBST_TIM | UBST_NED; + return 1; +} + +int +uba_write(t_addr addr, int ctl, uint64 data, int access) +{ + DEVICE *dptr; + int i; + int ubm = uba_device[ctl]; + + if (ctl == 0 && addr == 0100000) { + return 1; + } + if (ubm == -1) { + sim_debug(DEBUG_EXP, &cpu_dev, "No UBA adaptor %02o %08o %012llo\n", ctl, addr, data); + return 1; + } + + sim_debug(DEBUG_EXP, &cpu_dev, "UBA device write %02o %08o %012llo %d\n", ctl, addr, data, access); + if (access == BYTE) { + if ((addr & 1) != 0) + data = (data & 0377) << 8; + else + data = (data & 0377); + } + + /* Check if in UBA map */ + if ((addr & 0777400) == 0763000) { + if ((addr & 0100) == 0) { + uint32 map = (uint32)(data & 03777) << 9; + map |= (uint32)(data & 0740000) << 13; + uba_map[ubm][addr & 077] = map; + sim_debug(DEBUG_EXP, &cpu_dev, "Wr MAP %02o %012llo %06o\n", + addr & 077, data, map); + return 0; + } else if ((addr & 077) == 0) { + uba_status[ubm] &= (uint32)(074000 ^ data) | 0746000; + if (data & 0100) { + uba_status[ubm] = 0; + for(i = 0; (dptr = sim_devices[i]) != NULL; i++) { + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + continue; + if (ctl == dibp->uba_ctl && dptr->reset != NULL) + (void)(dptr->reset)(dptr); + } + } + for (i = 0; i < 128; i++) { + if ((uba_irq_ctlr[i] & VECT_CTR) == ctl) { + uba_irq_ctlr[i] = 0; + clr_interrupt(i << 2); + } + } + uba_status[ubm] |= (uint32)(0277 & data); + return 0; + } else if ((addr & 077) == 1) { + return 0; + } + uba_status[ubm] |= UBST_TIM | UBST_NED; + return 1; + } + + /* Look for device */ + for(i = 0; (dptr = sim_devices[i]) != NULL; i++) { + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + continue; + if (ctl == dibp->uba_ctl && dibp->uba_addr == (addr & (~dibp->uba_mask))) { + uint16 buf = (uint16)(data & 0177777); + int r = dibp->wr_io(dptr, addr, buf, access); + sim_debug(DEBUG_EXP, &cpu_dev, "UBA device write %02o %08o %012llo %06o\n", ctl, addr, data, buf); + if (r) + break; + return r; + } + } + sim_debug(DEBUG_EXP, &cpu_dev, "No UBA device write %02o %08o %012llo\n", ctl, addr, data); + uba_status[ubm] |= UBST_TIM | UBST_NED; + return 1; +} + +int +uba_read_npr(t_addr addr, uint16 ctl, uint64 *data) +{ + int ubm = uba_device[ctl]; + uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + if ((addr & 0400000) != 0) + return 0; + if ((map & MAP_VALID) == 0) + return 0; + addr = (map & PAGE_MASK) | (addr >> 2) & 0777; + *data = M[addr]; + return 1; +} + +int +uba_write_npr(t_addr addr, uint16 ctl, uint64 data) +{ + int ubm = uba_device[ctl]; + uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; + if ((addr & 0400000) != 0) + return 0; + if ((map & MAP_VALID) == 0) + return 0; + addr = (map & PAGE_MASK) | (addr >> 2) & 0777; + sim_debug(DEBUG_DATA, &cpu_dev, "Wr NPR %08o %08o %012llo\n", oaddr, addr, data); + M[addr] = data; + return 1; +} + +int +uba_read_npr_byte(t_addr addr, uint16 ctl, uint8 *data) +{ + int ubm = uba_device[ctl]; + uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; + uint64 wd; + if ((addr & 0400000) != 0) + return 0; + if ((map & MAP_VALID) == 0) + return 0; + addr = (map & PAGE_MASK) | (addr >> 2) & 0777; + wd = M[addr]; + sim_debug(DEBUG_DATA, &kmc_dev, "RD NPR B %08o %08o %012llo ", oaddr, addr, wd); + if ((oaddr & 02) == 0) + wd >>= 18; + if ((oaddr & 01)) + wd >>= 8; + sim_debug(DEBUG_DATA, &kmc_dev, "%03llo\n", wd & 0377); + *data = (uint8)(wd & 0377); + return 1; +} + +int +uba_write_npr_byte(t_addr addr, uint16 ctl, uint8 data) +{ + int ubm = uba_device[ctl]; + uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; + uint64 wd; + uint64 msk; + uint64 buf; + if ((addr & 0400000) != 0) + return 0; + if ((map & MAP_VALID) == 0) + return 0; + addr = (map & PAGE_MASK) | (addr >> 2) & 0777; + msk = 0377; + buf = (uint64)(data & msk); + wd = M[addr]; + sim_debug(DEBUG_DATA, &kmc_dev, "WR NPR B %08o %08o %012llo ", oaddr, addr, wd); + if ((oaddr & 02) == 0) { + buf <<= 18; + msk <<= 18; + } + if ((oaddr & 01)) { + buf <<= 8; + msk <<= 8; + } + wd &= ~msk; + wd |= buf; + M[addr] = wd; + sim_debug(DEBUG_DATA, &kmc_dev, "%012llo\n", wd); + return 1; +} + +int +uba_read_npr_word(t_addr addr, uint16 ctl, uint16 *data) +{ + int ubm = uba_device[ctl]; + uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; + uint64 wd; + if ((addr & 0400000) != 0) + return 0; + if ((map & MAP_VALID) == 0) + return 0; + addr = (map & PAGE_MASK) | (addr >> 2) & 0777; + wd = M[addr]; + sim_debug(DEBUG_EXP, &cpu_dev, "RD NPR W %08o %08o %012llo m=%o\n", oaddr, addr, wd, map); + if ((oaddr & 02) == 0) + wd >>= 18; + *data = (uint16)(wd & 0177777); + return 1; +} + +int +uba_write_npr_word(t_addr addr, uint16 ctl, uint16 data) +{ + int ubm = uba_device[ctl]; + uint32 map = uba_map[ubm][(077) & (addr >> 11)]; + t_addr oaddr = addr; + uint64 wd; + uint64 msk; + uint64 buf; + if ((addr & 0400000) != 0) + return 0; + if ((map & MAP_VALID) == 0) + return 0; + addr = (map & PAGE_MASK) | (addr >> 2) & 0777; + msk = 0177777; + buf = (uint64)(data & msk); + wd = M[addr]; + sim_debug(DEBUG_EXP, &cpu_dev, "WR NPR W %08o %08o %012llo m=%o\n", oaddr, addr, wd, map); + if ((oaddr & 02) == 0) { + buf <<= 18; + msk <<= 18; + } + wd &= ~msk; + wd |= buf; + M[addr] = wd; + return 1; +} + +void +uba_set_irq(DIB *dibp, int vect) +{ + int ubm = uba_device[dibp->uba_ctl]; + int pi; + int flg; + + if (ubm < 0) + return; + /* Figure out what channel device should IRQ on */ + if (dibp->uba_br > 5) { + pi = uba_status[ubm] >> 3; + flg = VECT_H; + } else { + pi = uba_status[ubm]; + flg = VECT_L; + } + sim_debug(DEBUG_IRQ, &cpu_dev, "set uba irq %06o %03o %o pi=%o\n", + dibp->uba_addr, vect, dibp->uba_br, pi); + /* Save in device temp the irq value */ + set_interrupt(vect, pi); + uba_irq_ctlr[vect >> 2] = flg | dibp->uba_ctl; +} + +void +uba_clr_irq(DIB *idev, int vect) +{ + int ubm = uba_device[idev->uba_ctl]; + + if (ubm < 0) + return; + sim_debug(DEBUG_IRQ, &cpu_dev, "clr uba irq %06o %03o %o\n", + idev->uba_addr, vect, idev->uba_br); + clr_interrupt(vect); + uba_irq_ctlr[vect >> 2] = 0; +} + +void +uba_reset() +{ + int i; + + /* Clear the Unibus map */ + for (i = 0; i < 64; i++) { + uba_map[0][i] = 0; + uba_map[1][i] = 0; + } + uba_status[0] = 0; + uba_status[1] = 0; + + for (i = 0; i < 128; i++) + uba_irq_ctlr[i] = 0; +} + +t_addr +uba_get_vect(t_addr addr, int lvl, int dev) +{ + uint64 buffer; + int ubm; + + ubm = uba_irq_ctlr[dev]; + if (ubm != 0) { + /* Fetch vector base */ + ubm &= VECT_CTR; + if (Mem_read_word(0100 | ubm, &buffer, 1)) + return addr; + /* Compute unibus vector */ + addr = (buffer + dev) & RMASK; + sim_debug(DEBUG_IRQ, &cpu_dev, "get_vect d=%03o l=%03o ir=%02o v=%012llo\n", + dev << 2, lvl, uba_irq_ctlr[dev], buffer); + uba_irq_ctlr[dev] = 0; + } + return addr; +} + +void +uba_set_parity(uint16 ctl) +{ + int ubm = uba_device[ctl]; + if (ubm >= 0) + uba_status[ubm] |= UBST_PAR; +} + +t_stat +uba_set_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value newaddr; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + newaddr = get_uint (cptr, 18, 0777777, &r); + + if (r != SCPE_OK) + return r; + dibp->uba_addr = (uint32)(newaddr & RMASK); + return SCPE_OK; +} + +t_stat +uba_show_addr (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + fprintf(st, "addr=%07o", dibp->uba_addr); + return SCPE_OK; +} + +t_stat +uba_set_br(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value br; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + br = get_uint (cptr, 3, 07, &r); + + if (r != SCPE_OK) + return r; + + if (br < 4 || br > 7) + return SCPE_ARG; + dibp->uba_br = (uint16)br; + return SCPE_OK; +} + +t_stat +uba_show_br (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + fprintf(st, "br=%o", dibp->uba_br); + return SCPE_OK; +} + +t_stat +uba_set_vect(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value vect; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + vect = get_uint (cptr, 8, 0377, &r); + + if (r != SCPE_OK) + return r; + + dibp->uba_vect = (uint16)vect; + return SCPE_OK; +} + +t_stat +uba_show_vect (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + fprintf(st, "vect=%03o", dibp->uba_vect); + return SCPE_OK; +} + +t_stat +uba_set_ctl(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + DEVICE *dptr; + DIB *dibp; + t_value ctl; + t_stat r; + + if (cptr == NULL) + return SCPE_ARG; + if (uptr == NULL) + return SCPE_IERR; + dptr = find_dev_from_unit(uptr); + if (dptr == NULL) + return SCPE_IERR; + + dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + + ctl = get_uint (cptr, 4, 017, &r); + + if (r != SCPE_OK) + return r; + + if (ctl != 1 && ctl != 3) + return SCPE_ARG; + dibp->uba_ctl = (uint16)ctl; + return SCPE_OK; +} + +t_stat +uba_show_ctl (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + DEVICE *dptr = find_dev_from_unit(uptr); + DIB *dibp = (DIB *) dptr->ctxt; + if (dibp == NULL) + return SCPE_IERR; + fprintf(st, "uba%o", dibp->uba_ctl); + return SCPE_OK; +} + + diff --git a/PDP10/kx10_cpu.c b/PDP10/kx10_cpu.c index 5cbf0be0..ef2cd3e3 100644 --- a/PDP10/kx10_cpu.c +++ b/PDP10/kx10_cpu.c @@ -1,6 +1,6 @@ /* kx10_cpu.c: PDP-10 CPU simulator - Copyright (c) 2011-2020, Richard Cornwell + Copyright (c) 2011-2021, 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/KI10/KL10 central processor + cpu KA10/KI10/KL10/KS10 central processor The 36b system family had six different implementions: PDP-6, KA10, KI10, @@ -107,7 +107,7 @@ uint64 M[MAXMEMSIZE]; /* Memory */ -#if KL +#if KL | KS uint64 FM[128]; /* Fast memory register */ #elif KI uint64 FM[64]; /* Fast memory register */ @@ -131,6 +131,7 @@ int uuo_cycle; /* Uuo cycle in progress */ int SC; /* Shift count */ int SCAD; /* Shift count extension */ int FE; /* Exponent */ +t_addr last_addr; /* Last addressed accessed */ #if KA | PDP6 t_addr Pl, Ph, Rl, Rh, Pflag; /* Protection registers */ int push_ovf; /* Push stack overflow */ @@ -161,7 +162,7 @@ int pi_restore; /* Restore previous level */ int pi_hold; /* Hold onto interrupt */ int modify; /* Modify cycle */ int xct_flag; /* XCT flags */ -#if KI | KL +#if KI | KL | KS uint64 ARX; /* Extension to AR */ uint64 BRX; /* Extension to BR */ uint64 ADX; /* Extension to AD */ @@ -170,7 +171,25 @@ 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 +#if KS +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 */ +uint64 tim_low; /* Low order timer word */ +uint64 tim_high; /* High order timer word */ +uint64 int_val; /* Interval timer */ +uint64 int_cur; /* Current interval */ +int t20_page; /* Tops 20 paging selected */ +int ptr_flg; /* Access to pointer value */ +int extend = 0; /* Process extended instruction */ +int fe_xct = 0; /* Execute instruction at address */ +int pi_vect; /* Last pi location used for IRQ */ +#if KS_ITS +uint64 qua_time; /* Quantum clock value */ +uint8 pi_act; /* Current active PI level */ +#endif +#elif KL int pi_vect; /* Last pi location used for IRQ */ int ext_ac; /* Extended instruction AC */ uint8 prev_ctx; /* Previous AC context */ @@ -192,12 +211,12 @@ int cur_sect; /* Current section */ int prev_sect; /* Previous section */ int pc_sect; /* Program counter section */ int glb_sect; /* Global section access */ -#else +#elif KI int small_user; /* Small user flag */ #endif int user_addr_cmp; /* User address compare flag */ #endif -#if KI | KL | ITS | BBN +#if KI | KL | ITS | BBN | KS uint32 e_tlb[512]; /* Executive TLB */ uint32 u_tlb[546]; /* User TLB */ int page_enable; /* Enable paging */ @@ -242,6 +261,29 @@ int MAGIC = 1; /* Magic switch. */ #define mar brk_addr; #endif +#if KL +#define spt FM[(06<<4)|3] +#define cst FM[(06<<4)|2] +#define cst_msk FM[(06<<4)|0] +#define cst_dat FM[(06<<4)|1] +#endif + +#if KS +uint64 spt; +uint64 cst; +uint64 cst_msk; +uint64 cst_dat; +uint64 hsb; +#endif + +#if KS_ITS +#define dbr1 spt +#define dbr2 cst +#define dbr3 cst_dat +#define dbr4 cst_msk +uint64 pcst; +#endif + int watch_stop; /* Stop at memory watch point */ int maoff = 0; /* Offset for traps */ @@ -249,7 +291,11 @@ uint16 dev_irq[128]; /* Pending irq by device */ t_stat (*dev_tab[128])(uint32 dev, uint64 *data); t_addr (*dev_irqv[128])(uint32 dev, t_addr addr); t_stat rtc_srv(UNIT * uptr); +#if KS +int32 rtc_tps = 500; +#else int32 rtc_tps = 60; +#endif #if ITS t_stat qua_srv(UNIT * uptr); int32 qua_tps = 125000; @@ -260,11 +306,12 @@ t_stat tim_srv(UNIT * uptr); int32 tmxr_poll = 10000; /* Physical address range for Rubin 10-11 interface. */ -#define T11RANGE(addr) ((addr) >= 03040000) +#define T11RANGE(addr) ((addr) >= ten11_base && (addr) < ten11_end) /* Physical address range for auxiliary PDP-6. */ #define AUXCPURANGE(addr) ((addr) >= auxcpu_base && (addr) < (auxcpu_base + 040000)) - +#if (NUM_DEVS_RP + NUM_DEVS_RS + NUM_DEVS_TU) > 0 +#if KA | KI | KL /* List of RH10 & RH20 devices */ DEVICE *rh_devs[] = { #if (NUM_DEVS_RS > 0) @@ -294,6 +341,8 @@ DEVICE *rh_devs[] = { int rh_nums[] = { 0270, 0274, 0360, 0364, 0370, 0374, 0}; /* Maps RH10 & RH20 device number to DEVICE structure */ struct rh_dev rh[8]; +#endif +#endif typedef struct { uint32 pc; @@ -312,7 +361,7 @@ InstHistory *hst = NULL; /* instruction history */ /* Forward and external declarations */ -#if KL +#if KL | KS int do_extend(uint32 IA); #endif t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); @@ -321,7 +370,7 @@ 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 | KL +#if KI | KL | KS 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 @@ -330,10 +379,10 @@ t_stat cpu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, 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_read)(int flag, int cur_context, int fetch, int mod); int (*Mem_write)(int flag, int cur_context); #else -int Mem_read(int flag, int cur_context, int fetch); +int Mem_read(int flag, int cur_context, int fetch, int mod); int Mem_write(int flag, int cur_context); #endif @@ -347,7 +396,15 @@ t_bool build_dev_tab (void); cpu_mod CPU modifier list */ -UNIT cpu_unit[] = { { UDATA (&rtc_srv, UNIT_IDLE|UNIT_FIX|UNIT_BINK|UNIT_TWOSEG, 256 * 1024) }, +#if KL +#define DEFMEM 4096 +#elif KS +#define DEFMEM 512 +#else +#define DEFMEM 256 +#endif + +UNIT cpu_unit[] = { { UDATA (&rtc_srv, UNIT_IDLE|UNIT_FIX|UNIT_BINK|UNIT_TWOSEG, DEFMEM * 1024) }, #if ITS { UDATA (&qua_srv, UNIT_IDLE|UNIT_DIS, 0) } #endif @@ -375,7 +432,7 @@ REG cpu_reg[] = { { ORDATA (FM15, FM[015], 36) }, { ORDATA (FM16, FM[016], 36) }, { ORDATA (FM17, FM[017], 36) }, -#if KL +#if KL | KS { BRDATA (FM, FM, 8, 36, 128)}, #elif KI { BRDATA (FM, FM, 8, 36, 64)}, @@ -427,11 +484,11 @@ REG cpu_reg[] = { { ORDATAD (UB, ub_ptr, 18, "User Base Pointer") }, { ORDATAD (EB, eb_ptr, 18, "Executive Base Pointer") }, #endif -#if KL +#if KL | KS { ORDATAD (UB, ub_ptr, 22, "User Base Pointer") }, { ORDATAD (EB, eb_ptr, 22, "Executive Base Pointer") }, #endif -#if KI | KL +#if KI | KL | KS { ORDATAD (FMSEL, fm_sel, 8, "Register set select") }, { ORDATAD (SERIAL, apr_serial, 10, "System Serial Number") }, { FLDATA (INOUT, inout_fail, 0), REG_RO}, @@ -440,14 +497,16 @@ REG cpu_reg[] = { #endif { FLDATA (ADRCMP, user_addr_cmp, 0), REG_HRO}, #endif -#if KL | KI | ITS | BBN +#if KL | KI | ITS | BBN | KS { FLDATAD (PAGE_ENABLE, page_enable, 0, "Paging enabled")}, { FLDATAD (PAGE_FAULT, page_fault, 0, "Page fault"), REG_RO}, +#if KI | ITS | BBN { ORDATAD (AC_STACK, ac_stack, 18, "AC Stack"), REG_RO}, +#endif { 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 +#if !(KL | KS) { ORDATAD (LST_PAGE, last_page, 9, "Last page"), REG_HRO}, #endif #endif @@ -473,6 +532,19 @@ REG cpu_reg[] = { { ORDATAD (MAGIC, MAGIC, 1, "Magic switch"), REG_FIT}, #endif /* MAGIC_SWITCH */ #endif /* ITS */ +#if KS + { 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 (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 (SPT, spt, 18, "Special Page table"),}, + { ORDATAD (CST, cst, 18, "Memory status table"),}, + { ORDATAD (PU, cst_dat, 36, "User data"),}, + { ORDATAD (CSTM, cst_msk, 36, "Status mask"),}, +#endif #if KL { ORDATAD (EXT_AC, ext_ac, 4, "Extended Instruction AC"), REG_HRO}, { ORDATAD (PREV_CTX, prev_ctx, 5, "Previous context"), REG_HRO}, @@ -513,7 +585,7 @@ MTAB cpu_mod[] = { { UNIT_MSIZE, 8, "128K", "128K", &cpu_set_size }, { UNIT_MSIZE, 12, "196K", "196K", &cpu_set_size }, { UNIT_MSIZE, 16, "256K", "256K", &cpu_set_size }, -#if KI_22BIT|KI|ITS +#if KI_22BIT|KI|ITS|KS { UNIT_MSIZE, 32, "512K", "512K", &cpu_set_size }, { UNIT_MSIZE, 48, "768K", "768K", &cpu_set_size }, { UNIT_MSIZE, 64, "1024K", "1024K", &cpu_set_size }, @@ -522,7 +594,7 @@ MTAB cpu_mod[] = { { UNIT_MSIZE, 128, "2048K", "2048K", &cpu_set_size }, { UNIT_MSIZE, 256, "4096K", "4096K", &cpu_set_size }, #endif -#if KI|KL +#if KI|KL|KS { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "SERIAL", "SERIAL", &cpu_set_serial, &cpu_show_serial, NULL, "CPU Serial Number" }, #if KL @@ -538,7 +610,7 @@ MTAB cpu_mod[] = { { UNIT_M_PAGE, UNIT_TWOSEG, "TWOSEG", "TWOSEG", NULL, NULL, NULL, "Two Relocation Registers"}, #endif -#if ITS | KL_ITS +#if ITS | KL_ITS | KS_ITS { UNIT_M_PAGE, UNIT_ITSPAGE, "ITS", "ITS", NULL, NULL, NULL, "Paging hardware for ITS"}, #endif @@ -575,6 +647,11 @@ DEBTAB cpu_debug[] = { {"CONI", DEBUG_CONI, "Show coni instructions"}, {"CONO", DEBUG_CONO, "Show cono instructions"}, {"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"}, +#if KS + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, +#endif {0, 0} }; @@ -606,277 +683,6 @@ DEVICE cpu_dev = { #define P10(x) x #endif -int opflags[] = { - /* UUO Opcodes */ - /* UUO00 */ /* LUUO01 */ /* LUUO02 */ /* LUUO03 */ - 0, 0, 0, 0, - /* LUUO04 */ /* LUUO05 */ /* LUUO06 */ /* LUUO07 */ - 0, 0, 0, 0, - /* LUUO10 */ /* LUUO11 */ /* LUUO12 */ /* LUUO13 */ - 0, 0, 0, 0, - /* LUUO14 */ /* LUUO15 */ /* LUUO16 */ /* LUUO17 */ - 0, 0, 0, 0, - /* LUUO20 */ /* LUUO21 */ /* LUUO22 */ /* LUUO23 */ - 0, 0, 0, 0, - /* LUUO24 */ /* LUUO25 */ /* LUUO26 */ /* LUUO27 */ - 0, 0, 0, 0, - /* LUUO30 */ /* LUUO31 */ /* LUUO32 */ /* LUUO33 */ - 0, 0, 0, 0, - /* LUUO34 */ /* LUUO35 */ /* LUUO36 */ /* LUUO37 */ - 0, 0, 0, 0, - /* MUUO40 */ /* MUUO41 */ /* MUUO42 */ /* MUUO43 */ - 0, 0, 0, 0, - /* MUUO44 */ /* MUUO45 */ /* MUUO46 */ /* MUUO47 */ - 0, 0, 0, 0, - /* MUUO50 */ /* MUUO51 */ /* MUUO52 */ /* MUUO53 */ - 0, 0, 0, 0, - /* MUUO54 */ /* MUUO55 */ /* MUUO56 */ /* MUUO57 */ - 0, 0, 0, 0, - /* MUUO60 */ /* MUUO61 */ /* MUUO62 */ /* MUUO63 */ - 0, 0, 0, 0, - /* MUUO64 */ /* MUUO65 */ /* MUUO66 */ /* MUUO67 */ - 0, 0, 0, 0, - /* MUUO70 */ /* MUUO71 */ /* MUUO72 */ /* MUUO73 */ - 0, 0, 0, 0, - /* MUUO74 */ /* MUUO75 */ /* MUUO76 */ /* MUUO77 */ - 0, 0, 0, 0, - - /* Double precsision math */ - /* UJEN */ /* UUO101 */ /* GFAD */ /* GFSB */ - 0, 0, 0, 0, - /* JSYS */ /* ADJSP */ /* GFMP */ /* GFDV */ - 0, 0, 0, 0, - /* DFAD */ /* DFSB */ /* DFMP */ /* DFDV */ - 0, 0, 0, 0, - /* DADD */ /* DSUB */ /* DMUL */ /* DDIV */ - 0, 0, 0, 0, - /* DMOVE */ /* DMOVN */ /* FIX */ /* EXTEND */ - 0, 0, 0, 0, - /* DMOVEM */ /* DMOVNM */ /* FIXR */ /* FLTR */ - 0, 0, 0, 0, - - /* UFA */ /* DFN */ /* FSC */ /* IBP */ - P10(FCE|FBR), P10(FCE|FAC), FAC|SAC, 0, - /* ILDB */ /* LDB */ /* IDPB */ /* DPB */ - 0, 0, 0, 0, - /* Floating point */ - /* FAD */ /* FADL */ /* FADM */ /* FADB */ - SAC|FCE|FBR, SAC|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FADR */ /* FADRI */ /* FADRM */ /* FADRB */ - SAC|FCE|FBR, SAC|P6(SAC2|FCE)|P10(SWAR)|FBR, - FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FSB */ /* FSBL */ /* FSBM */ /* FSBB */ - SAC|FCE|FBR, SAC|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FSBR */ /* FSBRL */ /* FSBRM */ /* FSBRB */ - SAC|FCE|FBR, SAC|P6(SAC2|FCE)|P10(SWAR)|FBR, - FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FMP */ /* FMPL */ /* FMPM */ /* FMPB */ - SAC|FCE|FBR, SAC|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FMPR */ /* FMPRI */ /* FMPRM */ /* FMPRB */ - SAC|FCE|FBR, SAC|P6(SAC2|FCE)|P10(SWAR)|FBR, - FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FDV */ /* FDVL */ /* FDVM */ /* FDVB */ - SAC|FCE|FBR, SAC|FAC2|SAC2|FCE|FBR, FCEPSE|FBR, SAC|FBR|FCEPSE, - /* FDVR */ /* FDVRL */ /* FDVRM */ /* FDVRB */ - SAC|FCE|FBR, SAC|P6(FAC2|SAC2|FCE)|P10(SWAR)|FBR, - FCEPSE|FBR, SAC|FBR|FCEPSE, - - /* Full word operators */ - /* MOVE */ /* MOVEI */ /* MOVEM */ /* MOVES */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* MOVS */ /* MOVSI */ /* MOVSM */ /* MOVSS */ - SWAR|SAC|FCE, SWAR|SAC, SWAR|FAC|SCE, SWAR|SACZ|FCEPSE, - /* MOVN */ /* MOVNI */ /* MOVNM */ /* MOVNS */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* MOVM */ /* MOVMI */ /* MOVMM */ /* MOVMS */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* IMUL */ /* IMULI */ /* IMULM */ /* IMULB */ - SAC|FCE|FBR, SAC|FBR, FCEPSE|FBR, SAC|FCEPSE|FBR, - /* MUL */ /* MULI */ /* MULM */ /* MULB */ - SAC2|SAC|FCE|FBR, SAC2|SAC|FBR, FCEPSE|FBR, SAC2|SAC|FCEPSE|FBR, - /* IDIV */ /* IDIVI */ /* IDIVM */ /* IDIVB */ - SAC2|SAC|FCE|FAC, SAC2|SAC|FAC, FCEPSE|FAC, SAC2|SAC|FCEPSE|FAC, - /* DIV */ /* DIVI */ /* DIVM */ /* DIVB */ - SAC2|SAC|FCE|FAC|FAC2, SAC2|SAC|FAC|FAC2, - FCEPSE|FAC|FAC2, SAC2|SAC|FCEPSE|FAC\ - |FAC2, - /* Shift operators */ - /* ASH */ /* ROT */ /* LSH */ /* JFFO */ - FAC|SAC, FAC|SAC, FAC|SAC, FAC, - /* ASHC */ /* ROTC */ /* LSHC */ /* UUO247 */ - FAC|SAC|SAC2|FAC2, FAC|SAC|SAC2|FAC2, FAC|SAC|SAC2|FAC2, 0, - - /* Branch operators */ - /* EXCH */ /* BLT */ /* AOBJP */ /* AOBJN */ - FAC|FCE, FAC, FAC|SAC, FAC|SAC, - /* JRST */ /* JFCL */ /* XCT */ /* MAP */ - 0, 0, 0, 0, - /* PUSHJ */ /* PUSH */ /* POP */ /* POPJ */ - FAC|SAC, FAC|FCE|SAC, FAC, FAC|SAC, - /* JSR */ /* JSP */ /* JSA */ /* JRA */ - 0, SAC, FBR|SCE, 0, - /* ADD */ /* ADDI */ /* ADDM */ /* ADDB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* SUB */ /* SUBI */ /* SUBM */ /* SUBB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - - /* Compare operators */ - /* CAI */ /* CAIL */ /* CAIE */ /* CAILE */ - FBR, FBR, FBR, FBR, - /* CAIA */ /* CAIGE */ /* CAIN */ /* CAIG */ - FBR, FBR, FBR, FBR, - /* CAM */ /* CAML */ /* CAME */ /* CAMLE */ - FBR|FCE, FBR|FCE, FBR|FCE, FBR|FCE, - /* CAMA */ /* CAMGE */ /* CAMN */ /* CAMG */ - FBR|FCE, FBR|FCE, FBR|FCE, FBR|FCE, - - /* Jump and skip operators */ - /* JUMP */ /* JUMPL */ /* JUMPE */ /* JUMPLE */ - FAC, FAC, FAC, FAC, - /* JUMPA */ /* JUMPGE */ /* JUMPN */ /* JUMPG */ - FAC, FAC, FAC, FAC, - /* SKIP */ /* SKIPL */ /* SKIPE */ /* SKIPLE */ - SACZ|FCE, SACZ|FCE, SACZ|FCE, SACZ|FCE, - /* SKIPA */ /* SKIPGE */ /* SKIPN */ /* SKIPG */ - SACZ|FCE, SACZ|FCE, SACZ|FCE, SACZ|FCE, - /* AOJ */ /* AOJL */ /* AOJE */ /* AOJLE */ - SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, - /* AOJA */ /* AOJGE */ /* AOJN */ /* AOJG */ - SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, - /* AOS */ /* AOSL */ /* AOSE */ /* AOSLE */ - SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, - /* AOSA */ /* AOSGE */ /* AOSN */ /* AOSG */ - SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, - /* SOJ */ /* SOJL */ /* SOJE */ /* SOJLE */ - SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, - /* SOJA */ /* SOJGE */ /* SOJN */ /* SOJG */ - SAC|FAC, SAC|FAC, SAC|FAC, SAC|FAC, - /* SOS */ /* SOSL */ /* SOSE */ /* SOSLE */ - SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, - /* SOSA */ /* SOSGE */ /* SOSN */ /* SOSG */ - SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, SACZ|FCEPSE, - - /* Boolean operators */ - /* SETZ */ /* SETZI */ /* SETZM */ /* SETZB */ - SAC, SAC, SCE, SAC|SCE, - /* AND */ /* ANDI */ /* ANDM */ /* ANDB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* ANDCA */ /* ANDCAI */ /* ANDCAM */ /* ANDCAB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* SETM */ /* SETMI */ /* SETMM */ /* SETMB */ - SAC|FCE, SAC, 0, SAC|FCE, - /* ANDCM */ /* ANDCMI */ /* ANDCMM */ /* ANDCMB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* SETA */ /* SETAI */ /* SETAM */ /* SETAB */ - FBR|SAC, FBR|SAC, FBR|SCE, FBR|SAC|SCE, - /* XOR */ /* XORI */ /* XORM */ /* XORB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* IOR */ /* IORI */ /* IORM */ /* IORB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* ANDCB */ /* ANDCBI */ /* ANDCBM */ /* ANDCBB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* EQV */ /* EQVI */ /* EQVM */ /* EQVB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* SETCA */ /* SETCAI */ /* SETCAM */ /* SETCAB */ - FBR|SAC, FBR|SAC, FBR|SCE, FBR|SAC|SCE, - /* ORCA */ /* ORCAI */ /* ORCAM */ /* ORCAB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* SETCM */ /* SETCMI */ /* SETCMM */ /* SETCMB */ - SAC|FCE, SAC, FCEPSE, SAC|FCEPSE, - /* ORCM */ /* ORCMI */ /* ORCMM */ /* ORCMB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* ORCB */ /* ORCBI */ /* ORCBM */ /* ORCBB */ - FBR|SAC|FCE, FBR|SAC, FBR|FCEPSE, FBR|SAC|FCEPSE, - /* SETO */ /* SETOI */ /* SETOM */ /* SETOB */ - SAC, SAC, SCE, SAC|SCE, - - /* Half word operators */ - /* HLL */ /* HLLI */ /* HLLM */ /* HLLS */ - FBR|SAC|FCE, FBR|SAC, FAC|FCEPSE, SACZ|FCEPSE, - /* HRL */ /* HRLI */ /* HRLM */ /* HRLS */ - SWAR|FBR|SAC|FCE, SWAR|FBR|SAC, FAC|SWAR|FCEPSE,SACZ|FCEPSE, - /* HLLZ */ /* HLLZI */ /* HLLZM */ /* HLLZS */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* HRLZ */ /* HRLZI */ /* HRLZM */ /* HRLZS */ - SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, - /* HLLO */ /* HLLOI */ /* HLLOM */ /* HLLOS */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* HRLO */ /* HRLOI */ /* HRLOM */ /* HRLOS */ - SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, - /* HLLE */ /* HLLEI */ /* HLLEM */ /* HLLES */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* HRLE */ /* HRLEI */ /* HRLEM */ /* HRLES */ - SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, - /* HRR */ /* HRRI */ /* HRRM */ /* HRRS */ - FBR|SAC|FCE, FBR|SAC, FAC|FCEPSE, SACZ|FCEPSE, - /* HLR */ /* HLRI */ /* HLRM */ /* HLRS */ - SWAR|FBR|SAC|FCE, SWAR|FBR|SAC, FAC|SWAR|FCEPSE,SACZ|FCEPSE, - /* HRRZ */ /* HRRZI */ /* HRRZM */ /* HRRZS */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* HLRZ */ /* HLRZI */ /* HLRZM */ /* HLRZS */ - SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, - /* HRRO */ /* HRROI */ /* HRROM */ /* HRROS */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* HLRO */ /* HLROI */ /* HLROM */ /* HLROS */ - SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, - /* HRRE */ /* HRREI */ /* HRREM */ /* HRRES */ - SAC|FCE, SAC, FAC|SCE, SACZ|FCEPSE, - /* HLRE */ /* HLREI */ /* HLREM */ /* HLRES */ - SWAR|SAC|FCE, SWAR|SAC, FAC|SWAR|SCE, SWAR|SACZ|FCEPSE, - - /* Test operators */ - /* TRN */ /* TLN */ /* TRNE */ /* TLNE */ - FBR, FBR|SWAR, FBR, FBR|SWAR, - /* TRNA */ /* TLNA */ /* TRNN */ /* TLNN */ - FBR, FBR|SWAR, FBR, FBR|SWAR, - /* TDN */ /* TSN */ /* TDNE */ /* TSNE */ - FBR|FCE, FBR|SWAR|FCE, FBR|FCE, FBR|SWAR|FCE, - /* TDNA */ /* TSNA */ /* TDNN */ /* TSNN */ - FBR|FCE, FBR|SWAR|FCE, FBR|FCE, FBR|SWAR|FCE, - /* TRZ */ /* TLZ */ /* TRZE */ /* TLZE */ - FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, - /* TRZA */ /* TLZA */ /* TRZN */ /* TLZN */ - FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, - /* TDZ */ /* TSZ */ /* TDZE */ /* TSZE */ - FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, - /* TDZA */ /* TSZA */ /* TDZN */ /* TSZN */ - FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, - /* TRC */ /* TLC */ /* TRCE */ /* TLCE */ - FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, - /* TRCA */ /* TLCA */ /* TRCN */ /* TLCN */ - FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, - /* TDC */ /* TSC */ /* TDCE */ /* TSCE */ - FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, - /* TDCA */ /* TSCA */ /* TDCN */ /* TSCN */ - FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, - /* TRO */ /* TLO */ /* TROE */ /* TLOE */ - FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, - /* TROA */ /* TLOA */ /* TRON */ /* TLON */ - FBR|SAC, FBR|SWAR|SAC, FBR|SAC, FBR|SWAR|SAC, - /* TDO */ /* TSO */ /* TDOE */ /* TSOE */ - FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, - /* TDOA */ /* TSOA */ /* TDON */ /* TSON */ - FBR|SAC|FCE, FBR|SWAR|SAC|FCE, FBR|SAC|FCE, FBR|SWAR|SAC|FCE, - - /* IOT Instructions */ - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, -}; - #if PDP6 #define PC_CHANGE FLAGS |= PCHNG; check_apr_irq(); #else @@ -886,7 +692,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 | KL +#if KI | KL | KS #define AOB(x) ((x + 1) & RMASK) | ((x + 01000000LL) & (C1|LMASK)) #define SOB(x) ((x + RMASK) & RMASK) | ((x + LMASK) & (C1|LMASK)); #else @@ -898,7 +704,7 @@ int opflags[] = { #define QTEN11 (ten11_unit[0].flags & UNIT_ATT) #define QAUXCPU (auxcpu_unit[0].flags & UNIT_ATT) #else -#if KL_ITS +#if KL_ITS | KS_ITS #define QITS (cpu_unit[0].flags & UNIT_ITSPAGE) #else #define QITS 0 @@ -924,6 +730,7 @@ int opflags[] = { #else #define QSLAVE 0 #endif +#define MAX_DEV 128 #if KL struct _byte { @@ -1012,6 +819,7 @@ get_quantum() } #endif + /* * Set device to interrupt on a given level 1-7 * Level 0 means that device interrupt is not enabled @@ -1021,8 +829,10 @@ void set_interrupt(int dev, int lvl) { if (lvl) { dev_irq[dev>>2] = 0200 >> lvl; pi_pending = 1; +#if DEBUG sim_debug(DEBUG_IRQ, &cpu_dev, "set irq %o %o %03o %03o %03o\n", dev & 0774, lvl, PIE, PIR, PIH); +#endif } } @@ -1034,8 +844,10 @@ void set_interrupt_mpx(int dev, int lvl, int mpx) { if (lvl == 1 && mpx != 0) dev_irq[dev>>2] |= mpx << 8; pi_pending = 1; +#if DEBUG sim_debug(DEBUG_IRQ, &cpu_dev, "set mpx irq %o %o %o %03o %03o %03o\n", dev & 0774, lvl, mpx, PIE, PIR, PIH); +#endif } } #endif @@ -1045,8 +857,10 @@ void set_interrupt_mpx(int dev, int lvl, int mpx) { */ void clr_interrupt(int dev) { dev_irq[dev>>2] = 0; +#if DEBUG if (dev > 4) sim_debug(DEBUG_IRQ, &cpu_dev, "clear irq %o\n", dev & 0774); +#endif } /* @@ -1054,64 +868,65 @@ void clr_interrupt(int dev) { * else set pi_enc to highest level and return 1. */ int check_irq_level() { - int i, lvl; - int pi_req; + int i, lvl; + int pi_req; - /* If PXCT don't check for interrupts */ - if (xct_flag != 0) - return 0; - check_apr_irq(); + /* If PXCT don't check for interrupts */ + if (xct_flag != 0) + return 0; - /* If not enabled, check if any pending Processor IRQ */ - if (pi_enable == 0) { + /* If not enabled, check if any pending Processor IRQ */ + if (pi_enable == 0) { #if !PDP6 - if (PIR != 0) { - pi_enc = 1; - for(lvl = 0100; lvl != 0; lvl >>= 1) { - if (lvl & PIH) - break; - if (PIR & lvl) - return 1; - pi_enc++; + if (PIR != 0) { + pi_enc = 1; + for(lvl = 0100; lvl != 0; lvl >>= 1) { + if (lvl & PIH) + break; + if (PIR & lvl) + return 1; + pi_enc++; + } + } +#endif + return 0; + } + + /* Scan all devices */ + for(i = lvl = 0; i < MAX_DEV; i++) + lvl |= dev_irq[i]; + if (lvl == 0) + pi_pending = 0; + pi_req = (lvl & PIE) | PIR; +#if MPX_DEV + /* Check if interrupt on PI channel 1 */ + if (mpx_enable && cpu_unit[0].flags & UNIT_MPX && + (pi_req & 0100) && (PIH & 0100) == 0) { + pi_enc = 010; + for(i = lvl = 0; i < MAX_DEV; i++) { + if (dev_irq[i] & 0100) { + int l = dev_irq[i] >> 8; + if (l != 0 && l < pi_enc) + pi_enc = l; } } + if (pi_enc != 010) { + pi_enc += 010; + return 1; + } + } #endif - return 0; - } - - /* Scan all devices */ - for(i = lvl = 0; i < 128; i++) - lvl |= dev_irq[i]; - if (lvl == 0) - pi_pending = 0; - pi_req = (lvl & PIE) | PIR; -#if MPX_DEV - /* Check if interrupt on PI channel 1 */ - if (mpx_enable && cpu_unit[0].flags & UNIT_MPX && - (pi_req & 0100) && (PIH & 0100) == 0) { - pi_enc = 010; - for(i = lvl = 0; i < 128; i++) { - int l = dev_irq[i] >> 8; - if (dev_irq[i] & 0100 && l != 0 && l < pi_enc) - pi_enc = l; - } - if (pi_enc != 010) { - pi_enc += 010; - return 1; - } - } -#endif - /* Handle held interrupt requests */ - i = 1; - for(lvl = 0100; lvl != 0; lvl >>= 1, i++) { - if (lvl & PIH) - break; - if (pi_req & lvl) { - pi_enc = i; - return 1; - } - } - return 0; + /* Handle held interrupt requests */ + i = 1; + for(lvl = 0100; lvl != 0; lvl >>= 1, i++) { + if (lvl & PIH) + break; + if (pi_req & lvl) { + pi_enc = i; + return 1; + } + } + return 0; } /* @@ -1126,8 +941,13 @@ void restore_pi_hold() { for(lvl = 0100; lvl != 0; lvl >>= 1) { if (lvl & PIH) { PIR &= ~lvl; +#if DEBUG sim_debug(DEBUG_IRQ, &cpu_dev, "restore irq %o %03o\n", lvl, PIH); +#endif PIH &= ~lvl; +#if KS_ITS + pi_act &= ~lvl; +#endif break; } } @@ -1148,8 +968,9 @@ void set_pi_hold() { PIH |= (0200 >> pi); } +#if !KS /* - * PI device for KA and KI + * PI device for KA and KI and KL */ t_stat dev_pi(uint32 dev, uint64 *data) { uint64 res = 0; @@ -1272,6 +1093,7 @@ t_stat null_dev(uint32 dev, uint64 *data) { } return SCPE_OK; } +#endif #if KL void @@ -1395,7 +1217,7 @@ t_stat dev_pag(uint32 dev, uint64 *data) { * All operations set sweep done. */ t_stat dev_cca(uint32 dev, uint64 *data) { - irq_flags |= 020; + irq_flags |= SWP_DONE; *data = 0; check_apr_irq(); return SCPE_OK; @@ -1463,33 +1285,9 @@ t_stat dev_apr(uint32 dev, uint64 *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); - } + *data = ((uint64)brk_flags) << 23; + *data |= (uint64)brk_addr; + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI APR %012llo\n", *data); break; } return SCPE_OK; @@ -1536,39 +1334,6 @@ t_stat dev_mtr(uint32 dev, uint64 *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; @@ -1617,53 +1382,9 @@ t_stat dev_tim(uint32 dev, uint64 *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 */ @@ -1923,7 +1644,6 @@ void check_apr_irq() { } } - /* * APR Device for KA10. */ @@ -2028,6 +1748,529 @@ sim_debug(DEBUG_DATAIO, &cpu_dev, "Rl=%06o Pl=%06o, Rh=%06o, Ph=%06o\n", Rl, Pl, } #endif +#if KS + +/* + * 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); + } +} + +/* + * Set interrupt from CTY. + */ +void cty_interrupt() +{ + irq_flags |= CON_IRQ; + sim_debug(DEBUG_IRQ, &cpu_dev, "cty interrupt %06o\n", irq_enable); + check_apr_irq(); +} + +/* + * Execute instruction at location 071. + */ +void cty_execute(int addr) +{ + fe_xct = addr; +} + +int +load_tlb(int uf, int page, int wr) +{ + uint64 data; + +#if KS_ITS + if (QITS) { + 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 &= ~(0020000LL << 18); + else + data &= ~0020000LL; + M[dbr + pg] = data; + if ((page & 02) == 0) + data >>= 18; + data &= RMASK; + pg = 0; + switch(data >> 16) { + case 0: + fault_data = 0; + page_fault = 1; + return 0; /* No access */ + case 2: /* R/W First */ + pg |= KL_PAG_S; /* Indicate writable */ + /* Fall through */ + case 1: /* Read Only */ + if (wr) { + fault_data = ((data >> 16) << 28) | (010000LL << 18); + page_fault = 1; + return 0; + } + /* Remap the flag bits */ + pg |= KL_PAG_A; + break; + case 3: pg = KL_PAG_A|KL_PAG_W|KL_PAG_S; break; /* R/W */ + } + pg |= (data & 001777) << 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 0003777 + if (t20_page) { /* Start with full access */ + int acc_bits = PG_WRT|PG_KEP|PG_CAC; + uint64 cst_val = 0; + int index; + int pg; + + /* Get segment pointer */ + /* And save it */ + sim_interval--; + if (uf) + data = M[ub_ptr + 0540]; + else + data = M[eb_ptr + 0540]; + /* 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 & PG_MASK)]; + break; + + case 3: /* Indirect page */ + acc_bits &= (data >> 18) & RMASK; + index = (data >> 18) & PG_IDX; + sim_interval--; + if (index != 0) { + fault_data = 037LL << 30 | BIT8 | + ((data & ((PG_IDX << 18)|RMASK)) + (spt & PG_MASK)); + page_fault = 1; + return 0; + } + data = M[(data & RMASK) + (spt & PG_MASK)]; + 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 & PG_MASK)) { + sim_interval--; + cst_val = M[(cst & PG_MASK) + pg]; + if ((cst_val & PG_AGE) == 0) { + fault_data = 0; + page_fault = 1; + return 0; + } + M[(cst & PG_MASK) + 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 & PG_MASK)]; + break; + + case 3: /* Indirect page */ + acc_bits &= (data >> 18) & RMASK; + index = (data >> 18) & PG_IDX; + sim_interval--; + data = M[(data & RMASK) + (spt & PG_MASK)]; + 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 & PG_MASK)) { + sim_interval--; + cst_val = M[(cst & PG_MASK) + 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_MASK) + pg] = (cst_val & cst_msk) | cst_dat; + } else { + if (acc_bits & PG_WRT) { + cst_val = 1; + } + } + /* Now construct a TBL entry */ + /* A = accessable */ + /* W = writable */ + /* S = user */ + /* C = cache */ + data = pg | KL_PAG_A; + 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; + /* And save it */ + if (uf) + u_tlb[page] = data & RMASK; + else + e_tlb[page] = data & 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); +} + +/* + * Handle page lookup on KS10 + * + * 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 page = (RMASK & addr) >> 9; + int uf = (FLAGS & USER) != 0; + int upmp = 0; + + /* If paging is not enabled, address is direct */ + if (!page_enable) { + *loc = addr; + return 1; + } + + /* Figure out if this is a user space access */ + /* 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 && !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; + } + } + + /* 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 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 KS_ITS + if (QITS) { + return 0; + } +#endif + fault_data |= BIT8; + 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)) { + /* Update CST entry if needed */ + if ((cst & PG_MASK)) { + uint64 cst_val; + int pg = data & 03777; + sim_interval--; + cst_val = M[(cst & PG_MASK) + pg]; + M[(cst & PG_MASK) + 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 & 03777) << 9) + (addr & 0777); + + /* KS does page fault if accessing outside of memory */ + if (*loc >= MEMSIZE) { + fault_data = 037LL << 30 | BIT8 | *loc; + page_fault = 1; + irq_flags |= NXM_MEM; + return 0; + } + + /* Check for access error */ + if ((data & KL_PAG_A) == 0 || (wr != 0 && ((data & KL_PAG_W) == 0))) { + fault_data = (uint64)addr; + if (uf) { /* U */ + fault_data |= SMASK; /* BIT0 */ + u_tlb[page] = 0; + } else { + e_tlb[page] = 0; + } +#if KS_ITS + if (QITS) { + /* Access bits: + * KL_PAG_A means valid page. + * KL_PAG_S means read write first + * KL_PAG_W means read/write + * + * 00 no access = 0 + * 01 Read only = KL_PAG_A + * 10 Read write first = KL_PAG_A|KL_PAG_S + * 11 R/W = KL_PAG_A|KL_PAG_S|KL_PAG_W + */ + /* Check if accessable */ + if ((data & KL_PAG_A) != 0) { + if ((data & KL_PAG_S) != 0) { + fault_data |= 004000LL << 18; /* PF2.9 */ + } + if ((data & KL_PAG_W) != 0) { + fault_data |= 002000LL << 18; /* PF2.8 */ + } + } + if (wr) { + fault_data |= 010000LL << 18; + } + page_fault = 1; + return 0; + } +#endif + fault_data |= BIT8; + if (wr) /* T */ + fault_data |= BIT5; /* BIT5 */ + if (data & KL_PAG_A) { /* A */ + fault_data |= BIT2; /* BIT2 */ + if (data & KL_PAG_S) /* S */ + fault_data |= BIT4; /* BIT4 */ + } + page_fault = 1; + return 0; + } + + return 1; +} + +/* + * Register access on KS 10 + */ +#define get_reg(reg) FM[fm_sel|((reg) & 017)] + +#define set_reg(reg, value) FM[fm_sel|((reg) & 017)] = (value) + +int Mem_read(int flag, int cur_context, int fetch, int mod) { + t_addr addr; + + if (AB < 020) { + 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]; + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } + return 0; + } + } + /* Check if invalid section */ + MB = get_reg(AB); + } else { + if (!page_lookup(AB, flag, &addr, mod, cur_context, fetch)) + return 1; + if (addr >= MEMSIZE) { + irq_flags |= NXM_MEM; + check_apr_irq(); + return 1; + } + if (sim_brk_summ && sim_brk_test(addr, SWMASK('R'))) + watch_stop = 1; + sim_interval--; + MB = M[addr]; + modify = mod; + last_addr = addr; + } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } + return 0; +} + +int Mem_write(int flag, int cur_context) { + t_addr addr; + + if (AB < 020) { + 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; + } + } + set_reg(AB, MB); + } else { + if (modify) { + if (sim_brk_summ && sim_brk_test(last_addr, SWMASK('W'))) + watch_stop = 1; + M[last_addr] = MB; + modify = 0; + return 0; + } + + if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) + return 1; + if (addr >= MEMSIZE) { + irq_flags |= NXM_MEM; + check_apr_irq(); + return 1; + } + if (sim_brk_summ && sim_brk_test(addr, SWMASK('W'))) + watch_stop = 1; + sim_interval--; + M[addr] = MB; + } + return 0; +} +#endif + #if KL int load_tlb(int uf, int page, int wr) @@ -2094,10 +2337,6 @@ load_tlb(int uf, int page, int wr) #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; @@ -2134,14 +2373,14 @@ sect_loop: acc_bits &= (data >> 18) & RMASK; sim_interval--; index = data & RMASK; - data = M[index + spt]; + data = M[index + (spt & PG_MASK)]; break; case 3: /* Indirect page */ acc_bits &= (data >> 18) & RMASK; index = (data >> 18) & PG_IDX; sim_interval--; - data = M[(data & RMASK) + spt]; + data = M[(data & RMASK) + (spt & PG_MASK)]; if ((data & PG_STG) != 0) { fault_data = 0; page_fault = 1; @@ -2149,7 +2388,7 @@ sect_loop: } pg = data & PG_PAG; sim_interval--; - data = M[(pg << 9) | index]; + data = M[(pg << 9) + index]; goto sect_loop; } if ((data & PG_STG) != 0) { @@ -2160,15 +2399,15 @@ sect_loop: pg = data & PG_PAG; /* Update CST entry if needed */ - if (cst) { + if ((cst & PG_MASK)) { sim_interval--; - cst_val = M[cst + pg]; + cst_val = M[(cst & PG_MASK) + pg]; if ((cst_val & PG_AGE) == 0) { fault_data = 0; page_fault = 1; return 0; } - M[cst + pg] = (cst_val & cst_msk) | cst_dat; + M[(cst & PG_MASK) + pg] = (cst_val & cst_msk) | cst_dat; } /* Get address of page */ @@ -2193,14 +2432,14 @@ pg_loop: acc_bits &= (data >> 18) & RMASK; sim_interval--; index = data & RMASK; - data = M[index + spt]; + data = M[index + (spt & PG_MASK)]; break; case 3: /* Indirect page */ acc_bits &= (data >> 18) & RMASK; index = (data >> 18) & PG_IDX; sim_interval--; - data = M[(data & RMASK) + spt]; + data = M[(data & RMASK) + (spt & PG_MASK)]; if ((data & PG_STG) != 0) { fault_data = 0; page_fault = 1; @@ -2208,7 +2447,7 @@ pg_loop: } pg = data & RMASK; sim_interval--; - data = M[(pg << 9) | index]; + data = M[(pg << 9) + index]; goto pg_loop; } @@ -2221,9 +2460,9 @@ pg_loop: pg = data & PG_PAG; /* Check outside of memory */ /* Update CST entry if needed */ - if (cst) { + if ((cst & PG_MASK)) { sim_interval--; - cst_val = M[cst + pg]; + cst_val = M[(cst & PG_MASK) + pg]; if ((cst_val & PG_AGE) == 0) { fault_data = 0; page_fault = 1; @@ -2237,7 +2476,7 @@ pg_loop: page_fault = 1; return 0; } - M[cst + pg] = (cst_val & cst_msk) | cst_dat; + M[(cst & PG_MASK) + pg] = (cst_val & cst_msk) | cst_dat; } else { if (acc_bits & PG_WRT) { cst_val = 1; @@ -2322,9 +2561,6 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int } } - /* If this is modify instruction use write access */ - wr |= modify; - /* Figure out if this is a user space access */ /* AC = 1 use BYF5 */ @@ -2342,7 +2578,7 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int uf = (FLAGS & USERIO) != 0; pub = (FLAGS & PRV_PUB) != 0; - if ((xct_flag & 014) == 04 && !ptr_flg && glb_sect == 0) + if ((xct_flag & 014) == 04 && !cur_context && !ptr_flg && glb_sect == 0) sect = prev_sect; if ((xct_flag & 03) == 01 && BYF5 && glb_sect == 0) sect = prev_sect; @@ -2397,16 +2633,13 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int /* 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) { + if ((cst & PG_MASK)) { uint64 cst_val; int pg = data & 017777; sim_interval--; - cst_val = M[cst + pg]; - M[cst + pg] = (cst_msk & cst_val) | cst_dat | 1; + cst_val = M[(cst & PG_MASK) + pg]; + M[(cst & PG_MASK) + pg] = (cst_msk & cst_val) | cst_dat | 1; } data |= KL_PAG_W; /* Map the page */ @@ -2486,15 +2719,11 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int /* * Register access on KL 10 */ -uint64 get_reg(int reg) { - return FM[fm_sel|(reg & 017)]; -} +#define get_reg(reg) FM[fm_sel|((reg) & 017)] -void set_reg(int reg, uint64 value) { - FM[fm_sel|(reg & 017)] = value; -} +#define set_reg(reg, value) FM[fm_sel|((reg) & 017)] = (value) -int Mem_read(int flag, int cur_context, int fetch) { +int Mem_read(int flag, int cur_context, int fetch, int mod) { t_addr addr; if (AB < 020 && ((QKLB && (glb_sect == 0 || sect == 0 || @@ -2505,6 +2734,9 @@ int Mem_read(int flag, int cur_context, int fetch) { ((xct_flag & 2) != 0 && !cur_context && ptr_flg) || ((xct_flag & 1) != 0 && !cur_context && BYF5 )) { MB = FM[prev_ctx|AB]; + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } } @@ -2514,20 +2746,25 @@ int Mem_read(int flag, int cur_context, int fetch) { if (USER==0) /* U */ fault_data |= SMASK; /* BIT0 */ page_fault = 1; - return 0; + return 1; } MB = get_reg(AB); } else { - if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch)) + if (!page_lookup(AB, flag, &addr, mod, cur_context, fetch)) return 1; if (addr >= MEMSIZE) { - irq_flags |= 02000; + irq_flags |= NXM_MEM; return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; sim_interval--; MB = M[addr]; + modify = mod; + last_addr = addr; + } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; } return 0; } @@ -2556,10 +2793,17 @@ int Mem_write(int flag, int cur_context) { } set_reg(AB, MB); } else { + if (modify) { + if (sim_brk_summ && sim_brk_test(last_addr, SWMASK('W'))) + watch_stop = 1; + M[last_addr] = MB; + modify = 0; + return 0; + } if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) return 1; if (addr >= MEMSIZE) { - irq_flags |= 02000; + irq_flags |= NXM_MEM; return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) @@ -2758,24 +3002,18 @@ load_tlb(int uf, int page) /* Map the page */ sim_interval--; if (base) { + data = M[eb_ptr + (page >> 1)]; + e_tlb[page & 0776] = RMASK & (data >> 18); + e_tlb[page | 1] = RMASK & data; data = e_tlb[page]; - if (data == 0) { - data = M[eb_ptr + (page >> 1)]; - e_tlb[page & 0776] = RMASK & (data >> 18); - e_tlb[page | 1] = RMASK & data; - data = e_tlb[page]; - pag_reload = ((pag_reload + 1) & 037) | 040; - } + pag_reload = ((pag_reload + 1) & 037) | 040; last_page = ((page ^ 0777) << 1)|1; } else { + data = M[ub_ptr + (page >> 1)]; + u_tlb[page & 01776] = RMASK & (data >> 18); + u_tlb[page | 1] = RMASK & data; data = u_tlb[page]; - if (data == 0) { - data = M[ub_ptr + (page >> 1)]; - u_tlb[page & 01776] = RMASK & (data >> 18); - u_tlb[page | 1] = RMASK & data; - data = u_tlb[page]; - pag_reload = ((pag_reload + 1) & 037) | 040; - } + pag_reload = ((pag_reload + 1) & 037) | 040; if (upmp) last_page = (((page-0440) ^ 0777) << 1) | 1; else @@ -2794,7 +3032,7 @@ load_tlb(int uf, int page) * 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 page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int fetch, int modify) { int data; int page = (RMASK & addr) >> 9; int uf = (FLAGS & USER) != 0; @@ -2809,13 +3047,6 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int 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; @@ -2827,6 +3058,9 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int } } + /* If this is modify instruction use write access */ + wr |= modify; + /* If user, check if small user enabled */ if (uf) { if (small_user && (page & 0340) != 0) { @@ -2897,38 +3131,44 @@ void set_reg(int reg, uint64 value) { FM[reg & 017] = value; } -int Mem_read(int flag, int cur_context, int fetch) { +int Mem_read(int flag, int cur_context, int fetch, int mod) { t_addr addr; if (AB < 020) { - if (FLAGS & USER) { - MB = get_reg(AB); - return 0; - } else { + if ((FLAGS & USER) == 0) { if (!cur_context && ((xct_flag & 1) != 0)) { - if (FLAGS & USERIO) { - if (fm_sel == 0) - goto read; - MB = FM[fm_sel|AB]; - return 0; - } - MB = M[ub_ptr + ac_stack + AB]; - return 0; + if (FLAGS & USERIO) { + if (fm_sel == 0) + goto read; + MB = FM[fm_sel|AB]; + } else { + MB = M[ub_ptr + ac_stack + AB]; + } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } + return 0; } } MB = get_reg(AB); } else { read: - if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch)) + if (!page_lookup(AB, flag, &addr, 0, cur_context, fetch, mod)) return 1; if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; sim_interval--; MB = M[addr]; + modify = mod; + last_addr = addr; + } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; } return 0; } @@ -2957,11 +3197,19 @@ int Mem_write(int flag, int cur_context) { } set_reg(AB, MB); } else { + if (modify) { + if (sim_brk_summ && sim_brk_test(last_addr, SWMASK('W'))) + watch_stop = 1; + M[last_addr] = MB; + modify = 0; + return 0; + } write: - if (!page_lookup(AB, flag, &addr, 1, cur_context, 0)) + if (!page_lookup(AB, flag, &addr, 1, cur_context, 0, 0)) return 1; if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) @@ -2994,6 +3242,7 @@ int its_load_tlb(uint32 reg, int page, uint32 *tlb) { if (entry >= MEMSIZE) { nxm_flag = 1; fault_data |= 0400; + check_apr_irq(); return 1; } sim_interval--; @@ -3018,7 +3267,7 @@ int its_load_tlb(uint32 reg, int page, uint32 *tlb) { * Translation logic for KA10 */ -int page_lookup_its(t_addr addr, int flag, t_addr *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, int modify) { uint64 data; int page = (RMASK & addr) >> 10; int acc; @@ -3031,13 +3280,6 @@ int page_lookup_its(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, 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; @@ -3048,6 +3290,9 @@ int page_lookup_its(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, } } + /* If this is modify instruction use write access */ + wr |= modify; + /* AC & 1 = ??? */ /* AC & 2 = Read User */ /* AC & 4 = Write User */ @@ -3060,6 +3305,7 @@ int page_lookup_its(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, case 1: if (fetch) { mem_prot = 1; fault_data |= 2; + check_apr_irq(); } break; case 2: if (!wr) @@ -3067,6 +3313,7 @@ int page_lookup_its(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, /* Fall through */ case 3: mem_prot = 1; fault_data |= 2; + check_apr_irq(); break; } } @@ -3140,6 +3387,7 @@ fault: } else { PC = (PC + 1) & RMASK; } + check_apr_irq(); return 0; } @@ -3148,43 +3396,54 @@ fault: * * Return of 0 if successful, 1 if there was an error. */ -int Mem_read_its(int flag, int cur_context, int fetch) { +int Mem_read_its(int flag, int cur_context, int fetch, int mod) { t_addr addr; if (AB < 020) { if ((xct_flag & 1) != 0 && !cur_context) { MB = M[(ac_stack & 01777777) + AB]; + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } MB = get_reg(AB); } else { - if (!page_lookup_its(AB, flag, &addr, 0, cur_context, fetch)) + if (!page_lookup_its(AB, flag, &addr, 0, cur_context, fetch, mod)) return 1; +#if NUM_DEVS_AUXCPU > 0 + if (AUXCPURANGE(addr) && QAUXCPU) { + if (auxcpu_read (addr, &MB)) { + nxm_flag = 1; + check_apr_irq(); + return 1; + } + } +#endif #if NUM_DEVS_TEN11 > 0 if (T11RANGE(addr) && QTEN11) { if (ten11_read (addr, &MB)) { nxm_flag = 1; + check_apr_irq(); return 1; } return 0; } -#endif -#if NUM_DEVS_AUXCPU > 0 - if (AUXCPURANGE(addr) && QAUXCPU) { - if (auxcpu_read (addr, &MB)) { - nxm_flag = 1; - return 1; - } - } #endif if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; sim_interval--; MB = M[addr]; + last_addr = addr; + modify = mod; + } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; } return 0; } @@ -3204,12 +3463,20 @@ int Mem_write_its(int flag, int cur_context) { } set_reg(AB, MB); } else { - if (!page_lookup_its(AB, flag, &addr, 1, cur_context, 0)) + if (modify) { + if (sim_brk_summ && sim_brk_test(last_addr, SWMASK('W'))) + watch_stop = 1; + M[last_addr] = MB; + modify = 0; + return 0; + } + if (!page_lookup_its(AB, flag, &addr, 1, cur_context, 0, 0)) return 1; #if NUM_DEVS_TEN11 > 0 if (T11RANGE(addr) && QTEN11) { if (ten11_write (addr, MB)) { nxm_flag = 1; + check_apr_irq(); return 1; } return 0; @@ -3219,12 +3486,14 @@ int Mem_write_its(int flag, int cur_context) { if (AUXCPURANGE(addr) && QAUXCPU) { if (auxcpu_write (addr, MB)) { nxm_flag = 1; + check_apr_irq(); return 1; } } #endif if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) @@ -3288,9 +3557,6 @@ int page_lookup_bbn(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, return 1; } - /* If this is modify instruction use write access */ - wr |= modify; - /* Umove instructions handled here */ if ((IR & 0774) == 0100 && (FLAGS & EXJSYS) == 0) uf = 1; @@ -3497,28 +3763,40 @@ 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 Mem_read_bbn(int flag, int cur_context, int fetch, int mod) { 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); + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } - if (!page_lookup_bbn(AB, flag, &addr, 0, cur_context, fetch)) + if (!page_lookup_bbn(AB, flag, &addr, mod, cur_context, fetch)) return 1; if (addr < 020) { MB = get_reg(AB); + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; sim_interval--; MB = M[addr]; + last_addr = addr; + modify = mod; + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } @@ -3535,6 +3813,13 @@ int Mem_write_bbn(int flag, int cur_context) { set_reg(AB, MB); return 0; } + if (modify) { + if (sim_brk_summ && sim_brk_test(last_addr, SWMASK('W'))) + watch_stop = 1; + M[last_addr] = MB; + modify = 0; + return 0; + } if (!page_lookup_bbn(AB, flag, &addr, 1, cur_context, 0)) return 1; if (addr < 020) { @@ -3543,6 +3828,7 @@ int Mem_write_bbn(int flag, int cur_context) { } if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) @@ -3584,6 +3870,7 @@ int page_lookup_waits(t_addr addr, int flag, t_addr *loc, int wr, int cur_contex } } mem_prot = 1; + check_apr_irq(); return 0; } else { *loc = addr; @@ -3591,23 +3878,32 @@ int page_lookup_waits(t_addr addr, int flag, t_addr *loc, int wr, int cur_contex return 1; } -int Mem_read_waits(int flag, int cur_context, int fetch) { +int Mem_read_waits(int flag, int cur_context, int fetch, int mod) { t_addr addr; if (AB < 020 && ((xct_flag == 0 || fetch || cur_context || (FLAGS & USER) != 0))) { MB = get_reg(AB); + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } - if (!page_lookup_waits(AB, flag, &addr, 0, cur_context, fetch)) + if (!page_lookup_waits(AB, flag, &addr, mod, cur_context, fetch)) return 1; if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) watch_stop = 1; sim_interval--; MB = M[addr]; + modify = mod; + last_addr = addr; + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } @@ -3625,10 +3921,18 @@ int Mem_write_waits(int flag, int cur_context) { set_reg(AB, MB); return 0; } + if (modify) { + if (sim_brk_summ && sim_brk_test(last_addr, SWMASK('W'))) + watch_stop = 1; + M[last_addr] = MB; + modify = 0; + return 0; + } if (!page_lookup_waits(AB, flag, &addr, 1, cur_context, 0)) return 1; if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) @@ -3653,6 +3957,7 @@ int page_lookup_ka(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, } } mem_prot = 1; + check_apr_irq(); return 0; } else { *loc = addr; @@ -3660,16 +3965,17 @@ int page_lookup_ka(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, return 1; } -int Mem_read_ka(int flag, int cur_context, int fetch) { +int Mem_read_ka(int flag, int cur_context, int fetch, int mod) { t_addr addr; if (AB < 020) { MB = get_reg(AB); } else { - if (!page_lookup_ka(AB, flag, &addr, 0, cur_context, fetch)) + if (!page_lookup_ka(AB, flag, &addr, mod, cur_context, fetch)) return 1; if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('R'))) @@ -3677,6 +3983,9 @@ int Mem_read_ka(int flag, int cur_context, int fetch) { sim_interval--; MB = M[addr]; } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } @@ -3696,6 +4005,7 @@ int Mem_write_ka(int flag, int cur_context) { return 1; if (addr >= MEMSIZE) { nxm_flag = 1; + check_apr_irq(); return 1; } if (sim_brk_summ && sim_brk_test(AB, SWMASK('W'))) @@ -3824,7 +4134,7 @@ int page_lookup(t_addr addr, int flag, t_addr *loc, int wr, int cur_context, int return 1; } -int Mem_read(int flag, int cur_context, int fetch) { +int Mem_read(int flag, int cur_context, int fetch, int mod) { t_addr addr; sim_interval--; @@ -3841,6 +4151,9 @@ int Mem_read(int flag, int cur_context, int fetch) { watch_stop = 1; MB = M[addr]; } + if (fetch == 0 && hst_lnt) { + hst[hst_p].mb = MB; + } return 0; } @@ -3881,11 +4194,12 @@ int Mem_read_nopage() { MB = get_reg(AB); } else { if (AB >= MEMSIZE) { -#if KL - irq_flags |= 02000; +#if KL | KS + irq_flags |= NXM_MEM; #else nxm_flag = 1; #endif + check_apr_irq(); return 1; } sim_interval--; @@ -3904,11 +4218,12 @@ int Mem_write_nopage() { set_reg(AB, MB); } else { if (AB >= MEMSIZE) { -#if KL - irq_flags |= 02000; +#if KL | KS + irq_flags |= NXM_MEM; #else nxm_flag = 1; #endif + check_apr_irq(); return 1; } sim_interval--; @@ -3923,7 +4238,7 @@ int Mem_write_nopage() { */ int Mem_read_word(t_addr addr, uint64 *data, int ept) { -#if KL | KI +#if KL | KI | KS if (ept) addr += eb_ptr; #endif @@ -3935,7 +4250,7 @@ int Mem_read_word(t_addr addr, uint64 *data, int ept) int Mem_write_word(t_addr addr, uint64 *data, int ept) { -#if KL | KI +#if KL | KI | KS if (ept) addr += eb_ptr; #endif @@ -3964,7 +4279,6 @@ int nlzero(uint64 w) { t_stat sim_instr (void) { t_stat reason; -int i_flags; /* Instruction mode flags */ int pi_rq; /* Interrupt request */ int pi_ov; /* Overflow during PI cycle */ int ind; /* Indirect bit */ @@ -3974,13 +4288,12 @@ int f_inst_fetch; /* Fetch new instruction */ int f_pc_inh; /* Inhibit PC increment after instruction */ int nrf; /* Normalize flag */ int fxu_hold_set; /* Negitive exponent */ -int sac_inh; /* Inihibit saving AC after instruction */ int f; /* Temporary variables */ int flag1; int flag3; int instr_count = 0; /* Number of instructions to execute */ -uint32 IA; /* Initial address of first fetch */ -#if ITS | KL_ITS +t_addr IA; /* Initial address of first fetch */ +#if ITS | KL_ITS | KS_ITS char one_p_arm = 0; /* One proceed arm */ #endif @@ -3989,10 +4302,13 @@ if (sim_step != 0) { sim_cancel_step(); } +#if KS +reason = SCPE_OK; +#else /* Build device table */ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ return reason; - +#endif /* Main instruction fetch/decode loop: check clock queue, intr, trap, bkpt */ f_load_pc = 1; @@ -4003,9 +4319,9 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ pi_rq = 0; pi_ov = 0; BYF5 = 0; -#if KI | KL +#if KI | KL | KS page_fault = 0; -#if KL +#if KL | KS ptr_flg = 0; #endif #endif @@ -4015,7 +4331,7 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ set_quantum(); } #endif -#if KL_ITS +#if KL_ITS | KS_ITS if (QITS) one_p_arm = 0; #endif @@ -4050,19 +4366,20 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ } #endif /* MAGIC_SWITCH */ - check_apr_irq(); /* Normal instruction */ if (f_load_pc) { modify = 0; xct_flag = 0; -#if KI | KL +#if KI | KL | KS trap_flag = 0; -#if KL - sect = cur_sect = pc_sect; - glb_sect = 0; +#endif +#if KL | KS extend = 0; ptr_flg = 0; #endif +#if KL + sect = cur_sect = pc_sect; + glb_sect = 0; #endif AB = PC; uuo_cycle = 0; @@ -4070,7 +4387,7 @@ if ((reason = build_dev_tab ()) != SCPE_OK) /* build, chk dib_tab */ } if (f_inst_fetch) { -#if !(KI | KL) +#if !(KI|KL) fetch: #endif #if ITS @@ -4082,18 +4399,31 @@ fetch: } } #endif - if (Mem_read(pi_cycle | uuo_cycle, 1, 1)) { +#if KS + if (fe_xct != 0) { + AB = (t_addr)fe_xct; + fe_xct = 0; + } +#if KS_ITS + if (QITS && pi_cycle == 0 && (FLAGS & ADRFLT) != 0) { + one_p_arm = 1; + FLAGS &= ~ADRFLT; + } +#endif +#endif + if (Mem_read(pi_cycle | uuo_cycle, 1, 1, 0)) { #if KA | PDP6 - pi_rq = check_irq_level(); - if (pi_rq) - goto st_pi; + pi_rq = check_irq_level(); + if (pi_rq) + goto st_pi; #endif #if KL - if (((fault_data >> 30) & 037) == 021) - PC = (PC + 1) & RMASK; + /* Handling for PUBLIC violation */ + if (((fault_data >> 30) & 037) == 021) + PC = (PC + 1) & RMASK; #endif - goto last; - } + goto last; + } no_fetch: IR = (MB >> 27) & 0777; @@ -4103,11 +4433,10 @@ no_fetch: #if KL glb_sect = 0; #endif - i_flags = opflags[IR]; BYF5 = 0; } -#if KI | KL +#if KI | KL | KS /* Handle page fault and traps */ if (page_enable && trap_flag == 0 && (FLAGS & (TRP1|TRP2))) { if (FLAGS & ADRFLT) { @@ -4152,24 +4481,18 @@ no_fetch: #endif /* Handle indirection repeat until no longer indirect */ do { - if ((!pi_cycle) & pi_pending -#if KI | KL - & (!trap_flag) -#endif - ) { - pi_rq = check_irq_level(); - } - ind = TST_IND(MB) != 0; - AR = MB; - AB = MB & RMASK; - ix = GET_XR(MB); - if (ix) { -#if KL + ind = TST_IND(MB) != 0; + AR = MB; + AB = MB & RMASK; + ix = GET_XR(MB); + if (ix) { +#if KL | KS if (((xct_flag & 8) != 0 && !ptr_flg) || ((xct_flag & 2) != 0 && ptr_flg)) AR = FM[prev_ctx|ix]; else AR = get_reg(ix); +#if KL /* 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); @@ -4178,11 +4501,17 @@ no_fetch: AB = 0; } else glb_sect = 0; - /* For KL */ +#endif + /* For KL and KS */ AR = MB = (AB + AR) & FMASK; #else - /* For KA & KI */ + /* For KA & KI & PDP6 */ AR = MB = (AB + get_reg(ix)) & FMASK; +#endif +#if KS + if (ind == 0 && (IR & 0700) == 0700) + AB = MB & (IOCTL|RMASK); + else #endif AB = MB & RMASK; } @@ -4190,7 +4519,7 @@ no_fetch: in_loop: #endif if (ind & !pi_rq) { - if (Mem_read(pi_cycle | uuo_cycle, 1, 0)) + if (Mem_read(pi_cycle | uuo_cycle, 1, 0, 0)) goto last; #if KL /* Check if extended indexing */ @@ -4198,7 +4527,7 @@ in_loop: 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); + (MB & RMASK) | ((uint64)cur_sect << 18); page_fault = 1; goto last; } @@ -4254,6 +4583,20 @@ in_loop: if (ind) goto in_loop; } +#endif +#if KS +#if KS_ITS + /* ITS I/O instruction are standard */ + /* Instructions don't do repeat indirect */ + if (!QITS && (IR & 0700) == 0700) { +#else + /* I/O Instructions don't do repeat indirect */ + if ((IR & 0700) == 0700) { +#endif + AR = MB & (IOCTL|RMASK); + AB = (t_addr)AR; + ind = 0; + } #endif } /* Handle events during a indirect loop */ @@ -4263,42 +4606,67 @@ in_loop: return reason; } } + + if ((!pi_cycle) & pi_pending +#if KI | KL | KS + & (!trap_flag) +#endif + ) { + pi_rq = check_irq_level(); + } } while (ind & !pi_rq); - /* If not a JRST clear the upper half of AR. */ - if (IR != 0254) { - AR &= RMASK; - } - - /* If there is a interrupt handle it. */ if (pi_rq) { +#if KI | KL | KS + int pi_mask = (0200 >> pi_enc); +#endif #if KA | PDP6 st_pi: #endif +#if DEBUG sim_debug(DEBUG_IRQ, &cpu_dev, "trap irq %o %03o %03o \n", pi_enc, PIR, PIH); +#endif pi_cycle = 1; pi_rq = 0; pi_hold = 0; pi_ov = 0; AB = 040 | (pi_enc << 1) | maoff; xct_flag = 0; -#if KI | KL -#if KL - sect = cur_sect = 0; +#if KS + AB |= eb_ptr; extend = 0; + if ((dev_irq[0] & pi_mask) == 0) { + for (f = 1; f < MAX_DEV; f++) { + if (dev_irq[f] & pi_mask) { + AB = uba_get_vect(AB, pi_mask, f); + dev_irq[f] = 0; + break; + } +#if DEBUG + sim_debug(DEBUG_IRQ, &cpu_dev, "vect irq %o %06o\n", pi_enc, AB); #endif + } + } +#if KS_ITS + pi_act |= pi_mask; +#endif + pi_vect = AB; + goto fetch; +#endif +#if KI | KL /* * Scan through the devices and allow KI devices to have first * hit at a given level. */ - for (f = 0; f < 128; f++) { - if (dev_irqv[f] != 0 && dev_irq[f] & (0200 >> pi_enc)) { + for (f = 0; f < MAX_DEV; f++) { + if (dev_irqv[f] != 0 && dev_irq[f] & pi_mask) { AB = dev_irqv[f](f << 2, AB); - if (dev_irqv[f] != 0) +#if DEBUG sim_debug(DEBUG_IRQ, &cpu_dev, "vect irq %o %03o %06o\n", pi_enc, dev_irq[f], AB); +#endif break; } } @@ -4307,17 +4675,19 @@ st_pi: else AB |= eb_ptr; #if KL + sect = cur_sect = 0; + extend = 0; pi_vect = AB; #endif Mem_read_nopage(); goto no_fetch; -#else +#elif PDP6 | KA goto fetch; #endif } -#if KI | KL +#if KI | KL | KS if (page_enable && page_fault) { if (!f_pc_inh && !pi_cycle) PC = (PC + 1) & RMASK; @@ -4327,22 +4697,25 @@ st_pi: /* Check if possible idle loop */ if (sim_idle_enab && - (((FLAGS & USER) != 0 && PC < 020 && AB < 020 && (IR & 0760) == 0340) || + (((FLAGS & USER) != 0 && PC < 020 && AB < 020 && (IR & 0740) == 0340) || (uuo_cycle && (IR & 0740) == 0 && IA == 041))) { sim_idle (TMR_RTC, FALSE); } /* Update history */ - if (hst_lnt && PC > 017) { - hst_p = hst_p + 1; + if (hst_lnt) { + if (PC >= 020) + 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 KL | KS if (extend) hst[hst_p].pc |= HIST_PCE; +#endif +#if KL hst[hst_p].pc |= (pc_sect << 18); hst[hst_p].ea |= (sect << 18); #endif @@ -4357,7 +4730,7 @@ st_pi: | ill_op #endif #endif -#if KL +#if KL | KS | (fm_sel >> 4) #endif @@ -4365,6 +4738,7 @@ st_pi: #if KL hst[hst_p].prev_sect = prev_sect; #endif + hst[hst_p].mb = AR; hst[hst_p].ac = get_reg(AC); } @@ -4374,10 +4748,9 @@ st_pi: f_load_pc = 1; nrf = 0; fxu_hold_set = 0; - sac_inh = 0; modify = 0; f_pc_inh = 0; -#if KL +#if KL | KS if (extend) { if (IR == 0 || IR > 031 || AC != 0 || do_extend(IA)) { IR = 0123; @@ -4387,37 +4760,7 @@ st_pi: goto last; } #endif - /* Load pseudo registers based on flags */ - if (i_flags & (FCEPSE|FCE)) { - if (i_flags & FCEPSE) - modify = 1; - if (Mem_read(0, 0, 0)) - goto last; - AR = MB; - } - - if (i_flags & FAC) { - BR = AR; - AR = get_reg(AC); - } - - if (i_flags & FBR) { - BR = get_reg(AC); - } - - if (hst_lnt && PC >= 020) { - hst[hst_p].mb = AR; - } - - if (i_flags & FAC2) { - MQ = get_reg(AC + 1); - } else if (!BYF5) { - MQ = 0; - } - - if (i_flags & SWAR) { - AR = SWAP_AR; - } + BR = get_reg(AC); /* Process the instruction */ switch (IR) { @@ -4425,7 +4768,7 @@ st_pi: case 0052: /* PMOVE */ case 0053: /* PMOVEM */ if (QKLB && t20_page && (FLAGS & USER) == 0) { - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AB = MB & (SECTM|RMASK); if (IR & 1) { @@ -4459,17 +4802,21 @@ muuo: /* MUUO */ -#if KI | KL +#if KI | KL | KS case 0100: /* UJEN */ - case 0101: case 0102: case 0103: + case 0101: +#if !KS_ITS + case 0102: case 0103: +#endif case 0104: /* JSYS */ case 0106: case 0107: -#if !KL_ITS +#if !(KL_ITS | KS_ITS) case 0247: /* UUO */ #endif unasign: /* Save Opcode */ + modify = 0; #if KL if (QKLB && t20_page) { AR = (uint64)AB; /* Save address */ @@ -4486,9 +4833,14 @@ unasign: MB |= (uint64)(prev_sect); } } else +#elif KS + if (t20_page) { + AR = (uint64)AB; /* Save address */ + MB = (((uint64)((IR << 9) | (AC << 5))) | ((uint64)(FLAGS) << 23)) & FMASK; + } else #endif MB = ((uint64)(IR) << 27) | ((uint64)(AC) << 23) | (uint64)(AB); - AB = ub_ptr | 0424; + AB = ub_ptr + 0424; #if KL /* If single sections KL10 UUO starts at 425 */ if (!QKLB && !QITS && t20_page) @@ -4502,22 +4854,31 @@ unasign: MB = ((uint64)(pc_sect) << 18) | ((PC + (trap_flag == 0)) & RMASK); else { MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + (trap_flag == 0)) & RMASK); + /* Save public setting */ if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; } } +#elif KS + if (t20_page) + MB = (PC + (trap_flag == 0)) & RMASK; + else + MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + (trap_flag == 0)) & RMASK); #else MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + (trap_flag == 0)) & RMASK); +#if KI + /* Save public setting */ if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; } +#endif #endif Mem_write_nopage(); #if KL extend = 0; - if (QKLB && t20_page) { /* Save address */ + if (QKLB && t20_page) { /* Restore address */ if (pc_sect != 0 && glb_sect == 0 && AR < 020) AR |= BIT17; else @@ -4537,14 +4898,40 @@ unasign: prev_sect = pc_sect & 037; } Mem_write_nopage(); +#elif KS + extend = 0; + if (t20_page) { /* Restore address */ + MB = AR; + AB ++; + Mem_write_nopage(); + /* Save context */ + AB ++; + MB = SMASK| + ((uint64)(fm_sel & 0160) << 23) | + ((uint64)(prev_ctx & 0160) << 20) | + (ub_ptr >> 9); + Mem_write_nopage(); +#if KS_ITS + } else if (QITS) { + /* Save context */ + AB ++; + MB = SMASK|BIT2| + ((uint64)(fm_sel & 0160) << 23) | + ((uint64)(prev_ctx & 0160) << 20) | + ub_ptr & 03777777; + Mem_write_nopage(); +#endif + } #endif /* Read in new PC and flags */ FLAGS &= ~ (PRV_PUB|BYTI|ADRFLT|TRP1|TRP2); - AB = ub_ptr | 0430; + AB = ub_ptr + 0430; if (trap_flag != 0) AB |= 1; +#if !KS if (FLAGS & PUBLIC) AB |= 2; +#endif if (FLAGS & USER) AB |= 4; Mem_read_nopage(); @@ -4560,8 +4947,10 @@ unasign: if ((FLAGS & USER) == 0) { if ((AB & 4) != 0) FLAGS |= USERIO; +#if !KS if ((AB & 2 || (FLAGS & OVR) != 0)) FLAGS |= PRV_PUB|OVR; +#endif } PC = MB & RMASK; f_pc_inh = 1; @@ -4705,23 +5094,38 @@ unasign: } goto unasign; #endif +#if KS_ITS + case 0102: /* XCTI */ + case 0103: /* XCT */ + if (QITS && (FLAGS & USER) == 0) { + f_load_pc = 0; + f_pc_inh = 1; + xct_flag = AC; + break; + } + goto unasign; +#endif -#if KI | KL -#if KL +#if KI | KL | KS +#if KL | KS case 0105: /* ADJSP */ - BR = get_reg(AC); + AR &= RMASK; +#if KL 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 { +#endif AD = (BR + AR) & RMASK; AD |= (BR & LMASK) + ((AR << 18) & LMASK); +#if KL if (QKLB && pc_sect == 0 && ((BR ^ AD) & SMASK) != 0) FLAGS |= TRP2; } - i_flags = SAC; +#endif AR = AD & FMASK; + set_reg(AC, AR); break; #endif @@ -4730,7 +5134,7 @@ unasign: /* On Load AR,MQ has memory operand */ /* AR,MQ = AC BR,MB = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; BR = AR; @@ -4738,7 +5142,7 @@ unasign: MQ = get_reg(AC + 1); AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; /* Make into 64 bit numbers */ SC = GET_EXPO(BR); @@ -4849,14 +5253,14 @@ dpnorm: /* On Load AR,MQ has memory operand */ /* AR,MQ = AC BR,MB = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; BR = AR; AR = get_reg(AC); MQ = get_reg(AC + 1); AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; /* Make into 64 bit numbers */ SC = GET_EXPO(AR); @@ -4921,7 +5325,7 @@ dpnorm: if ((AR & 0777) == 0777) AR &= (FPFMASK << 8); } -#if KL +#if KL | KS while (((AR & (FPSBIT|FPNBIT)) == (FPSBIT|FPNBIT)) || ((AR & (FPSBIT|FPNBIT)) == 0)) { #else @@ -4936,7 +5340,7 @@ dpnorm: MQ &= FMASK; nrf = 1; } -#if KL +#if KL | KS /* Handle special minus case */ if (AR == (FPHBIT|FPSBIT)) { SC += 1; @@ -4979,14 +5383,14 @@ dpnorm: /* On Load AR,MQ has memory operand */ /* AR,MQ = AC BR,MB = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; BR = AR; AR = get_reg(AC); MQ = get_reg(AC + 1); AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; /* Make into 64 bit numbers */ SC = GET_EXPO(AR); @@ -4997,7 +5401,7 @@ dpnorm: SMEAR_SIGN(BR); BR <<= 35; BR |= MB & CMASK; -#if KL +#if KL | KS /* One extra bit for KL */ AR <<= 1; BR <<= 1; @@ -5017,12 +5421,10 @@ dpnorm: if (!pi_cycle) FLAGS |= OVR|FLTOVR|NODIV|TRP1; AR = 0; /* For clean history */ - sac_inh = 1; break; } /* Divide by zero */ if (AR == 0) { - sac_inh = 1; break; } /* Compute exponents */ @@ -5036,7 +5438,7 @@ dpnorm: FLAGS |= FLTUND|OVR|FLTOVR|TRP1; /* Do divide */ AD = 0; - for (FE = 0; FE < (62 + KL); FE++) { + for (FE = 0; FE < (62 + KL + KS); FE++) { AD <<= 1; if (AR >= BR) { AR = AR - BR; @@ -5049,9 +5451,9 @@ dpnorm: if (flag1) { AR = (AR ^ FPFMASK) + 1; } -#if KL +#if KL | KS else - AR++; /* Round on KL */ + AR++; /* Round on KL & KS */ AR = (AR & FPHBIT) | (AR >> 1); /* Remove extra bit */ #endif /* Check potential overflow */ @@ -5084,16 +5486,16 @@ dpnorm: set_reg(AC+1, MQ); break; -#if KL +#if KL | KS case 0114: /* DADD */ flag1 = flag3 = 0; /* AR,ARX = AC BR,BX = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BRX = MB; AR = get_reg(AC); @@ -5115,7 +5517,6 @@ dpnorm: if (flag1 != flag3) { if (!pi_cycle) FLAGS |= OVR|TRP1; - check_apr_irq(); } ARX &= CMASK; ARX |= AR & SMASK; @@ -5127,11 +5528,11 @@ dpnorm: flag1 = flag3 = 0; /* AR,AX = AC BR,BX = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BRX = MB; AR = get_reg(AC); @@ -5153,7 +5554,6 @@ dpnorm: if (flag1 != flag3) { if (!pi_cycle) FLAGS |= OVR|TRP1; - check_apr_irq(); } ARX &= CMASK; ARX |= AR & SMASK; @@ -5165,11 +5565,11 @@ dpnorm: flag1 = flag3 = 0; /* AR,ARX = AC BR,BRX = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BRX = MB; AR = get_reg(AC); @@ -5183,7 +5583,11 @@ dpnorm: flag1 = 1; /* Can only occur if 2**-70 */ if (BR & SMASK) +#if KL FLAGS |= OVR|TRP1; +#else + flag3 = 1; +#endif } /* Make AR,ARX positive */ if (AR & SMASK) { @@ -5194,7 +5598,11 @@ dpnorm: flag1 ^= 1; /* Can only occur if 2**-70 */ if (AR & SMASK) +#if KL FLAGS |= OVR|TRP1; +#else + flag3 |= 2; +#endif } /* Form product in AD,ADX,BR,BX */ AD = ADX = 0; @@ -5227,6 +5635,10 @@ dpnorm: ADX = CCM(ADX) + ((BR & SMASK) != 0); AD = CM(AD) + ((ADX & SMASK) != 0); } +#if KS + if (flag3 == 3) + FLAGS |= OVR|TRP1; +#endif /* Copy signs */ BRX &= CMASK; BR &= CMASK; @@ -5246,11 +5658,11 @@ dpnorm: flag1 = flag3 = 0; /* AR,ARX = AC BR,BRX = mem */ /* AR High */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BRX = MB; /* Make BR,BX positive */ @@ -5266,7 +5678,11 @@ dpnorm: } } if ((BR | BRX) == 0) { +#if KL FLAGS |= NODIV; +#else + FLAGS |= NODIV|OVR|TRP1; +#endif break; } /* Get dividend */ @@ -5355,11 +5771,11 @@ dpnorm: #endif case 0120: /* DMOVE */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; MQ = MB; set_reg(AC, AR); @@ -5367,15 +5783,15 @@ dpnorm: break; case 0121: /* DMOVN */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; MQ = CCM(MB) + 1; /* Low */ /* High */ -#if KL +#if KL | KS flag1 = flag3 = 0; if ((CCM(AR) + ((MQ & SMASK) != 0)) & SMASK) { FLAGS |= CRY1; @@ -5384,14 +5800,13 @@ dpnorm: #endif AR = (CM(AR) + ((MQ & SMASK) != 0)); MQ &= CMASK; -#if KL +#if KL | KS 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; @@ -5402,14 +5817,19 @@ dpnorm: break; case 0123: /* Extend */ -#if KL +#if KL | KS +#if KS_ITS + if (QITS) { + goto unasign; + } +#endif /* 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)) + if (Mem_read(0, 1, 0, 0)) goto last; goto no_fetch; #else @@ -5417,27 +5837,40 @@ dpnorm: #endif case 0124: /* DMOVEM */ - AR = get_reg(AC); -#if KL +#if KS MQ = get_reg(AC + 1); -#endif + if ((FLAGS & BYTI) == 0) { + IA = AB; + AB = (AB + 1) & RMASK; + MB = MQ; + if (Mem_write(0, 0)) + goto last; + AB = IA; + FLAGS |= BYTI; + } + if ((FLAGS & BYTI)) { + MB = BR; + if (Mem_write(0, 0)) + goto last; + FLAGS &= ~BYTI; + } +#else /* Handle each half as seperate instruction */ if ((FLAGS & BYTI) == 0) { - MB = AR; + MB = BR; if (Mem_write(0, 0)) 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; } +#endif break; case 0125: /* DMOVNM */ @@ -5449,7 +5882,7 @@ dpnorm: BR = BR + 1; MQ = CCM(MQ) + 1; if (MQ & SMASK) { -#if KL +#if KL | KS flag1 = flag3 = 0; if ((CCM(get_reg(AC)) + 1) & SMASK) { FLAGS |= CRY1; @@ -5457,25 +5890,32 @@ dpnorm: } #endif AR = BR; -#if KL +#if KL | KS 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 } +#if KS + IA = AB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0, 1)) + goto last; + AB = IA; + modify = 0; +#endif AR &= FMASK; MB = AR; if (Mem_write(0, 0)) goto last; FLAGS |= BYTI; -#if KL +#if KL | KS AB = (AB + 1) & RMASK; MB = MQ & CMASK; if (Mem_write(0, 0)) @@ -5497,7 +5937,7 @@ dpnorm: case 0122: /* FIX */ case 0126: /* FIXR */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; MQ = 0; @@ -5520,19 +5960,18 @@ dpnorm: } if (((IR & 04) != 0 && (MQ & SMASK) != 0) || ((IR & 04) == 0 && (AR & SMASK) != 0 && - ((MQ & CMASK) != 0 || (MQ & SMASK) != 0))) + ( MQ != 0))) AR ++; } else { if (!pi_cycle) FLAGS |= OVR|TRP1; /* OV & T1 */ - sac_inh = 1; + break; } - if (!sac_inh) - set_reg(AC, AR); + set_reg(AC, AR & FMASK); break; case 0127: /* FLTR */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; AR <<= 27; @@ -5541,19 +5980,18 @@ dpnorm: AR |= FPHBIT; } else flag1 = 0; - i_flags = SAC; SC = 162; goto fnorm; #else case 0100: /* TENEX UMOVE */ #if BBN if (QBBN) { - if (Mem_read(0, 0, 0)) { + if (Mem_read(0, 0, 0, 0)) { IR = 0; goto last; } AR = MB; - set_reg(AC, AR); /* blank, I, B */ + set_reg(AC, AR); IR = 0; break; } @@ -5562,7 +6000,7 @@ dpnorm: case 0101: /* TENEX UMOVEI */ #if BBN if (QBBN) { - set_reg(AC, AR); /* blank, I, B */ + set_reg(AC, AR); IR = 0; break; } @@ -5667,10 +6105,8 @@ dpnorm: AR = get_reg(AC); MB = AR; if (Mem_write(0, 0)) { - IR = 0; goto last; } - IR = 0; break; } #endif @@ -5691,19 +6127,15 @@ dpnorm: #endif #if BBN if (QBBN) { - if (Mem_read(0, 0, 0)) { - IR = 0; + if (Mem_read(0, 0, 0, 1)) { goto last; } - modify = 1; AR = MB; if (Mem_write(0, 0)) { - IR = 0; goto last; } if (AC != 0) - set_reg(AC, AR); /* blank, I, B */ - IR = 0; + set_reg(AC, AR); break; } #endif @@ -5720,7 +6152,7 @@ dpnorm: FLAGS |= EXJSYS; FLAGS &= ~USER; } - if (Mem_read(0, 0, 0)) { + if (Mem_read(0, 0, 0, 0)) { FLAGS = (uint32)(BR >> 23); /* On error restore flags */ goto last; } @@ -5738,9 +6170,9 @@ dpnorm: goto unasign; case 0247: /* UUO or ITS CIRC instruction */ -#if ITS | KL_ITS +#if ITS | KL_ITS | KS_ITS if (QITS) { - BR = AR; + BR = AR & RMASK; AR = get_reg(AC); if (hst_lnt) { hst[hst_p].mb = AR; @@ -5766,6 +6198,7 @@ dpnorm: #endif #if WAITS if (QWAITS) { /* WAITS FIX instruction */ + AR &= RMASK; BR = get_reg(AC); if (hst_lnt) { hst[hst_p].mb = AR; @@ -5798,6 +6231,12 @@ unasign: FLAGS |= ONEP; one_p_arm = 0; } +#endif +#if KS_ITS + if (QITS && one_p_arm) { + FLAGS |= ADRFLT; + one_p_arm = 0; + } #endif f_load_pc = 0; #endif @@ -5805,9 +6244,9 @@ unasign: #endif case 0133: /* IBP/ADJBP */ -#if KL +#if KL | KS if (AC != 0) { /* ADJBP */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; SC = (AR >> 24) & 077; /* S */ @@ -5816,6 +6255,7 @@ unasign: FE = (AR >> 30) & 077; /* P */ f = 0; +#if KL if (QKLB && t20_page && pc_sect != 0 && FE > 36) { if (FE == 077) goto muuo; @@ -5823,6 +6263,7 @@ unasign: SC = _byte_adj[(FE - 37)].s; FE = _byte_adj[(FE - 37)].p; } +#endif left = (36 - FE) / SC; /* Number bytes left (36 - P)/S */ bpw = left + (FE / SC); /* Bytes per word */ if (bpw == 0) { @@ -5843,6 +6284,7 @@ unasign: adjw--; } FE = 36 - (adjb * SC) - ((36 - FE) % SC); /* New P */ +#if KL if (f) { /* Short pointer */ for (f = 0; f < 28; f++) { @@ -5858,7 +6300,7 @@ unasign: } else if (QKLB && t20_page && pc_sect != 0 && (AR & BIT12) != 0) { /* Full pointer */ AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = (((uint64)(FE & 077)) << 30) | /* Make new BP */ (AR & PMASK); /* S and below */ @@ -5876,6 +6318,7 @@ unasign: set_reg(AC+1, BR); break; } +#endif AR = (((uint64)(FE & 077)) << 30) | /* Make new BP */ (AR & PMASK & LMASK) | /* S,IX,I */ ((AR + adjw) & RMASK); @@ -5887,14 +6330,12 @@ unasign: case 0134: /* ILDB */ case 0136: /* IDPB */ if ((FLAGS & BYTI) == 0) { /* BYF6 */ -#if KL - if (Mem_read(0, 0, 0)) { +#if KL | KS + if (Mem_read(0, 0, 0, 1)) { #elif KI - modify = 1; - if (Mem_read(0, 1, 0)) { + if (Mem_read(0, 1, 0, 1)) { #else - modify = 1; - if (Mem_read(0, !QITS, 0)) { + if (Mem_read(0, !QITS, 0, 1)) { #endif #if PDP6 FLAGS |= BYTI; @@ -5936,7 +6377,7 @@ unasign: #if KL if (QKLB && t20_page && pc_sect != 0 && (AR & BIT12) != 0) { /* Full pointer */ AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; if (MB & SMASK) { if (MB & BIT1) { @@ -5953,7 +6394,7 @@ unasign: AB = (AB - 1) & RMASK; } else AR = (AR & LMASK) | ((AR + 1) & RMASK); -#elif KI +#elif KI | KS AR = (AR & LMASK) | ((AR + 1) & RMASK); #else AR = (AR + 1) & FMASK; @@ -5962,7 +6403,7 @@ unasign: AR &= PMASK; AR |= (uint64)(SCAD & 077) << 30; MB = AR; -#if KL +#if KL | KS if (Mem_write(0, 0)) #elif KI if (Mem_write(0, 1)) @@ -5972,7 +6413,6 @@ unasign: goto last; if ((IR & 04) == 0) break; - modify = 0; goto ldb_ptr; } /* Fall through */ @@ -5980,12 +6420,12 @@ unasign: case 0135:/* LDB */ case 0137:/* DPB */ if ((FLAGS & BYTI) == 0 || !BYF5) { -#if KL - if (Mem_read(0, 0, 0)) +#if KL | KS + if (Mem_read(0, 0, 0, 0)) #elif KI - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) #else - if (Mem_read(0, !QITS, 0)) + if (Mem_read(0, !QITS, 0, 0)) #endif goto last; AR = MB; @@ -6018,8 +6458,10 @@ ldb_ptr: #endif FLAGS |= BYTI; BYF5 = 1; -#if KL +#if KL | KS ptr_flg = 1; +#endif +#if KL if (QKLB && t20_page && (SC < 36) && pc_sect != 0 && (glb_sect || cur_sect != 0) && (AR & BIT12) != 0) { @@ -6034,17 +6476,25 @@ ldb_ptr: } #endif } else { -#if KL +#if KL | KS ptr_flg = 0; +#endif +#if KL ld_exe: +#endif + f = 0; +#if !KS +#if KL + if (!QKLB && (IR & 06) == 6) #else if ((IR & 06) == 6) - modify = 1; +#endif + f = 1; #endif AB = AR & RMASK; MQ = (uint64)(1) << SC; MQ -= 1; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, f)) goto last; AR = MB; if ((IR & 06) == 4) { @@ -6066,8 +6516,13 @@ ld_exe: } break; - case 0131:/* DFN */ + case 0131:/* DFN FCE|FAC */ + #if !PDP6 + if (Mem_read(0, 0, 0, 0)) + goto last; + BR = MB; + AR = get_reg(AC); AD = (CM(BR) + 1) & FMASK; SC = (BR >> 27) & 0777; BR = AR; @@ -6084,20 +6539,25 @@ ld_exe: #endif break; - case 0132:/* FSC */ + case 0132:/* FSC FAC|SAC */ + BR = AR & RMASK; + AR = get_reg(AC); SC = ((AB & RSIGN) ? 0400 : 0) | (AB & 0377); SCAD = GET_EXPO(AR); -#if KL +#if KL | KS 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 KS + if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) + fxu_hold_set = 1; +#endif #if PDP6 if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) fxu_hold_set = 1; @@ -6113,6 +6573,7 @@ ld_exe: SC = 0; AR &= SMASK|MMASK; AR |= ((uint64)((SC) & 0377)) << 27; + set_reg(AC, AR); break; #else SMEAR_SIGN(AR); @@ -6121,33 +6582,84 @@ ld_exe: #endif - case 0150: /* FSB */ - case 0151: /* FSBL */ - case 0152: /* FSBM */ - case 0153: /* FSBB */ - case 0154: /* FSBR */ - case 0155: /* FSBRI, FSBRL on PDP6 */ - case 0156: /* FSBRM */ - case 0157: /* FSBRB */ + case 0150: /* FSB */ /* SAC|FCE|FBR */ + case 0151: /* FSBL */ /* SAC|SAC2|FCE|FBR */ + case 0152: /* FSBM */ /* FCEPSE|FBR */ + case 0153: /* FSBB */ /* SAC|FBR|FCEPSE */ + case 0154: /* FSBR */ /* SAC|FCE|FBR */ + case 0155: /* FSBRI, FSBRL on PDP6 */ /* SAC|SWAR|FBR SAC|SAC2|FCE|FBR */ + case 0156: /* FSBRM */ /* FBR|FCEPSE */ + case 0157: /* FSBRB */ /* SAC|FBR|FCEPSE */ + + switch (IR & 07) { + case 5: +#if !PDP6 + AR &= RMASK; + AR = SWAP_AR; + break; +#endif + case 0: + case 1: + case 4: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 2: + case 3: + case 6: + case 7: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } + AD = (CM(AR) + 1) & FMASK; AR = BR; BR = AD; + goto fadd; /* Fall through */ #if !PDP6 - case 0130: /* UFA */ + case 0130: /* UFA */ /* FCE|FBR */ #endif #if WAITS ufa: #endif - case 0140: /* FAD */ - case 0141: /* FADL */ - case 0142: /* FADM */ - case 0143: /* FADB */ - case 0144: /* FADR */ - case 0145: /* FADRI, FSBRL on PDP6 */ - case 0146: /* FADRM */ - case 0147: /* FADRB */ + case 0140: /* FAD */ /* SAC|FCE|FBR */ + case 0141: /* FADL */ /* SAC|SAC2|FCE|FBR */ + case 0142: /* FADM */ /* FCEPSE|FBR */ + case 0143: /* FADB */ /* SAC|FBR|FCEPSE */ + case 0144: /* FADR */ /* SAC|FCE|FBR */ + case 0145: /* FADRI FADRL on PDP6 */ /* SAC|SWAR|FBR SAC|SAC2|FCE|FBR */ + case 0146: /* FADRM*/ /* FBR|FCEPSE */ + case 0147: /* FADRB*/ /* SAC|FBR|FCEPSE */ + switch (IR & 07) { + case 5: +#if !PDP6 + AR &= RMASK; + AR = SWAP_AR; + break; +#endif + case 0: + case 1: + case 4: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 2: + case 3: + case 6: + case 7: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } + +fadd: flag3 = 0; SC = ((BR >> 27) & 0777); if ((BR & SMASK) == (AR & SMASK)) { @@ -6173,7 +6685,7 @@ ufa: /* Get exponent */ SC = GET_EXPO(AR); -#if KL +#if KL | KS SC |= (SC & 0400) ? 0777000 : 0; /* Extend sign */ #endif /* Smear the signs */ @@ -6214,7 +6726,7 @@ fnorm: #if !PDP6 AR &= ~077; /* Save one extra bit */ #endif -#if !KL +#if !(KL | KS) if (((SC & 0400) != 0) ^ ((SC & 0200) != 0)) fxu_hold_set = 1; #endif @@ -6270,7 +6782,7 @@ fnormx: AR = MQ = 0; SC = 0; } -#if KL +#if KL | KS if (!pi_cycle && (SC & 0400) != 0) { FLAGS |= OVR|FLTOVR|TRP1; if ((SC & RSIGN) != 0) @@ -6285,7 +6797,9 @@ fnormx: MQ = 0; } #endif +#if PDP6 | KA check_apr_irq(); +#endif } #endif #if WAITS @@ -6331,25 +6845,85 @@ fnormx: /* Handle UFA */ if (IR == 0130) { set_reg(AC + 1, AR); + break; + } + if (IR == 0127 || IR == 0132) { /* FLTR */ + set_reg(AC, AR & FMASK); + break; + } + switch (IR & 07) { +#if PDP6 + case 5: +#endif + case 1: + set_reg(AC + 1, MQ & FMASK); + /* Fall through */ +#if !PDP6 + case 5: +#endif + case 0: + case 4: + set_reg(AC, AR & FMASK); + break; + case 2: + case 6: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 3: + case 7: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR & FMASK); + break; } break; - case 0160: /* FMP */ - case 0161: /* FMPL */ - case 0162: /* FMPM */ - case 0163: /* FMPB */ - case 0164: /* FMPR */ - case 0165: /* FMPRI, FMPRL on PDP6 */ - case 0166: /* FMPRM */ - case 0167: /* FMPRB */ + case 0160: /* FMP */ /* SAC|FCE|FBR */ + case 0161: /* FMPL */ /* SAC|SAC2|FCE|FBR */ + case 0162: /* FMPM */ /* FCEPSE|FBR */ + case 0163: /* FMPB */ /* SAC|FBR|FCEPSE */ + case 0164: /* FMPR */ /* SAC|FCE|FBR */ + case 0165: /* FMPRI FMPRL on PDP6 */ /* SAC|SWAR|FBR SAC|SAC2|FCE|FBR */ + case 0166: /* FMPRM */ /* FBR|FCEPSE */ + case 0167: /* FMPRB */ /* SAC|FBR|FCEPSE */ + switch (IR & 07) { + case 5: +#if !PDP6 + AR &= RMASK; + AR = SWAP_AR; + break; +#endif + case 0: + case 1: + case 4: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 2: + case 3: + case 6: + case 7: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } + /* Compute exponent */ 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 */ + SCAD = (((AR & SMASK) ? 0777 : 0) ^ (AR >> 27)) & 0777; +#if KL | KS + SC |= (SC & 0400) ? 0777000 : 0; /* Extend sign */ + SCAD |= (SCAD & 0400) ? 0777000 : 0; /* Extend sign */ + SC = (SC + SCAD + (RMASK ^ 0200) + 1) & RMASK; #else - SC &= 0777; + SC = (SC + SCAD + 0600) & 0777; #endif /* Make positive and compute result sign */ flag1 = 0; @@ -6384,15 +6958,39 @@ fnormx: #endif goto fnorm; - case 0170: /* FDV */ - case 0172: /* FDVM */ - case 0173: /* FDVB */ - case 0174: /* FDVR */ + case 0170: /* FDV */ /* SAC|FCE|FBR */ + case 0172: /* FDVM */ /* FCEPSE|FBR */ + case 0173: /* FDVB */ /* SAC|FCEPSE|FBR */ + case 0174: /* FDVR */ /* SAC|FBR|FCE */ #if !PDP6 - case 0175: /* FDVRI */ + case 0175: /* FDVR FDVL on PDP6 */ /* SAC|SWAR|FBR */ #endif - case 0176: /* FDVRM */ - case 0177: /* FDVRB */ + case 0176: /* FDVRM*/ /* FBR|FCEPSE */ + case 0177: /* FDVRB */ /* SAC|FBR|FCEPSE */ + switch (IR & 07) { + case 5: +#if !PDP6 + AR &= RMASK; + AR = SWAP_AR; + break; +#endif + case 0: + case 1: + case 4: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 2: + case 3: + case 6: + case 7: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } + flag1 = 0; flag3 = 0; SC = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777); @@ -6403,6 +7001,9 @@ fnormx: SC--; } else { AR = BR; +#if KS + FLAGS |= NODIV|TRP1; +#endif break; } } @@ -6418,8 +7019,10 @@ fnormx: AR = CM(AR) + 1; flag1 = !flag1; } -#if KL - SC = (SC + ((0777 ^ SCAD) + 1) + 0201); +#if KL | KS + SC |= (SC & 0400) ? 0777000 : 0; /* Extend sign */ + SCAD |= (SCAD & 0400) ? 0777000 : 0; /* Extend sign */ + SC = (SC + ((RMASK ^ SCAD) + 1) + 0201) & RMASK; #else SC = (SC + ((0777 ^ SCAD) + 1) + 0201) & 0777; #endif @@ -6428,18 +7031,20 @@ fnormx: BR &= MMASK; /* Check if we need to fix things */ if (BR >= (AR << 1)) { - if (!pi_cycle) + if (!pi_cycle) { FLAGS |= OVR|NODIV|FLTOVR|TRP1; - check_apr_irq(); - sac_inh = 1; +#if PDP6 | KA + check_apr_irq(); +#endif + } break; /* Done */ } BR = (BR << 28); MB = AR; AR = BR / AR; if (AR != 0) { -#if !PDP6 -#if KL +#if KL | KS + /* KL and KS code */ if (flag1) { AR = ((AR ^ FMASK) + 1) & FMASK; } @@ -6457,14 +7062,17 @@ fnormx: SC--; } AR &= FMASK; +#if KL | KS if ((SC & 01600) != 01600) fxu_hold_set = 1; +#endif if (AR == (SMASK|EXPO)) { AR = (AR >> 1) | (AR & SMASK); SC ++; } AR &= SMASK|MMASK; -#else +#elif KA | KI + /* KA and KI code */ if ((AR & BIT7) != 0) { AR >>= 1; } else { @@ -6480,8 +7088,8 @@ fnormx: AR <<= 1; SC--; } -#endif -#else +#elif PDP6 + /* PDP6 code */ if (flag1) { AR = ((AR ^ FMASK) + 1) & FMASK; if ((AR & BIT7) == 0) { @@ -6517,14 +7125,19 @@ fnormx: AR = 0; SC = 0; } - if (((SC & 0400) != 0) && !pi_cycle) { - FLAGS |= OVR|FLTOVR|TRP1; - if (!fxu_hold_set) { + if (!pi_cycle && (SC & 0400) != 0) { + FLAGS |= OVR|FLTOVR; +#if KL | KS + if ((SC & RSIGN) != 0) +#else + if (!fxu_hold_set) +#endif FLAGS |= FLTUND; - } +#if PDP6 | KA check_apr_irq(); +#endif } -#if !PDP6 & !KL +#if !(PDP6 | KL | KS) if (flag1) { AR = ((AR ^ MMASK) + 1) & MMASK; AR |= SMASK; @@ -6532,11 +7145,48 @@ fnormx: #endif SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); AR |= ((uint64)(SCAD & 0377)) << 27; + switch (IR & 07) { +#if PDP6 + case 5: +#endif + case 1: + set_reg(AC + 1, MQ & FMASK); + /* Fall through */ +#if !PDP6 + case 5: +#endif + case 0: + case 4: + set_reg(AC, AR & FMASK); + break; + case 2: + case 6: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 3: + case 7: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR & FMASK); + break; + } break; - case 0171: /* FDVL */ -#if PDP6 - case 0175: /* FDVRL */ + case 0171: /* FDVL */ /* SAC|SAC2|FAC2|FCE|FBR */ +#if KS + goto muuo; +#elif PDP6 + case 0175: /* FDVRL */ /* SAC|SAC2|FAC2||FCE|FBR */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + MQ = get_reg(AC + 1); + flag1 = flag3 = 0; MQ = 0; if (BR & SMASK) { @@ -6644,6 +7294,11 @@ left: AR |= ((uint64)(SCAD & 0377)) << 27; #else + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + MQ = get_reg(AC + 1); + flag1 = flag3 = 0; SC = (int)((((BR & SMASK) ? 0777 : 0) ^ (BR >> 27)) & 0777); SC += (int)((((AR & SMASK) ? 0 : 0777) ^ (AR >> 27)) & 0777); @@ -6667,10 +7322,12 @@ left: BR &= MMASK; /* Check if we need to fix things */ if (BR >= (AR << 1)) { - if (!pi_cycle) + if (!pi_cycle) { FLAGS |= OVR|NODIV|FLTOVR|TRP1; - check_apr_irq(); - sac_inh = 1; +#if KA + check_apr_irq(); +#endif + } break; /* Done */ } BR = (BR << 27) + MQ; @@ -6721,7 +7378,9 @@ left: if (!fxu_hold_set) { FLAGS |= FLTUND; } +#if KA check_apr_irq(); +#endif } SCAD = SC ^ ((AR & SMASK) ? 0377 : 0); AR &= SMASK|MMASK; @@ -6741,66 +7400,273 @@ left: MQ |= ((uint64)(FE & 0377)) << 27; } #endif + set_reg(AC + 1, MQ & FMASK); + set_reg(AC, AR & FMASK); break; /* FWT */ - case 0200: /* MOVE */ - case 0201: /* MOVEI */ - case 0202: /* MOVEM */ - case 0203: /* MOVES */ - case 0204: /* MOVS */ - case 0205: /* MOVSI */ - case 0206: /* MOVSM */ - case 0207: /* MOVSS */ - case 0503: /* HLLS */ - case 0543: /* HRRS */ + case 0200: /* MOVE */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) { + goto last; + } + AR = MB; + set_reg(AC, AR); break; - case 0214: /* MOVM */ - case 0215: /* MOVMI */ - case 0216: /* MOVMM */ - case 0217: /* MOVMS */ - if ((AR & SMASK) == 0) - break; - /* Fall though */ + case 0201: /* MOVEI */ /* SAC */ + AR &= RMASK; + set_reg(AC, AR); + break; + + case 0202: /* MOVEM */ /* FAC|SCE */ + MB = BR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0203: /* MOVES */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) { + goto last; + } + AR = MB; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0204: /* MOVS */ /* SWAR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) { + goto last; + } + AR = MB; + AR = SWAP_AR; + set_reg(AC, AR); + break; + + case 0205: /* MOVSI */ /* SWAR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + set_reg(AC, AR); + break; + + case 0206: /* MOVSM */ /* SWAR|FAC|SCE */ + AR = get_reg(AC); + AR = SWAP_AR; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0207: /* MOVSS */ /* SWAR|SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) { + goto last; + } + AR = MB; + AR = SWAP_AR; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; - case 0210: /* MOVN */ - case 0211: /* MOVNI */ - case 0212: /* MOVNM */ - case 0213: /* MOVNS */ - flag1 = flag3 = 0; - AD = CM(AR) + 1; - if ((CCM(AR) + 1) & SMASK) { #if !PDP6 - FLAGS |= CRY1; +#define NEG flag1 = flag3 = 0; \ + AD = CM(AR) + 1; \ + if ((CCM(AR) + 1) & SMASK) { \ + FLAGS |= CRY1; \ + flag1 = 1; \ + } \ + if (AD & C1) { \ + FLAGS |= CRY0; \ + flag3 = 1; \ + } \ + if (flag1 != flag3 && !pi_cycle) { \ + FLAGS |= OVR|TRP1; \ + } \ + AR = AD & FMASK; +#else +#define NEG flag1 = flag3 = 0; \ + AD = CM(AR) + 1; \ + if ((CCM(AR) + 1) & SMASK) { \ + flag1 = 1; \ + } \ + if (AD & C1) { \ + flag3 = 1; \ + } \ + if (flag1 != flag3 && !pi_cycle) { \ + FLAGS |= OVR|TRP1; \ + } \ + AR = AD & FMASK; #endif - flag1 = 1; - } - if (AD & C1) { -#if !PDP6 - FLAGS |= CRY0; + + case 0214: /* MOVM */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + if ((AR & SMASK) != 0) { + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; #endif - flag3 = 1; } - if (flag1 != flag3 && !pi_cycle) { - FLAGS |= OVR|TRP1; - check_apr_irq(); + set_reg(AC, AR); + break; + + case 0215: /* MOVMI */ /* SAC */ + AR &= RMASK; + set_reg(AC, AR); + break; + + case 0216: /* MOVMM */ /* FAC|SCE */ + AR = get_reg(AC); + if ((AR & SMASK) != 0) { + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; +#endif } -#if KI | KL + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0217: /* MOVMS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + if ((AR & SMASK) != 0) { + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; +#endif + } + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0210: /* MOVN */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS if (AR == SMASK && !pi_cycle) FLAGS |= TRP1; #endif - AR = AD & FMASK; + set_reg(AC, AR); break; - case 0220: /* IMUL */ - case 0221: /* IMULI */ - case 0222: /* IMULM */ - case 0223: /* IMULB */ - case 0224: /* MUL */ - case 0225: /* MULI */ - case 0226: /* MULM */ - case 0227: /* MULB */ + case 0211: /* MOVNI */ /* SAC */ + AR &= RMASK; + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; +#endif + set_reg(AC, AR); + break; + + case 0212: /* MOVNM */ /* SCE|FAC */ + AR = get_reg(AC); + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; +#endif + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0213: /* MOVNS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + NEG; +#if PDP6 | KA + if (flag1 != flag3 && !pi_cycle) + check_apr_irq(); +#endif +#if KI | KL | KS + if (AR == SMASK && !pi_cycle) + FLAGS |= TRP1; +#endif + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0220: /* IMUL */ /* SAC|FCE|FBR */ + case 0221: /* IMULI */ /* SAC|FBR */ + case 0222: /* IMULM */ /* FCEPSE|FBR */ + case 0223: /* IMULB */ /* SAC|FCEPSE|FBR */ + case 0224: /* MUL */ /* SAC2|SAC|FCE|FBR */ + case 0225: /* MULI */ /* SAC2|SAC|FBR */ + case 0226: /* MULM */ /* FCEPSE|FBR */ + case 0227: /* MULB */ /* SAC2|SAC|FCEPSR|FBR */ + switch (IR & 07) { + case 1: + case 5: + AR &= RMASK; + break; + case 0: + case 4: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 2: + case 3: + case 6: + case 7: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } + flag3 = 0; if (AR & SMASK) { AR = (CM(AR) + 1) & FMASK; @@ -6813,8 +7679,12 @@ left: if ((AR == 0) || (BR == 0)) { AR = MQ = 0; - break; + goto mul_done; } +#if KS + if (AR == SMASK && BR == SMASK) /* Handle special case */ + flag3 = !flag3; +#endif #if KA if (BR == SMASK) /* Handle special case */ flag3 = !flag3; @@ -6828,7 +7698,9 @@ left: if ((IR & 4) == 0) { /* IMUL */ if (AR > (uint64)flag3 && !pi_cycle) { FLAGS |= OVR|TRP1; +#if PDP6 | KA check_apr_irq(); +#endif } if (flag3) { MQ ^= CMASK; @@ -6836,11 +7708,13 @@ left: MQ |= SMASK; } AR = MQ; - break; + goto mul_done; } if ((AR & SMASK) != 0 && !pi_cycle) { FLAGS |= OVR|TRP1; +#if PDP6 | KA check_apr_irq(); +#endif } if (flag3) { AR ^= FMASK; @@ -6854,15 +7728,69 @@ left: AR &= FMASK; MQ = (MQ & ~SMASK) | (AR & SMASK); #if KA - if (BR == SMASK && (AR & SMASK)) /* Handle special case */ - FLAGS |= OVR; + if (BR == SMASK && (AR & SMASK)) { /* Handle special case */ + FLAGS |= OVR|TRP1; + check_apr_irq(); + } #endif +mul_done: + switch (IR & 07) { + case 7: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + /* Fall through */ + case 5: + case 4: + set_reg(AC + 1, MQ); + /* Fall through */ + case 1: + case 0: + set_reg(AC, AR); + break; + case 2: + case 6: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 3: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + } break; - case 0230: /* IDIV */ - case 0231: /* IDIVI */ - case 0232: /* IDIVM */ - case 0233: /* IDIVB */ + case 0230: /* IDIV */ /* SAC2|SAC|FCE|FAC */ + case 0231: /* IDIVI */ /* SAC2|SAC|FAC */ + case 0232: /* IDIVM */ /* FCEPSE|FAC */ + case 0233: /* IDIVB */ /* SAC2|SAC|FCEPSE|FAC */ + switch (IR & 03) { + case 0: + AR = BR; + if (Mem_read(0, 0, 0, 0)) + goto last; + BR = MB; + break; + case 1: + MQ = AR & RMASK; + AR = BR; + BR = MQ; + break; + case 2: + case 3: + AR = BR; + if (Mem_read(0, 0, 0, 1)) + goto last; + BR = MB; + break; + } + flag1 = 0; flag3 = 0; if (BR & SMASK) { @@ -6871,17 +7799,19 @@ left: } if (BR == 0) { /* Check for overflow */ - FLAGS |= OVR|NODIV; /* Overflow and No Divide */ - sac_inh=1; /* Don't touch AC */ + FLAGS |= OVR|NODIV|TRP1; /* Overflow and No Divide */ +#if PDP6 | KA check_apr_irq(); +#endif break; /* Done */ } #if !PDP6 if (AR == SMASK && BR == 1) { - FLAGS |= OVR|NODIV; /* Overflow and No Divide */ - sac_inh=1; /* Don't touch AC */ + FLAGS |= OVR|NODIV|TRP1; /* Overflow and No Divide */ +#if PDP6 | KA check_apr_irq(); +#endif break; /* Done */ } #else @@ -6904,12 +7834,53 @@ left: AR = (CM(AR) + 1) & FMASK; if (flag3) MQ = (CM(MQ) + 1) & FMASK; + switch (IR & 03) { + case 3: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + /* Fall through */ + case 1: + case 0: + set_reg(AC, AR); + set_reg(AC + 1, MQ); + break; + case 2: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + } break; - case 0234: /* DIV */ - case 0235: /* DIVI */ - case 0236: /* DIVM */ - case 0237: /* DIVB */ + case 0234: /* DIV */ /* SAC2|SAC|FCE|FAC|FAC2 */ + case 0235: /* DIVI */ /* SAC2|SAC|FAC|FAC2 */ + case 0236: /* DIVM */ /* FCEPSE|FAC|FAC2 */ + case 0237: /* DIVB */ /* SAC2|SAC|FCEPSE|FAC|FAC */ + switch (IR & 3) { + case 0: + AR = BR; + if (Mem_read(0, 0, 0, 0)) + goto last; + BR = MB; + break; + case 1: + MQ = AR & RMASK; + AR = BR; + BR = MQ; + break; + case 2: + case 3: + AR = BR; + if (Mem_read(0, 0, 0, 1)) + goto last; + BR = MB; + break; + } + MQ = get_reg(AC + 1); + flag1 = 0; if (AR & SMASK) { AD = (CM(MQ) + 1) & FMASK; @@ -6932,9 +7903,9 @@ left: SC = 35; if ((AD & SMASK) == 0) { FLAGS |= OVR|NODIV|TRP1; /* Overflow and No Divide */ - i_flags = 0; - sac_inh=1; +#if PDP6 | KA check_apr_irq(); +#endif break; /* Done */ } @@ -6975,13 +7946,33 @@ left: MQ = AR; AR = AD; } + switch (IR & 03) { + case 3: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + /* Fall through */ + case 1: + case 0: + set_reg(AC, AR); + set_reg(AC + 1, MQ); + break; + case 2: + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + } break; /* Shift */ - case 0240: /* ASH */ + case 0240: /* ASH */ /* FAC|SAC */ SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; if (SC == 0) break; + AR = BR; AD = (AR & SMASK) ? FMASK : 0; if (AB & RSIGN) { if (SC < 35) @@ -6991,13 +7982,16 @@ left: } else { if (((AD << SC) & ~CMASK) != ((AR << SC) & ~CMASK)) { FLAGS |= OVR|TRP1; +#if PDP6 | KA check_apr_irq(); +#endif } AR = ((AR << SC) & CMASK) | (AR & SMASK); } + set_reg(AC, AR); break; - case 0241: /* ROT */ + case 0241: /* ROT */ /* FAC|SAC */ SC = (AB & RSIGN) ? ((AB & 0377) ? (((0377 ^ AB) + 1) & 0377) : 0400) : (AB & 0377); @@ -7006,26 +8000,29 @@ left: SC = SC % 36; if (AB & RSIGN) SC = 36 - SC; - AR = ((AR << SC) | (AR >> (36 - SC))) & FMASK; + AR = ((BR << SC) | (BR >> (36 - SC))) & FMASK; + set_reg(AC, AR); break; - case 0242: /* LSH */ + case 0242: /* LSH */ /* FAC|SAC */ SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; if (SC != 0) { if (SC > 36){ - AR = 0; + BR = 0; } else if (AB & RSIGN) { - AR = AR >> SC; + BR = BR >> SC; } else { - AR = (AR << SC) & FMASK; + BR = (BR << SC) & FMASK; } } + AR = BR; + set_reg(AC, AR); break; - case 0243: /* JFFO */ + case 0243: /* JFFO */ /* FAC */ #if !PDP6 SC = 0; - if (AR != 0) { + if (BR != 0) { #if ITS | KL_ITS if (QITS && (FLAGS & USER)) { jpc = PC; @@ -7033,13 +8030,15 @@ left: #endif PC = AB; f_pc_inh = 1; - SC = nlzero(AR); + SC = nlzero(BR); } set_reg(AC + 1, SC); #endif break; - case 0244: /* ASHC */ + case 0244: /* ASHC */ /* FAC|SAC|SAC2|FAC2 */ + AR = BR; + MQ = get_reg(AC + 1); SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; if (SC == 0) break; @@ -7063,7 +8062,9 @@ left: #if !PDP6 if (((AD << SC) & ~CMASK) != ((AR << SC) & ~CMASK)) { FLAGS |= OVR|TRP1; +#if KA check_apr_irq(); +#endif } #endif AR = (AD & SMASK) | ((MQ << (SC - 35)) & CMASK); @@ -7071,16 +8072,22 @@ left: } else { if ((((AD & CMASK) << SC) & ~CMASK) != ((AR << SC) & ~CMASK)) { FLAGS |= OVR|TRP1; +#if PDP6 | KA check_apr_irq(); +#endif } AR = (AD & SMASK) | ((AR << SC) & CMASK) | (MQ >> (35 - SC)); MQ = (AD & SMASK) | ((MQ << SC) & CMASK); } } + set_reg(AC, AR); + set_reg(AC+1, MQ); break; - case 0245: /* ROTC */ + case 0245: /* ROTC */ /* FAC|SAC|SAC2|FAC2 */ + AR = BR; + MQ = get_reg(AC + 1); SC = (AB & RSIGN) ? ((AB & 0377) ? (((0377 ^ AB) + 1) & 0377) : 0400) : (AB & 0377); @@ -7098,9 +8105,13 @@ left: AD = ((AR << SC) | (MQ >> (36 - SC))) & FMASK; MQ = ((MQ << SC) | (AR >> (36 - SC))) & FMASK; AR = AD; + set_reg(AC, AR); + set_reg(AC+1, MQ); break; - case 0246: /* LSHC */ + case 0246: /* LSHC */ /* FAC|SAC|SAC2|FAC2 */ + AR = BR; + MQ = get_reg(AC + 1); SC = ((AB & RSIGN) ? (0377 ^ AB) + 1 : AB) & 0377; if (SC == 0) break; @@ -7126,47 +8137,59 @@ left: MQ = (MQ << SC) & FMASK; } } + set_reg(AC, AR); + set_reg(AC+1, MQ); break; /* Branch */ - case 0250: /* EXCH */ - MB = AR; + case 0250: /* EXCH */ /* FAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + MB = BR; if (Mem_write(0, 0)) { goto last; } - set_reg(AC, BR); + set_reg(AC, AR); break; - case 0251: /* BLT */ + case 0251: /* BLT */ /* FAC */ + AR = BR; BR = AB; +#if KL | KS + /* Precompute end of transfer address */ + AD = (CM(AR) + BR + 1) & RMASK; + AD = ((AR + (AD << 18)) & LMASK) | ((AR + AD) & RMASK); + set_reg(AC, AOB(AD)); +#endif do { AIO_CHECK_EVENT; /* queue async events */ if (sim_interval <= 0) { - 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) { - pi_rq = check_irq_level(); - if (pi_rq) { + 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) { + pi_rq = check_irq_level(); + if (pi_rq) { + f_pc_inh = 1; + f_load_pc = 0; + f_inst_fetch = 0; + set_reg(AC, AR); + break; + } + } } AB = (AR >> 18) & RMASK; -#if KL +#if KL | KS BYF5 = 1; #endif - if (Mem_read(0, 0, 0)) { -#if KL + if (Mem_read(0, 0, 0, 0)) { +#if KL | KS BYF5 = 0; #endif #if ITS @@ -7190,9 +8213,8 @@ left: goto last; } AB = (AR & RMASK); -#if KL +#if KL | KS BYF5 = 0; - set_reg(AC, AOB(AR)); #endif if (Mem_write(0, 0)) { #if ITS @@ -7220,8 +8242,8 @@ left: } while ((AD & C1) == 0); break; - case 0252: /* AOBJP */ - AR = AOB(AR); + case 0252: /* AOBJP */ /* FAC|SAC */ + AR = AOB(BR); if ((AR & SMASK) == 0) { #if ITS | KL_ITS if (QITS && (FLAGS & USER)) { @@ -7232,10 +8254,12 @@ left: PC = AB; f_pc_inh = 1; } + AR &= FMASK; + set_reg(AC, AR); break; - case 0253: /* AOBJN */ - AR = AOB(AR); + case 0253: /* AOBJN */ /* FAC|SAC */ + AR = AOB(BR); if ((AR & SMASK) != 0) { #if ITS | KL_ITS if (QITS && (FLAGS & USER)) { @@ -7246,11 +8270,13 @@ left: PC = AB; f_pc_inh = 1; } + AR &= FMASK; + set_reg(AC, AR); break; case 0254: /* JRST */ /* AR Frm PC */ -#if KL -#if KL_ITS +#if KL | KS +#if KL_ITS | KS_ITS if (uuo_cycle | pi_cycle) { if (QITS && one_p_arm) { FLAGS |= ADRFLT; @@ -7260,32 +8286,47 @@ left: #endif switch (AC) { case 000: /* JRST */ +#if KL + if (QKLB && t20_page) + pc_sect = sect; +#endif break; case 001: /* PORTAL */ +#if KL + if (QKLB && t20_page) + pc_sect = sect; FLAGS &= ~(PUBLIC|PRV_PUB); +#endif break; case 005: /* XJRSTF */ xjrstf: - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; BR = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; /* Get PC. */ +#if KL if (QKLB && t20_page) { pc_sect = (AR >> 18) & 07777; if (AC != 07 && (FLAGS & USER) == 0 && ((BR >> 23) & USER) == 0) prev_sect = BR & 037; } +#endif BR = BR >> 23; /* Move flags into position */ goto jrstf; case 006: /* XJEN */ case 012: /* JEN */ +#if KL /* Restore interrupt level. */ if ((FLAGS & (USER|USERIO)) == USER || (FLAGS & (USER|PUBLIC)) == PUBLIC) { +#else + /* Restore interrupt level. */ + if ((FLAGS & (USER|USERIO)) == USER) { +#endif goto muuo; } else { pi_restore = 1; @@ -7310,15 +8351,16 @@ jrstf: FLAGS &= ~USERIO; FLAGS |= BR & (OVR|NODIV|FLTUND|BYTI|FLTOVR|CRY1|CRY0|\ TRP1|TRP2|PUBLIC|PCHNG|ADRFLT); +#if KL FLAGS &= ~PRV_PUB; if ((FLAGS & USER) == 0) { FLAGS |= (BR & OVR) ? PRV_PUB : 0; } +#endif #if KL_ITS if (QITS) FLAGS |= f; #endif - check_apr_irq(); break; case 017: /* Invalid */ @@ -7334,22 +8376,28 @@ jrstf: case 007: /* XPCW */ MB = (((uint64)FLAGS) << 23) & FMASK; /* Save Previous Public context */ +#if KL if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; if (QKLB && t20_page) MB |= (uint64)(prev_sect & 037); } +#endif if (uuo_cycle | pi_cycle) { FLAGS &= ~(USER|PUBLIC); /* Clear USER */ +#if KL sect = 0; /* Force section zero on IRQ */ +#endif } if (Mem_write(0, 0)) goto last; AB = (AB + 1) & RMASK; +#if KL if (QKLB && t20_page) MB = (((((uint64)pc_sect) << 18) | PC) + !pi_cycle) & (SECTM|RMASK); else +#endif MB = (PC + !pi_cycle) & (RMASK); if (Mem_write(0, 0)) goto last; @@ -7357,22 +8405,31 @@ jrstf: goto xjrstf; case 015: /* XJRST */ - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto last; AR = MB; /* Get PC. */ +#if KL if (QKLB && t20_page) { pc_sect = (AR >> 18) & 07777; } +#endif break; case 014: /* SFM */ +#if KS + if ((FLAGS & USER) != 0) { + goto muuo; + } +#endif MB = (((uint64)FLAGS) << 23) & FMASK; +#if KL if ((FLAGS & USER) == 0) { MB &= ~SMASK; MB |= (FLAGS & PRV_PUB) ? SMASK : 0; if (QKLB && t20_page) MB |= (uint64)(prev_sect & 037); } +#endif (void)Mem_write(0, 0); goto last; @@ -7383,17 +8440,28 @@ jrstf: goto muuo; case 004: /* HALT */ +#if KL + /* Restore interrupt level. */ if ((FLAGS & (USER|USERIO)) == USER || (FLAGS & (USER|PUBLIC)) == PUBLIC) { +#else + /* Restore interrupt level. */ + if ((FLAGS & (USER|USERIO)) == USER) { +#endif goto muuo; } else { reason = STOP_HALT; } break; case 010: /* JEN */ +#if KL /* Restore interrupt level. */ if ((FLAGS & (USER|USERIO)) == USER || (FLAGS & (USER|PUBLIC)) == PUBLIC) { +#else + /* Restore interrupt level. */ + if ((FLAGS & (USER)) == USER) { +#endif goto muuo; } else { pi_restore = 1; @@ -7406,9 +8474,12 @@ jrstf: } #endif PC = AR & RMASK; +#if KL if (QKLB && t20_page && glb_sect) - pc_sect = (AR >> 18) & 07777; + pc_sect = (AR >> 18) & 07777; +#endif #else + /* JRST for PDP6, KA and KI */ if (uuo_cycle | pi_cycle) { FLAGS &= ~USER; /* Clear USER */ #if ITS @@ -7504,6 +8575,7 @@ jrstf: break; case 0256: /* XCT */ + f_load_pc = 0; f_pc_inh = 1; xct_flag = 0; @@ -7511,6 +8583,14 @@ jrstf: if (QBBN && (FLAGS & USER) == 0) xct_flag = AC; #endif +#if KS +#if KS_ITS + if (!QITS && (FLAGS & USER) == 0) +#else + if ((FLAGS & USER) == 0) +#endif + xct_flag = AC; +#endif #if KI | KL if ((FLAGS & USER) == 0) xct_flag = AC; @@ -7525,7 +8605,7 @@ jrstf: one_p_arm = 0; } #endif -#if KL_ITS +#if KL_ITS | KS_ITS if (QITS && one_p_arm) { FLAGS |= ADRFLT; one_p_arm = 0; @@ -7534,7 +8614,11 @@ jrstf: break; case 0257: /* MAP */ -#if KI | KL +#if KI | KL | KS +#if KS_ITS + if (QITS) + goto muuo; +#endif f = AB >> 9; flag1 = (FLAGS & USER) != 0; flag3 = 0; @@ -7548,7 +8632,13 @@ jrstf: flag1 = (FLAGS & USERIO) != 0; sect = prev_sect; } - +#endif +#if KS + if (xct_flag & 4) { + flag1 = (FLAGS & USERIO) != 0; + } +#endif +#if KL | KS /* Check if Paging Enabled */ if (!page_enable) { AR = AB; /* direct map */ @@ -7560,6 +8650,7 @@ jrstf: } /* Check if access to register */ +#if KL if (AB < 020 && ((QKLB && (glb_sect == 0 || sect == 0 || (glb_sect && sect == 1))) || !QKLB)) { AR = AB; /* direct map */ @@ -7569,6 +8660,7 @@ jrstf: set_reg(AC, AR); break; } +#endif /* Handle KI paging odditiy */ if (!flag1 && !t20_page && (f & 0740) == 0340) { @@ -7589,12 +8681,18 @@ jrstf: BR = AR; /* Remap the flag bits */ if (BR & KL_PAG_A) { /* A */ +#if KL AR = ((AR & 017777LL) << 9) + (AB & 0777); +#else + AR = ((AR & 003777LL) << 9) + (AB & 0777); +#endif if (flag1) /* U */ AR |= SMASK; /* BIT0 */ AR |= BIT2; /* BIT2 */ +#if KL if (BR & KL_PAG_P) /* P */ AR |= BIT6; /* BIT6 */ +#endif if (BR & KL_PAG_W) /* W */ AR |= BIT3; /* BIT3 */ if (BR & KL_PAG_S) /* S */ @@ -7602,7 +8700,11 @@ jrstf: if (BR & KL_PAG_C) /* C */ AR |= BIT7; /* BIT7 */ } else +#if KS + AR = AB; +#else AR = (f & 01740) ? 0 : 0377777LL; +#endif AR |= BIT8; #else /* Check if Paging Enabled */ @@ -7642,11 +8744,13 @@ jrstf: break; /* Stack, JUMP */ - case 0260: /* PUSHJ */ /* AR Frm PC */ + case 0260: /* PUSHJ */ /* FAC|SAC */ #if KL - if (QKLB && t20_page && pc_sect != 0) + if (QKLB && t20_page) + AR = (sect << 18) | (AR & RMASK);; + if (QKLB && t20_page && pc_sect != 0) { MB = ((uint64)pc_sect << 18) + (PC + !pi_cycle); - else { + } else { #endif MB = (((uint64)(FLAGS) << 23) & LMASK) | ((PC + !pi_cycle) & RMASK); #if KI | KL @@ -7659,21 +8763,22 @@ jrstf: } #endif #endif -#if KL +#if KL | KS 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; +#endif +#if KL + if (QKLB && t20_page && pc_sect != 0 && (BR & SMASK) == 0 && (BR & SECTM) != 0) { + BR = (BR + 1) & FMASK; + sect = (BR >> 18) & 07777; glb_sect = 1; } else { sect = pc_sect; glb_sect = 0; #endif - AR = AOB(AR); + BR = AOB(BR); FLAGS &= ~ (BYTI|ADRFLT|TRP1|TRP2); - if (AR & C1) { -#if KI | KL + if (BR & C1) { +#if KI | KL | KS if (!pi_cycle) FLAGS |= TRP2; #else @@ -7684,7 +8789,7 @@ jrstf: #if KL } #endif - AB = AR & RMASK; + AB = BR & RMASK; if (hst_lnt) hst[hst_p].mb = MB; if (Mem_write(uuo_cycle | pi_cycle, 0)) @@ -7706,26 +8811,33 @@ jrstf: } #endif #if KL - if (QKLB && t20_page && f) - pc_sect = cur_sect; + if (QKLB && t20_page) + pc_sect = (AR >> 18) & 0037; #endif - PC = BR & RMASK; + PC = AR & RMASK; PC_CHANGE f_pc_inh = 1; + AR = BR & FMASK; + set_reg(AC, AR); break; - case 0261: /* PUSH */ -#if KL + case 0261: /* PUSH */ /* FAC|FCE|SAC */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; +#if KL | KS BYF5 = 1; - if (QKLB && t20_page &&pc_sect != 0 && (AR & SMASK) == 0 && (AR & SECTM) != 0) { - AR = (AR + 1) & FMASK; - sect = (AR >> 18) & 07777; +#endif +#if KL + if (QKLB && t20_page &&pc_sect != 0 && (BR & SMASK) == 0 && (BR & SECTM) != 0) { + BR = (BR + 1) & FMASK; + sect = (BR >> 18) & 07777; } else { sect = pc_sect; #endif - AR = AOB(AR); - if (AR & C1) { -#if KI | KL + BR = AOB(BR); + if (BR & C1) { +#if KI | KL | KS if (!pi_cycle) FLAGS |= TRP2; #else @@ -7736,17 +8848,22 @@ jrstf: #if KL } #endif - AB = AR & RMASK; - MB = BR; + AB = BR & RMASK; + MB = AR; if (hst_lnt) hst[hst_p].mb = MB; if (Mem_write(0, 0)) goto last; + AR = BR & FMASK; + set_reg(AC, AR); break; - case 0262: /* POP */ -#if KL + case 0262: /* POP */ /* FAC */ + +#if KL | KS BYF5 = 1; /* Tell PXCT that this is stack */ +#endif +#if KL flag1 = glb_sect; glb_sect = 0; sect = pc_sect; @@ -7754,23 +8871,25 @@ jrstf: 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; + if (sect != 0 && (BR & SMASK) == 0 && (BR & SECTM) != 0) { + sect = (BR >> 18) & 07777; glb_sect = 1; } } #endif /* Fetch top of stack */ - AB = AR & RMASK; - if (Mem_read(0, 0, 0)) + AB = BR & RMASK; + if (Mem_read(0, 0, 0, 0)) goto last; if (hst_lnt) hst[hst_p].mb = MB; /* Save in location */ - AB = BR & RMASK; -#if KL + AB = AR & RMASK; +#if KL | KS BYF5 = 0; /* Now back to data */ +#endif +#if KL if (QKLB && t20_page) { sect = cur_sect; glb_sect = flag1; @@ -7779,9 +8898,10 @@ jrstf: #if KA | KI /* On KA or KI the AC is stored before Memory */ - MQ = AR; - AR = SOB(AR); - set_reg(AC, AR & FMASK); + MQ = BR; /* Save original stack in case fault on write */ + BR = SOB(BR); + AR = BR & FMASK; + set_reg(AC, AR); #endif if (Mem_write(0, 0)) { @@ -7792,25 +8912,26 @@ jrstf: goto last; } #if KL - /* Determine if we had globabl stack pointer or not */ + /* Determine if we had global stack pointer or not */ sect = pc_sect; 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; - set_reg(AC, AR & FMASK); + if (sect != 0 && (BR & SMASK) == 0 && (BR & SECTM) != 0) { + AR = (BR - 1) & FMASK; + set_reg(AC, AR); break; } } #endif -#if PDP6 | KL - /* This has to before the check for KL10 B extended check */ - AR = SOB(AR); - set_reg(AC, AR & FMASK); +#if PDP6 | KL | KS + /* This has to after the check for KL10 B extended check */ + BR = SOB(BR); + AR = BR & FMASK; + set_reg(AC, AR); #endif - if ((AR & C1) == 0) { -#if KI | KL + if ((BR & C1) == 0) { +#if KI | KL | KS if (!pi_cycle) FLAGS |= TRP2; #else @@ -7820,32 +8941,33 @@ jrstf: } break; - case 0263: /* POPJ */ - AB = AR & RMASK; -#if KL + case 0263: /* POPJ */ /* FAC|SAC */ + AB = BR & RMASK; +#if KL | KS BYF5 = 1; /* Tell PXCT that this is stack */ +#endif +#if KL 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; + if (QKLB && t20_page && sect != 0 && (BR & SMASK) == 0 && (BR & SECTM) != 0) { + sect = (BR >> 18) & 07777; glb_sect = 1; - AR = (AR - 1) & FMASK; + BR = (BR - 1) & FMASK; } else #endif - AR = SOB(AR); + BR = SOB(BR); - if (Mem_read(0, 0, 0)) - goto last; 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 (Mem_read(0, 0, 0, 0)) + goto last; #if ITS | KL_ITS if (QITS && (FLAGS & USER)) { jpc = PC; @@ -7854,16 +8976,21 @@ jrstf: f_pc_inh = 1; PC_CHANGE PC = MB & RMASK; -#if KL +#if KL | KS BYF5 = 0; /* Tell PXCT that this is stack */ +#endif +#if KL if (QKLB && t20_page && pc_sect != 0) { pc_sect = (MB >> 18) & 07777; - if ((AR & SMASK) == 0 && (AR & SECTM) != 0) + if ((BR & SMASK) == 0 && (BR & SECTM) != 0) { + AR = BR & FMASK; + set_reg(AC, AR); break; + } } #endif - if ((AR & C1) == 0) { -#if KI | KL + if ((BR & C1) == 0) { +#if KI | KL | KS if (!pi_cycle) FLAGS |= TRP2; #else @@ -7871,9 +8998,12 @@ jrstf: check_apr_irq(); #endif } + AR = BR & FMASK; + set_reg(AC, AR); break; - case 0264: /* JSR */ /* AR Frm PC */ + case 0264: /* JSR */ + AR &= RMASK; #if KL if (QKLB && t20_page && pc_sect != 0) MB = ((uint64)pc_sect << 18) + (PC + !pi_cycle); @@ -7921,7 +9051,7 @@ jrstf: f_pc_inh = 1; break; - case 0265: /* JSP */ /* AR Frm PC */ + case 0265: /* JSP */ /* SAC */ #if KL if (QKLB && t20_page && pc_sect != 0) AD = ((uint64)pc_sect << 18) + (PC + !pi_cycle); @@ -7957,10 +9087,13 @@ jrstf: PC = AR & RMASK; AR = AD; f_pc_inh = 1; + set_reg(AC, AR); break; - case 0266: /* JSA */ /* AR Frm PC */ - set_reg(AC, (AR << 18) | ((PC + 1) & RMASK)); + case 0266: /* JSA */ /* FBR|SCE */ + AR = ((AR & RMASK) << 18) | ((PC + 1) & RMASK); + MB = BR; + set_reg(AC, AR); #if !PDP6 if (uuo_cycle | pi_cycle) { FLAGS &= ~(USER|PUBLIC); /* Clear USER */ @@ -7976,14 +9109,16 @@ jrstf: if (QKLB && t20_page && glb_sect) pc_sect = cur_sect; #endif - PC = AR & RMASK; - AR = BR; + PC = AB; + if (Mem_write(0, 0)) { + goto last; + } break; case 0267: /* JRA */ AD = AB; AB = (get_reg(AC) >> 18) & RMASK; - if (Mem_read(uuo_cycle | pi_cycle, 0, 0)) + if (Mem_read(uuo_cycle | pi_cycle, 0, 0, 0)) goto last; set_reg(AC, MB); #if ITS | KL_ITS @@ -7992,14 +9127,31 @@ jrstf: } #endif PC_CHANGE - PC = AD & RMASK; + PC = (t_addr)(AD & RMASK); f_pc_inh = 1; break; - case 0270: /* ADD */ - case 0271: /* ADDI */ - case 0272: /* ADDM */ - case 0273: /* ADDB */ + case 0270: /* ADD */ /* FBR|SAC|FCE */ + case 0271: /* ADDI */ /* FBR|SAC */ + case 0272: /* ADDM */ /* FBR|FCEPSE */ + case 0273: /* ADDB */ /* FBR|SAC|FCEPSE */ + switch (IR & 3) { + case 0: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 1: + AR &= RMASK; + break; + case 2: + case 3: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } + flag1 = flag3 = 0; if (((AR & CMASK) + (BR & CMASK)) & SMASK) { FLAGS |= CRY1; @@ -8012,16 +9164,44 @@ jrstf: flag3 = 1; } if (flag1 != flag3) { - if (!pi_cycle) + if (!pi_cycle) { FLAGS |= OVR|TRP1; - check_apr_irq(); +#if PDP6 | KA + check_apr_irq(); +#endif + } } + AR &= FMASK; + if ((IR & 2) == 2) { + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + } + if ((IR & 3) != 2) + set_reg(AC, AR); break; - case 0274: /* SUB */ - case 0275: /* SUBI */ - case 0276: /* SUBM */ - case 0277: /* SUBB */ + case 0274: /* SUB */ /* FBR|SAC|FCE */ + case 0275: /* SUBI */ /* FBR|SAC */ + case 0276: /* SUBM */ /* FBR|FCEPSE */ + case 0277: /* SUBB */ /* FBR|SAC|FCEPSE */ + switch (IR & 3) { + case 0: + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + break; + case 1: + AR &= RMASK; + break; + case 2: + case 3: + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + break; + } flag1 = flag3 = 0; if ((CCM(AR) + (BR & CMASK) + 1) & SMASK) { FLAGS |= CRY1; @@ -8034,28 +9214,33 @@ jrstf: flag3 = 1; } if (flag1 != flag3) { - if (!pi_cycle) + if (!pi_cycle) { FLAGS |= OVR|TRP1; - check_apr_irq(); +#if PDP6 | KA + check_apr_irq(); +#endif + } } + AR &= FMASK; + if ((IR & 2) == 2) { + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + } + if ((IR & 3) != 2) + set_reg(AC, AR); break; - case 0300: /* CAI */ - case 0301: /* CAIL */ - case 0302: /* CAIE */ - case 0303: /* CAILE */ - case 0304: /* CAIA */ - case 0305: /* CAIGE */ - case 0306: /* CAIN */ - case 0307: /* CAIG */ - case 0310: /* CAM */ - case 0311: /* CAML */ - case 0312: /* CAME */ - case 0313: /* CAMLE */ - case 0314: /* CAMA */ - case 0315: /* CAMGE */ - case 0316: /* CAMN */ - case 0317: /* CAMG */ + case 0300: /* CAI */ /* FBR */ + case 0301: /* CAIL */ /* FBR */ + case 0302: /* CAIE */ /* FBR */ + case 0303: /* CAILE */ /* FBR */ + case 0304: /* CAIA */ /* FBR */ + case 0305: /* CAIGE */ /* FBR */ + case 0306: /* CAIN */ /* FBR */ + case 0307: /* CAIG */ /* FBR */ + AR &= RMASK; f = 0; AD = (CM(AR) + BR) + 1; #if PDP6 @@ -8071,54 +9256,85 @@ jrstf: f = 1; goto skip_op; - case 0320: /* JUMP */ - case 0321: /* JUMPL */ - case 0322: /* JUMPE */ - case 0323: /* JUMPLE */ - case 0324: /* JUMPA */ - case 0325: /* JUMPGE */ - case 0326: /* JUMPN */ - case 0327: /* JUMPG */ - AD = AR; + case 0310: /* CAM */ /* FBR|FCE */ + case 0311: /* CAML */ /* FBR|FCE */ + case 0312: /* CAME */ /* FBR|FCE */ + case 0313: /* CAMLE */ /* FBR|FCE */ + case 0314: /* CAMA */ /* FBR|FCE */ + case 0315: /* CAMGE */ /* FBR|FCE */ + case 0316: /* CAMN */ /* FBR|FCE */ + case 0317: /* CAMG */ /* FBR|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + + f = 0; + AD = (CM(AR) + BR) + 1; +#if PDP6 + if (AD & C1) + FLAGS |= CRY0; + if ((AR & SMASK) != (BR & SMASK)) + FLAGS |= CRY1; +#endif + if (((BR & SMASK) != 0) && (AR & SMASK) == 0) + f = 1; + if (((BR & SMASK) == (AR & SMASK)) && + (AD & SMASK) != 0) + f = 1; + goto skip_op; + + case 0320: /* JUMP */ /* FAC */ + case 0321: /* JUMPL */ /* FAC */ + case 0322: /* JUMPE */ /* FAC */ + case 0323: /* JUMPLE */ /* FAC */ + case 0324: /* JUMPA */ /* FAC */ + case 0325: /* JUMPGE */ /* FAC */ + case 0326: /* JUMPN */ /* FAC */ + case 0327: /* JUMPG */ /* FAC */ + AD = BR; + BR = AR & RMASK; f = ((AD & SMASK) != 0); goto jump_op; /* JUMP, SKIP */ - case 0330: /* SKIP */ - case 0331: /* SKIPL */ - case 0332: /* SKIPE */ - case 0333: /* SKIPLE */ - case 0334: /* SKIPA */ - case 0335: /* SKIPGE */ - case 0336: /* SKIPN */ - case 0337: /* SKIPG */ + case 0330: /* SKIP */ /* SACZ|FCE */ + case 0331: /* SKIPL */ /* SACZ|FCE */ + case 0332: /* SKIPE */ /* SACZ|FCE */ + case 0333: /* SKIPLE */ /* SACZ|FCE */ + case 0334: /* SKIPA */ /* SACZ|FCE */ + case 0335: /* SKIPGE */ /* SACZ|FCE */ + case 0336: /* SKIPN */ /* SACZ|FCE */ + case 0337: /* SKIPG */ /* SACZ|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AD = AR; f = ((AD & SMASK) != 0); goto skip_op; /* JUMP, SKIP */ - case 0340: /* AOJ */ - case 0341: /* AOJL */ - case 0342: /* AOJE */ - case 0343: /* AOJLE */ - case 0344: /* AOJA */ - case 0345: /* AOJGE */ - case 0346: /* AOJN */ - case 0347: /* AOJG */ - case 0360: /* SOJ */ - case 0361: /* SOJL */ - case 0362: /* SOJE */ - case 0363: /* SOJLE */ - case 0364: /* SOJA */ - case 0365: /* SOJGE */ - case 0366: /* SOJN */ - case 0367: /* SOJG */ + case 0340: /* AOJ */ /* SAC|FAC */ + case 0341: /* AOJL */ /* SAC|FAC */ + case 0342: /* AOJE */ /* SAC|FAC */ + case 0343: /* AOJLE */ /* SAC|FAC */ + case 0344: /* AOJA */ /* SAC|FAC */ + case 0345: /* AOJGE */ /* SAC|FAC */ + case 0346: /* AOJN */ /* SAC|FAC */ + case 0347: /* AOJG */ /* SAC|FAC */ + case 0360: /* SOJ */ /* SAC|FAC */ + case 0361: /* SOJL */ /* SAC|FAC */ + case 0362: /* SOJE */ /* SAC|FAC */ + case 0363: /* SOJLE */ /* SAC|FAC */ + case 0364: /* SOJA */ /* SAC|FAC */ + case 0365: /* SOJGE */ /* SAC|FAC */ + case 0366: /* SOJN */ /* SAC|FAC */ + case 0367: /* SOJG */ /* SAC|FAC */ flag1 = flag3 = 0; AD = (IR & 020) ? FMASK : 1; - if (((AR & CMASK) + (AD & CMASK)) & SMASK) { + if (((BR & CMASK) + (AD & CMASK)) & SMASK) { if (!pi_cycle) FLAGS |= CRY1; flag1 = 1; } - AD = AR + AD; + AD = BR + AD; #if PDP6 if (AD == FMASK && !pi_cycle) FLAGS |= CRY0; @@ -8132,7 +9348,9 @@ jrstf: } if (flag1 != flag3 && !pi_cycle) { FLAGS |= OVR|TRP1; +#if PDP6 | KA check_apr_irq(); +#endif } f = ((AD & SMASK) != 0); jump_op: @@ -8150,24 +9368,30 @@ jump_op: PC = AB; f_pc_inh = 1; } + AR &= FMASK; + if ((IR & 040) != 0) + set_reg(AC, AR); break; - case 0350: /* AOS */ - case 0351: /* AOSL */ - case 0352: /* AOSE */ - case 0353: /* AOSLE */ - case 0354: /* AOSA */ - case 0355: /* AOSGE */ - case 0356: /* AOSN */ - case 0357: /* AOSG */ - case 0370: /* SOS */ - case 0371: /* SOSL */ - case 0372: /* SOSE */ - case 0373: /* SOSLE */ - case 0374: /* SOSA */ - case 0375: /* SOSGE */ - case 0376: /* SOSN */ - case 0377: /* SOSG */ + case 0350: /* AOS */ /* SACZ|FCEPSE */ + case 0351: /* AOSL */ /* SACZ|FCEPSE */ + case 0352: /* AOSE */ /* SACZ|FCEPSE */ + case 0353: /* AOSLE */ /* SACZ|FCEPSE */ + case 0354: /* AOSA */ /* SACZ|FCEPSE */ + case 0355: /* AOSGE */ /* SACZ|FCEPSE */ + case 0356: /* AOSN */ /* SACZ|FCEPSE */ + case 0357: /* AOSG */ /* SACZ|FCEPSE */ + case 0370: /* SOS */ /* SACZ|FCEPSE */ + case 0371: /* SOSL */ /* SACZ|FCEPSE */ + case 0372: /* SOSE */ /* SACZ|FCEPSE */ + case 0373: /* SOSLE */ /* SACZ|FCEPSE */ + case 0374: /* SOSA */ /* SACZ|FCEPSE */ + case 0375: /* SOSGE */ /* SACZ|FCEPSE */ + case 0376: /* SOSN */ /* SACZ|FCEPSE */ + case 0377: /* SOSG */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; flag1 = flag3 = 0; AD = (IR & 020) ? FMASK : 1; if (((AR & CMASK) + (AD & CMASK)) & SMASK) { @@ -8183,13 +9407,14 @@ jump_op: } if (flag1 != flag3 && !pi_cycle) { FLAGS |= OVR|TRP1; +#if PDP6 | KA check_apr_irq(); +#endif } f = ((AD & SMASK) != 0); skip_op: - AD &= FMASK; - AR = AD; - f |= ((AD == 0) << 1); + AR = AD & FMASK; + f |= ((AR == 0) << 1); f = f & IR; if (((IR & 04) != 0) == (f == 0)) { #if PDP6 @@ -8203,31 +9428,103 @@ skip_op: pi_ov = pi_hold = 1; #endif } + if (((IR & 060) != 0) && (AC != 0)) { + set_reg(AC, AR); + } + if ((IR & 040) != 0) { + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + } break; /* Bool */ - case 0400: /* SETZ */ - case 0401: /* SETZI */ - case 0402: /* SETZM */ - case 0403: /* SETZB */ + case 0400: /* SETZ */ /* SAC */ + case 0401: /* SETZI */ /* SAC */ AR = 0; /* SETZ */ + set_reg(AC, AR); break; - case 0404: /* AND */ - case 0405: /* ANDI */ - case 0406: /* ANDM */ - case 0407: /* ANDB */ - AR = AR & BR; /* AND */ + case 0402: /* SETZM */ /* SCE */ + MB = AR = 0; /* SETZ */ + if (Mem_write(0, 0)) { + goto last; + } break; - case 0410: /* ANDCA */ - case 0411: /* ANDCAI */ - case 0412: /* ANDCAM */ - case 0413: /* ANDCAB */ + case 0403: /* SETZB */ /* SAC|SCE */ + MB = AR = 0; /* SETZ */ + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0404: /* AND */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB & BR; /* AND */ + set_reg(AC, AR); + break; + case 0405: /* ANDI */ /* FBR|SAC */ + AR = (AR & RMASK) & BR; /* AND */ + set_reg(AC, AR); + break; + case 0406: /* ANDM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB & BR; /* AND */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0407: /* ANDB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB & BR; /* AND */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0410: /* ANDCA */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB & CM(BR); /* ANDCA */ + set_reg(AC, AR); + break; + case 0411: /* ANDCAI */ /* FBR|SAC */ + AR &= RMASK; AR = AR & CM(BR); /* ANDCA */ + set_reg(AC, AR); + break; + case 0412: /* ANDCAM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB & CM(BR); /* ANDCA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } break; - case 0415: /* SETMI */ + case 0413: /* ANDCAB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB & CM(BR); /* ANDCA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0415: /* SETMI */ /* SAC */ + AR &= RMASK; #if KL /* XMOVEI for extended addressing */ if (QKLB && t20_page && pc_sect != 0) { @@ -8237,277 +9534,1156 @@ skip_op: AR |= ((uint64)cur_sect) << 18; } #endif - case 0414: /* SETM */ - case 0416: /* SETMM */ - case 0417: /* SETMB */ - /* SETM */ + set_reg(AC, AR); + break; + + case 0414: /* SETM */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + set_reg(AC, AR); + break; + + case 0416: /* SETMM */ /* FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + if (Mem_write(0, 0)) { + goto last; + } + AR = MB; break; - case 0420: /* ANDCM */ - case 0421: /* ANDCMI */ - case 0422: /* ANDCMM */ - case 0423: /* ANDCMB */ + case 0417: /* SETMB */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + set_reg(AC, AR); + break; + + case 0420: /* ANDCM */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = CM(MB) & BR; /* ANDCM */ + set_reg(AC, AR); + break; + + case 0421: /* ANDCMI */ /* FBR|SAC */ + AR &= RMASK; AR = CM(AR) & BR; /* ANDCM */ + set_reg(AC, AR); break; - case 0424: /* SETA */ - case 0425: /* SETAI */ - case 0426: /* SETAM */ - case 0427: /* SETAB */ + case 0422: /* ANDCMM */ /* FBR|FECPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = CM(MB) & BR; /* ANDCM */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0423: /* ANDCMB */ /* FBR|SAC|FECPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = CM(MB) & BR; /* ANDCM */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0424: /* SETA */ /* FBR|SAC */ AR = BR; /* SETA */ + set_reg(AC, AR); + break; + case 0425: /* SETAI */ /* FBR|SAC */ + AR = BR; /* SETA */ + set_reg(AC, AR); + break; + case 0426: /* SETAM */ /* FBR|SCE */ + AR = BR; /* SETA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0427: /* SETAB */ /* FBR|SAC|SCE */ + AR = BR; /* SETA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); break; - case 0430: /* XOR */ - case 0431: /* XORI */ - case 0432: /* XORM */ - case 0433: /* XORB */ + case 0430: /* XOR */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB ^ BR; /* XOR */ + set_reg(AC, AR); + break; + case 0431: /* XORI */ /* FBR|SAC */ + AR &= RMASK; AR = AR ^ BR; /* XOR */ + set_reg(AC, AR); + break; + case 0432: /* XORM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB ^ BR; /* XOR */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0433: /* XORB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB ^ BR; /* XOR */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); break; - case 0434: /* IOR */ - case 0435: /* IORI */ - case 0436: /* IORM */ - case 0437: /* IORB */ + case 0434: /* IOR */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = CM(CM(MB) & CM(BR)); /* IOR */ + set_reg(AC, AR); + break; + + case 0435: /* IORI */ /* FBR|SAC */ + AR &= RMASK; AR = CM(CM(AR) & CM(BR)); /* IOR */ + set_reg(AC, AR); break; - case 0440: /* ANDCB */ - case 0441: /* ANDCBI */ - case 0442: /* ANDCBM */ - case 0443: /* ANDCBB */ + case 0436: /* IORM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = CM(CM(MB) & CM(BR)); /* IOR */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0437: /* IORB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = CM(CM(MB) & CM(BR)); /* IOR */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0440: /* ANDCB */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = CM(AR) & CM(BR); /* ANDCB */ + set_reg(AC, AR); + break; + case 0441: /* ANDCBI */ /* FBR|SAC */ + AR &= RMASK; + AR = CM(AR) & CM(BR); /* ANDCB */ + set_reg(AC, AR); + break; + case 0442: /* ANDCBM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR) & CM(BR); /* ANDCB */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0443: /* ANDCBB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR) & CM(BR); /* ANDCB */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); break; - case 0444: /* EQV */ - case 0445: /* EQVI */ - case 0446: /* EQVM */ - case 0447: /* EQVB */ + case 0444: /* EQV */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = CM(AR ^ BR); /* EQV */ + set_reg(AC, AR); + break; + case 0445: /* EQVI */ /* FBR|SAC */ + AR &= RMASK; + AR = CM(AR ^ BR); /* EQV */ + set_reg(AC, AR); + break; + case 0446: /* EQVM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR ^ BR); /* EQV */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0447: /* EQVB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR ^ BR); /* EQV */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); break; - case 0450: /* SETCA */ - case 0451: /* SETCAI */ - case 0452: /* SETCAM */ - case 0453: /* SETCAB */ + case 0450: /* SETCA */ /* FBR|SAC */ AR = CM(BR); /* SETCA */ + set_reg(AC, AR); break; - case 0454: /* ORCA */ - case 0455: /* ORCAI */ - case 0456: /* ORCAM */ - case 0457: /* ORCAB */ + case 0451: /* SETCAI */ /* FBR|SAC */ + AR = CM(BR); /* SETCA */ + set_reg(AC, AR); + break; + + case 0452: /* SETCAM */ /* FBR|SCE */ + AR = CM(BR); /* SETCA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0453: /* SETCAB */ /* FBR|SAC|SCE */ + AR = CM(BR); /* SETCA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0454: /* ORCA */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = CM(CM(AR) & BR); /* ORCA */ + set_reg(AC, AR); + break; + case 0455: /* ORCAI */ /* FBR|SAC */ + AR &= RMASK; + AR = CM(CM(AR) & BR); /* ORCA */ + set_reg(AC, AR); + break; + case 0456: /* ORCAM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(CM(AR) & BR); /* ORCA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0457: /* ORCAB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(CM(AR) & BR); /* ORCA */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); break; - case 0460: /* SETCM */ - case 0461: /* SETCMI */ - case 0462: /* SETCMM */ - case 0463: /* SETCMB */ + case 0460: /* SETCM */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = CM(AR); /* SETCM */ + set_reg(AC, AR); break; - case 0464: /* ORCM */ - case 0465: /* ORCMI */ - case 0466: /* ORCMM */ - case 0467: /* ORCMB */ + case 0461: /* SETCMI */ /* SAC */ + AR &= RMASK; + AR = CM(AR); /* SETCM */ + set_reg(AC, AR); + break; + case 0462: /* SETCMM */ /* FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR); /* SETCM */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0463: /* SETCMB */ /* SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR); /* SETCM */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0464: /* ORCM */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = CM(AR & CM(BR)); /* ORCM */ + set_reg(AC, AR); break; - case 0470: /* ORCB */ - case 0471: /* ORCBI */ - case 0472: /* ORCBM */ - case 0473: /* ORCBB */ + case 0465: /* ORCMI */ /* FBR|SAC */ + AR &= RMASK; + AR = CM(AR & CM(BR)); /* ORCM */ + set_reg(AC, AR); + break; + case 0466: /* ORCMM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR & CM(BR)); /* ORCM */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0467: /* ORCMB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR & CM(BR)); /* ORCM */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; + + case 0470: /* ORCB */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = CM(AR & BR); /* ORCB */ + set_reg(AC, AR); + break; + case 0471: /* ORCBI */ /* FBR|SAC */ + AR &= RMASK; + AR = CM(AR & BR); /* ORCB */ + set_reg(AC, AR); + break; + case 0472: /* ORCBM */ /* FBR|FCEPSE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = CM(AR & BR); /* ORCB */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0473: /* ORCBB */ /* FBR|SAC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = CM(AR & BR); /* ORCB */ + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); break; - case 0474: /* SETO */ - case 0475: /* SETOI */ - case 0476: /* SETOM */ - case 0477: /* SETOB */ + case 0474: /* SETO */ /* SAC */ AR = FMASK; /* SETO */ + set_reg(AC, AR); break; + case 0475: /* SETOI */ /* SAC */ + AR = FMASK; /* SETO */ + set_reg(AC, AR); + break; - case 0547: /* HLRS */ - BR = SWAP_AR; - /* Fall Through */ + case 0476: /* SETOM */ /* SCE */ + MB = AR = FMASK; /* SETO */ + if (Mem_write(0, 0)) { + goto last; + } + break; + case 0477: /* SETOB */ /* SAC|SCE */ + MB = AR = FMASK; /* SETO */ + if (Mem_write(0, 0)) { + goto last; + } + set_reg(AC, AR); + break; - case 0501: /* HLLI */ + case 0500: /* HLL */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) { + goto last; + } + AR = MB; + AR = (AR & LMASK) | (BR & RMASK); + set_reg(AC, AR); + break; + + case 0501: /* HLLI */ /* FBR|SAC */ + AR &= RMASK; #if KL /* XHLLI for extended addressing */ - if (QKLB && t20_page && IR == 0501 && pc_sect != 0) { + if (QKLB && t20_page && 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 */ - case 0506: /* HRLM */ AR = (AR & LMASK) | (BR & RMASK); + set_reg(AC, AR); break; - case 0510: /* HLLZ */ - case 0511: /* HLLZI */ - case 0512: /* HLLZM */ - case 0513: /* HLLZS */ - case 0514: /* HRLZ */ - case 0515: /* HRLZI */ - case 0516: /* HRLZM */ - case 0517: /* HRLZS */ + case 0502: /* HLLM */ /* FAC|FCEPSE */ + AR = BR; + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = (AR & LMASK) | (MB & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0503: /* HLLS */ /* SACZ|FCEPSE */ + case 0543: /* HRRS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0547: /* HLRS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + BR = SWAP_AR; + AR = (AR & LMASK) | (BR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0504: /* HRL */ /* SWAR|FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = (AR & LMASK) | (BR & RMASK); + set_reg(AC, AR); + break; + + case 0505: /* HRLI */ /* SWAR|FBR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AR = (AR & LMASK) | (BR & RMASK); + set_reg(AC, AR); + break; + + case 0506: /* HRLM */ /* SWAR|FAC|FCEPSE */ + AR = BR; + if (Mem_read(0, 0, 0, 1)) + goto last; + BR = MB; + AR = SWAP_AR; + AR = (AR & LMASK) | (BR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0507: /* HRLS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + BR = SWAP_AR; + AR = (BR & LMASK) | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0510: /* HLLZ */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = (AR & LMASK); + set_reg(AC, AR); break; - case 0520: /* HLLO */ - case 0521: /* HLLOI */ - case 0522: /* HLLOM */ - case 0523: /* HLLOS */ - case 0524: /* HRLO */ - case 0525: /* HRLOI */ - case 0526: /* HRLOM */ - case 0527: /* HRLOS */ + case 0511: /* HLLZI */ /* SAC */ + AR &= RMASK; + AR = (AR & LMASK); + set_reg(AC, AR); + break; + + case 0512: /* HLLZM */ /* FAC|SCE */ + AR &= RMASK; + + BR = AR; + AR = get_reg(AC); + AR = (AR & LMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0513: /* HLLZS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = (AR & LMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0514: /* HRLZ */ /* SWAR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = (AR & LMASK); + set_reg(AC, AR); + break; + + case 0515: /* HRLZI */ /* SWAR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AR = (AR & LMASK); + set_reg(AC, AR); + break; + + case 0516: /* HRLZM */ /* SWAR|FAC|SCE */ + BR = AR; + AR = get_reg(AC); + AR = SWAP_AR; + AR = (AR & LMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0517: /* HRLZS */ /* SWAR|SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = SWAP_AR; + + AR = (AR & LMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0520: /* HLLO */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = (AR & LMASK) | RMASK; + set_reg(AC, AR); break; - case 0530: /* HLLE */ - case 0531: /* HLLEI */ - case 0532: /* HLLEM */ - case 0533: /* HLLES */ - case 0534: /* HRLE */ - case 0535: /* HRLEI */ - case 0536: /* HRLEM */ - case 0537: /* HRLES */ + case 0521: /* HLLOI */ /* SAC */ + AR &= RMASK; + AR = (AR & LMASK) | RMASK; + set_reg(AC, AR); + break; + + case 0522: /* HLLOM */ /* FAC|SCE */ + BR = AR; + AR = get_reg(AC); + AR = (AR & LMASK) | RMASK; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0523: /* HLLOS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = (AR & LMASK) | RMASK; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0524: /* HRLO */ /* SWAR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = (AR & LMASK) | RMASK; + set_reg(AC, AR); + break; + + case 0525: /* HRLOI */ /* SWAR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AR = (AR & LMASK) | RMASK; + set_reg(AC, AR); + break; + + case 0526: /* HRLOM */ /* SWAR|FAC|SCE */ + BR = AR & RMASK; + AR = get_reg(AC); + AR = SWAP_AR; + AR = (AR & LMASK) | RMASK; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0527: /* HRLOS */ /* SWAR|SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = (AR & LMASK) | RMASK; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0530: /* HLLE */ /* SAC|FCE */ + AR &= RMASK; + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AD = ((AR & SMASK) != 0) ? RMASK : 0; AR = (AR & LMASK) | AD; + set_reg(AC, AR); break; - case 0507: /* HRLS */ - BR = SWAP_AR; - /* Fall Through */ + case 0531: /* HLLEI */ /* SAC */ + AR &= RMASK; + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + set_reg(AC, AR); + break; - case 0540: /* HRR */ - case 0541: /* HRRI */ - case 0542: /* HRRM */ - case 0544: /* HLR */ - case 0545: /* HLRI */ - case 0546: /* HLRM */ + case 0532: /* HLLEM */ /* FAC|SCE */ + BR = AR & RMASK; + AR = get_reg(AC); + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0533: /* HLLES */ /* SAZC|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0534: /* HRLE */ /* SAC|SWAR|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + set_reg(AC, AR); + break; + + case 0535: /* HRLEI */ /* SAC|SWAR*/ + AR &= RMASK; + AR = SWAP_AR; + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + set_reg(AC, AR); + break; + + case 0536: /* HRLEM */ /* FAC|SWAR|SCE */ + BR = AR & RMASK; + AR = get_reg(AC); + AR = SWAP_AR; + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0537: /* HRLES */ /* SACZ|SWAR|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = SWAP_AR; + AD = ((AR & SMASK) != 0) ? RMASK : 0; + AR = (AR & LMASK) | AD; + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0540: /* HRR */ /* FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = (BR & LMASK) | (AR & RMASK); + set_reg(AC, AR); break; - case 0550: /* HRRZ */ - case 0551: /* HRRZI */ - case 0552: /* HRRZM */ - case 0553: /* HRRZS */ - case 0554: /* HLRZ */ - case 0555: /* HLRZI */ - case 0556: /* HLRZM */ - case 0557: /* HLRZS */ + case 0541: /* HRRI */ /* FBR|SAC */ + AR &= RMASK; + AR = (BR & LMASK) | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0542: /* HRRM */ /* FAC|FCEPSE */ + AR = BR; + if (Mem_read(0, 0, 0, 1)) + goto last; + BR = MB; + AR = (BR & LMASK) | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0544: /* HLR */ /* SWAR|FBR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + BR = get_reg(AC); + AR = SWAP_AR; + AR = (BR & LMASK) | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0545: /* HLRI */ /* SWAR|FBR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AR = (BR & LMASK) | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0546: /* HLRM */ /* SWAR|FAC|FCEPSE */ + AR = BR; + if (Mem_read(0, 0, 0, 1)) + goto last; + BR = MB; + AR = SWAP_AR; + AR = (BR & LMASK) | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0550: /* HRRZ */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = (AR & RMASK); + set_reg(AC, AR); break; - case 0560: /* HRRO */ - case 0561: /* HRROI */ - case 0562: /* HRROM */ - case 0563: /* HRROS */ - case 0564: /* HLRO */ - case 0565: /* HLROI */ - case 0566: /* HLROM */ - case 0567: /* HLROS */ + case 0551: /* HRRZI */ /* SAC */ + AR &= RMASK; + AR = (AR & RMASK); + set_reg(AC, AR); + break; + + case 0552: /* HRRZM */ /* FAC|SCE */ + BR = AR; + AR = get_reg(AC); + AR = (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0553: /* HRRZS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0554: /* HLRZ */ /* SWAR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = (AR & RMASK); + set_reg(AC, AR); + break; + + case 0555: /* HLRZI */ /* SWAR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AR = (AR & RMASK); + set_reg(AC, AR); + break; + + case 0556: /* HLRZM */ /* SWAR|FAC|SCE */ + BR = AR; + AR = get_reg(AC); + AR = SWAP_AR; + AR = (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0557: /* HLRZS */ /* SWAR|SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + + break; + + case 0560: /* HRRO */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AR = LMASK | (AR & RMASK); + set_reg(AC, AR); break; - case 0570: /* HRRE */ - case 0571: /* HRREI */ - case 0572: /* HRREM */ - case 0573: /* HRRES */ - case 0574: /* HLRE */ - case 0575: /* HLREI */ - case 0576: /* HLREM */ - case 0577: /* HLRES */ + case 0561: /* HRROI */ /* SAC */ + AR &= RMASK; + AR = LMASK | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0562: /* HRROM */ /* FAC|SCE */ + AR &= RMASK; + BR = AR; + AR = get_reg(AC); + AR = LMASK | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0563: /* HRROS */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = LMASK | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0564: /* HLRO */ /* SWAR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = LMASK | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0565: /* HLROI */ /* SWAR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AR = LMASK | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0566: /* HLROM */ /* SWAR|FAC|SCE */ + AR = BR; + if (Mem_read(0, 0, 0, 0)) + goto last; + BR = MB; + AR = SWAP_AR; + AR = LMASK | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0567: /* HLROS */ /* SWAR|SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = SWAP_AR; + AR = LMASK | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0570: /* HRRE */ /* SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; AD = ((AR & RSIGN) != 0) ? LMASK: 0; AR = AD | (AR & RMASK); + set_reg(AC, AR); break; - case 0600: /* TRN */ - case 0601: /* TLN */ - case 0602: /* TRNE */ - case 0603: /* TLNE */ - case 0604: /* TRNA */ - case 0605: /* TLNA */ - case 0606: /* TRNN */ - case 0607: /* TLNN */ - case 0610: /* TDN */ - case 0611: /* TSN */ - case 0612: /* TDNE */ - case 0613: /* TSNE */ - case 0614: /* TDNA */ - case 0615: /* TSNA */ - case 0616: /* TDNN */ - case 0617: /* TSNN */ - MQ = AR; /* N */ - goto test_op; + case 0571: /* HRREI */ /* SAC */ + AR &= RMASK; + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + set_reg(AC, AR); + break; - case 0620: /* TRZ */ - case 0621: /* TLZ */ - case 0622: /* TRZE */ - case 0623: /* TLZE */ - case 0624: /* TRZA */ - case 0625: /* TLZA */ - case 0626: /* TRZN */ - case 0627: /* TLZN */ - case 0630: /* TDZ */ - case 0631: /* TSZ */ - case 0632: /* TDZE */ - case 0633: /* TSZE */ - case 0634: /* TDZA */ - case 0635: /* TSZA */ - case 0636: /* TDZN */ - case 0637: /* TSZN */ - MQ = CM(AR) & BR; /* Z */ - goto test_op; + case 0572: /* HRREM */ /* FAC|SCE */ + AR &= RMASK; + BR = AR; + AR = get_reg(AC); + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; - case 0640: /* TRC */ - case 0641: /* TLC */ - case 0642: /* TRCE */ - case 0643: /* TLCE */ - case 0644: /* TRCA */ - case 0645: /* TLCA */ - case 0646: /* TRCN */ - case 0647: /* TLCN */ - case 0650: /* TDC */ - case 0651: /* TSC */ - case 0652: /* TDCE */ - case 0653: /* TSCE */ - case 0654: /* TDCA */ - case 0655: /* TSCA */ - case 0656: /* TDCN */ - case 0657: /* TSCN */ - MQ = AR ^ BR; /* C */ - goto test_op; + case 0573: /* HRRES */ /* SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; - case 0660: /* TRO */ - case 0661: /* TLO */ - case 0662: /* TROE */ - case 0663: /* TLOE */ - case 0664: /* TROA */ - case 0665: /* TLOA */ - case 0666: /* TRON */ - case 0667: /* TLON */ - case 0670: /* TDO */ - case 0671: /* TSO */ - case 0672: /* TDOE */ - case 0673: /* TSOE */ - case 0674: /* TDOA */ - case 0675: /* TSOA */ - case 0676: /* TDON */ - case 0677: /* TSON */ - MQ = AR | BR; /* O */ -test_op: + case 0574: /* HLRE */ /* SWAR|SAC|FCE */ + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + AR = SWAP_AR; + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0575: /* HLREI */ /* SWAR|SAC */ + AR &= RMASK; + AR = SWAP_AR; + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + set_reg(AC, AR); + break; + + case 0576: /* HLREM */ /* SWAR|FAC|SCE */ + AR &= RMASK; + BR = AR; + AR = get_reg(AC); + AR = SWAP_AR; + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + break; + + case 0577: /* HLRES */ /* SWAR|SACZ|FCEPSE */ + if (Mem_read(0, 0, 0, 1)) + goto last; + AR = MB; + AR = SWAP_AR; + AD = ((AR & RSIGN) != 0) ? LMASK: 0; + AR = AD | (AR & RMASK); + MB = AR; + if (Mem_write(0, 0)) { + goto last; + } + if (AC != 0) + set_reg(AC, AR); + break; + + case 0600: /* TRN */ /* FBR */ + case 0601: /* TLN */ /* FBR|SWAR */ + case 0602: /* TRNE */ /* FBR */ + case 0603: /* TLNE */ /* FBR|SWAR */ + case 0604: /* TRNA */ /* FBR */ + case 0605: /* TLNA */ /* FBR|SWAR */ + case 0606: /* TRNN */ /* FBR */ + case 0607: /* TLNN */ /* FBR|SWAR */ + case 0610: /* TDN */ /* FBR|FCE */ + case 0611: /* TSN */ /* FBR|SWAR|FCE */ + case 0612: /* TDNE */ /* FBR|FCE */ + case 0613: /* TSNE */ /* FBR|SWAR|FCE */ + case 0614: /* TDNA */ /* FBR|FCE */ + case 0615: /* TSNA */ /* FBR|SWAR|FCE */ + case 0616: /* TDNN */ /* FBR|FCE */ + case 0617: /* TSNN */ /* FBR|SWAR|FCE */ + case 0620: /* TRZ */ /* FBR|SAC */ + case 0621: /* TLZ */ /* FBR|SWAR|SAC */ + case 0622: /* TRZE */ /* FBR|SAC */ + case 0623: /* TLZE */ /* FBR|SWAR|SAC */ + case 0624: /* TRZA */ /* FBR|SAC */ + case 0625: /* TLZA */ /* FBR|SWAR|SAC */ + case 0626: /* TRZN */ /* FBR|SAC */ + case 0627: /* TLZN */ /* FBR|SWAR|SAC */ + case 0630: /* TDZ */ /* FBR|SAC|FCE */ + case 0631: /* TSZ */ /* FBR|SWAR|SAC|FCE */ + case 0632: /* TDZE */ /* FBR|SAC|FCE */ + case 0633: /* TSZE */ /* FBR|SWAR|SAC|FCE */ + case 0634: /* TDZA */ /* FBR|SAC|FCE */ + case 0635: /* TSZA */ /* FBR|SWAR|SAC|FCE */ + case 0636: /* TDZN */ /* FBR|SAC|FCE */ + case 0637: /* TSZN */ /* FBR|SWAR|SAC|FCE */ + case 0640: /* TRC */ /* FBR|SAC */ + case 0641: /* TLC */ /* FBR|SWAR|SAC */ + case 0642: /* TRCE */ /* FBR|SAC */ + case 0643: /* TLCE */ /* FBR|SWAR|SAC */ + case 0644: /* TRCA */ /* FBR|SAC */ + case 0645: /* TLCA */ /* FBR|SWAR|SAC */ + case 0646: /* TRCN */ /* FBR|SAC */ + case 0647: /* TLCN */ /* FBR|SWAR|SAC */ + case 0650: /* TDC */ /* FBR|SAC|FCE */ + case 0651: /* TSC */ /* FBR|SWAR|SAC|FCE */ + case 0652: /* TDCE */ /* FBR|SAC|FCE */ + case 0653: /* TSCE */ /* FBR|SWAR|SAC|FCE */ + case 0654: /* TDCA */ /* FBR|SAC|FCE */ + case 0655: /* TSCA */ /* FBR|SWAR|SAC|FCE */ + case 0656: /* TDCN */ /* FBR|SAC|FCE */ + case 0657: /* TSCN */ /* FBR|SWAR|SAC|FCE */ + case 0660: /* TRO */ /* FBR|SAC */ + case 0661: /* TLO */ /* FBR|SWAR|SAC */ + case 0662: /* TROE */ /* FBR|SAC */ + case 0663: /* TLOE */ /* FBR|SWAR|SAC */ + case 0664: /* TROA */ /* FBR|SAC */ + case 0665: /* TLOA */ /* FBR|SWAR|SAC */ + case 0666: /* TRON */ /* FBR|SAC */ + case 0667: /* TLON */ /* FBR|SWAR|SAC */ + case 0670: /* TDO */ /* FBR|SAC|FCE */ + case 0671: /* TSO */ /* FBR|SWAR|SAC|FCE */ + case 0672: /* TDOE */ /* FBR|SAC|FCE */ + case 0673: /* TSOE */ /* FBR|SWAR|SAC|FCE */ + case 0674: /* TDOA */ /* FBR|SAC|FCE */ + case 0675: /* TSOA */ /* FBR|SWAR|SAC|FCE */ + case 0676: /* TDON */ /* FBR|SAC|FCE */ + case 0677: /* TSON */ /* FBR|SWAR|SAC|FCE */ + + /* Load pseudo registers based on Opcode */ + if (IR & 010) { + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + } else + AR &= RMASK; + + if (IR & 01) { + AR = SWAP_AR; + } + + + switch (IR & 060) { + case 0: + break; + case 020: + MQ = CM(AR) & BR; /* Z */ + break; + case 040: + MQ = AR ^ BR; /* C */ + break; + case 060: + MQ = AR | BR; /* O */ + break; + } AR &= BR; f = ((AR == 0) & ((IR >> 1) & 1)) ^ ((IR >> 2) & 1); if (f) { @@ -8515,6 +10691,8 @@ test_op: PC = (PC + 1) & RMASK; } AR = MQ; + if ((IR & 060) != 0) + set_reg(AC, AR); break; /* IOT */ @@ -8546,43 +10724,982 @@ test_op: /* User and not User I/O */ goto muuo; } else { +#if KS + int ctl = (int)((MB >> 18) & 017); + double us; + AB = AR & RMASK; + + switch (IR & 077) { + case 000: /* APR0 */ + switch (AC) { + /* 7000 */ + case 000: /* APRID */ + /* APRID */ + MB = SMASK | BIT4 | BIT5 | (270LL << 18); /* MC level 270 */ + /* Bit 0 Inhibit CST Update available */ + /* Bit 1 No CST at all */ + /* Bit 2 Exotic microcode */ + /* Bit 3 UBABLT Instructions present */ + /* Bit 4 KI style paging */ + /* Bit 5 for TOPS-20 paging */ +#if KS_ITS + if (QITS) + MB |= BIT2; +#endif + MB |= (uint64)((apr_serial == -1) ? DEF_SERIAL : apr_serial); + sim_debug(DEBUG_DATAIO, &cpu_dev, "APRID %012llo\n", MB); + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70020 */ + case 004: /* WRAPR */ + /* Set trap conditions */ + /* Bit 20 100000 Enable interrupts */ + /* Bit 21 040000 Disable interrupts */ + /* Bit 22 020000 Clear pending */ + /* Bit 23 010000 Set irq */ + /* Bit 24 004000 Flag 24 */ + /* Bit 25 002000 Wake up front end */ + /* Bit 26 001000 Power fail interrupt */ + /* Bit 27 000400 NXM */ + /* Bit 28 000200 Hard memory error */ + /* Bit 29 000100 Soft memory error */ + /* Bit 30 000040 Timer interrupt */ + /* Bit 31 000020 Interrupt from Front end */ + /* Bit 32 000010 Generate interrupt request */ + /* 000007 PIA */ + apr_irq = AR & 07; + clr_interrupt(0); + if (AR & 0100000) { /* Enable interrupts */ + irq_enable |= 07760 & AR; + } + if (AR & 0040000) { /* Disable interrupts */ + irq_enable &= ~(07760 & AR); + } + if (AR & 0020000) { /* Clear interrupt */ + irq_flags &= ~(05760 & AR); + } + if (AR & 0010000) { /* Set interrupt */ + irq_flags |= (05760 & AR); + if (AR & 02000) + cty_wakeup(); + } + check_apr_irq(); + sim_debug(DEBUG_CONO, &cpu_dev, "WRAPR %012llo\n", AR); + break; + + /* 70024 */ + case 005: /* RDAPR */ + /* Read trap conditions */ + MB = irq_flags | apr_irq; + MB |= ((uint64)irq_enable) << 18; + if (irq_flags & irq_enable) + MB |= 010; + if (Mem_write(0, 0)) + goto last; + sim_debug(DEBUG_CONI, &cpu_dev, "RDAPR %012llo\n", MB); + AR = MB; + break; + + /* 70030 */ + case 006: /* CONSZ APR */ + /* Read trap conditions */ + BR = irq_flags | apr_irq; + BR |= ((uint64)irq_enable) << 18; + if (irq_flags & irq_enable) + BR |= 010; + BR = (BR & AR) & RMASK; + if (BR == 0) + PC = (PC + 1) & RMASK; + sim_debug(DEBUG_CONI, &cpu_dev, "CONSZ %012llo\n", AR); + break; + + /* 70034 */ + case 007: /* CONSO APR */ + /* Read trap conditions */ + BR = irq_flags | apr_irq; + BR |= ((uint64)irq_enable) << 18; + if (irq_flags & irq_enable) + BR |= 010; + BR = (BR & AR) & RMASK; + if (BR != 0) + PC = (PC + 1) & RMASK; + sim_debug(DEBUG_CONI, &cpu_dev, "CONSO %012llo\n", AR); + break; + + /* 70060 */ + case 014: /* WRPI */ + /* Set PI flags */ + if (AR & 010000) { /* Bit 23 */ + PIR = PIH = PIE = 0; + pi_enable = 0; + parity_irq = 0; + } + if (AR & 0200) { /* Bit 28 */ + pi_enable = 1; + } + if (AR & 0400) /* Bit 27 */ + pi_enable = 0; + if (AR & 01000) { /* Bit 26 */ + PIE &= ~(AR & 0177); + } + if (AR & 02000) /* Bit 25 */ + PIE |= (AR & 0177); + if (AR & 04000) { /* Bit 24 */ + PIR |= (AR & 0177); + pi_pending = 1; + } + if (AR & 020000) { /* Bit 22 */ + PIR &= ~(AR & 0177); + } + check_apr_irq(); + sim_debug(DEBUG_IRQ, &cpu_dev, "WRPI %012llo\n", AR); + break; + + /* 70064 */ + case 015: /* RDPI */ + MB = PIE; + MB |= (pi_enable << 7); + MB |= (PIH << 8); + MB |= ((uint64)(PIR) << 18); + MB |= (parity_irq << 15); + sim_debug(DEBUG_IRQ, &cpu_dev, "RDPI %012llo\n", MB); + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70070 */ + case 016: /* CONSZ PI */ + /* Read PI conditions */ + BR = PIE; + BR |= (pi_enable << 7); + BR |= (PIH << 8); + BR = (BR & AR) & RMASK; + if (BR == 0) + PC = (PC + 1) & RMASK; + sim_debug(DEBUG_CONI, &cpu_dev, "CONSZ PI %012llo\n", AR); + break; + + /* 70074 */ + case 017: /* CONSO PI */ + /* Read PI conditions */ + BR = PIE; + BR |= (pi_enable << 7); + BR |= (PIH << 8); + BR = (BR & AR) & RMASK; + if (BR != 0) + PC = (PC + 1) & RMASK; + sim_debug(DEBUG_CONI, &cpu_dev, "CONSO PI %012llo\n", AR); + break; + + default: + goto muuo; + } + break; + + case 001: /* APR1 */ + switch (AC) { + case 000: /* CLRSCH */ + break; + + /* 70104 */ + case 001: /* RDUBR */ +#if KS_ITS + if (QITS) + MB = ub_ptr & 03777777; + else +#endif + MB = (ub_ptr >> 9); + /* Set previous section */ + MB |= ((uint64)(prev_ctx & 0160)) << 20; + MB |= ((uint64)(fm_sel & 0160)) << 23; + MB |= SMASK|BIT2; + sim_debug(DEBUG_DATAIO, &cpu_dev, "RDUBR %012llo\n", MB); + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70110 */ + case 002: /* CLRPT */ + f = (RMASK & AB) >> 9; + /* Map the page */ + u_tlb[f] = 0; + e_tlb[f] = 0; + /* If not user do exec mappping */ + if (!t20_page && (f & 0740) == 0340) { + /* Pages 340-377 via UBT */ + f += 01000 - 0340; + u_tlb[f] = 0; + } + break; + + /* 70114 */ + case 003: /* WRUBR */ + if (Mem_read(0, 0, 0, 0)) + goto last; + if (MB & SMASK) { + fm_sel = (uint8)(MB >> 23) & 0160; + prev_ctx = (uint8)(MB >> 20) & 0160; + } + if (MB & BIT2) { +#if KS_ITS + if (QITS) + ub_ptr = MB & 03777777; + else +#endif + ub_ptr = (MB & 03777) << 9; + for (f = 0; f < 512; f++) { + u_tlb[f] = 0; + e_tlb[f] = 0; + } + for (;f < 546; f++) + u_tlb[f] = 0; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, + "WRUBR %012llo ebr=%06o ubr=%06o\n", + MB, eb_ptr, ub_ptr); + break; + + /* 70120 */ + case 004: /* WREBR */ + eb_ptr = (AR & 03777) << 9; + for (f = 0; f < 512; f++) { + e_tlb[f] = 0; + u_tlb[f] = 0; + } + for (;f < 546; f++) + u_tlb[f] = 0; + page_enable = (AR & 020000) != 0; + t20_page = (AR & 040000) != 0; + page_fault = 0; + sim_debug(DEBUG_CONO, &cpu_dev, "WREBR %012llo\n", AR); + break; + + /* 70124 */ + case 005: /* RDEBR */ + MB = (eb_ptr >> 9); + if (page_enable) + MB |= 020000; + if (t20_page) + MB |= 040000; + sim_debug(DEBUG_CONI, &cpu_dev, "RDEBR %012llo\n", MB); + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; +#if KS_ITS + /* 70144 */ + case 011: /* RDPCST */ + if (QITS) { + MB = pcst; + if (Mem_write(0, 0)) + goto last; + break; + } + /* Fall through */ + + /* 70154 */ + case 013: /* WRPCST */ + if (QITS) { + if (Mem_read(0, 0, 0, 0)) + goto last; + pcst = MB; + break; + } +#endif + default: + goto muuo; + } + break; + + case 002: /* APR2 */ + switch (AC) { + /* 70200 */ + case 000: /* RDSPB */ /* ITS SDBR1 */ + sim_debug(DEBUG_CONI, &cpu_dev, "RDSPD %012llo\n", spt); + MB = spt; + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70204 */ + case 001: /* RDCSB */ /* ITS SDBR2 */ + sim_debug(DEBUG_CONI, &cpu_dev, "RDSPD %012llo\n", cst); + MB = cst; + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70250 */ + case 002: /* RDPUR */ /* ITS SDBR3 */ + sim_debug(DEBUG_CONI, &cpu_dev, "RDSPD %012llo\n", cst_dat); + MB = cst_dat; + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70214 */ + case 003: /* RDCSTM */ /* ITS SDBR4 */ + sim_debug(DEBUG_CONI, &cpu_dev, "RDSPD %012llo\n", cst_msk); + MB = cst_msk; + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70220 */ + case 004: /* RDTIME */ + MB = tim_high; + if (Mem_write(0, 0)) + goto last; + us = sim_activate_time_usecs (&cpu_unit[0]); + f = 2000 - ((int)us); + MB = tim_low | (f << 2); + sim_debug(DEBUG_CONI, &cpu_dev, "RDTIME %012llo %012llo\n", MB, tim_high); + AB = (AB + 1) & RMASK; + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70224 */ + case 005: /* RDINT */ + MB = int_val; + sim_debug(DEBUG_CONI, &cpu_dev, "RDINT %012llo\n", MB); + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70230 */ + case 006: /* RDHSB */ + MB = hsb; + sim_debug(DEBUG_CONI, &cpu_dev, "RDHSB %012llo\n", MB); + if (Mem_write(0, 0)) + goto last; + AR = MB; + break; + + /* 70234 */ + case 007: /* ITS SPM */ + if (QITS) { + MB = dbr1; + if (Mem_write(0, 0)) + goto last; + AB = (AB + 1) & RMASK; + MB = dbr2; + if (Mem_write(0, 0)) + goto last; + AB = (AB + 1) & RMASK; + MB = qua_time; + if (Mem_write(0, 0)) + goto last; + } + break; + + /* 70240 */ + case 010: /* WRSPB */ /* ITS LDBR1 */ +#if KS_ITS + if (QITS) { + dbr1 = AB; + for (f = 0; f < 512; f++) { + u_tlb[f] = 0; + e_tlb[f] = 0; + } + sim_debug(DEBUG_CONI, &cpu_dev, "WRDBR1 %012llo\n", dbr1); + break; + } +#endif + if (Mem_read(0, 0, 0, 0)) + goto last; + spt = MB; + sim_debug(DEBUG_CONI, &cpu_dev, "WRSPB %012llo\n", spt); + break; + + /* 70244 */ + case 011: /* WRCSB */ /* ITS LDBR2 */ +#if KS_ITS + if (QITS) { + dbr2 = AB; + for (f = 0; f < 512; f++) { + u_tlb[f] = 0; + e_tlb[f] = 0; + } + sim_debug(DEBUG_CONI, &cpu_dev, "WRDBR2 %012llo\n", dbr2); + break; + } +#endif + if (Mem_read(0, 0, 0, 0)) + goto last; + cst = MB; + sim_debug(DEBUG_CONI, &cpu_dev, "WRCSB %012llo\n", cst); + break; + + /* 70250 */ + case 012: /* WRPUR */ /* ITS LDBR3 */ +#if KS_ITS + if (QITS) { + dbr3 = AB; + for (f = 0; f < 512; f++) { + u_tlb[f] = 0; + e_tlb[f] = 0; + } + sim_debug(DEBUG_CONI, &cpu_dev, "WRDBR3 %012llo\n", dbr3); + break; + } +#endif + if (Mem_read(0, 0, 0, 0)) + goto last; + cst_dat = MB; + sim_debug(DEBUG_CONI, &cpu_dev, "WRPUR %012llo\n", cst_dat); + break; + + /* 70254 */ + case 013: /* WRCSTM */ /* ITS LDBR4 */ +#if KS_ITS + if (QITS) { + dbr4 = AB; + for (f = 0; f < 512; f++) { + u_tlb[f] = 0; + e_tlb[f] = 0; + } + sim_debug(DEBUG_CONI, &cpu_dev, "WRDBR4 %012llo\n", dbr4); + break; + } +#endif + if (Mem_read(0, 0, 0, 0)) + goto last; + cst_msk = MB; + sim_debug(DEBUG_CONI, &cpu_dev, "WRCSTM %012llo\n", cst_msk); + break; + + /* 70264 */ + case 015: /* WRINT */ + if (Mem_read(0, 0, 0, 0)) + goto last; + int_val = MB & ~07777; + sim_debug(DEBUG_DATAIO, &cpu_dev, "WRINT %012llo\n", int_val); + break; + + /* 70260 */ + case 014: /* WRTIME */ + if (Mem_read(0, 0, 0, 0)) + goto last; + tim_high = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0, 0)) + goto last; + tim_low = MB & ~07777; + sim_debug(DEBUG_CONI, &cpu_dev, "WRTIME %012llo %012llo\n", tim_low, tim_high); + break; + /* 70270 */ + case 016: /* WRHSB */ + if (Mem_read(0, 0, 0, 0)) + goto last; + hsb = MB; + sim_debug(DEBUG_CONI, &cpu_dev, "WRHSB %012llo\n", MB); + break; + + /* 70274 */ + case 017: /* ITS LPMR */ +#if KS_ITS + if (QITS) { + if (Mem_read(0, 0, 0, 0)) + goto last; + dbr1 = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0, 0)) + goto last; + dbr2 = MB; + AB = (AB + 1) & RMASK; + if (Mem_read(0, 0, 0, 0)) + goto last; + qua_time = MB; + for (f = 0; f < 512; f++) { + u_tlb[f] = 0; + e_tlb[f] = 0; + } + for (;f < 546; f++) + u_tlb[f] = 0; + break; + } +#endif + default: + goto muuo; + } + break; + + /* 70400 */ + case 004: /* UMOVE */ + xct_flag = 4; + AB &= RMASK; + if (Mem_read(0, 0, 0, 0)) + goto last; + AR = MB; + set_reg(AC, AR); + xct_flag = 0; + break; + + /* 70500 */ + case 005: /* UMOVEM */ + MB = BR; + AB &= RMASK; + xct_flag = 4; + if (Mem_write(0, 0)) + goto last; + xct_flag = 0; + break; + + case 010: /* TIOE , ITS RDIOI */ +#if KS_ITS + if (QITS) { + ctl = 3; + goto its_rd; + } +#endif + if (uba_read(AB, ctl, &MB, WORD)) { +io_fault: + fault_data = (020LL << 30) | BIT8 | BIT10; + fault_data |= (uint64)((020 & IR) << 18); + fault_data |= AB | (ctl << 18); + page_fault = 1; + goto last; + } + BR &= 0177777; + if ((BR & MB) == 0) + PC = (PC + 1) & RMASK; + AR = MB; + break; + + case 011: /* TION, ITS RDIOQ */ +#if KS_ITS + if (QITS) { + ctl = 1; + goto its_rd; + } +#endif + if (uba_read(AB, ctl, &MB, WORD)) + goto io_fault; + BR &= 0177777; + if ((BR & MB) != 0) + PC = (PC + 1) & RMASK; + AR = MB; + break; + + case 012: /* RDIO */ +#if KS_ITS + if (QITS) { + if (Mem_read(pi_cycle, 0, 0, 0)) + goto last; + AB = MB & RMASK; + ctl = (int)((MB >> 18) & 017); + } +its_rd: +#endif + if (uba_read(AB, ctl, &AR, WORD)) + goto io_fault; + set_reg(AC, AR); + break; + + case 013: /* WRIO */ +#if KS_ITS + if (QITS) { + if (Mem_read(pi_cycle, 0, 0, 0)) + goto last; + AB = MB & RMASK; + ctl = (int)((MB >> 18) & 017); + } +its_wr: +#endif + if (uba_write(AB, ctl, BR, WORD)) + goto io_fault; + break; + + case 014: /* BSIO, ITS WRIOI */ +#if KS_ITS + if (QITS) { + ctl = 3; + if (AB == 0777000) + break; + goto its_wr; + } +#endif + if (uba_read(AB, ctl, &MB, WORD)) + goto io_fault; + MB |= BR; + if (uba_write(AB, ctl, MB, WORD)) + goto io_fault; + AR = MB; + break; + + case 015: /* BCIO, ITS WRIOQ */ +#if KS_ITS + if (QITS) { + ctl = 1; + goto its_wr; + } +#endif + if (uba_read(AB, ctl, &MB, WORD)) + goto io_fault; + MB &= ~BR; + if (uba_write(AB, ctl, MB, WORD)) + goto io_fault; + AR = MB; + break; + + case 016: /* BLTBU */ + case 017: /* BLTUB */ + AR = get_reg(AC); + BR = AB; + /* Precompute end of transfer address */ + AD = (CM(AR) + BR + 1) & RMASK; + AD = ((AR + (AD << 18)) & LMASK) | ((AR + AD) & RMASK); + set_reg(AC, AOB(AD)); + do { + AIO_CHECK_EVENT; /* queue async events */ + if (sim_interval <= 0) { + 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) { + pi_rq = check_irq_level(); + if (pi_rq) { + f_pc_inh = 1; + f_load_pc = 0; + f_inst_fetch = 0; + set_reg(AC, AR); + break; + } + } + } + AB = (AR >> 18) & RMASK; + BYF5 = 1; + if (Mem_read(0, 0, 0, 0)) { + BYF5 = 0; + f_pc_inh = 1; + set_reg(AC, AR); + goto last; + } +#define BMASK1 0776000000000LL +#define BMASK2 0001774000000LL +#define BMASK3 0000003770000LL +#define BMASK4 0000000007760LL + if (IR & 1) { + MB = ((MB << 10) & BMASK1) | + ((MB >> 6) & BMASK2) | + ((MB << 12) & BMASK4) | + ((MB >> 4) & BMASK3); + } else { + MB = ((MB & BMASK1) >> 10) | + ((MB & BMASK2) << 6) | + ((MB & BMASK4) >> 12) | + ((MB & BMASK3) << 4); + } + AB = (AR & RMASK); + BYF5 = 0; + if (Mem_write(0, 0)) { + f_pc_inh = 1; + set_reg(AC, AR); + goto last; + } + AD = (AR & RMASK) + CM(BR) + 1; + AR = AOB(AR); + } while ((AD & C1) == 0); + break; + + case 020: /* TIOEB */ +#if KS_ITS + if (QITS) { + ctl = 3; + goto its_rdb; + } +#endif + if (uba_read(AB, ctl, &MB, BYTE)) + goto io_fault; + BR &= 0377; + if ((BR & MB) == 0) + PC = (PC + 1) & RMASK; + AR = MB; + break; + + case 021: /* TIONB */ +#if KS_ITS + if (QITS) { + ctl = 1; + goto its_rdb; + } +#endif + if (uba_read(AB, ctl, &MB, BYTE)) + goto io_fault; + BR &= 0377; + if ((BR & MB) != 0) + PC = (PC + 1) & RMASK; + break; + + case 022: /* RDIOB */ +#if KS_ITS + if (QITS) { + if (Mem_read(pi_cycle, 0, 0, 0)) + goto last; + AB = MB & RMASK; + ctl = (int)((MB >> 18) & 017); + } +its_rdb: +#endif + if (uba_read(AB, ctl, &AR, BYTE)) + goto io_fault; + set_reg(AC, AR); + break; + + case 023: /* WRIOB */ +#if KS_ITS + if (QITS) { + if (Mem_read(pi_cycle, 0, 0, 0)) + goto last; + AB = MB & RMASK; + ctl = (int)((MB >> 18) & 017); + } +its_wrb: +#endif + if (uba_write(AB, ctl, BR, BYTE)) + goto io_fault; + break; + + case 024: /* BSIOB */ +#if KS_ITS + if (QITS) { + ctl = 3; + goto its_wrb; + } +#endif + if (uba_read(AB, ctl, &MB, BYTE)) + goto io_fault; + MB |= BR; + if (uba_write(AB, ctl, MB, BYTE)) + goto io_fault; + AR = MB; + break; + + case 025: /* BCIOB */ +#if KS_ITS + if (QITS) { + ctl = 1; + goto its_wrb; + } +#endif + if (uba_read(AB, ctl, &MB, BYTE)) + goto io_fault; + MB &= ~(BR); + if (uba_write(AB, ctl, MB, BYTE)) + goto io_fault; + AR = MB; + break; + + default: + goto muuo; + } +#else int d = ((IR & 077) << 1) | ((AC & 010) != 0); + AR &= RMASK; #if KL if (d == 3) { - irq_flags |= 020; + irq_flags |= SWP_DONE; 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 */ + /* For KL10 special devices */ 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); + double us; + switch (d) { + case 0: /* APR */ + /* BLKI 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 |= BIT4; +#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); 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; + + case 1: /* PI */ + /* BLKI RDERA */ + case 2: /* PAG */ + case 3: /* CCA */ + MB = 0; + if (Mem_write(pi_cycle, 0)) + goto last; + break; + + case 4: /* TIM */ + /* BLKI RDPERF */ + /* Read process execution time */ + us = sim_activate_time_usecs (&cpu_unit[0]); + f = rtc_tim - ((int)us); + update_times(f); + rtc_tim = ((int)us); + if (page_enable) { + AR = (M[ub_ptr + 0505]); + BR = M[ub_ptr + 0504]; + } else { + AR = 0 << 12; + BR = f; } + sim_debug(DEBUG_DATAIO, &cpu_dev, "RDPERF %012llo %012llo\n", AR, BR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + AB = (AB + 1) & RMASK; + MB = BR; + if (Mem_write(pi_cycle, 0)) + goto last; + break; + case 5: /* MTR */ + /* BLKI RDMACT */ + /* RDMACT */ + /* Read memory accounting */ + if (page_enable) { + AR = M[ub_ptr + 0507]; + BR = (M[ub_ptr + 0506] & CMASK); + } else { + AR = 0 << 12; + BR = 0; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "RDMACT %012llo %012llo\n", AR, BR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + AB = (AB + 1) & RMASK; + MB = BR; + if (Mem_write(pi_cycle, 0)) + goto last; + MB = AR; + break; } break; } + /* Fall through */ #endif - if (Mem_read(pi_cycle, 0, 0)) + + case 2: /* 10 BLKO */ + +#if KL + /* For special KL devices */ + if (d <= 05) { + int ctl; + int fcn; + int page; + + switch (d) { + case 0: /* APR */ + /* BLKO WRFIL */ + break; + + case 1: /* PI */ + /* BLKO SBDIAG */ + if (Mem_read(pi_cycle, 0, 0, 0)) + goto last; + AB = (AB + 1) & RMASK; + ctl = (int)(MB >> 31); + fcn = (int)(MB & 037); + if (ctl >= 010 && ctl < 030) { + int mc = MEMSIZE / (512 * 1024); + int c = (ctl - 010); + int s = 0; + if (c < mc) { + switch(fcn) { + case 0: MB = 06000000000LL; break; + case 1: MB = 00500000000LL; break; + case 2: switch ((MB >> 21) & 077) { + case 000: /* yyyyWWWW */ + case 001: /* ww###### */ + case 002: /* #####ppA */ + MB = (ctl << 3) + 06; + break; + case 003: /* bNNSSmmm */ + MB = 0; + break; + } + break; + case 012: + s = ((int)(0176000 & MB)) / 8192; + MB = 0; + if (s > mc || c != s) + MB = 010000000LL; + break; + default: MB = 0; break; + } + } else { + MB = 0; + } + if (Mem_write(pi_cycle, 0)) + goto last; + } + break; + + case 2: /* PAG */ + /* BLKO CLRPT */ + page = (RMASK & AB) >> 9; + + page &= ~7; + /* Map the page */ + for(f = 0; f < 8; f++) { + u_tlb[page+f] = 0; + e_tlb[page+f] = 0; + } + /* If not user do exec mappping */ + if (!t20_page && (page & 0740) == 0340) { + /* Pages 340-377 via UBT */ + page += 01000 - 0340; + for(f = 0; f < 8; f++) + u_tlb[page+f] = 0; + } + break; + + case 3: /* CCA */ + /* BLKO SWPVA */ + case 4: /* TIM */ + /* BLKO WRPAE */ + case 5: /* MTR */ + break; + } + break; + } + +#endif + if (Mem_read(pi_cycle, 0, 0, 0)) goto last; AR = MB; if (hst_lnt) { @@ -8603,21 +11720,64 @@ fetch_opr: goto fetch_opr; case 1: /* 04 DATAI */ - dev_tab[d](DATAI|(d<<2), &AR); - MB = AR; - if (Mem_write(pi_cycle, 0)) - goto last; #if KL - if (d == 4 || d == 5) { /* DATAI TIM, MTR is two words */ + /* For KL10 special devices */ + if (d == 4) { /* DATAI TIM is two words */ + /* DATAI RDTIME */ + double us = sim_activate_time_usecs (&cpu_unit[0]); + f = rtc_tim - ((int)us); + update_times(f); + rtc_tim = ((int)us); + if (page_enable) { + AR = (M[eb_ptr + 0510]); + BR = M[eb_ptr + 0511]; + } else { + AR = 0; + BR = f << 12; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "RDTIM %012llo, %012llo\n", AR, BR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; AB = (AB + 1) & RMASK; MB = BR; if (Mem_write(pi_cycle, 0)) goto last; + break; + } + if (d == 5) { /* DATAI MTR is two words */ + /* RDEACT */ + /* Read executive accounting */ + double us = sim_activate_time_usecs (&cpu_unit[0]); + f = rtc_tim - ((int)us); + update_times(f); + rtc_tim = ((int)us); + if (page_enable) { + AR = M[ub_ptr + 0505]; + BR = (M[ub_ptr + 0504] & CMASK); + } else { + AR = 0; + BR = f << 12; + } + sim_debug(DEBUG_DATAIO, &cpu_dev, "DATAI MTR %012llo %012llo\n", AR, BR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; + AB = (AB + 1) & RMASK; + MB = BR; + if (Mem_write(pi_cycle, 0)) + goto last; + break; } #endif + + dev_tab[d](DATAI|(d<<2), &AR); + MB = AR; + if (Mem_write(pi_cycle, 0)) + goto last; break; case 3: /* 14 DATAO */ - if (Mem_read(pi_cycle, 0, 0)) + if (Mem_read(pi_cycle, 0, 0, 0)) goto last; AR = MB; dev_tab[d](DATAO|(d<<2), &AR); @@ -8644,30 +11804,17 @@ fetch_opr: PC = (PC + 1) & RMASK; break; } - } - break; +#endif + } + break; } - AR &= FMASK; - if (!sac_inh && (i_flags & (SCE|FCEPSE))) { - MB = AR; - if (Mem_write(0, 0)) { - goto last; - } - } - if (!sac_inh && ((i_flags & SAC) || ((i_flags & SACZ) && AC != 0))) - set_reg(AC, AR); /* blank, I, B */ - - if (!sac_inh && (i_flags & SAC2)) { - MQ &= FMASK; - set_reg(AC+1, MQ); - } - - if (hst_lnt && PC >= 020) { - hst[hst_p].fmb = (i_flags & SAC) ? AR: MB; + if (hst_lnt) { + hst[hst_p].fmb = AR; } last: + modify = 0; #if BBN if (QBBN && page_fault) { page_fault = 0; @@ -8677,15 +11824,67 @@ last: goto fetch; } #endif +#if KS + /* Handle page fault and traps */ + if (page_enable && page_fault) { + page_fault = 0; + BYF5 = 0; +#if KS_ITS + if (QITS) { + AB = eb_ptr + 0440; + if (pi_act != 0) { + for(f = 0200; f != 0; f >>= 1) { + if (f & pi_act) + break; + AB += 3; + } + } + if (one_p_arm) + FLAGS |= ADRFLT; + one_p_arm = 0; + } else +#endif + AB = ub_ptr + 0500; + 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 (t20_page == 0) + MB |= (PC & RMASK); + Mem_write_nopage(); + if (t20_page) { + AB++; + MB = (PC & RMASK); + Mem_write_nopage(); + } + AB++; + flag1 = 0; + if (FLAGS & USER) + flag1 = 1; + Mem_read_nopage(); + if (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; + } + PC = MB & RMASK; + xct_flag = 0; + f_load_pc = 1; + f_pc_inh = 1; + } +#endif #if KL /* Handle page fault and traps */ if (page_enable && page_fault) { - if (hst_lnt) { - hst_p = hst_p + 1; - if (hst_p >= hst_lnt) { - hst_p = 0; - } - } page_fault = 0; BYF5 = 0; #if KL_ITS @@ -8747,9 +11946,8 @@ last: f_pc_inh = 1; if (pi_cycle) { pi_cycle = 0; - irq_flags |= 01000; FM[(7 << 4) | 2] = fault_data; - pi_enable = 0; + pi_enable = 0; } } #endif @@ -8765,7 +11963,7 @@ last: Mem_write_nopage(); FLAGS |= trap_flag & (TRP1|TRP2); trap_flag = 1; - AB = ((FLAGS & USER) ? ub_ptr : eb_ptr) | 0420; + AB = ((FLAGS & USER) ? ub_ptr : eb_ptr) + 0420; f_pc_inh = 1; pi_cycle = 1; Mem_read_nopage(); @@ -8773,7 +11971,8 @@ last: } #endif -#if KI | KL + +#if KI | KL | KS if (!f_pc_inh && (trap_flag == 0) && !pi_cycle) { FLAGS &= ~ADRFLT; #else @@ -8783,16 +11982,44 @@ last: } #if ITS + /* Handle 1 proceed for KA ITS */ if (QITS && one_p_arm && (FLAGS & BYTI) == 0) { fault_data |= 02000; mem_prot = 1; one_p_arm = 0; + check_apr_irq(); + } +#endif + +#if KS_ITS + /* Handle 1 proceed for KS ITS */ + if (QITS && one_p_arm && (FLAGS & BYTI) == 0) { + modify = 0; + extend = 0; + one_p_arm = 0; + AB = ub_ptr + 0432; + /* Save flags */ + MB = (((uint64)(FLAGS) << 23) & LMASK) | (PC & RMASK); + Mem_write_nopage(); + /* Read in new PC and flags */ + AB ++; + f = 0; + if (FLAGS & USER) + f = 1; + Mem_read_nopage(); + + FLAGS = (MB >> 23) & 017777; + /* If transistioning from user to executive adjust flags */ + if ((FLAGS & USER) == 0 && f) { + FLAGS |= USERIO; + } + PC = MB & RMASK; } #endif /* Dismiss an interrupt */ if (pi_cycle) { -#if KI | KL +#if KI | KL | KS if (trap_flag != 0) { pi_hold = pi_ov = 0; f_pc_inh = 0; @@ -8804,15 +12031,17 @@ last: if ((!pi_hold) & f_inst_fetch) { pi_cycle = 0; } else { -#if KL +#if KL | KS AB = pi_vect | pi_ov; #else AB = 040 | (pi_enc << 1) | maoff | pi_ov; #endif #if KI | KL Mem_read_nopage(); +#elif KS + Mem_read_word(AB, &MB, 1); #else - Mem_read(1, 0, 1); + Mem_read(1, 0, 1, 0); #endif goto no_fetch; } @@ -8820,7 +12049,7 @@ last: if ((IR & 0700) == 0700) { (void)check_irq_level(); } -#if KL +#if KL | KS AB = pi_vect | pi_ov; #else AB = 040 | (pi_enc << 1) | maoff | pi_ov; @@ -8829,12 +12058,14 @@ last: pi_hold = 0; #if KI | KL Mem_read_nopage(); +#elif KS + Mem_read_word(AB, &MB, 1); #else - Mem_read(1, 0, 1); + Mem_read(1, 0, 1, 0); #endif goto no_fetch; } else { -#if KI | KL +#if KI | KL | KS if (f_pc_inh && trap_flag == 0) set_pi_hold(); /* Hold off all lower interrupts */ #else @@ -8874,7 +12105,7 @@ if (QITS) return reason; } -#if KL +#if KL | KS /* Handle indirection for extended byte instructions */ int @@ -8897,6 +12128,7 @@ do_byte_setup(int n, int wr, int *pos, int *sz) p = (val1 >> 30) & 077; np = (p + (0777 ^ s) + 1) & 0777; /* Advance pointer */ +#if KL if (QKLB && t20_page && pc_sect != 0) { if (p > 36) { /* Extended pointer */ int i = p - 37; @@ -8953,6 +12185,7 @@ do_byte_setup(int n, int wr, int *pos, int *sz) glb_sect = 0; } } else { +#endif if (np & 0400) { np = p = ((0777 ^ s) + 044 + 1) & 0777; val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); @@ -8960,15 +12193,18 @@ do_byte_setup(int n, int wr, int *pos, int *sz) ix = GET_XR(val1); ind = TST_IND(val1) != 0; MB = (val1 & RMASK) | ((val1 & RSIGN)? LMASK:0); +#if KL sect = cur_sect; glb_sect = 0; } +#endif *pos = np & 077; AB = MB & RMASK; if (ix) { temp = get_reg(ix); /* Check if extended indexing */ +#if KL if (QKLB && t20_page && glb_sect != 0 && (temp & SMASK) == 0 && (temp & SECTM) != 0) { temp = (temp + MB) & (SECTM|RMASK); sect = (temp >> 18) & 07777; @@ -8976,13 +12212,15 @@ do_byte_setup(int n, int wr, int *pos, int *sz) glb_sect = 1; } else glb_sect = 0; +#endif temp = MB = (MB + temp) & FMASK; AB = MB & RMASK; } - while (ind & !check_irq_level()) { - if (Mem_read(0, 1, 0)) { + while (ind && pi_pending && !check_irq_level()) { + if (Mem_read(0, 1, 0, 0)) { return 1; } +#if KL /* Check if extended indexing */ if (QKLB && sect != 0) { if (MB & SMASK) { /* Instruction format IFIW */ @@ -9029,11 +12267,13 @@ do_byte_setup(int n, int wr, int *pos, int *sz) glb_sect = 1; } } else { +#endif ix = GET_XR(MB); ind = TST_IND(MB) != 0; AB = MB & RMASK; if (ix) { temp = get_reg(ix); +#if KL /* Check if extended indexing */ if (QKLB && sect != 0 && (temp & SMASK) == 0 && (temp & SECTM) != 0) { temp = (temp + ((AB & RSIGN) ? @@ -9044,10 +12284,13 @@ do_byte_setup(int n, int wr, int *pos, int *sz) AB = 0; } else glb_sect = 0; +#endif temp = MB = (MB + temp) & FMASK; AB = MB & RMASK; } +#if KL } +#endif }; /* Update pointer */ val1 &= PMASK; @@ -9057,13 +12300,11 @@ do_byte_setup(int n, int wr, int *pos, int *sz) 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; + if (Mem_read(0, 0, 0, wr)) { + ptr_flg = BYF5 = 0; return 1; } - modify = 0; return 0; } @@ -9086,7 +12327,7 @@ load_byte(int n, uint64 *data, uint64 fill, int cnt) if (do_byte_setup(n, 0, &p, &s)) goto back; - ptr_flg = 0; +ptr_flg = 0; /* Generate mask for given size */ msk = (uint64)(1) << s; msk--; @@ -9101,7 +12342,7 @@ load_byte(int n, uint64 *data, uint64 fill, int cnt) return 1; back: - ptr_flg = 0; +ptr_flg = 0; val1 = get_reg(n+1); val1 &= PMASK; val1 |= (uint64)(p + s) << 30; @@ -9178,6 +12419,7 @@ adj_byte(int n) p = (val1 >> 30) & 077; /* Advance pointer */ np = (p + (0777 ^ s) + 1) & 0777; +#if KL if (QKLB && t20_page && pc_sect != 0) { if (p > 36) { /* Extended pointer */ int i = p - 37; @@ -9197,9 +12439,12 @@ adj_byte(int n) val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); } } else { +#endif if (np & 0400) val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); +#if KL } +#endif if ((np & 0400) == 0) return; /* Update pointer */ @@ -9235,6 +12480,7 @@ adv_byte(int n) p = (val1 >> 30) & 077; /* Advance pointer */ np = (p + (0777 ^ s) + 1) & 0777; +#if KL if (QKLB && t20_page && pc_sect != 0) { if (p > 36) { /* Extended pointer */ int i = p - 37; @@ -9259,11 +12505,14 @@ adv_byte(int n) } } } else { +#endif if (np & 0400) { np = ((0777 ^ s) + 044 + 1) & 0777; val1 = (val1 & LMASK) | ((val1 + 1) & RMASK); } +#if KL } +#endif np &= 077; /* Update pointer */ val1 &= PMASK; @@ -9311,7 +12560,7 @@ do_xlate(uint32 tbl, uint64 val, int mask) int f; AB = (tbl + (val >> 1)) & RMASK; - if (Mem_read(0, 0, 0)) { + if (Mem_read(0, 0, 0, 0)) { /* Backup ext_ac */ return -2; } @@ -9396,7 +12645,9 @@ do_extend(uint32 ia) uint64 val1, val2; uint64 msk; uint64 reg; +#if KL int xlat_sect; +#endif int f, i; @@ -9411,11 +12662,11 @@ do_extend(uint32 ia) return 1; /* Fetch filler values */ AB = (ia + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill1 = MB; AB = (AB + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill2 = MB; @@ -9452,18 +12703,20 @@ do_extend(uint32 ia) case 004: /* EDIT */ val2 = MB; /* Save address of translate table */ +#if KL if (QKLB && pc_sect != 0 && glb_sect) xlat_sect = (val2 >> 18) & 07777; else xlat_sect = cur_sect; +#endif /* Fetch filler values */ AB = (ia + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill1 = MB; /* Get floating character */ AB = (AB + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill2 = MB; f = 1; @@ -9472,7 +12725,12 @@ do_extend(uint32 ia) /* Read in pattern control */ reg = get_reg(ext_ac); +#if KS + if ((reg & SECTM) != 0) + return 1; +#endif AB = reg & RMASK; +#if KL if (QKLB && pc_sect != 0) { sect = (reg >> 18) & 07777; glb_sect = 1; @@ -9480,16 +12738,19 @@ do_extend(uint32 ia) sect = cur_sect; glb_sect = 0; } - if (Mem_read(0, 0, 0)) +#endif + if (Mem_read(0, 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 KL if (QKLB && pc_sect != 0) reg = (reg & ~(SECTM|RMASK)) | ((reg + 1) & (SECTM|RMASK)); else +#endif reg = (reg & LMASK) | ((reg+1) & RMASK); i = 0; } @@ -9507,8 +12768,10 @@ do_extend(uint32 ia) return 0; a = 1; AB = (val2 + (val1 >> 1)) & RMASK; +#if KL sect = xlat_sect; - if (Mem_read(0, 0, 0)) +#endif + if (Mem_read(0, 0, 0, 0)) return 0; if ((val1 & 1) == 0) MB >>= 18; @@ -9538,6 +12801,7 @@ do_extend(uint32 ia) adj_byte(ext_ac+3); reg |= SMASK; AR = get_reg(ext_ac+3); +#if KL if (QKLB && pc_sect != 0) { sect = (AR >> 18) & 07777; glb_sect = 1; @@ -9545,10 +12809,12 @@ do_extend(uint32 ia) sect = cur_sect; glb_sect = 0; } +#endif AB = AR & RMASK; MB = get_reg(ext_ac+4); if (Mem_write(0, 0)) return 0; +#if KL if (QKLB && pc_sect != 0 && (MB & BIT12) != 0) { AB = (++AR) & RMASK; sect = (AR >> 18) & 07777; @@ -9556,6 +12822,7 @@ do_extend(uint32 ia) if (Mem_write(0,0)) return 0; } +#endif if (fill2 != 0) { if (!store_byte(ext_ac+3, fill1, 0)) { return 0; @@ -9579,6 +12846,7 @@ do_extend(uint32 ia) case 2: /* Set signifigance */ if ((reg & SMASK) == 0) { AR = get_reg(ext_ac+3); +#if KL if (QKLB && pc_sect != 0) { sect = (AR >> 18) & 07777; glb_sect = 1; @@ -9586,10 +12854,12 @@ do_extend(uint32 ia) sect = cur_sect; glb_sect = 0; } +#endif AB = AR & RMASK; MB = get_reg(ext_ac+4); if (Mem_write(0, 0)) return 0; +#if KL if (QKLB && pc_sect != 0 && (MB & BIT12) != 0) { AB = (++AR) & RMASK; sect = (AR >> 18) & 07777; @@ -9597,6 +12867,7 @@ do_extend(uint32 ia) if (Mem_write(0,0)) return 0; } +#endif if (fill2 != 0) { val1 = fill2; i = 1; @@ -9609,6 +12880,7 @@ do_extend(uint32 ia) break; case 4: /* Exchange Mark */ AR = get_reg(ext_ac+3); +#if KL if (QKLB && pc_sect != 0) { sect = (AR >> 18) & 07777; glb_sect = 1; @@ -9616,8 +12888,9 @@ do_extend(uint32 ia) sect = cur_sect; glb_sect = 0; } +#endif AB = AR & RMASK; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) return 0; BR = MB; MB = get_reg(ext_ac+4); @@ -9626,10 +12899,11 @@ do_extend(uint32 ia) return 0; if (Mem_write(0, 0)) return 0; +#if KL if (QKLB && pc_sect != 0 && (BR & BIT12) != 0) { AB = (AR + 1) & RMASK; sect = ((AR + 1)>> 18) & 07777; - if (Mem_read(0, 0, 0)) { + if (Mem_read(0, 0, 0, 0)) { AB = AR & RMASK; /* Restore lower pointer */ sect = (AR >> 18) & 07777; MB = BR; @@ -9647,6 +12921,7 @@ do_extend(uint32 ia) } set_reg(ext_ac+5, AD); } +#endif set_reg(ext_ac+4, BR); break; case 5: @@ -9657,8 +12932,10 @@ do_extend(uint32 ia) case 1: /* Insert Message char */ if ((reg & SMASK) != 0) { AB = (ia + (val1 & 077) + 1) & RMASK; +#if KL sect = cur_sect; - if (Mem_read(0, 0, 0)) +#endif + if (Mem_read(0, 0, 0, 0)) return 0; i = 1; val1 = MB; @@ -9700,10 +12977,12 @@ do_extend(uint32 ia) case 010: /* CVTDBO */ case 011: /* CVTDBT */ +#if KL if (QKLB && pc_sect != 0 && glb_sect) xlat_sect = (AR >> 18) & 07777; else xlat_sect = cur_sect; +#endif val2 = ((AR & RSIGN) ? LMASK : 0) | (AR & RMASK); /* Check if conversion started */ if ((get_reg(ext_ac) & SMASK) == 0) { @@ -9726,7 +13005,9 @@ do_extend(uint32 ia) if (IR == 010) { val1 = (val1 + val2) & FMASK; } else { +#if KL sect = xlat_sect; +#endif f = do_xlate((uint32)(val2 & RMASK), val1, 017); if (f < 0) break; @@ -9772,17 +13053,21 @@ do_extend(uint32 ia) /* Save E1 */ if (IR == 012) { val2 = ((AR & RSIGN) ? LMASK : 0) | (AR & RMASK); +#if KL xlat_sect = cur_sect; +#endif } else { val2 = AB; +#if KL if (QKLB && pc_sect != 0 && glb_sect) xlat_sect = (AR >> 18) & 07777; else xlat_sect = cur_sect; +#endif } /* Get fill */ AB = (ia + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill1 = MB; AR = get_reg(ext_ac); @@ -9798,7 +13083,9 @@ do_extend(uint32 ia) /* Set N bit if non-zero number */ if ((AR | ARX) != 0) reg |= BIT1; +#if KL set_reg(ext_ac+3, reg); +#endif /* Compute number of digits needed for value */ for (f = 0; f < 22; f++) { BRX = ARX + CCM(pow10_tab[f][1]) + 1; @@ -9811,6 +13098,9 @@ do_extend(uint32 ia) /* Check if room to save it */ if (f > (int)(reg & MANT)) return 0; +#if KS + set_reg(ext_ac+3, reg); +#endif /* Fill out left justify */ /* If L, fill leading zeros with fill char */ while ((reg & SMASK) != 0 && (int)(reg & MANT) > f) { @@ -9833,8 +13123,10 @@ do_extend(uint32 ia) if (IR == 013) { /* Read first translation entry */ AB = (val1 + val2) & RMASK; +#if KL sect = xlat_sect; - if (Mem_read(0, 0, 0)) { +#endif + if (Mem_read(0, 0, 0, 0)) { set_reg(ext_ac + 3, (reg & (SMASK|EXPO)) | (f+1)); return 0; } @@ -9862,13 +13154,16 @@ do_extend(uint32 ia) case 015: /* MOVST */ case 016: /* MOVSLJ */ get_mask(ext_ac+3, &msk); +#if KL xlat_sect = cur_sect; +#endif 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 KL if (QKLB) { if (pc_sect != 0 && glb_sect) xlat_sect = (AR >> 18) & 07777; @@ -9876,7 +13171,8 @@ do_extend(uint32 ia) xlat_sect = cur_sect; } else xlat_sect = 0; - if (Mem_read(0, 1, 0)) +#endif + if (Mem_read(0, 1, 0, 0)) return 0; val2 = MB; } else { @@ -9884,7 +13180,7 @@ do_extend(uint32 ia) } /* Fetch filler values */ AB = (ia + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill1 = MB; while ((get_reg(ext_ac) & MANT) != 0) { @@ -9898,7 +13194,9 @@ do_extend(uint32 ia) if ((val1 & ~msk) != 0) return 0; } else if (IR == 015) { +#if KL sect = xlat_sect; +#endif f = do_xlate((uint32)(val2), val1, 07777); if (f < 0) return 0; @@ -9922,7 +13220,7 @@ do_extend(uint32 ia) if (((get_reg(ext_ac) | get_reg(ext_ac+3)) & EMASK) != 0) return 1; AB = (ia + 1) & RMASK; - if (Mem_read(0, 1, 0)) + if (Mem_read(0, 1, 0, 0)) return 0; fill1 = MB; /* While source is larger, skip source */ @@ -9951,6 +13249,7 @@ do_extend(uint32 ia) break; case 020: /* XBLT */ +#if KL if (QKLB) { glb_sect = 1; reg = get_reg(ext_ac); @@ -9962,7 +13261,7 @@ do_extend(uint32 ia) sect = (val1 >> 18) & 00037; AB = val1 & RMASK; ptr_flg = 1; - if (Mem_read(0, 0, 0)) { + if (Mem_read(0, 0, 0, 0)) { val1 = (val1 + 1) & (SECTM|RMASK); goto xblt_done; } @@ -9982,7 +13281,7 @@ do_extend(uint32 ia) sect = (val1 >> 18) & 00037; AB = val1 & RMASK; ptr_flg = 1; - if (Mem_read(0, 0, 0)) + if (Mem_read(0, 0, 0, 0)) goto xblt_done; sect = (val2 >> 18) & 00037; AB = val2 & RMASK; @@ -10003,6 +13302,7 @@ xblt_done: set_reg(ext_ac + 2, val2); return 0; } +#endif case 021: /* GSNGL */ case 022: /* GDBLE */ case 023: /* GDFIX */ @@ -10032,8 +13332,22 @@ rtc_srv(UNIT * uptr) sim_debug(DEBUG_CONO, &cpu_dev, "CONO timmer\n"); set_interrupt(4, clk_irq); } +#elif KS + int_cur -= 2*4096; + if (int_cur & C1) { + irq_flags |= INT_DONE; + int_cur = int_val; + check_apr_irq(); + } + tim_low += 2*4096; + if (tim_low & SMASK) { + tim_high += 1; + tim_low = 0; + } +#if KS_ITS + qua_time += 2*4096; #endif -#if KL +#elif KL update_times(rtc_tim); rtc_tim = (1000000/rtc_tps); #endif @@ -10089,56 +13403,77 @@ 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; + int i; + sim_debug(DEBUG_CONO, dptr, "CPU reset\n"); + BYF5 = uuo_cycle = 0; #if KA | PDP6 -Pl = Ph = 01777; -Rl = Rh = Pflag = 0; -push_ovf = mem_prot = 0; + Pl = Ph = 01777; + Rl = Rh = Pflag = 0; + push_ovf = mem_prot = 0; #if PDP6 -user_io = 0; + user_io = 0; #endif #if ITS | BBN -page_enable = 0; + page_enable = 0; #endif #endif -nxm_flag = clk_flg = 0; -PIR = PIH = PIE = pi_enable = parity_irq = 0; -pi_pending = pi_enc = apr_irq = 0; -ov_irq =fov_irq =clk_en =clk_irq = 0; -pi_restore = pi_hold = 0; -FLAGS = 0; -#if KI | KL -ub_ptr = eb_ptr = 0; -pag_reload = ac_stack = 0; + nxm_flag = clk_flg = 0; + PIR = PIH = PIE = pi_enable = parity_irq = 0; + pi_pending = pi_enc = apr_irq = 0; + ov_irq =fov_irq =clk_en =clk_irq = 0; + pi_restore = pi_hold = 0; + FLAGS = 0; +#if KI | ITS | BBN + ac_stack = 0; +#endif +#if KI | KL | KS + ub_ptr = eb_ptr = 0; + pag_reload = 0; #if KI -fm_sel = small_user = user_addr_cmp = page_enable = 0; + 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; + fm_sel = prev_ctx = user_addr_cmp = page_enable = t20_page = 0; + irq_enable = irq_flags = 0; +#if KL + sect = cur_sect = pc_sect = 0; +#endif #endif #endif #if BBN -exec_map = 0; + exec_map = 0; +#endif + for(i=0; i < 128; dev_irq[i++] = 0); +#if KS | KL + cst = 0; +#endif +#if KS + int_cur = int_val = 0; + uba_reset(); +#endif +#if KI | KL | ITS | BBN | KS + for (i = 0; i < 512; i++) { + e_tlb[i] = 0; + u_tlb[i] = 0; + } + for (;i < 546; i++) + u_tlb[i] = 0; #endif -for(i=0; i < 128; dev_irq[i++] = 0); -sim_brk_types = SWMASK('E') | SWMASK('W') | SWMASK('R'); -sim_brk_dflt = SWMASK ('E'); -sim_clock_precalibrate_commands = pdp10_clock_precalibrate_commands; -sim_vm_initial_ips = 4 * SIM_INITIAL_IPS; -sim_rtcn_init_unit (&cpu_unit[0], cpu_unit[0].wait, TMR_RTC); -sim_activate(&cpu_unit[0], 10000); + sim_brk_types = SWMASK('E') | SWMASK('W') | SWMASK('R'); + sim_brk_dflt = SWMASK ('E'); + sim_clock_precalibrate_commands = pdp10_clock_precalibrate_commands; + sim_vm_initial_ips = 4 * SIM_INITIAL_IPS; + sim_rtcn_init_unit (&cpu_unit[0], cpu_unit[0].wait, TMR_RTC); + sim_activate(&cpu_unit[0], 1000); #if MPX_DEV -mpx_enable = 0; + mpx_enable = 0; #endif #ifdef PANDA_LIGHTS -ka10_lights_init (); + ka10_lights_init (); #endif -sim_vm_interval_units = "cycles"; -sim_vm_step_unit = "instruction"; -return SCPE_OK; + sim_vm_interval_units = "cycles"; + sim_vm_step_unit = "instruction"; + return SCPE_OK; } /* Memory examine */ @@ -10150,12 +13485,12 @@ if (vptr == NULL) if (ea < 020) *vptr = FM[ea] & FMASK; else { -#if KL | KI +#if KL | KI | KS if (sw & SWMASK ('V')) { int uf = ((sw & SWMASK('U')) != 0); int page = ea >> 9; uint32 tlb; -#if KL +#if KL | KS if (!uf && !t20_page && (page & 0740) == 0340) { #else if (!uf && (page & 0740) == 0340) { @@ -10188,12 +13523,12 @@ 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 KL | KI | KS if (sw & SWMASK ('V')) { int uf = ((sw & SWMASK('U')) != 0); int page = ea >> 9; uint32 tlb; -#if KL +#if KL | KS if (!uf && !t20_page && (page & 0740) == 0340) { #else if (!uf && (page & 0740) == 0340) { @@ -10241,136 +13576,146 @@ cpu_unit[0].capac = (uint32)val; return SCPE_OK; } +#if !KS /* Build device dispatch table */ t_bool build_dev_tab (void) { -DEVICE *dptr; -DIB *dibp; -uint32 i, j, d; + DEVICE *dptr; + DIB *dibp; + uint32 i, j, d; #if KL -uint32 rh20; + uint32 rh20; +#endif +#if !PDP6 + int rh_idx; #endif -int rh_idx; -/* Set trap offset based on MAOFF flag */ -maoff = (cpu_unit[0].flags & UNIT_MAOFF)? 0100 : 0; + /* Set trap offset based on MAOFF flag */ + maoff = (cpu_unit[0].flags & UNIT_MAOFF)? 0100 : 0; #if KA -/* Set up memory access routines based on current CPU type. */ + /* Set up memory access routines based on current CPU type. */ -/* Default to KA */ -Mem_read = &Mem_read_ka; -Mem_write = &Mem_write_ka; + /* Default to KA */ + Mem_read = &Mem_read_ka; + Mem_write = &Mem_write_ka; #if ITS -if (QITS) { - Mem_read = &Mem_read_its; - Mem_write = &Mem_write_its; -} + if (QITS) { + Mem_read = &Mem_read_its; + Mem_write = &Mem_write_its; + } #endif #if BBN -if (QBBN) { - Mem_read = &Mem_read_bbn; - Mem_write = &Mem_write_bbn; -} + if (QBBN) { + Mem_read = &Mem_read_bbn; + Mem_write = &Mem_write_bbn; + } #endif #if WAITS /* Waits without BBN pager */ -if (QWAITS && !QBBN) { - Mem_read = &Mem_read_waits; - Mem_write = &Mem_write_waits; -} + if (QWAITS && !QBBN) { + Mem_read = &Mem_read_waits; + Mem_write = &Mem_write_waits; + } #endif #endif -/* Clear device and interrupt table */ -for (i = 0; i < 128; i++) { - dev_tab[i] = &null_dev; - dev_irqv[i] = NULL; -} + /* Clear device and interrupt table */ + for (i = 0; i < 128; i++) { + dev_tab[i] = &null_dev; + dev_irqv[i] = NULL; + } -/* Set up basic devices. */ -dev_tab[0] = &dev_apr; -dev_tab[1] = &dev_pi; + /* Set up basic devices. */ + dev_tab[0] = &dev_apr; + dev_tab[1] = &dev_pi; #if KI | KL -dev_tab[2] = &dev_pag; + 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; + 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 & RH20 devices */ -#if KL -rh20 = 0540; -#endif -rh_idx = 0; -for (i = 0; (dptr = rh_devs[i]) != NULL; i++) { - dibp = (DIB *) dptr->ctxt; - if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ - 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[rh_idx].dev_num = d; - rh[rh_idx].dev = dptr; - rh[rh_idx].rh = dibp->rh; - dibp->rh->devnum = d; - rh_idx++; + if (QBBN) { + dev_tab[024>>2] = &dev_pag; } -} +#endif -/* Assign all remaining devices */ -for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { - dibp = (DIB *) dptr->ctxt; - if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ - for (j = 0; j < dibp->num_devs; j++) { /* loop thru disp */ - if (dibp->io) { /* any dispatch? */ - d = dibp->dev_num; - if (d & (RH10_DEV|RH20_DEV)) /* Skip RH10 & RH20 devices */ - continue; - if (dev_tab[(d >> 2) + j] != &null_dev) { - /* already filled? */ - sim_printf ("%s device number conflict at %02o\n", - sim_dname (dptr), d + (j << 2)); - return TRUE; - } - dev_tab[(d >> 2) + j] = dibp->io; /* fill */ - dev_irqv[(d >> 2) + j] = dibp->irq; - } /* end if dsp */ - } /* end for j */ - } /* end if enb */ - } /* end for i */ +#if (NUM_DEVS_RP + NUM_DEVS_RS + NUM_DEVS_TU) > 0 +#if !PDP6 + /* Assign all RH10 & RH20 devices */ +#if KL + rh20 = 0540; +#endif + rh_idx = 0; + for (i = 0; (dptr = rh_devs[i]) != NULL; i++) { + dibp = (DIB *) dptr->ctxt; + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + 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[rh_idx].dev_num = d; + rh[rh_idx].dev = dptr; + rh[rh_idx].rh = dibp->rh; + dibp->rh->devnum = d; + rh_idx++; + } + } +#endif +#endif + + /* Assign all remaining devices */ + for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + dibp = (DIB *) dptr->ctxt; + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + for (j = 0; j < dibp->num_devs; j++) { /* loop thru disp */ + if (dibp->io) { /* any dispatch? */ + d = dibp->dev_num; +#if (NUM_DEVS_RP + NUM_DEVS_RS + NUM_DEVS_TU) > 0 + if (d & (RH10_DEV|RH20_DEV)) /* Skip RH10 & RH20 devices */ + continue; +#endif + if (dev_tab[(d >> 2) + j] != &null_dev) { + /* already filled? */ + sim_printf ("%s device number conflict at %02o\n", + sim_dname (dptr), d + (j << 2)); + return TRUE; + } + dev_tab[(d >> 2) + j] = dibp->io; /* fill */ + dev_irqv[(d >> 2) + j] = dibp->irq; + } + } + } + } return FALSE; } +#endif -#if KI | KL +#if KI | KL | KS /* Set serial */ t_stat cpu_set_serial (UNIT *uptr, int32 val, CONST char *cptr, void *desc) @@ -10470,10 +13815,15 @@ for (k = 0; k < lnt; k++) { /* print specified */ fputs (" ", st); #if KL if (QKLB) - fprintf(st, "%08o ", h->ea & 0777777777); + fprintf(st, "%08o ", h->ea & 077777777); else #endif +#if KS + fprintf (st, "%c", (h->ea & 07000000) ? ((h->ea >> 18) & 07) + '0': ' '); + fprintf (st, "%06o ", h->ea & 0777777); +#else fprintf (st, "%06o ", h->ea); +#endif fputs (" ", st); fprint_val (st, h->mb, 8, 36, PV_RZRO); fputs (" ", st); @@ -10481,7 +13831,9 @@ 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); +#if KL fprintf (st, "%02o ", h->prev_sect); +#endif #else fprintf (st, "%06o ", h->flags); #endif @@ -10518,6 +13870,9 @@ cpu_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) const char * cpu_description (DEVICE *dptr) { +#if KS + return "KS10 CPU"; +#endif #if KL return "KL10 CPU"; #endif diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index 7e90cc60..27fe635f 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -50,11 +50,15 @@ #define KL 0 #endif +#ifndef KS +#define KS 0 +#endif + #if KL #define EPT440 0 /* Force KL10 to use as 440 section address */ #endif -#if (PDP6 + KA + KI + KL) != 1 +#if (PDP6 + KA + KI + KL + KS) != 1 #error "Please define only one type of CPU" #endif @@ -82,6 +86,11 @@ #define KL_ITS KL #endif +/* Support for ITS on KS */ +#ifndef KS_ITS +#define KS_ITS KS +#endif + #ifndef PDP6_DEV /* Include PDP6 devices */ #define PDP6_DEV PDP6|WAITS #endif @@ -195,6 +204,30 @@ extern DEBTAB crd_debug[]; #define FPMMASK 00000000000077777777777LL #define FPRBIT2 00000000000100000000000LL #define FPRBIT1 00000000000200000000000LL +#if KS +#define IOCTL 00000017000000LL +#endif + +/* IRQ Flags in APR */ +#if KL +#define SWP_DONE 0000020 /* Cache sweep done */ +#define PFAIL 0000040 /* Power failure */ +#define ADDR_PAR 0000100 /* Address Parity error */ +#define CACHE_DIR 0000200 /* Cache Parity error */ +#define MB_PAR 0000400 /* Memory parity error */ +#define INOUT_FAIL 0001000 /* Fault during IRQ */ +#define NXM_MEM 0002000 /* Non-existent memory */ +#define SBUS_ERR 0004000 /* SBus Error */ +#endif +#if KS +#define CON_IRQ 0000020 /* Interrupt from Console */ +#define INT_DONE 0000040 /* Interval timer complete */ +#define COR_MEM 0000100 /* Corrected memory error */ +#define MB_ERR 0000200 /* Uncorrectable memory error */ +#define NXM_MEM 0000400 /* No memory */ +#define PFAIL 0001000 /* Power Failure */ +#define FLAG_24 0004000 /* Spare */ +#endif #define CM(x) (FMASK ^ (x)) #define CCM(x) ((CMASK ^ (x)) & CMASK) @@ -231,12 +264,16 @@ extern DEBTAB crd_debug[]; #define NODIV 000001 /* 000040 */ #define FLTUND 000002 /* 000100 */ #endif -#if KI|KL +#if KI|KL|KS #define TRP1 000004 /* 000200 */ #define TRP2 000010 /* 000400 */ #define ADRFLT 000020 /* 001000 */ +#if KI | KL #define PUBLIC 000040 /* 002000 */ #else +#define PUBLIC 000000 /* 000000 */ +#endif +#else #define TRP1 000000 #define TRP2 000000 #define ADRFLT 000000 @@ -261,7 +298,7 @@ extern DEBTAB crd_debug[]; #if KI|KL #define PRV_PUB 020000 /* Overflow in excutive mode */ #else -#define PRV_PUB 000000 /* Not on KA or PDP6 */ +#define PRV_PUB 000000 /* Not on KA, KS or PDP6 */ #endif #ifdef ITS #ifdef PURE @@ -280,13 +317,13 @@ extern DEBTAB crd_debug[]; #if KI_22BIT|KI #define MAXMEMSIZE 4096 * 1024 -#else -#if PDP6 +#elif PDP6 #define MAXMEMSIZE 256 * 1024 +#elif KS +#define MAXMEMSIZE 1024 * 1024 #else #define MAXMEMSIZE 1024 * 1024 #endif -#endif #define MEMSIZE (cpu_unit[0].capac) #define ICWA 0000000000776 @@ -323,6 +360,10 @@ extern DEBTAB crd_debug[]; #define DEF_SERIAL 1025 /* Default DEC test machine */ #endif +#if KS +#define DEF_SERIAL 4097 /* Default DEC test machine */ +#endif + #if BBN #define BBN_PAGE 0000017777777LL #define BBN_TRPPG 0000017000000LL @@ -339,7 +380,7 @@ extern DEBTAB crd_debug[]; #define BBN_MERGE 0161740000000LL #endif -#if KL +#if KL|KS /* KL10 TLB paging bits */ #define KL_PAG_A 0400000 /* Access */ #define KL_PAG_P 0200000 /* Public */ @@ -367,7 +408,11 @@ extern DEBTAB crd_debug[]; #define UNIT_KL10B (1 << UNIT_V_PAGE) #define UNIT_TWOSEG (0) #else +#if KA #define UNIT_TWOSEG (1 << UNIT_V_PAGE) +#else +#define UNIT_TWOSEG (0) +#endif #endif #define UNIT_ITSPAGE (2 << UNIT_V_PAGE) #define UNIT_BBNPAGE (4 << UNIT_V_PAGE) @@ -415,6 +460,10 @@ extern void restore_pi_hold(); extern void set_pi_hold(); extern UNIT cpu_unit[]; extern UNIT ten11_unit[]; +#if KS +extern DEVICE lp20_dev; +extern DEVICE ch11_dev; +#endif #if KL extern DEVICE dte_dev; extern DEVICE lp20_dev; @@ -462,15 +511,104 @@ extern DEVICE dkb_dev; extern DEVICE auxcpu_dev; extern DEVICE slave_dev; extern DEVICE dpk_dev; +extern DEVICE tv_dev; extern DEVICE wcnsls_dev; /* MIT Spacewar Consoles */ extern DEVICE ocnsls_dev; /* Old MIT Spacewar Consoles */ extern DEVICE ai_dev; +extern DEVICE dn_dev; extern DEVICE dct_dev; /* PDP6 devices. */ extern DEVICE dtc_dev; extern DEVICE mtc_dev; extern DEVICE dsk_dev; extern DEVICE dcs_dev; +extern DEVICE dz_dev; +extern DEVICE kmc_dev; +extern DEVICE dup_dev; +extern DEVICE tcu_dev; +#if KS + +struct rh_if { + int (*dev_write)(DEVICE *dptr, struct rh_if *rh, int reg, uint32 data); + int (*dev_read)(DEVICE *dptr, struct rh_if *rh, int reg, uint32 *data); + void (*dev_reset)(DEVICE *dptr); + struct pdp_dib *dib; /* Pointer back to DIB */ + int drive; /* Last drive selected */ + t_uint64 buf; /* Data buffer */ + uint32 status; /* Status word */ + uint16 cs1; /* Control register 1 */ + uint16 cs2; /* Control register 1 */ + uint16 error; /* Controller Error register */ + uint32 wcr; /* Current word count */ + uint32 cda; /* Current bus address */ + uint16 dba; /* Input data buffer */ + uint16 dbb; /* Output data buffer*/ + int rae; /* Access register error */ + int attn; /* Attention bits */ + int xfer_drive; /* Current transfering drive */ + uint16 regs[16]; /* Space for TM03 formater */ +}; + +/* Device context block */ +struct pdp_dib { + uint32 uba_addr; /* device address, includes adaptor */ + uint32 uba_mask; /* Compare mask */ + uint16 uba_vect; /* Floating IRQ vector */ + uint16 uba_br; /* Unibus IRQ level */ + uint16 uba_ctl; /* Unibus controller number */ + t_stat (*rd_io)(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); + t_stat (*wr_io)(DEVICE *dptr, t_addr addr, uint16 data, int32 access); + uint16 (*irqv)(struct pdp_dib *dibp); + uint8 uba_irq_pend; /* Device has pending */ + struct rh_if *rh11_if; +}; +typedef struct pdp_dib DIB; + +void cty_wakeup(); +void cty_interrupt(); +void cty_execute(int addr); +t_stat cty_reset (DEVICE *dptr); + + +#define WORD 0 +#define BYTE 1 +int uba_read(t_addr addr, int ctl, uint64 *data, int access); +int uba_write(t_addr addr, int ctl, uint64 data, int access); +int uba_read_npr(t_addr addr, uint16 ctl, uint64 *data); +int uba_write_npr(t_addr addr, uint16 ctl, uint64 data); +int uba_read_npr_byte(t_addr addr, uint16 ctl, uint8 *data); +int uba_write_npr_byte(t_addr addr, uint16 ctl, uint8 data); +int uba_read_npr_word(t_addr addr, uint16 ctl, uint16 *data); +int uba_write_npr_word(t_addr addr, uint16 ctl, uint16 data); +void uba_set_irq(DIB *dibp, int vect); +void uba_clr_irq(DIB *dibp, int vect); +t_addr uba_get_vect(t_addr addr, int lvl, int dev); +void uba_set_parity(uint16 ctl); +uint16 uba_rh_vect(struct pdp_dib *dibp); +int uba_rh_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +int uba_rh_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +void uba_reset(); + +t_stat uba_set_addr(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat uba_show_addr (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat uba_set_br(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat uba_show_br (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat uba_set_vect(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat uba_show_vect (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat uba_set_ctl(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat uba_show_ctl (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +void rh_reset(DEVICE *dptr, struct rh_if *rh); +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); +#else extern t_stat (*dev_tab[128])(uint32 dev, t_uint64 *data); #define VEC_DEVMAX 8 /* max device vec */ @@ -512,6 +650,7 @@ struct rh_if { int rae; /* Access register error */ int attn; /* Attention bits */ int xfer_drive; /* Current transfering drive */ + uint16 regs[16]; /* Space for TM03 formater */ }; /* Device context block */ @@ -551,6 +690,7 @@ 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); +void rh_reset(DEVICE *dptr, struct rh_if *rh); #if KL void rh20_setup(struct rh_if *rhc); #endif @@ -565,23 +705,18 @@ 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); extern void ka10_lights_main (t_uint64); extern void ka10_lights_set_aux (int); extern void ka10_lights_clear_aux (int); - +#endif /* I/O system parameters */ +#if !(PDP6 | KS) #define NUM_DEVS_LP 1 -#if KL -#define NUM_DEVS_PT 0 -#define NUM_DEVS_CR 0 -#define NUM_DEVS_CP 0 -#else +#endif +#if !(KL | KS) #define NUM_DEVS_PT 1 #define NUM_DEVS_CR 1 #define NUM_DEVS_CP 1 @@ -597,34 +732,48 @@ extern void ka10_lights_clear_aux (int); #define NUM_DEVS_DCS 1 #define NUM_DEVS_SLAVE PDP6 #endif -#if !PDP6 +#if !(PDP6 | KS) #define NUM_DEVS_DC 1 #define NUM_DEVS_MT 1 +#endif #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 64 #define NUM_DEVS_NIA 1 -#else +#define NUM_DEVS_DN 0 +#elif KS +#define NUM_DEVS_LP20 1 +#define NUM_DEVS_DZ 4 +#define NUM_DEVS_TCU 1 +#define NUM_DEVS_DUP 2 +#define NUM_DEVS_KMC 2 +#if KS_ITS +#define NUM_DEVS_IMP KS_ITS +#define NUM_DEVS_CH11 KS_ITS +#endif +#endif +#if KA | KI #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 +#if KS +#define NUM_DEVS_RP 1 +#elif KA | KI | KL #define NUM_DEVS_RP 4 #define NUM_DEVS_RS 1 +#endif +#if !(PDP6) #define NUM_DEVS_TU 1 +#endif +#if KA #define NUM_DEVS_PMP WAITS #define NUM_DEVS_DKB (WAITS * USE_DISPLAY) #define NUM_DEVS_III (WAITS * USE_DISPLAY) -#define NUM_DEVS_PD ITS | KL_ITS +#define NUM_DEVS_TV (WAITS * USE_DISPLAY) +#define NUM_DEVS_PD ITS #define NUM_DEVS_PCLK WAITS #define NUM_DEVS_IMX ITS #define NUM_DEVS_STK ITS @@ -632,11 +781,16 @@ extern void ka10_lights_clear_aux (int); #define NUM_DEVS_MTY ITS #define NUM_DEVS_TEN11 ITS #define NUM_DEVS_AUXCPU ITS -#define NUM_DEVS_IMP 1 -#define NUM_DEVS_CH10 ITS | KL_ITS +#define NUM_DEVS_IMP ITS +#define NUM_DEVS_CH10 ITS #define NUM_DEVS_DPK ITS #define NUM_DEVS_AI ITS #endif +#if KL_ITS +#define NUM_DEVS_PD KL_ITS +#define NUM_DEVS_IMP KL_ITS +#define NUM_DEVS_CH10 KL_ITS +#endif #if MAGIC_SWITCH && !KA && !ITS #error "Magic switch only valid on KA10 with ITS mods" #endif @@ -645,12 +799,20 @@ extern void ka10_lights_clear_aux (int); extern t_bool sim_idle_enab; +#if !KS extern struct rh_dev rh[]; +#endif extern t_uint64 M[MAXMEMSIZE]; extern t_uint64 FM[]; extern uint32 PC; extern uint32 FLAGS; +#if NUM_DEVS_TEN11 +int ten11_read (t_addr addr, t_uint64 *data); +int ten11_write (t_addr addr, t_uint64 data); +extern t_addr ten11_base; +extern t_addr ten11_end; +#endif #if NUM_DEVS_AUXCPU extern t_addr auxcpu_base; int auxcpu_read (t_addr addr, uint64 *); diff --git a/PDP10/kx10_disk.h b/PDP10/kx10_disk.h index 465f0b78..829dc3bf 100644 --- a/PDP10/kx10_disk.h +++ b/PDP10/kx10_disk.h @@ -24,7 +24,7 @@ /* Flags in the unit flags word */ -#define UNIT_V_FMT (UNIT_V_UF + 8) +#define UNIT_V_FMT (UNIT_V_UF + 7) #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) diff --git a/PDP10/kx10_dp.c b/PDP10/kx10_dp.c index aea78c2d..9418c2cc 100644 --- a/PDP10/kx10_dp.c +++ b/PDP10/kx10_dp.c @@ -717,7 +717,7 @@ t_stat dp_svc (UNIT *uptr) CLR_BUF(uptr); } if (r) - sim_activate(uptr, 40); + sim_activate(uptr, 25); else { uptr->STATUS &= ~(SRC_DONE|END_CYL|BUSY); uptr->UFLAGS |= DONE; diff --git a/PDP10/kx10_dpy.c b/PDP10/kx10_dpy.c index 38f5d9b3..45cab60e 100644 --- a/PDP10/kx10_dpy.c +++ b/PDP10/kx10_dpy.c @@ -109,6 +109,7 @@ #include "kx10_defs.h" #include "sim_video.h" +#include #ifndef NUM_DEVS_DPY #define NUM_DEVS_DPY 0 @@ -120,6 +121,8 @@ #define DPY_DEVNUM 0130 +#define FULLSCREEN (1 << (UNIT_V_UF)) + #define RRZ(W) ((W) & RMASK) #define XWD(L,R) ((((uint64)(L))<<18)|((uint64)(R))) @@ -187,12 +190,20 @@ UNIT dpy_unit[] = { #define UPTR(UNIT) (dpy_unit+(UNIT)) +MTAB dpy_mod[] = { + { FULLSCREEN, FULLSCREEN, "FULLSCREEN", "FULLSCREEN", NULL, NULL, NULL, + "Display in fullscreen"}, + { FULLSCREEN, 0, NULL, "WINDOW", NULL, NULL, NULL, + "Display in window"}, + { 0 } +}; + DEVICE dpy_dev = { - "DPY", dpy_unit, NULL, NULL, + "DPY", dpy_unit, NULL, dpy_mod, NUM_DEVS_DPY, 0, 0, 0, 0, 0, NULL, NULL, dpy_reset, NULL, NULL, NULL, - &dpy_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_DISPLAY, 0, NULL, + &dpy_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_DISPLAY, 0, dev_debug, NULL, NULL, NULL, NULL, NULL, &dpy_description }; @@ -348,10 +359,21 @@ static void dpy_joy_motion(int which, int axis, int value) static void dpy_joy_button(int which, int button, int state) { if (which < JOY_MAX_UNITS && button < JOY_MAX_BUTTONS) { - joy_buttons[which * JOY_MAX_UNITS + button] = state; + joy_buttons[which * JOY_MAX_BUTTONS + button] = state; } } +static int dpy_keyboard (SIM_KEY_EVENT *ev) +{ + sim_debug(DEBUG_DATA, &dpy_dev, "Key %d %s\n", + ev->key, ev->state == SIM_KEYPRESS_DOWN ? "down" : "up"); + if (ev->state == SIM_KEYPRESS_UP && ev->key == SIM_KEY_F11) { + vid_set_fullscreen (!vid_is_fullscreen ()); + return 1; + } + return 0; +} + /* Reset routine */ t_stat dpy_reset (DEVICE *dptr) @@ -359,8 +381,15 @@ t_stat dpy_reset (DEVICE *dptr) if (dptr->flags & DEV_DIS) { display_close(dptr); } else { +#if ITS + if (stk_dev.flags & DEV_DIS) { + sim_debug(DEBUG_DETAIL, &dpy_dev, "Grabbing keyboard\n"); + vid_display_kb_event_process = dpy_keyboard; + } +#endif display_reset(); ty340_reset(dptr); + vid_set_fullscreen (dpy_unit[0].flags & FULLSCREEN); vid_register_gamepad_motion_callback (dpy_joy_motion); vid_register_gamepad_button_callback (dpy_joy_button); } @@ -427,37 +456,63 @@ 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) +#define UNIT_JOY (1 << DEV_V_UF) /* Use USB gaming devices. */ +#define UNIT_CSCOPE (1 << (DEV_V_UF+1)) /* Enable color scope. */ t_stat wcnsls_devio(uint32 dev, uint64 *data); +t_stat wcnsls_svc (UNIT *uptr); +t_stat wcnsls_reset (DEVICE *dptr); const char *wcnsls_description (DEVICE *dptr); +static uint64 dev420_cono = 0; +static uint8 cscope_r = 0; +static uint8 cscope_g = 0; +static uint8 cscope_b = 0; +static VID_DISPLAY *cscope_display = NULL; +static uint32 fade[512 * 512]; +static uint32 dot[7 * 7]; + DIB wcnsls_dib[] = { { WCNSLS_DEVNUM, 1, &wcnsls_devio, NULL }}; MTAB wcnsls_mod[] = { { UNIT_JOY, UNIT_JOY, "JOYSTICK", "JOYSTICK", NULL, NULL, NULL, "Use USB joysticks"}, + { UNIT_CSCOPE, UNIT_CSCOPE, "CSCOPE", "CSCOPE", NULL, NULL, NULL, + "Enable color scope"}, { 0 } }; UNIT wcnsls_unit[] = { - { UDATA (NULL, UNIT_IDLE, 0) }}; + { UDATA (wcnsls_svc, UNIT_IDLE, 0) }}; + +REG wcnsls_reg[] = { + {ORDATA(CONO, dev420_cono, 18)}, + {0} +}; DEVICE wcnsls_dev = { - "WCNSLS", wcnsls_unit, NULL, wcnsls_mod, + "WCNSLS", wcnsls_unit, wcnsls_reg, wcnsls_mod, NUM_DEVS_WCNSLS, 0, 0, 0, 0, 0, + NULL, NULL, wcnsls_reset, NULL, NULL, NULL, - NULL, NULL, NULL, - &wcnsls_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, NULL, + &wcnsls_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG, 0, dev_debug, NULL, NULL, NULL, NULL, NULL, &wcnsls_description }; const char *wcnsls_description (DEVICE *dptr) { - return "MIT Spacewar Consoles"; + return "MIT Spacewar Consoles, and DEC color scope"; } +/* Device 420 CONO bits. */ +#define CONO_BLUE 0000020 /* Color scope blue enable */ +#define CONO_SPCWAR 0000040 /* Spacewar consoles */ +#define CONO_GREEN 0002000 /* Color scope green enable */ +#define CONO_RANDOM 0004000 /* Random switches */ +#define CONO_RED 0200000 /* Color scope red enable */ +#define CONO_KNIGHT 0400000 /* Switches in TK's office */ + /* * map 32-bit "spacewar_switches" value to what PDP-6/10 game expects * (four 9-bit bytes) @@ -485,55 +540,86 @@ const char *wcnsls_description (DEVICE *dptr) #define BUT2 (JOY_MAX_BUTTONS*2) #define BUT3 (JOY_MAX_BUTTONS*3) +static void +cscope_init (void) +{ + int i; + fade[0] = vid_map_rgba_window (cscope_display, 0, 0, 0, 240); + for (i = 1; i < 512 * 512; i++) + fade[i] = fade[0]; +} + +static void +cscope_plot(int x, int y) +{ + int i, j; + for (i = 0; i < 7; i++) { + for (j = 0; j < 7; j++) { + int dx = i - 3, dy = j - 3; + int r2 = dx*dx + dy*dy; + double focus = 0.2; + double intensity = 0xFF/15.0; + double alpha = 0xFF*exp(-focus*r2); + dot[i + 7*j] = vid_map_rgba_window (cscope_display, + (uint8)(intensity*(cscope_r << 4)), + (uint8)(intensity*(cscope_g << 4)), + (uint8)(intensity*(cscope_b << 4)), + (uint8)alpha); + } + } + + vid_draw_window (cscope_display, x - 3, 511 - y - 3, 7, 7, dot); +} + static uint64 joystick_switches (void) { - uint64 switches = 0777777777777LL; + 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[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[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[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); + 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; + return switches; } static uint64 keyboard_switches (void) @@ -562,17 +648,66 @@ static uint64 keyboard_switches (void) return switches; } +t_stat wcnsls_svc (UNIT *uptr) +{ + vid_refresh_window (cscope_display); + vid_draw_window (cscope_display, 0, 0, 512, 512, fade); + sim_activate_after (uptr, 33333); + return SCPE_OK; +} + +t_stat +wcnsls_reset (DEVICE *dptr) +{ + t_stat r; + if (sim_switches & SWMASK('P') || dptr->flags & DEV_DIS || + (wcnsls_unit->flags & UNIT_CSCOPE) == 0) { + sim_cancel (wcnsls_unit); + if (cscope_display != NULL) + vid_close_window (cscope_display); + cscope_display = NULL; + } else if (cscope_display == NULL) { + r = vid_open_window (&cscope_display, dptr, "Color scope", 512, 512, 0); + if (r != SCPE_OK) + return r; + /* Allow time for window to open and data structures to settle. */ + sim_os_sleep (1); + r = vid_set_alpha_mode (cscope_display, SIM_ALPHA_BLEND); + if (r != SCPE_OK) + return r; + sim_activate_abs (wcnsls_unit, 0); + cscope_init (); + } + return SCPE_OK; +} + t_stat wcnsls_devio(uint32 dev, uint64 *data) { switch (dev & 3) { case CONO: /* CONO WCNSLS,40 ;enable spacewar consoles */ + sim_debug (DEBUG_CONO, &wcnsls_dev, "%06llo\n", *data); + dev420_cono = *data; + if (dev420_cono & CONO_RED) + cscope_r = (dev420_cono >> 12) & 017; + if (dev420_cono & CONO_GREEN) + cscope_g = (dev420_cono >> 6) & 017; + if (dev420_cono & CONO_BLUE) + cscope_b = dev420_cono & 017; + break; + + case DATAO: + sim_debug (DEBUG_DATAIO, &wcnsls_dev, "DATAO %012llo\n", *data); + if (wcnsls_unit->flags & UNIT_CSCOPE) + cscope_plot((*data >> 9) & 0777, *data & 0777); break; case DATAI: - if (wcnsls_unit->flags & UNIT_JOY) { - *data = joystick_switches (); - } else { - *data = keyboard_switches (); + if (dev420_cono & CONO_SPCWAR) { + if (wcnsls_unit->flags & UNIT_JOY) { + *data = joystick_switches (); + } else { + *data = keyboard_switches (); + } } sim_debug(DEBUG_DATAIO, &wcnsls_dev, "WCNSLS %03o DATI %012llo PC=%06o\n", @@ -621,39 +756,39 @@ const char *ocnsls_description (DEVICE *dptr) static uint64 old_switches (void) { - uint64 switches = 0; + 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[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; + 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; + return switches; } t_stat ocnsls_devio(uint32 dev, uint64 *data) { diff --git a/PDP10/kx10_dt.c b/PDP10/kx10_dt.c index fe4a7461..242243c9 100644 --- a/PDP10/kx10_dt.c +++ b/PDP10/kx10_dt.c @@ -1233,7 +1233,6 @@ void dt_flush (UNIT* uptr) uint32 ba, k, *fbuf; if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ - sim_printf ("%s: writing buffer to file: %s\n", sim_uname (uptr), uptr->filename); rewind (uptr->fileref); /* start of file */ fbuf = (uint32 *) uptr->filebuf; /* file buffer */ if (uptr->flags & UNIT_8FMT) { /* 12b? */ @@ -1285,8 +1284,10 @@ t_stat dt_detach (UNIT* uptr) sim_cancel (uptr); uptr->CMD = uptr->pos = 0; } - if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) /* any data? */ - dt_flush(uptr); /* end if hwmark */ + 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 */ diff --git a/PDP10/kx10_imp.c b/PDP10/kx10_imp.c index c4bcc714..31bfb293 100644 --- a/PDP10/kx10_imp.c +++ b/PDP10/kx10_imp.c @@ -44,6 +44,59 @@ #define TYPE_BBN 1 /* BBN style interface TENEX */ #define TYPE_WAITS 2 /* IMP connected to waits system. */ +#define TYPE_UNI 0 /* Unibus byte order */ +#define TYPE_SIMP 1 /* PDP10 string byte order */ + +#if KS +/* IMP11 interface */ + +/* CSR values */ +#define CSR_GO 0000001 /* Go transfer */ +#define CSR_RST 0000002 /* Reset interface */ +#define CSR_UBA 0000060 /* Unibus upper address */ +#define CSR_IE 0000100 /* Interrupt enable */ +#define CSR_RDY 0000200 /* Device ready */ +#define CSR_MRE 0001000 /* Master error */ +#define CSR_NXM 0040000 /* Non existant memory */ +#define CSR_ERR 0100000 /* Error present */ + +/* Input CSR 0767600 */ +#define CSR_HRC 0000004 /* Host Ready Relay Control */ +#define CSR_SE 0000010 /* Store enable */ +#define CSR_IBF 0000400 /* Input Buffer full */ +#define CSR_INR 0002000 /* IMP not ready */ +#define CSR_HR 0004000 /* Host Ready */ +#define CSR_EOM 0020000 /* End of Message */ + +/* Input data buffer 0767602 */ +/* Input Bus Address 0767604 */ +/* Input Word Count 0767606 */ + +/* Output CSR 07676010 */ +#define CSR_ELB 0000004 /* Send EOM indication to IMP */ +#define CSR_BB 0000010 /* Bus Back */ +#define CSR_OBE 0000400 /* OUtput Buffer Empty */ +#define CSR_WC0 0020000 /* Output Word Count 0 */ + +/* Output data buffer 0767612 */ +/* Output Bus Address 0767614 */ +/* Output Word Count 0767616 */ + +/* Bits in STATUS */ +#define IMPID 010 /* Input done. */ +#define IMPIB 040 /* Input busy. */ +#define IMPOD 0100 /* Output done. */ +#define IMPOB 0400 /* Output busy. */ +#define IMPERR 01000 /* IMP error. */ +#define IMPR 02000 /* IMP ready. */ +#define IMPIC 04000 /* IMP interrupt condition. */ +#define IMPHER 010000 /* Host error. */ +#define IMPHR 020000 /* Host ready. */ +#define IMPIHE 040000 /* Inhibit interrupt on host error. */ +#define IMPLW 0100000 /* Last IMP word. */ +#else + + /* ITS IMP Bits */ /* CONI */ @@ -123,6 +176,7 @@ /* CONI timeout. If no CONI instruction is executed for 3-5 seconds, the interface will raise the host error signal. */ #define CONI_TIMEOUT 3000000 +#endif #define STATUS u3 #define OPOS u4 /* Output bit position */ @@ -435,8 +489,14 @@ static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff}; static CONST in_addr_T broadcast_ipaddr = {0xffffffff}; +#if KS +int imp_wr(DEVICE *dptr, t_addr addr, uint16 data, int32 access); +int imp_rd(DEVICE *dptr, t_addr addr, uint16 *data, int32 access); +uint16 imp_vect(struct pdp_dib *dibp); +#else t_stat imp_devio(uint32 dev, uint64 *data); t_addr imp_devirq(uint32 dev, t_addr addr); +#endif t_stat imp_srv(UNIT *); t_stat imp_eth_srv(UNIT *); t_stat imp_tim_srv(UNIT *); @@ -480,22 +540,38 @@ const char *imp_description (DEVICE *dptr); static char *ipv4_inet_ntoa(struct in_addr ip); static int ipv4_inet_aton(const char *str, struct in_addr *inp); +#if KS +uint16 imp_icsr; +uint16 imp_idb; +uint32 imp_iba; +uint16 imp_iwcnt; +uint16 imp_ocsr; +uint16 imp_odb; +uint32 imp_oba; +uint16 imp_owcnt; +#endif + int imp_mpx_lvl = 0; double last_coni; UNIT imp_unit[] = { {UDATA(imp_srv, UNIT_IDLE+UNIT_ATTABLE+UNIT_DHCP, 0)}, /* 0 */ - {UDATA(imp_eth_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 0 */ - {UDATA(imp_tim_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 0 */ + {UDATA(imp_eth_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 1 */ + {UDATA(imp_tim_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 2 */ }; -DIB imp_dib = {IMP_DEVNUM, 1, &imp_devio, + +#if KS +DIB imp_dib = {0767600, 017, 0250, 6, 3, &imp_rd, &imp_wr, 0, 0, 0}; +#else +DIB imp_dib = {IMP_DEVNUM, 1, &imp_devio, #if KL &imp_devirq, #else NULL #endif }; +#endif MTAB imp_mod[] = { { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", @@ -516,16 +592,33 @@ MTAB imp_mod[] = { "Use DHCP to set IP address"}, { MTAB_XTD|MTAB_VDV, 0, "DHCPIP", NULL, NULL, &imp_show_dhcpip, NULL, "DHCP info" }, +#if KS + { UNIT_DTYPE, (TYPE_UNI << UNIT_V_DTYPE), "UNI", "UNI", NULL, NULL, NULL, + "Standard Unibus transfers"}, + { UNIT_DTYPE, (TYPE_SIMP << UNIT_V_DTYPE), "SIMP", "SIMP", NULL, NULL, NULL, + "PDP10 byte order transfers"}, +#else { UNIT_DTYPE, (TYPE_MIT << UNIT_V_DTYPE), "MIT", "MIT", NULL, NULL, NULL, "ITS/MIT style interface"}, { UNIT_DTYPE, (TYPE_BBN << UNIT_V_DTYPE), "BBN", "BBN", NULL, NULL, NULL, "Tenex/BBN style interface"}, { UNIT_DTYPE, (TYPE_WAITS << UNIT_V_DTYPE), "WAITS", "WAITS", NULL, NULL, NULL, "WAITS style interface"}, +#endif { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "ARP", NULL, NULL, &imp_show_arp, NULL, "ARP IP address->MAC address table" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "ARP=ddd.ddd.ddd.ddd=XX:XX:XX:XX:XX:XX", &imp_set_arp, NULL, NULL, "Create a static ARP Entry" }, +#if KS + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of CH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of CH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of CH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of CH11" }, +#endif { 0 } }; @@ -570,6 +663,180 @@ DEVICE imp_dev = { #define IMP_ICHN 0000070 #define IMP_ECHN 0000700 +#if KS +static void check_interrupts (UNIT *uptr) +{ + DEVICE *dptr = &imp_dev; + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + + if ((uptr->STATUS & IMPID) != 0 && imp_icsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect); + } +} + +int +imp_wr(DEVICE *dptr, t_addr addr, uint16 data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + UNIT *uptr = imp_unit; + + addr &= dibp->uba_mask; + sim_debug(DEBUG_DETAIL, dptr, "IMP11 write %06o %06o %o\n", + addr, data, access); + + switch (addr & 016) { + case 000: /* Input CSR */ + if (access == BYTE) { + if (addr & 1) + data = data | (imp_icsr & 0377); + else + data = (imp_icsr & 0177400) | data; + } + if (data & CSR_RST) { + imp_icsr = CSR_INR|CSR_RDY; + imp_iba = 0; + imp_iwcnt = 0; + uba_clr_irq(dibp, dibp->uba_vect); + } + data &= (CSR_GO|CSR_RST|CSR_UBA|CSR_IE|CSR_HRC|CSR_SE); + imp_icsr &= ~(CSR_GO|CSR_RST|CSR_UBA|CSR_IE|CSR_HRC|CSR_SE|CSR_ERR + |CSR_NXM|CSR_MRE); + imp_icsr |= data; + if (data & CSR_HRC) { + imp_icsr |= CSR_HR; + } else { + imp_icsr &= ~CSR_HR; + imp_icsr |= CSR_INR; + } + if (data & CSR_GO) { + imp_icsr &= ~CSR_RDY; + uptr->STATUS &= ~(IMPID|IMPLW); + uba_clr_irq(dibp, dibp->uba_vect); + } + sim_debug(DEBUG_DETAIL, dptr, "IMP11 icsr %06o\n", imp_icsr); + break; + + case 002: /* Input Data Buffer */ + if (access == BYTE) { + if (addr & 1) + data = data | (imp_idb & 0377); + else + data = (imp_idb & 0177400) | data; + } + imp_idb = data; + break; + + case 004: /* Input Bus Address */ + imp_iba = (data & 0177777); + imp_icsr &= ~(CSR_ERR); + break; + + case 006: /* Input Word Count */ + imp_iwcnt = (data & 0177777); + break; + + case 010: /* Output CSR */ + if (access == BYTE) { + if (addr & 1) + data = data | (imp_ocsr & 0377); + else + data = (imp_ocsr & 0177400) | data; + } + if (data & CSR_RST) { + imp_ocsr |= CSR_RDY; + imp_oba = 0; + imp_owcnt = 0; + uba_clr_irq(dibp, dibp->uba_vect + 4); + } + data &= (CSR_GO|CSR_RST|CSR_UBA|CSR_IE|CSR_BB|CSR_ELB); + imp_ocsr &= ~(CSR_GO|CSR_RST|CSR_UBA|CSR_IE|CSR_BB|CSR_ELB|CSR_ERR + |CSR_NXM|CSR_MRE); + imp_ocsr |= data; + if (data & CSR_GO) { + imp_ocsr &= ~CSR_RDY; + uptr->STATUS &= ~(IMPOD); + uba_clr_irq(dibp, dibp->uba_vect + 4); + } + sim_debug(DEBUG_DETAIL, dptr, "IMP11 ocsr %06o\n", imp_ocsr); + break; + + case 012: /* Output Data Buffer */ + if (access == BYTE) { + if (addr & 1) + data = data | (imp_odb & 0377); + else + data = (imp_odb & 0177400) | data; + } + imp_odb = data; + break; + + case 014: /* Output Bus Address */ + imp_oba = (data & 0177777); + imp_ocsr &= ~(CSR_ERR); + break; + + case 016: /* Output Word Count */ + imp_owcnt = (data & 0177777); + break; + } + if (imp_ocsr & CSR_GO || imp_icsr & CSR_GO) + sim_activate(uptr, 100); + return 0; +} + +int +imp_rd(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) +{ + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + UNIT *uptr = imp_unit; + + addr &= dibp->uba_mask; + + switch (addr & 016) { + case 000: /* Input CSR */ + *data = imp_icsr; + if ((uptr->STATUS & (IMPID)) != 0) + *data |= CSR_IBF; + if ((uptr->STATUS & (IMPLW)) != 0) + *data |= CSR_EOM; + break; + case 002: /* Input Data Buffer */ + *data = imp_idb; + break; + + case 004: /* Input Bus Address */ + *data = imp_iba; + break; + + case 006: /* Input Word Count */ + *data = imp_iwcnt; + break; + + case 010: /* Output CSR */ + *data = imp_ocsr; + if ((uptr->STATUS & (IMPOD)) != 0) + *data |= CSR_OBE; + break; + + case 012: /* Output Data Buffer */ + *data = imp_odb; + break; + + case 014: /* Output Bus Address */ + *data = imp_oba; + break; + + case 016: /* Output Word Count */ + *data = imp_owcnt; + break; + } + sim_debug(DEBUG_DETAIL, dptr, "IMP11 read %06o %06o %o\n", + addr, *data, access); + return 0; + +} + +#else static void check_interrupts (UNIT *uptr) { clr_interrupt (DEVNUM); @@ -660,7 +927,7 @@ t_stat imp_devio(uint32 dev, uint64 *data) memset(imp_data.sbuffer, 0, ETH_FRAME_SIZE); uptr->OPOS = 0; uptr->STATUS &= ~(IMPLHW); - } else + } else uptr->STATUS |= IMPLHW; } if (*data & IMP_STROUT) @@ -740,9 +1007,171 @@ imp_devirq(uint32 dev, t_addr addr) { return addr; } #endif +#endif t_stat imp_srv(UNIT * uptr) { +#if KS + DEVICE *dptr = find_dev_from_unit (uptr); + struct pdp_dib *dibp = (DIB *)dptr->ctxt; + t_addr pa; + uint64 wd64; + uint16 wd; + + if (imp_ocsr & CSR_GO && imp_data.sendq == NULL) { + pa = ((imp_ocsr & CSR_UBA) << 12) | imp_oba; + switch (GET_DTYPE(uptr->flags)) { + case TYPE_UNI: + if (uba_read_npr_word(pa, dibp->uba_ctl, &wd) == 0) { + imp_ocsr |= CSR_NXM; + imp_ocsr &= ~CSR_GO; + uptr->STATUS |= IMPOD; + if (imp_ocsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect + 4); + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP out npr failed\n"); + return SCPE_OK; + } + imp_data.sbuffer[uptr->OPOS >> 3] = (wd & 0377); + uptr->OPOS += 8; + imp_data.sbuffer[uptr->OPOS >> 3] = ((wd >> 8) & 0377); + uptr->OPOS += 8; + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP send %04x %07o %06o %06o\n", + wd, pa, imp_owcnt, imp_ocsr); + break; + case TYPE_SIMP: + if (uba_read_npr(pa, dibp->uba_ctl, &wd64) == 0) { + imp_ocsr |= CSR_NXM; + imp_ocsr &= ~CSR_GO; + uptr->STATUS |= IMPOD; + if (imp_ocsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect + 4); + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP out npr failed\n"); + return SCPE_OK; + } + imp_data.sbuffer[uptr->OPOS >> 3] = (wd64 >> 28) & 0377; + uptr->OPOS += 8; + imp_data.sbuffer[uptr->OPOS >> 3] = (wd64 >> 20) & 0377; + uptr->OPOS += 8; + imp_data.sbuffer[uptr->OPOS >> 3] = (wd64 >> 12) & 0377; + uptr->OPOS += 8; + imp_data.sbuffer[uptr->OPOS >> 3] = (wd64 >> 4) & 0377; + uptr->OPOS += 8; + imp_oba += 2; + imp_owcnt++; + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP send %08llx %07o %06o %06o\n", + (wd64 >> 4), pa, imp_owcnt, imp_ocsr); + break; + } + imp_oba += 2; + imp_owcnt++; + if (imp_oba == 0) { + imp_ocsr |= CSR_ERR; + imp_ocsr &= ~CSR_GO; + uptr->STATUS |= IMPOD; + if (imp_ocsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect + 4); + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP oba overflow\n"); + return SCPE_OK; + } + if (imp_owcnt == 0) { + imp_ocsr |= CSR_RDY; + imp_ocsr &= ~CSR_GO; + uptr->STATUS |= IMPOD; + if (imp_ocsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect + 4); + } + if (imp_ocsr & CSR_ELB) { + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP send\n"); + + imp_send_packet (&imp_data, uptr->OPOS >> 3); + memset(imp_data.sbuffer, 0, ETH_FRAME_SIZE); + uptr->OPOS = 0; + } + } + } + if (uptr->STATUS & IMPIB && imp_icsr & CSR_GO) { + pa = ((imp_icsr & CSR_UBA) << 12) | imp_iba; + if (imp_iba == 0) { + imp_icsr |= CSR_ERR; + imp_icsr &= ~CSR_GO; + uptr->STATUS |= IMPID; + if (imp_icsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect); + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP oba overflow\n"); + return SCPE_OK; + } + switch (GET_DTYPE(uptr->flags)) { + case TYPE_UNI: + wd = imp_data.rbuffer[uptr->IPOS >> 3] << 8; + uptr->IPOS += 8; + wd |= imp_data.rbuffer[uptr->IPOS >> 3]; + uptr->IPOS += 8; + if (uba_write_npr_word(pa, dibp->uba_ctl, wd) == 0) { + imp_icsr |= CSR_NXM; + imp_icsr &= ~CSR_GO; + uptr->STATUS |= IMPID; + if (imp_icsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect); + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP in npr failed\n"); + return SCPE_OK; + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP rec %04x %07o %06o %06o\n", + wd, pa, imp_owcnt, imp_ocsr); + break; + case TYPE_SIMP: + wd64 = (uint64)imp_data.rbuffer[uptr->IPOS >> 3] << 28; + uptr->IPOS += 8; + wd64 |= (uint64)imp_data.rbuffer[uptr->IPOS >> 3] << 20; + uptr->IPOS += 8; + wd64 |= (uint64)imp_data.rbuffer[uptr->IPOS >> 3] << 12; + uptr->IPOS += 8; + wd64 |= (uint64)imp_data.rbuffer[uptr->IPOS >> 3] << 4; + uptr->IPOS += 8; + if (uba_write_npr(pa, dibp->uba_ctl, wd64) == 0) { + imp_ocsr |= CSR_NXM; + imp_ocsr &= ~CSR_GO; + uptr->STATUS |= IMPID; + if (imp_icsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect); + } + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP out npr failed\n"); + return SCPE_OK; + } + imp_iba += 2; + imp_iwcnt++; + sim_debug(DEBUG_DETAIL, &imp_dev, "IMP rec %08llx %07o %06o %06o\n", + (wd64 >> 4), pa, imp_owcnt, imp_ocsr); + break; + } + imp_iba += 2; + imp_iwcnt++; + if (uptr->IPOS > uptr->ILEN) { + imp_icsr |= CSR_EOM|CSR_RDY; + imp_icsr &= ~CSR_GO; + uptr->STATUS |= IMPID|IMPLW; + uptr->STATUS &= ~IMPIB; + if (imp_icsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect); + } + uptr->ILEN = 0; + } + if (imp_iwcnt == 0) { + imp_icsr |= CSR_RDY; + imp_icsr &= ~CSR_GO; + uptr->STATUS |= IMPID; + if (imp_icsr & CSR_IE) { + uba_set_irq(dibp, dibp->uba_vect); + } + } + } + if (imp_ocsr & CSR_GO || imp_icsr & CSR_GO) + sim_activate(uptr, 100); +#else int i; int l; @@ -782,6 +1211,7 @@ t_stat imp_srv(UNIT * uptr) uptr->STATUS |= IMPID; check_interrupts (uptr); } +#endif if (uptr->ILEN == 0 && (uptr->STATUS & (IMPIB|IMPID)) == 0) imp_packet_in(&imp_data); return SCPE_OK; @@ -870,8 +1300,8 @@ t_stat imp_eth_srv(UNIT * uptr) imp_packet_in(&imp_data); if (imp_data.init_state >= 3 && imp_data.init_state < 6) { - if (imp_unit[0].flags & UNIT_DHCP && - imp_data.dhcp_state != DHCP_STATE_BOUND && + if (imp_unit[0].flags & UNIT_DHCP && + imp_data.dhcp_state != DHCP_STATE_BOUND && imp_data.dhcp_state != DHCP_STATE_REBINDING && imp_data.dhcp_state != DHCP_STATE_RENEWING) return SCPE_OK; @@ -879,11 +1309,16 @@ t_stat imp_eth_srv(UNIT * uptr) imp_data.init_state); if (imp_unit[0].ILEN == 0) { /* Queue up a nop packet */ - imp_data.rbuffer[0] = 0x4; -#if 0 +#if KS imp_data.rbuffer[0] = 0xf; imp_data.rbuffer[3] = 4; +#else + imp_data.rbuffer[0] = 0x4; #endif + imp_data.rbuffer[1] = (ntohl(imp_data.ip) >> 24) & 0xff; + imp_data.rbuffer[5] = (ntohl(imp_data.ip) >> 16) & 0xff; + imp_data.rbuffer[6] = (ntohl(imp_data.ip) >> 8) & 0xff; + imp_data.rbuffer[7] = ntohl(imp_data.ip) & 0xff; imp_unit[0].STATUS |= IMPIB; imp_unit[0].IPOS = 0; imp_unit[0].ILEN = 12*8; @@ -938,6 +1373,9 @@ t_stat imp_tim_srv(UNIT * uptr) imp_dhcp_timer(&imp_data); imp_arp_age(&imp_data); +#if KS + imp_icsr &= ~CSR_HR; +#endif return SCPE_OK; } @@ -981,7 +1419,7 @@ imp_packet_in(struct imp_device *imp) (ip_hdr->ip_v_hl & 0xf) * 4]); struct udp *udp_hdr = (struct udp *)payload; /* Check for DHCP traffic */ - if (ip_hdr->ip_p == UDP_PROTO && + if (ip_hdr->ip_p == UDP_PROTO && ntohs(udp_hdr->udp_dport) == DHCP_UDP_PORT_CLIENT && ntohs(udp_hdr->udp_sport) == DHCP_UDP_PORT_SERVER) { imp_do_dhcp_client(imp, &read_buffer); @@ -996,12 +1434,20 @@ imp_packet_in(struct imp_device *imp) memset(&imp->rbuffer[0], 0, 256); imp->rbuffer[0] = 0xf; imp->rbuffer[3] = 0; +#if KS + imp->rbuffer[1] = (ntohl(ip_hdr->ip_src) >> 24) & 0xff; imp->rbuffer[5] = (ntohl(ip_hdr->ip_src) >> 16) & 0xff; + imp->rbuffer[6] = (ntohl(ip_hdr->ip_src) >> 8) & 0xff; + imp->rbuffer[7] = ntohl(ip_hdr->ip_src) & 0xff; +#else imp->rbuffer[7] = 14; +#endif imp->rbuffer[8] = 0233; +#if !KS imp->rbuffer[18] = 0; imp->rbuffer[19] = 0x80; imp->rbuffer[21] = 0x30; +#endif /* Copy message over */ pad = 12 + (imp->padding / 8); @@ -1144,6 +1590,12 @@ imp_packet_in(struct imp_device *imp) imp_unit[0].STATUS |= IMPIB; imp_unit[0].IPOS = 0; imp_unit[0].ILEN = n*8; + imp->rbuffer[1] = (ntohl(ip_hdr->ip_src) >> 24) & 0xff; + imp->rbuffer[5] = (ntohl(ip_hdr->ip_src) >> 16) & 0xff; + imp->rbuffer[6] = (ntohl(ip_hdr->ip_src) >> 8) & 0xff; + imp->rbuffer[7] = ntohl(ip_hdr->ip_src) & 0xff; + imp->rbuffer[10] = (n >> 8) & 0xff; + imp->rbuffer[11] = n & 0xff; } if (!sim_is_active(&imp_unit[0])) sim_activate(&imp_unit[0], 100); @@ -1187,10 +1639,11 @@ imp_send_packet (struct imp_device *imp, int len) break; } sim_debug(DEBUG_DETAIL, &imp_dev, - "IMP packet Type=%d ht=%d dh=%d imp=%d lk=%d %d st=%d Len=%d\n", + "IMP packet H=%x Type=%d ht=%d dh=%d imp=%d lk=%d %d st=%d Len=%d mt=%d\n", + imp->sbuffer[0], imp->sbuffer[3], imp->sbuffer[4], imp->sbuffer[5], (imp->sbuffer[6] * 256) + imp->sbuffer[7], - lk, imp->sbuffer[9] >> 4, st, n); + lk, imp->sbuffer[9] >> 4, st, n, mt); switch(mt) { case 0: /* Regular packet */ switch(st) { @@ -1205,6 +1658,15 @@ imp_send_packet (struct imp_device *imp, int len) imp_packet_out(imp, &write_buffer); } break; + case 4: /* Nop */ + if (imp->init_state < 3) + imp->init_state++; + imp->padding = 0; + sim_debug(DEBUG_DETAIL, &imp_dev, + "IMP recieve Nop %d padding= %d\n", + imp->init_state, imp->padding); + sim_activate(uptr, tmxr_poll); /* Start reciever task */ + break; case 2: /* Getting ready */ case 3: /* Uncontrolled */ default: @@ -1214,7 +1676,6 @@ imp_send_packet (struct imp_device *imp, int len) case 1: /* Error */ break; case 2: /* Host going down */ -fprintf(stderr, "IMP: Host shutdown\n\r"); break; case 4: /* Nop */ if (imp->init_state < 3) @@ -1499,7 +1960,7 @@ void imp_packet_debug(struct imp_device *imp, const char *action, ETH_PACK *pack memcpy(&in_addr, &arp->dipaddr, sizeof(in_addr)); strlcpy(arp_dipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_dipaddr)); sim_debug(DEBUG_ARP, &imp_dev, - "%s %s EthDst=%s EthSrc=%s shwaddr=%s sipaddr=%s dhwaddr=%s dipaddr=%s\n", + "%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; } @@ -1519,17 +1980,17 @@ void imp_packet_debug(struct imp_device *imp, const char *action, ETH_PACK *pack snprintf(dst_port, sizeof(dst_port), "%d", ntohs(udp->udp_dport)); sim_debug(DEBUG_UDP, &imp_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 ((imp_dev.dctrl & DEBUG_DHCP) && + if ((imp_dev.dctrl & DEBUG_DHCP) && (((ntohs(udp->udp_sport) == DHCP_UDP_PORT_CLIENT) && - (ntohs(udp->udp_dport) == DHCP_UDP_PORT_SERVER)) || + (ntohs(udp->udp_dport) == DHCP_UDP_PORT_SERVER)) || ((ntohs(udp->udp_dport) == DHCP_UDP_PORT_CLIENT) && (ntohs(udp->udp_sport) == DHCP_UDP_PORT_SERVER)))) { struct dhcp *dhcp = (struct dhcp *)(payload + sizeof(struct udp)); uint8 *opt = &dhcp->options[0]; - sim_debug(DEBUG_DHCP, &imp_dev, "%s XID=%08X", - (dhcp->op == DHCP_BOOTREQUEST) ? "REQUEST" : - (dhcp->op == DHCP_BOOTREPLY) ? "REPLY" : + sim_debug(DEBUG_DHCP, &imp_dev, "%s XID=%08X", + (dhcp->op == DHCP_BOOTREQUEST) ? "REQUEST" : + (dhcp->op == DHCP_BOOTREPLY) ? "REPLY" : "??????", dhcp->xid); if (dhcp->ciaddr) { @@ -1619,7 +2080,7 @@ void imp_packet_debug(struct imp_device *imp, const char *action, ETH_PACK *pack sim_debug(DEBUG_DHCP, &imp_dev, "\n"); } else { if (udp->len && (imp_dev.dctrl & DEBUG_UDP)) - sim_data_trace(&imp_dev, imp_unit, payload + sizeof(struct udp), "", + sim_data_trace(&imp_dev, imp_unit, payload + sizeof(struct udp), "", ntohs(udp->len), "", DEBUG_DATA); } break; @@ -1678,7 +2139,7 @@ imp_arp_update(struct imp_device *imp, in_addr_T ipaddr, ETH_MAC *ethaddr, int a memcpy(&tabptr->ethaddr, ethaddr, sizeof(ETH_MAC)); eth_mac_fmt(ethaddr, mac_buf); sim_debug(DEBUG_ARP, &imp_dev, - "updating entry for IP %s to %s\n", + "updating entry for IP %s to %s\n", ipv4_inet_ntoa(*((struct in_addr *)&ipaddr)), mac_buf); } if (tabptr->age != ARP_DONT_AGE) @@ -1716,7 +2177,7 @@ imp_arp_update(struct imp_device *imp, in_addr_T ipaddr, ETH_MAC *ethaddr, int a tabptr->age = age; eth_mac_fmt(ethaddr, mac_buf); sim_debug(DEBUG_ARP, &imp_dev, - "creating entry for IP %s to %s, initial age=%d\n", + "creating entry for IP %s to %s, initial age=%d\n", ipv4_inet_ntoa(*((struct in_addr *)&ipaddr)), mac_buf, age); } @@ -1738,8 +2199,8 @@ void imp_arp_age(struct imp_device *imp) char mac_buf[20]; eth_mac_fmt(&tabptr->ethaddr, mac_buf); - sim_debug(DEBUG_ARP, &imp_dev, - "discarding ARP entry for IP %s %s after %d seconds\n", + sim_debug(DEBUG_ARP, &imp_dev, + "discarding ARP entry for IP %s %s after %d seconds\n", ipv4_inet_ntoa(*((struct in_addr *)&tabptr->ipaddr)), mac_buf, IMP_ARP_MAX_AGE); memset(tabptr, 0, sizeof(*tabptr)); } @@ -1993,7 +2454,7 @@ imp_do_send_dhcp(struct imp_device *imp, eth_mac_fmt (&pkt->ethhdr.dest, mac_buf); memcpy(&in_addr, &udp_hdr.ip_dst, sizeof(in_addr)); sim_debug(DEBUG_DHCP, &imp_dev, - "client sent %s packet to: %s:%d(%s)\n", + "client sent %s packet to: %s:%d(%s)\n", op, ipv4_inet_ntoa(in_addr), ntohs(udp->udp_dport), mac_buf); } @@ -2027,7 +2488,7 @@ imp_do_dhcp_client(struct imp_device *imp, ETH_PACK *read_buffer) ip_checksum((uint8 *)&sum, (uint8 *)ip_hdr, hl); if (sum != 0) { - sim_printf("IP checksum error %x\n\r", sum); + sim_printf("IP checksum error %x\r\n", sum); return; } ip_checksum((uint8 *)(&sum), (uint8 *)(upkt), ntohs(upkt->len)); @@ -2038,7 +2499,7 @@ imp_do_dhcp_client(struct imp_device *imp, ETH_PACK *read_buffer) udp_hdr.hlen = upkt->len; checksumadjust((uint8 *)&sum, 0, 0, (uint8 *)(&udp_hdr), sizeof(udp_hdr)); if (sum != 0) { - sim_printf("UDP checksum error %x\n\r", sum); + sim_printf("UDP checksum error %x\r\n", sum); return; } @@ -2110,8 +2571,8 @@ imp_do_dhcp_client(struct imp_device *imp, ETH_PACK *read_buffer) eth_mac_fmt(ð->src, mac_buf); memcpy(&in_addr, &udp_hdr.ip_src, sizeof(in_addr)); sim_debug(DEBUG_DHCP, &imp_dev, - "client incoming %s packet: dhcp_state=%s - wait_time=%d from: %s:%d(%s)\n", - (opr == -1) ? "" : dhcp_opr_names[opr], + "client incoming %s packet: dhcp_state=%s - wait_time=%d from: %s:%d(%s)\n", + (opr == -1) ? "" : dhcp_opr_names[opr], dhcp_state_names[imp->dhcp_state], imp->dhcp_wait_time, ipv4_inet_ntoa(in_addr), ntohs(upkt->udp_sport), mac_buf); @@ -2574,7 +3035,7 @@ t_stat imp_show_dhcpip (FILE *st, UNIT *uptr, int32 val, CONST void *desc) fprintf (st, ", Lease Expires in %d seconds", imp_data.dhcp_lease); } else { fprintf (st, ", State:%s, Waited %d seconds", dhcp_state_names[imp_data.dhcp_state], - imp_data.dhcp_wait_time); + imp_data.dhcp_wait_time); } } return SCPE_OK; @@ -2647,6 +3108,10 @@ t_stat imp_reset (DEVICE *dptr) imp_data.freeq = p; imp_data.init_state = 0; last_coni = sim_gtime(); +#if KS + imp_icsr = CSR_RDY; + imp_ocsr = CSR_RDY; +#endif if (imp_unit[0].flags & UNIT_ATT) sim_activate_after(&imp_unit[2], 1000000); /* Start Timer service */ return SCPE_OK; @@ -2659,6 +3124,7 @@ t_stat imp_attach(UNIT* uptr, CONST char* cptr) char* tptr; char buf[32]; +#if !KS /* Set to correct device number */ switch(GET_DTYPE(imp_unit[0].flags)) { case TYPE_MIT: @@ -2669,8 +3135,9 @@ t_stat imp_attach(UNIT* uptr, CONST char* cptr) imp_dib.dev_num = WA_IMP_DEVNUM; break; } +#endif if (!(uptr->flags & UNIT_DHCP) && imp_data.ip == 0) - return sim_messagef (SCPE_NOATT, "%s: An IP Address must be specified when DHCP is disabled\n", + return sim_messagef (SCPE_NOATT, "%s: An IP Address must be specified when DHCP is disabled\n", imp_dev.name); tptr = (char *) malloc(strlen(cptr) + 1); @@ -2710,7 +3177,7 @@ t_stat imp_attach(UNIT* uptr, CONST char* cptr) return sim_messagef (status, "%s: Can't initialize receive queue\n", imp_dev.name); } - /* Pick a relatively unique starting xid, presuming that the + /* Pick a relatively unique starting xid, presuming that the MAC address has been verified unique on the LAN by eth_open */ imp_data.dhcp_xid = (imp_data.mac[0] | (imp_data.mac[1] << 8) | (imp_data.mac[2] << 16) | (imp_data.mac[3] << 24)) + (uint32)time(NULL); @@ -2718,7 +3185,7 @@ t_stat imp_attach(UNIT* uptr, CONST char* cptr) memset(imp_data.arp_table, 0, sizeof(imp_data.arp_table)); /* If we're not doing DHCP and a gateway is defined on the network - then define a static APR entry for the gateway to facilitate + then define a static APR entry for the gateway to facilitate outbound routing */ if (!(uptr->flags & UNIT_DHCP) && imp_data.gwip) { ETH_PACK read_buffer; diff --git a/PDP10/kx10_mt.c b/PDP10/kx10_mt.c index 57b5a984..f253811e 100644 --- a/PDP10/kx10_mt.c +++ b/PDP10/kx10_mt.c @@ -178,10 +178,8 @@ UNIT mt_unit[] = { DIB mt_dib = {MT_DEVNUM, 2, &mt_devio, NULL}; MTAB mt_mod[] = { - { MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", - &set_writelock, &show_writelock, NULL, "Write ring in place" }, - { MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", - &set_writelock, NULL, NULL, "no Write ring in place" }, + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL}, {MTAB_XTD|MTAB_VDV|MTAB_VALR, MTDF_TYPEB, "TYPE", "TYPE", &mt_set_mta, &mt_show_mta}, {MTUF_7TRK, 0, "9T", "9T", NULL, NULL}, {MTUF_7TRK, MTUF_7TRK, "7T", "7T", NULL, NULL}, @@ -297,7 +295,7 @@ t_stat mt_devio(uint32 dev, uint64 *data) { break; case WRITE: - if ((uptr->flags & MTUF_WRP) != 0) { + if ((uptr->flags & MTUF_WLK) != 0) { mt_status |= IDLE_UNIT|ILL_OPR|EOF_FLAG; break; } @@ -372,7 +370,7 @@ t_stat mt_devio(uint32 dev, uint64 *data) { res |= SEVEN_CHAN; if ((uptr->flags & UNIT_ATT) != 0 && (uptr->CNTRL & MT_MOTION) == 0) res |= IDLE_UNIT; - if ((uptr->flags & MTUF_WRP) != 0) + if ((uptr->flags & MTUF_WLK) != 0) res |= WRITE_LOCK; if (sim_tape_bot(uptr)) res |= BOT_FLAG; @@ -534,8 +532,8 @@ t_stat mt_error(UNIT * uptr, t_stat r, DEVICE * dptr) /* Handle processing of tape requests. */ t_stat mt_srv(UNIT * uptr) { - DEVICE *dptr = find_dev_from_unit(uptr); - int unit = (uptr - dptr->units) & 7; + DEVICE *dptr = uptr->dptr; + int unit; int cmd = (uptr->CNTRL & FUNCTION) >> 9; t_mtrlnt reclen; t_stat r = SCPE_ARG; /* Force error if not set */ @@ -559,6 +557,10 @@ t_stat mt_srv(UNIT * uptr) cc_max = (4 + ((uptr->CNTRL & CORE_DUMP) != 0)); } + if (dptr == NULL) + dptr = find_dev_from_unit(uptr); + + unit = (uptr - dptr->units) & 7; switch(cmd) { case NOP_IDLE: sim_debug(DEBUG_DETAIL, dptr, "MT%o Idle\n", unit); @@ -802,7 +804,7 @@ t_stat mt_srv(UNIT * uptr) break; case WTM: - if ((uptr->flags & MTUF_WRP) != 0) + if ((uptr->flags & MTUF_WLK) != 0) return mt_error(uptr, MTSE_WRP, dptr); if (uptr->CPOS == 0) { mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOT_FLAG); @@ -821,7 +823,7 @@ t_stat mt_srv(UNIT * uptr) break; case ERG: - if ((uptr->flags & MTUF_WRP) != 0) + if ((uptr->flags & MTUF_WLK) != 0) return mt_error(uptr, MTSE_WRP, dptr); uptr->CNTRL &= ~MT_MOTION; mt_status &= ~(IDLE_UNIT|BOT_FLAG|EOT_FLAG); diff --git a/PDP10/kx10_pt.c b/PDP10/kx10_pt.c index 842463af..35b7eca8 100644 --- a/PDP10/kx10_pt.c +++ b/PDP10/kx10_pt.c @@ -130,7 +130,7 @@ t_stat ptp_devio(uint32 dev, uint64 *data) { if (cpu_unit[0].flags & UNIT_WAITS) *data |= 0200; #endif - sim_debug(DEBUG_CONI, &ptp_dev, "PP: CONI %012llo\n\r", *data); + sim_debug(DEBUG_CONI, &ptp_dev, "PP: CONI %012llo\n", *data); break; case CONO: @@ -144,7 +144,7 @@ t_stat ptp_devio(uint32 dev, uint64 *data) { } if (uptr->STATUS & DONE_FLG) set_interrupt(dev, uptr->STATUS); - sim_debug(DEBUG_CONO, &ptp_dev, "PP: CONO %012llo\n\r", *data); + sim_debug(DEBUG_CONO, &ptp_dev, "PP: CONO %012llo\n", *data); break; case DATAO: @@ -159,7 +159,7 @@ t_stat ptp_devio(uint32 dev, uint64 *data) { clr_interrupt(dev); sim_activate (&ptp_unit, ptp_unit.wait); } - sim_debug(DEBUG_DATAIO, &ptp_dev, "PP: DATAO %012llo\n\r", *data); + sim_debug(DEBUG_DATAIO, &ptp_dev, "PP: DATAO %012llo\n", *data); break; case DATAI: *data = 0; @@ -181,12 +181,12 @@ t_stat ptp_svc (UNIT *uptr) return SCPE_OK; } fputc (uptr->CHR, uptr->fileref); /* print char */ + uptr->pos = ftell (uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ perror ("PTP I/O error"); clearerr (uptr->fileref); return SCPE_IOERR; } - uptr->pos = uptr->pos + 1; return SCPE_OK; } @@ -230,7 +230,7 @@ t_stat ptr_devio(uint32 dev, uint64 *data) { switch(dev & 3) { case CONI: *data = uptr->STATUS; - sim_debug(DEBUG_CONI, &ptr_dev, "PT: CONI %012llo\n\r", *data); + sim_debug(DEBUG_CONI, &ptr_dev, "PT: CONI %012llo\n", *data); break; case CONO: @@ -245,7 +245,7 @@ t_stat ptr_devio(uint32 dev, uint64 *data) { } if (uptr->STATUS & DONE_FLG) set_interrupt(dev, uptr->STATUS); - sim_debug(DEBUG_CONO, &ptr_dev, "PT: CONO %012llo\n\r", *data); + sim_debug(DEBUG_CONO, &ptr_dev, "PT: CONO %012llo\n", *data); break; case DATAI: @@ -257,7 +257,7 @@ t_stat ptr_devio(uint32 dev, uint64 *data) { sim_activate (&ptr_unit, ptr_unit.wait); } uptr->STATUS |= BUSY_FLG; - sim_debug(DEBUG_DATAIO, &ptr_dev, "PT: DATAI %012llo\n\r", *data); + sim_debug(DEBUG_DATAIO, &ptr_dev, "PT: DATAI %012llo\n", *data); break; case DATAO: break; diff --git a/PDP10/kx10_rh.c b/PDP10/kx10_rh.c index dc0e5dd6..6de74142 100644 --- a/PDP10/kx10_rh.c +++ b/PDP10/kx10_rh.c @@ -24,7 +24,66 @@ #include "kx10_defs.h" +#if KS +#define CS1_GO 1 /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) +#define CS1_IE 0000100 /* Enable interrupts */ +#define CS1_RDY 0000200 /* Drive ready */ +#define CS1_UBA 0001400 /* High order UBA bits */ +#define CS1_PSEL 0002000 /* */ +#define CS1_DVA 0004000 /* drive avail */ +#define CS1_MCPE 0020000 /* */ +#define CS1_TRE 0040000 /* Set if CS2 0177400 */ +#define CS1_SC 0100000 /* Set if TRE or ATTN */ +#define CS2_V_UNIT 0 /* unit pos */ +#define CS2_M_UNIT 07 /* unit mask */ +#define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) +#define CS2_UAI 0000010 /* addr inhibit */ +#define CS2_PAT 0000020 /* parity test NI */ +#define CS2_CLR 0000040 /* controller clear */ +#define CS2_IR 0000100 /* input ready */ +#define CS2_OR 0000200 /* output ready */ +#define CS2_MDPE 0000400 /* Mbus par err NI */ +#define CS2_MXF 0001000 /* missed xfer NI */ +#define CS2_PGE 0002000 /* program err */ +#define CS2_NEM 0004000 /* nx mem err */ +#define CS2_NED 0010000 /* nx drive err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write check err */ +#define CS2_DLT 0100000 /* data late NI */ + +/* Controller error bits, saved in error register */ +#define ER1_ILF 0000001 /* illegal func */ +#define ER1_ILR 0000002 /* illegal register */ +#define ER1_RMR 0000004 /* reg mod refused */ +#define ER1_PAR 0000010 /* parity err */ + +/* Map addresses into RH registers */ +int rh_map[] = { 0, /* 776700 */ + -1, /* 776702 */ + -1, /* 776704 */ + 05, /* 776706 */ + -1, /* 776710 */ + 01, /* 776712 */ + 02, /* 776714 */ + 04, /* 776716 */ + 07, /* 776720 */ + -1, /* 776722 */ + 03, /* 776724 */ + 06, /* 776726 */ + 010, /* 776730 */ + 011, /* 776732 */ + 012, /* 776734 */ + 013, /* 776736 */ + 014, /* 776740 */ + 015, /* 776742 */ + 016, /* 776744 */ + 017 /* 776746 */ +}; +#else /* CONI Flags */ #define IADR_ATTN 0000000000040LL /* Interrupt on attention */ #define IARD_RAE 0000000000100LL /* Interrupt on register access error */ @@ -103,6 +162,19 @@ #define IRQ_VECT 0000000000777LL /* Interupt vector */ #define IRQ_KI10 0000002000000LL #define IRQ_KA10 0000001000000LL + +/* 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 */ + +#endif #define FNC_XFER 024 /* >=? data xfr */ /* Status register settings */ @@ -118,17 +190,234 @@ #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 */ +#if KS +DEVICE *rh_boot_dev = NULL; +int rh_boot_unit = 0; + + +int +uba_rh_write(DEVICE *dptr, t_addr addr, uint16 data, int32 access) { + int r = 0; + struct pdp_dib *dibp = (DIB *) dptr->ctxt; + struct rh_if *rhc; + int reg; + uint32 temp; + + if (dibp == NULL) + return 1; + rhc = dibp->rh11_if; + /* Check for parity error during access */ + if (rhc->cs2 & CS2_PAT) { + uba_set_parity(dibp->uba_ctl); + rhc->error |= ER1_PAR; + } + addr &= dibp->uba_mask; + reg = rh_map[addr >> 1]; + + switch(addr & 076) { + case 000: /* CS1 */ + if (access == BYTE) { + if (addr & 1) { + rhc->dev_read(dptr, rhc, 0, &temp); + data = data | (rhc->cs1 & 0300) | (temp & 076); + } else { + data = data | (rhc->cs1 & CS1_UBA); + } + } + rhc->cs1 &= ~(CS1_IE); + rhc->cs1 |= data & (CS1_IE); + if ((rhc->status & BUSY) == 0) + rhc->cda = ((data << 8) & 0600000) | (rhc->cda & 0177777); + if ((data & CS1_GO) != 0 || (rhc->cs1 & CS1_IE) == 0) + uba_clr_irq(rhc->dib, rhc->dib->uba_vect); + if ((data & CS1_GO) != 0 && (rhc->status & BUSY) != 0 && GET_FNC(data) >= FNC_XFER) { + rhc->cs2 |= CS2_PGE; + break; + } + r = rhc->dev_write(dptr, rhc, 0, data); + /* Check if we had a go with a data transfer command */ + if (r == 0 && (data & CS1_GO) != 0 && GET_FNC(data) >= FNC_XFER) { + rhc->cs2 = ((CS2_UAI|CS2_OR|CS2_IR|CS2_PAT|CS2_UNIT) & rhc->cs2); + rhc->xfer_drive = rhc->drive; + rhc->status |= BUSY; + } + break; + case 002: /* RPWC - 176702 - word count */ /* 1 */ + if (access == BYTE) { + if (addr & 1) + data = data | (rhc->wcr & 0377); + else + data = (rhc->wcr & 0177400) | data; + } + rhc->wcr = data; + break; + case 004: /* RPBA - 176704 - base address */ /* 2 */ + if (access == BYTE) { + if (addr & 1) + data = data | (rhc->cda & 0377); + else + data = (rhc->cda & 0177400) | data; + } + rhc->cda = (rhc->cda & 0600000) | (data & 0177776); + break; + + case 010: /* RPCS2 - 176710 - Control and Status register 2 */ /* 4 */ + if (access == BYTE) { + if (addr & 1) + data = data | (rhc->cs2 & 0377); + else + data = (rhc->cs2 & 0177400) | data; + } + if (data & CS2_CLR) { + rh_reset(dptr, rhc); + if (rhc->dev_reset != NULL) + rhc->dev_reset(dptr); + else + dptr->reset(dptr); + rhc->cs2 |= CS2_CLR; /* Hack for tops 10 7.04 */ + break; + } + /* Don't allow UAI to be set just after reset */ + if (rhc->cs2 & CS2_CLR) { + data &= ~CS2_UAI; + rhc->cs2 &= ~CS2_CLR; + } + + rhc->cs2 &= ~(CS2_PE|CS2_MXF|CS2_PAT|CS2_UNIT); + if ((rhc->status & BUSY) == 0) { + rhc->cs2 &= ~(CS2_UAI); + if( data & CS2_UAI) + sim_debug(DEBUG_DETAIL, dptr, "RH%o set no UAI %06o\n", rhc->drive, PC); + rhc->cs2 |= CS2_UAI & data; + } + rhc->cs2 |= (CS2_PE|CS2_MXF|CS2_PAT|CS2_UNIT) & data; + rhc->cs2 |= CS2_IR; + rhc->drive = rhc->cs2 & CS2_UNIT; + /* Try reading the command register */ + r = rhc->dev_read(dptr, rhc, 0, &temp); + if (r < 0) + rhc->cs2 |= CS2_NED; + break; + + case 022: /* RPDB - 176722 - data buffer */ /* 11 */ + if ((rhc->cs2 & CS2_IR) == 0) { + rhc->cs2 |= CS2_DLT; + break; + } + rhc->dba = rhc->dbb; + rhc->dbb = data; + if (rhc->cs2 & CS2_IR) + rhc->dba = rhc->dbb; + rhc->cs2 |= CS2_OR; + rhc->cs2 &= ~CS2_IR; + break; + + case 014: /* RPER1 - 176714 - error status 1 */ /* 6 */ + rhc->error = data; + /* Fall through */ + + default: + if (access == BYTE) { + rhc->dev_read(dptr, rhc, reg, &temp); + if (addr & 1) + data = data | (temp & 0377); + else + data = (temp & 0177400) | data; + } + r = rhc->dev_write(dptr, rhc, reg, data); + } + if (r < 0) { + rhc->cs2 |= CS2_NED; + r = 0; + } + sim_debug(DEBUG_DETAIL, dptr, "RH%o write %06o %06o %o\n", rhc->drive, + addr, data, access); + return r; +} + +int +uba_rh_read(DEVICE *dptr, t_addr addr, uint16 *data, int32 access) { + int r = 1; + struct pdp_dib *dibp = (DIB *) dptr->ctxt; + struct rh_if *rhc; + int reg; + uint32 temp = 0; + + if (dibp == NULL) + return 1; + rhc = dibp->rh11_if; + addr &= dibp->uba_mask; + reg = rh_map[addr >> 1]; + + if (reg >= 0) { + r = rhc->dev_read(dptr, rhc, reg, &temp); + if (r < 0) { + rhc->cs2 |= CS2_NED; + return 0; + } + } + + switch(addr & 076) { + case 000: /* RPC - 176700 - control */ + temp |= (uint16)(rhc->cs1 & (CS1_IE)); + temp |= (rhc->cda & 0600000) >> 8; + if ((rhc->status & BUSY) == 0) + temp |= CS1_RDY; + if (rhc->cs2 & (CS2_MDPE|CS2_MXF|CS2_PGE|CS2_NEM|CS2_NED|CS2_PE|CS2_WCE|CS2_DLT)) + temp |= CS1_TRE; + if (rhc->attn || (temp & CS1_TRE) != 0) + temp |= CS1_SC; + r = 0; + break; + case 002: /* RPWC - 176702 - word count */ + temp = rhc->wcr; + r = 0; + break; + case 004: /* RPBA - 176704 - base address */ + temp = (uint16)(rhc->cda & 0177776); + r = 0; + break; + case 010: /* RPCS2 - 176710 - control/status 2 */ + temp = rhc->cs2 & ~CS2_CLR; + r = 0; + break; + case 014: /* RPER1 - 176714 - error status 1 */ + temp |= rhc->error; + r = 0; + break; + case 022: /* RPDB - 176722 - data buffer */ + r = 0; + if ((rhc->cs2 & CS2_OR) == 0) { + rhc->cs2 |= CS2_DLT; + break; + } + temp = rhc->dba; + rhc->dba = rhc->dbb; + rhc->cs2 &= ~CS2_OR; + rhc->cs2 |= CS2_IR; + break; + default: + break; + } + *data = temp; + sim_debug(DEBUG_DETAIL, dptr, "RH%o read %o %d %06o %06o %06o\n", rhc->drive, r, reg, + addr, temp, PC); + /* Check for parity error during access */ + if (rhc->cs2 & CS2_PAT) { + uba_set_parity(dibp->uba_ctl); + rhc->error |= ER1_PAR; + } + return r; +} + +uint16 +uba_rh_vect(struct pdp_dib *dibp) +{ + return dibp->uba_vect; +} + +#else /* 0-37 mass bus register. 70 SBAR, block address. 71 STCR, neg block count, func @@ -446,14 +735,16 @@ t_stat rh_devio(uint32 dev, uint64 *data) { dptr->name, dev, *data, rhc->drive, PC, rhc->status); return SCPE_OK; } + rhc->drive = (int)(*data >> 18) & 07; /* 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; if (rhc->dev_write(dptr, rhc, 0, (uint32)(*data & 077))) { rhc->status |= CR_DRE; + } else { + rh_setup(rhc, (uint32)(*data >> 6)); + rhc->xfer_drive = rhc->drive; } sim_debug(DEBUG_DATAIO, dptr, "%s %03o command %012llo, %d PC=%06o %06o\n", @@ -506,23 +797,51 @@ rh_devirq(uint32 dev, t_addr addr) { else if (rhc->imode == 2) /* RH20 style */ addr = rhc->ivect; } else { - sim_printf("Unable to find device %03o\n\r", dev); + sim_printf("Unable to find device %03o\r\n", dev); } return addr; } +#endif + +/* Reset the RH to a known clear condiguration */ +void rh_reset(DEVICE *dptr, struct rh_if *rhc) +{ + rhc->status = 0; + rhc->attn = 0; + rhc->rae = 0; + rhc->wcr = 0; + rhc->cda = 0; + rhc->drive = 0; +// rhc->xfer_drive = -1; +#if KS + rhc->dib = (DIB *)dptr->ctxt; + rhc->cs1 = 0; + rhc->cs2 = CS2_IR; + rhc->dba = 0; + rhc->dbb = 0; + rhc->error = 0; +#endif +} /* 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) +#if KS + if ((rhc->status & BUSY) == 0 && (rhc->cs1 & CS1_IE) != 0) + uba_set_irq(rhc->dib, rhc->dib->uba_vect); +#else + if ((rhc->status & BUSY) == 0 && (rhc->status & IADR_ATTN) != 0) set_interrupt(rhc->devnum, rhc->status); +#endif } void rh_error(struct rh_if *rhc) { +#if !KS if (rhc->imode == 2) rhc->status |= RH20_DR_EXC; +#endif } /* Decrement block count for RH20, nop for RH10 */ @@ -542,12 +861,18 @@ int rh_blkend(struct rh_if *rhc) /* Set an IRQ for a DF10 device */ void rh_setirq(struct rh_if *rhc) { - rhc->status |= PI_ENABLE; - set_interrupt(rhc->devnum, rhc->status); + rhc->status |= PI_ENABLE; +#if KS + if ((rhc->status & BUSY) == 0 && (rhc->cs1 & CS1_IE) != 0) + uba_set_irq(rhc->dib, rhc->dib->uba_vect); +#else + set_interrupt(rhc->devnum, rhc->status); +#endif } /* Generate the DF10 complete word */ void rh_writecw(struct rh_if *rhc, int nxm) { +#if !KS uint64 wrd1; #if KL if (rhc->imode == 2) { @@ -564,7 +889,7 @@ void rh_writecw(struct rh_if *rhc, int nxm) { if (nxm) { wrd1 |= RH20_NXM_ERR; rhc->status |= RH20_CHAN_ERR; - } + } if (wc != 0) { wrd1 |= RH20_NOT_WC0; if (rhc->status & RH20_XEND) { @@ -598,14 +923,16 @@ void rh_writecw(struct rh_if *rhc, int nxm) { rhc->cda++; wrd1 = ((uint64)(rhc->ccw & WMASK) << CSHIFT) | ((uint64)(rhc->cda) & AMASK); (void)Mem_write_word(rhc->cia|1, &wrd1, 0); +#endif } /* Finish off a DF10 transfer */ void rh_finish_op(struct rh_if *rhc, int nxm) { #if KL + rhc->status &= ~(CC_CHAN_ACT); if (rhc->imode != 2) #endif - rhc->status &= ~BUSY; + rhc->status &= ~(BUSY); rh_writecw(rhc, nxm); rh_setirq(rhc); #if KL @@ -621,7 +948,9 @@ void rh_finish_op(struct rh_if *rhc, int nxm) { void rh20_setup(struct rh_if *rhc) { DEVICE *dptr = NULL; + uint32 data; int reg; + int drv; for (reg = 0; rh[reg].dev_num != 0; reg++) { if (rh[reg].rh == rhc) { @@ -631,15 +960,24 @@ void rh20_setup(struct rh_if *rhc) } if (dptr == 0) return; - rhc->pbar = rhc->sbar; + /* Check to see if drive currently doing something */ + drv = rhc->drive; + rhc->drive = (rhc->stcr >> 18) & 07; + if (rhc->dev_read != NULL) { + (void)rhc->dev_read(dptr, rhc, 0, &data); + if (data & 1) { + rhc->drive = drv; + return; + } + } rhc->ptcr = rhc->stcr; - /* Read drive status */ + rhc->pbar = rhc->sbar; rhc->drive = (rhc->ptcr >> 18) & 07; + /* Read drive status */ 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) (void)rhc->dev_write(dptr, rhc, 5, (rhc->pbar & 0177777)); rhc->status &= ~RH20_SBAR; @@ -649,28 +987,33 @@ void rh20_setup(struct rh_if *rhc) 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) (void)rhc->dev_write(dptr, rhc, 0, (rhc->ptcr & 077)); rhc->cop = 0; rhc->wcr = 0; + rhc->xfer_drive = rhc->drive; rhc->status &= ~RH20_CHAN_RDY; + rhc->status |= BUSY; + rhc->drive = drv; } #endif /* Setup for a DF10 transfer */ void rh_setup(struct rh_if *rhc, uint32 addr) { +#if !KS rhc->cia = addr & ICWA; rhc->ccw = rhc->cia; rhc->wcr = 0; +#endif rhc->status |= BUSY; } /* Fetch the next IO control word */ int rh_fetch(struct rh_if *rhc) { +#if !KS uint64 data; int reg; DEVICE *dptr = NULL; @@ -727,12 +1070,30 @@ int rh_fetch(struct rh_if *rhc) { rhc->wcr = (uint32)((data >> CSHIFT) & WMASK); rhc->cda = (uint32)(data & AMASK); rhc->ccw = (uint32)((rhc->ccw + 1) & AMASK); +#endif return 1; } /* Read next word */ int rh_read(struct rh_if *rhc) { +#if KS + if ((rhc->status & BUSY) == 0) + return 0; + if (uba_read_npr(rhc->cda, rhc->dib->uba_ctl, &rhc->buf) == 0) { + rhc->cs2 |= CS2_NEM; + rhc->status &= ~BUSY; + return 0; + } + if ((rhc->cs2 & CS2_UAI) == 0) + rhc->cda += 4; + rhc->wcr = (rhc->wcr + 2) & 0177777; + if (rhc->wcr == 0) { + rhc->status &= ~BUSY; + return 0; + } +#else uint64 data; + if (rhc->wcr == 0) { if (!rh_fetch(rhc)) return 0; @@ -774,11 +1135,28 @@ int rh_read(struct rh_if *rhc) { if (rhc->wcr == 0) { return rh_fetch(rhc); } +#endif return 1; } /* Write next word */ int rh_write(struct rh_if *rhc) { +#if KS + if ((rhc->status & BUSY) == 0) + return 0; + if (uba_write_npr(rhc->cda, rhc->dib->uba_ctl, rhc->buf) == 0) { + rhc->cs2 |= CS2_NEM; + rhc->status &= ~BUSY; + return 0; + } + if ((rhc->cs2 & CS2_UAI) == 0) + rhc->cda += 4; + rhc->wcr = (rhc->wcr + 2) & 0177777; + if (rhc->wcr == 0) { + rhc->status &= ~BUSY; + return 0; + } +#else if (rhc->wcr == 0) { if (!rh_fetch(rhc)) return 0; @@ -817,6 +1195,7 @@ int rh_write(struct rh_if *rhc) { if (rhc->wcr == 0) { return rh_fetch(rhc); } +#endif return 1; } diff --git a/PDP10/kx10_rp.c b/PDP10/kx10_rp.c index 8ee2862a..a278e83f 100644 --- a/PDP10/kx10_rp.c +++ b/PDP10/kx10_rp.c @@ -44,9 +44,8 @@ #define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) /* Parameters in the unit descriptor */ -#define CMD u3 -/* u3 low */ -/* RPC - 00 - control */ +#define RPCS1 00 +/* RPCS1 - 00 - control */ #define CS1_GO 1 /* go */ #define CS1_V_FNC 1 /* function pos */ @@ -70,10 +69,12 @@ #define FNC_WRITEH 031 /* write w/ headers */ #define FNC_READ 034 /* read */ #define FNC_READH 035 /* read w/ headers */ +#define CS1_RDY 0000200 /* Drive ready */ #define CS1_DVA 0004000 /* drive avail NI */ +#define CS1_SC 0100000 /* Set if TRE or ATTN */ #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) -/* u3 low */ +#define RPDS 01 /* RPDS - 01 - drive status */ #define DS_OFF 0000001 /* offset mode */ @@ -89,7 +90,7 @@ #define DS_ATA 0100000 /* attention active */ #define DS_MBZ 0000076 -/* u3 high */ +#define RPER1 02 /* RPER1 - 02 - error status 1 */ #define ER1_ILF 0000001 /* illegal func */ @@ -109,61 +110,62 @@ #define ER1_UNS 0040000 /* drive unsafe */ #define ER1_DCK 0100000 /* data check NI */ +#define RPMR 03 /* RPMR - 03 - maintenace register */ +#define RPAS 04 /* RPAS - 04 - attention summary */ #define AS_U0 0000001 /* unit 0 flag */ -#define DA u4 -/* u4 high */ -/* RPDC - 05 - desired sector */ +#define RPDA 05 +/* RPDA - 05 - desired sector */ -#define DA_V_SC 16 /* sector pos */ +#define DA_V_SC 0 /* sector pos */ #define DA_M_SC 077 /* sector mask */ -#define DA_V_SF 24 /* track pos */ +#define DA_V_SF 8 /* track pos */ #define DA_M_SF 077 /* track mask */ #define DA_MBZ 0140300 #define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) #define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) +#define RPDT 06 /* RPDT - 06 - drive type */ +#define RPLA 07 /* RPLA - 07 - look ahead register */ -#define LA_REG us9 #define LA_V_SC 6 /* sector pos */ +#define RPER2 010 /* RPER2 - 10 - error status 2 - drive unsafe conditions - unimplemented */ -/* us10 */ +#define RPOF 011 /* RPOF - 11 - offset register */ -/* u4 low */ +#define RPDC 012 /* RPDC - 12 - desired cylinder */ #define DC_V_CY 0 /* cylinder pos */ #define DC_M_CY 01777 /* cylinder mask */ #define DC_MBZ 0176000 -#define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) -#define GET_DA(c,d) ((((GET_CY (c) * rp_drv_tab[d].surf) + GET_SF (c)) \ - * rp_drv_tab[d].sect) + GET_SC (c)) -#define CCYL u5 -/* u5 low */ +#define GET_CY (((regs[RPDC]) >> DC_V_CY) & DC_M_CY) +#define GET_DA(d) ((((GET_CY * rp_drv_tab[d].surf) + GET_SF (regs[RPDA])) \ + * rp_drv_tab[d].sect) + GET_SC (regs[RPDA])) +#define CCYL u4 /* RPCC - 13 - current cylinder */ +#define RPSN 014 /* RPSN - 14 - serial number */ +#define RPER3 015 /* RPER3 - 15 - error status 3 - more unsafe conditions - unimplemented */ -#define ERR2 us9 -/* us9 */ -#define ERR3 us10 - -/* RPDB - 176722 - data buffer */ - #define OF_HCI 0002000 /* hdr cmp inh NI */ #define OF_ECI 0004000 /* ECC inhibit NI */ #define OF_F22 0010000 /* format NI */ #define OF_MBZ 0161400 -#define DATAPTR u6 +#define DATAPTR u6 + +#define RPEC1 016 /* RPEC1 - 16 - ECC status 1 - unimplemented */ +#define RPEC2 017 /* RPEC2 - 17 - ECC status 2 - unimplemented */ @@ -238,6 +240,10 @@ 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]; +#if KS +extern DEVICE *rh_boot_dev; +extern int rh_boot_unit; +#endif UNIT rp_unit[] = { @@ -317,6 +323,15 @@ UNIT rp_unit[] = { #endif }; +#if KS +struct rh_if rp_rh[NUM_DEVS_RP] = { + { &rp_write, &rp_read, &rp_rst}, +}; + +DIB rp_dib[] = { + {0776700, 077, 0254, 6, 1, &uba_rh_read, &uba_rh_write, 0, 0, &rp_rh[0]}, +}; +#else struct rh_if rp_rh[NUM_DEVS_RP] = { { &rp_write, &rp_read, &rp_rst}, { &rp_write, &rp_read, &rp_rst}, @@ -329,7 +344,7 @@ DIB rp_dib[] = { {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]}}; - +#endif MTAB rp_mod[] = { #if KL @@ -346,23 +361,35 @@ MTAB rp_mod[] = { {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 }, +#if KS + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of RH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of RH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of RH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of RH11" }, +#endif {0} }; REG rpa_reg[] = { +#if !KS {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(CIA, rp_rh[0].cia, 18)}, + {ORDATA(CCW, rp_rh[0].ccw, 18)}, + {ORDATA(DEVNUM, rp_rh[0].devnum, 9), REG_HRO}, +#endif + {ORDATA(DRIVE, rp_rh[0].drive, 3), REG_HRO}, {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} @@ -381,16 +408,16 @@ REG rpb_reg[] = { {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(CIA, rp_rh[1].cia, 18)}, + {ORDATA(CCW, rp_rh[1].ccw, 18)}, + {ORDATA(DEVNUM, rp_rh[1].devnum, 9), REG_HRO}, + {ORDATA(DRIVE, rp_rh[1].drive, 3), REG_HRO}, {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} @@ -409,16 +436,16 @@ REG rpc_reg[] = { {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(CIA, rp_rh[2].cia, 18)}, + {ORDATA(CCW, rp_rh[2].ccw, 18)}, {ORDATA(REG, rp_rh[2].reg, 6), REG_RO}, + {ORDATA(DEVNUM, rp_rh[2].devnum, 9), REG_HRO}, + {ORDATA(DRIVE, rp_rh[2].drive, 3), REG_HRO}, {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} @@ -437,8 +464,8 @@ REG rpd_reg[] = { {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(DRIVE, rp_rh[3].drive, 3), REG_HRO}, {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}, @@ -481,15 +508,26 @@ DEVICE *rp_devs[] = { 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; - } + UNIT *uptr = dptr->units; + uint16 *regs; + int ctlr = GET_CNTRL_RH(uptr->flags); + uint32 i; + + rh_reset(dptr, &rp_rh[ctlr]); + for (i = 0; i < dptr->numunits; i++) { + regs = (uint16 *)(uptr->up7); + regs[RPDS] &= DS_VV; + if (regs[RPMR] & 1) { + uptr->CCYL = GET_CY; + regs[RPOF] &= OF_HCI|OF_ECI|OF_F22; + } + if ((uptr->flags & UNIT_ATT) != 0) /* attached? */ + regs[RPDS] |= DS_DRY; + regs[RPER1] = 0; + regs[RPER2] = 0; + regs[RPER3] = 0; + uptr++; + } } int @@ -498,47 +536,44 @@ rp_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { int unit = rhc->drive; UNIT *uptr = &dptr->units[unit]; int dtype = GET_DTYPE(uptr->flags); + uint16 *regs = (uint16 *)(uptr->up7); if ((uptr->flags & UNIT_DIS) != 0 && reg != 04) return 1; - if ((uptr->CMD & CS1_GO) && reg != 04) { - uptr->CMD |= (ER1_RMR << 16)|DS_ERR; + if ((regs[RPCS1] & CS1_GO) && reg != 04) { + regs[RPER1] |= ER1_RMR; return 0; } switch(reg) { case 000: /* control */ - sim_debug(DEBUG_DETAIL, dptr, "%s%o Status=%06o\n", dptr->name, unit, uptr->CMD); - /* Set if drive not writable */ - if (uptr->flags & UNIT_WPRT) - uptr->CMD |= DS_WRL; + sim_debug(DEBUG_DETAIL, dptr, "%s%o Status=%06o\n", dptr->name, unit, regs[RPCS1]); /* If drive not ready don't do anything */ - if ((uptr->CMD & DS_DRY) == 0) { - uptr->CMD |= (ER1_RMR << 16)|DS_ERR; + if ((regs[RPCS1] & CS1_GO) != 0) { + regs[RPER1] |= ER1_RMR; sim_debug(DEBUG_DETAIL, dptr, "%s%o not ready\n", dptr->name, unit); return 0; } /* Check if GO bit set */ if ((data & 1) == 0) { - uptr->CMD &= ~076; - uptr->CMD |= data & 076; - sim_debug(DEBUG_DETAIL, dptr, "%s%o no go\n", dptr->name, unit); + regs[RPCS1] = data & 076; + sim_debug(DEBUG_DETAIL, dptr, "%s%o no go %06o\n", dptr->name, unit, data); return 0; /* No, nop */ } - uptr->CMD &= DS_ATA|DS_VV|DS_DPR|DS_MOL|DS_WRL; - uptr->CMD |= data & 076; + regs[RPDS] &= DS_ATA|DS_VV|DS_OFF; + regs[RPCS1] = data & 076; switch (GET_FNC(data)) { case FNC_NOP: - uptr->CMD |= DS_DRY; + regs[RPDS] |= DS_DRY; break; case FNC_RECAL: /* recalibrate */ - uptr->DA &= ~0177777; + regs[RPDC] = 0; /* Fall through */ case FNC_RETURN: /* return to center */ case FNC_OFFSET: /* offset */ case FNC_UNLOAD: /* unload */ - uptr->CMD &= ~DS_OFF; + regs[RPDS] &= ~DS_OFF; /* Fall through */ case FNC_SEARCH: /* search */ @@ -548,101 +583,105 @@ rp_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { case FNC_WRITEH: /* write w/ headers */ case FNC_READ: /* read */ case FNC_READH: /* read w/ headers */ - uptr->CMD |= DS_PIP; - if (GET_CY(uptr->DA) >= rp_drv_tab[dtype].cyl || - GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || - GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { + if (GET_CY >= rp_drv_tab[dtype].cyl || + GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect || + GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) { rhc->attn &= ~(1<CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; - uptr->CMD &= ~DS_PIP; + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPER1] |= ER1_IAE; break; } - - uptr->CMD |= CS1_GO; + regs[RPDS] |= DS_PIP; + regs[RPDS] &= ~DS_DRY; + regs[RPCS1] |= CS1_GO; CLR_BUF(uptr); uptr->DATAPTR = 0; break; case FNC_DCLR: /* drive clear */ - uptr->CMD |= DS_DRY; - uptr->CMD &= ~(DS_ATA|CS1_GO); - uptr->DA &= 003400177777; - uptr->CCYL &= 0177777; - uptr->ERR2 = 0; - uptr->ERR3 = 0; + regs[RPDS] &= ~DS_ATA; + regs[RPDS] |= DS_DRY; + regs[RPOF] = 0; + regs[RPER1] = 0; + regs[RPER2] = 0; + regs[RPER3] = 0; +#if KS + rhc->error = 0; +#endif rhc->attn &= ~(1<DA = 0; - uptr->CCYL &= 0177777; - uptr->CMD &= ~DS_OFF; + regs[RPDA] = 0; + regs[RPDC] = 0; + regs[RPDS] &= ~DS_OFF; + regs[RPDS] |= DS_DRY; + regs[RPOF] = 0; /* Fall through */ case FNC_RELEASE: /* port release */ case FNC_PACK: /* pack acknowledge */ if ((uptr->flags & UNIT_ATT) != 0) - uptr->CMD |= DS_VV; - uptr->CMD |= DS_DRY; + regs[RPDS] |= DS_VV|DS_DRY; break; default: - uptr->CMD |= DS_DRY|DS_ERR|DS_ATA; - uptr->CMD |= (ER1_ILF << 16); + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPER1] |= ER1_ILF; rhc->attn |= (1<CMD & CS1_GO) + if (regs[RPCS1] & CS1_GO) sim_activate(uptr, 1000); - sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, uptr->CMD); + sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, regs[RPCS1]); return 0; case 001: /* status */ break; case 002: /* error register 1 */ - uptr->CMD &= 0177777; - uptr->CMD |= data << 16; - uptr->CMD &= ~DS_ERR; - if ((((uptr->CMD >> 16) & 0177777) | uptr->ERR2 | uptr->ERR3) != 0) - uptr->CMD |= DS_ERR; + regs[RPER1] = data; break; case 003: /* maintenance */ + /* RPLA has 10 bits for position in sector */ + if (data & 1) { + if ((data & 076) == 0) { + if (regs[RPMR] & 010) + regs[RPLA] ++; + if (regs[RPMR] & 04) + regs[RPLA] = 0; + } + regs[RPMR] = data; + fprintf(stderr, "Write %o MR=%06o\r\n", unit, data); + } else + regs[RPMR] = 0; break; case 004: /* atten summary */ for (i = 0; i < 8; i++) { if (data & (1<units[i]; - u->CMD &= ~DS_ATA; + UNIT *u = &dptr->units[i]; + uint16 *r = (uint16 *)(u->up7); + r[RPDS] &= ~DS_ATA; rhc->attn &= ~(1<DA &= 0177777; - uptr->DA |= data << 16; + regs[RPDA] = data; break; case 014: /* error register 2 */ - uptr->ERR2 = data; - uptr->CMD &= ~DS_ERR; - if ((((uptr->CMD >> 16) & 0177777) | uptr->ERR2 | uptr->ERR3) != 0) - uptr->CMD |= DS_ERR; + regs[RPER2] = data; break; case 006: /* drive type */ case 007: /* look ahead */ break; case 011: /* offset */ - uptr->CCYL &= 0177777; - uptr->CCYL |= data << 16; + regs[RPOF] = data & 0016277; break; case 012: /* desired cylinder */ - uptr->DA &= ~0177777; - uptr->DA |= data; + regs[RPDC] = (data & DC_M_CY) << DC_V_CY; break; case 015: /* error register 3 */ - uptr->ERR3 = data; - uptr->CMD &= ~DS_ERR; - if ((((uptr->CMD >> 16) & 0177777) | uptr->ERR2 | uptr->ERR3) != 0) - uptr->CMD |= DS_ERR; + regs[RPER3] = data & 076210; break; case 013: /* current cylinder */ case 010: /* serial no */ @@ -650,7 +689,7 @@ rp_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { case 017: /* ecc pattern */ break; default: - uptr->CMD |= (ER1_ILR<<16)|DS_ERR; + regs[RPER1] |= ER1_ILR; rhc->rae |= 1 << unit; } return 0; @@ -660,6 +699,7 @@ int rp_read(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 *data) { int unit = rhc->drive; UNIT *uptr = &dptr->units[unit]; + uint16 *regs = (uint16 *)(uptr->up7); uint32 temp = 0; int i; @@ -671,63 +711,75 @@ rp_read(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 *data) { } switch(reg) { case 000: /* control */ - temp = uptr->CMD & 076; + temp = regs[RPCS1]; if (uptr->flags & UNIT_ATT) temp |= CS1_DVA; - if (uptr->CMD & CS1_GO) - temp |= CS1_GO; break; case 001: /* status */ - temp = uptr->CMD & 0177700; + temp = regs[RPDS]; + if ((regs[RPER1] | regs[RPER2] | regs[RPER3]) != 0) + temp |= DS_ERR; + if ((uptr->flags & UNIT_DIS) == 0) + temp |= DS_DPR; + if ((uptr->flags & UNIT_ATT) != 0) + temp |= DS_MOL; + if ((uptr->flags & UNIT_WPRT) != 0) + temp |= DS_WRL; break; case 002: /* error register 1 */ - temp = (uptr->CMD >> 16) & 0177777; + temp = regs[RPER1]; break; case 003: /* maintenance */ + if ((regs[RPMR] & 1) != 0) + temp = regs[RPMR]; break; case 004: /* atten summary */ for (i = 0; i < 8; i++) { - UNIT *u = &dptr->units[i]; - if (u->CMD & DS_ATA) { + UNIT *u = &dptr->units[i]; + uint16 *r = (uint16 *)(u->up7); + if (r[RPDS] & DS_ATA) { temp |= 1 << i; } } break; case 005: /* sector/track */ - temp = (uptr->DA >> 16) & 0177777; + temp = regs[RPDA]; break; case 006: /* drive type */ temp = rp_drv_tab[GET_DTYPE(uptr->flags)].devtype; break; case 011: /* offset */ - temp = (uptr->CCYL >> 16) & 0177777; + temp = regs[RPOF]; break; case 012: /* desired cylinder */ - temp = uptr->DA & 0177777; + temp = regs[RPDC]; break; case 013: /* current cylinder */ - temp = uptr->CCYL & 0177777; + temp = uptr->CCYL; break; case 010: /* serial no */ i = GET_CNTRL_RH(uptr->flags); temp = (020 * i) + (unit + 1); break; case 014: /* error register 2 */ - temp = uptr->ERR2; + temp = regs[RPER2]; break; case 015: /* error register 3 */ - temp = uptr->ERR3; + temp = regs[RPER3]; break; case 007: /* look ahead */ - uptr->LA_REG += 0100; - uptr->LA_REG &= 07700; - temp = uptr->LA_REG; + if ((regs[RPLA] >> 10) >= 23) + regs[RPLA] = 0; + temp = GET_SC(regs[RPDA]) << 6; + temp ^= ((regs[RPLA] + 1) >> 4) & 07760; + if ((regs[RPMR] & 1) == 0) + regs[RPLA] += 1024; break; case 016: /* ecc position */ case 017: /* ecc pattern */ break; default: - uptr->CMD |= (ER1_ILR<<16); + regs[RPER1] |= ER1_ILR; rhc->rae |= 1 << unit; } *data = temp; @@ -739,7 +791,8 @@ t_stat rp_svc (UNIT *uptr) { int dtype = GET_DTYPE(uptr->flags); int ctlr = GET_CNTRL_RH(uptr->flags); - int cyl = GET_CY(uptr->DA); + uint16 *regs = (uint16 *)(uptr->up7); + int cyl = GET_CY; int unit; DEVICE *dptr; struct rh_if *rhc; @@ -749,9 +802,11 @@ t_stat rp_svc (UNIT *uptr) dptr = rp_devs[ctlr]; rhc = &rp_rh[ctlr]; unit = uptr - dptr->units; - 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 */ + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + regs[RPDS] |= DS_ATA; /* set drive error */ + regs[RPDS] &= ~DS_DRY; + regs[RPER1] |= ER1_UNS; /* set drive error */ + if (GET_FNC(regs[RPCS1]) >= FNC_XFER) { /* xfr? set done */ rh_setirq(rhc); } else { rh_setattn(rhc, unit); @@ -760,11 +815,13 @@ t_stat rp_svc (UNIT *uptr) } /* Check if seeking */ - if (uptr->CMD & DS_PIP) { + if (regs[RPDS] & DS_PIP) { 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; + regs[RPDS] &= ~DS_PIP; + regs[RPDS] |= (DS_ATA|DS_DRY); + regs[RPCS1] &= ~CS1_GO; + regs[RPER1] |= ER1_IAE; rh_setattn(rhc, unit); } diff = cyl - (uptr->CCYL & 01777); @@ -793,12 +850,12 @@ t_stat rp_svc (UNIT *uptr) } return SCPE_OK; } else { - uptr->CMD &= ~DS_PIP; + regs[RPDS] &= ~DS_PIP; uptr->DATAPTR = 0; } } - switch (GET_FNC(uptr->CMD)) { + switch (GET_FNC(regs[RPCS1])) { case FNC_NOP: case FNC_DCLR: /* drive clear */ case FNC_RELEASE: /* port release */ @@ -806,60 +863,65 @@ t_stat rp_svc (UNIT *uptr) break; case FNC_UNLOAD: /* unload */ rp_detach(uptr); - /* Fall through */ + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPCS1] &= ~CS1_GO; + rh_setattn(rhc, unit); + sim_debug(DEBUG_DETAIL, dptr, "%s%o unload %d %o\n", dptr->name, unit, cyl, regs[RPCS1]); + break; case FNC_OFFSET: /* offset */ - uptr->CMD |= DS_OFF; + regs[RPDS] |= DS_OFF; /* Fall through */ case FNC_RETURN: /* return to center */ case FNC_PRESET: /* read-in preset */ case FNC_RECAL: /* recalibrate */ case FNC_SEEK: /* seek */ - if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || - GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) - uptr->CMD |= (ER1_IAE << 16)|DS_ERR; - uptr->CMD |= DS_DRY|DS_ATA; - uptr->CMD &= ~CS1_GO; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect || + GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) + regs[RPER1] |= ER1_IAE; + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPCS1] &= ~CS1_GO; rh_setattn(rhc, unit); - sim_debug(DEBUG_DETAIL, dptr, "%s%o seekdone %d %o\n", dptr->name, unit, cyl, uptr->CMD); + sim_debug(DEBUG_DETAIL, dptr, "%s%o seekdone %d %o\n", dptr->name, unit, cyl, regs[RPCS1]); 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; - uptr->CMD |= DS_DRY|DS_ATA; - uptr->CMD &= ~CS1_GO; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect || + GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) + regs[RPER1] |= ER1_IAE; + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPCS1] &= ~CS1_GO; rh_setattn(rhc, unit); - sim_debug(DEBUG_DETAIL, dptr, "%s%o searchdone %d %o\n", dptr->name, unit, cyl, uptr->CMD); + sim_debug(DEBUG_DETAIL, dptr, "%s%o searchdone %d %o\n", dptr->name, unit, cyl, regs[RPCS1]); break; case FNC_READ: /* read */ case FNC_READH: /* read w/ headers */ case FNC_WCHK: /* write check */ - if (uptr->CMD & DS_ERR) { + if (regs[RPER1] != 0) { sim_debug(DEBUG_DETAIL, dptr, "%s%o read error\n", dptr->name, unit); goto rd_end; } if (BUF_EMPTY(uptr)) { - if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || - GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { - uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; - uptr->CMD &= ~CS1_GO; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect || + GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) { + regs[RPER1] |= ER1_IAE; + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPCS1] &= ~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, "%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); + GET_SF(regs[RPDA]), GET_SC(regs[RPDA])); + da = GET_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) { + if (GET_FNC(regs[RPCS1]) == FNC_READH) { rhc->buf = (((uint64)cyl) << 18) | - ((uint64)((GET_SF(uptr->DA) << 8) | GET_SF(uptr->DA))); + ((uint64)((GET_SF(regs[RPDA]) << 8) | GET_SF(regs[RPDA]))); 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) @@ -880,14 +942,14 @@ t_stat rp_svc (UNIT *uptr) /* Increment to next sector. Set Last Sector */ uptr->DATAPTR = 0; CLR_BUF(uptr); - uptr->DA += 1 << DA_V_SC; - if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect) { - uptr->DA &= (DA_M_SF << DA_V_SF) | (DC_M_CY << DC_V_CY); - uptr->DA += 1 << DA_V_SF; - if (GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { - uptr->DA &= (DC_M_CY << DC_V_CY); - uptr->DA += 1 << DC_V_CY; - uptr->CMD |= DS_PIP; + regs[RPDA] += 1 << DA_V_SC; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect) { + regs[RPDA] &= (DA_M_SF << DA_V_SF); + regs[RPDA] += 1 << DA_V_SF; + if (GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) { + regs[RPDA] = 0; + regs[RPDC] += 1 << DC_V_CY; + regs[RPDS] |= DS_PIP; } } if (rh_blkend(rhc)) @@ -897,8 +959,9 @@ t_stat rp_svc (UNIT *uptr) } else { rd_end: sim_debug(DEBUG_DETAIL, dptr, "%s%o read done\n", dptr->name, unit); - uptr->CMD |= DS_DRY; - uptr->CMD &= ~CS1_GO; + regs[RPCS1] &= ~CS1_GO; + regs[RPDS] &= ~DS_PIP; + regs[RPDS] |= DS_DRY; if (uptr->DATAPTR == RP_NUMWD) (void)rh_blkend(rhc); rh_finish_op(rhc, 0); @@ -908,22 +971,23 @@ rd_end: case FNC_WRITE: /* write */ case FNC_WRITEH: /* write w/ headers */ - if (uptr->CMD & DS_ERR) { + if (regs[RPER1] != 0) { sim_debug(DEBUG_DETAIL, dptr, "%s%o read error\n", dptr->name, unit); goto wr_end; } if (BUF_EMPTY(uptr)) { - if (GET_SC(uptr->DA) >= rp_drv_tab[dtype].sect || - GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { - uptr->CMD |= (ER1_IAE << 16)|DS_ERR|DS_DRY|DS_ATA; - uptr->CMD &= ~CS1_GO; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect || + GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) { + regs[RPER1] |= ER1_IAE; + regs[RPDS] |= DS_ATA|DS_DRY; + regs[RPCS1] &= ~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 (GET_FNC(regs[RPCS1]) == FNC_WRITEH) { if (rh_read(rhc) == 0) goto wr_end; sim_debug(DEBUG_DATA, dptr, "%s%o write word h1 %012llo %06o\n", @@ -946,20 +1010,20 @@ rd_end: } 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); + unit, cyl, GET_SF(regs[RPDA]), GET_SC(regs[RPDA])); + da = GET_DA(dtype); (void)disk_write(uptr, &rp_buf[ctlr][0], da, RP_NUMWD); uptr->DATAPTR = 0; CLR_BUF(uptr); 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); - uptr->DA += 1 << DA_V_SF; - if (GET_SF(uptr->DA) >= rp_drv_tab[dtype].surf) { - uptr->DA &= (DC_M_CY << DC_V_CY); - uptr->DA += 1 << DC_V_CY; - uptr->CMD |= DS_PIP; + regs[RPDA] += 1 << DA_V_SC; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect) { + regs[RPDA] &= (DA_M_SF << DA_V_SF); + regs[RPDA] += 1 << DA_V_SF; + if (GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) { + regs[RPDA] = 0; + regs[RPDC] += 1 << DC_V_CY; + regs[RPDS] |= DS_PIP; } } } @@ -971,8 +1035,8 @@ rd_end: } else { wr_end: sim_debug(DEBUG_DETAIL, dptr, "RP%o write done\n", unit); - uptr->CMD |= DS_DRY; - uptr->CMD &= ~CS1_GO; + regs[RPCS1] &= ~CS1_GO; + regs[RPDS] |= DS_DRY; rh_finish_op(rhc, 0); return SCPE_OK; } @@ -999,11 +1063,27 @@ rp_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc) t_stat rp_reset(DEVICE * rptr) { - int ctlr; - for (ctlr = 0; ctlr < NUM_DEVS_RP; ctlr++) { - rp_rh[ctlr].status = 0; - rp_rh[ctlr].attn = 0; - rp_rh[ctlr].rae = 0; + UNIT *uptr = rptr->units; + uint16 *regs; + int ctlr = GET_CNTRL_RH(uptr->flags); + uint32 i; + + rh_reset(rptr, &rp_rh[ctlr]); + for (i = 0; i < rptr->numunits; i++) { + if (uptr->up7 == 0 && (uptr->up7 = calloc(16, sizeof(uint16))) == 0) + return SCPE_IERR; + regs = (uint16 *)(uptr->up7); + regs[RPDS] &= DS_VV; + if ((uptr->flags & UNIT_ATT) != 0) /* attached? */ + regs[RPDS] |= DS_DRY; + if (regs[RPMR] & 1) + uptr->CCYL = GET_CY; + regs[RPER1] = 0; + regs[RPER2] = 0; + regs[RPER3] = 0; + regs[RPDC] = 0; + regs[RPMR] = 0; + uptr++; } return SCPE_OK; } @@ -1014,13 +1094,87 @@ rp_boot(int32 unit_num, DEVICE * rptr) { UNIT *uptr = &rptr->units[unit_num]; int ctlr = GET_CNTRL_RH(uptr->flags); + int dtype = GET_DTYPE(uptr->flags); + uint16 *regs = (uint16 *)(uptr->up7); struct rh_if *rhc = &rp_rh[ctlr]; - DEVICE *dptr = rp_devs[ctlr]; + DEVICE *dptr = uptr->dptr; uint32 addr; uint32 ptr = 0; - int wc; uint64 word; -#if KL +#if !KS + int wc; +#endif + +#if KS + int da; + t_stat r; + uint64 len; + + if ((r = rp_reset(dptr)) != SCPE_OK) + return r; + if ((r = cty_reset(&cty_dev)) != SCPE_OK) + return r; + /* Read in block 1 and see if it is a home block */ + disk_read(uptr, &rp_buf[0][0], 1, RP_NUMWD); + if (rp_buf[0][0] != 0505755000000LL) { + /* Try blocks 10 and 12 if fail */ + disk_read(uptr, &rp_buf[0][0], 010, RP_NUMWD); + if (rp_buf[0][0] != 0505755000000LL) { + disk_read(uptr, &rp_buf[0][0], 012, RP_NUMWD); + if (rp_buf[0][0] != 0505755000000LL) + return SCPE_IERR; + } + } + + /* Word 103 and 102 contain pointer to SMFILE block */ + regs[RPDA] = (int32)((rp_buf[0][0103] & 077) << DA_V_SC) | + (int32)(((rp_buf[0][0103] >> 8) & 077) << DA_V_SF); + regs[RPDC] = (int32)((rp_buf[0][0103] >> 24) << DC_V_CY); + len = (int)(rp_buf[0][0102] & RMASK); + da = GET_DA(dtype); + disk_read(uptr, &rp_buf[0][0], da, RP_NUMWD); + /* For diagnostics use locations 6 and 7 */ + if (sim_switches & SWMASK ('D')) { + sim_messagef(SCPE_OK, "Diags boot\n"); + regs[RPDA] = (int32)((rp_buf[0][06] & 077) << DA_V_SC) | + (int32)(((rp_buf[0][06] >> 8) & 077) << DA_V_SF); + regs[RPDC] = (int32)((rp_buf[0][06] >> 24) << DC_V_CY); + len = (int)(((rp_buf[0][07] & 077) * 4) & RMASK); + } else { + /* Normal is at 4 and 5*/ + regs[RPDA] = (int32)((rp_buf[0][04] & 077) << DA_V_SC) | + (int32)(((rp_buf[0][04] >> 8) & 077) << DA_V_SF); + regs[RPDC] = (int32)((rp_buf[0][04] >> 24) << DC_V_CY); + len = (int)(((rp_buf[0][05] & 077) * 4) & RMASK); + } +if (len == 0) + len = 4; + /* Read len sectors into address 1000 */ + addr = 01000; + for (; len > 0; len--) { + int i; + + da = GET_DA(dtype); + disk_read(uptr, &rp_buf[0][0], da, RP_NUMWD); + for (i = 0; i < RP_NUMWD; i++) { + M[addr++] = rp_buf[0][i]; + } + regs[RPDA] += 1 << DA_V_SC; + if (GET_SC(regs[RPDA]) >= rp_drv_tab[dtype].sect) { + regs[RPDA] &= (DA_M_SF << DA_V_SF); + regs[RPDA] += 1 << DA_V_SF; + if (GET_SF(regs[RPDA]) >= rp_drv_tab[dtype].surf) { + regs[RPDC] += 1 << DC_V_CY; + } + } + } + /* Start location, and set up load info */ + word = 01000; + M[036] = rhc->dib->uba_addr | (rhc->dib->uba_ctl << 18); + M[037] = unit_num; + rh_boot_dev = rptr; + rh_boot_unit = unit_num; +#elif 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 */ @@ -1051,11 +1205,13 @@ rp_boot(int32 unit_num, DEVICE * rptr) wc = (rp_buf[0][ptr++] >> 18) & RMASK; word = rp_buf[0][ptr++]; #endif - PC = word & RMASK; - uptr->CMD |= DS_VV; +#if !KS rhc->reg = 040; - rhc->drive = uptr - dptr->units; rhc->status |= CCW_COMP_1|PI_ENABLE; +#endif + rhc->drive = uptr - dptr->units; + PC = word & RMASK; + regs[RPDS] |= DS_VV; return SCPE_OK; } @@ -1063,10 +1219,10 @@ rp_boot(int32 unit_num, DEVICE * rptr) t_stat rp_attach (UNIT *uptr, CONST char *cptr) { - t_stat r; - DEVICE *rptr; - DIB *dib; - int ctlr; + t_stat r; + DEVICE *rptr; + int ctlr; + uint16 *regs = (uint16 *)(uptr->up7); uptr->capac = rp_drv_tab[GET_DTYPE (uptr->flags)].size; r = disk_attach (uptr, cptr); @@ -1075,20 +1231,20 @@ t_stat rp_attach (UNIT *uptr, CONST char *cptr) rptr = find_dev_from_unit(uptr); if (rptr == 0) return SCPE_OK; - dib = (DIB *) rptr->ctxt; +#if KS + ctlr = 0; +#else for (ctlr = 0; rh[ctlr].dev_num != 0; ctlr++) { if (rh[ctlr].dev == rptr) break; } - if (uptr->flags & UNIT_WPRT) - uptr->CMD |= DS_WRL; +#endif if (sim_switches & SIM_SW_REST) return SCPE_OK; - uptr->DA = 0; - uptr->CMD &= ~DS_VV; - uptr->CMD |= DS_DPR|DS_MOL|DS_DRY; - rp_rh[ctlr].status |= PI_ENABLE; - set_interrupt(dib->dev_num, rp_rh[ctlr].status); + regs[RPDA] = 0; + regs[RPDS] &= ~DS_VV; + regs[RPDS] |= DS_DRY; + rh_setirq(&rp_rh[ctlr]); return SCPE_OK; } @@ -1096,11 +1252,13 @@ t_stat rp_attach (UNIT *uptr, CONST char *cptr) t_stat rp_detach (UNIT *uptr) { + uint16 *regs = (uint16 *)(uptr->up7); + if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (sim_is_active (uptr)) /* unit active? */ sim_cancel (uptr); /* cancel operation */ - uptr->CMD &= ~(DS_VV|DS_WRL|DS_DPR|DS_DRY); + regs[RPDS] &= ~(DS_VV|DS_DRY); return disk_detach (uptr); } @@ -1116,6 +1274,17 @@ fprint_set_help (st, dptr); fprint_show_help (st, dptr); fprintf (st, "\nThe type options can be used only when a unit is not attached to a file.\n"); fprintf (st, "The RP device supports the BOOT command.\n"); +#if KS +fprintf (st, "The RH11 is a unibus device, various parameters can be changed on these devices\n"); +fprintf (st, "\n The address of the device can be set with: \n"); +fprintf (st, " sim> SET RPA ADDR=octal default address= 776700\n"); +fprintf (st, "\n The interrupt vector can be set with: \n"); +fprintf (st, " sim> SET RPA VECT=octal default 254\n"); +fprintf (st, "\n The interrupt level can be set with: \n"); +fprintf (st, " sim> SET RPA BR=# # should be between 4 and 7.\n"); +fprintf (st, "\n The unibus addaptor that the DZ is on can be set with:\n"); +fprintf (st, " sim> SET RPA CTL=# # can be either 1 or 3\n"); +#endif fprint_reg_help (st, dptr); return SCPE_OK; } diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index 2204e2bd..64269957 100644 --- a/PDP10/kx10_sys.c +++ b/PDP10/kx10_sys.c @@ -42,6 +42,9 @@ sim_load binary loader */ +#if KS +char sim_name[] = "KS-10"; +#endif #if KL char sim_name[] = "KL-10"; #endif @@ -62,7 +65,7 @@ int32 sim_emax = 1; DEVICE *sim_devices[] = { &cpu_dev, -#if PDP6 | KA | KI +#if PDP6 | KA | KI | KS &cty_dev, #endif #if KL @@ -171,6 +174,9 @@ DEVICE *sim_devices[] = { #if (NUM_DEVS_III > 0) &iii_dev, #endif +#if (NUM_DEVS_TV > 0) + &tv_dev, +#endif #if NUM_DEVS_IMP > 0 &imp_dev, #endif @@ -180,6 +186,9 @@ DEVICE *sim_devices[] = { #if NUM_DEVS_CH10 > 0 &ch10_dev, #endif +#if NUM_DEVS_CH11 > 0 + &ch11_dev, +#endif #if NUM_DEVS_IMX > 0 &imx_dev, #endif @@ -211,6 +220,21 @@ DEVICE *sim_devices[] = { #endif #if NUM_DEVS_AI > 0 &ai_dev, +#endif +#if NUM_DEVS_DZ > 0 + &dz_dev, +#endif +#if NUM_DEVS_TCU > 0 + &tcu_dev, +#endif +#if NUM_DEVS_KMC > 0 + &kmc_dev, +#endif +#if NUM_DEVS_DUP > 0 + &dup_dev, +#endif +#if NUM_DEVS_DN > 0 + &dn_dev, #endif NULL }; @@ -279,24 +303,20 @@ t_stat load_dmp (FILE *fileref) uint64 data; uint32 high = 0; - while (fgets((char *)buffer, 80, fileref) != 0) { - p = (char *)buffer; - while (*p >= '0' && *p <= '7') { - data = 0; - while (*p >= '0' && *p <= '7') { - data = (data << 3) + *p - '0'; - p++; - } - if (addr == 0135 && data != 0) - high = (uint32)(data & RMASK); - if (high != 0 && high == addr) { - addr = 0400000; - high = 0; - } - M[addr++] = data; - if (*p == ' ' || *p == '\t') - p++; - } + while (fgets(&buffer[0], 80, fileref) != 0) { + data = 0; + p = &buffer[0]; + if (*p >= '0' && *p <= '7') { + for (; *p >= '0' && *p <= '7'; p++) + data = (data << 3) + *p - '0'; + if (addr == 0135 && data != 0) + high = (uint32)(data & RMASK); + if (high != 0 && high == addr) { + addr = 0400000; + high = 0; + } + M[addr++] = data; + } } return SCPE_OK; } @@ -585,7 +605,7 @@ t_stat load_sav (FILE *fileref, int ftype) wc = (int32)(data >> 18); pa = (uint32) (data & RMASK); if (wc == (OP_JRST << 9)) { - printf("Start addr=%06o\n", pa); + sim_printf("Start addr=%06o\n", pa); PC = pa; return SCPE_OK; } @@ -769,6 +789,8 @@ t_stat load_exb (FILE *fileref, int ftype) return SCPE_FMT; addr |= byt << 24; /* Empty record gives start address */ + if (addr > MEMSIZE) + return SCPE_FMT; if (wc == 0) { PC = addr; return SCPE_OK; @@ -784,6 +806,8 @@ t_stat load_exb (FILE *fileref, int ftype) case 2: word |= ((uint64)byt) << 16; break; case 3: word |= ((uint64)byt) << 24; break; case 4: word |= ((uint64)(byt & 017)) << 32; + if (addr > MEMSIZE) + return SCPE_FMT; M[addr++] = word; pos = -1; break; @@ -902,7 +926,7 @@ static const char *opcode[] = { "SETSTS", "STATO", "STATUS", "GETSTS", "INBUF", "OUTBUF", "INPUT", "OUTPUT", "CLOSE", "RELEAS", "MTAPE", "UGETF", "USETI", "USETO", "LOOKUP", "ENTER", -#if KL +#if KL | KS "UJEN", "GFAD", "GFSB", "JSYS", "ADJSP", "GFMP", "GFDV ", "DFAD", "DFSB", "DFMP", "DFDV", "DADD", "DSUB", "DMUL", "DDIV", #else @@ -961,6 +985,19 @@ static const char *opcode[] = { "TRO", "TLO", "TROE", "TLOE", "TROA", "TLOA", "TRON", "TLON", "TDO", "TSO", "TDOE", "TSOE", "TDOA", "TSOA", "TDON", "TSON", +#if KS + "APRID", "CLRSCH", "RDUBR", "CLRPT", + "WRUBR", "WREBR", "RDEBR", + "RDSPB", "RDCSB", "RDPUR", "RDCSTM", + "RDTIME", "RDINT", "RDHSB", "SPM", + "WRSPB", "WRCSB", "WRPUR", "WRCSTM", + "WRTIME", "WRINT", "WRHSB", "LPMR", + "UMOVE", "UMOVEM", + "TIOE", "TION", "RDIO", "WRIO", + "BSIO", "BCIO", + "TIOEB", "TIONB", "RDIOB", "WRIOB", + "BSIOB", "BCIOB", +#endif "BLKI", "DATAI", "BLKO", "DATAO", /* classic I/O */ "CONO", "CONI", "CONSZ", "CONSO", @@ -1095,9 +1132,21 @@ static const t_int64 opc_val[] = { 0670000000000+I_AC, 0671000000000+I_AC, 0672000000000+I_AC, 0673000000000+I_AC, 0674000000000+I_AC, 0675000000000+I_AC, 0676000000000+I_AC, 0677000000000+I_AC, +#if KS + 0700000000000+I_OP, 0701000000000+I_OP, 0701040000000+I_OP, 0701100000000+I_OP, + 0701140000000+I_OP, 0701200000000+I_OP, 0701240000000+I_OP, + 0702000000000+I_OP, 0702040000000+I_OP, 0702100000000+I_OP, 0702140000000+I_OP, + 0702200000000+I_OP, 0702240000000+I_OP, 0702300000000+I_OP, 0702340000000+I_OP, + 0702400000000+I_OP, 0702440000000+I_OP, 0702500000000+I_OP, 0702540000000+I_OP, + 0702600000000+I_OP, 0702640000000+I_OP, 0702700000000+I_OP, 0702740000000+I_OP, + 0704000000000+I_AC, 0705000000000+I_AC, + 0710000000000+I_AC, 0711000000000+I_AC, 0712000000000+I_AC, 0713000000000+I_AC, + 0714000000000+I_AC, 0715000000000+I_AC, + 0720000000000+I_AC, 0721000000000+I_AC, 0722000000000+I_AC, 0723000000000+I_AC, + 0724000000000+I_AC, 0725000000000+I_AC, +#endif 0700000000000+I_IO, 0700040000000+I_IO, 0700100000000+I_IO, 0700140000000+I_IO, 0700200000000+I_IO, 0700240000000+I_IO, 0700300000000+I_IO, 0700340000000+I_IO, - -1 }; diff --git a/PDP10/kx10_tu.c b/PDP10/kx10_tu.c index 8fb8b5f3..a49e0bcf 100644 --- a/PDP10/kx10_tu.c +++ b/PDP10/kx10_tu.c @@ -41,7 +41,6 @@ #define TU_UNIT UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE #define CMD u3 -/* u3 low */ /* TUC - 00 - control */ #define CS1_GO 1 /* go */ @@ -64,15 +63,12 @@ #define FNC_READ 034 /* read */ #define FNC_READREV 037 /* read reverse */ #define CS1_DVA 0004000 /* drive avail NI */ +#define CS1_TRE 0040000 /* Set if errors */ +#define CS1_SC 0100000 /* Set if TRE or ATTN */ #define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) -#define CS_TM 001000 /* Tape mark sensed */ -#define CS_MOTION 002000 /* Tape moving */ -#define CS_PIP 004000 /* Tape Position command */ -#define CS_ATA 010000 /* Tape signals attention */ -#define CS_CHANGE 020000 /* Status changed */ -#define STATUS u5 -/* u5 low */ +#define TUDS 01 +#define STATUS u4 /* Attentions status for device */ /* TUDS - 01 - drive status */ #define DS_SLA 0000001 /* Drive has become ready */ @@ -92,7 +88,7 @@ #define DS_ERR 0040000 /* error */ #define DS_ATA 0100000 /* attention active */ -/* u5 high */ +#define TUER1 02 /* TUER1 - 02 - error status 1 */ #define ER1_ILF 0000001 /* illegal func */ @@ -112,20 +108,27 @@ #define ER1_UNS 0040000 /* drive unsafe */ #define ER1_DCK 0100000 /* data check NI */ +#define TUMR 03 /* TUMR - 03 - maintenace register */ +#define TUAS 04 /* TUAS - 04 - attention summary */ #define AS_U0 0000001 /* unit 0 flag */ +#define TUDC 05 /* TUDC - 05 - frame count */ +#define TUDT 06 /* TUDT - 06 - drive type */ +#define TULA 07 /* TULA - 07 - Check Character */ +#define TUSN 010 /* TUSN - 10 - serial number */ +#define TUTC 011 /* TUTC - 11 - Tape control register */ #define TC_SS 0000007 /* Slave select mask */ #define TC_EVPAR 0000010 /* Even parity */ @@ -153,12 +156,10 @@ /* TUER3 - 15 - error status 3 - more unsafe conditions - unimplemented */ -#define CPOS u4 +#define CPOS u5 #define DATAPTR u6 uint8 tu_buf[NUM_DEVS_TU][TU_NUMFR]; -uint16 tu_frame[NUM_DEVS_TU]; -uint16 tu_tcr[NUM_DEVS_TU]; static uint64 tu_boot_buffer; int tu_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data); @@ -173,6 +174,10 @@ t_stat tu_detach(UNIT *); t_stat tu_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); const char *tu_description (DEVICE *dptr); +#if KS +extern DEVICE *rh_boot_dev; +extern int rh_boot_unit; +#endif UNIT tu_unit[] = { @@ -187,6 +192,15 @@ UNIT tu_unit[] = { { UDATA (&tu_srv, TU_UNIT+CNTRL_RH(0), 0) }, }; +#if KS +struct rh_if tu_rh[NUM_DEVS_TU] = { + { &tu_write, &tu_read}, +}; + +DIB tu_dib[NUM_DEVS_TU] = { + {0772440, 037, 0224, 6, 3, &uba_rh_read, &uba_rh_write, 0, 0, &tu_rh[0]}, +}; +#else struct rh_if tu_rh[NUM_DEVS_TU] = { { &tu_write, &tu_read} }; @@ -194,6 +208,7 @@ struct rh_if tu_rh[NUM_DEVS_TU] = { DIB tu_dib[] = { {RH10_DEV, 1, &rh_devio, &rh_devirq, &tu_rh[0]} }; +#endif MTAB tu_mod[] = { #if KL @@ -202,35 +217,45 @@ MTAB tu_mod[] = { {MTAB_XTD|MTAB_VDV, TYPE_RH20, "RH20", "RH20", &rh_set_type, &rh_show_type, NULL, "Sets controller to RH20"}, #endif - { MTAB_XTD|MTAB_VUN, 0, "write enabled", "WRITEENABLED", - &set_writelock, &show_writelock, NULL, "Write ring in place" }, - { MTAB_XTD|MTAB_VUN, 1, NULL, "LOCKED", - &set_writelock, NULL, NULL, "no Write ring in place" }, + {MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL}, + {MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL}, {MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_tape_set_fmt, &sim_tape_show_fmt, NULL}, {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LENGTH", "LENGTH", &sim_tape_set_capac, &sim_tape_show_capac, NULL}, {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DENSITY", "DENSITY", &sim_tape_set_dens, &sim_tape_show_dens, NULL}, +#if KS + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr, + NULL, "Sets address of RH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect, + NULL, "Sets vect of RH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br, + NULL, "Sets br of RH11" }, + {MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl, + NULL, "Sets uba of RH11" }, +#endif {0} }; REG tua_reg[] = { +#if !KS {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_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(CIA, tu_rh[0].cia, 18)}, + {ORDATA(CCW, tu_rh[0].ccw, 18)}, + {ORDATA(DEVNUM, tu_rh[0].devnum, 9), REG_HRO}, +#endif + {ORDATA(FRAME, tu_rh[0].regs[TUDC], 16)}, + {ORDATA(TCR, tu_rh[0].regs[TUTC], 16)}, + {ORDATA(DRIVE, tu_rh[0].drive, 3), REG_HRO}, {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} @@ -252,15 +277,16 @@ DEVICE *tu_devs[] = { int 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; + uint16 *regs = &rhc->regs[0]; + int unit = regs[TUTC] & 07; UNIT *uptr = &dptr->units[unit]; int i; if (rhc->drive != 0 && reg != 04) /* Only one unit at 0 */ - return 1; + return -1; - if (uptr->CMD & CS1_GO) { - uptr->STATUS |= (ER1_RMR); + if ((uptr->CMD & CS1_GO) != 0 || (uptr->STATUS & DS_PIP) != 0) { + regs[TUER1] |= ER1_RMR; return 0; } @@ -268,93 +294,122 @@ tu_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { case 000: /* control */ 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)) { - case FNC_NOP: - break; - - case FNC_PRESET: /* read-in preset */ - 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_WTM: /* Write tape mark */ - case FNC_WCHK: /* write check */ - case FNC_REWIND: /* rewind */ - case FNC_UNLOAD: /* unload */ - case FNC_WCHKREV: /* write w/ headers */ - 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; - sim_activate(uptr, 100); - break; - - case FNC_DCLR: /* drive clear */ - uptr->CMD &= ~(CS_ATA|CS1_GO|CS_TM); - uptr->STATUS = 0; - rhc->status &= ~PI_ENABLE; - rhc->attn = 0; - for (i = 0; i < 8; i++) { - if (dptr->units[i].CMD & CS_ATA) - rhc->attn = 1; - } - break; - default: - uptr->STATUS |= (ER1_ILF); - uptr->CMD |= CS_ATA; - rhc->attn = 1; - } - sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, - uptr->CMD); + uptr->CMD = data & 076; + if ((data & 01) == 0) { + sim_debug(DEBUG_DETAIL, &tua_dev, "TU%o no go %06o\n", unit, data); + return 0; /* No, nop */ } + + if ((uptr->flags & UNIT_ATT) == 0) { + if (GET_FNC(data) == FNC_DCLR) { + uptr->STATUS = 0; + rhc->attn = 0; + for (i = 0; i < NUM_UNITS_TU; i++) { + if (dptr->units[i].STATUS & DS_ATA) + rhc->attn = 1; + } + } + sim_debug(DEBUG_DETAIL, &tua_dev, "TU%o unattached %06o\n", unit, data); + return 0; /* No, nop */ + } + + switch (GET_FNC(data)) { + case FNC_NOP: + break; + + case FNC_PRESET: /* read-in preset */ + case FNC_READ: /* read */ + case FNC_READREV: /* read w/ headers */ + regs[TUDC] = 0; + regs[TUTC] |= TC_FCS; + /* Fall through */ + + case FNC_WRITE: /* write */ + case FNC_SPACEF: /* Space forward */ + case FNC_SPACEB: /* Space backward */ + if ((regs[TUTC] & TC_FCS) == 0) { + regs[TUER1] |= ER1_NEF; + break; + } + /* Fall through */ + + case FNC_ERASE: /* Erase gap */ + case FNC_WTM: /* Write tape mark */ + case FNC_WCHK: /* write check */ + case FNC_REWIND: /* rewind */ + case FNC_UNLOAD: /* unload */ + case FNC_WCHKREV: /* write w/ headers */ + uptr->CMD |= CS1_GO; + uptr->STATUS = DS_PIP; + regs[TUTC] |= TC_ACCL; + regs[TUER1] = 0; + rhc->attn = 0; + for (i = 0; i < NUM_UNITS_TU; i++) { + if (dptr->units[i].STATUS & DS_ATA) + rhc->attn = 1; + } + CLR_BUF(uptr); + uptr->DATAPTR = 0; + sim_activate(uptr, 100); + break; + + case FNC_DCLR: /* drive clear */ + uptr->CMD &= ~(CS1_GO); + uptr->STATUS = 0; + regs[TUER1] = 0; + regs[TUTC] &= ~TC_FCS; + rhc->status &= ~PI_ENABLE; + rhc->attn = 0; + for (i = 0; i < NUM_UNITS_TU; i++) { + if (dptr->units[i].STATUS & DS_ATA) + rhc->attn = 1; + } + break; + default: + regs[TUER1] |= ER1_ILF; + uptr->STATUS = DS_ATA; + rhc->attn = 1; + rh_setattn(rhc, 0); + } + sim_debug(DEBUG_DETAIL, dptr, "%s%o AStatus=%06o\n", dptr->name, unit, + uptr->CMD); return 0; case 001: /* status */ break; case 002: /* error register 1 */ - uptr->STATUS &= ~0177777; - uptr->STATUS |= data; + regs[TUER1] = data; break; case 003: /* maintenance */ + regs[TUMR] = data; + fprintf(stderr, "TU MR=%06o\r\n", data); break; case 004: /* atten summary */ rhc->attn = 0; if (data & 1) { - for (i = 0; i < 8; i++) - dptr->units[i].CMD &= ~CS_ATA; + for (i = 0; i < NUM_UNITS_TU; i++) + dptr->units[i].STATUS &= ~DS_ATA; } break; case 005: /* frame count */ - tu_frame[ctlr] = data & 0177777; - tu_tcr[ctlr] |= TC_FCS; + regs[TUDC] = data; + regs[TUTC] |= TC_FCS; break; case 006: /* drive type */ case 007: /* look ahead */ + case 010: /* look ahead */ + break; case 011: /* tape control register */ - tu_tcr[ctlr] = data & 0177777 ; + regs[TUTC] = data; break; default: - uptr->STATUS |= ER1_ILR; - uptr->CMD |= CS_ATA; +#if KS + return 1; +#else + regs[TUER1] |= ER1_ILR; + uptr->STATUS = DS_ATA; rhc->attn = 1; rhc->rae = 1; +#endif } return 0; } @@ -362,77 +417,76 @@ tu_write(DEVICE *dptr, struct rh_if *rhc, int reg, uint32 data) { int tu_read(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; + uint16 *regs = &rhc->regs[0]; + int unit = regs[TUTC] & 07; UNIT *uptr = &dptr->units[unit]; uint32 temp = 0; int i; if (rhc->drive != 0 && reg != 4) /* Only one unit at 0 */ - return 1; + return -1; switch(reg) { case 000: /* control */ - temp = uptr->CMD & 076; + temp = uptr->CMD & 077; temp |= CS1_DVA; - if (uptr->CMD & CS1_GO) - temp |= CS1_GO; break; case 001: /* status */ - temp = DS_DPR; - if (uptr->CMD & CS_ATA) - temp |= DS_ATA; - if (uptr->CMD & CS_CHANGE) - temp |= DS_SSC; - if ((uptr->STATUS & 0177777) != 0) + temp = DS_DPR | uptr->STATUS; + if (regs[TUER1] != 0) temp |= DS_ERR; if ((uptr->flags & UNIT_ATT) != 0) { temp |= DS_MOL; - if (uptr->CMD & CS_TM) - temp |= DS_TM; - if (uptr->flags & MTUF_WRP) + if ((regs[TUTC] & TC_DENS) == TC_1600) + temp |= DS_PES; + if (uptr->flags & MTUF_WLK) temp |= DS_WRL; - if ((uptr->CMD & (CS_MOTION|CS_PIP|CS1_GO)) == 0) + if ((uptr->CMD & (CS1_GO)) == 0 && (uptr->STATUS & (DS_PIP)) == 0) temp |= DS_DRY; if (sim_tape_bot(uptr)) temp |= DS_BOT; if (sim_tape_eot(uptr)) temp |= DS_EOT; - if ((uptr->CMD & CS_MOTION) == 0) + if ((uptr->CMD & CS1_GO) == 0) temp |= DS_SDWN; - if (uptr->CMD & CS_PIP) - temp |= DS_PIP; } break; case 002: /* error register 1 */ - temp = uptr->STATUS & 0177777; + temp = regs[TUER1]; break; case 004: /* atten summary */ - for (i = 0; i < 8; i++) { - if (dptr->units[i].CMD & CS_ATA) + for (i = 0; i < NUM_UNITS_TU; i++) { + if (dptr->units[i].STATUS & DS_ATA) temp |= 1; } break; case 005: /* frame count */ - temp = tu_frame[ctlr]; + temp = regs[TUDC]; break; case 006: /* drive type */ if ((uptr->flags & UNIT_DIS) == 0) - temp = 042054; - break; - case 011: /* tape control register */ - temp = tu_tcr[ctlr]; + temp = 0142054; break; case 010: /* serial no */ temp = 020 + (unit + 1); break; + case 011: /* tape control register */ + temp = regs[TUTC]; + break; case 003: /* maintenance */ + temp = regs[TUMR]; + break; case 007: /* look ahead */ break; default: - uptr->STATUS |= (ER1_ILR); - uptr->CMD |= CS_ATA; +#if KS + return 1; +#else + regs[TUER1] |= ER1_ILR; + uptr->STATUS |= DS_ATA; rhc->attn = 1; rhc->rae = 1; +#endif } *data = temp; return 0; @@ -445,18 +499,19 @@ void tu_error(UNIT * uptr, t_stat r) int ctlr = GET_CNTRL_RH(uptr->flags); DEVICE *dptr = tu_devs[ctlr]; struct rh_if *rhc = &tu_rh[ctlr]; + uint16 *regs = &rhc->regs[0]; switch (r) { case MTSE_OK: /* no error */ break; case MTSE_TMK: /* tape mark */ - uptr->CMD |= CS_TM; + uptr->STATUS |= DS_TM; break; case MTSE_WRP: /* write protected */ - uptr->STATUS |= (ER1_NEF); - uptr->CMD |= CS_ATA; + regs[TUER1] |= ER1_NEF; + uptr->STATUS |= DS_ATA; break; case MTSE_UNATT: /* unattached */ @@ -466,26 +521,27 @@ void tu_error(UNIT * uptr, t_stat r) case MTSE_IOERR: /* IO error */ case MTSE_FMT: /* invalid format */ - uptr->STATUS |= (ER1_PEF); - uptr->CMD |= CS_ATA; + regs[TUER1] |= ER1_PEF; + uptr->STATUS |= DS_ATA; break; case MTSE_RECE: /* error in record */ - uptr->STATUS |= (ER1_DPAR); - uptr->CMD |= CS_ATA; + regs[TUER1] |= ER1_DPAR; + uptr->STATUS |= DS_ATA; break; case MTSE_INVRL: /* invalid rec lnt */ - uptr->STATUS |= (ER1_FCE); - uptr->CMD |= CS_ATA; + regs[TUER1] |= ER1_FCE; + uptr->STATUS |= DS_ATA; break; } - if (uptr->CMD & CS_ATA) + if (uptr->STATUS & DS_ATA) rh_setattn(rhc, 0); - if (GET_FNC(uptr->CMD) >= FNC_XFER && uptr->CMD & (CS_ATA | CS_TM)) + if (GET_FNC(uptr->CMD) >= FNC_XFER && uptr->STATUS & (DS_ATA | DS_TM)) rh_error(rhc); - uptr->CMD &= ~(CS_MOTION|CS_PIP|CS1_GO); + uptr->CMD &= ~(CS1_GO); + uptr->STATUS &= ~(DS_PIP); sim_debug(DEBUG_EXP, dptr, "Setting status %d\n", r); } @@ -495,6 +551,7 @@ t_stat tu_srv(UNIT * uptr) int ctlr = GET_CNTRL_RH(uptr->flags); int unit; struct rh_if *rhc; + uint16 *regs; DEVICE *dptr; t_stat r; t_mtrlnt reclen; @@ -505,8 +562,10 @@ t_stat tu_srv(UNIT * uptr) /* Find dptr, and df10 */ dptr = tu_devs[ctlr]; rhc = &tu_rh[ctlr]; + regs = &rhc->regs[0]; unit = uptr - dptr->units; - cc_max = (4 + ((tu_tcr[ctlr] & TC_FMTSEL) == 0)); + cc_max = (4 + ((regs[TUTC] & TC_FMTSEL) == 0)); + regs[TUTC] &= ~TC_ACCL; if ((uptr->flags & UNIT_ATT) == 0) { tu_error(uptr, MTSE_UNATT); /* attached? */ rh_setirq(rhc); @@ -522,38 +581,35 @@ t_stat tu_srv(UNIT * uptr) case FNC_REWIND: sim_debug(DEBUG_DETAIL, dptr, "%s%o rewind\n", dptr->name, unit); - if (uptr->CMD & CS1_GO) { + if (uptr->STATUS & 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; - rh_setattn(rhc, 0); - (void)sim_tape_rewind(uptr); + sim_debug(DEBUG_DETAIL, dptr, "%s%o rewind done\n", dptr->name, unit); + uptr->STATUS |= DS_SSC|DS_ATA; + tu_error(uptr, sim_tape_rewind(uptr)); } return SCPE_OK; case FNC_UNLOAD: sim_debug(DEBUG_DETAIL, dptr, "%s%o unload\n", dptr->name, unit); - uptr->CMD |= CS_CHANGE|CS_ATA; + uptr->STATUS |= DS_SSC|DS_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; + uptr->STATUS &= ~DS_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; + regs[TUER1] |= 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; @@ -563,7 +619,9 @@ t_stat tu_srv(UNIT * uptr) return SCPE_OK; } if (uptr->DATAPTR >= 0) { - tu_frame[ctlr]++; + regs[TUDC]++; + if (regs[TUDC] == 0) + regs[TUTC] &= ~TC_FCS; cc = (8 * (3 - uptr->CPOS)) + 4; ch = tu_buf[ctlr][uptr->DATAPTR]; if (cc < 0) @@ -596,13 +654,12 @@ t_stat tu_srv(UNIT * uptr) case FNC_WCHK: case FNC_READ: if (BUF_EMPTY(uptr)) { - uptr->CMD &= ~CS_PIP; - uptr->CMD |= CS_MOTION; + uptr->STATUS &= ~DS_PIP; 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; + regs[TUER1] |= ER1_FCE; tu_error(uptr, r); rh_finish_op(rhc, 0); } else { @@ -616,7 +673,9 @@ t_stat tu_srv(UNIT * uptr) return SCPE_OK; } if ((uint32)uptr->DATAPTR < uptr->hwmark) { - tu_frame[ctlr]++; + regs[TUDC]++; + if (regs[TUDC] == 0) + regs[TUTC] &= ~TC_FCS; cc = (8 * (3 - uptr->CPOS)) + 4; ch = tu_buf[ctlr][uptr->DATAPTR]; if (cc < 0) @@ -644,8 +703,8 @@ t_stat tu_srv(UNIT * uptr) dptr->name, unit, rhc->buf); rh_write(rhc); } - if (tu_frame[ctlr] != 0) - uptr->STATUS |= ER1_FCE; + if (regs[TUDC] != 0) + regs[TUER1] |= ER1_FCE; tu_error(uptr, MTSE_OK); (void)rh_blkend(rhc); rh_finish_op(rhc, 0); @@ -655,52 +714,49 @@ t_stat tu_srv(UNIT * uptr) case FNC_WRITE: if (BUF_EMPTY(uptr)) { - uptr->CMD &= ~CS_PIP; - if (tu_frame[ctlr] == 0) { - uptr->STATUS |= ER1_NEF; - uptr->CMD |= CS_ATA; + uptr->STATUS &= ~DS_PIP; + if (regs[TUDC] == 0) { + regs[TUER1] |= ER1_NEF; + uptr->STATUS |= DS_ATA; rhc->attn = 1; tu_error(uptr, MTSE_OK); rh_finish_op(rhc, 0); return SCPE_OK; } - if ((uptr->flags & MTUF_WRP) != 0) { + if ((uptr->flags & MTUF_WLK) != 0) { tu_error(uptr, MTSE_WRP); rh_finish_op(rhc, 0); return SCPE_OK; } - uptr->CMD |= CS_MOTION; sim_debug(DEBUG_EXP, dptr, "%s%o Init write\n", dptr->name, unit); uptr->hwmark = 0; uptr->CPOS = 0; uptr->DATAPTR = 0; rhc->buf = 0; } - if (tu_frame[ctlr] != 0 && uptr->CPOS == 0 && rh_read(rhc) == 0) + if (regs[TUDC] != 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, "%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 = rhc->buf & 0x0f; - else - ch = (rhc->buf >> cc) & 0xff; - tu_buf[ctlr][uptr->DATAPTR] = ch; - uptr->DATAPTR++; - uptr->hwmark = uptr->DATAPTR; - uptr->CPOS = (uptr->CPOS & 010) | ((uptr->CPOS & 07) + 1); - if ((uptr->CPOS & 7) == cc_max) { - uptr->CPOS &= 010; - } - tu_frame[ctlr] = 0177777 & (tu_frame[ctlr] + 1); - if (tu_frame[ctlr] == 0) { - uptr->CPOS = 010; - tu_tcr[ctlr] &= ~(TC_FCS); - } + if (uptr->CPOS == 0) + 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 = rhc->buf & 0x0f; + else + ch = (rhc->buf >> cc) & 0xff; + tu_buf[ctlr][uptr->DATAPTR] = ch; + uptr->DATAPTR++; + uptr->hwmark = uptr->DATAPTR; + uptr->CPOS = (uptr->CPOS & 010) | ((uptr->CPOS & 07) + 1); + if ((uptr->CPOS & 7) == cc_max) { + uptr->CPOS &= 010; + } + regs[TUDC]++; + if (regs[TUDC] == 0) { + uptr->CPOS = 010; + regs[TUTC] &= ~(TC_FCS); } if (uptr->CPOS == 010) { /* Write out the block */ @@ -718,9 +774,9 @@ t_stat tu_srv(UNIT * uptr) break; case FNC_WTM: - uptr->CMD &= ~CS_PIP; - uptr->CMD |= CS_ATA; - if ((uptr->flags & MTUF_WRP) != 0) { + uptr->STATUS &= ~DS_PIP; + uptr->STATUS |= DS_ATA; + if ((uptr->flags & MTUF_WLK) != 0) { tu_error(uptr, MTSE_WRP); } else { tu_error(uptr, sim_tape_wrtmk(uptr)); @@ -729,9 +785,10 @@ t_stat tu_srv(UNIT * uptr) return SCPE_OK; case FNC_ERASE: - uptr->CMD &= ~CS_PIP; - uptr->CMD |= CS_ATA; - if ((uptr->flags & MTUF_WRP) != 0) { + uptr->STATUS &= ~DS_PIP; + uptr->STATUS |= DS_ATA; + sim_tape_sprecf(uptr, &reclen); + if ((uptr->flags & MTUF_WLK) != 0) { tu_error(uptr, MTSE_WRP); } else { tu_error(uptr, sim_tape_wrgap(uptr, 35)); @@ -741,47 +798,42 @@ t_stat tu_srv(UNIT * uptr) case FNC_SPACEF: case FNC_SPACEB: - 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; + sim_debug(DEBUG_DETAIL, dptr, "%s%o space %06o %o\n", dptr->name, unit, regs[TUDC], GET_FNC(uptr->CMD)); + uptr->STATUS &= ~DS_PIP; /* 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); + regs[TUDC]++; + if (regs[TUDC] == 0) + regs[TUTC] &= ~TC_FCS; switch (r) { case MTSE_OK: /* no error */ break; case MTSE_BOT: /* beginning of tape */ - uptr->STATUS |= ER1_NEF; + regs[TUER1] |= ER1_NEF; /* Fall Through */ 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; + if (regs[TUDC] != 0) + regs[TUER1] |= ER1_FCE; + uptr->STATUS |= DS_ATA; /* Stop motion if we recieve any of these */ tu_error(uptr, r); + sim_debug(DEBUG_DETAIL, dptr, "%s%o space finish %06o %o\n", dptr->name, unit, regs[TUDC], GET_FNC(uptr->CMD)); return SCPE_OK; } - if (tu_frame[ctlr] == 0) { - uptr->CMD |= CS_ATA; + if (regs[TUDC] == 0) { + uptr->STATUS |= DS_ATA; tu_error(uptr, MTSE_OK); + sim_debug(DEBUG_DETAIL, dptr, "%s%o space finish1 %06o %o\n", dptr->name, unit, regs[TUDC], GET_FNC(uptr->CMD)); return SCPE_OK; } else { - tu_tcr[ctlr] &= ~(TC_FCS); sim_activate(uptr, reclen * 100); + sim_debug(DEBUG_DETAIL, dptr, "%s%o space cont %06o %o\n", dptr->name, unit, regs[TUDC], GET_FNC(uptr->CMD)); } return SCPE_OK; } @@ -795,11 +847,12 @@ t_stat tu_srv(UNIT * uptr) t_stat tu_reset(DEVICE * dptr) { - int ctlr; - for (ctlr = 0; ctlr < NUM_DEVS_TU; ctlr++) { - tu_rh[ctlr].attn = 0; - tu_rh[ctlr].rae = 0; - } + struct rh_if *rhc = &tu_rh[0]; + uint16 *regs = &rhc->regs[0]; + + rh_reset(dptr, &tu_rh[0]); + regs[TUER1] = 0; + regs[TUTC] = TC_1600; return SCPE_OK; } @@ -822,11 +875,14 @@ void tu_read_word(UNIT *uptr) { t_stat tu_boot(int32 unit_num, DEVICE * dptr) { - UNIT *uptr = &dptr->units[unit_num]; - t_mtrlnt reclen; - t_stat r; - uint32 addr; - int wc; + UNIT *uptr = &dptr->units[unit_num]; + int ctlr = GET_CNTRL_RH(uptr->flags); + struct rh_if *rhc = &tu_rh[ctlr]; + uint16 *regs = &rhc->regs[0]; + t_mtrlnt reclen; + t_stat r; + uint32 addr; + int wc; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ @@ -834,6 +890,36 @@ tu_boot(int32 unit_num, DEVICE * dptr) r = sim_tape_rewind(uptr); if (r != SCPE_OK) return r; + uptr->CMD = 0; +#if KS + /* Skip first file, which is micro code */ + while (r == MTSE_OK) + r = sim_tape_rdrecf(uptr, &tu_buf[0][0], &reclen, TU_NUMFR); + + if (r != MTSE_TMK) + return r; + /* Next read in the boot block */ + r = sim_tape_rdrecf(uptr, &tu_buf[0][0], &reclen, TU_NUMFR); + if (r != MTSE_OK) + return r; + uptr->DATAPTR = 0; + uptr->hwmark = reclen; + wc = reclen; + + addr = 01000; + while (uptr->DATAPTR < wc) { + tu_read_word(uptr); + M[addr] = tu_boot_buffer; + addr ++; + } + regs[TUTC] |= unit_num; + M[036] = rhc->dib->uba_addr | (rhc->dib->uba_ctl << 18); + M[037] = 0; + M[040] = regs[TUTC]; + PC = 01000; + rh_boot_dev = dptr; + rh_boot_unit = unit_num; +#else r = sim_tape_rdrecf(uptr, &tu_buf[0][0], &reclen, TU_NUMFR); if (r != SCPE_OK) return r; @@ -865,6 +951,8 @@ tu_boot(int32 unit_num, DEVICE * dptr) M[addr] = tu_boot_buffer; PC = tu_boot_buffer & RMASK; + regs[TUTC] |= unit_num; +#endif return SCPE_OK; } @@ -879,7 +967,7 @@ tu_attach(UNIT * uptr, CONST char *file) uptr->STATUS = 0; r = sim_tape_attach_ex(uptr, file, 0, 0); if (r == SCPE_OK && (sim_switches & SIM_SW_REST) == 0) { - uptr->CMD = CS_ATA|CS_CHANGE; + uptr->STATUS = DS_ATA|DS_SSC; rh_setattn(rhc, 0); } return r; @@ -891,9 +979,7 @@ tu_detach(UNIT * uptr) int ctlr = GET_CNTRL_RH(uptr->flags); struct rh_if *rhc = &tu_rh[ctlr]; - /* Find df10 */ - uptr->STATUS = 0; - uptr->CMD = CS_ATA|CS_CHANGE; + uptr->STATUS = DS_ATA|DS_SSC; rh_setattn(rhc, 0); return sim_tape_detach(uptr); } @@ -910,6 +996,17 @@ fprint_show_help (st, dptr); fprintf (st, "\nThe type options can be used only when a unit is not attached to a file.\n"); fprintf (st, "The TU device supports the BOOT command.\n"); sim_tape_attach_help (st, dptr, uptr, flag, cptr); +#if KS +fprintf (st, "The RH11 is a unibus device, various parameters can be changed on these devices\n"); +fprintf (st, "\n The address of the device can be set with: \n"); +fprintf (st, " sim> SET TUA ADDR=octal default address= 772440\n"); +fprintf (st, "\n The interrupt vector can be set with: \n"); +fprintf (st, " sim> SET TUA VECT=octal default 224\n"); +fprintf (st, "\n The interrupt level can be set with: \n"); +fprintf (st, " sim> SET TUA BR=# # should be between 4 and 7.\n"); +fprintf (st, "\n The unibus addaptor that the DZ is on can be set with:\n"); +fprintf (st, " sim> SET TUA CTL=# # can be either 1 or 3\n"); +#endif fprint_reg_help (st, dptr); return SCPE_OK; } diff --git a/PDP10/pdp6_dct.c b/PDP10/pdp6_dct.c index 787794bd..7005f3e4 100644 --- a/PDP10/pdp6_dct.c +++ b/PDP10/pdp6_dct.c @@ -118,7 +118,7 @@ dct_devio(uint32 dev, uint64 *data) { clr_interrupt(dev); if (uptr->STATUS & DB_RQ) { *data = dct_buf[u]; - uptr->STATUS &= ~(DB_RQ); + uptr->STATUS &= ~(NUM_CHARS|DB_RQ); uptr->STATUS |= DB_MV; sim_activate(uptr, 10); } @@ -132,7 +132,7 @@ dct_devio(uint32 dev, uint64 *data) { dev, *data, u, PC); if (uptr->STATUS & DB_RQ) { dct_buf[u] = *data; - uptr->STATUS &= ~(DB_RQ); + uptr->STATUS &= ~(NUM_CHARS|DB_RQ); uptr->STATUS |= DB_MV; sim_activate(uptr, 10); } diff --git a/doc/ka10_doc.doc b/doc/ka10_doc.doc index 8110df02..29f40edd 100644 Binary files a/doc/ka10_doc.doc and b/doc/ka10_doc.doc differ diff --git a/doc/ki10_doc.doc b/doc/ki10_doc.doc index d202f9ea..6260e294 100644 Binary files a/doc/ki10_doc.doc and b/doc/ki10_doc.doc differ diff --git a/doc/kl10_doc.doc b/doc/kl10_doc.doc index 9fe1eb69..f59bde71 100644 Binary files a/doc/kl10_doc.doc and b/doc/kl10_doc.doc differ diff --git a/doc/ks10_doc.doc b/doc/ks10_doc.doc new file mode 100644 index 00000000..d66961f3 Binary files /dev/null and b/doc/ks10_doc.doc differ diff --git a/doc/pdp6_doc.doc b/doc/pdp6_doc.doc index e5f6c2dc..be6bc75e 100644 Binary files a/doc/pdp6_doc.doc and b/doc/pdp6_doc.doc differ