From 424682447c153e777c589cceb1decbf9962d10c6 Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Thu, 4 Jul 2019 07:55:02 +0200 Subject: [PATCH 1/2] KA10: Systems Concepts DC-10 disk controller. --- PDP10/ka10_ai.c | 1036 +++++++++++++++++++++++++++++++++++++++++++++ PDP10/kx10_defs.h | 2 + PDP10/kx10_sys.c | 3 + 3 files changed, 1041 insertions(+) create mode 100644 PDP10/ka10_ai.c diff --git a/PDP10/ka10_ai.c b/PDP10/ka10_ai.c new file mode 100644 index 0000000..4941a5b --- /dev/null +++ b/PDP10/ka10_ai.c @@ -0,0 +1,1036 @@ +/* ka10_ai.c: Systems Concepts DC-10 disk control + + Copyright (c) 2019, Lars Brinkhoff + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + LARS BRINKHOFF BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + This disk controller was probably only ever used with the MIT AI + Lab PDP-10. Since the device name DC is alreay claimed, we call + this AI. +*/ + +#include "kx10_defs.h" + +#ifndef NUM_DEVS_AI +#define NUM_DEVS_AI 0 +#endif + + +#if (NUM_DEVS_AI > 0) + +/* Disk pack geometry. The track format is software defined. ITS and + SALV makes it hold two sectors with 1024 words of regular data and + 4 extra words. */ +#define SECTOR_SIZE 1024 +#define SECTORS 2 +#define SURFACES 20 +#define MEMOREX_CYLINDERS 203 +#define CALCOMP_CYLINDERS (2 * MEMOREX_CYLINDERS) +#define CYLINDER_SIZE (SECTOR_SIZE * SECTORS * SURFACES) +#define MEMOREX_SIZE (CYLINDER_SIZE * MEMOREX_CYLINDERS) +#define CALCOMP_SIZE (CYLINDER_SIZE * CALCOMP_CYLINDERS) + +/* The real sector size, including 2 header words, 4 extra data words, + and 2 checksum words. */ +#define SECTOR_REAL_SIZE (SECTOR_SIZE + 8) +/* A track actually has some more free space. Cylinder 0, surface 0 + has a readin block there. */ +#define TRACK_REAL_SIZE ((SECTORS + 1) * SECTOR_REAL_SIZE) +#define CYLINDER_REAL_SIZE (SURFACES * TRACK_REAL_SIZE) + +#define AI_DEVNUM 0610 /* First device number; 614 also used. */ +#define AI_NAME "AI" +#define NUM_UNITS 16 /* Hardware units, but ITS only supports 8. */ + +/* All bit definitions are from the ITS file SYSTEM; DC10 DEFS27. */ + +/* CONI DC0 */ +#define DASSGN 0400000000000LL /* ASSIGNED TO PROC (WITH SWITCH) */ +#define DPIRQC 0400000 /* PI REQ BEING GENERATED */ +#define DSSRQ 0200000 /* SEEK REQUEST */ +#define DSDEEB 0010000 /* ENABLE INTERRUPT ON DATA ERROR OR READ/ COMP ERROR */ +#define DSSERR 0004000 /* ERROR FLAG */ +#define DSSAEB 0002000 /* ATTENTION ENABLE FLAG */ +#define DSSATT 0001000 /* ATTENTION FLAG */ +#define DSIENB 0000400 /* IDLE FLAG ENABLE */ +#define DSSRUN 0000200 /* RUN */ +#define DSSACT 0000100 /* ACTIVE */ +#define DSSCEB 0000040 /* CHANNEL ENABLE */ +#define DSSCHF 0000020 /* CHANNEL FLAG */ +#define DSSCFL 0000010 /* CPU FLAG */ + +/* CONO DC0 */ +#define DCSET 0400000 /* SET SELECTED */ +#define DCCLR 0200000 /* CLEAR SELECTED */ +#define DCCSET 0600000 /* RESET CONTROLLER THEN SET SELECTED */ +#define DCDENB 0010000 /* DATA ERROR ENABLE */ +#define DCERR 0004000 /* SET ERROR FLAG OR CLEAR ALL ERRORS */ +#define DCATEB 0002000 /* ATTENTION ENABLE */ +#define DCCATT 0001000 /* CLEAR ATTENTION */ +#define DCSSRQ 0001000 /* SET SEEK REQUEST */ +#define DCIENB 0000400 /* IDLE ENABLE */ +#define DCSTAR 0000200 /* START (SET) */ +#define DCSSTP 0000200 /* STOP (CLEAR) */ +#define DCSGL 0000100 /* DO SINGLE COMMAND */ +#define DCCENB 0000040 /* CHANNEL ENABLE */ +#define DCCFLG 0000020 /* CHANNEL FLAG */ +#define DCCPUF 0000010 /* CPU FLAG */ + +/* Bits to set or clear with DCSET or DCCLR. */ +#define SET_MASK (DCDENB|DCERR|DCATEB|DCIENB|DCSTAR) +#define CLEAR_MASK (DCDENB|DCERR|DCATEB|DCCATT|DCIENB|DCSSTP) + +/* CONI DC1 */ +#define DIPE 04000 /* INTERNAL PARITY ERROR */ +#define DRLNER 02000 /* RECORD LENGTH */ +#define DRCER 01000 /* READ COMPARE ERROR */ +#define DOVRRN 00400 /* OVERRUN */ +#define DCKSER 00200 /* CKSUM OR DECODER ERR */ +#define DWTHER 00100 /* WATCHDOG TIMER */ +#define DFUNSF 00040 /* FILE UNSAFE, SEEK INCOMPLETE OR END OR DSK */ +#define DOFFL 00020 /* OFF LINE OR MULT SEL */ +#define DPROT 00010 /* WRT KEY OR RD ONLY OR PROTECT */ +#define DDOBSY 00004 /* DATAO WHEN BSY */ +#define DNXM 00002 /* NON-EX MEM */ +#define DCPERR 00001 /* CORE PARITY ERR */ + +/* Channel commands */ +#define DUNENB 0020000000000LL /* ENABLE LOAD UNIT FIELD */ +#define DCMD 0740000000000LL +#define DCOPY 0040000000000LL /* COPY */ +#define DCCOMP 0100000000000LL /* COMPARE */ +#define DCSKIP 0140000000000LL /* SKIP */ +#define DOPR 0200000000000LL /* BASIC OPR */ +#define DSDRST 0240000000000LL /* STORE DRIVE STATUS */ +#define DALU 0300000000000LL /* BASIC ALU OP CODE */ +#define DRC 0400000000000LL /* READ COMPARE */ +#define DWRITE 0440000000000LL /* WRITE */ +#define DREAD 0500000000000LL /* READ */ +#define DSEEK 0540000000000LL /* SEEK */ +#define DRCC 0600000000000LL /* READ COMPARE CONTINUOUS */ +#define DWRITC 0640000000000LL /* WRITE CONTINUOUS */ +#define DREADC 0700000000000LL /* READ CONTINUOUS */ +#define DSPC 0740000000000LL /* Special command. */ + +#define DHLT 0 /* 0 IN 4.9-4.5 = JUMP AND IN 3.5,3.6 = HALT */ +#define DXCT 0000020000000LL /* XCT */ +#define DJMP 0000040000000LL /* JUMP */ +#define DJSR 0000060000000LL /* JSR */ +#define DJMASK 0000060000000LL + +/* OPR */ +#define DOHXFR 0400000000LL /* HALT DURING XFER (SO MB WILL BE SAFE) */ + +/* Special command, E condition (wait). */ +#define DSWIDX 0020000000LL /* WAIT UNTIL INDEX PULSE */ +#define DSWSEC 0040000000LL /* WAIT UNTIL SECTOR PULSE */ +#define DSWINF 0060000000LL /* NEVER (USE WITH G=3 OR 7) */ +/* Special command, F condition (other wait). */ +#define DSWNUL 0014000000LL /* NO WAIT */ +/* Special command, G operation. */ +#define DSCRHD 0200000000LL /* READ HEADER WORDS */ +#define DSRCAL 0300000000LL /* (RECALIBRATE) */ +#define DSCWIM 0500000000LL /* WRITE IMAGE */ + +/* ALU */ +#define DLCC 010000000LL /* OP FROM CC, STORE IN CC */ +#define DLDBWC 030000000LL /* OP A FROM DB, STORE IN WC */ + +#define WC 0037774000000LL /* Word count. */ +#define ADDR 0000003777777LL /* Address field. */ + +/* Drive status. */ + +#define DDSWC 040000000LL /* WRITE CURRENT SENSED */ +#define DDSUNS 020000000LL /* DRIVE UNSAFE */ +#define DDSRDO 010000000LL /* READ ONLY */ +#define DDSSIC 004000000LL /* SEEK INCOMPLETE */ +#define DDSRDY 002000000LL /* DRIVE READY */ +#define DDSONL 001000000LL /* DRIVE ON LINE */ +#define DDSSEL 000400000LL /* DRIVE SELECTED */ + +enum { + MODE_ERROR = 0, + MODE_WRITE, /* Write sector data. */ + MODE_READ, /* Read sector data. */ + MODE_READ_HEADERS, /* Read sector headers. */ + MODE_COMPARE, /* Compare sector data. */ + MODE_IMAGE /* Write raw image. */ +}; + +enum image_state { + IMAGE_GAP, /* Empty bits (ones) between sectors. */ + IMAGE_PREAMBLE, /* Bit pattern before sector header. */ + IMAGE_HEADER, /* Sector header, in FM encoding. */ + IMAGE_POSTAMBLE, /* Empty bits (ones). */ + IMAGE_POSTAMBLE2, /* A "01" to start the sector data. */ + IMAGE_SECTOR, /* Sector data, in FM encoding. */ + IMAGE_ERROR, +}; + +static enum image_state image_state = IMAGE_ERROR; +static int image_count, image_sector_length; + +static t_stat ai_devio(uint32 dev, uint64 *data); +static t_stat ai_svc(UNIT *); +static t_stat ai_reset(DEVICE *); +static t_stat ai_attach(UNIT *, CONST char *); +static t_stat ai_detach(UNIT *); +static t_stat ai_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, + const char *cptr); +static const char *ai_description (DEVICE *dptr); + + +UNIT ai_unit[] = { + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, + { UDATA (&ai_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CALCOMP_SIZE) }, +}; + +DIB ai_dib = { AI_DEVNUM, 2, &ai_devio, NULL }; + +MTAB ai_mod[] = { + {0} +}; + +DEBTAB ai_debug[] = { + {"IRQ", DEBUG_IRQ, "Debug IRQ requests"}, + {"CMD", DEBUG_CMD, "Show command execution to devices"}, + {"DATA", DEBUG_DATA, "Show data transfers"}, + {"DETAIL", DEBUG_DETAIL, "Show details about device"}, + {"EXP", DEBUG_EXP, "Show exception information"}, + {"CONI", DEBUG_CONI, "Show CONI instructions"}, + {"CONO", DEBUG_CONO, "Show CONO instructions"}, + {"DATAIO", DEBUG_DATAIO, "Show DATAI and DATAO instructions"}, + {0, 0} +}; + +static UNIT *channel_unit = ai_unit; +static int latency_unit = 0; +static int channel_pc = 0; +static int channel_status = 0; +static int channel_errors = 0; +static int channel_cc = 0; +static int channel_wc = 0; +static int channel_mode = MODE_ERROR; +static int channel_delay; +static int channel_default_delay = 1000; +static int channel_seek_initial = 25000; /* Milliseconds. */ +static int channel_seek_delay = 500; /* Per cylinder travelled. */ +static int channel_cylinder = 0; + +REG ai_reg[] = { + {ORDATA(PC, channel_pc, 20)}, + {ORDATA(STS, channel_status, 18)}, + {ORDATA(ERR, channel_errors, 12)}, + {ORDATA(CC, channel_cc, 20)}, + {ORDATA(WC, channel_wc, 12)}, + {ORDATA(SI, channel_seek_initial, 32)}, + {ORDATA(SD, channel_seek_delay, 32)}, + {ORDATA(CYL, channel_cylinder, 9)}, + {0} +}; + +DEVICE ai_dev = { + AI_NAME, ai_unit, ai_reg, ai_mod, + NUM_UNITS, 8, 18, 1, 8, 36, + NULL, NULL, &ai_reset, NULL, &ai_attach, &ai_detach, + &ai_dib, DEV_DISABLE | DEV_DEBUG, 0, ai_debug, + NULL, NULL, &ai_help, NULL, NULL, &ai_description +}; + +static void clear_interrupt (void) +{ + if ((channel_status & (DSDEEB|DSSERR)) == (DSDEEB|DSSERR) + || (channel_status & (DSSAEB|DSSATT)) == (DSSAEB|DSSATT) + || (channel_status & (DSIENB|DSSRUN)) == DSIENB) { + channel_status |= DPIRQC; + sim_debug(DEBUG_IRQ, &ai_dev, "Set interrupt: %06o\n", channel_status); + set_interrupt (AI_DEVNUM, channel_status); + } else { + channel_status &= ~DPIRQC; + sim_debug(DEBUG_IRQ, &ai_dev, "Clear interrupt\n"); + clr_interrupt (AI_DEVNUM); + } +} + +static void channel_error (int errors) +{ + channel_errors |= errors; + channel_status |= DSSERR; + if (channel_status & DSDEEB) { + channel_status |= DPIRQC; + sim_debug(DEBUG_IRQ, &ai_dev, "Set error interrupt\n"); + set_interrupt (AI_DEVNUM, channel_status); + } +} + +static void channel_seek (const char *cmd, uint64 data, int offset) +{ + int cyl, sur, sec, x; + uint64 da; + + if (data & DUNENB) + channel_unit = &ai_unit[(data >> 033) & 017]; + + cyl = (data >> 11) & 0777; + sur = (data >> 6) & 037; + sec = data & 077; + + if (cyl >= CALCOMP_CYLINDERS && sur >= SURFACES && sec >= SECTORS) { + sim_debug(DEBUG_EXP, &ai_dev, "Seek outside geometry\n"); + channel_error (DOVRRN); + return; + } + + da = SECTOR_REAL_SIZE * sec; + da += TRACK_REAL_SIZE * sur; + da += CYLINDER_REAL_SIZE * cyl; + da += offset; + if (channel_unit->flags & UNIT_ATT) { + (void)sim_fseek(channel_unit->fileref, da * sizeof(uint64), SEEK_SET); + x = channel_cylinder - cyl; + if (x < 0) + x = -x; + if (x > 0) + channel_delay = channel_seek_initial + x * channel_seek_delay; + channel_cylinder = cyl; + sim_debug(DEBUG_CMD, &ai_dev, "%s: unit %d seek %d (%d,%d,%d)\n", + cmd, (int)(channel_unit - ai_unit), channel_delay, + cyl, sur, sec); + } else { + sim_debug(DEBUG_EXP, &ai_dev, "Drive offline\n"); + channel_error (DOFFL); + } +} + +static void channel_special (uint64 data) +{ + if (data & DUNENB) + channel_unit = &ai_unit[(data >> 033) & 017]; + + switch (data & 0700000000LL) { + case DSCRHD: + channel_mode = MODE_READ_HEADERS; + channel_seek ("READ HEADER WORDS", data, 0); + break; + case DSRCAL: + sim_debug(DEBUG_CMD, &ai_dev, "Command: (RECALIBRATE)\n"); + channel_status |= DSSATT; + channel_errors &= ~(017 << 036); + channel_errors |= (channel_unit - ai_unit) << 036; + if (channel_status & DSSAEB) { + channel_status |= DPIRQC; + sim_debug(DEBUG_IRQ, &ai_dev, "Set attention interrupt\n"); + set_interrupt (AI_DEVNUM, channel_status); + } + break; + case DSCWIM: + if ((channel_unit->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive offline\n"); + channel_error (DOFFL); + } else if (channel_unit->flags & UNIT_RO) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive read only\n"); + channel_error (DPROT); + } else { + channel_mode = MODE_IMAGE; + image_state = IMAGE_GAP; + image_count = 0; + channel_seek ("WRITE IMAGE", data, 0); + } + break; + default: + sim_debug(DEBUG_CMD, &ai_dev, "(unknown special: %012llo)\n", data); + break; + } +} + +static void channel_alu (uint64 data) +{ + switch (data & 034000000LL) { + case DLCC: + channel_cc = data & ADDR; + sim_debug(DEBUG_CMD, &ai_dev, "ALU: OP FROM CC, STORE IN CC: %o\n", channel_cc); + break; + case DLDBWC: + channel_wc = data & 07777; + sim_debug(DEBUG_CMD, &ai_dev, "ALU: OP A FROM DB, STORE IN WC: %o\n", channel_wc); + break; + default: + sim_debug(DEBUG_CMD, &ai_dev, "ALU: (unkownn)\n"); + break; + } +} + +static void print_data (uint64 *data, int n) +{ + int i; + for (i = 0; i < n; i++) + sim_debug(DEBUG_DATA, &ai_dev, "Data %012llo\n", + *data++); +} + +static t_stat sim_fcompare (void *x, size_t m, size_t n, FILE *f) +{ + static uint64 buf[10240]; + + if ((channel_unit->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive offline\n"); + channel_error (DOFFL); + return SCPE_OK; + } + + (void)sim_fread (buf, m, n, f); + sim_debug(DEBUG_DATA, &ai_dev, "Memory contents:\n"); + print_data ((uint64 *)x, n); + sim_debug(DEBUG_DATA, &ai_dev, "Disk contents:\n"); + print_data (buf, n); + if (memcmp (x, buf, m * n) != 0) { + sim_debug(DEBUG_EXP, &ai_dev, "Compare failed.\n"); + channel_error (DRCER); + } + return SCPE_OK; +} + +/* The WRITE IMAGE command writes the sector headers as 56 continuous + bits. However, the READ HEADERS command presents them as 28-bit + halves, each right aligned in a 36-bit word. The image file stores + the first format, so here we need to split the words apart. Also + skip over the sector data to get to next header. */ +static t_stat sim_freadh (uint64 *x, size_t n, FILE *f) +{ + uint64 buf[2]; + int i; + + if ((channel_unit->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive offline\n"); + channel_error (DOFFL); + return SCPE_OK; + } + + for (i = 0; i < n; i++) { + if ((i & 1) == 0) { + (void)sim_fread (buf, sizeof (uint64), 2, f); + (void)sim_fseek(f, (SECTOR_REAL_SIZE-2) * sizeof(uint64), SEEK_CUR); + x[i] = buf[0] >> 8; + } else { + x[i] = (buf[0] & 0377) << 20; + x[i] |= buf[1] >> 16; + } + } + + return SCPE_OK; +} + +/* The track data fields are in somthing close to FM encoding. Here + we decode three bits at a time to yeild two bits of data. When 36 + data bits have been decoded, output a word to the image file. */ +static void decode_fm (int bit, FILE *f) +{ + static int state = 0; + static uint64 word = 0; + static int n = 0; + static int bits = 1; + + bits = (bits << 1) + bit; + state++; + + if (state != 3) + return; + + word <<= 2; + + switch (bits & 017) { + case 005: + case 007: + word |= (bits >> 4) & 2; + word |= (bits >> 1) & 1; + break; + case 012: + case 016: + break; + case 013: + case 015: + case 017: + word |= (bits >> 1) & 3; + break; + default: + sim_debug(DEBUG_EXP, &ai_dev, "Error in FM encoding: %o\n", bits); + channel_error (DCKSER); + break; + } + + state = 0; + n += 2; + + //sim_debug(DEBUG_DETAIL, &ai_dev, "FM: %o, %d, %012llo\n", + // bits, n, word); + + if (n == 36) { + //sim_debug(DEBUG_DETAIL, &ai_dev, "Data: %012llo\n", word); + (void)sim_fwrite (&word, sizeof word, 1, f); + n = 0; + word = 0; + } +} + +/* Decode a bit stream from the WRITE IMAGE command. */ +static void decode_bit (int bit, FILE *f) +{ + static const int preamble_bits[] = { 1, 0, 1, 0, 1 }; + + //sim_debug(DEBUG_DETAIL, &ai_dev, "Image: bit %o\n", bit); + + switch (image_state) { + case IMAGE_GAP: + if (bit == 0) { + sim_debug(DEBUG_DETAIL, &ai_dev, "Image: %d gap bits\n", + image_count); + image_state = IMAGE_PREAMBLE; + image_count = 0; + } else { + image_count++; + } + break; + case IMAGE_PREAMBLE: + if (bit != preamble_bits[image_count % 5]) { + sim_debug(DEBUG_DETAIL, &ai_dev, "Image: error in preamble bit %d\n", + image_count); + image_state = IMAGE_ERROR; + break; + } + image_count++; + if (image_count == 5*8) { + sim_debug(DEBUG_DETAIL, &ai_dev, "Image: preamble ok\n"); + image_state = IMAGE_HEADER; + image_count = 0; + } + break; + case IMAGE_HEADER: + decode_fm (bit, f); + image_count++; + if (image_count == 108) { + long pos; + uint64 header[2]; + image_state = IMAGE_POSTAMBLE; + image_count = 0; + pos = sim_ftell (channel_unit->fileref); + (void)sim_fseek(channel_unit->fileref, pos - 2 * sizeof(uint64), SEEK_SET); + (void)sim_fread(header, sizeof(uint64), 2, channel_unit->fileref); + (void)sim_fseek(channel_unit->fileref, pos, SEEK_SET); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: key %03llo\n", + (header[0] >> 28) & 0377); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: cylinder %lld\n", + (header[0] >> 19) & 0777); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: surface %lld\n", + (header[0] >> 14) & 037); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: sector %lld\n", + (header[0] >> 8) & 077); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: indirect %llo\n", + (header[0] >> 7) & 1); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: software protect %llo\n", + (header[0] >> 6) & 1); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: hardware protect %llo\n", + (header[0] >> 5) & 1); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: parity %llo\n", + header[0] & 3); + image_sector_length = 040000 - ((header[1] >> 16) & 037777); + sim_debug(DEBUG_DETAIL, &ai_dev, "Header: length %o\n", + image_sector_length); + if (image_sector_length > 02004) { + sim_debug(DEBUG_EXP, &ai_dev, "Record length error\n"); + channel_error (DRLNER); + image_state = IMAGE_SECTOR; + } + image_sector_length += 2; /* Checksum */ + image_sector_length *= 54; /* 36-bit words, FM coded. */ + } + break; + case IMAGE_POSTAMBLE: + image_count++; + if (bit == 0) { + sim_debug(DEBUG_DETAIL, &ai_dev, "Image: %d gap bits\n", + image_count); + image_state = IMAGE_POSTAMBLE2; + image_count = 0; + } + break; + case IMAGE_POSTAMBLE2: + if (bit == 0) { + sim_debug(DEBUG_DETAIL, &ai_dev, "Image: error in postamble\n"); + image_state = IMAGE_ERROR; + } else { + image_state = IMAGE_SECTOR; + } + break; + case IMAGE_SECTOR: + decode_fm (bit, f); + image_count++; + if (image_count == image_sector_length) { + image_state = IMAGE_GAP; + image_count = 0; + } + break; + case IMAGE_ERROR: + break; + } +} + +static void decode_image (uint64 *data, int n, FILE *f) +{ + int i, j; + for (i = 0; i < n; i++) { + for (j = 35; j >= 0; j--) + decode_bit ((int)(*data >> j) & 1, f); + data++; + } +} + +static int check_nxm (uint64 data, int *n, uint64 *data2, int *n2) +{ + int addr = data & ADDR; + *data2 = 0; + *n2 = 0; + if (addr + *n > MEMSIZE) { + if (MEMSIZE < (ADDR+1)) { + sim_debug(DEBUG_EXP, &ai_dev, "Access outside core memory\n"); + *n = MEMSIZE - addr; + channel_error (DNXM); + return 1; + } else { + /* Access wraps around 21-bit address. */ + *n2 = addr + *n - MEMSIZE; + *data2 = 0; + *n = MEMSIZE - addr; + return 0; + } + } + return 0; +} + +/* Execute one channel instruction. It may come from a channel + program in core, or from a DATAO DC0, */ +static void channel_command (uint64 data) +{ + struct timespec ts; + int latency_timer; + int n, n2; + uint64 data2; + int nxm = 0; + + if ((data & (DCMD|DUNENB)) == 0) { + switch (data & DJMASK) { + case DHLT: + sim_debug(DEBUG_CMD, &ai_dev, "Command: DHLT\n"); + channel_status &= ~(DSSRUN|DSSACT); + if (channel_status & DSIENB) { + channel_status |= DPIRQC; + sim_debug(DEBUG_IRQ, &ai_dev, "Set idle interrupt\n"); + set_interrupt (AI_DEVNUM, channel_status); + } + break; + case DXCT: + sim_debug(DEBUG_CMD, &ai_dev, "Command: XCT\n"); + break; + case DJMP: + channel_status |= DSSRUN|DSSACT; + sim_activate(ai_unit, channel_default_delay); + clear_interrupt (); + if ((data & 014000000LL) == 004000000LL) { + sim_debug(DEBUG_CMD, &ai_dev, "Command: JUMP DAOJNC: %o\n", + channel_cc); + channel_cc++; + if (channel_cc != ADDR+1) + channel_pc = data & ADDR; + } else { + sim_debug(DEBUG_CMD, &ai_dev, "Command: JUMP\n"); + channel_pc = data & ADDR; + } + break; + case DJSR: + sim_debug(DEBUG_CMD, &ai_dev, "Command: JSR\n"); + n = 1; + if (check_nxm (data, &n, &data2, &n2)) + break; + M[data & ADDR] = channel_pc + (channel_unit - ai_unit) << 036; + channel_pc++; + channel_status |= DSSRUN|DSSACT; + sim_activate(ai_unit, channel_default_delay); + break; + } + return; + } + + switch (data & DCMD) { + case DCOPY: + n = (data & WC) >> 20; + if (n == 0) + n = channel_wc; + n = 010000 - n; + sim_debug(DEBUG_CMD, &ai_dev, "COPY %d words to/from %012llo.\n", + n, data & ADDR); + if ((channel_unit->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive offline\n"); + channel_error (DOFFL); + break; + } + nxm = check_nxm (data, &n, &data2, &n2); + switch (channel_mode) { + case MODE_READ: + (void)sim_fread (&M[data & ADDR], sizeof(uint64), n, + channel_unit->fileref); + if (nxm) + break; + (void)sim_fread (&M[data2], sizeof(uint64), n2, + channel_unit->fileref); + print_data (&M[data & ADDR], n); + break; + case MODE_READ_HEADERS: + (void)sim_freadh (&M[data & ADDR], n, channel_unit->fileref); + if (nxm) + break; + (void)sim_freadh (&M[data2], n2, channel_unit->fileref); + break; + case MODE_WRITE: + if (channel_unit->flags & UNIT_RO) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive read only\n"); + channel_error (DPROT); + } else { + (void)sim_fwrite (&M[data & ADDR], sizeof(uint64), n, + channel_unit->fileref); + if (nxm) + break; + (void)sim_fwrite (&M[data2], sizeof(uint64), n2, + channel_unit->fileref); + } + break; + case MODE_COMPARE: + (void)sim_fcompare (&M[data & ADDR], sizeof(uint64), n, + channel_unit->fileref); + if (nxm) + break; + (void)sim_fcompare (&M[data2], sizeof(uint64), n2, + channel_unit->fileref); + /* If at the end of the sector, skip to next sector. */ + if ((sim_ftell (channel_unit->fileref) / sizeof(uint64)) + % SECTOR_REAL_SIZE == 1030) + (void)sim_fseek(channel_unit->fileref, 4 * sizeof(uint64), SEEK_CUR); + break; + case MODE_IMAGE: + decode_image (&M[data & ADDR], n, channel_unit->fileref); + break; + default: + break; + } + break; + case DCCOMP: + n = (data & WC) >> 20; + if (n == 0) + n = channel_wc; + n = 010000 - n; + sim_debug(DEBUG_CMD, &ai_dev, "COMPARE %d words\n", n); + if ((channel_unit->flags & UNIT_ATT) == 0) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive offline\n"); + channel_error (DOFFL); + break; + } + nxm = check_nxm (data, &n, &data2, &n2); + (void)sim_fcompare (&M[data & ADDR], sizeof(uint64), n, + channel_unit->fileref); + if (nxm) + break; + (void)sim_fcompare (&M[data2], sizeof(uint64), n2, + channel_unit->fileref); + break; + case DCSKIP: + n = 010000 - ((data & WC) >> 20); + sim_debug(DEBUG_CMD, &ai_dev, "SKIP %o words\n", n); + (void)sim_fseek(channel_unit->fileref, n * sizeof(uint64), SEEK_CUR); + break; + case DOPR: + if (data & DOHXFR) + sim_debug(DEBUG_CMD, &ai_dev, "OPR: Hang during xfer\n"); + else + sim_debug(DEBUG_CMD, &ai_dev, "OPR ...\n"); + break; + case DSDRST: + if (data & DUNENB) + channel_unit = &ai_unit[(data >> 033) & 017]; + + sim_debug(DEBUG_CMD, &ai_dev, + "DSDRST, store unit %d status in %012llo.\n", + (int)(channel_unit - ai_unit), data & ADDR); + + n = 1; + if (check_nxm (data, &n, &data2, &n2)) + break; + + (void)clock_gettime(CLOCK_REALTIME, &ts); + latency_timer = ts.tv_nsec / 100000; + latency_timer %= 254; + M[data & ADDR] = latency_timer & 0377; + M[data & ADDR] |= channel_cylinder << 8; + if (channel_unit->flags & UNIT_ATT) + /* Drive online. */ + M[data & ADDR] |= DDSONL; + if (channel_unit->flags & UNIT_RO) + /* Drive read-only. */ + M[data & ADDR] |= DDSRDO; + break; + case DALU: + channel_alu (data); + break; + case DRC: + channel_mode = MODE_COMPARE; + channel_seek ("READ COMPARE", data, 2); + break; + case DWRITE: + if (channel_unit->flags & UNIT_RO) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive read only\n"); + channel_error (DPROT); + channel_mode = MODE_ERROR; + } else { + channel_mode = MODE_WRITE; + channel_seek ("WRITE", data, 2); + } + break; + case DREAD: + channel_mode = MODE_READ; + channel_seek ("READ", data, 2); + break; + case DSEEK: + channel_seek ("SEEK", data, 2); + break; + case DRCC: + channel_mode = MODE_COMPARE; + channel_seek ("READ COMPARE CONTINUOUS", data, 2); + break; + case DWRITC: + if (channel_unit->flags & UNIT_RO) { + sim_debug(DEBUG_EXP, &ai_dev, "Drive read only\n"); + channel_error (DPROT); + channel_mode = MODE_ERROR; + } else { + channel_mode = MODE_WRITE; + channel_seek ("WRITE CONTINUOUS", data, 2); + } + break; + case DREADC: + channel_mode = MODE_READ; + channel_seek ("READ CONTINUOUS", data, 2); + break; + case DSPC: + channel_special (data); + break; + default: + sim_debug(DEBUG_CMD, &ai_dev, "(unknown command: %012llo)\n", data); + break; + } +} + +/* Process one channel instruction and update the channel state. */ +static void channel_run (void) +{ + uint64 data, data2; + int n = 1, n2; + if (check_nxm (channel_pc, &n, &data2, &n2)) + return; + data = M[channel_pc]; + //sim_debug(DEBUG_CMD, &ai_dev, "Channel PC=%06o %012llo\n", + // channel_pc, data); + channel_pc++; + channel_command (data); +} + +t_stat ai_devio(uint32 dev, uint64 *data) { + struct timespec ts; + int latency_timer; + + switch(dev & 7) { + case CONI: + *data = channel_status; + sim_debug(DEBUG_CONI, &ai_dev, "DC0, PC=%06o %012llo\n", PC, *data); + return SCPE_OK; + + case CONO: + sim_debug(DEBUG_CONO, &ai_dev, "DC0, PC=%06o %012llo\n", PC, *data); + if ((*data & DCCSET) == DCCSET) { + sim_debug(DEBUG_CMD, &ai_dev, "Reset controller then set selected.\n"); + ai_reset (&ai_dev); + } + channel_status &= ~7; + channel_status |= *data & 7; + if (*data & DCSET) { + channel_status |= *data & SET_MASK; + if (*data & DCSSRQ) + channel_status |= DSSRQ; + sim_debug(DEBUG_CMD, &ai_dev, "Set bits: %012llo -> %06o\n", + *data & SET_MASK, channel_status); + } else if (*data & DCCLR) { + channel_status &= ~(*data & CLEAR_MASK); + sim_debug(DEBUG_CMD, &ai_dev, "Clear bits: %012llo -> %06o\n", + *data & CLEAR_MASK, channel_status); + if (*data & DCERR) + channel_errors = 0; + } + clear_interrupt (); + return SCPE_OK; + + case DATAI: + *data = 0; + sim_debug(DEBUG_DATAIO, &ai_dev, "DATAI DC0, PC=%06o %012llo\n", + PC, *data); + return SCPE_OK; + + case DATAO: + sim_debug(DEBUG_DATAIO, &ai_dev, "DATAO DC0, PC=%06o %012llo\n", + PC, *data); + if (channel_status & (DSSRUN|DSSACT)) { + sim_debug(DEBUG_EXP, &ai_dev, "DATAO when busy\n"); + channel_error (DDOBSY); + } else + channel_command (*data); + return SCPE_OK; + + case CONI|4: + /* Latency timer, timer unit, attention unit. */ + (void)clock_gettime(CLOCK_REALTIME, &ts); + latency_timer = ts.tv_nsec / 100000; + latency_timer %= 254; + *data = (latency_timer << 022) + | (latency_unit << 032) | channel_errors; + sim_debug(DEBUG_CONI, &ai_dev, "DC1, PC=%06o %012llo\n", PC, *data); + return SCPE_OK; + + case CONO|4: + sim_debug(DEBUG_CONO, &ai_dev, "DC1, PC=%06o %012llo\n", PC, *data); + sim_debug(DEBUG_CMD, &ai_dev, "Latency timer set to unit %llo\n", *data); + latency_unit = *data & 7; + return SCPE_OK; + + case DATAI|4: + *data = 0; + sim_debug(DEBUG_DATAIO, &ai_dev, "DATAI DC1, PC=%06o %012llo\n", + PC, *data); + return SCPE_OK; + + case DATAO|4: + sim_debug(DEBUG_DATAIO, &ai_dev, "DATAO DC1, PC=%06o %012llo\n", + PC, *data); + return SCPE_OK; + } + return SCPE_OK; /* Unreached */ +} + +t_stat ai_svc (UNIT *uptr) +{ + int i; + channel_delay = channel_default_delay; + for (i = 0; (channel_status & DSSRUN) && i < 10; i++) + channel_run (); + if (channel_status & DSSRUN) + sim_activate(uptr, channel_delay); + return SCPE_OK; +} + +t_stat +ai_reset(DEVICE *dptr) +{ + channel_status = 0; + channel_errors = 0; + channel_pc = 0; + channel_cc = 0; + channel_wc = 0; + channel_mode = 0; + return SCPE_OK; +} + +/* Device attach */ +t_stat ai_attach (UNIT *uptr, CONST char *cptr) +{ + t_stat r; + DEVICE *rptr; + DIB *dib; + int ctlr; + + r = attach_unit (uptr, cptr); + if (r != SCPE_OK) + return r; + rptr = find_dev_from_unit(uptr); + if (rptr == 0) + return SCPE_OK; + dib = (DIB *) rptr->ctxt; + set_interrupt(dib->dev_num, 0); + return SCPE_OK; +} + +/* Device detach */ +t_stat ai_detach (UNIT *uptr) +{ + if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; + if (sim_is_active (uptr)) /* unit active? */ + sim_cancel (uptr); /* cancel operation */ + return detach_unit (uptr); +} + +t_stat ai_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +fprintf (st, "Systems Concepts DC-10\n\n"); +fprint_set_help (st, dptr); +fprint_show_help (st, dptr); +fprint_reg_help (st, dptr); +return SCPE_OK; +} + +const char *ai_description (DEVICE *dptr) +{ + return "Systems Concepts DC-10 disk controller"; +} + + +#endif diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index d4bb9ce..13c453c 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -376,6 +376,7 @@ extern DEVICE dkb_dev; extern DEVICE auxcpu_dev; extern DEVICE dpk_dev; extern DEVICE wcnsls_dev; /* MIT Spacewar Consoles */ +extern DEVICE ai_dev; extern DEVICE dct_dev; /* PDP6 devices. */ extern DEVICE dtc_dev; extern DEVICE mtc_dev; @@ -479,6 +480,7 @@ int auxcpu_write (int addr, t_uint64); #define NUM_DEVS_IMP 1 #define NUM_DEVS_CH10 ITS #define NUM_DEVS_DPK ITS +#define NUM_DEVS_AI ITS #endif /* Global data */ diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index 8378e0e..d9e0faf 100644 --- a/PDP10/kx10_sys.c +++ b/PDP10/kx10_sys.c @@ -187,6 +187,9 @@ DEVICE *sim_devices[] = { #endif #if NUM_DEVS_DPK > 0 &dpk_dev, +#endif +#if NUM_DEVS_AI > 0 + &ai_dev, #endif NULL }; From 1963a094b65b4d84fbfe9e69ae1ca82104a6b9a3 Mon Sep 17 00:00:00 2001 From: Lars Brinkhoff Date: Thu, 11 Jul 2019 21:05:09 +0200 Subject: [PATCH 2/2] SCP: Add KA10 device DC-10. --- makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/makefile b/makefile index 4753f4b..dfd1e30 100644 --- a/makefile +++ b/makefile @@ -1236,7 +1236,8 @@ KA10 = ${KA10D}/kx10_cpu.c ${KA10D}/kx10_sys.c ${KA10D}/kx10_df.c \ ${KA10D}/ka10_ten11.c ${KA10D}/ka10_auxcpu.c $(KA10D)/ka10_pmp.c \ ${KA10D}/ka10_dkb.c ${KA10D}/pdp6_dct.c ${KA10D}/pdp6_dtc.c \ ${KA10D}/pdp6_mtc.c ${KA10D}/pdp6_dsk.c ${KA10D}/pdp6_dcs.c \ - ${KA10D}/ka10_dpk.c ${KA10D}/kx10_dpy.c ${DISPLAYL} $(DISPLAY340) + ${KA10D}/ka10_dpk.c ${KA10D}/kx10_dpy.c ${PDP10D}/ka10_ai.c \ + ${DISPLAYL} $(DISPLAY340) KA10_OPT = -DKA=1 -DUSE_INT64 -I $(KA10D) -DUSE_SIM_CARD ${NETWORK_OPT} $(DISPLAY_OPT) $(KA10_DISPLAY_OPT) ifneq ($(PANDA_LIGHTS),) # ONLY for Panda display.