3b2: Refactor DUART and DMA
This change is a major refactor of how DMA and the DUART interact. DMA implementation can now be overridden by individual devices that require DMA. Disk and Floppy both continue to use a generic DMA implementation, but the DUART code replaces the generic DMA with its own implementation that correctly rate-limits TX. Among other things, this allows the simulator to work correctly with real serial terminals. This functionality has been tested on an AT&T 5620 "Blit" terminal, which can run the 'layers' windowing software from the simulator.
This commit is contained in:
parent
da31dfa7a9
commit
177be95e5d
12 changed files with 266 additions and 171 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
262
3B2/3b2_iu.c
262
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;
|
||||
}
|
||||
|
|
13
3B2/3b2_iu.h
13
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ uint32 *NVRAM = NULL;
|
|||
|
||||
extern DEVICE cpu_dev;
|
||||
|
||||
int32 tmxr_poll = 0;
|
||||
int32 tmxr_poll = 16667;
|
||||
|
||||
/* CSR */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue