mirror of
https://github.com/simh/simh.git
synced 2026-01-15 08:02:40 +00:00
3b2: Refactor IU into 5 devices
This commit is contained in:
parent
f6e63892b4
commit
b23ebbe312
@ -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;
|
||||
|
||||
509
3B2/3b2_iu.c
509
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
|
||||
|
||||
66
3B2/3b2_iu.h
66
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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user