diff --git a/PDP10/ka10_ai.c b/PDP10/ka10_ai.c new file mode 100644 index 00000000..263bafa5 --- /dev/null +++ b/PDP10/ka10_ai.c @@ -0,0 +1,1035 @@ +/* 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 uint64 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; + int 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 &= ~(017LL << 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]; + size_t 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) { + t_offset pos; + uint64 header[2]; + image_state = IMAGE_POSTAMBLE; + image_count = 0; + pos = sim_ftell (channel_unit->fileref); + (void)sim_fseeko(channel_unit->fileref, pos - 2 * sizeof(uint64), SEEK_SET); + (void)sim_fread(header, sizeof(uint64), 2, channel_unit->fileref); + (void)sim_fseeko(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) +{ + unsigned 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; + + 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/ka10_auxcpu.c b/PDP10/ka10_auxcpu.c index b3352577..fcda5e81 100644 --- a/PDP10/ka10_auxcpu.c +++ b/PDP10/ka10_auxcpu.c @@ -54,12 +54,15 @@ static int pia = 0; static int status = 0; +t_value auxcpu_base = 03000000; static t_stat auxcpu_devio(uint32 dev, t_uint64 *data); static t_stat auxcpu_svc (UNIT *uptr); static t_stat auxcpu_reset (DEVICE *dptr); static t_stat auxcpu_attach (UNIT *uptr, CONST char *ptr); static t_stat auxcpu_detach (UNIT *uptr); +static t_stat auxcpu_set_base (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +static t_stat auxcpu_show_base (FILE *st, UNIT *uptr, int32 val, CONST void *desc); static t_stat auxcpu_attach_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); static const char *auxcpu_description (DEVICE *dptr); @@ -73,6 +76,8 @@ static REG auxcpu_reg[] = { }; static MTAB auxcpu_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "base address", "BASE", + &auxcpu_set_base, &auxcpu_show_base }, { 0 } }; @@ -389,4 +394,26 @@ t_stat auxcpu_devio(uint32 dev, t_uint64 *data) return SCPE_OK; } + +static t_stat auxcpu_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; + + auxcpu_base = x; + return SCPE_OK; +} + +static t_stat auxcpu_show_base (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + fprintf (st, "Base: %llo", auxcpu_base); + return SCPE_OK; +} #endif diff --git a/PDP10/ka10_stk.c b/PDP10/ka10_stk.c index cb423397..98911033 100644 --- a/PDP10/ka10_stk.c +++ b/PDP10/ka10_stk.c @@ -24,12 +24,14 @@ */ #include -#include "sim_video.h" -#include "display/display.h" #include "kx10_defs.h" #ifdef USE_DISPLAY #if NUM_DEVS_STK > 0 + +#include "sim_video.h" +#include "display/display.h" + #define STK_DEVNUM 070 /* CONI/O bits. */ diff --git a/PDP10/ka10_ten11.c b/PDP10/ka10_ten11.c index 81863c98..b7f817d1 100644 --- a/PDP10/ka10_ten11.c +++ b/PDP10/ka10_ten11.c @@ -262,7 +262,11 @@ static int read_word (int addr, int *data) build (request, (addr >> 8) & 0377); build (request, (addr) & 0377); - transaction (request, response); + if (transaction (request, response) == -1) { + /* Network error. */ + *data = 0; + return 0; + } switch (response[0]) { diff --git a/PDP10/kx10_cp.c b/PDP10/kx10_cp.c index f8750f18..a0e910a1 100644 --- a/PDP10/kx10_cp.c +++ b/PDP10/kx10_cp.c @@ -34,7 +34,7 @@ #include "sim_defs.h" #if (NUM_DEVS_CP > 0) -#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_029 +#define UNIT_CDP UNIT_ATTABLE | UNIT_DISABLE | MODE_026 #define CP_DEVNUM 0110 @@ -92,7 +92,7 @@ uint16 cp_buffer[80]; DIB cp_dib = { CP_DEVNUM, 1, cp_devio, NULL}; -UNIT cp_unit = {UDATA(cp_srv, UNIT_CDP, 0), 600 }; +UNIT cp_unit = {UDATA(cp_srv, UNIT_CDP, 0), 2000 }; MTAB cp_mod[] = { {MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", @@ -121,6 +121,7 @@ DEVICE cp_dev = { t_stat cp_devio(uint32 dev, uint64 *data) { UNIT *uptr = &cp_unit; + uint16 col; switch(dev & 3) { case CONI: @@ -170,7 +171,24 @@ t_stat cp_devio(uint32 dev, uint64 *data) { *data = 0; break; case DATAO: - cp_buffer[uptr->COL++] = *data & 0xfff; + col = *data & 0xfff; + switch(col) { + case 04006: col = 03000; break; /* ! - */ + case 01022: col = 00006; break; /* " - */ + case 01012: col = 01202; break; /* # - */ + case 01006: col = 01042; break; /* % - */ + case 02006: col = 05000; break; /* & - */ + case 00012: col = 00042; break; /* ' - */ + case 03000: col = 00022; break; /* : - */ + case 01202: col = 02012; break; /* ; - */ + case 02012: col = 00012; break; /* > - */ + case 05000: col = 04202; break; /* ? - */ + case 02022: col = 04022; break; /* [ - */ + case 00006: col = 01012; break; /* \ - */ + case 04022: col = 02022; break; /* ] - */ + case 00022: col = 00202; break; /* ^ - */ + } + cp_buffer[uptr->COL++] = col; uptr->STATUS &= ~DATA_REQ; clr_interrupt(dev); sim_debug(DEBUG_DATAIO, &cp_dev, "CP: DATAO %012llo %d\n", *data, diff --git a/PDP10/kx10_cpu.c b/PDP10/kx10_cpu.c index 60b10fcc..b93bda28 100644 --- a/PDP10/kx10_cpu.c +++ b/PDP10/kx10_cpu.c @@ -221,7 +221,8 @@ int32 tmxr_poll = 10000; /* Physical address range for Rubin 10-11 interface. */ #define T11RANGE(addr) ((addr) >= 03040000) /* Physical address range for auxiliary PDP-6. */ -#define AUXCPURANGE(addr) ((addr) >= 03000000 && (addr) < 03040000) +extern int auxcpu_base; +#define AUXCPURANGE(addr) ((addr) >= auxcpu_base && (addr) < (auxcpu_base + 040000)) DEVICE *rh_devs[] = { #if (NUM_DEVS_RS > 0) @@ -813,12 +814,12 @@ int opflags[] = { void set_quantum() { + double us; sim_cancel(&cpu_unit[1]); - if ((qua_time & RSIGN) == 0) { - double us; - us = (double)(RSIGN - qua_time); - (void)sim_activate_after_d(&cpu_unit[1], us); - } + if (qua_time & BIT17) + return; + us = (double)(BIT17 - qua_time); + (void)sim_activate_after_d(&cpu_unit[1], us); } /* @@ -830,7 +831,10 @@ load_quantum() if (sim_is_active(&cpu_unit[1])) { double us; us = sim_activate_time_usecs (&cpu_unit[1]); - qua_time = RSIGN - (uint32)us; + if ((uint32)us > BIT17) + qua_time = BIT17; + else + qua_time = (BIT17 - (uint32)us) & RMASK; sim_cancel(&cpu_unit[1]); } } @@ -841,11 +845,11 @@ load_quantum() uint32 get_quantum() { - uint32 t = 0; + uint32 t = qua_time; if (sim_is_active(&cpu_unit[1])) { double us; us = sim_activate_time_usecs (&cpu_unit[1]); - t = RSIGN - (uint32)us; + t = (BIT17 - (uint32)us) & RMASK; } return t; } @@ -3491,9 +3495,10 @@ dpnorm: AB = (AB + 1) & RMASK; MB = M[AB]; /* WD 3 */ /* Store Quantum */ - qua_time = MB & RMASK; + qua_time = MB & (RMASK|BIT17); set_quantum(); fault_data = (MB >> 18) & RMASK; + fault_data &= ~1; /* Clear high quantum bit */ mem_prot = 0; if ((fault_data & 0777772) != 0) mem_prot = 1; @@ -5997,9 +6002,8 @@ qua_srv(UNIT * uptr) { if ((fault_data & 1) == 0 && pi_enable && !pi_pending && (FLAGS & USER) != 0) { mem_prot = 1; - fault_data |= 1; } - qua_time = RSIGN; + qua_time = BIT17; return SCPE_OK; } #endif diff --git a/PDP10/kx10_cr.c b/PDP10/kx10_cr.c index ef1bc18a..be886cbf 100644 --- a/PDP10/kx10_cr.c +++ b/PDP10/kx10_cr.c @@ -34,7 +34,7 @@ #include "sim_defs.h" #if (NUM_DEVS_CR > 0) -#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | MODE_029 +#define UNIT_CDR UNIT_ATTABLE | UNIT_RO | UNIT_DISABLE | MODE_029 | MODE_LOWER #define CR_DEVNUM 0150 @@ -94,7 +94,7 @@ uint16 cr_buffer[80]; DIB cr_dib = { CR_DEVNUM, 1, cr_devio, NULL}; UNIT cr_unit = { - UDATA(cr_srv, UNIT_CDR, 0), 300, + UDATA(cr_srv, UNIT_CDR, 0), 2000, }; MTAB cr_mod[] = { @@ -136,7 +136,7 @@ t_stat cr_devio(uint32 dev, uint64 *data) { case CONO: clr_interrupt(dev); - sim_debug(DEBUG_CONO, &cr_dev, "CR: CONO %012llo\n", *data); + sim_debug(DEBUG_CONO, &cr_dev, "CR: CONO %012llo PC=%06o\n", *data, PC); if (*data & CLR_READER) { uptr->STATUS = 0; if (!CARD_RDY(uptr)) @@ -171,7 +171,9 @@ t_stat cr_devio(uint32 dev, uint64 *data) { case DATAI: clr_interrupt(dev); if (uptr->STATUS & DATA_RDY) { - *data = uptr->DATA; + *data = uptr->DATA & ~RSIGN; + if (uptr->DATA & RSIGN) + *data |= SMASK; sim_debug(DEBUG_DATAIO, &cr_dev, "CR: DATAI %012llo\n", *data); uptr->STATUS &= ~DATA_RDY; } else @@ -215,7 +217,7 @@ cr_srv(UNIT *uptr) { case CDSE_EMPTY: sim_debug(DEBUG_EXP, &cr_dev, "CR: card empty\n"); uptr->STATUS &= ~(CARD_IN_READ|READING); - uptr->STATUS |= HOPPER_EMPTY|TROUBLE|STOP; + uptr->STATUS |= HOPPER_EMPTY|TROUBLE; if (uptr->STATUS & TROUBLE_EN) set_interrupt(CR_DEVNUM, uptr->STATUS); return SCPE_OK; @@ -238,14 +240,43 @@ cr_srv(UNIT *uptr) { /* Copy next column over */ if (uptr->STATUS & CARD_IN_READ) { + uint32 data; + int i; if (uptr->COL >= 80) { uptr->STATUS &= ~(CARD_IN_READ|READING); + if (sim_card_input_hopper_count(uptr) == 0) + uptr->STATUS |= HOPPER_EMPTY; uptr->STATUS |= END_CARD; set_interrupt(CR_DEVNUM, uptr->STATUS); sim_activate(uptr, uptr->wait); return SCPE_OK; } - uptr->DATA = cr_buffer[uptr->COL++]; + data = cr_buffer[uptr->COL++]; + switch(data) { + case 0x482: data = 0x806; break; /* ! - 12 8 7 */ + case 0xA00: data = 0x882; break; /* [ - 12 8 2 */ + case 0x882: data = 0x482; break; /* ] - 11 8 2 */ + case 0x405: data = 0xa00; break; /* { - 12 0 */ + case 0x600: data = 0xc00; break; /* | - 12 11 */ + case 0x805: data = 0x600; break; /* } - 11 0 */ + case 0x806: data = 0x700; break; /* ~ - 11 0 1 */ + } + uptr->DATA = data; + /* Generate upper 18 bits of data */ + uptr->DATA |= ((data & 0x001) << 25) | + ((data & 0xe00) << 13) | + ((data & 0x002) << 20); + for (i = 1; i <= 7; i++) { + if (data & 0x100) { + /* Set flag it more then one punch */ + if ((uptr->DATA & 07000000) != 0){ + uptr->DATA |= (int32)RSIGN; + break; + } + uptr->DATA |= i << 18; + } + data <<= 1; + } if (uptr->STATUS & DATA_RDY) { uptr->STATUS |= DATA_MISS; } diff --git a/PDP10/kx10_dc.c b/PDP10/kx10_dc.c index 54001e46..f54ce793 100644 --- a/PDP10/kx10_dc.c +++ b/PDP10/kx10_dc.c @@ -107,7 +107,7 @@ const char *dc_description (DEVICE *dptr); DIB dc_dib = { DC_DEVNUM, 1, &dc_devio, NULL }; UNIT dc_unit = { - UDATA (&dc_svc, TT_MODE_7B+UNIT_IDLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT + UDATA (&dc_svc, TT_MODE_7B+UNIT_IDLE+UNIT_DISABLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT }; REG dc_reg[] = { diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index 26f2b5e0..13c453cb 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -167,6 +167,7 @@ extern DEBTAB crd_debug[]; #define BIT9 00000400000000LL #define BIT10 00000200000000LL #define BIT10_35 00000377777777LL +#define BIT17 00000001000000LL #define MANT 00000777777777LL #define EXPO 00377000000000LL #define FPHBIT 01000000000000000000000LL @@ -375,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; @@ -478,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_lp.c b/PDP10/kx10_lp.c index ceb0ecf9..fc1aafbe 100644 --- a/PDP10/kx10_lp.c +++ b/PDP10/kx10_lp.c @@ -40,6 +40,8 @@ #define POS u5 #define LINE u6 +#define MARGIN 6 + #define UNIT_V_CT (UNIT_V_UF + 0) #define UNIT_UC (1 << UNIT_V_CT) #define UNIT_UTF8 (2 << UNIT_V_CT) @@ -62,6 +64,10 @@ t_stat lpt_svc (UNIT *uptr); t_stat lpt_reset (DEVICE *dptr); t_stat lpt_attach (UNIT *uptr, CONST char *cptr); t_stat lpt_detach (UNIT *uptr); +t_stat lpt_setlpp(UNIT *, int32, CONST char *, void *); +t_stat lpt_getlpp(FILE *, UNIT *, int32, CONST void *); +t_stat lpt_setdev(UNIT *, int32, CONST char *, void *); +t_stat lpt_getdev(FILE *, UNIT *, int32, CONST void *); t_stat lpt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); const char *lpt_description (DEVICE *dptr); @@ -79,7 +85,7 @@ uint8 lpt_chbuf[5]; /* Read in Character buffers */ DIB lpt_dib = { LP_DEVNUM, 1, &lpt_devio, NULL }; UNIT lpt_unit = { - UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), 100 + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 66), 100 }; REG lpt_reg[] = { @@ -94,6 +100,10 @@ MTAB lpt_mod[] = { {UNIT_CT, 0, "Lower case", "LC", NULL}, {UNIT_CT, UNIT_UC, "Upper case", "UC", NULL}, {UNIT_CT, UNIT_UTF8, "UTF8 ouput", "UTF8", NULL}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE", + &lpt_setlpp, &lpt_getlpp, NULL, "Number of lines per page"}, + {MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "DEV", "DEV", + &lpt_setdev, &lpt_getdev, NULL, "Device address of printer defualt 124"}, { 0 } }; @@ -174,11 +184,19 @@ lpt_printline(UNIT *uptr, int nl) { /* Stick a carraige return and linefeed as needed */ if (uptr->COL != 0 || trim) lpt_buffer[uptr->POS++] = '\r'; - if (nl) { + if (nl != 0) { lpt_buffer[uptr->POS++] = '\n'; uptr->LINE++; } + if (nl > 0 && uptr->LINE >= ((int32)uptr->capac - MARGIN)) { + lpt_buffer[uptr->POS++] = '\f'; + uptr->LINE = 0; + } else if (nl < 0 && uptr->LINE >= (int32)uptr->capac) { + uptr->LINE = 0; + } + sim_fwrite(&lpt_buffer, 1, uptr->POS, uptr->fileref); + uptr->pos += uptr->POS; uptr->COL = 0; uptr->POS = 0; if (ferror (uptr->fileref)) { /* error? */ @@ -231,6 +249,8 @@ lpt_output(UNIT *uptr, char c) { if (c == 0) return; + if (uptr->COL == 132) + lpt_printline(uptr, 1); if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140) c &= 0137; if ((uptr->flags & UNIT_UTF8) && c < 040) { @@ -250,8 +270,6 @@ lpt_output(UNIT *uptr, char c) { lpt_buffer[uptr->POS++] = c; uptr->COL++; } - if (uptr->COL == 132) - lpt_printline(uptr, 1); return; } @@ -265,6 +283,7 @@ t_stat lpt_svc (UNIT *uptr) set_interrupt(LP_DEVNUM, uptr->STATUS); return SCPE_OK; } + if ((uptr->flags & UNIT_ATT) == 0) { uptr->STATUS |= ERR_FLG; set_interrupt(LP_DEVNUM, (uptr->STATUS >> 3)); @@ -300,45 +319,47 @@ t_stat lpt_svc (UNIT *uptr) break; case 012: /* Line feed, print line, space one line */ lpt_printline(uptr, 1); - uptr->LINE++; break; case 014: /* Form feed, skip to top of page */ lpt_printline(uptr, 0); sim_fwrite("\014", 1, 1, uptr->fileref); + uptr->pos++; uptr->LINE = 0; break; case 013: /* Vertical tab, Skip mod 20 */ lpt_printline(uptr, 1); while((uptr->LINE % 20) != 0) { sim_fwrite("\r\n", 1, 2, uptr->fileref); + uptr->pos+=2; uptr->LINE++; } break; - case 020: /* Skip even lines */ + case 020: /* Skip half page */ + lpt_printline(uptr, 1); + while((uptr->LINE % 30) != 0) { + sim_fwrite("\r\n", 1, 2, uptr->fileref); + uptr->pos+=2; + uptr->LINE++; + } + break; + case 021: /* Skip even lines */ lpt_printline(uptr, 1); while((uptr->LINE % 2) != 0) { sim_fwrite("\r\n", 1, 2, uptr->fileref); + uptr->pos+=2; uptr->LINE++; } break; - case 021: /* Skip third lines */ + case 022: /* Skip triple lines */ lpt_printline(uptr, 1); while((uptr->LINE % 3) != 0) { sim_fwrite("\r\n", 1, 2, uptr->fileref); + uptr->pos+=2; uptr->LINE++; } break; - case 022: /* Skip one line */ - lpt_printline(uptr, 1); - sim_fwrite("\r\n", 1, 2, uptr->fileref); - uptr->LINE+=2; - break; - case 023: /* Skip every 10 lines */ - lpt_printline(uptr, 1); - while((uptr->LINE % 10) != 0) { - sim_fwrite("\r\n", 1, 2, uptr->fileref); - uptr->LINE++; - } + case 023: /* Skip one line */ + lpt_printline(uptr, -1); break; default: /* Ignore */ break; @@ -388,12 +409,74 @@ t_stat lpt_detach (UNIT *uptr) return detach_unit (uptr); } +/* + * Line printer routines + */ + +t_stat +lpt_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 +lpt_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 +lpt_setdev(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, 8, 01000, &r); + if (r != SCPE_OK) + return SCPE_ARG; + if ((i & 03) != 0) + return SCPE_ARG; + lpt_dib.dev_num = (int)i; + return SCPE_OK; +} + +t_stat +lpt_getdev(FILE *st, UNIT *uptr, int32 v, CONST void *desc) +{ + if (uptr == NULL) + return SCPE_IERR; + fprintf(st, "dev=%03o", lpt_dib.dev_num); + return SCPE_OK; +} + t_stat lpt_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "Line Printer (LPT)\n\n"); fprintf (st, "The line printer (LPT) writes data to a disk file. The POS register specifies\n"); fprintf (st, "the number of the next data item to be written. Thus, by changing POS, the\n"); fprintf (st, "user can backspace or advance the printer.\n"); +fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n"); +fprintf (st, " sim> SET %s0 LINESPERPAGE=n\n\n", dptr->name); +fprintf (st, "The default is 66 lines per page.\n\n"); +fprintf (st, "The device address of the Line printer can be changed\n"); +fprintf (st, " sim> SET %s0 DEV=n\n\n", dptr->name); fprint_set_help (st, dptr); fprint_show_help (st, dptr); fprint_reg_help (st, dptr); diff --git a/PDP10/kx10_mt.c b/PDP10/kx10_mt.c index 0ce3ee87..9e45039f 100644 --- a/PDP10/kx10_mt.c +++ b/PDP10/kx10_mt.c @@ -240,6 +240,8 @@ t_stat mt_devio(uint32 dev, uint64 *data) { res |= ((uint64)wr_eor) << 21; if (dptr->flags & MTDF_TYPEB) res |= 7LL; /* Force DATA PIA to 7 on type B */ + if (cpu_unit[0].flags & UNIT_ITSPAGE) + res |= SMASK; *data = res; sim_debug(DEBUG_CONI, dptr, "MT CONI %03o status %06o %o %o PC=%06o\n", dev, (uint32)res, mt_sel_unit, mt_pia, PC); @@ -613,7 +615,7 @@ t_stat mt_srv(UNIT * uptr) if (uptr->flags & MTUF_7TRK) { cc = 6 * (5 - uptr->CPOS); ch = mt_buffer[uptr->BPOS]; - if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ + if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ (ch & 0x40) ^ parity_table[ch & 0x3f]) != 0) { mt_status |= PARITY_ERR; } @@ -692,8 +694,8 @@ t_stat mt_srv(UNIT * uptr) if ((uptr->CNTRL & MT_BRFUL) != 0) { if (uptr->flags & MTUF_7TRK) { ch = mt_buffer[uptr->BPOS]; - if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ - parity_table[ch & 0x3f]) != (ch & 0x40)) { + if ((((uptr->CNTRL & ODD_PARITY) ? 0x40 : 0) ^ (ch & 0x40) ^ + parity_table[ch & 0x3f]) != 0) { mt_status |= PARITY_ERR; } mt_buffer[uptr->BPOS] &= 0x3f; @@ -869,16 +871,22 @@ t_stat mt_srv(UNIT * uptr) } void mt_read_word(UNIT *uptr) { - int i, cc, ch; + int i, cc, ch, cc_max; mt_df10.buf = 0; - for(i = 0; i <= 4; i++) { - cc = (8 * (3 - i)) + 4; + cc_max = (uptr->flags & MTUF_7TRK) ? 5: 4; + for(i = 0; i <= cc_max; i++) { ch = mt_buffer[uptr->BPOS]; - if (cc < 0) - mt_df10.buf |= (uint64)(ch & 0x3f); - else - mt_df10.buf |= (uint64)(ch & 0xff) << cc; + if (uptr->flags & MTUF_7TRK) { + cc = 6 * (5 - i); + mt_df10.buf |= (uint64)(ch & 0x3f) << cc; + } else { + cc = (8 * (3 - i)) + 4; + if (cc < 0) + mt_df10.buf |= (uint64)(ch & 0x3f); + else + mt_df10.buf |= (uint64)(ch & 0xff) << cc; + } uptr->BPOS++; } } diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index 534942c5..d9e0faff 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 }; @@ -385,14 +388,16 @@ t_stat load_sblk (FILE *fileref) and then comes the data. Last is a checksum word. */ while (get_evac (fileref, &word) == 0 && (word & SMASK)) { check = word; - count = (int)((((word >> 18) ^ RMASK) + 1) & RMASK); + count = (word >> 18) & RMASK; addr = word & RMASK; - while (count-- > 0) { + while (count != 0) { if (get_evac (fileref, &word)) return SCPE_FMT; M[addr++] = word; check = (check << 1) + (check >> 35) + word; check &= FMASK; + count++; + count &= RMASK; } if (get_evac (fileref, &word)) return SCPE_FMT; diff --git a/Visual Studio Projects/PDP10-KA.vcproj b/Visual Studio Projects/PDP10-KA.vcproj index 975736b7..6b1a13b5 100644 --- a/Visual Studio Projects/PDP10-KA.vcproj +++ b/Visual Studio Projects/PDP10-KA.vcproj @@ -193,6 +193,10 @@ Name="Source Files" Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm" > + + diff --git a/descrip.mms b/descrip.mms index 51711e50..6acbcaef 100644 --- a/descrip.mms +++ b/descrip.mms @@ -655,7 +655,7 @@ KA10_SOURCE = $(KA10_DIR)KX10_CPU.C,\ $(KA10_DIR)PDP6_DTC.C,$(KA10_DIR)PDP6_MTC.C,\ $(KA10_DIR)PDP6_DSK.C,$(KA10_DIR)PDP6_DCS.C,\ $(KA10_DIR)KA10_DPK.C,$(KA10_DIR)KX10_DPY.C,\ - $(SIMH_DIR)SIM_CARD.C + $(KA10_DIR)KA10_AI.C,$(SIMH_DIR)SIM_CARD.C KA10_OPTIONS = /INCL=($(SIMH_DIR),$(KA10_DIR))\ /DEF=($(CC_DEFS),"KA=1","USE_INT64=1","USE_SIM_CARD=1"$(PCAP_DEFS)) diff --git a/doc/ka10_doc.doc b/doc/ka10_doc.doc index caa10026..9bdca481 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 06a99b2d..42c1f6bd 100644 Binary files a/doc/ki10_doc.doc and b/doc/ki10_doc.doc differ diff --git a/makefile b/makefile index bd016ca0..8b30f740 100644 --- a/makefile +++ b/makefile @@ -1915,7 +1915,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.