diff --git a/3B2/3b2_dmac.c b/3B2/3b2_dmac.c index f836f355..c752c469 100644 --- a/3B2/3b2_dmac.c +++ b/3B2/3b2_dmac.c @@ -55,8 +55,8 @@ DEVICE dmac_dev = { dmac_drq_handler dmac_drq_handlers[] = { {DMA_ID_CHAN, IDBASE+ID_DATA_REG, &id_drq, id_drq_handled}, {DMA_IF_CHAN, IFBASE+IF_DATA_REG, &if_state.drq, if_drq_handled}, - {DMA_IUA_CHAN, IUBASE+IUA_DATA_REG, &iu_state.drqa, iua_drq_handled}, - {DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_state.drqb, iub_drq_handled}, + {DMA_IUA_CHAN, IUBASE+IUA_DATA_REG, &iu_port_a.drq, iua_drq_handled}, + {DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_port_b.drq, iub_drq_handled}, {0, 0, NULL, NULL } }; @@ -251,6 +251,8 @@ void dmac_program(uint8 reg, uint8 val) dma_state.mask |= (1 << channel_id); } else { dma_state.mask &= ~(1 << channel_id); + /* Set the appropriate DRQ */ + /* *dmac_drq_handlers[channel_id].drq = TRUE; */ } sim_debug(WRITE_MSG, &dmac_dev, @@ -278,7 +280,7 @@ void dmac_program(uint8 reg, uint8 val) break; case 15: /* Write All Mask Register Bits */ sim_debug(WRITE_MSG, &dmac_dev, - "[%08x] Clear DMAC Interrupt. val=%02x\n", + "[%08x] Write DMAC mask (all bits). Val=%02x\n", R[NUM_PC], val); dma_state.mask = val & 0xf; break; diff --git a/3B2/3b2_iu.c b/3B2/3b2_iu.c index 4e7a03d9..f5eeaad4 100644 --- a/3B2/3b2_iu.c +++ b/3B2/3b2_iu.c @@ -30,25 +30,48 @@ #include "3b2_iu.h" +/* + * The 3B2/400 has two on-board serial ports, labeled CONSOLE and + * CONTTY. The CONSOLE port is (naturally) the system console. The + * CONTTY port serves as a secondary serial port for an additional + * terminal. + * + * These lines are driven by an SCN2681A Dual UART, with two receivers + * and two transmitters. + * + * In addition to the two TX/RX ports, the SCN27681A also has one + * programmable timer. + * + * The SCN2681A UART is represented here by five devices: + * + * - Console TTI (Input, port A) + * - Console TTO (Output, port A) + * - Contty TTI (Input, port B) + * - Contty TTO (Output, port B) + * - IU Timer + */ + + /* * Registers */ -/* The IU state */ +/* The IU state shared between A and B */ IU_STATE iu_state; +/* The tx/rx state for ports A and B */ +IU_PORT iu_port_a; +IU_PORT iu_port_b; + +/* The timer state */ +IU_TIMER_STATE iu_timer_state; + +/* Flags for incrementing mode pointers */ t_bool iu_increment_a = FALSE; t_bool iu_increment_b = FALSE; extern uint16 csr_data; -UNIT iu_unit[] = { - { UDATA(&iu_svc_tti, UNIT_IDLE, 0), TMLN_SPD_9600_BPS }, - { UDATA(&iu_svc_tto, TT_MODE_8B, 0), SERIAL_OUT_WAIT }, - { UDATA(&iu_svc_timer, 0, 0) }, - { NULL } -}; - BITFIELD sr_bits[] = { BIT(RXRDY), BIT(FFULL), @@ -89,86 +112,202 @@ BITFIELD conf_bits[] = { ENDBITS }; -REG iu_reg[] = { - { HRDATADF(ISTAT, iu_state.istat, 8, "Interrupt Status", isr_bits) }, - { HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") }, - { HRDATADF(ACR, iu_state.acr, 8, "Auxiliary Control Register", acr_bits) }, - { HRDATAD(CTR, iu_state.c_set, 16, "Counter Setting") }, - { HRDATAD(IP, iu_state.inprt, 8, "Input Port") }, - { HRDATADF(STAT_A, iu_state.port[0].stat, 8, "Status (Port A)", sr_bits) }, - { HRDATAD(DATA_A, iu_state.port[0].buf, 8, "Data (Port A)") }, - { HRDATADF(CONF_A, iu_state.port[0].conf, 8, "Config (Port A)", conf_bits) }, - { HRDATADF(STAT_B, iu_state.port[1].stat, 8, "Status (Port B)", sr_bits) }, - { HRDATAD(DATA_B, iu_state.port[1].buf, 8, "Data (Port B)") }, - { HRDATADF(CONF_B, iu_state.port[1].conf, 8, "Config (Port B)", conf_bits) }, +/* TTI (Port A) data structures */ + +REG tti_a_reg[] = { + { HRDATADF(STAT, iu_port_a.stat, 8, "Status", sr_bits) }, + { HRDATADF(CONF, iu_port_a.conf, 8, "Config", conf_bits) }, + { BRDATAD(DATA, iu_port_a.rxbuf, 16, 8, IU_BUF_SIZE, "Data") }, { NULL } }; -DEVICE iu_dev = { - "IU", iu_unit, iu_reg, NULL, - 3, 8, 32, 1, 8, 8, - NULL, NULL, &iu_reset, +UNIT tti_a_unit = { UDATA(&iu_svc_tti_a, UNIT_IDLE, 0), TMLN_SPD_9600_BPS }; + +DEVICE tti_a_dev = { + "TTIA", &tti_a_unit, tti_a_reg, NULL, + 1, 8, 32, 1, 8, 8, + NULL, NULL, &tti_a_reset, NULL, NULL, NULL, NULL, DEV_DEBUG, 0, sys_deb_tab }; +/* TTO (Port A) data structures */ + +REG tto_a_reg[] = { + { HRDATADF(STAT, iu_port_a.stat, 8, "Status", sr_bits) }, + { HRDATADF(ISTAT, iu_state.istat, 8, "Interrupt Status", isr_bits) }, + { HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") }, + { HRDATADF(ACR, iu_state.acr, 8, "Auxiliary Control Register", acr_bits) }, + { HRDATAD(DATA, iu_port_a.txbuf, 8, "Data") }, + { NULL } +}; + +UNIT tto_a_unit = { UDATA(&iu_svc_tto_a, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; + +DEVICE tto_a_dev = { + "TTOA", &tto_a_unit, tto_a_reg, NULL, + 1, 8, 32, 1, 8, 8, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + DEV_DEBUG, 0, sys_deb_tab +}; + +/* TTI (Port B) data structures */ + +REG tti_b_reg[] = { + { HRDATADF(STAT, iu_port_b.stat, 8, "Status", sr_bits) }, + { HRDATADF(CONF, iu_port_b.conf, 8, "Config", conf_bits) }, + { BRDATAD(DATA, iu_port_b.rxbuf, 16, 8, IU_BUF_SIZE, "Data") }, + { NULL } +}; + +UNIT tti_b_unit = { UDATA(&iu_svc_tti_b, UNIT_IDLE, 0), TMLN_SPD_9600_BPS }; + +DEVICE tti_b_dev = { + "TTIB", &tti_b_unit, tti_b_reg, NULL, + 1, 8, 32, 1, 8, 8, + NULL, NULL, &tti_b_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG, 0, sys_deb_tab +}; + +/* TTO (Port B) data structures */ + +REG tto_b_reg[] = { + { HRDATADF(STAT, iu_port_b.stat, 8, "Status", sr_bits) }, + { HRDATADF(ISTAT, iu_state.istat, 8, "Interrupt Status", isr_bits) }, + { HRDATAD(IMR, iu_state.imr, 8, "Interrupt Mask") }, + { HRDATADF(ACR, iu_state.acr, 8, "Auxiliary Control Register", acr_bits) }, + { HRDATAD(DATA, iu_port_b.txbuf, 8, "Data") }, + { NULL } +}; + +UNIT tto_b_unit = { UDATA(&iu_svc_tto_b, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; + +DEVICE tto_b_dev = { + "TTOB", &tto_b_unit, tto_b_reg, NULL, + 1, 8, 32, 1, 8, 8, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + DEV_DEBUG, 0, sys_deb_tab +}; + +/* IU Timer data structures */ + +REG iu_timer_reg[] = { + { HRDATAD(CTR_SET, iu_timer_state.c_set, 16, "Counter Setting") }, + { NULL } +}; + +UNIT iu_timer_unit = { UDATA(&iu_svc_timer, 0, 0) }; + +DEVICE iu_timer_dev = { + "IUTIMER", &iu_timer_unit, iu_timer_reg, NULL, + 1, 8, 32, 1, 8, 8, + NULL, NULL, &iu_timer_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG, 0, sys_deb_tab +}; + + void increment_modep_a() { iu_increment_a = FALSE; - iu_state.port[PORT_A].modep++; + iu_port_a.modep++; - if (iu_state.port[PORT_A].modep > 1) { - iu_state.port[PORT_A].modep = 0; + if (iu_port_a.modep > 1) { + iu_port_a.modep = 0; } } void increment_modep_b() { iu_increment_b = FALSE; - iu_state.port[PORT_B].modep++; + iu_port_b.modep++; - if (iu_state.port[PORT_B].modep > 1) { - iu_state.port[PORT_B].modep = 0; + if (iu_port_b.modep > 1) { + iu_port_b.modep = 0; } } -void iu_txrdy_irq(uint8 portno) { - uint8 irq_mask = (uint8) (1u << (portno * 4)); - - if ((iu_state.imr & irq_mask) && - (iu_state.port[portno].conf & TX_EN) && - (iu_state.port[portno].stat & STS_TXR)) { - sim_debug(EXECUTE_MSG, &iu_dev, - "Firing IU TTY IRQ 13 ON TX/State Change\n"); +void iu_txrdy_a_irq() { + if ((iu_state.imr & ISTS_TAI) && + (iu_port_a.conf & TX_EN) && + (iu_port_a.stat & STS_TXR)) { + sim_debug(EXECUTE_MSG, &tto_a_dev, + "Firing IU TTY IRQ 13 ON TX/State Change: PORT A\n"); csr_data |= CSRUART; } } -t_stat iu_reset(DEVICE *dptr) +void iu_txrdy_b_irq() { + if ((iu_state.imr & ISTS_TBI) && + (iu_port_b.conf & TX_EN) && + (iu_port_b.stat & STS_TXR)) { + sim_debug(EXECUTE_MSG, &tto_b_dev, + "Firing IU TTY IRQ 13 ON TX/State Change: PORT B\n"); + csr_data |= CSRUART; + } +} + +t_stat iu_reset() { - uint8 portno; + t_stat result; - memset(&iu_state, 0, sizeof(struct iu_state)); - - iu_state.opcr = 0; - - if (!sim_is_active(&iu_unit[UNIT_CONSOLE_TTI])) { - iu_unit[UNIT_CONSOLE_TTI].wait = IU_TTY_DELAY; - sim_activate(&iu_unit[UNIT_CONSOLE_TTI], - iu_unit[UNIT_CONSOLE_TTI].wait); + result = tti_a_reset(&tti_a_dev); + if (result != SCPE_OK) { + return result; } - for (portno = 0; portno < 2; portno++) { - iu_state.port[portno].buf = 0; - iu_state.port[portno].modep = 0; - iu_state.port[portno].conf = 0; - iu_state.port[portno].stat = 0; + result = tti_b_reset(&tti_b_dev); + if (result != SCPE_OK) { + return result; + } + + result = iu_timer_reset(&iu_timer_dev); + if (result != SCPE_OK) { + return result; } return SCPE_OK; } -t_stat iu_svc_tti(UNIT *uptr) +t_stat tti_a_reset(DEVICE *dptr) +{ + memset(&iu_state, 0, sizeof(IU_STATE)); + memset(&iu_port_a, 0, sizeof(IU_PORT)); + + /* Start the TTI A polling loop */ + if (!sim_is_active(&tti_a_unit)) { + sim_activate(&tti_a_unit, tti_a_unit.wait); + } + + return SCPE_OK; +} + +t_stat tti_b_reset(DEVICE *dtpr) +{ + memset(&iu_state, 0, sizeof(IU_STATE)); + memset(&iu_port_b, 0, sizeof(IU_PORT)); + + /* Start the TTI B polling loop */ + if (!sim_is_active(&tti_b_unit)) { + sim_activate(&tti_b_unit, tti_b_unit.wait); + } + + return SCPE_OK; +} + +t_stat iu_timer_reset(DEVICE *dptr) +{ + memset(&iu_timer_state, 0, sizeof(IU_TIMER_STATE)); + + return SCPE_OK; +} + +/* Service routines */ + +t_stat iu_svc_tti_a(UNIT *uptr) { int32 temp; @@ -188,13 +327,24 @@ t_stat iu_svc_tti(UNIT *uptr) return temp; } - if (iu_state.port[PORT_A].conf & RX_EN) { - iu_state.port[PORT_A].buf = (temp & 0xff); - iu_state.port[PORT_A].stat |= STS_RXR; + sim_debug(READ_MSG, &tti_a_dev, + ">>> TTIA: Receive %02x (%c)\n", + temp & 0xff, temp & 0xff); + + if (iu_port_a.conf & RX_EN) { + if ((iu_port_a.stat & STS_FFL == 0) && iu_port_a.w_p == iu_port_a.r_p) { + sim_debug(READ_MSG, &tti_a_dev, + ">>> FIFO FULL ON KEYBOARD READ!!!! <<<\n"); + iu_port_a.stat |= STS_FFL; + } else { + iu_port_a.rxbuf[iu_port_a.w_p] = (temp & 0xff); + iu_port_a.w_p = (iu_port_a.w_p + 1) % IU_BUF_SIZE; + } + iu_port_a.stat |= STS_RXR; iu_state.istat |= ISTS_RAI; if (iu_state.imr & 0x02) { - sim_debug(EXECUTE_MSG, &iu_dev, - "Firing IU TTY IRQ 13 ON RECEIVE (%c)\n", + sim_debug(EXECUTE_MSG, &tti_a_dev, + "Firing IRQ 13 ON TTI A RECEIVE (%c)\n", (temp & 0xff)); csr_data |= CSRUART; } @@ -203,16 +353,27 @@ t_stat iu_svc_tti(UNIT *uptr) return SCPE_OK; } -t_stat iu_svc_tto(UNIT *uptr) +t_stat iu_svc_tti_b(UNIT *uptr) { - sim_debug(EXECUTE_MSG, &iu_dev, - "Calling iu_txrdy_irq on iu_svc_tto\n"); + sim_clock_coschedule_tmr_abs(uptr, TMR_CLK, 2); - iu_txrdy_irq(PORT_A); + /* TODO: Handle TTIB as a terminal */ return SCPE_OK; } +t_stat iu_svc_tto_a(UNIT *uptr) +{ + iu_txrdy_a_irq(); + return SCPE_OK; +} + +t_stat iu_svc_tto_b(UNIT *uptr) +{ + iu_txrdy_b_irq(); + return SCPE_OK; +} + t_stat iu_svc_timer(UNIT *uptr) { iu_state.istat |= ISTS_CRI; @@ -255,16 +416,20 @@ uint32 iu_read(uint32 pa, size_t size) switch (reg) { case MR12A: - modep = iu_state.port[PORT_A].modep; - data = iu_state.port[PORT_A].mode[modep]; + modep = iu_port_a.modep; + data = iu_port_a.mode[modep]; iu_increment_a = TRUE; break; case SRA: - data = iu_state.port[PORT_A].stat; + data = iu_port_a.stat; break; case RHRA: - data = iu_state.port[PORT_A].buf; - iu_state.port[PORT_A].stat &= ~STS_RXR; + data = iu_port_a.rxbuf[iu_port_a.r_p]; + iu_port_a.r_p = (iu_port_a.r_p + 1) % IU_BUF_SIZE; + sim_debug(READ_MSG, &tti_a_dev, + "[%08x] RHRA = %02x (%c)\n", + R[NUM_PC], (data & 0xff), (data & 0xff)); + iu_port_a.stat &= ~(STS_RXR|STS_FFL); iu_state.istat &= ~ISTS_RAI; csr_data &= ~CSRUART; break; @@ -278,22 +443,29 @@ uint32 iu_read(uint32 pa, size_t size) data = iu_state.istat; break; case CTU: - data = (iu_state.c_set >> 8) & 0xff; + data = (iu_timer_state.c_set >> 8) & 0xff; break; case CTL: - data = iu_state.c_set & 0xff; + data = iu_timer_state.c_set & 0xff; break; case MR12B: - modep = iu_state.port[PORT_B].modep; - data = iu_state.port[PORT_B].mode[modep]; + modep = iu_port_b.modep; + data = iu_port_b.mode[modep]; iu_increment_b = TRUE; break; case SRB: - data = iu_state.port[PORT_B].stat; + data = iu_port_b.stat; + sim_debug(READ_MSG, &tti_b_dev, + "[%08x] SRB = %02x\n", + R[NUM_PC], (data & 0xff)); break; case RHRB: - data = iu_state.port[PORT_B].buf; - iu_state.port[PORT_B].stat &= ~STS_RXR; + data = iu_port_b.rxbuf[iu_port_b.r_p]; + iu_port_b.r_p = (iu_port_b.r_p + 1) % IU_BUF_SIZE; + sim_debug(READ_MSG, &tti_b_dev, + "[%08x] RHRB = %02x (%c)\n", + R[NUM_PC], (data & 0xff), (data & 0xff)); + iu_port_b.stat &= ~(STS_RXR|STS_FFL); iu_state.istat &= ~ISTS_RBI; break; case INPRT: @@ -304,19 +476,25 @@ uint32 iu_read(uint32 pa, size_t size) case START_CTR: data = 0; iu_state.istat &= ~ISTS_CRI; - delay = (uint32) (IU_TIMER_STP * iu_state.c_set); - sim_activate_abs(&iu_unit[UNIT_IU_TIMER], (int32) DELAY_US(delay)); + delay = (uint32) (IU_TIMER_STP * iu_timer_state.c_set); + sim_activate_abs(&iu_timer_unit, (int32) DELAY_US(delay)); + sim_debug(READ_MSG, &iu_timer_dev, + "[%08x] Activating IU timer to fire in %04x steps\n", + R[NUM_PC], iu_timer_state.c_set); break; case STOP_CTR: data = 0; iu_state.istat &= ~ISTS_CRI; csr_data &= ~CSRUART; - sim_cancel(&iu_unit[UNIT_IU_TIMER]); + sim_cancel(&iu_timer_unit); + sim_debug(READ_MSG, &iu_timer_dev, + "[%08x] Cancelling IU timer\n", + R[NUM_PC]); break; case 17: /* Clear DMAC interrupt */ data = 0; - iu_state.drqa = FALSE; - iu_state.drqb = FALSE; + iu_port_a.drq = FALSE; + iu_port_b.drq = FALSE; csr_data &= ~CSRDMA; break; default: @@ -336,21 +514,39 @@ void iu_write(uint32 pa, uint32 val, size_t size) switch (reg) { case MR12A: - modep = iu_state.port[PORT_A].modep; - iu_state.port[PORT_A].mode[modep] = val & 0xff; + modep = iu_port_a.modep; + iu_port_a.mode[modep] = val & 0xff; iu_increment_a = TRUE; break; case CSRA: /* Set baud rate - not implemented */ break; case CRA: /* Command A */ + sim_debug(WRITE_MSG, &tti_a_dev, + "[%08x] CRA = %02x\n", + R[NUM_PC], (val & 0xff)); iu_w_cmd(PORT_A, (uint8) val); break; case THRA: /* TX/RX Buf A */ + sim_debug(WRITE_MSG, &tto_a_dev, + "[%08x] THRA = %02x (%c)\n", + R[NUM_PC], (val & 0xff), (val & 0xff)); /* Loopback mode */ - if ((iu_state.port[PORT_A].mode[1] & 0xc0) == 0x80) { - iu_state.port[PORT_A].buf = (uint8) val; - iu_state.port[PORT_A].stat |= STS_RXR; + if ((iu_port_a.mode[1] & 0xc0) == 0x80) { + iu_port_a.txbuf = (uint8) val; + + /* This is also a Receive */ + if ((iu_port_a.stat & STS_FFL) == 0) { + iu_port_a.rxbuf[iu_port_a.w_p] = (uint8) val; + iu_port_a.w_p = (iu_port_a.w_p + 1) % IU_BUF_SIZE; + if (iu_port_a.w_p == iu_port_b.r_p) { + sim_debug(WRITE_MSG, &tto_a_dev, + ">>> FIFO FULL ON LOOPBACK THRA! <<<"); + iu_port_a.stat |= STS_FFL; + } + } + + iu_port_a.stat |= STS_RXR; iu_state.istat |= ISTS_RAI; } else { iu_tx(PORT_A, (uint8) val); @@ -361,42 +557,61 @@ void iu_write(uint32 pa, uint32 val, size_t size) iu_state.acr = (uint8) val; break; case IMR: + sim_debug(WRITE_MSG, &tti_a_dev, + "[%08x] IMR = %02x\n", + R[NUM_PC], (val & 0xff)); iu_state.imr = (uint8) val; csr_data &= ~CSRUART; /* Possibly cause an interrupt */ - sim_debug(EXECUTE_MSG, &iu_dev, - ">>> calling iu_txrdy_irq() on IMR write.\n"); - iu_txrdy_irq(PORT_A); - iu_txrdy_irq(PORT_B); + iu_txrdy_a_irq(PORT_A); + iu_txrdy_b_irq(PORT_B); break; case CTUR: /* Counter/Timer Upper Preset Value */ /* Clear out high byte */ - iu_state.c_set &= 0x00ff; + iu_timer_state.c_set &= 0x00ff; /* Set high byte */ - iu_state.c_set |= (val & 0xff) << 8; + iu_timer_state.c_set |= (val & 0xff) << 8; break; case CTLR: /* Counter/Timer Lower Preset Value */ /* Clear out low byte */ - iu_state.c_set &= 0xff00; + iu_timer_state.c_set &= 0xff00; /* Set low byte */ - iu_state.c_set |= (val & 0xff); + iu_timer_state.c_set |= (val & 0xff); break; case MR12B: - modep = iu_state.port[PORT_B].modep; - iu_state.port[PORT_B].mode[modep] = val & 0xff; + modep = iu_port_b.modep; + iu_port_b.mode[modep] = val & 0xff; iu_increment_b = TRUE; break; case CRB: /* Command B */ + sim_debug(WRITE_MSG, &tti_b_dev, + "[%08x] CRB = %02x\n", + R[NUM_PC], (val & 0xff)); iu_w_cmd(PORT_B, (uint8) val); break; case CSRB: break; case THRB: /* TX/RX Buf B */ + sim_debug(WRITE_MSG, &tto_b_dev, + "[%08x] THRB = %02x (%c)\n", + R[NUM_PC], (val & 0xff), (val & 0xff)); /* Loopback mode */ - if ((iu_state.port[PORT_B].mode[1] & 0xc0) == 0x80) { - iu_state.port[PORT_B].buf = (uint8) val; - iu_state.port[PORT_B].stat |= STS_RXR; - iu_state.istat |= ISTS_RAI; + if ((iu_port_b.mode[1] & 0xc0) == 0x80) { + iu_port_a.txbuf = (uint8) val; + + /* This is also a Receive */ + if ((iu_port_b.stat & STS_FFL) == 0) { + iu_port_b.rxbuf[iu_port_b.w_p] = (uint8) val; + iu_port_b.w_p = (iu_port_b.w_p + 1) % IU_BUF_SIZE; + if (iu_port_b.w_p == iu_port_b.r_p) { + sim_debug(WRITE_MSG, &tto_b_dev, + ">>> FIFO FULL ON LOOPBACK THRB! <<<"); + iu_port_b.stat |= STS_FFL; + } + } + + iu_port_b.stat |= STS_RXR; + iu_state.istat |= ISTS_RBI; } else { iu_tx(PORT_B, (uint8) val); } @@ -415,99 +630,115 @@ void iu_write(uint32 pa, uint32 val, size_t size) void iua_drq_handled() { - sim_debug(EXECUTE_MSG, &iu_dev, - "Firing IU TTY IRQ 13 On DRQ Handled\n"); - + sim_debug(EXECUTE_MSG, &tto_a_dev, + "Firing IU IRQ 13 on DRQ (A) Hanlded\n"); csr_data |= CSRDMA; } void iub_drq_handled() { - sim_debug(EXECUTE_MSG, &iu_dev, - ">>> DRQB handled.\n"); + sim_debug(EXECUTE_MSG, &tto_a_dev, + "Firing IU IRQ 13 on DRQ (B) Hanlded\n"); + csr_data |= CSRDMA; } static SIM_INLINE void iu_tx(uint8 portno, uint8 val) { - struct port *p; + IU_PORT *p; + UNIT *uptr; - p = &iu_state.port[portno]; + if (portno == 0) { + p = &iu_port_a; + uptr = &tto_a_unit; + } else { + p = &iu_port_b; + uptr = &tto_b_unit; + } - p->buf = val; + p->txbuf = val; if (p->conf & TX_EN) { - sim_debug(EXECUTE_MSG, &iu_dev, - "[%08x] TRANSMIT: %02x (%c)\n", - R[NUM_PC], val, val); p->stat &= ~(STS_TXR|STS_TXE); iu_state.istat &= ~(1 << (portno*4)); - /* Write the character to the SIMH console */ - sim_putchar(p->buf); + if (portno == PORT_A) { + /* Write the character to the SIMH console */ + sim_putchar(val); + } /* The buffer is now empty, we've transmitted, so set TXR */ p->stat |= STS_TXR; iu_state.istat |= (1 << (portno*4)); /* Possibly cause an interrupt */ - sim_activate_abs(&iu_unit[UNIT_CONSOLE_TTO], - iu_unit[UNIT_CONSOLE_TTO].wait); + sim_activate_abs(uptr, uptr->wait); } } static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd) { + + IU_PORT *p; + + if (portno == 0) { + p = &iu_port_a; + } else { + p = &iu_port_b; + } + /* Enable or disable transmitter */ /* Disable always wins, if both are set */ if (cmd & CMD_DTX) { - iu_state.port[portno].conf &= ~TX_EN; - iu_state.port[portno].stat &= ~STS_TXR; - iu_state.port[portno].stat &= ~STS_TXE; - iu_state.drqa = FALSE; - sim_debug(EXECUTE_MSG, &iu_dev, - ">>> Disabling transmitter.\n"); + p->conf &= ~TX_EN; + p->stat &= ~STS_TXR; + p->stat &= ~STS_TXE; + p->drq = FALSE; } else if (cmd & CMD_ETX) { - iu_state.port[portno].conf |= TX_EN; + p->conf |= TX_EN; /* TXE and TXR are always set by an ENABLE */ - iu_state.port[portno].stat |= STS_TXR; - iu_state.port[portno].stat |= STS_TXE; + p->stat |= STS_TXR; + p->stat |= STS_TXE; + p->drq = TRUE; iu_state.istat |= 1 << (portno*4); - iu_state.drqa = TRUE; - sim_debug(EXECUTE_MSG, &iu_dev, - ">>> Calling iu_txrdy_irq() on TX Enable\n"); - iu_txrdy_irq(portno); + if (portno == 0) { + iu_txrdy_a_irq(); + } else { + iu_txrdy_b_irq(); + } } /* Enable or disable receiver. */ /* Disable always wins, if both are set */ if (cmd & CMD_DRX) { - iu_state.port[portno].conf &= ~RX_EN; - iu_state.port[portno].stat &= ~STS_RXR; + p->conf &= ~RX_EN; + p->stat &= ~STS_RXR; } else if (cmd & CMD_ERX) { - iu_state.port[portno].conf |= RX_EN; + p->conf |= RX_EN; } /* Command register bits 6-4 have special meaning */ switch ((cmd >> CMD_MISC_SHIFT) & CMD_MISC_MASK) { case 1: /* Causes the Channel A MR pointer to point to MR1. */ - iu_state.port[portno].modep = 0; + p->modep = 0; break; case 2: /* Reset receiver. Resets the Channel's receiver as if a hardware reset had been applied. The receiver is disabled and the FIFO is flushed. */ - iu_state.port[portno].stat &= ~STS_RXR; - iu_state.port[portno].conf &= ~RX_EN; - iu_state.port[portno].buf = 0; + p->stat &= ~STS_RXR; + p->conf &= ~RX_EN; + p->w_p = 0; + p->r_p = 0; break; case 3: /* Reset transmitter. Resets the Channel's transmitter as if a hardware reset had been applied. */ - iu_state.port[portno].stat &= ~STS_TXR; - iu_state.port[portno].stat &= ~STS_TXE; - iu_state.port[portno].conf &= ~TX_EN; - iu_state.port[portno].buf = 0; + p->stat &= ~STS_TXR; + p->stat &= ~STS_TXE; + p->conf &= ~TX_EN; + p->w_p = 0; + p->r_p = 0; break; case 4: /* Reset error status. Clears the Channel's Received Break, @@ -516,7 +747,7 @@ static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd) (although RB, PE and FE bits will also be cleared) and in block mode to clear all error status after a block of data has been received. */ - iu_state.port[portno].stat &= ~(STS_FER|STS_PER|STS_OER); + p->stat &= ~(STS_FER|STS_PER|STS_OER); break; case 5: /* Reset Channel's break change interrupt. Causes the Channel diff --git a/3B2/3b2_iu.h b/3B2/3b2_iu.h index 4db16d95..5c80806c 100644 --- a/3B2/3b2_iu.h +++ b/3B2/3b2_iu.h @@ -62,9 +62,6 @@ #define MODE_V_CHM 6 /* Channel mode */ #define MODE_M_CHM 0x3 -#define PORT_A 0 -#define PORT_B 1 - /* Used by the DMAC */ #define IUA_DATA_REG 3 #define IUB_DATA_REG 11 @@ -120,11 +117,16 @@ #define UM_MASK 0x70 #define UM_SHIFT 4 +#define PORT_A 0 +#define PORT_B 1 + #define IU_MODE(x) ((x & UM_MASK) >> UM_SHIFT) -extern DEVICE iu_dev; - -#define IU_TTY_DELAY 25000 +extern DEVICE tti_a_dev; +extern DEVICE tto_a_dev; +extern DEVICE tti_b_dev; +extern DEVICE tto_b_dev; +extern DEVICE iu_timer_dev; #define IUBASE 0x49000 #define IUSIZE 0x100 @@ -146,42 +148,56 @@ extern DEVICE iu_dev; #define IU_TIMER_STP 4.33792 -struct port { - uint8 stat; /* Port Status */ - uint8 cmd; /* Command */ - uint8 mode[2]; /* Two mode buffers */ - uint8 modep; /* Point to mode[0] or mode[1] */ - uint8 conf; /* Configuration bits */ - uint8 buf; /* Character data */ -}; +#define IU_BUF_SIZE 3 + +typedef struct iu_port { + uint8 stat; /* Port Status */ + uint8 cmd; /* Command */ + uint8 mode[2]; /* Two mode buffers */ + uint8 modep; /* Point to mode[0] or mode[1] */ + uint8 conf; /* Configuration bits */ + uint8 txbuf; /* Transmit Holding Register */ + uint8 rxbuf[IU_BUF_SIZE]; /* Receive Holding Register (3 bytes) */ + uint8 w_p; /* Buffer Write Pointer */ + uint8 r_p; /* Buffer Read Pointer */ + t_bool drq; /* DRQ enabled */ +} IU_PORT; typedef struct iu_state { uint8 istat; /* Interrupt Status */ uint8 imr; /* Interrupt Mask Register */ - uint16 c_set; /* Timer / Counter Setting */ - int32 c_val; /* Timer / Counter Value */ - t_bool c_en; /* Counter Enabled */ - t_bool drqa; /* Port A DRQ */ - t_bool drqb; /* Port B DRQ */ uint8 acr; uint8 opcr; /* Output Port Configuration */ uint8 inprt; /* Input Port Data */ uint8 ipcr; /* Input Port Change Register */ - struct port port[2]; /* Port A and B */ } IU_STATE; -extern IU_STATE iu_state; +typedef struct iu_timer_state { + uint16 c_set; + t_bool c_en; +} IU_TIMER_STATE; + +extern IU_PORT iu_port_a; +extern IU_PORT iu_port_b; + +/* Global reset */ +extern t_stat iu_reset(); /* Function prototypes */ - -t_stat iu_reset(DEVICE *dptr); -t_stat iu_svc_tti(UNIT *uptr); -t_stat iu_svc_tto(UNIT *uptr); +t_stat tti_a_reset(DEVICE *dptr); +t_stat tti_b_reset(DEVICE *dptr); +t_stat iu_timer_reset(DEVICE *dptr); +t_stat iu_svc_tti_a(UNIT *uptr); +t_stat iu_svc_tto_a(UNIT *uptr); +t_stat iu_svc_tti_b(UNIT *uptr); +t_stat iu_svc_tto_b(UNIT *uptr); t_stat iu_svc_timer(UNIT *uptr); uint32 iu_read(uint32 pa, size_t size); void iu_write(uint32 pa, uint32 val, size_t size); void iua_drq_handled(); void iub_drq_handled(); +void iu_txrdy_a_irq(); +void iu_txrdy_b_irq(); static SIM_INLINE void iu_tx(uint8 portno, uint8 val); static SIM_INLINE void iu_w_buf(uint8 portno, uint8 val); diff --git a/3B2/3b2_sys.c b/3B2/3b2_sys.c index 5ae8704c..99799e37 100644 --- a/3B2/3b2_sys.c +++ b/3B2/3b2_sys.c @@ -54,7 +54,11 @@ DEVICE *sim_devices[] = { &tod_dev, &nvram_dev, &csr_dev, - &iu_dev, + &tti_a_dev, + &tto_a_dev, + &tti_b_dev, + &tto_b_dev, + &iu_timer_dev, &dmac_dev, &if_dev, &id_dev, diff --git a/3B2/3b2_sysdev.c b/3B2/3b2_sysdev.c index d828ab31..94131c8f 100644 --- a/3B2/3b2_sysdev.c +++ b/3B2/3b2_sysdev.c @@ -151,7 +151,7 @@ void csr_write(uint32 pa, uint32 val, size_t size) csr_data &= ~CSRPARE; break; case 0x0b: /* Set System Reset Request */ - iu_reset(&iu_dev); + iu_reset(); cpu_reset(&cpu_dev); cpu_boot(0, &cpu_dev); break;