diff --git a/3B2/3b2_cpu.c b/3B2/3b2_cpu.c index becc7de8..42f73baa 100644 --- a/3B2/3b2_cpu.c +++ b/3B2/3b2_cpu.c @@ -1,4 +1,4 @@ -/* 3b2_cpu.c: AT&T 3B2 Model 400 CPU (WE32100) Implementation + /* 3b2_cpu.c: AT&T 3B2 Model 400 CPU (WE32100) Implementation Copyright (c) 2017, Seth J. Morabito diff --git a/3B2/3b2_defs.h b/3B2/3b2_defs.h index cfa235c6..fdc9ed01 100644 --- a/3B2/3b2_defs.h +++ b/3B2/3b2_defs.h @@ -329,6 +329,40 @@ extern t_bool cpu_km; typedef void (*callback)(void); /* global symbols from the DMAC */ +typedef struct { + uint8 page; + uint16 addr; /* Original addr */ + uint16 wcount; /* Original wcount */ + uint16 addr_c; /* Current addr */ + int32 wcount_c; /* Current word-count */ + uint16 ptr; /* Pointer into memory */ +} dma_channel; + +typedef struct { + /* Byte (high/low) flip-flop */ + uint8 bff; + + /* Address and count registers for channels 0-3 */ + dma_channel channels[4]; + + /* DMAC programmable registers */ + uint8 command; + uint8 mode; + uint8 request; + uint8 mask; + uint8 status; +} DMA_STATE; + +extern DMA_STATE dma_state; + +static SIM_INLINE uint32 dma_address(uint8 channel, uint32 offset, t_bool r) { + uint32 addr; + addr = (PHYS_MEM_BASE + dma_state.channels[channel].addr + offset); + /* The top bit of the page address is a R/W bit, so we mask it here */ + addr |= (uint32) (((uint32)dma_state.channels[channel].page & 0x7f) << 16); + return addr; +} + extern DEVICE dmac_dev; /* global symbols from the CSR */ diff --git a/3B2/3b2_dmac.c b/3B2/3b2_dmac.c index 534025f5..677d9bd6 100644 --- a/3B2/3b2_dmac.c +++ b/3B2/3b2_dmac.c @@ -52,12 +52,12 @@ DEVICE dmac_dev = { DEV_DEBUG, 0, sys_deb_tab }; -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_console.drq, iua_drq_handled}, - {DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_contty.drq, iub_drq_handled}, - {0, 0, NULL, NULL } +dmac_dma_handler device_dma_handlers[] = { + {DMA_ID_CHAN, IDBASE+ID_DATA_REG, &id_drq, dmac_generic_dma, id_after_dma}, + {DMA_IF_CHAN, IFBASE+IF_DATA_REG, &if_state.drq, dmac_generic_dma, if_after_dma}, + {DMA_IUA_CHAN, IUBASE+IUA_DATA_REG, &iu_console.drq, iu_dma, NULL}, + {DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_contty.drq, iu_dma, NULL}, + {0, 0, NULL, NULL, NULL } }; t_stat dmac_reset(DEVICE *dptr) @@ -72,6 +72,7 @@ t_stat dmac_reset(DEVICE *dptr) dma_state.channels[i].wcount = 0; dma_state.channels[i].addr_c = 0; dma_state.channels[i].wcount_c = 0; + dma_state.channels[i].ptr = 0; } return SCPE_OK; @@ -209,6 +210,7 @@ void dmac_program(uint8 reg, uint8 val) channel->wcount &= ~(0xff << dma_state.bff * 8); channel->wcount |= (val & 0xff) << (dma_state.bff * 8); channel->wcount_c = channel->wcount; + channel->ptr = 0; sim_debug(WRITE_MSG, &dmac_dev, "Set word count channel %d byte %d = %08x\n", chan_num, dma_state.bff, channel->wcount); @@ -268,9 +270,12 @@ void dmac_program(uint8 reg, uint8 val) dma_state.command = 0; dma_state.status = 0; for (i = 0; i < 4; i++) { + dma_state.channels[i].page = 0; dma_state.channels[i].addr = 0; dma_state.channels[i].wcount = 0; - dma_state.channels[i].page = 0; + dma_state.channels[i].addr_c = 0; + dma_state.channels[i].wcount_c = 0; + dma_state.channels[i].ptr = 0; } break; case 15: /* Write All Mask Register Bits */ @@ -352,69 +357,56 @@ void dmac_write(uint32 pa, uint32 val, size_t size) } } -static SIM_INLINE uint32 dma_address(uint8 channel, uint32 offset, t_bool r) { - uint32 addr; - - addr = (PHYS_MEM_BASE + - dma_state.channels[channel].addr + - offset); - - /* The top bit of the page address is a R/W bit, so we mask it here */ - addr |= (uint32) (((uint32)dma_state.channels[channel].page & 0x7f) << 16); - - return addr; -} - -void dmac_transfer(uint8 channel, uint32 service_address) +void dmac_generic_dma(uint8 channel, uint32 service_address) { uint8 data; int32 i; - uint16 offset; uint32 addr; - dma_channel *chan = &dma_state.channels[channel]; + i = (int32) chan->wcount_c; + /* TODO: This does not handle decrement-mode transfers, which don't seem to be used in SVR3 */ switch ((dma_state.mode >> 2) & 0xf) { case DMA_MODE_VERIFY: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_transfer channel=%d] unhandled VERIFY request.\n", + "[%08x] [dmac_generic_dma channel=%d] unhandled VERIFY request.\n", R[NUM_PC], channel); break; case DMA_MODE_WRITE: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_transfer channel=%d] write: %d bytes from %08x\n", + "[%08x] [dmac_generic_dma channel=%d] write: %d bytes from %08x\n", R[NUM_PC], channel, chan->wcount + 1, dma_address(channel, 0, TRUE)); - offset = 0; - for (i = chan->wcount; i >= 0; i--) { - addr = dma_address(channel, offset, TRUE); - chan->addr_c = dma_state.channels[channel].addr + offset; - offset++; + for (; i >= 0; i--) { + chan->wcount_c--; + addr = dma_address(channel, chan->ptr, TRUE); + chan->addr_c = dma_state.channels[channel].addr + chan->ptr; + chan->ptr++; data = pread_b(service_address); write_b(addr, data); } break; case DMA_MODE_READ: sim_debug(EXECUTE_MSG, &dmac_dev, - "[%08x] [dmac_transfer channel=%d] read: %d bytes to %08x\n", + "[%08x] [dmac_generic_dma channel=%d] read: %d bytes to %08x\n", R[NUM_PC], channel, chan->wcount + 1, dma_address(channel, 0, TRUE)); - offset = 0; - for (i = chan->wcount; i >= 0; i--) { - addr = dma_address(channel, offset++, TRUE); - chan->addr_c = dma_state.channels[channel].addr + offset; + for (; i >= 0; i--) { + chan->wcount_c = i; + addr = dma_address(channel, chan->ptr++, TRUE); + chan->addr_c = dma_state.channels[channel].addr + chan->ptr; data = pread_b(addr); write_b(service_address, data); } break; } - /* End of Process must set the IF channel's mask bit */ + /* End of Process must set the channel's mask bit */ dma_state.mask |= (1 << channel); dma_state.status |= (1 << channel); } @@ -424,16 +416,16 @@ void dmac_transfer(uint8 channel, uint32 service_address) */ void dmac_service_drqs() { - dmac_drq_handler *h; + dmac_dma_handler *h; - for (h = &dmac_drq_handlers[0]; h->drq != NULL; h++) { + for (h = &device_dma_handlers[0]; h->drq != NULL; h++) { /* Only trigger if the channel has a DRQ set and its channel's mask bit is 0 */ if (*h->drq && ((dma_state.mask >> h->channel) & 0x1) == 0) { - dmac_transfer(h->channel, h->service_address); - *h->drq = FALSE; /* Immediately clear DRQ state */ - if (h->handled_callback != NULL) { - h->handled_callback(); + h->dma_handler(h->channel, h->service_address); + /* Each handler is responsible for clearing its own DRQ line! */ + if (h->after_dma_callback != NULL) { + h->after_dma_callback(); } } } diff --git a/3B2/3b2_dmac.h b/3B2/3b2_dmac.h index 55bc30a9..4ff3b9ec 100644 --- a/3B2/3b2_dmac.h +++ b/3B2/3b2_dmac.h @@ -72,42 +72,19 @@ #define DMA_IF_READ (IFBASE + IF_DATA_REG) - -typedef struct { - uint8 page; - uint16 addr; /* Original addr */ - uint16 wcount; /* Original wcount */ - uint16 addr_c; /* Current addr */ - uint16 wcount_c; /* Current word-count */ -} dma_channel; - -typedef struct { - /* Byte (high/low) flip-flop */ - uint8 bff; - - /* Address and count registers for channels 0-3 */ - dma_channel channels[4]; - - /* DMAC programmable registers */ - uint8 command; - uint8 mode; - uint8 request; - uint8 mask; - uint8 status; -} DMA_STATE; - typedef struct { uint8 channel; uint32 service_address; t_bool *drq; - void (*handled_callback)(); -} dmac_drq_handler; + void (*dma_handler)(uint8 channel, uint32 service_address); + void (*after_dma_callback)(); +} dmac_dma_handler; /* DMAC */ t_stat dmac_reset(DEVICE *dptr); uint32 dmac_read(uint32 pa, size_t size); void dmac_write(uint32 pa, uint32 val, size_t size); void dmac_service_drqs(); -void dmac_transfer(uint8 channel, uint32 service_address); +void dmac_generic_dma(uint8 channel, uint32 service_address); #endif diff --git a/3B2/3b2_id.c b/3B2/3b2_id.c index 099fe23a..522abfa8 100644 --- a/3B2/3b2_id.c +++ b/3B2/3b2_id.c @@ -939,7 +939,7 @@ void id_handle_command(uint8 val) } } -void id_drq_handled() +void id_after_dma() { id_status &= ~ID_STAT_DRQ; id_drq = FALSE; diff --git a/3B2/3b2_id.h b/3B2/3b2_id.h index 178e41d7..852a6081 100644 --- a/3B2/3b2_id.h +++ b/3B2/3b2_id.h @@ -155,6 +155,8 @@ #define ID_NUM_UNITS 2 +#define DMA_ID_SVC IDBASE+ID_DATA_REG + extern DEVICE id_dev; extern DEBTAB sys_deb_tab[]; extern t_bool id_drq; @@ -180,9 +182,8 @@ CONST char *id_description(DEVICE *dptr); t_stat id_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); void id_handle_data(uint8 val); void id_handle_command(uint8 val); +void id_after_dma(); static SIM_INLINE t_lba id_lba(uint16 cyl, uint8 head, uint8 sec); -void id_drq_handled(); - #endif diff --git a/3B2/3b2_if.c b/3B2/3b2_if.c index f946bf52..3d6e291e 100644 --- a/3B2/3b2_if.c +++ b/3B2/3b2_if.c @@ -532,7 +532,8 @@ static SIM_INLINE uint32 if_buf_offset() return pos; } -void if_drq_handled() +void if_after_dma() { + if_state.drq = FALSE; if_state.status &= ~IF_DRQ; } diff --git a/3B2/3b2_if.h b/3B2/3b2_if.h index 6a9227e2..7b867d8f 100644 --- a/3B2/3b2_if.h +++ b/3B2/3b2_if.h @@ -132,7 +132,7 @@ t_stat if_svc(UNIT *uptr); t_stat if_reset(DEVICE *dptr); uint32 if_read(uint32 pa, size_t size); void if_write(uint32 pa, uint32 val, size_t size); -void if_drq_handled(); void if_handle_command(); +void if_after_dma(); #endif diff --git a/3B2/3b2_iu.c b/3B2/3b2_iu.c index 5511dd81..6504e5c2 100644 --- a/3B2/3b2_iu.c +++ b/3B2/3b2_iu.c @@ -121,7 +121,7 @@ REG tti_reg[] = { { NULL } }; -UNIT tti_unit = { UDATA(&iu_svc_tti, UNIT_IDLE, 0), TMLN_SPD_19200_BPS }; +UNIT tti_unit = { UDATA(&iu_svc_tti, UNIT_IDLE, 0), TMLN_SPD_9600_BPS }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, NULL, @@ -198,13 +198,25 @@ UNIT contty_unit[2] = { UNIT *contty_rcv_unit = &contty_unit[0]; UNIT *contty_xmt_unit = &contty_unit[1]; +DEBTAB contty_deb_tab[] = { + {"EXEC", EXECUTE_MSG, "Execute"}, + {"XMT", TMXR_DBG_XMT, "Transmitted Data"}, + {"RCV", TMXR_DBG_RCV, "Received Data"}, + {"MDM", TMXR_DBG_MDM, "Modem Signals"}, + {"CON", TMXR_DBG_CON, "connection activities"}, + {"TRC", TMXR_DBG_TRC, "trace routine calls"}, + {"ASY", TMXR_DBG_ASY, "Asynchronous Activities"}, + {0} +}; + + DEVICE contty_dev = { "CONTTY", contty_unit, contty_reg, NULL, 1, 8, 32, 1, 8, 8, &tmxr_ex, &tmxr_dep, &contty_reset, NULL, &contty_attach, &contty_detach, NULL, DEV_DISABLE|DEV_DEBUG|DEV_MUX, - 0, sys_deb_tab, NULL, NULL, + 0, contty_deb_tab, NULL, NULL, NULL, NULL, (void *)&contty_desc, NULL @@ -235,9 +247,14 @@ uint8 bits_per_char = 7; t_stat contty_attach(UNIT *uptr, CONST char *cptr) { - t_stat r = tmxr_attach(&contty_desc, uptr, cptr); + t_stat r; TMLN *lp; + tmxr_set_modem_control_passthru(&contty_desc); + tmxr_set_line_unit(&contty_desc, 0, contty_rcv_unit); + tmxr_set_line_output_unit(&contty_desc, 0, contty_xmt_unit); + + r = tmxr_attach(&contty_desc, uptr, cptr); if (r != SCPE_OK) { tmxr_clear_modem_control_passthru(&contty_desc); return r; @@ -281,6 +298,9 @@ void iu_txrdy_a_irq() { if ((iu_state.imr & IMR_TXRA) && (iu_console.conf & TX_EN) && (iu_console.stat & STS_TXR)) { + sim_debug(EXECUTE_MSG, &tto_dev, + "[iu_txrdy_a_irq()] Firing IRQ after transmit of %02x (%c)\n", + (uint8) iu_console.txbuf, (char) iu_console.txbuf); csr_data |= CSRUART; } } @@ -289,6 +309,9 @@ void iu_txrdy_b_irq() { if ((iu_state.imr & IMR_TXRB) && (iu_contty.conf & TX_EN) && (iu_contty.stat & STS_TXR)) { + sim_debug(EXECUTE_MSG, &contty_dev, + "[iu_txrdy_b_irq()] Firing IRQ after transmit of %02x (%c)\n", + (uint8) iu_contty.txbuf, (char) iu_contty.txbuf); csr_data |= CSRUART; } } @@ -298,8 +321,6 @@ t_stat tti_reset(DEVICE *dptr) memset(&iu_state, 0, sizeof(IU_STATE)); memset(&iu_console, 0, sizeof(IU_PORT)); - tmxr_set_console_units(&tti_unit, &tto_unit); - /* Input Port logic is inverted - 0 means set */ iu_state.inprt = ~(IU_DCDA); @@ -314,6 +335,7 @@ t_stat tti_reset(DEVICE *dptr) t_stat contty_reset(DEVICE *dtpr) { char line_config[16]; + t_stat result; if (contty_ldsc == NULL) { contty_desc.ldsc = @@ -321,6 +343,12 @@ t_stat contty_reset(DEVICE *dtpr) (TMLN *)calloc(1, sizeof(*contty_ldsc)); } + tmxr_set_port_speed_control(&contty_desc); + + /* tmxr_set_line_unit(&contty_desc, 0, contty_rcv_unit); */ + /* tmxr_set_line_output_unit(&contty_desc, 0, contty_xmt_unit); */ + /* tmxr_set_console_units(&tti_unit, &tto_unit); */ + memset(&iu_state, 0, sizeof(IU_STATE)); memset(&iu_contty, 0, sizeof(IU_PORT)); brg_reg = 0; @@ -393,7 +421,16 @@ t_stat iu_svc_tti(UNIT *uptr) t_stat iu_svc_tto(UNIT *uptr) { - iu_txrdy_a_irq(); + /* If there's more DMA to do, do it */ + if (iu_console.dma && ((dma_state.mask >> DMA_IUA_CHAN) & 0x1) == 0) { + iu_dma(DMA_IUA_CHAN, IUBASE+IUA_DATA_REG); + } else { + /* The buffer is now empty, we've transmitted, so set TXR */ + iu_console.stat |= STS_TXR; + iu_state.istat |= 1; + iu_txrdy_a_irq(); + } + return SCPE_OK; } @@ -444,8 +481,20 @@ t_stat iu_svc_contty_rcv(UNIT *uptr) t_stat iu_svc_contty_xmt(UNIT *uptr) { + dma_channel *chan = &dma_state.channels[DMA_IUB_CHAN]; + tmxr_poll_tx(&contty_desc); - iu_txrdy_b_irq(); + + if (chan->wcount_c >= 0) { + /* More DMA to do */ + iu_dma(DMA_IUB_CHAN, IUBASE+IUB_DATA_REG); + } else { + /* The buffer is now empty, we've transmitted, so set TXR */ + iu_contty.stat |= STS_TXR; + iu_state.istat |= 0x10; + iu_txrdy_b_irq(); + } + return SCPE_OK; } @@ -501,9 +550,14 @@ uint32 iu_read(uint32 pa, size_t size) case RHRA: data = iu_console.rxbuf[iu_console.r_p]; iu_console.r_p = (iu_console.r_p + 1) % IU_BUF_SIZE; - iu_console.stat &= ~(STS_RXR|STS_FFL); - iu_state.istat &= ~ISTS_RAI; - csr_data &= ~CSRUART; + /* 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; + } break; case IPCR: data = iu_state.ipcr; @@ -557,8 +611,12 @@ 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: @@ -591,28 +649,8 @@ void iu_write(uint32 pa, uint32 val, size_t size) iu_w_cmd(PORT_A, bval); break; case THRA: /* TX/RX Buf A */ - /* Loopback mode */ - if ((iu_console.mode[1] & 0xc0) == 0x80) { - iu_console.txbuf = bval; - - /* This is also a Receive */ - if ((iu_console.stat & STS_FFL) == 0) { - iu_console.rxbuf[iu_console.w_p] = bval; - iu_console.w_p = (iu_console.w_p + 1) % IU_BUF_SIZE; - if (iu_console.w_p == iu_console.r_p) { - iu_console.stat |= STS_FFL; - } - } - - iu_console.stat |= STS_RXR; - iu_state.istat |= ISTS_RAI; - if (iu_state.imr & IMR_RXRA) { - csr_data |= CSRUART; - } - } else { - iu_tx(PORT_A, bval); - } - csr_data &= ~CSRUART; + iu_tx(PORT_A, bval); + sim_activate_abs(&tto_unit, tto_unit.wait); break; case ACR: /* Auxiliary Control Register */ iu_state.acr = bval; @@ -678,27 +716,8 @@ void iu_write(uint32 pa, uint32 val, size_t size) break; case THRB: /* TX/RX Buf B */ - /* Loopback mode */ - if ((iu_contty.mode[1] & 0xc0) == 0x80) { - iu_contty.txbuf = bval; - - /* This is also a Receive */ - if ((iu_contty.stat & STS_FFL) == 0) { - iu_contty.rxbuf[iu_contty.w_p] = bval; - iu_contty.w_p = (iu_contty.w_p + 1) % IU_BUF_SIZE; - if (iu_contty.w_p == iu_contty.r_p) { - iu_contty.stat |= STS_FFL; - } - } - - iu_contty.stat |= STS_RXR; - iu_state.istat |= ISTS_RBI; - if (iu_state.imr & IMR_RXRB) { - csr_data |= CSRUART; - } - } else { - iu_tx(PORT_B, bval); - } + iu_tx(PORT_B, bval); + sim_activate_abs(contty_xmt_unit, contty_ldsc[0].txdelta); break; case OPCR: iu_state.opcr = bval; @@ -718,51 +737,60 @@ void iu_write(uint32 pa, uint32 val, size_t size) } } -void iua_drq_handled() +t_stat iu_tx(uint8 portno, uint8 val) { - csr_data |= CSRDMA; -} - -void iub_drq_handled() -{ - csr_data |= CSRDMA; -} - -static SIM_INLINE void iu_tx(uint8 portno, uint8 val) -{ - IU_PORT *p; - UNIT *uptr; - TMLN *lp; - - if (portno == 0) { - p = &iu_console; - uptr = &tto_unit; - } else { - p = &iu_contty; - uptr = contty_xmt_unit; - } - - p->txbuf = val; + IU_PORT *p = (portno == PORT_A) ? &iu_console : &iu_contty; + UNIT *uptr = (portno == PORT_A) ? &tto_unit : contty_xmt_unit; + uint8 ists = (portno == PORT_A) ? ISTS_RAI : ISTS_RBI; + uint8 imr_mask = (portno == PORT_A) ? IMR_RXRA : IMR_RXRB; + int32 c; + t_stat status = SCPE_OK; if (p->conf & TX_EN) { - p->stat &= ~(STS_TXR|STS_TXE); - iu_state.istat &= ~(1 << (portno*4)); + if ((p->mode[1] & 0xc0) == 0x80) { /* Loopback mode */ + p->txbuf = val; - if (portno == PORT_A) { - /* Write the character to the SIMH console */ - sim_putchar(val); - } else { - lp = &contty_ldsc[0]; - tmxr_putc_ln(lp, val); + /* This is also a Receive */ + if ((p->stat & STS_FFL) == 0) { + p->rxbuf[p->w_p] = val; + p->w_p = (p->w_p + 1) % IU_BUF_SIZE; + if (p->w_p == p->r_p) { + p->stat |= STS_FFL; + } + } + + p->stat |= STS_RXR; + iu_state.istat |= ists; + if (iu_state.imr & imr_mask) { + csr_data |= CSRUART; + } + + return SCPE_OK; + } else { /* Direct mode */ + c = sim_tt_outcvt(val, TTUF_MODE_8B); + + if (c >= 0) { + p->txbuf = c; + p->stat &= ~(STS_TXR|STS_TXE); + iu_state.istat &= ~(1 << (portno*4)); + + if (portno == PORT_A) { + /* Write the character to the SIMH console */ + sim_debug(EXECUTE_MSG, &tto_dev, + "[iu_tx] CONSOLE transmit %02x (%c)\n", + (uint8) c, (char) c); + status = sim_putchar(c); + } else { + sim_debug(EXECUTE_MSG, &contty_dev, + "[iu_tx] CONTTY transmit %02x (%c)\n", + (uint8) c, (char) c); + status = tmxr_putc_ln(&contty_ldsc[0], c); + } + } } - - /* 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(uptr, uptr->wait); } + + return status; } static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd) @@ -783,6 +811,7 @@ static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd) p->stat &= ~STS_TXR; p->stat &= ~STS_TXE; p->drq = FALSE; + p->dma = FALSE; } else if (cmd & CMD_ETX) { p->conf |= TX_EN; /* TXE and TXR are always set by an ENABLE */ @@ -865,3 +894,52 @@ static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 cmd) break; } } + +/* + * Initiate DMA transfer or continue one already in progress. + */ +void iu_dma(uint8 channel, uint32 service_address) +{ + uint8 data; + uint32 addr; + t_stat status = SCPE_OK; + dma_channel *chan = &dma_state.channels[channel]; + UNIT *uptr = (channel == DMA_IUA_CHAN) ? &tto_unit : contty_xmt_unit; + IU_PORT *port = (channel == DMA_IUA_CHAN) ? &iu_console : &iu_contty; + + /* Immediate acknowledge of DMA */ + port->drq = FALSE; + + if (!port->dma) { + /* Set DMA transfer type */ + port->dma = 1u << ((dma_state.mode >> 2) & 0xf); + } + + if (port->dma == DMA_READ) { + addr = dma_address(channel, chan->ptr, TRUE); + chan->addr_c = chan->addr + chan->ptr + 1; + data = pread_b(addr); + status = iu_tx(channel - 2, data); + if (status == SCPE_OK) { + chan->ptr++; + chan->wcount_c--; + } else if (status == SCPE_LOST) { + chan->ptr = 0; + chan->wcount_c = -1; + } + + sim_activate_abs(uptr, uptr->wait); + + if (chan->wcount_c >= 0) { + /* Return early so we don't finish DMA */ + return; + } + } + + /* Done with DMA */ + port->dma = DMA_NONE; + + dma_state.mask |= (1 << channel); + dma_state.status |= (1 << channel); + csr_data |= CSRDMA; +} diff --git a/3B2/3b2_iu.h b/3B2/3b2_iu.h index 191897f0..187bbc15 100644 --- a/3B2/3b2_iu.h +++ b/3B2/3b2_iu.h @@ -140,6 +140,8 @@ extern DEVICE tto_dev; extern DEVICE contty_dev; extern DEVICE iu_timer_dev; +extern int32 tmxr_poll; + #define IUBASE 0x49000 #define IUSIZE 0x100 @@ -167,6 +169,11 @@ extern DEVICE iu_timer_dev; #define IU_DTRA 0x01 #define IU_DTRB 0x02 +#define DMA_NONE 0 +#define DMA_VERIFY 1 +#define DMA_WRITE 2 +#define DMA_READ 4 + /* Default baud rate generator (9600 baud) */ #define BRG_DEFAULT 11 @@ -181,7 +188,8 @@ typedef struct iu_port { 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 */ + uint8 dma; /* Currently active DMA mode */ + t_bool drq; /* DMA request enabled */ } IU_PORT; typedef struct iu_state { @@ -212,14 +220,15 @@ t_stat iu_svc_tto(UNIT *uptr); t_stat iu_svc_contty_rcv(UNIT *uptr); t_stat iu_svc_contty_xmt(UNIT *uptr); t_stat iu_svc_timer(UNIT *uptr); +t_stat iu_tx(uint8 portno, uint8 val); 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(); +void iu_dma(uint8 channel, uint32 service_address); -static SIM_INLINE void iu_tx(uint8 portno, uint8 val); static SIM_INLINE void iu_w_buf(uint8 portno, uint8 val); static SIM_INLINE void iu_w_cmd(uint8 portno, uint8 val); static SIM_INLINE void iu_update_rxi(uint8 c); diff --git a/3B2/3b2_mmu.c b/3B2/3b2_mmu.c index c15e02e5..3f330d97 100644 --- a/3B2/3b2_mmu.c +++ b/3B2/3b2_mmu.c @@ -695,8 +695,11 @@ t_stat mmu_decode_va(uint32 va, uint8 r_acc, t_bool fc, uint32 *pa) if (fc && mmu_check_perm(SD_ACC(sd0), r_acc) != SCPE_OK) { sim_debug(EXECUTE_MSG, &mmu_dev, "[%08x] CONTIGUOUS: NO ACCESS TO MEMORY AT %08x.\n" + "\t\tsd0=%08x sd0_addr=%08x\n" "\t\tcpu_cm=%d acc_req=%x sd_acc=%02x\n", - R[NUM_PC], va, CPU_CM, r_acc, SD_ACC(sd0)); + R[NUM_PC], va, + sd0, SD_ADDR(va), + CPU_CM, r_acc, SD_ACC(sd0)); MMU_FAULT(MMU_F_ACC); return SCPE_NXM; } diff --git a/3B2/3b2_sysdev.c b/3B2/3b2_sysdev.c index 0008d786..fa2f8c75 100644 --- a/3B2/3b2_sysdev.c +++ b/3B2/3b2_sysdev.c @@ -59,7 +59,7 @@ uint32 *NVRAM = NULL; extern DEVICE cpu_dev; -int32 tmxr_poll = 0; +int32 tmxr_poll = 16667; /* CSR */