diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index be044dfc..93c9a57a 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -127,8 +127,8 @@ static DEBTAB cpu_deb_tab[] = { { "EXECUTE", EXECUTE_MSG, "Instruction execute" }, { "INIT", INIT_MSG, "Initialization" }, { "IRQ", IRQ_MSG, "Interrupt Handling" }, - { "IO", IO_D_MSG, "I/O Dispatch" }, - { "TRACE", TRACE_MSG, "Call Trace" }, + { "IO", IO_DBG, "I/O Dispatch" }, + { "TRACE", TRACE_DBG, "Call Trace" }, { "ERROR", ERR_MSG, "Error" }, { NULL, 0 } }; @@ -138,6 +138,11 @@ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX|UNIT_BINK|UNIT_IDLE, MAXMEMSIZE) }; #define UNIT_V_EXHALT (UNIT_V_UF + 0) /* halt to console */ #define UNIT_EXHALT (1u << UNIT_V_EXHALT) +const char *cio_names[8] = { + "", "*VOID*", "*VOID*", "PORTS", + "*VOID*", "CTC", "*VOID*", "*VOID*" +}; + MTAB cpu_mod[] = { { UNIT_MSIZE, (1u << 20), NULL, "1M", &cpu_set_size, NULL, NULL, "Set Memory to 1M bytes" }, @@ -149,8 +154,10 @@ MTAB cpu_mod[] = { &cpu_set_hist, &cpu_show_hist, NULL, "Displays instruction history" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, NULL, &cpu_show_virt, NULL, "Show translation for virtual address" }, - { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", "STACK", + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "STACK", NULL, NULL, &cpu_show_stack, NULL, "Display the current stack with optional depth" }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "CIO", NULL, + NULL, &cpu_show_cio, NULL, "Display CIO configuration" }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_EXHALT, UNIT_EXHALT, "Halt on Exception", "EXHALT", @@ -532,6 +539,19 @@ t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc) return SCPE_OK; } +t_stat cpu_show_cio(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + uint32 i; + + fprintf(st, " SLOT DEVICE\n"); + fprintf(st, "---------------------\n"); + for (i = 0; i < CIO_SLOTS; i++) { + fprintf(st, " %d %s\n", i, cio_names[cio[i].id & 0x7]); + } + + return SCPE_OK; +} + void cpu_load_rom() { uint32 i, index, sc, mask, val; @@ -988,13 +1008,18 @@ t_stat cpu_show_virt(FILE *of, UNIT *uptr, int32 val, CONST void *desc) if (r == SCPE_OK) { fprintf(of, "Virtual %08x = Physical %08x\n", va, pa); return SCPE_OK; + } else { + fprintf(of, "Translation not possible for virtual address.\n"); + return SCPE_ARG; } + } else { + fprintf(of, "Illegal address format.\n"); + return SCPE_ARG; } } - fprintf(of, "Translation not possible.\n"); - - return SCPE_OK; + fprintf(of, "Address argument required.\n"); + return SCPE_ARG; } @@ -1557,6 +1582,10 @@ void cpu_on_interrupt(uint16 vec) { uint32 new_pcbp; + sim_debug(IRQ_MSG, &cpu_dev, + "[%08x] [cpu_on_interrupt] vec=%02x (%d)\n", + R[NUM_PC], vec, vec); + /* * "If a nonmaskable interrupt request is received, an auto-vector * interrupt acknowledge cycle is performed (as if an autovector @@ -1733,6 +1762,10 @@ t_stat sim_instr(void) if (cio[i].intr && cio[i].ipl == cpu_int_ipl && cio[i].ivec == cpu_int_vec) { + sim_debug(IO_DBG, &cpu_dev, + "[%08x] [IRQ] Handling CIO interrupt for card %d\n", + R[NUM_PC], i); + cio[i].intr = FALSE; } } diff --git a/3B2/3b2_cpu.h b/3B2/3b2_cpu.h index 1de5728b..739cff0f 100644 --- a/3B2/3b2_cpu.h +++ b/3B2/3b2_cpu.h @@ -397,6 +397,7 @@ 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); t_stat cpu_show_virt(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat cpu_show_stack(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat cpu_show_cio(FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat cpu_set_halt(UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_clear_halt(UNIT *uptr, int32 val, char *cptr, void *desc); t_stat cpu_boot(int32 unit_num, DEVICE *dptr); diff --git a/3B2/3b2_ctc.c b/3B2/3b2_ctc.c new file mode 100644 index 00000000..9386e5bb --- /dev/null +++ b/3B2/3b2_ctc.c @@ -0,0 +1,817 @@ +/* 3b2_ctc.c: AT&T 3B2 Model 400 "CTC" feature card + + Copyright (c) 2018, Seth J. Morabito + + 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 AUTHORS OR COPYRIGHT HOLDERS + 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 "3b2_ctc.h" + +extern CIO_STATE cio[CIO_SLOTS]; +extern UNIT cio_unit; + +#define CTQRESIZE 20 +#define CTQCESIZE 16 + +#define DELAY_SYSGEN 2500 +#define DELAY_FMT 1000000 +#define DELAY_RW 10000 +#define DELAY_OPEN 2500 +#define DELAY_CLOSE 2500 +#define DELAY_CONFIG 2500 +#define DELAY_DLM 1000 +#define DELAY_ULM 1000 +#define DELAY_FCF 1000 +#define DELAY_DOS 1000 +#define DELAY_DSD 1000 +#define DELAY_UNK 1000 +#define DELAY_CATCHUP 10000 + +#define TAPE_DEV 0 /* CTAPE device */ +#define XMF_DEV 1 /* XM Floppy device */ + +#define VTOC_BLOCK 0 + +#define ATOW(arr,i) ((uint32)arr[i+3] + ((uint32)arr[i+2] << 8) + \ + ((uint32)arr[i+1] << 16) + ((uint32)arr[i] << 24)) + +static uint8 int_cid; /* Interrupting card ID */ +static uint8 int_subdev; /* Interrupting subdevice */ +static t_bool ctc_conf = FALSE; /* Has a CTC card been configured? */ + +struct partition vtoc_table[VTOC_PART] = { + { 2, 0, 5272, 8928 }, /* 00 */ + { 3, 1, 126, 5146 }, /* 01 */ + { 4, 0, 14200, 31341 }, /* 02 */ + { 0, 0, 2, 45539 }, /* 03 */ + { 0, 1, 0, 0 }, /* 04 */ + { 0, 1, 0, 0 }, /* 05 */ + { 5, 1, 0, 45541 }, /* 06 */ + { 1, 1, 0, 126 }, /* 07 */ + { 0, 1, 0, 0 }, /* 08 */ + { 0, 1, 0, 0 }, /* 09 */ + { 0, 1, 0, 0 }, /* 10 */ + { 0, 1, 0, 0 }, /* 11 */ + { 0, 1, 0, 0 }, /* 12 */ + { 0, 1, 0, 0 }, /* 13 */ + { 0, 1, 0, 0 }, /* 14 */ + { 0, 1, 0, 0 } /* 15 */ +}; + +/* State. Although we technically have two devices (tape and floppy), + * only the tape drive is supported at this time. */ + +CTC_STATE ctc_state[2]; + +UNIT ctc_unit = { + UDATA (&ctc_svc, UNIT_FIX|UNIT_ATTABLE|UNIT_DISABLE| + UNIT_ROABLE|UNIT_BINK, CTC_CAPACITY) +}; + +MTAB ctc_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", + NULL, NULL, NULL, "Write enabled tape drive" }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", + NULL, NULL, NULL, "Write lock tape drive" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL, + NULL, &ctc_show_rqueue, NULL, "Display Request Queue for card n" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL, + NULL, &ctc_show_cqueue, NULL, "Display Completion Queue for card n" }, + { 0 } +}; + +static DEBTAB ctc_debug[] = { + { "IO", IO_DBG, "I/O" }, + { "TRACE", TRACE_DBG, "Call Trace" }, + { NULL } +}; + +DEVICE ctc_dev = { + "CTC", /* name */ + &ctc_unit, /* units */ + NULL, /* registers */ + ctc_mod, /* modifiers */ + 1, /* #units */ + 16, /* address radix */ + 32, /* address width */ + 1, /* address incr. */ + 16, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &ctc_reset, /* reset routine */ + NULL, /* boot routine */ + &ctc_attach, /* attach routine */ + &ctc_detach, /* detach routine */ + NULL, /* context */ + DEV_DISABLE|DEV_DIS|DEV_DEBUG|DEV_SECTORS, /* flags */ + 0, /* debug control flags */ + ctc_debug, /* debug flag names */ + NULL, /* memory size change */ + NULL, /* logical name */ + NULL, /* help routine */ + NULL, /* attach help routine */ + NULL, /* help context */ + NULL, /* device description */ +}; + +static void cio_irq(uint8 cid, uint8 dev, int32 delay) +{ + int_cid = cid; + int_subdev = dev & 0x3f; + sim_activate_after(&ctc_unit, delay); +} + +/* + * Write a VTOC and pdinfo to the tape file + */ +static t_stat ctc_write_vtoc(struct vtoc *vtoc, struct pdinfo *pdinfo, uint32 maxpass) +{ + uint8 buf[PD_BYTES]; + uint32 wr, offset; + + memcpy(buf, vtoc, sizeof(struct vtoc)); + offset = sizeof(struct vtoc); + memcpy(buf + offset, pdinfo, sizeof(struct pdinfo)); + offset += sizeof(struct pdinfo); + memcpy(buf + offset, &maxpass, sizeof(uint32)); + + return sim_disk_wrsect(&ctc_unit, VTOC_BLOCK, buf, &wr, 1); +} + +/* + * Load a VTOC and pdinfo from the tape file + */ +static t_stat ctc_read_vtoc(struct vtoc *vtoc, struct pdinfo *pdinfo, uint32 *maxpass) +{ + uint8 buf[PD_BYTES]; + uint32 wr, offset; + t_stat result; + + result = sim_disk_rdsect(&ctc_unit, VTOC_BLOCK, buf, &wr, 1); + + if (result != SCPE_OK) { + return result; + } + + memcpy(vtoc, buf, sizeof(struct vtoc)); + offset = sizeof(struct vtoc); + memcpy(pdinfo, buf + offset, sizeof(struct pdinfo)); + offset += sizeof(struct pdinfo); + memcpy(maxpass, buf + offset, sizeof(uint32)); + + return result; +} + +/* + * Update the host's in-memory copy of the VTOC and pdinfo + */ +static void ctc_update_vtoc(uint32 maxpass, + uint32 vtoc_addr, uint32 pdinfo_addr, + struct vtoc *vtoc, struct pdinfo *pdinfo) +{ + uint32 i; + + pwrite_w(vtoc_addr + 12, VTOC_VALID); + pwrite_w(vtoc_addr + 16, vtoc->version); + for (i = 0; i < 8; i++) { + pwrite_b(vtoc_addr + 20 + i, (uint8)(vtoc->volume[i])); + } + pwrite_h(vtoc_addr + 28, vtoc->sectorsz); + pwrite_h(vtoc_addr + 30, vtoc->nparts); + + for (i = 0; i < VTOC_PART; i++) { + pwrite_h(vtoc_addr + 72 + (i * 12) + 0, vtoc_table[i].id); + pwrite_h(vtoc_addr + 72 + (i * 12) + 2, vtoc_table[i].flag); + pwrite_w(vtoc_addr + 72 + (i * 12) + 4, vtoc_table[i].sstart); + pwrite_w(vtoc_addr + 72 + (i * 12) + 8, vtoc_table[i].ssize); + } + + /* Write the pdinfo */ + pwrite_w(pdinfo_addr, pdinfo->driveid); + pwrite_w(pdinfo_addr + 4, pdinfo->sanity); + pwrite_w(pdinfo_addr + 8, pdinfo->version); + for (i = 0; i < 12; i++) { + pwrite_b(pdinfo_addr + 12 + i, pdinfo->serial[i]); + } + pwrite_w(pdinfo_addr + 24, pdinfo->cyls); + pwrite_w(pdinfo_addr + 28, pdinfo->tracks); + pwrite_w(pdinfo_addr + 32, pdinfo->sectors); + pwrite_w(pdinfo_addr + 36, pdinfo->bytes); + pwrite_w(pdinfo_addr + 40, pdinfo->logicalst); + pwrite_w(pdinfo_addr + 44, pdinfo->errlogst); + pwrite_w(pdinfo_addr + 48, pdinfo->errlogsz); + pwrite_w(pdinfo_addr + 52, pdinfo->mfgst); + pwrite_w(pdinfo_addr + 56, pdinfo->mfgsz); + pwrite_w(pdinfo_addr + 60, pdinfo->defectst); + pwrite_w(pdinfo_addr + 64, pdinfo->defectsz); + pwrite_w(pdinfo_addr + 68, pdinfo->relno); + pwrite_w(pdinfo_addr + 72, pdinfo->relst); + pwrite_w(pdinfo_addr + 76, pdinfo->relsz); + pwrite_w(pdinfo_addr + 80, pdinfo->relnext); + + /* Now something horrible happens. We sneak RIGHT off the end of + * the pdinfo struct and reach deep into the pdsector struct that + * it is part of. */ + + pwrite_w(pdinfo_addr + 128, maxpass); +} + +/* + * Handle a single request taken from the Request Queue. + * + * Note that the driver stuffs parameters into various different + * fields of the Request Queue entry seemingly at random, and also + * expects response parameters to be placed in specific fields of the + * Completion Queue entry. It can be confusing to follow. + */ +static void ctc_cmd(uint8 cid, + cio_entry *rqe, uint8 *rapp_data, + cio_entry *cqe, uint8 *capp_data) +{ + uint32 vtoc_addr, pdinfo_addr, ctjob_addr; + uint32 maxpass, blkno, delay; + uint8 dev; + uint8 sec_buf[512]; + int32 secrw = 0; + int32 b; + struct vtoc vtoc = {0}; + struct pdinfo pdinfo = {0}; + + uint32 lba; /* Logical Block Address */ + + dev = rqe->subdevice & 1; /* Tape or Floppy device */ + + capp_data[7] = rqe->opcode; + cqe->subdevice = rqe->subdevice; + + switch(rqe->opcode) { + case CIO_DLM: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CIO Download Memory: bytecnt=%04x " + "addr=%08x return_addr=%08x subdev=%02x\n", + rqe->byte_count, rqe->address, + rqe->address, rqe->subdevice); + delay = DELAY_DLM; + cqe->address = rqe->address + rqe->byte_count; + cqe->opcode = CTC_SUCCESS; + break; + case CIO_ULM: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CIO Upload Memory: return opcode 0\n"); + delay = DELAY_ULM; + cqe->opcode = CTC_SUCCESS; + break; + case CIO_FCF: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CIO Force Function Call: return opcode 0\n"); + delay = DELAY_FCF; + + /* This is to pass diagnostics. TODO: Figure out how to parse + * the given test x86 code and determine how to respond + * correctly */ + pwrite_h(0x200f000, 0x1); /* Test success */ + pwrite_h(0x200f002, 0x0); /* Test Number */ + pwrite_h(0x200f004, 0x0); /* Actual */ + pwrite_h(0x200f006, 0x0); /* Expected */ + pwrite_b(0x200f008, 0x1); /* Success flag again */ + pwrite_b(0x200f009, 0x30); /* ??? */ + + /* An interesting (?) side-effect of FORCE FUNCTION CALL is + * that it resets the card state such that a new SYSGEN is + * required in order for new commands to work. In fact, an + * INT0/INT1 combo _without_ a RESET can sysgen the board. So, + * we reset the command bits here. */ + cio[cid].sysgen_s = 0; + cqe->opcode = CTC_SUCCESS; + break; + case CIO_DOS: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CIO_DOS (%d)\n", + rqe->opcode); + delay = DELAY_DOS; + cqe->opcode = CTC_SUCCESS; + break; + case CIO_DSD: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_DSD (%d)\n", + rqe->opcode); + delay = DELAY_DSD; + /* The system wants us to write sub-device structures at the + * supplied address, but we have nothing to write. */ + pwrite_h(rqe->address, 0x0); + cqe->opcode = CTC_SUCCESS; + break; + case CTC_FORMAT: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_FORMAT (%d)\n", + rqe->opcode); + + delay = DELAY_FMT; + + /* FORMAT stores the job pointer in the jio_start field of the + * completion queue entry's application data */ + capp_data[0] = rapp_data[4]; + capp_data[1] = rapp_data[5]; + capp_data[2] = rapp_data[6]; + capp_data[3] = rapp_data[7]; + + if (dev == XMF_DEV) { + cqe->opcode = CTC_NOTREADY; + break; + } + + if ((ctc_unit.flags & UNIT_ATT) == 0) { + cqe->opcode = CTC_NOMEDIA; + break; + } + + if (ctc_unit.flags & UNIT_WLK) { + cqe->opcode = CTC_RDONLY; + break; + } + + /* Write a valid VTOC and pdinfo to the tape */ + + vtoc.sanity = VTOC_VALID; + vtoc.version = 1; + strcpy(vtoc.volume, "ctctape"); + vtoc.sectorsz = PD_BYTES; + vtoc.nparts = VTOC_PART; + + pdinfo.driveid = PD_DRIVEID; + pdinfo.sanity = PD_VALID; + pdinfo.version = 0; + memset(pdinfo.serial, 0, 12); + pdinfo.cyls = PD_CYLS; + pdinfo.tracks = PD_TRACKS; + pdinfo.sectors = PD_SECTORS; + pdinfo.bytes = PD_BYTES; + pdinfo.logicalst = PD_LOGICALST; + pdinfo.errlogst = 0xffffffff; + pdinfo.errlogsz = 0xffffffff; + pdinfo.mfgst = 0xffffffff; + pdinfo.mfgsz = 0xffffffff; + pdinfo.defectst = 0xffffffff; + pdinfo.defectsz = 0xffffffff; + pdinfo.relno = 0xffffffff; + pdinfo.relst = 0xffffffff; + pdinfo.relsz = 0xffffffff; + pdinfo.relnext = 0xffffffff; + + maxpass = rqe->address; + + ctc_write_vtoc(&vtoc, &pdinfo, maxpass); + + cqe->opcode = CTC_SUCCESS; + + /* The address field holds the total amount of time (in 25ms + * chunks) used during this format session. We'll fudge and + * say 1 minute for formatting. */ + cqe->address = 2400; + + break; + case CTC_OPEN: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_OPEN (%d)\n", + rqe->opcode); + + delay = DELAY_OPEN; + + ctc_state[dev].time = 0; /* Opening always resets session time to 0 */ + + vtoc_addr = rqe->address; + pdinfo_addr = ATOW(rapp_data, 4); + ctjob_addr = ATOW(rapp_data, 8); + + /* For OPEN commands, the Completion Queue Entry's address + * field contains a pointer to the ctjobstat. */ + cqe->address = ctjob_addr; + + if (dev == XMF_DEV) { + cqe->opcode = CTC_NOTREADY; + break; + } + + if ((ctc_unit.flags & UNIT_ATT) == 0) { + cqe->opcode = CTC_NOMEDIA; + break; + } + + /* Load the vtoc, pdinfo, and maxpass from the tape */ + ctc_read_vtoc(&vtoc, &pdinfo, &maxpass); + + ctc_update_vtoc(maxpass, vtoc_addr, pdinfo_addr, &vtoc, &pdinfo); + cqe->opcode = CTC_SUCCESS; + break; + case CTC_CLOSE: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_CLOSE (%d)\n", + rqe->opcode); + + delay = DELAY_CLOSE; + + /* The Request Queue Entry's address field contains the + * ctjobstat pointer, which the driver will want to find in + * the first word of our Completion Queue Entry's application + * data. This must be in place whether we have media attached + * or not. */ + capp_data[3] = rqe->address & 0xff; + capp_data[2] = (rqe->address & 0xff00) >> 8; + capp_data[1] = (rqe->address & 0xff0000) >> 16; + capp_data[0] = (rqe->address & 0xff000000) >> 24; + + /* The Completion Queue Entry's address field holds the total + * tape time used in this session. */ + cqe->address = ctc_state[dev].time; + cqe->opcode = CTC_SUCCESS; + + break; + case CTC_WRITE: + case CTC_VWRITE: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_WRITE or CTC_VWRITE (%d)\n", + rqe->opcode); + + delay = DELAY_RW; + + cqe->byte_count = rqe->byte_count; + cqe->subdevice = rqe->subdevice; + cqe->address = ATOW(rapp_data, 4); + + if (dev == XMF_DEV) { + cqe->opcode = CTC_NOTREADY; + break; + } + + if ((ctc_unit.flags & UNIT_ATT) == 0) { + cqe->opcode = CTC_NOMEDIA; + break; + } + + if (ctc_unit.flags & UNIT_WLK) { + cqe->opcode = CTC_RDONLY; + break; + } + + blkno = ATOW(rapp_data, 0); + + for (b = 0; b < rqe->byte_count / 512; b++) { + ctc_state[dev].time += 10; + for (int j = 0; j < 512; j++) { + /* Fill the buffer */ + sec_buf[j] = pread_b(rqe->address + (b * 512) + j); + } + lba = blkno + b; + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] ... CTC_WRITE: 512 bytes at block %d (0x%x)\n", + lba, lba); + sim_disk_wrsect(&ctc_unit, lba, sec_buf, &secrw, 1); + } + + cqe->opcode = CTC_SUCCESS; + break; + case CTC_READ: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_READ (%d)\n", + rqe->opcode); + delay = DELAY_RW; + cqe->byte_count = rqe->byte_count; + cqe->subdevice = rqe->subdevice; + cqe->address = ATOW(rapp_data, 4); + + if (dev == XMF_DEV) { + cqe->opcode = CTC_NOTREADY; + break; + } + + if ((ctc_unit.flags & UNIT_ATT) == 0) { + cqe->opcode = CTC_NOMEDIA; + break; + } + + blkno = ATOW(rapp_data, 0); + + for (b = 0; b < rqe->byte_count / 512; b++) { + ctc_state[dev].time += 10; + lba = blkno + b; + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] ... CTC_READ: 512 bytes from block %d (0x%x)\n", + lba, lba); + sim_disk_rdsect(&ctc_unit, lba, sec_buf, &secrw, 1); + for (int j = 0; j < 512; j++) { + /* Drain the buffer */ + pwrite_b(rqe->address + (b * 512) + j, sec_buf[j]); + } + } + + cqe->opcode = CTC_SUCCESS; + break; + case CTC_CONFIG: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] CTC_CONFIG (%d)\n", + rqe->opcode); + delay = DELAY_CONFIG; + cqe->opcode = CTC_SUCCESS; + break; + default: + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_cmd] UNHANDLED OP: %d (0x%02x)\n", + rqe->opcode, rqe->opcode); + delay = DELAY_UNK; + cqe->opcode = CTC_HWERROR; + break; + } + + cio_irq(cid, rqe->subdevice, delay); +} + +void ctc_sysgen(uint8 cid) +{ + cio_entry cqe = {0}; + uint8 rapp_data[12] = {0}; + + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] Handling Sysgen.\n"); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqp=%08x\n", cio[cid].rqp); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqp=%08x\n", cio[cid].cqp); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] rqs=%d\n", cio[cid].rqs); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] cqs=%d\n", cio[cid].cqs); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] ivec=%d\n", cio[cid].ivec); + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_sysgen] no_rque=%d\n", cio[cid].no_rque); + + cqe.opcode = 3; /* Sysgen success! */ + + cio_cexpress(cid, CTQCESIZE, &cqe, rapp_data); + cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, rapp_data); + + int_cid = cid; + sim_activate_after(&ctc_unit, DELAY_SYSGEN); +} + +void ctc_express(uint8 cid) +{ + cio_entry rqe, cqe; + uint8 rapp_data[12] = {0}; + uint8 capp_data[8] = {0}; + + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_express] Handling Express Request\n"); + + cio_rexpress(cid, CTQRESIZE, &rqe, rapp_data); + ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data); + + dump_entry(TRACE_DBG, &ctc_dev, "COMPLETION", + CTQCESIZE, &cqe, capp_data); + + cio_cexpress(cid, CTQCESIZE, &cqe, capp_data); +} + +void ctc_full(uint8 cid) +{ + cio_entry rqe, cqe; + uint8 rapp_data[12] = {0}; + uint8 capp_data[8] = {0}; + + sim_debug(TRACE_DBG, &ctc_dev, "[ctc_full] Handling Full Request\n"); + + while (cio_cqueue_avail(cid, CTQCESIZE) && + cio_rqueue(cid, TAPE_DEV, CTQRESIZE, &rqe, rapp_data) == SCPE_OK) { + ctc_cmd(cid, &rqe, rapp_data, &cqe, capp_data); + } + cio_cqueue(cid, CIO_STAT, CTQCESIZE, &cqe, capp_data); +} + +t_stat ctc_reset(DEVICE *dptr) +{ + uint32 i; + uint8 cid, end_slot; + + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_reset] Resetting CTC device\n"); + + memset(ctc_state, 0, 2 * sizeof(CTC_STATE)); + + if (dptr->flags & DEV_DIS) { + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_reset] REMOVING CARD\n"); + + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == CTC_ID) { + break; + } + } + + if (cid == CIO_SLOTS) { + /* No card was ever attached */ + return SCPE_OK; + } + + cio[cid].id = 0; + cio[cid].ipl = 0; + cio[cid].ivec = 0; + cio[cid].exp_handler = NULL; + cio[cid].full_handler = NULL; + cio[cid].sysgen = NULL; + + ctc_conf = FALSE; + } else if (!ctc_conf) { + sim_debug(TRACE_DBG, &ctc_dev, + "[ctc_reset] ATTACHING CARD\n"); + + /* Find the first avaialable slot */ + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == 0) { + break; + } + } + + /* Do we have room? */ + if (cid == CIO_SLOTS) { + return SCPE_NXM; + } + + cio[cid].id = CTC_ID; + cio[cid].ipl = CTC_IPL; + cio[cid].exp_handler = &ctc_express; + cio[cid].full_handler = &ctc_full; + cio[cid].sysgen = &ctc_sysgen; + + ctc_conf = TRUE; + } + + return SCPE_OK; +} + +t_stat ctc_svc(UNIT *uptr) +{ + uint16 lp, ulp; + + if (cio[int_cid].ivec > 0) { + sim_debug(TRACE_DBG, &ctc_dev, + "[cio_svc] IRQ for board %d (VEC=%d)\n", + int_cid, cio[int_cid].ivec); + cio[int_cid].intr = TRUE; + } + + /* Check to see if the completion queue has more work in it. We + * need to schedule an interrupt for each job if we've fallen + * behind (this should be rare) */ + lp = cio_c_lp(int_cid, CTQCESIZE); + ulp = cio_c_ulp(int_cid, CTQCESIZE); + + if ((ulp + CTQCESIZE) % (CTQCESIZE * cio[int_cid].cqs) != lp) { + sim_debug(TRACE_DBG, &ctc_dev, + "[cio_svc] Completion queue has fallen behind (lp=%04x ulp=%04x)\n", + lp, ulp); + /* Schedule a catch-up interrupt */ + sim_activate_abs(&ctc_unit, DELAY_CATCHUP); + } + + return SCPE_OK; +} + +t_stat ctc_attach(UNIT *uptr, CONST char *cptr) +{ + return sim_disk_attach(uptr, cptr, 512, 1, TRUE, 0, "CIPHER23", 0, 0); +} + +t_stat ctc_detach(UNIT *uptr) +{ + return sim_disk_detach(uptr); +} + +t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + return ctc_show_queue_common(st, uptr, val, desc, TRUE); +} + +t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + return ctc_show_queue_common(st, uptr, val, desc, FALSE); +} + + +static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val, + CONST void *desc, t_bool rq) +{ + uint8 cid; + char *cptr = (char *) desc; + t_stat result; + uint32 ptr, size, no_rque, i, j; + uint8 op, dev, seq, cmdstat; + + if (cptr) { + cid = (uint8) get_uint(cptr, 10, 12, &result); + if (result != SCPE_OK) { + return SCPE_ARG; + } + } else { + return SCPE_ARG; + } + + /* If the card is not sysgen'ed, give up */ + if (cio[cid].sysgen_s != CIO_SYSGEN) { + fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid); + return SCPE_ARG; + } + + if (rq) { + ptr = cio[cid].rqp; + size = cio[cid].rqs; + no_rque = cio[cid].no_rque; + fprintf(st, "Dumping %d Request Queues\n", no_rque); + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "EXPRESS ENTRY:\n"); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); + fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); + ptr += CTQRESIZE; + + for (i = 0; i < no_rque; i++) { + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "REQUEST QUEUE %d\n", i); + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQRESIZE); + fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQRESIZE); + fprintf(st, "---------------------------------------------------------\n"); + ptr += 4; + for (j = 0; j < size; j++) { + dev = pread_b(ptr + 2); + op = pread_b(ptr + 3); + seq = (dev & 0x40) >> 6; + cmdstat = (dev & 0x80) >> 7; + fprintf(st, "REQUEST ENTRY %d\n", j); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", dev & 0x3f); + fprintf(st, " Cmd/Stat: %d\n", cmdstat); + fprintf(st, " Seqbit: %d\n", seq); + fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x 0x%08x 0x%08x\n", + pread_w(ptr + 8), pread_w(ptr + 12), pread_w(ptr + 16)); + ptr += CTQRESIZE; + } + } + } else { + ptr = cio[cid].cqp; + size = cio[cid].cqs; + no_rque = 0; /* Not used */ + fprintf(st, "Dumping Completion Queue\n"); + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "EXPRESS ENTRY:\n"); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); + fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); + ptr += CTQCESIZE; + + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / CTQCESIZE); + fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / CTQCESIZE); + fprintf(st, "---------------------------------------------------------\n"); + ptr += 4; + for (i = 0; i < size; i++) { + dev = pread_b(ptr + 2); + op = pread_b(ptr + 3); + seq = (dev & 0x40) >> 6; + cmdstat = (dev & 0x80) >> 7; + fprintf(st, "COMPLETION ENTRY %d\n", i); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", dev & 0x3f); + fprintf(st, " Cmd/Stat: %d\n", cmdstat); + fprintf(st, " Seqbit: %d\n", seq); + fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x 0x%08x\n", + pread_w(ptr + 8), pread_w(ptr + 12)); + ptr += CTQCESIZE; + } + } + + return SCPE_OK; +} diff --git a/3B2/3b2_ctc.h b/3B2/3b2_ctc.h new file mode 100644 index 00000000..ea5e1527 --- /dev/null +++ b/3B2/3b2_ctc.h @@ -0,0 +1,158 @@ +/* 3b2_ctc.h: AT&T 3B2 Model 400 "CTC" feature card + + Copyright (c) 2018, Seth J. Morabito + + 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 AUTHORS OR COPYRIGHT HOLDERS + 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. +*/ + +/* + * CTC is an intelligent feature card for the 3B2 that supports a + * Cipher "FloppyTape(tm)" 525 drive that can read and write 23MB + * DC600A cartridges. + * + * The CTC card is based on the Common I/O (CIO) platform. + * + * Notes: + * ------ + * + * The Cipher FloppyTape is an odd beast. Although it's a tape drive, + * it is controlled by a floppy controller. It is divided into virtual + * sectors that can be addressed by Cylinder / Track / Sector. + * Stepping and head select pulses dictate where on the tape to read + * from or write to. Moreover, System V maps a filesystem onto the + * tape, and a properly formatted tape drive will have a VTOC on + * partition 0. + * + */ + +#ifndef _3B2_CTC_H_ +#define _3B2_CTC_H_ + +#include "3b2_defs.h" +#include "3b2_io.h" + +#define UNIT_V_WLK (DKUF_V_UF + 0) /* Write-locked tape */ +#define UNIT_WLK (1 << UNIT_V_WLK) + +#define CTC_ID 0x0005 +#define CTC_IPL 12 +#define CTC_VERSION 1 + +/* Request Opcodes */ +#define CTC_CONFIG 30 +#define CTC_CLOSE 31 +#define CTC_FORMAT 32 +#define CTC_OPEN 33 +#define CTC_READ 34 +#define CTC_WRITE 35 +#define CTC_VWRITE 36 + +/* Completion Opcodes */ +#define CTC_SUCCESS 0 +#define CTC_HWERROR 32 +#define CTC_RDONLY 33 +#define CTC_NOTREADY 36 +#define CTC_NOMEDIA 42 + +/* VTOC values */ +#define VTOC_VERSION 1 +#define VTOC_SECSZ 512 +#define VTOC_PART 16 /* Number of "partitions" on tape */ +#define VTOC_VALID 0x600DDEEE /* Magic number for valid VTOC */ + +/* Physical Device Info (pdinfo) values */ +#define PD_VALID 0xCA5E600D /* Magic number for valid PDINFO */ +#define PD_DRIVEID 5 +#define PD_VERSION 0 +#define PD_CYLS 6 +#define PD_TRACKS 245 +#define PD_SECTORS 31 +#define PD_BYTES 512 +#define PD_LOGICALST 29 + +#define CTC_CAPACITY (PD_CYLS * PD_TRACKS * PD_SECTORS) /* In blocks */ + +struct partition { + uint16 id; /* Partition ID */ + uint16 flag; /* Permission Flags */ + uint32 sstart; /* Starting Sector */ + uint32 ssize; /* Size in Sectors */ +}; + +struct vtoc { + uint32 bootinfo[3]; /* n/a */ + uint32 sanity; /* magic number */ + uint32 version; /* layout version */ + uint8 volume[8]; /* volume name */ + uint16 sectorsz; /* sector size in bytes */ + uint16 nparts; /* number of partitions */ + uint32 reserved[10]; /* free space */ + struct partition part[VTOC_PART]; /* partition headers */ + uint32 timestamp[VTOC_PART]; /* partition timestamp */ +}; + +struct pdinfo { + uint32 driveid; /* identifies the device type */ + uint32 sanity; /* verifies device sanity */ + uint32 version; /* version number */ + uint8 serial[12]; /* serial number of the device */ + uint32 cyls; /* number of cylinders per drive */ + uint32 tracks; /* number tracks per cylinder */ + uint32 sectors; /* number sectors per track */ + uint32 bytes; /* number of bytes per sector */ + uint32 logicalst; /* sector address of logical sector 0 */ + uint32 errlogst; /* sector address of error log area */ + uint32 errlogsz; /* size in bytes of error log area */ + uint32 mfgst; /* sector address of mfg. defect info */ + uint32 mfgsz; /* size in bytes of mfg. defect info */ + uint32 defectst; /* sector address of the defect map */ + uint32 defectsz; /* size in bytes of defect map */ + uint32 relno; /* number of relocation areas */ + uint32 relst; /* sector address of relocation area */ + uint32 relsz; /* size in sectors of relocation area */ + uint32 relnext; /* address of next avail reloc sector */ +}; + +typedef struct { + uint32 time; /* Time used during a tape session (in 25ms chunks) */ +} CTC_STATE; + +extern DEVICE ctc_dev; + +t_stat ctc_reset(DEVICE *dptr); +t_stat ctc_svc(UNIT *uptr); +t_stat ctc_attach(UNIT *uptr, CONST char *cptr); +t_stat ctc_detach(UNIT *uptr); +void ctc_sysgen(uint8 cid); +void ctc_express(uint8 cid); +void ctc_full(uint8 cid); + +/* Largely here for debugging purposes */ +static t_stat ctc_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat ctc_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +static t_stat ctc_show_queue_common(FILE *st, UNIT *uptr, int32 val, CONST void *desc, t_bool rq); + +#endif /* _3B2_CTC_H_ */ diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index 408d9491..b0fede4c 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -83,10 +83,10 @@ noret __libc_longjmp (jmp_buf buf, int val); #define PHYS_MEM_BASE 0x2000000 #define ROM_BASE 0 -#define IO_BASE 0x40000 -#define IO_SIZE 0x10000 -#define IOB_BASE 0x200000 -#define IOB_SIZE 0x1E00000 + +/* IO area */ +#define IO_BOTTOM 0x40000 +#define IO_TOP 0x50000 /* Register numbers */ #define NUM_FP 9 @@ -140,8 +140,8 @@ noret __libc_longjmp (jmp_buf buf, int val); #define EXECUTE_MSG 0x008 #define INIT_MSG 0x010 #define IRQ_MSG 0x020 -#define IO_D_MSG 0x040 -#define TRACE_MSG 0x080 +#define IO_DBG 0x040 +#define TRACE_DBG 0x080 #define ERR_MSG 0x100 /* Data types operated on by instructions. NB: These integer values @@ -307,33 +307,18 @@ noret __libc_longjmp (jmp_buf buf, int val); #define MEMSIZE_REG 0x4C003 #define CIO_BOTTOM 0x200000 #define CIO_TOP 0x2000000 +#define CIO_SLOTS 12 -#define CIO_CSBIT 0x80 +#define CIO_CMDSTAT 0x80 #define CIO_SEQBIT 0x40 #define CIO_INT_DELAY 8000 - /* Timer definitions */ #define TMR_CLK 0 /* The clock responsible for IPL 15 interrupts */ #define TPS_CLK 100 /* 100 ticks per second */ -#define CLK_MIN_TICKS 500 /* No fewer than 500 sim steps between ticks */ - - -/* TIMING SECTION */ -/* ----------------------------------------------- */ -/* Calculate delays (in simulator steps) for times */ -/* System clock runs at 10MHz; 100ns period. */ - -#define US_PER_INST 1.6 - -#define INST_PER_MS (1000.0 / US_PER_INST) - -#define DELAY_US(us) ((uint32)((us) / US_PER_INST)) -#define DELAY_MS(ms) ((uint32)(((ms) * 1000) / US_PER_INST)) - /* global symbols from the CPU */ extern jmp_buf save_env; @@ -415,6 +400,7 @@ extern void cpu_clear_irq(uint8 ipl, uint16 csr_flags); /* global symbols from the IO system */ extern uint32 io_read(uint32 pa, size_t size); extern void io_write(uint32 pa, uint32 val, size_t size); +extern void cio_clear(uint8 cid); extern void cio_xfer(); extern uint8 cio_int; extern uint16 cio_ipl; diff --git a/3B2/3b2_id.c b/3B2/3b2_id.c index 1dd2b59a..790b9c1f 100644 --- a/3B2/3b2_id.c +++ b/3B2/3b2_id.c @@ -44,27 +44,14 @@ #include "3b2_id.h" -/* Wait times, in CPU steps, for various actions */ - -/* Each step is 50 us in buffered mode */ -#define ID_SEEK_WAIT 100 /* us */ -#define ID_SEEK_BASE 700 /* us */ -#define ID_RECAL_WAIT 6000 /* us */ - -/* Reading data takes about 8ms per sector */ -#define ID_RW_WAIT 8000 /* us */ - -/* Sense Unit Status completes in about 200 us */ -#define ID_SUS_WAIT 200 /* us */ - -/* Specify takes a bit longer, 1.25 ms */ -#define ID_SPEC_WAIT 1250 /* us */ - -/* Sense Interrupt Status is about 142 us */ -#define ID_SIS_WAIT 142 /* us */ - -/* The catch-all command wait time is about 140 us */ -#define ID_CMD_WAIT 140 /* us */ +#define ID_SEEK_WAIT 50 +#define ID_SEEK_BASE 700 +#define ID_RECAL_WAIT 6000 +#define ID_RW_WAIT 1000 +#define ID_SUS_WAIT 200 +#define ID_SPEC_WAIT 1250 +#define ID_SIS_WAIT 142 +#define ID_CMD_WAIT 140 /* Data FIFO pointer - Read */ uint8 id_dpr = 0; @@ -198,10 +185,9 @@ static SIM_INLINE void id_clear_fifo() id_dpw = 0; } -/* TODO: Remove after debugging */ static SIM_INLINE void id_activate(UNIT *uptr, int32 delay) { - sim_activate(uptr, delay); + sim_activate_abs(uptr, delay); } /* @@ -282,7 +268,7 @@ t_stat id_unit_svc(UNIT *uptr) "[%08x]\tINTR\t\tCOMPLETING Recal/Seek SEEK_0 UNIT %d\n", R[NUM_PC], unit); id_seek_state[unit] = ID_SEEK_1; - id_activate(uptr, DELAY_US(8000)); /* TODO: Correct Delay based on steps */ + id_activate(uptr, 8000); /* TODO: Correct Delay based on steps */ break; case ID_SEEK_1: sim_debug(EXECUTE_MSG, &id_dev, @@ -738,7 +724,7 @@ void id_handle_command(uint8 val) "[%08x]\tCOMMAND\t%02x\tSense Int. Status\n", R[NUM_PC], val); id_status &= ~ID_STAT_SRQ; /* SIS immediately de-asserts SRQ */ - id_activate(id_ctlr_unit, DELAY_US(ID_SIS_WAIT)); + id_activate(id_ctlr_unit, ID_SIS_WAIT); break; case ID_CMD_SPEC: sim_debug(WRITE_MSG, &id_dev, @@ -748,19 +734,19 @@ void id_handle_command(uint8 val) id_etn = id_data[3]; id_esn = id_data[4]; id_polling = (id_dtlh & ID_DTLH_POLL) == 0; - id_activate(id_ctlr_unit, DELAY_US(ID_SPEC_WAIT)); + id_activate(id_ctlr_unit, ID_SPEC_WAIT); break; case ID_CMD_SUS: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tSense Unit Status - %d\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_SUS_WAIT)); + id_activate(id_sel_unit, ID_SUS_WAIT); break; case ID_CMD_DERR: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tDetect Error\n", R[NUM_PC], val); - id_activate(id_ctlr_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_ctlr_unit, ID_CMD_WAIT); break; case ID_CMD_RECAL: time = id_cyl[id_unit_num]; @@ -770,12 +756,12 @@ void id_handle_command(uint8 val) sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - POLLING\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(1000)); + id_activate(id_sel_unit, 1000); } else { sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tRecalibrate - %d - NORMAL\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_RECAL_WAIT + (time * ID_SEEK_WAIT))); + id_activate(id_sel_unit, (ID_RECAL_WAIT + (time * ID_SEEK_WAIT))); } break; case ID_CMD_SEEK: @@ -790,12 +776,12 @@ void id_handle_command(uint8 val) sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tSeek - %d - POLLING\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(1000)); + id_activate(id_sel_unit, 4000); } else { sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tSeek - %d - NORMAL\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_SEEK_BASE + (time * ID_SEEK_WAIT))); + id_activate(id_sel_unit, ID_SEEK_BASE + (time * ID_SEEK_WAIT)); } break; case ID_CMD_FMT: @@ -838,7 +824,7 @@ void id_handle_command(uint8 val) id_data[1] = id_scnt; - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_VID: sim_debug(WRITE_MSG, &id_dev, @@ -846,7 +832,7 @@ void id_handle_command(uint8 val) R[NUM_PC], val, id_ua); id_data[0] = 0; id_data[1] = 0x05; /* What do we put here? */ - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_RID: sim_debug(WRITE_MSG, &id_dev, @@ -867,13 +853,13 @@ void id_handle_command(uint8 val) "[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ ID.\n", R[NUM_PC], id_ua); } - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_RDIAG: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tRead Diag - %d\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_RDATA: sim_debug(WRITE_MSG, &id_dev, @@ -895,25 +881,25 @@ void id_handle_command(uint8 val) "[%08x]\tUNIT %d NOT ATTACHED, CANNOT READ DATA.\n", R[NUM_PC], id_ua); } - id_activate(id_sel_unit, DELAY_US(ID_RW_WAIT)); + id_activate(id_sel_unit, ID_RW_WAIT); break; case ID_CMD_CHECK: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tCheck - %d\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_SCAN: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tScan - %d\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_VDATA: sim_debug(WRITE_MSG, &id_dev, "[%08x]\tCOMMAND\t%02x\tVerify Data - %d\n", R[NUM_PC], val, id_ua); - id_activate(id_sel_unit, DELAY_US(ID_CMD_WAIT)); + id_activate(id_sel_unit, ID_CMD_WAIT); break; case ID_CMD_WDATA: sim_debug(WRITE_MSG, &id_dev, @@ -935,7 +921,7 @@ void id_handle_command(uint8 val) "[%08x]\tUNIT %d NOT ATTACHED, CANNOT WRITE.\n", R[NUM_PC], id_ua); } - id_activate(id_sel_unit, DELAY_US(ID_RW_WAIT)); + id_activate(id_sel_unit, ID_RW_WAIT); break; } } diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index e38531a7..8ba0eaf5 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -35,13 +35,11 @@ */ double if_start_time; -#define IF_START_TIME() { if_start_time = sim_gtime(); } -#define IF_DIFF_MS() ((sim_gtime() - if_start_time) / INST_PER_MS) -#ifndef max -#define max(x,y) ((x) > (y) ? (x) : (y)) +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) #endif -#ifndef min -#define min(x,y) ((x) < (y) ? (x) : (y)) +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) #endif /* @@ -59,12 +57,12 @@ double if_start_time; * step is 6ms and head settling time is 30ms. */ -#define IF_STEP_DELAY 6 /* ms */ -#define IF_R_DELAY 85 /* ms */ -#define IF_W_DELAY 90 /* ms */ -#define IF_VERIFY_DELAY 30 /* ms */ -#define IF_HLD_DELAY 80 /* ms */ -#define IF_HSW_DELAY 60 /* ms */ +#define IF_STEP_DELAY 6000 /* us */ +#define IF_R_DELAY 85000 /* us */ +#define IF_W_DELAY 90000 /* us */ +#define IF_VERIFY_DELAY 30000 /* us */ +#define IF_HLD_DELAY 80000 /* us */ +#define IF_HSW_DELAY 60000 /* us */ UNIT if_unit = { UDATA (&if_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ @@ -103,8 +101,7 @@ static SIM_INLINE void if_clear_irq() static SIM_INLINE void if_activate(uint32 delay) { - IF_START_TIME(); - sim_activate_abs(&if_unit, (int32) DELAY_MS(delay)); + sim_activate_abs(&if_unit, delay); } static SIM_INLINE void if_cancel_pending_irq() @@ -131,7 +128,7 @@ t_stat if_svc(UNIT *uptr) if_state.cmd = 0; /* Request an interrupt */ - sim_debug(IRQ_MSG, &if_dev, "\tINTR\t\tDELTA=%f ms\n", IF_DIFF_MS()); + sim_debug(IRQ_MSG, &if_dev, "\tINTR\n"); if_set_irq(); return SCPE_OK; @@ -322,20 +319,20 @@ void if_handle_command() case IF_STEP_T: sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep\n", if_state.cmd); if_activate(IF_STEP_DELAY); - if_state.track = (uint8) min(max((int) if_state.track + if_state.step_dir, 0), 0x4f); + if_state.track = (uint8) MIN(MAX((int) if_state.track + if_state.step_dir, 0), 0x4f); break; case IF_STEP_IN: case IF_STEP_IN_T: sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep In\n", if_state.cmd); if_state.step_dir = IF_STEP_IN_DIR; - if_state.track = (uint8) max((int) if_state.track + if_state.step_dir, 0); + if_state.track = (uint8) MAX((int) if_state.track + if_state.step_dir, 0); if_activate(IF_STEP_DELAY); break; case IF_STEP_OUT: case IF_STEP_OUT_T: sim_debug(EXECUTE_MSG, &if_dev, "\tCOMMAND\t%02x\tStep Out\n", if_state.cmd); if_state.step_dir = IF_STEP_OUT_DIR; - if_state.track = (uint8) min((int) if_state.track + if_state.step_dir, 0x4f); + if_state.track = (uint8) MIN((int) if_state.track + if_state.step_dir, 0x4f); if_activate(IF_STEP_DELAY); break; case IF_SEEK: diff --git a/3B2/3b2_io.c b/3B2/3b2_io.c index 8a12e60f..61f58d70 100644 --- a/3B2/3b2_io.c +++ b/3B2/3b2_io.c @@ -49,15 +49,32 @@ struct iolink iotable[] = { { 0, 0, NULL, NULL} }; +void cio_clear(uint8 cid) +{ + cio[cid].id = 0; + cio[cid].exp_handler = NULL; + cio[cid].full_handler = NULL; + cio[cid].sysgen = NULL; + cio[cid].rqp = 0; + cio[cid].cqp = 0; + cio[cid].rqs = 0; + cio[cid].cqs = 0; + cio[cid].ivec = 0; + cio[cid].no_rque = 0; + cio[cid].ipl = 0; + cio[cid].intr = FALSE; + cio[cid].sysgen_s = 0; + cio[cid].seqbit = 0; + cio[cid].op = 0; +} + void cio_sysgen(uint8 cid) { uint32 sysgen_p; - uint32 cq_exp; - cio_entry cqe; sysgen_p = pread_w(SYSGEN_PTR); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[%08x] [SYSGEN] Starting sysgen for card %d. sysgen_p=%08x\n", R[NUM_PC], cid, sysgen_p); @@ -71,75 +88,68 @@ void cio_sysgen(uint8 cid) cio[cid].ivec = pread_b(sysgen_p + 10); cio[cid].no_rque = pread_b(sysgen_p + 11); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[SYSGEN] sysgen rqp = %08x\n", cio[cid].rqp); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[SYSGEN] sysgen cqp = %08x\n", cio[cid].cqp); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[SYSGEN] sysgen rqs = %02x\n", cio[cid].rqs); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[SYSGEN] sysgen cqs = %02x\n", cio[cid].cqs); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[SYSGEN] sysgen ivec = %02x\n", cio[cid].ivec); - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[SYSGEN] sysgen no_rque = %02x\n", cio[cid].no_rque); - cq_exp = cio[cid].cqp; - - cqe.byte_count = 0; - cqe.subdevice = 0; - cqe.opcode = 3; - cqe.address = 0; - cqe.app_data = 0; - - cio_cexpress(cid, &cqe); - sim_debug(IO_D_MSG, &cpu_dev, - "[SYSGEN] Sysgen complete. Completion Queue written.\n"); - /* If the card has a custom sysgen handler, run it */ if (cio[cid].sysgen != NULL) { cio[cid].sysgen(cid); } else { - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[%08x] [cio_sysgen] Not running custom sysgen.\n", R[NUM_PC]); } } -void cio_cexpress(uint8 cid, cio_entry *cqe) +void cio_cexpress(uint8 cid, uint16 esize, cio_entry *cqe, uint8 *app_data) { - uint32 cqp; + uint32 cqp, i; cqp = cio[cid].cqp; - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[%08x] [cio_cexpress] cqp = %08x seqbit = %d\n", R[NUM_PC], cqp, cio[cid].seqbit); cio[cid].seqbit ^= 1; - if (cio[cid].seqbit) { - cqe->subdevice |= CIO_SEQBIT; - } + cqe->subdevice |= (cio[cid].seqbit << 6); pwrite_h(cqp, cqe->byte_count); pwrite_b(cqp + 2, cqe->subdevice); pwrite_b(cqp + 3, cqe->opcode); pwrite_w(cqp + 4, cqe->address); - pwrite_w(cqp + 8, cqe->app_data); + + /* Write application-specific data. */ + for (i = 0; i < (esize - QESIZE); i++) { + pwrite_b(cqp + 8 + i, app_data[i]); + } } -/* Write an entry into the Completion Queue */ -void cio_cqueue(uint8 cid, cio_entry *cqe) +void cio_cqueue(uint8 cid, uint8 cmd_stat, uint16 esize, + cio_entry *cqe, uint8 *app_data) { - uint32 cqp, top; - uint16 lp; + uint32 cqp, top, i; + uint16 lp, ulp; + + /* Apply the CMD/STAT bit */ + cqe->subdevice |= (cmd_stat << 7); /* Get the physical address of the completion queue * in main memory */ @@ -147,90 +157,163 @@ void cio_cqueue(uint8 cid, cio_entry *cqe) /* Get the physical address of the first entry in * the completion queue */ - top = cqp + QUE_OFFSET; + top = cqp + esize + LUSIZE; /* Get the load pointer. This is a 16-bit absolute offset * from the top of the queue to the start of the entry. */ - lp = pread_h(cqp + LOAD_OFFSET); + lp = pread_h(cqp + esize); + ulp = pread_h(cqp + esize + 2); /* Load the entry at the supplied address */ pwrite_h(top + lp, cqe->byte_count); pwrite_b(top + lp + 2, cqe->subdevice); pwrite_b(top + lp + 3, cqe->opcode); pwrite_w(top + lp + 4, cqe->address); - pwrite_w(top + lp + 8, cqe->app_data); + + /* Write application-specific data. */ + for (i = 0; i < (esize - QESIZE); i++) { + pwrite_b(top + lp + 8 + i, app_data[i]); + } /* Increment the load pointer to the next queue location. * If we go past the end of the queue, wrap around to the * start of the queue */ if (cio[cid].cqs > 0) { - lp = (lp + QUE_E_SIZE) % (QUE_E_SIZE * cio[cid].cqs); + lp = (lp + esize) % (esize * cio[cid].cqs); /* Store it back to the correct location */ - pwrite_h(cqp + LOAD_OFFSET, lp); + pwrite_h(cqp + esize, lp); } else { - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[%08x] [cio_cqueue] ERROR! Completion Queue Size is 0!", R[NUM_PC]); } - } -/* Retrieve an entry from the Request Queue */ -void cio_rqueue(uint8 cid, cio_entry *cqe) +/* + * Retrieve the Express Entry from the Request Queue + */ +void cio_rexpress(uint8 cid, uint16 esize, cio_entry *rqe, uint8 *app_data) +{ + uint32 rqp, i; + + rqp = cio[cid].rqp; + + /* Unload the express entry from the request queue */ + rqe->byte_count = pread_h(rqp); + rqe->subdevice = pread_b(rqp + 2); + rqe->opcode = pread_b(rqp + 3); + rqe->address = pread_w(rqp + 4); + + for (i = 0; i < (esize - QESIZE); i++) { + app_data[i] = pread_b(rqp + 8 + i); + } +} + +/* + * Retrieve an entry from the Request Queue. This function + * returns the load pointer that points to the NEXT available slot. + * This may be used by callers to determine which queue(s) need to + * be serviced. + * + * Returns SCPE_OK on success, or SCPE_NXM if no entry was found. + */ +t_stat cio_rqueue(uint8 cid, uint8 qnum, uint16 esize, + cio_entry *rqe, uint8 *app_data) { uint32 rqp, top, i; - uint16 ulp; + uint16 lp, ulp; /* Get the physical address of the request queue in main memory */ - rqp = cio[cid].rqp + 12; /* Skip past the Express Queue Entry */ + rqp = cio[cid].rqp + + esize + + (qnum * (LUSIZE + (esize * cio[cid].rqs))); - /* Scan each queue until we find one with a command in it. */ - for (i = 0; i < cio[cid].no_rque; i++) { - /* Get the physical address of the first entry in the request - * queue */ - top = rqp + 4; + lp = pread_h(rqp); + ulp = pread_h(rqp + 2); - /* Check to see what we've got in the queue. */ - ulp = pread_h(rqp + 2); - - cqe->opcode = pread_b(top + ulp + 3); - - if (cqe->opcode > 0) { - break; - } - - rqp += 4 + (12 * cio[cid].rqs); + if (lp == ulp) { + return SCPE_NXM; } - if (i >= cio[cid].no_rque) { - sim_debug(IO_D_MSG, &cpu_dev, - "[%08x] [cio_rque] FAILURE! NO MORE QUEUES TO EXAMINE.\n", - R[NUM_PC]); - return; - } + top = rqp + LUSIZE; /* Retrieve the entry at the supplied address */ - cqe->byte_count = pread_h(top + ulp); - cqe->subdevice = pread_b(top + ulp + 2); - cqe->address = pread_w(top + ulp + 4); - cqe->app_data = pread_w(top + ulp + 8); + rqe->byte_count = pread_h(top + ulp); + rqe->subdevice = pread_b(top + ulp + 2); + rqe->opcode = pread_b(top + ulp + 3); + rqe->address = pread_w(top + ulp + 4); - dump_entry("REQUEST", cqe); + /* Read application-specific data. */ + for (i = 0; i < (esize - QESIZE); i++) { + app_data[i] = pread_b(top + ulp + 8 + i); + } /* Increment the unload pointer to the next queue location. If we * go past the end of the queue, wrap around to the start of the * queue */ if (cio[cid].rqs > 0) { - ulp = (ulp + QUE_E_SIZE) % (QUE_E_SIZE * cio[cid].rqs); - - /* Store it back to the correct location */ + ulp = (ulp + esize) % (esize * cio[cid].rqs); pwrite_h(rqp + 2, ulp); - } else { - sim_debug(IO_D_MSG, &cpu_dev, - "[%08x] [cio_rqueue] ERROR! Request Queue Size is 0!", - R[NUM_PC]); } + + return SCPE_OK; +} + +/* + * Return the Load Pointer for the given request queue + */ +uint16 cio_r_lp(uint8 cid, uint8 qnum, uint16 esize) +{ + uint32 rqp; + + rqp = cio[cid].rqp + + esize + + (qnum * (LUSIZE + (esize * cio[cid].rqs))); + + return pread_h(rqp); +} + +/* + * Return the Unload Pointer for the given request queue + */ +uint16 cio_r_ulp(uint8 cid, uint8 qnum, uint16 esize) +{ + uint32 rqp; + + rqp = cio[cid].rqp + + esize + + (qnum * (LUSIZE + (esize * cio[cid].rqs))); + + return pread_h(rqp + 2); +} + +uint16 cio_c_lp(uint8 cid, uint16 esize) +{ + uint32 cqp; + cqp = cio[cid].cqp + esize; + return pread_h(cqp); +} + +uint16 cio_c_ulp(uint8 cid, uint16 esize) +{ + uint32 cqp; + cqp = cio[cid].cqp + esize; + return pread_h(cqp + 2); +} + +/* + * Returns true if there is room in the completion queue + * for a new entry. + */ +t_bool cio_cqueue_avail(uint cid, uint16 esize) +{ + uint32 lp, ulp; + + lp = pread_h(cio[cid].cqp + esize); + ulp = pread_h(cio[cid].cqp + esize + 2); + + return(((lp + esize) % (cio[cid].cqs * esize)) != ulp); } uint32 io_read(uint32 pa, size_t size) @@ -241,7 +324,7 @@ uint32 io_read(uint32 pa, size_t size) /* Special devices */ if (pa == MEMSIZE_REG) { - /* It appears that the following values map to memory sizes: + /* The following values map to memory sizes: 0x00: 512KB ( 524,288 B) 0x01: 2MB (2,097,152 B) 0x02: 1MB (1,048,576 B) @@ -261,13 +344,16 @@ uint32 io_read(uint32 pa, size_t size) } } - /* IO Board Area - Unimplemented */ + /* CIO board area */ if (pa >= CIO_BOTTOM && pa < CIO_TOP) { cid = CID(pa); reg = pa - CADDR(cid); if (cio[cid].id == 0) { /* Nothing lives here */ + sim_debug(IO_DBG, &cpu_dev, + "[READ] [%08x] No card at cid=%d reg=%d\n", + R[NUM_PC], cid, reg); csr_data |= CSRTIMO; cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; @@ -282,10 +368,11 @@ uint32 io_read(uint32 pa, size_t size) switch (reg) { case IOF_ID: case IOF_VEC: - switch(cio[cid].cmdbits) { - case 0x00: /* We've never seen an INT0 or INT1 */ - case 0x01: /* We've seen an INT0 but not an INT1. */ - sim_debug(IO_D_MSG, &cpu_dev, + switch(cio[cid].sysgen_s) { + case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ + case CIO_INT0: /* We've seen an INT0 but not an INT1. */ + cio[cid].sysgen_s |= CIO_INT0; + sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] (%d INT0) ID\n", R[NUM_PC], cid); /* Return the correct byte of our board ID */ @@ -295,15 +382,17 @@ uint32 io_read(uint32 pa, size_t size) data = (cio[cid].id & 0xff); } break; - case 0x02: /* We've seen an INT1 but not an INT0. Time to sysgen */ - sim_debug(IO_D_MSG, &cpu_dev, + case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */ + cio[cid].sysgen_s |= CIO_INT0; + sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] (%d INT0) SYSGEN\n", R[NUM_PC], cid); cio_sysgen(cid); data = cio[cid].ivec; break; - case 0x03: /* We've already sysgen'ed */ - sim_debug(IO_D_MSG, &cpu_dev, + case CIO_SYSGEN: /* We've already sysgen'ed */ + cio[cid].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */ + sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] (%d INT0) EXPRESS JOB\n", R[NUM_PC], cid); cio[cid].exp_handler(cid); @@ -312,55 +401,60 @@ uint32 io_read(uint32 pa, size_t size) default: /* This should never happen */ stop_reason = STOP_ERR; - sim_debug(IO_D_MSG, &cpu_dev, - "[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE cmdbits=%02x\n", - R[NUM_PC], cid, cio[cid].cmdbits); + sim_debug(IO_DBG, &cpu_dev, + "[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", + R[NUM_PC], cid, cio[cid].sysgen_s); data = 0; break; } - /* Record that we've seen an INT0 */ - cio[cid].cmdbits |= CIO_INT0; return data; case IOF_CTRL: - switch(cio[cid].cmdbits) { - case 0x00: /* We've never seen an INT0 or INT1 */ - case 0x02: /* We've seen an INT1 but not an INT0 */ + switch(cio[cid].sysgen_s) { + case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ + case CIO_INT1: /* We've seen an INT1 but not an INT0 */ /* There's nothing to do in this instance */ + sim_debug(IO_DBG, &cpu_dev, + "[READ] [%08x] (%d INT1) IGNORED\n", + R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT1; break; - case 0x01: /* We've seen an INT0 but not an INT1. Time to sysgen */ - sim_debug(IO_D_MSG, &cpu_dev, + case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */ + sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] (%d INT1) SYSGEN\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT1; cio_sysgen(cid); break; - case 0x03: /* We've already sysgen'ed */ - sim_debug(IO_D_MSG, &cpu_dev, + case CIO_SYSGEN: /* We've already sysgen'ed */ + sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] (%d INT1) FULL\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */ cio[cid].full_handler(cid); break; default: /* This should never happen */ stop_reason = STOP_ERR; - sim_debug(IO_D_MSG, &cpu_dev, - "[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE cmdbits=%02x\n", - R[NUM_PC], cid, cio[cid].cmdbits); + sim_debug(IO_DBG, &cpu_dev, + "[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", + R[NUM_PC], cid, cio[cid].sysgen_s); break; } - /* Record that we've seen an INT1 */ - cio[cid].cmdbits |= CIO_INT1; return 0; /* Data returned is arbitrary */ case IOF_STAT: - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[READ] [%08x] (%d RESET)\n", R[NUM_PC], cid); - cio[cid].cmdbits = 0; + cio[cid].sysgen_s = 0; return 0; /* Data returned is arbitrary */ default: /* We should never reach here, but if we do, there's * nothing listening. */ + sim_debug(IO_DBG, &cpu_dev, + "[READ] [%08x] No card at cid=%d reg=%d\n", + R[NUM_PC], cid, reg); csr_data |= CSRTIMO; cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return 0; @@ -375,7 +469,7 @@ uint32 io_read(uint32 pa, size_t size) } /* Not found. */ - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[%08x] [io_read] ADDR=%08x: No device found.\n", R[NUM_PC], pa); csr_data |= CSRTIMO; @@ -395,6 +489,9 @@ void io_write(uint32 pa, uint32 val, size_t size) if (cio[cid].id == 0) { /* Nothing lives here */ + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] No card at cid=%d reg=%d\n", + R[NUM_PC], cid, reg); csr_data |= CSRTIMO; cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; @@ -409,79 +506,84 @@ void io_write(uint32 pa, uint32 val, size_t size) switch (reg) { case IOF_ID: case IOF_VEC: - switch(cio[cid].cmdbits) { - case 0x00: /* We've never seen an INT0 or INT1 */ - case 0x01: /* We've seen an INT0 but not an INT1. */ - sim_debug(IO_D_MSG, &cpu_dev, + switch(cio[cid].sysgen_s) { + case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ + case CIO_INT0: /* We've seen an INT0 but not an INT1. */ + sim_debug(IO_DBG, &cpu_dev, "[WRITE] [%08x] (%d INT0) ID\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT0; break; - case 0x02: /* We've seen an INT1 but not an INT0. Time to sysgen */ - sim_debug(IO_D_MSG, &cpu_dev, - "[READ] [%08x] (%d INT0) SYSGEN\n", + case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */ + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] (%d INT0) SYSGEN\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT0; cio_sysgen(cid); break; - case 0x03: /* We've already sysgen'ed */ - sim_debug(IO_D_MSG, &cpu_dev, - "[READ] [%08x] (%d INT0) EXPRESS JOB\n", + case CIO_SYSGEN: /* We've already sysgen'ed */ + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] (%d INT0) EXPRESS JOB\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT0; cio[cid].exp_handler(cid); break; default: /* This should never happen */ stop_reason = STOP_ERR; - sim_debug(IO_D_MSG, &cpu_dev, - "[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE cmdbits=%02x\n", - R[NUM_PC], cid, cio[cid].cmdbits); + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n", + R[NUM_PC], cid, cio[cid].sysgen_s); break; } - /* Record that we've seen an INT0 */ - cio[cid].cmdbits |= CIO_INT0; return; case IOF_CTRL: - switch(cio[cid].cmdbits) { - case 0x00: /* We've never seen an INT0 or INT1 */ - case 0x02: /* We've seen an INT1 but not an INT0 */ + switch(cio[cid].sysgen_s) { + case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */ + case CIO_INT1: /* We've seen an INT1 but not an INT0 */ /* There's nothing to do in this instance */ - sim_debug(IO_D_MSG, &cpu_dev, - "[WRITE] [%08x] (%d INT1)\n", + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] (%d INT1) IGNORED\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT1; break; - case 0x01: /* We've seen an INT0 but not an INT1. Time to sysgen */ - sim_debug(IO_D_MSG, &cpu_dev, + case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */ + sim_debug(IO_DBG, &cpu_dev, "[WRITE] [%08x] (%d INT1) SYSGEN\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT1; cio_sysgen(cid); break; - case 0x03: /* We've already sysgen'ed */ - sim_debug(IO_D_MSG, &cpu_dev, + case CIO_SYSGEN: /* We've already sysgen'ed */ + sim_debug(IO_DBG, &cpu_dev, "[WRITE] [%08x] (%d INT1) FULL\n", R[NUM_PC], cid); + cio[cid].sysgen_s |= CIO_INT1; cio[cid].full_handler(cid); break; default: /* This should never happen */ stop_reason = STOP_ERR; - sim_debug(IO_D_MSG, &cpu_dev, - "[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE cmdbits=%02x\n", - R[NUM_PC], cid, cio[cid].cmdbits); + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n", + R[NUM_PC], cid, cio[cid].sysgen_s); break; } - /* Record that we've seen an INT1 */ - cio[cid].cmdbits |= CIO_INT1; return; case IOF_STAT: - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[WRITE] [%08x] (%d RESET)\n", R[NUM_PC], cid); - cio[cid].cmdbits = 0; + cio[cid].sysgen_s = 0; return; default: /* We should never reach here, but if we do, there's * nothing listening. */ + sim_debug(IO_DBG, &cpu_dev, + "[WRITE] [%08x] No card at cid=%d reg=%d\n", + R[NUM_PC], cid, reg); csr_data |= CSRTIMO; cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT); return; @@ -497,7 +599,7 @@ void io_write(uint32 pa, uint32 val, size_t size) } /* Not found. */ - sim_debug(IO_D_MSG, &cpu_dev, + sim_debug(IO_DBG, &cpu_dev, "[%08x] [io_write] ADDR=%08x: No device found.\n", R[NUM_PC], pa); csr_data |= CSRTIMO; @@ -506,11 +608,13 @@ void io_write(uint32 pa, uint32 val, size_t size) /* For debugging only */ -void dump_entry(CONST char *type, cio_entry *entry) +void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type, + uint16 esize, cio_entry *entry, uint8 *app_data) { - sim_debug(IO_D_MSG, &cpu_dev, - "*** %s ENTRY: byte_count=%04x, subdevice=%02x,\n" - " opcode=%d, address=%08x, app_data=%08x\n", + uint32 i; + + sim_debug(dbits, dev, + "*** %s ENTRY: byte_count=%04x, subdevice=%02x, opcode=%d, address=%08x\n", type, entry->byte_count, entry->subdevice, - entry->opcode, entry->address, entry->app_data); + entry->opcode, entry->address); } diff --git a/3B2/3b2_io.h b/3B2/3b2_io.h index 660ef850..1fbc65ec 100644 --- a/3B2/3b2_io.h +++ b/3B2/3b2_io.h @@ -79,19 +79,33 @@ * * The Queue structures (one for request, one for completion) hold: * - An express entry + * + * And then one or more queues, each queue consiting of * - A set of pointers for load and unload from the queue - * - Zero or more Queue Entries + * - One or more Queue Entries * * | Address | Size | Contents | * +---------------+------+-----------------------------------------+ * | QUEUE_P | 12 | Express Queue Entry [1] | - * | QUEUE_P + 12 | 2 | Load Pointer | - * | QUEUE_P + 14 | 2 | Unload Pointer | - * | QUEUE_P + 16 | 12 | Entry 0 [1] | - * | QUEUE_P + 28 | 12 | Entry 1 [1] | + * +---------------+------+-----------------------------------------+ + * | QUEUE_P + 12 | 2 | Load Pointer for Queue 0 | + * | QUEUE_P + 14 | 2 | Unload Pointer for Queue 0 | + * | QUEUE_P + 16 | 12 | Queue 0 Entry 0 [1] | + * | QUEUE_P + 28 | 12 | Queue 0 Entry 1 [1] | + * | ... | ... | ... | + * +---------------+------+-----------------------------------------+ + * | QUEUE_P + n | 2 | Load Pointer for Queue 1 | + * | QUEUE_P + n | 2 | Unload Pointer for Queue 1 | + * | QUEUE_P + n | 12 | Queue 1 Entry 0 [1] | + * | QUEUE_P + n | 12 | Queue 1 Entry 1 [1] | * | ... | ... | ... | * * [1] See Queue Entry above + * + * NB: There are multiple Request queues, usually one per subdevice, + * and EACH Request queue starts with a Load Pointer, an Unload + * Pointer, and then 'n' Queue Entries. + * */ #ifndef _3B2_IO_H_ @@ -111,34 +125,33 @@ #define IOF_CTRL 3 #define IOF_STAT 5 -#define SYSGEN_PTR PHYS_MEM_BASE -#define CIO_LOAD_SIZE 0x4 -#define CIO_ENTRY_SIZE 0x0c -#define CIO_QUE_OFFSET 0x10 -#define CIO_SLOTS 12 +#define SYSGEN_PTR PHYS_MEM_BASE /* CIO opcodes */ -#define CIO_DLM 1 -#define CIO_ULM 2 -#define CIO_FCF 3 -#define CIO_DOS 4 -#define CIO_DSD 5 +#define CIO_DLM 1 +#define CIO_ULM 2 +#define CIO_FCF 3 +#define CIO_DOS 4 +#define CIO_DSD 5 /* Map a physical address to a card ID */ #define CID(pa) (((((pa) >> 0x14) & 0x1f) / 2) - 1) /* Map a card ID to a base address */ #define CADDR(bid) (((((bid) + 1) * 2) << 0x14)) -#define CIO_INT0 0x1 -#define CIO_INT1 0x2 - /* Offsets into the request/completion queues of various values */ -#define LOAD_OFFSET 12 -#define ULOAD_OFFSET 14 -#define QUE_OFFSET 16 -#define QUE_E_SIZE 12 +#define LUSIZE 4 /* Load/Unload pointers size */ +#define QESIZE 8 /* Queue entry is 8 bytes + application data */ + +#define CIO_STAT 0 +#define CIO_CMD 1 + +/* Sysgen State */ +#define CIO_INT_NONE 0 +#define CIO_INT0 1 +#define CIO_INT1 2 +#define CIO_SYSGEN 3 -#define CIO_SYGEN_MASK 0x3 typedef struct { uint16 id; /* Card ID */ @@ -153,10 +166,9 @@ typedef struct { uint8 no_rque; /* Number of request queues */ uint8 ipl; /* IPL that this card uses */ t_bool intr; /* Card needs to interrupt */ - uint8 cmdbits; /* Commands received since RESET */ + uint8 sysgen_s; /* Sysgen state */ uint8 seqbit; /* Squence Bit */ uint8 op; /* Last received opcode */ - TMLN *lines[4]; /* Terminal Multiplexer lines */ } CIO_STATE; typedef struct { @@ -164,7 +176,6 @@ typedef struct { uint8 subdevice; uint8 opcode; uint32 address; - uint32 app_data; } cio_entry; struct iolink { @@ -208,15 +219,21 @@ extern CIO_STATE cio[CIO_SLOTS]; t_stat cio_reset(DEVICE *dptr); t_stat cio_svc(UNIT *uptr); -/* Put an entry into the Completion Queue's Express entry */ -void cio_cexpress(uint8 cid, cio_entry *cqe); -/* Put an entry into the Completion Queue */ -void cio_cqueue(uint8 cid, cio_entry *cqe); -/* Get an entry from the Request Queue */ -void cio_rqueue(uint8 cid, cio_entry *cqe); -/* Perform a Sysgen */ +void cio_clear(uint8 cid); +void cio_cexpress(uint8 cid, uint16 esize, cio_entry *cqe, uint8 *app_data); +void cio_cqueue(uint8 cid, uint8 cmd_stat, uint16 esize, + cio_entry *cqe, uint8 *app_data); +void cio_rexpress(uint8 cid, uint16 esize, cio_entry *rqe, uint8 *app_data); +t_stat cio_rqueue(uint8 cid, uint8 qnum, uint16 esize, + cio_entry *rqe, uint8 *app_data); +t_bool cio_cqueue_avail(uint cid, uint16 esize); +uint16 cio_r_lp(uint8 cid, uint8 qnum, uint16 esize); +uint16 cio_r_ulp(uint8 cid, uint8 qnum, uint16 esize); +uint16 cio_c_lp(uint8 cid, uint16 esize); +uint16 cio_c_ulp(uint8 cid, uint16 esize); void cio_sysgen(uint8 cid); -/* Debugging only */ -void dump_entry(CONST char *type, cio_entry *entry); + +void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type, + uint16 esize, cio_entry *entry, uint8 *app_data); #endif diff --git a/3B2/3b2_iu.c b/3B2/3b2_iu.c index a68bd412..75e5dd5b 100644 --- a/3B2/3b2_iu.c +++ b/3B2/3b2_iu.c @@ -324,7 +324,7 @@ t_stat tti_reset(DEVICE *dptr) /* Start the Console TTI polling loop */ if (!sim_is_active(&tti_unit)) { - sim_activate(&tti_unit, tti_unit.wait); + sim_activate_after(&tti_unit, tti_unit.wait); } return SCPE_OK; @@ -347,6 +347,10 @@ t_stat contty_reset(DEVICE *dtpr) memset(&iu_state, 0, sizeof(IU_STATE)); memset(&iu_contty, 0, sizeof(IU_PORT)); + + /* DCD is off (inverted logic, 1 means off) */ + iu_state.inprt |= IU_DCDB; + brg_reg = 0; brg_clk = BRG_DEFAULT; parity_sel = IU_PARITY_EVEN; @@ -361,7 +365,7 @@ t_stat contty_reset(DEVICE *dtpr) /* Start the CONTTY polling loop */ if (!sim_is_active(contty_rcv_unit)) { - sim_activate(contty_rcv_unit, contty_rcv_unit->wait); + sim_activate_after(contty_rcv_unit, contty_rcv_unit->wait); } return SCPE_OK; @@ -393,7 +397,7 @@ t_stat iu_svc_tti(UNIT *uptr) */ if ((temp = sim_poll_kbd()) < SCPE_KFLAG) { - return temp; + return temp; } if (iu_console.conf & RX_EN) { @@ -438,22 +442,26 @@ t_stat iu_svc_contty_rcv(UNIT *uptr) return SCPE_OK; } - ln = tmxr_poll_conn(&contty_desc); - if (ln >= 0) { + /* Check for connect */ + if ((ln = tmxr_poll_conn(&contty_desc)) >= 0) { contty_ldsc[ln].rcve = 1; - /* Set DCD */ iu_state.inprt &= ~(IU_DCDB); iu_state.ipcr |= IU_DCDB; - /* Cause an interrupt */ csr_data |= CSRUART; } - tmxr_poll_rx(&contty_desc); + /* Check for disconnect */ + if (!contty_ldsc[0].conn && (iu_state.inprt & IU_DCDB) == 0) { + contty_ldsc[0].rcve = 0; + iu_state.inprt |= IU_DCDB; + iu_state.ipcr |= IU_DCDB; + csr_data |= CSRUART; + } else if (iu_contty.conf & RX_EN) { + tmxr_poll_rx(&contty_desc); - if (contty_ldsc[0].conn) { - temp = tmxr_getc_ln(&contty_ldsc[0]); - if (temp && !(temp & SCPE_BREAK)) { - if (iu_contty.conf & RX_EN) { + if (contty_ldsc[0].conn) { + temp = tmxr_getc_ln(&contty_ldsc[0]); + if (temp && !(temp & SCPE_BREAK)) { if ((iu_contty.stat & STS_FFL) == 0) { iu_contty.rxbuf[iu_contty.w_p] = (temp & 0xff); iu_contty.w_p = (iu_contty.w_p + 1) % IU_BUF_SIZE; @@ -530,7 +538,7 @@ t_stat iu_svc_timer(UNIT *uptr) uint32 iu_read(uint32 pa, size_t size) { uint8 reg, modep; - uint32 data, delay; + uint32 data; reg = (uint8) (pa - IUBASE); @@ -544,15 +552,17 @@ uint32 iu_read(uint32 pa, size_t size) data = iu_console.stat; break; case RHRA: - data = iu_console.rxbuf[iu_console.r_p]; - iu_console.r_p = (iu_console.r_p + 1) % IU_BUF_SIZE; - /* If the FIFO is not empty, we must cause another interrupt - * to continue reading */ - if (iu_console.r_p == iu_console.w_p) { - iu_console.stat &= ~(STS_RXR|STS_FFL); - iu_state.istat &= ~ISTS_RAI; - } else { - csr_data |= CSRUART; + if (iu_console.conf & RX_EN) { + data = iu_console.rxbuf[iu_console.r_p]; + iu_console.r_p = (iu_console.r_p + 1) % IU_BUF_SIZE; + /* If the FIFO is not empty, we must cause another interrupt + * to continue reading */ + if (iu_console.r_p == iu_console.w_p) { + iu_console.stat &= ~(STS_RXR|STS_FFL); + iu_state.istat &= ~ISTS_RAI; + } else if (iu_state.imr & IMR_RXRA) { + csr_data |= CSRUART; + } } break; case IPCR: @@ -579,15 +589,17 @@ uint32 iu_read(uint32 pa, size_t size) data = iu_contty.stat; break; case RHRB: - data = iu_contty.rxbuf[iu_contty.r_p]; - iu_contty.r_p = (iu_contty.r_p + 1) % IU_BUF_SIZE; - /* If the FIFO is not empty, we must cause another interrupt - * to continue reading */ - if (iu_contty.r_p == iu_contty.w_p) { - iu_contty.stat &= ~(STS_RXR|STS_FFL); - iu_state.istat &= ~ISTS_RBI; - } else { - csr_data |= CSRUART; + if (iu_contty.conf & RX_EN) { + data = iu_contty.rxbuf[iu_contty.r_p]; + iu_contty.r_p = (iu_contty.r_p + 1) % IU_BUF_SIZE; + /* If the FIFO is not empty, we must cause another interrupt + * to continue reading */ + if (iu_contty.r_p == iu_contty.w_p) { + iu_contty.stat &= ~(STS_RXR|STS_FFL); + iu_state.istat &= ~ISTS_RBI; + } else if (iu_state.imr & IMR_RXRB) { + csr_data |= CSRUART; + } } break; case INPRT: @@ -596,8 +608,7 @@ uint32 iu_read(uint32 pa, size_t size) case START_CTR: data = 0; iu_state.istat &= ~ISTS_CRI; - delay = (uint32) (IU_TIMER_STP * iu_timer_state.c_set); - sim_activate_abs(&iu_timer_unit, (int32) DELAY_US(delay)); + sim_activate_abs(&iu_timer_unit, iu_timer_state.c_set * IU_TIMER_RATE); break; case STOP_CTR: data = 0; @@ -607,12 +618,6 @@ uint32 iu_read(uint32 pa, size_t size) break; case 17: /* Clear DMAC interrupt */ data = 0; - /* - iu_console.drq = FALSE; - iu_console.dma = FALSE; - iu_contty.drq = FALSE; - iu_contty.dma = FALSE; - */ csr_data &= ~CSRDMA; break; default: @@ -639,7 +644,18 @@ void iu_write(uint32 pa, uint32 val, size_t size) iu_increment_a = TRUE; break; case CSRA: - /* Set baud rate - not yet implemented on console */ + if (brg_rates[brg_reg][brg_clk] != NULL) { + sprintf(line_config, "%s-%d%s1", + brg_rates[brg_reg][brg_clk], + bits_per_char, + parity[parity_sel]); + + sim_debug(EXECUTE_MSG, &contty_dev, + "Setting CONTTY line to %s\n", + line_config); + + tmxr_set_config_line(&contty_ldsc[0], line_config); + } break; case CRA: /* Command A */ iu_w_cmd(PORT_A, bval); @@ -756,8 +772,8 @@ t_stat iu_tx(uint8 portno, uint8 val) } p->stat |= STS_RXR; - iu_state.istat |= ists; if (iu_state.imr & imr_mask) { + iu_state.istat |= ists; csr_data |= CSRUART; } @@ -775,7 +791,7 @@ t_stat iu_tx(uint8 portno, uint8 val) sim_debug(EXECUTE_MSG, &tto_dev, "[iu_tx] CONSOLE transmit %02x (%c)\n", (uint8) c, (char) c); - status = sim_putchar(c); + status = sim_putchar_s(c); } else { sim_debug(EXECUTE_MSG, &contty_dev, "[iu_tx] CONTTY transmit %02x (%c)\n", diff --git a/3B2/3b2_iu.h b/3B2/3b2_iu.h index 187bbc15..1da96755 100644 --- a/3B2/3b2_iu.h +++ b/3B2/3b2_iu.h @@ -140,28 +140,9 @@ extern DEVICE tto_dev; extern DEVICE contty_dev; extern DEVICE iu_timer_dev; -extern int32 tmxr_poll; - #define IUBASE 0x49000 #define IUSIZE 0x100 -/* The UART is driven by a 3.6864 MHz crystal. This is divided by 16 - to clock the timer. (One peculiarity: 3.6864 MHz /16 is 230400 Hz, - but the SVR3 source code claims the /16 clock is actually 230525 - Hz. So, we'll go with 230525 Hz until proven otherwise.) - - UART clock period = 4338ns - System clock period = 100ns - - That means the system ticks 43.3792 times for every one tick of the - UART clock. - - But this is a simulated system, where each simulator step is - CYCLES_PER_INST long. So we take that into account. -*/ - -#define IU_TIMER_STP 4.33792 - #define IU_BUF_SIZE 3 #define IU_DCDA 0x01 @@ -177,6 +158,8 @@ extern int32 tmxr_poll; /* Default baud rate generator (9600 baud) */ #define BRG_DEFAULT 11 +#define IU_TIMER_RATE 2.114 /* microseconds per step */ + typedef struct iu_port { uint8 stat; /* Port Status */ diff --git a/3B2/3b2_mmu.c b/3B2/3b2_mmu.c index 5669dd97..d6b9e5b9 100644 --- a/3B2/3b2_mmu.c +++ b/3B2/3b2_mmu.c @@ -236,8 +236,8 @@ t_bool addr_is_mem(uint32 pa) t_bool addr_is_io(uint32 pa) { - return ((pa >= IO_BASE && pa < IO_BASE + IO_SIZE) || - (pa >= IOB_BASE && pa < IOB_BASE + IOB_SIZE)); + return ((pa >= IO_BOTTOM && pa < IO_TOP) || + (pa >= CIO_BOTTOM && pa < CIO_TOP)); } /* @@ -631,7 +631,7 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) uint8 pd_acc; t_stat sd_cached, pd_cached; - if (!mmu_state.enabled || addr_is_io(va)) { + if (!mmu_state.enabled) { *pa = va; return SCPE_OK; } diff --git a/3B2/3b2_ports.c b/3B2/3b2_ports.c new file mode 100644 index 00000000..281e5df3 --- /dev/null +++ b/3B2/3b2_ports.c @@ -0,0 +1,964 @@ +/* 3b2_ports.c: AT&T 3B2 Model 400 "PORTS" feature card + + Copyright (c) 2018, Seth J. Morabito + + 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 AUTHORS OR COPYRIGHT HOLDERS + 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. +*/ + +/* + * PORTS is an intelligent feature card for the 3B2 that supports four + * serial lines and one Centronics parallel port. + * + * The PORTS card is based on the Common I/O (CIO) platform. It uses + * two SCN2681A DUARTs to supply the four serial lines, and uses the + * SCN2681A parallel I/O pins for the Centronics parallel port. + * + * We make no attempt to emulate a PORTS card's internal workings + * precisely. Instead, we treat it as a black box as seen from the 3B2 + * system board's point of view. + * + */ + +#include "3b2_ports.h" + +extern CIO_STATE cio[CIO_SLOTS]; +extern UNIT cio_unit; + +/* Device and units for PORTS cards + * -------------------------------- + * + * A 3B2/400 system can have up to 12 PORTS cards installed. Each + * card, in turn, has 5 TTY devices - four serial TTYs and one + * parallel printer port (the printer port is not supported at this + * time, and is a no-op). + * + * The PORTS emulator is backed by a terminal multiplexer with up to + * 48 (12 * 4) serial lines. Lines can be specified with: + * + * SET PORTS LINES=n + * + * Lines must be specified in multiples of 4. + * + * Implementation8 + * -------------- + * + * Each set of 4 lines is mapped to a CIO_STATE struct in the "cio" + * CIO_STATE structure. + * + */ + + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define PPQESIZE 12 +#define DELAY_ASYNC 25 +#define DELAY_DLM 100 +#define DELAY_ULM 100 +#define DELAY_FCF 100 +#define DELAY_DOS 100 +#define DELAY_DSD 100 +#define DELAY_OPTIONS 100 +#define DELAY_VERS 100 +#define DELAY_CONN 100 +#define DELAY_XMIT 50 +#define DELAY_RECV 25 +#define DELAY_DEVICE 25 +#define DELAY_STD 100 + +#define LN(cid,port) ((PORTS_LINES * ((cid) - base_cid)) + (port)) +#define LCID(ln) (((ln) / PORTS_LINES) + base_cid) +#define LPORT(ln) ((ln) % PORTS_LINES) + +t_bool ports_conf = FALSE; /* Have PORTS cards been configured? */ +int8 base_cid; /* First cid in our contiguous block */ +uint8 int_cid; /* Interrupting card ID */ +uint8 int_subdev; /* Interrupting subdevice */ + +/* PORTS-specific state for each slot */ +PORTS_LINE_STATE *ports_state = NULL; + +/* Baud rates determined by the low nybble + * of the PORT_OPTIONS cflag */ +CONST char *ports_baud[16] = { + "75", "110", "134", "150", + "300", "600", "1200", "2000", + "2400", "4800", "1800", "9600", + "19200", "9600", "9600", "9600" +}; + +TMLN *ports_ldsc = NULL; +TMXR ports_desc = { 0, 0, 0, NULL }; + +/* Three units service the Receive, Transmit, and CIO */ +UNIT ports_unit[3] = { + { UDATA(&ports_rcv_svc, UNIT_IDLE|UNIT_ATTABLE|TT_MODE_8B, 0) }, + { UDATA(&ports_xmt_svc, UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA(&ports_cio_svc, UNIT_DIS, 0) } +}; + +MTAB ports_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "RQUEUE=n", NULL, + NULL, &ports_show_rqueue, NULL, "Display Request Queue for card n" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "CQUEUE=n", NULL, + NULL, &ports_show_cqueue, NULL, "Display Completion Queue for card n" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &ports_setnl, &tmxr_show_lines, (void *) &ports_desc, "Display number of lines" }, + { 0 } +}; + +static DEBTAB ports_debug[] = { + { "IO", IO_DBG, "I/O Character Trace" }, + { "TRACE", TRACE_DBG, "Call Trace" }, + { "XMT", TMXR_DBG_XMT, "TMXR Transmit Data" }, + { "RCV", TMXR_DBG_RCV, "TMXR Received Data" }, + { "RET", TMXR_DBG_RET, "TMXR Returned Received Data" }, + { "MDM", TMXR_DBG_XMT, "TMXR Modem Signals" }, + { "CON", TMXR_DBG_XMT, "TMXR Connection Activity" }, + { "ASY", TMXR_DBG_ASY, "TMXR Async Activity" }, + { "PXMT", TMXR_DBG_PXMT, "TMXR Transmit Packets" }, + { "PRCV", TMXR_DBG_PRCV, "TMXR Received Packets" }, + { NULL } +}; + +DEVICE ports_dev = { + "PORTS", /* name */ + ports_unit, /* units */ + NULL, /* registers */ + ports_mod, /* modifiers */ + 3, /* #units */ + 16, /* address radix */ + 32, /* address width */ + 1, /* address incr. */ + 16, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &ports_reset, /* reset routine */ + NULL, /* boot routine */ + &ports_attach, /* attach routine */ + &ports_detach, /* detach routine */ + NULL, /* context */ + DEV_DISABLE|DEV_DIS|DEV_DEBUG|DEV_MUX, /* flags */ + 0, /* debug control flags */ + ports_debug, /* debug flag names */ + NULL, /* memory size change */ + NULL, /* logical name */ + NULL, /* help routine */ + NULL, /* attach help routine */ + (void *)&ports_desc, /* help context */ + NULL, /* device description */ +}; + + +static void cio_irq(uint8 cid, uint8 dev, int32 delay) +{ + int_cid = cid; + int_subdev = dev & 0xf; + sim_activate(&ports_unit[2], delay); +} + +/* + * Set the number of lines for the PORTS mux. This will add or remove + * cards as necessary. The number of lines must be a multiple of 4. + */ +t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ + int32 newln, newcards, i, t; + t_stat r = SCPE_OK; + + if (cptr == NULL) { + return SCPE_ARG; + } + + newln = (int32) get_uint(cptr, 10, (MAX_PORTS_CARDS * PORTS_LINES), &r); + + if ((r != SCPE_OK) || (newln == ports_desc.lines)) { + return r; + } + + if ((newln == 0) || LPORT(newln) != 0) { + return SCPE_ARG; + } + + if (newln < ports_desc.lines) { + for (i = newln, t = 0; i < ports_desc.lines; i++) { + t = t | ports_ldsc[i].conn; + } + + if (t && !get_yn("This will disconnect users; proceed [N]?", FALSE)) { + return SCPE_OK; + } + + for (i = newln; i < ports_desc.lines; i++) { + if (ports_ldsc[i].conn) { + tmxr_linemsg(&ports_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_send_buffered_data(&ports_ldsc[i]); + } + /* completely reset line */ + tmxr_detach_ln(&ports_ldsc[i]); + if (LPORT(i) == (PORTS_LINES - 1)) { + /* Also drop the corresponding card from the CIO array */ + cio_clear(LCID(i)); + } + } + } + + ports_desc.ldsc = ports_ldsc = (TMLN *)realloc(ports_ldsc, newln*sizeof(*ports_ldsc)); + ports_state = (PORTS_LINE_STATE *)realloc(ports_state, newln*sizeof(*ports_state)); + + if (ports_desc.lines < newln) { + memset(ports_ldsc + ports_desc.lines, 0, sizeof(*ports_ldsc)*(newln-ports_desc.lines)); + memset(ports_state + ports_desc.lines, 0, sizeof(*ports_state)*(newln-ports_desc.lines)); + } + + ports_desc.lines = newln; + + /* setup lines and auto config */ + ports_conf = FALSE; + return ports_reset(&ports_dev); +} + + +static void ports_cmd(uint8 cid, cio_entry *rentry, uint8 *rapp_data) +{ + cio_entry centry = {0}; + uint32 i, ln; + char c; + PORTS_OPTIONS opts; + char line_config[16]; + uint8 app_data[4] = {0}; + + centry.address = rentry->address; + cio[cid].op = rentry->opcode; + ln = LN(cid, rentry->subdevice & 0xf); + + switch(rentry->opcode) { + case CIO_DLM: + centry.address = rentry->address + rentry->byte_count; + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] CIO Download Memory: bytecnt=%04x " + "addr=%08x return_addr=%08x subdev=%02x\n", + R[NUM_PC], + rentry->byte_count, rentry->address, + centry.address, centry.subdevice); + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_DLM); + break; + case CIO_ULM: + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] CIO Upload Memory\n", + R[NUM_PC]); + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_ULM); + break; + case CIO_FCF: + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] CIO Force Function Call\n", + R[NUM_PC]); + + /* This is to pass diagnostics. TODO: Figure out how to parse the + * given test x86 code and determine how to respond correctly */ + + pwrite_h(0x200f000, 0x1); /* Test success */ + pwrite_h(0x200f002, 0x0); /* Test Number */ + pwrite_h(0x200f004, 0x0); /* Actual */ + pwrite_h(0x200f006, 0x0); /* Expected */ + pwrite_b(0x200f008, 0x1); /* Success flag again */ + pwrite_b(0x200f009, 0x30); /* ??? */ + + /* An interesting (?) side-effect of FORCE FUNCTION CALL is + * that it resets the card state such that a new SYSGEN is + * required in order for new commands to work. In fact, an + * INT0/INT1 combo _without_ a RESET can sysgen the board. So, + * we reset the command bits here. */ + + cio[cid].sysgen_s = 0; + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_FCF); + break; + case CIO_DOS: + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] CIO Determine Op Status\n", + R[NUM_PC]); + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_DOS); + break; + case CIO_DSD: + /* Determine Sub-Devices. We have none. */ + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] Determine Sub-Devices.\n", + R[NUM_PC]); + + /* The system wants us to write sub-device structures + * at the supplied address */ + + pwrite_h(rentry->address, 0x0); + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_DSD); + break; + case PPC_OPTIONS: + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] PPC Options Operation\n", + R[NUM_PC]); + + opts.line = pread_h(rentry->address); + opts.iflag = pread_h(rentry->address + 4); + opts.oflag = pread_h(rentry->address + 6); + opts.cflag = pread_h(rentry->address + 8); + opts.lflag = pread_h(rentry->address + 10); + opts.cerase = pread_b(rentry->address + 11); + opts.ckill = pread_b(rentry->address + 12); + opts.cinter = pread_b(rentry->address + 13); + opts.cquit = pread_b(rentry->address + 14); + opts.ceof = pread_b(rentry->address + 15); + opts.ceol = pread_b(rentry->address + 16); + opts.itime = pread_b(rentry->address + 17); + opts.vtime = pread_b(rentry->address + 18); + opts.vcount = pread_b(rentry->address + 19); + + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: iflag=%04x\n", opts.iflag); + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: oflag=%04x\n", opts.oflag); + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: cflag=%04x\n", opts.cflag); + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: lflag=%04x\n", opts.lflag); + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: itime=%02x\n", opts.itime); + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: vtime=%02x\n", opts.vtime); + sim_debug(TRACE_DBG, &ports_dev, " PPC Options: vcount=%02x\n", opts.vcount); + + ports_state[ln].iflag = opts.iflag; + ports_state[ln].oflag = opts.oflag; + + if ((rentry->subdevice & 0xf) < PORTS_LINES) { + /* Adjust baud rate */ + sprintf(line_config, "%s-8N1", + ports_baud[opts.cflag&0xf]); + + sim_debug(TRACE_DBG, &ports_dev, + "Setting PORTS line %d to %s\n", + ln, line_config); + + tmxr_set_config_line(&ports_ldsc[ln], line_config); + } + + centry.byte_count = sizeof(PPC_OPTIONS); + centry.opcode = PPC_OPTIONS; + centry.subdevice = rentry->subdevice; + centry.address = rentry->address; + cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_OPTIONS); + break; + case PPC_VERS: + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] PPC Version\n", + R[NUM_PC]); + + /* Write the version number at the supplied address */ + pwrite_b(rentry->address, PORTS_VERSION); + + centry.opcode = CIO_ULM; + + /* TODO: It's unknown what the value 0x50 means, but this + * is what a real board sends. */ + app_data[0] = 0x50; + cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_VERS); + break; + case PPC_CONN: + /* CONNECT - Full request and completion queues */ + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] PPC CONNECT - subdevice = %02x\n", + R[NUM_PC], rentry->subdevice); + + ports_state[ln].conn = TRUE; + + centry.opcode = PPC_CONN; + centry.subdevice = rentry->subdevice; + centry.address = rentry->address; + cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_CONN); + break; + case PPC_XMIT: + /* XMIT - Full request and completion queues */ + + /* The port being referred to is in the subdevice. */ + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] PPC XMIT - subdevice = %02x, address=%08x, byte_count=%d\n", + R[NUM_PC], rentry->subdevice, rentry->address, rentry->byte_count); + + /* Set state for xmit */ + ports_state[ln].tx_addr = rentry->address; + ports_state[ln].tx_req_addr = rentry->address; + ports_state[ln].tx_chars = rentry->byte_count + 1; + ports_state[ln].tx_req_chars = rentry->byte_count + 1; + + sim_activate_after(&ports_unit[1], ports_unit[1].wait); + + break; + case PPC_DEVICE: + /* DEVICE Control - Express request and completion queues */ + /* The port being referred to is in the subdevice. */ + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] PPC DEVICE - subdevice = %02x\n", + R[NUM_PC], rentry->subdevice); + centry.subdevice = rentry->subdevice; + centry.opcode = PPC_DEVICE; + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_DEVICE); + break; + case PPC_RECV: + /* RECV - Full request and completion queues */ + + /* The port being referred to is in the subdevice. */ + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_cmd] PPC RECV - subdevice = %02x addr=%08x\n", + R[NUM_PC], rentry->subdevice, rentry->address); + + break; + case PPC_DISC: + /* Disconnect */ + centry.subdevice = rentry->subdevice; + centry.opcode = PPC_DISC; + ports_ldsc[ln].rcve = 0; + cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_STD); + break; + case PPC_BRK: + case PPC_CLR: + default: + sim_debug(TRACE_DBG, &ports_dev, + ">>> Op %d Not Handled Yet\n", + rentry->opcode); + + cio_cexpress(cid, PPQESIZE, ¢ry, app_data); + cio_irq(cid, rentry->subdevice, DELAY_STD); + break; + } +} + +/* + * Update the connection status of the given port. + */ +static void ports_update_conn(uint32 ln) +{ + cio_entry centry; + uint8 cid; + uint8 app_data[4] = {0}; + + cid = LCID(ln); + + /* If the card hasn't sysgened, there's no way to write a + * completion queue entry */ + if (cio[cid].sysgen_s != CIO_SYSGEN) { + return; + } + + if (ports_ldsc[ln].conn) { + app_data[0] = AC_CON; + ports_state[ln].conn = TRUE; + } else { + if (ports_state[ln].conn) { + app_data[0] = AC_DIS; + ports_state[ln].conn = FALSE; + } else { + app_data[0] = 0; + } + } + + centry.opcode = PPC_ASYNC; + centry.subdevice = LPORT(ln); + cio_cqueue(cid, CIO_CMD, PPQESIZE, ¢ry, app_data); + + /* Interrupt */ + if (cio[cid].ivec > 0) { + cio[cid].intr = TRUE; + } +} + +void ports_sysgen(uint8 cid) +{ + cio_entry cqe; + uint8 app_data[4] = {0}; + + cqe.opcode = 3; /* Sysgen success! */ + + /* It's not clear why we put a response in both the express + * and the full queue. */ + cio_cexpress(cid, PPQESIZE, &cqe, app_data); + cio_cqueue(cid, CIO_STAT, PPQESIZE, &cqe, app_data); + + int_cid = cid; + sim_activate(&ports_unit[2], DELAY_STD); +} + +void ports_express(uint8 cid) +{ + cio_entry rqe; + uint8 app_data[4] = {0}; + cio_rexpress(cid, PPQESIZE, &rqe, app_data); + ports_cmd(cid, &rqe, app_data); +} + +void ports_full(uint8 cid) +{ + uint32 i, ln; + cio_entry rqe; + uint8 app_data[4] = {0}; + + for (i = 0; i < PORTS_LINES; i++) { + if (cio_rqueue(cid, i, PPQESIZE, &rqe, app_data) == SCPE_OK) { + ports_cmd(cid, &rqe, app_data); + } + } +} + +t_stat ports_reset(DEVICE *dptr) +{ + uint32 i; + uint8 cid, line, cards, ln, end_slot; + TMLN *lp; + + sim_debug(TRACE_DBG, &ports_dev, + "[ports_reset] Resetting PORTS device\n"); + + if ((dptr->flags & DEV_DIS)) { + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == PORTS_ID) { + cio[cid].id = 0; + cio[cid].ipl = 0; + cio[cid].ivec = 0; + cio[cid].exp_handler = NULL; + cio[cid].full_handler = NULL; + cio[cid].sysgen = NULL; + } + } + + ports_conf = FALSE; + } else if (!ports_conf) { + + /* Clear out any old cards, we're starting fresh */ + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == PORTS_ID) { + cio[cid].id = 0; + cio[cid].ipl = 0; + cio[cid].ivec = 0; + cio[cid].exp_handler = NULL; + cio[cid].full_handler = NULL; + cio[cid].sysgen = NULL; + } + } + + /* Find the first avaialable slot */ + for (cid = 0; cid < CIO_SLOTS; cid++) { + if (cio[cid].id == 0) { + break; + } + } + + /* Do we have room? */ + if (cid >= CIO_SLOTS || cid > (CIO_SLOTS - (ports_desc.lines/PORTS_LINES))) { + return SCPE_NXM; + } + + /* Remember the base card slot */ + base_cid = cid; + + end_slot = (cid + (ports_desc.lines/PORTS_LINES)); + + for (; cid < end_slot; cid++) { + /* Set up the ports structure */ + cio[cid].id = PORTS_ID; + cio[cid].ipl = PORTS_IPL; + cio[cid].exp_handler = &ports_express; + cio[cid].full_handler = &ports_full; + cio[cid].sysgen = &ports_sysgen; + + for (line = 0; line < PORTS_LINES; line++) { + ln = LN(cid, line); + + sim_debug(TRACE_DBG, &ports_dev, + ">>> Setting up lp %d (card %d, line %d)\n", + ln, cid, line); + + lp = &ports_ldsc[ln]; + tmxr_set_get_modem_bits(lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); + } + } + + ports_conf = TRUE; + + if (ports_ldsc == NULL) { + ports_desc.lines = DEF_PORTS_CARDS * PORTS_LINES; + ports_desc.ldsc = ports_ldsc = + (TMLN *)calloc(ports_desc.lines, sizeof(*ports_ldsc)); + } + + if (ports_state == NULL) { + sim_debug(TRACE_DBG, &ports_dev, + "[ports_reset] calloc for ports_state...\n"); + ports_state = (PORTS_LINE_STATE *)calloc(ports_desc.lines, sizeof(*ports_state)); + } + + memset(ports_state, 0, ports_desc.lines*sizeof(*ports_state)); + + tmxr_set_port_speed_control(&ports_desc); + + for (i = 0; i < ports_desc.lines; i++) { + sim_debug(TRACE_DBG, &ports_dev, + "[ports_reset] Setting up line %d...\n", i); + tmxr_set_line_unit(&ports_desc, i, &ports_unit[0]); + tmxr_set_line_output_unit(&ports_desc, i, &ports_unit[1]); + if (!ports_ldsc[i].conn) { + ports_ldsc[i].xmte = 1; + } + ports_ldsc[i].rcve = 0; + tmxr_set_config_line(&ports_ldsc[i], "9600-8N1"); + } + } + + if (!sim_is_active(&ports_unit[0])) { + sim_debug(TRACE_DBG, &ports_dev, + "[ports_reset] starting receive polling...\n"); + sim_activate(&ports_unit[0], ports_unit[0].wait); + } + + sim_debug(TRACE_DBG, &ports_dev, + "[ports_reset] returning scpe_ok\n"); + return SCPE_OK; +} + +t_stat ports_cio_svc(UNIT *uptr) +{ + sim_debug(TRACE_DBG, &ports_dev, + "[ports_cio_svc] IRQ for board %d device %d\n", + int_cid, int_subdev); + + if (cio[int_cid].ivec > 0) { + cio[int_cid].intr = TRUE; + } + + switch (cio[int_cid].op) { + case PPC_CONN: + cio[int_cid].op = PPC_ASYNC; + ports_ldsc[LN(int_cid, int_subdev)].rcve = 1; + sim_activate(&ports_unit[2], DELAY_ASYNC); + break; + case PPC_ASYNC: + ports_update_conn(LN(int_cid, int_subdev)); + break; + default: + break; + } + + return SCPE_OK; +} + +t_stat ports_rcv_svc(UNIT *uptr) +{ + uint8 cid, subdev; + int32 temp, ln; + char c; + cio_entry rentry = {0}; + cio_entry centry = {0}; + uint8 rapp_data[4] = {0}; + uint8 capp_data[4] = {0}; + + if ((uptr->flags & UNIT_ATT) == 0) { + return SCPE_OK; + } + + ln = tmxr_poll_conn(&ports_desc); + if (ln >= 0) { + ports_update_conn(ln); + } + + tmxr_poll_rx(&ports_desc); + + for (ln = 0; ln < ports_desc.lines; ln++) { + cid = LCID(ln); + subdev = LPORT(ln); + + if (!ports_ldsc[ln].conn && ports_state[ln].conn) { + ports_update_conn(ln); + } else if (ports_ldsc[ln].conn && ports_state[ln].conn) { + temp = tmxr_getc_ln(&ports_ldsc[ln]); + + if (temp && !(temp & SCPE_BREAK)) { + + c = (char) (temp & 0xff); + + sim_debug(IO_DBG, &ports_dev, + "[LINE %d RECEIVE] char = %02x (%c)\n", + ln, c, c); + + if (c == 0xd && (ports_state[ln].iflag & ICRNL)) { + c = 0xa; + } + + if (cio[cid].ivec > 0 && + cio_rqueue(cid, PORTS_RCV_QUEUE, + PPQESIZE, &rentry, rapp_data) == SCPE_OK) { + cio[cid].intr = TRUE; + + /* Write the character to the memory address */ + pwrite_b(rentry.address, c); + centry.subdevice = LPORT(ln); + centry.opcode = PPC_RECV; + centry.address = rentry.address; + capp_data[3] = RC_TMR; + + cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, capp_data); + } + } + } + } + + tmxr_clock_coschedule(uptr, tmxr_poll); + + return SCPE_OK; +} + +t_stat ports_xmt_svc(UNIT *uptr) +{ + uint8 cid, ln; + char c; + t_bool tx = FALSE; /* Did a tx ever occur? */ + cio_entry centry; + uint8 app_data[4] = {0}; + uint32 wait = 0x7fffffff; + + /* Scan all lines for output */ + for (ln = 0; ln < ports_desc.lines; ln++) { + cid = LCID(ln); + if (ports_ldsc[ln].conn && ports_state[ln].tx_chars > 0) { + tx = TRUE; /* Even an attempt at TX counts for rescheduling */ + c = sim_tt_outcvt(pread_b(ports_state[ln].tx_addr), + TT_GET_MODE(ports_unit[0].flags)); + + /* The PORTS card optionally handles NL->CRLF */ + if (c == 0xa && + (ports_state[ln].oflag & ONLCR) && + !(ports_state[ln].crlf)) { + if (tmxr_putc_ln(&ports_ldsc[ln], 0xd) == SCPE_OK) { + wait = MIN(wait, ports_ldsc[ln].txdelta); + sim_debug(IO_DBG, &ports_dev, + "[%08x] [ports_xmt_svc] [LINE %d] XMIT (crlf): %02x (%c)\n", + R[NUM_PC], ln, 0xd, 0xd); + /* Indicate that we're in a CRLF translation */ + ports_state[ln].crlf = TRUE; + } + + break; + } + + ports_state[ln].crlf = FALSE; + + if (tmxr_putc_ln(&ports_ldsc[ln], c) == SCPE_OK) { + wait = MIN(wait, ports_ldsc[ln].txdelta); + ports_state[ln].tx_chars--; + ports_state[ln].tx_addr++; + sim_debug(IO_DBG, &ports_dev, + "[%08x] [ports_xmt_svc] [LINE %d] XMIT: %02x (%c)\n", + R[NUM_PC], ln, c, c); + } + + if (ports_state[ln].tx_chars == 0) { + sim_debug(TRACE_DBG, &ports_dev, + "[%08x] [ports_xmt_svc] Done with xmit, card=%d port=%d. Interrupting.\n", + R[NUM_PC], cid, LPORT(ln)); + centry.byte_count = ports_state[ln].tx_req_chars; + centry.subdevice = LPORT(ln); + centry.opcode = PPC_XMIT; + centry.address = ports_state[ln].tx_req_addr; + app_data[0] = RC_FLU; + cio_cqueue(cid, CIO_STAT, PPQESIZE, ¢ry, app_data); + cio[cid].intr = TRUE; + } + } + } + + tmxr_poll_tx(&ports_desc); + + if (tx) { + tmxr_activate_after(uptr, wait); + } + + return SCPE_OK; +} + +t_stat ports_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat r; + + sim_debug(TRACE_DBG, &ports_dev, "ports_attach()\n"); + + tmxr_set_modem_control_passthru(&ports_desc); + + r = tmxr_attach(&ports_desc, uptr, cptr); + if (r != SCPE_OK) { + tmxr_clear_modem_control_passthru(&ports_desc); + return r; + } + + return SCPE_OK; +} + +t_stat ports_detach(UNIT *uptr) +{ + t_stat r; + + r = tmxr_detach(&ports_desc, uptr); + + if (r != SCPE_OK) { + return r; + } + + if (sim_is_active(&ports_unit[0])) { + sim_debug(TRACE_DBG, &ports_dev, + "[ports_detach] Stopping receive polling...\n"); + sim_cancel(&ports_unit[0]); + } + + tmxr_clear_modem_control_passthru(&ports_desc); + + return SCPE_OK; +} + +/* + * Useful routines for debugging request and completion queues + */ + +t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + return ports_show_queue_common(st, uptr, val, desc, TRUE); +} + +t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ + return ports_show_queue_common(st, uptr, val, desc, FALSE); +} + + +static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, + CONST void *desc, t_bool rq) +{ + uint8 cid; + char *cptr = (char *) desc; + t_stat result; + uint32 ptr, size, no_rque, i, j; + uint8 op, dev, seq, cmdstat; + + if (cptr) { + cid = (uint8) get_uint(cptr, 10, 12, &result); + if (result != SCPE_OK) { + return SCPE_ARG; + } + } else { + return SCPE_ARG; + } + + /* If the card is not sysgen'ed, give up */ + if (cio[cid].sysgen_s != CIO_SYSGEN) { + fprintf(st, "No card in slot %d, or card has not completed sysgen\n", cid); + return SCPE_ARG; + } + + /* Get the top of the queue */ + if (rq) { + ptr = cio[cid].rqp; + size = cio[cid].rqs; + no_rque = cio[cid].no_rque; + } else { + ptr = cio[cid].cqp; + size = cio[cid].cqs; + no_rque = 0; /* Not used */ + } + + if (rq) { + fprintf(st, "Dumping %d Request Queues\n", no_rque); + } else { + fprintf(st, "Dumping Completion Queue\n"); + } + + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "EXPRESS ENTRY:\n"); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", pread_b(ptr + 2)); + fprintf(st, " Opcode: 0x%02x\n", pread_b(ptr + 3)); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); + ptr += 12; + + if (rq) { + for (i = 0; i < no_rque; i++) { + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "REQUEST QUEUE %d\n", i); + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12); + fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12); + fprintf(st, "---------------------------------------------------------\n"); + ptr += 4; + for (j = 0; j < size; j++) { + dev = pread_b(ptr + 2); + op = pread_b(ptr + 3); + seq = (dev & 0x40) >> 6; + cmdstat = (dev & 0x80) >> 7; + fprintf(st, "REQUEST ENTRY %d\n", j); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", dev & 0x3f); + fprintf(st, " Cmd/Stat: %d\n", cmdstat); + fprintf(st, " Seqbit: %d\n", seq); + fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); + ptr += 12; + } + } + } else { + fprintf(st, "---------------------------------------------------------\n"); + fprintf(st, "Load Pointer: %d\n", pread_h(ptr) / 12); + fprintf(st, "Unload Pointer: %d\n", pread_h(ptr + 2) / 12); + fprintf(st, "---------------------------------------------------------\n"); + ptr += 4; + for (i = 0; i < size; i++) { + dev = pread_b(ptr + 2); + op = pread_b(ptr + 3); + seq = (dev & 0x40) >> 6; + cmdstat = (dev & 0x80) >> 7; + fprintf(st, "COMPLETION ENTRY %d\n", i); + fprintf(st, " Byte Count: %d\n", pread_h(ptr)); + fprintf(st, " Subdevice: %d\n", dev & 0x3f); + fprintf(st, " Cmd/Stat: %d\n", cmdstat); + fprintf(st, " Seqbit: %d\n", seq); + fprintf(st, " Opcode: 0x%02x (%d)\n", op, op); + fprintf(st, " Addr/Data: 0x%08x\n", pread_w(ptr + 4)); + fprintf(st, " App Data: 0x%08x\n", pread_w(ptr + 8)); + ptr += 12; + } + } + + return SCPE_OK; +} diff --git a/3B2/3b2_ports.h b/3B2/3b2_ports.h new file mode 100644 index 00000000..d6580195 --- /dev/null +++ b/3B2/3b2_ports.h @@ -0,0 +1,237 @@ +/* 3b2_ports.h: AT&T 3B2 Model 400 "PORTS" feature card + + Copyright (c) 2018, Seth J. Morabito + + 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 AUTHORS OR COPYRIGHT HOLDERS + 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. +*/ + +/* + * PORTS is an intelligent feature card for the 3B2 that supports four + * serial lines and one Centronics parallel port. + * + * The PORTS card is based on the Common I/O (CIO) platform. It uses + * two SCN2681A DUARTs to supply the four serial lines, and uses the + * SCN2681A parallel I/O pins for the Centronics parallel port. + * + * This file implements the required logic for the PORTS CIO + * interface. The SCN2681A functionality is implemented in the file + * 3b2_duart.c, and is used by both this feature card and the System + * Board console/contty functionality. + */ + +#ifndef _3B2_PORTS_H_ +#define _3B2_PORTS_H_ + +#include "3b2_defs.h" +#include "3b2_io.h" + +#define PORTS_ID 0x0003 +#define PORTS_IPL 10 +#define PORTS_VERSION 1 + +#define DEF_PORTS_CARDS 1 +#define MAX_PORTS_CARDS 12 +#define PORTS_LINES 4 +#define PORTS_RCV_QUEUE 5 + +/* + * Sub-field values for the PPC_DEVICE request entry; these are placed + * in app_data.bt[0] in the PPC_DEVICE application field. The prefix + * DR indicates that this is a code for use in "device" request + * entries only. +*/ + +#define DR_ENA 1 /* enable a device */ +#define DR_DIS 2 /* disable a device */ +#define DR_ABR 3 /* abort reception on a device */ +#define DR_ABX 4 /* abort transmission on a device */ +#define DR_BRK 5 /* transmit "break" on a device */ +#define DR_SUS 6 /* suspend xmit on a device */ +#define DR_RES 7 /* resume xmit on a device */ +#define DR_BLK 8 /* transmit STOP character */ +#define DR_UNB 9 /* transmit START character */ + +/* + * Sub-field values for the PPC_DEVICE completion entry; these appear + * in app_data.bt[0] in the PPC_DEVICE application field. These are + * mutually exclusive and cannot be combined. The prefix DC indicates + * that this is a code for use in "device" completion entries only. + */ + +#define DC_NORM 0x00 /* command executed as requested */ +#define DC_DEV 0x01 /* bad device number */ +#define DC_NON 0x02 /* bad sub-code on request */ +#define DC_FAIL 0x03 /* failed to read express entry */ + +/* + * Sub-field values for the PPC_RECV completion entry; these appear in + * app_data.bt[0] in the PPC_RECV application field. These are NOT + * mutually exclusive and may appear in combination. The prefix RC + * indicates that this is a code for use in "read" completion entries + * only. +*/ + +#define RC_DSR 0x01 /* disruption of service */ +#define RC_FLU 0x02 /* buffer flushed */ +#define RC_TMR 0x04 /* inter-character timer expired */ +#define RC_BQO 0x08 /* PPC buffer queue overflow */ +#define RC_UAO 0x10 /* uart overrun */ +#define RC_PAR 0x20 /* parity error */ +#define RC_FRA 0x40 /* framing error */ +#define RC_BRK 0x80 /* break received */ + +/* + * The following codes are included on the DISC (disconnect) command. + * They are "or"ed into the app_data.bt[1] application field in a + * request. These codes are NOT mutually exclusive and can be used in + * any combination. + */ + +#define GR_DTR 0x01 +#define GR_CREAD 0x02 + +/* + * Sub-field values for the PPC_XMIT and PPC_OPTIONS completion + * entries; these appear in app_data.bt[0] in the application fields. + * These are NOT mutually exclusive and may appear in combination. + * The prefix GC indicates that this is a code for use in "general" + * completion entries only. +*/ + +#define GC_DSR 0x01 /* disruption of service */ +#define GC_FLU 0x02 /* buffer flushed */ + +/* + * Sub-field values for the PPC_ASYNC completion entry; these appear + * in app_data.bt[0] in the PPC_ASYNC application field. These are + * mutually exclusive and cannot be combined. The prefix AC indicates + * that this is a code for use in "asynchronous" completion entries + * only. +*/ + +#define AC_CON 0x01 /* connection detected */ +#define AC_DIS 0x02 /* disconnection detected */ +#define AC_BRK 0x03 /* asynchronous "break" */ +#define AC_FLU 0x04 /* xmit flush complete */ + +/* Line Discipline flags (input and output) */ + +#define IGNBRK 0x0001 +#define BRKINT 0x0002 +#define IGNPAR 0x0004 +#define PARMRK 0x0008 +#define INPCK 0x0010 +#define ISTRIP 0x0020 +#define INLCR 0x0040 +#define IGNCR 0x0080 +#define ICRNL 0x0100 +#define IUCLC 0x0200 +#define IXON 0x0400 +#define IXANY 0x0800 + +#define OPOST 0x0001 +#define OLCUC 0x0002 +#define ONLCR 0x0004 +#define OCRNL 0x0008 +#define ONOCR 0x0010 +#define ONLRET 0x0020 +#define OFILL 0x0040 +#define OFDEL 0x0080 +#define ONLDLY 0x0100 +#define OCRDLY 0x0600 +#define OTABDLY 0x1800 +#define OBSDLY 0x2000 +#define OVTDLY 0x4000 +#define OFFDLY 0x8000 + +/* Opcodes for PORTS card */ + +#define PPC_OPTIONS 32 /* GEN, COMP queues: set PPC options */ +#define PPC_XMIT 33 /* GEN, COMP queues: transmit a buffer */ +#define PPC_CONN 34 /* GEN, COMP queues: connect a device */ +#define PPC_DISC 35 /* GEN, COMP queues: disconnect a device */ +#define PPC_BRK 36 /* GEN, COMP queues: ioctl break */ +#define PPC_DEVICE 40 /* EXP, ECOMP entries: device control command */ +#define PPC_CLR 41 /* EXP, ECOMP entries: board clear */ +#define PPC_RECV 50 /* RECV, COMP queues: receive request */ +#define PPC_ASYNC 60 /* Asynchronous request */ +#define CFW_CONFIG 70 /* GEN, COMP queues: set PPC port 0 hardware options */ +#define CFW_IREAD 71 /* GEN, COMP queues: read immediate one to four bytes */ +#define CFW_IWRITE 72 /* GEN, COMP queues: write immediate one to four bytes */ +#define CFW_WRITE 73 /* GEN, COMP queues: write */ +#define PPC_VERS 80 /* EXP, COMP queues: Version */ + +typedef struct { + uint32 tx_addr; /* Address to next read from */ + uint32 tx_req_addr; /* Original request address */ + uint32 tx_chars; /* Number of chars left to transfer */ + uint32 tx_req_chars; /* Original number of chars */ + uint8 rlp; /* Last known load pointer */ + uint16 iflag; /* Line Discipline: Input flags */ + uint16 oflag; /* Line Discipline: Output flags */ + t_bool crlf; /* Indicates we are in a CRLF output transform */ + t_bool conn; /* TRUE if connected, FALSE otherwise */ +} PORTS_LINE_STATE; + +typedef struct { + uint16 line; /* line discipline */ + uint16 pad1; + uint16 iflag; /* input options word */ + uint16 oflag; /* output options word */ + uint16 cflag; /* hardware options */ + uint16 lflag; /* line discipline options */ + uint8 cerase; /* "erase" character */ + uint8 ckill; /* "kill" character */ + uint8 cinter; /* "interrupt" character */ + uint8 cquit; /* "quit" character */ + uint8 ceof; /* "end of file" character */ + uint8 ceol; /* "end of line" character */ + uint8 itime; /* inter character timer multiplier */ + uint8 vtime; /* user-specified inter char timer */ + uint8 vcount; /* user-specified maximum buffer char count */ + uint8 pad2; + uint16 pad3; +} PORTS_OPTIONS; + +extern DEVICE ports_dev; + +t_stat ports_reset(DEVICE *dptr); +t_stat ports_setnl(UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat ports_show_cqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat ports_show_rqueue(FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_stat ports_rcv_svc(UNIT *uptr); +t_stat ports_xmt_svc(UNIT *uptr); +t_stat ports_cio_svc(UNIT *uptr); +t_stat ports_attach(UNIT *uptr, CONST char *cptr); +t_stat ports_detach(UNIT *uptr); +void ports_sysgen(uint8 cid); +void ports_express(uint8 cid); +void ports_full(uint8 cid); + +static t_stat ports_show_queue_common(FILE *st, UNIT *uptr, int32 val, + CONST void *desc, t_bool rq); + +#endif /* _3B2_PORTS_H_ */ diff --git a/3B2/3b2_sys.c b/3B2/3b2_sys.c index 60b1eaf4..c0f77b40 100644 --- a/3B2/3b2_sys.c +++ b/3B2/3b2_sys.c @@ -35,6 +35,8 @@ #include "3b2_if.h" #include "3b2_id.h" #include "3b2_mmu.h" +#include "3b2_ctc.h" +#include "3b2_ports.h" #include "3b2_sysdev.h" char sim_name[] = "AT&T 3B2 Model 400"; @@ -61,6 +63,8 @@ DEVICE *sim_devices[] = { &dmac_dev, &if_dev, &id_dev, + &ports_dev, + &ctc_dev, NULL }; @@ -87,6 +91,8 @@ void full_reset() if_reset(&if_dev); id_reset(&id_dev); csr_reset(&csr_dev); + ports_reset(&ports_dev); + ctc_reset(&ctc_dev); } t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) diff --git a/3B2/3b2_sysdev.c b/3B2/3b2_sysdev.c index 1783b9ef..1414992c 100644 --- a/3B2/3b2_sysdev.c +++ b/3B2/3b2_sysdev.c @@ -49,7 +49,7 @@ DEBTAB sys_deb_tab[] = { { "WRITE", WRITE_MSG, "Write activity" }, { "EXECUTE", EXECUTE_MSG, "Execute activity" }, { "IRQ", IRQ_MSG, "Interrupt activity"}, - { "TRACE", TRACE_MSG, "Detailed activity" }, + { "TRACE", TRACE_DBG, "Detailed activity" }, { NULL, 0 } }; @@ -481,7 +481,7 @@ t_stat timer0_svc(UNIT *uptr) time = TIMER_STP_US; } - sim_activate_abs(uptr, (int32) DELAY_US(time)); + sim_activate_after_abs(uptr, time); return SCPE_OK; } @@ -489,7 +489,7 @@ t_stat timer0_svc(UNIT *uptr) t_stat timer1_svc(UNIT *uptr) { struct timer_ctr *ctr; - int32 ticks, t; + int32 t; ctr = (struct timer_ctr *)uptr->tmr; @@ -498,14 +498,8 @@ t_stat timer1_svc(UNIT *uptr) csr_data |= CSRCLK; } - ticks = ctr->divider / TIMER_STP_US; - - if (ticks < CLK_MIN_TICKS) { - ticks = TPS_CLK; - } - - t = sim_rtcn_calb(ticks, TMR_CLK); - sim_activate_after(uptr, (uint32) (1000000 / ticks)); + t = sim_rtcn_calb(TPS_CLK, TMR_CLK); + sim_activate_after_abs(uptr, 1000000/TPS_CLK); tmxr_poll = t; return SCPE_OK; @@ -524,7 +518,7 @@ t_stat timer2_svc(UNIT *uptr) time = TIMER_STP_US; } - sim_activate_abs(uptr, (int32) DELAY_US(time)); + sim_activate_after_abs(uptr, time); return SCPE_OK; } @@ -598,7 +592,7 @@ void handle_timer_write(uint8 ctrnum, uint32 val) ctr->enabled = TRUE; ctr->stime = sim_gtime(); sim_cancel(timer_clk_unit); - sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); + sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); break; case 0x20: ctr->divider &= 0x00ff; @@ -608,7 +602,7 @@ void handle_timer_write(uint8 ctrnum, uint32 val) ctr->stime = sim_gtime(); /* Kick the timer to get the new divider value */ sim_cancel(timer_clk_unit); - sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); + sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); break; case 0x30: if (ctr->lmb) { @@ -622,7 +616,7 @@ void handle_timer_write(uint8 ctrnum, uint32 val) R[NUM_PC], ctrnum, val & 0xff); /* Kick the timer to get the new divider value */ sim_cancel(timer_clk_unit); - sim_activate_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); + sim_activate_after_abs(timer_clk_unit, ctr->divider * TIMER_STP_US); } else { ctr->lmb = TRUE; ctr->divider = (ctr->divider & 0xff00) | (val & 0xff); diff --git a/makefile b/makefile index cc052368..6a5fd508 100644 --- a/makefile +++ b/makefile @@ -1730,7 +1730,8 @@ ATT3B2 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_mmu.c \ ${ATT3B2D}/3b2_iu.c ${ATT3B2D}/3b2_if.c \ ${ATT3B2D}/3b2_id.c ${ATT3B2D}/3b2_dmac.c \ ${ATT3B2D}/3b2_sys.c ${ATT3B2D}/3b2_io.c \ - ${ATT3B2D}/3b2_sysdev.c + ${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \ + ${ATT3B2D}/3b2_sysdev.c ATT3B2_OPT = -I ${ATT3B2D} -DUSE_INT64 -DUSE_ADDR64 # # Build everything (not the unsupported/incomplete or experimental simulators)