This commit fixes several issues with code hygine and eliminates warnings in the Windows build.
622 lines
21 KiB
C
622 lines
21 KiB
C
/* 3b2_cpu.h: AT&T 3B2 Model 400 IO and CIO feature cards
|
|
|
|
Copyright (c) 2017, Seth J. Morabito
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use, copy,
|
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of the author shall
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization
|
|
from the author.
|
|
*/
|
|
|
|
#include "3b2_io.h"
|
|
|
|
CIO_STATE cio[CIO_SLOTS] = { 0 };
|
|
|
|
struct iolink iotable[] = {
|
|
{ MMUBASE, MMUBASE+MMUSIZE, &mmu_read, &mmu_write },
|
|
{ IFBASE, IFBASE+IFSIZE, &if_read, &if_write },
|
|
{ IDBASE, IDBASE+IDSIZE, &id_read, &id_write },
|
|
{ TIMERBASE, TIMERBASE+TIMERSIZE, &timer_read, &timer_write },
|
|
{ NVRAMBASE, NVRAMBASE+NVRAMSIZE, &nvram_read, &nvram_write },
|
|
{ CSRBASE, CSRBASE+CSRSIZE, &csr_read, &csr_write },
|
|
{ IUBASE, IUBASE+IUSIZE, &iu_read, &iu_write },
|
|
{ DMAIDBASE, DMAIDBASE+DMAIDSIZE, &dmac_read, &dmac_write },
|
|
{ DMAIUABASE, DMAIUABASE+DMAIUASIZE, &dmac_read, &dmac_write },
|
|
{ DMAIUBBASE, DMAIUBBASE+DMAIUBSIZE, &dmac_read, &dmac_write },
|
|
{ DMACBASE, DMACBASE+DMACSIZE, &dmac_read, &dmac_write },
|
|
{ DMAIFBASE, DMAIFBASE+DMAIFSIZE, &dmac_read, &dmac_write },
|
|
{ TODBASE, TODBASE+TODSIZE, &tod_read, &tod_write },
|
|
{ 0, 0, NULL, NULL}
|
|
};
|
|
|
|
void cio_clear(uint8 cid)
|
|
{
|
|
cio[cid].id = 0;
|
|
cio[cid].exp_handler = NULL;
|
|
cio[cid].full_handler = NULL;
|
|
cio[cid].sysgen = NULL;
|
|
cio[cid].rqp = 0;
|
|
cio[cid].cqp = 0;
|
|
cio[cid].rqs = 0;
|
|
cio[cid].cqs = 0;
|
|
cio[cid].ivec = 0;
|
|
cio[cid].no_rque = 0;
|
|
cio[cid].ipl = 0;
|
|
cio[cid].intr = FALSE;
|
|
cio[cid].sysgen_s = 0;
|
|
cio[cid].seqbit = 0;
|
|
cio[cid].op = 0;
|
|
}
|
|
|
|
void cio_sysgen(uint8 cid)
|
|
{
|
|
uint32 sysgen_p;
|
|
|
|
sysgen_p = pread_w(SYSGEN_PTR);
|
|
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[%08x] [SYSGEN] Starting sysgen for card %d. sysgen_p=%08x\n",
|
|
R[NUM_PC], cid, sysgen_p);
|
|
|
|
/* seqbit is always reset to 0 on completion */
|
|
cio[cid].seqbit = 0;
|
|
|
|
cio[cid].rqp = pread_w(sysgen_p);
|
|
cio[cid].cqp = pread_w(sysgen_p + 4);
|
|
cio[cid].rqs = pread_b(sysgen_p + 8);
|
|
cio[cid].cqs = pread_b(sysgen_p + 9);
|
|
cio[cid].ivec = pread_b(sysgen_p + 10);
|
|
cio[cid].no_rque = pread_b(sysgen_p + 11);
|
|
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[SYSGEN] sysgen rqp = %08x\n",
|
|
cio[cid].rqp);
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[SYSGEN] sysgen cqp = %08x\n",
|
|
cio[cid].cqp);
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[SYSGEN] sysgen rqs = %02x\n",
|
|
cio[cid].rqs);
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[SYSGEN] sysgen cqs = %02x\n",
|
|
cio[cid].cqs);
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[SYSGEN] sysgen ivec = %02x\n",
|
|
cio[cid].ivec);
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[SYSGEN] sysgen no_rque = %02x\n",
|
|
cio[cid].no_rque);
|
|
|
|
/* If the card has a custom sysgen handler, run it */
|
|
if (cio[cid].sysgen != NULL) {
|
|
cio[cid].sysgen(cid);
|
|
} else {
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[%08x] [cio_sysgen] Not running custom sysgen.\n",
|
|
R[NUM_PC]);
|
|
}
|
|
}
|
|
|
|
void cio_cexpress(uint8 cid, uint16 esize, cio_entry *cqe, uint8 *app_data)
|
|
{
|
|
int32 i;
|
|
uint32 cqp;
|
|
|
|
cqp = cio[cid].cqp;
|
|
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[%08x] [cio_cexpress] cqp = %08x seqbit = %d\n",
|
|
R[NUM_PC], cqp, cio[cid].seqbit);
|
|
|
|
cio[cid].seqbit ^= 1;
|
|
|
|
cqe->subdevice |= (cio[cid].seqbit << 6);
|
|
|
|
pwrite_h(cqp, cqe->byte_count);
|
|
pwrite_b(cqp + 2, cqe->subdevice);
|
|
pwrite_b(cqp + 3, cqe->opcode);
|
|
pwrite_w(cqp + 4, cqe->address);
|
|
|
|
/* Write application-specific data. */
|
|
for (i = 0; i < (esize - QESIZE); i++) {
|
|
pwrite_b(cqp + 8 + i, app_data[i]);
|
|
}
|
|
}
|
|
|
|
void cio_cqueue(uint8 cid, uint8 cmd_stat, uint16 esize,
|
|
cio_entry *cqe, uint8 *app_data)
|
|
{
|
|
int32 i;
|
|
uint32 cqp, top;
|
|
uint16 lp, ulp;
|
|
|
|
/* Apply the CMD/STAT bit */
|
|
cqe->subdevice |= (cmd_stat << 7);
|
|
|
|
/* Get the physical address of the completion queue
|
|
* in main memory */
|
|
cqp = cio[cid].cqp;
|
|
|
|
/* Get the physical address of the first entry in
|
|
* the completion queue */
|
|
top = cqp + esize + LUSIZE;
|
|
|
|
/* Get the load pointer. This is a 16-bit absolute offset
|
|
* from the top of the queue to the start of the entry. */
|
|
lp = pread_h(cqp + esize);
|
|
ulp = pread_h(cqp + esize + 2);
|
|
|
|
/* Load the entry at the supplied address */
|
|
pwrite_h(top + lp, cqe->byte_count);
|
|
pwrite_b(top + lp + 2, cqe->subdevice);
|
|
pwrite_b(top + lp + 3, cqe->opcode);
|
|
pwrite_w(top + lp + 4, cqe->address);
|
|
|
|
/* Write application-specific data. */
|
|
for (i = 0; i < (esize - QESIZE); i++) {
|
|
pwrite_b(top + lp + 8 + i, app_data[i]);
|
|
}
|
|
|
|
/* Increment the load pointer to the next queue location.
|
|
* If we go past the end of the queue, wrap around to the
|
|
* start of the queue */
|
|
if (cio[cid].cqs > 0) {
|
|
lp = (lp + esize) % (esize * cio[cid].cqs);
|
|
|
|
/* Store it back to the correct location */
|
|
pwrite_h(cqp + esize, lp);
|
|
} else {
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[%08x] [cio_cqueue] ERROR! Completion Queue Size is 0!",
|
|
R[NUM_PC]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Retrieve the Express Entry from the Request Queue
|
|
*/
|
|
void cio_rexpress(uint8 cid, uint16 esize, cio_entry *rqe, uint8 *app_data)
|
|
{
|
|
int32 i;
|
|
uint32 rqp;
|
|
|
|
rqp = cio[cid].rqp;
|
|
|
|
/* Unload the express entry from the request queue */
|
|
rqe->byte_count = pread_h(rqp);
|
|
rqe->subdevice = pread_b(rqp + 2);
|
|
rqe->opcode = pread_b(rqp + 3);
|
|
rqe->address = pread_w(rqp + 4);
|
|
|
|
for (i = 0; i < (esize - QESIZE); i++) {
|
|
app_data[i] = pread_b(rqp + 8 + i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Retrieve an entry from the Request Queue. This function
|
|
* returns the load pointer that points to the NEXT available slot.
|
|
* This may be used by callers to determine which queue(s) need to
|
|
* be serviced.
|
|
*
|
|
* Returns SCPE_OK on success, or SCPE_NXM if no entry was found.
|
|
*/
|
|
t_stat cio_rqueue(uint8 cid, uint8 qnum, uint16 esize,
|
|
cio_entry *rqe, uint8 *app_data)
|
|
{
|
|
int32 i;
|
|
uint32 rqp, top;
|
|
uint16 lp, ulp;
|
|
|
|
/* Get the physical address of the request queue in main memory */
|
|
rqp = cio[cid].rqp +
|
|
esize +
|
|
(qnum * (LUSIZE + (esize * cio[cid].rqs)));
|
|
|
|
lp = pread_h(rqp);
|
|
ulp = pread_h(rqp + 2);
|
|
|
|
if (lp == ulp) {
|
|
return SCPE_NXM;
|
|
}
|
|
|
|
top = rqp + LUSIZE;
|
|
|
|
/* Retrieve the entry at the supplied address */
|
|
rqe->byte_count = pread_h(top + ulp);
|
|
rqe->subdevice = pread_b(top + ulp + 2);
|
|
rqe->opcode = pread_b(top + ulp + 3);
|
|
rqe->address = pread_w(top + ulp + 4);
|
|
|
|
/* Read application-specific data. */
|
|
for (i = 0; i < (esize - QESIZE); i++) {
|
|
app_data[i] = pread_b(top + ulp + 8 + i);
|
|
}
|
|
|
|
/* Increment the unload pointer to the next queue location. If we
|
|
* go past the end of the queue, wrap around to the start of the
|
|
* queue */
|
|
if (cio[cid].rqs > 0) {
|
|
ulp = (ulp + esize) % (esize * cio[cid].rqs);
|
|
pwrite_h(rqp + 2, ulp);
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Return the Load Pointer for the given request queue
|
|
*/
|
|
uint16 cio_r_lp(uint8 cid, uint8 qnum, uint16 esize)
|
|
{
|
|
uint32 rqp;
|
|
|
|
rqp = cio[cid].rqp +
|
|
esize +
|
|
(qnum * (LUSIZE + (esize * cio[cid].rqs)));
|
|
|
|
return pread_h(rqp);
|
|
}
|
|
|
|
/*
|
|
* Return the Unload Pointer for the given request queue
|
|
*/
|
|
uint16 cio_r_ulp(uint8 cid, uint8 qnum, uint16 esize)
|
|
{
|
|
uint32 rqp;
|
|
|
|
rqp = cio[cid].rqp +
|
|
esize +
|
|
(qnum * (LUSIZE + (esize * cio[cid].rqs)));
|
|
|
|
return pread_h(rqp + 2);
|
|
}
|
|
|
|
uint16 cio_c_lp(uint8 cid, uint16 esize)
|
|
{
|
|
uint32 cqp;
|
|
cqp = cio[cid].cqp + esize;
|
|
return pread_h(cqp);
|
|
}
|
|
|
|
uint16 cio_c_ulp(uint8 cid, uint16 esize)
|
|
{
|
|
uint32 cqp;
|
|
cqp = cio[cid].cqp + esize;
|
|
return pread_h(cqp + 2);
|
|
}
|
|
|
|
/*
|
|
* Returns true if there is room in the completion queue
|
|
* for a new entry.
|
|
*/
|
|
t_bool cio_cqueue_avail(uint8 cid, uint16 esize)
|
|
{
|
|
uint32 lp, ulp;
|
|
|
|
lp = pread_h(cio[cid].cqp + esize);
|
|
ulp = pread_h(cio[cid].cqp + esize + 2);
|
|
|
|
return(((lp + esize) % (cio[cid].cqs * esize)) != ulp);
|
|
}
|
|
|
|
uint32 io_read(uint32 pa, size_t size)
|
|
{
|
|
struct iolink *p;
|
|
uint8 cid, reg, data;
|
|
|
|
/* Special devices */
|
|
if (pa == MEMSIZE_REG) {
|
|
|
|
/* The following values map to memory sizes:
|
|
0x00: 512KB ( 524,288 B)
|
|
0x01: 2MB (2,097,152 B)
|
|
0x02: 1MB (1,048,576 B)
|
|
0x03: 4MB (4,194,304 B)
|
|
*/
|
|
switch(MEM_SIZE) {
|
|
case 0x80000: /* 512KB */
|
|
return 0;
|
|
case 0x100000: /* 1MB */
|
|
return 2;
|
|
case 0x200000: /* 2MB */
|
|
return 1;
|
|
case 0x400000: /* 4MB */
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* CIO board area */
|
|
if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
|
|
cid = CID(pa);
|
|
reg = pa - CADDR(cid);
|
|
|
|
if (cio[cid].id == 0) {
|
|
/* Nothing lives here */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] No card at cid=%d reg=%d\n",
|
|
R[NUM_PC], cid, reg);
|
|
csr_data |= CSRTIMO;
|
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
|
return 0;
|
|
}
|
|
|
|
/* A normal SYSGEN sequence is: RESET -> INT0 -> INT1.
|
|
* However, there's a bug in the 3B2/400 DGMON test suite that
|
|
* runs on every startup. This diagnostic code performs a
|
|
* SYSGEN by calling RESET -> INT1 -> INT0. So, we must handle
|
|
* both orders. */
|
|
|
|
switch (reg) {
|
|
case IOF_ID:
|
|
case IOF_VEC:
|
|
switch(cio[cid].sysgen_s) {
|
|
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
|
case CIO_INT0: /* We've seen an INT0 but not an INT1. */
|
|
cio[cid].sysgen_s |= CIO_INT0;
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT0) ID\n",
|
|
R[NUM_PC], cid);
|
|
/* Return the correct byte of our board ID */
|
|
if (reg == IOF_ID) {
|
|
data = (cio[cid].id >> 8) & 0xff;
|
|
} else {
|
|
data = (cio[cid].id & 0xff);
|
|
}
|
|
break;
|
|
case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
|
cio[cid].sysgen_s |= CIO_INT0;
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT0) SYSGEN\n",
|
|
R[NUM_PC], cid);
|
|
cio_sysgen(cid);
|
|
data = cio[cid].ivec;
|
|
break;
|
|
case CIO_SYSGEN: /* We've already sysgen'ed */
|
|
cio[cid].sysgen_s |= CIO_INT0; /* This must come BEFORE the exp_handler */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT0) EXPRESS JOB\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].exp_handler(cid);
|
|
data = cio[cid].ivec;
|
|
break;
|
|
default:
|
|
/* This should never happen */
|
|
stop_reason = STOP_ERR;
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
|
R[NUM_PC], cid, cio[cid].sysgen_s);
|
|
data = 0;
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
case IOF_CTRL:
|
|
switch(cio[cid].sysgen_s) {
|
|
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
|
case CIO_INT1: /* We've seen an INT1 but not an INT0 */
|
|
/* There's nothing to do in this instance */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT1) IGNORED\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT1;
|
|
break;
|
|
case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT1) SYSGEN\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT1;
|
|
cio_sysgen(cid);
|
|
break;
|
|
case CIO_SYSGEN: /* We've already sysgen'ed */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT1) FULL\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT1; /* This must come BEFORE the full handler */
|
|
cio[cid].full_handler(cid);
|
|
break;
|
|
default:
|
|
/* This should never happen */
|
|
stop_reason = STOP_ERR;
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
|
R[NUM_PC], cid, cio[cid].sysgen_s);
|
|
break;
|
|
}
|
|
|
|
return 0; /* Data returned is arbitrary */
|
|
case IOF_STAT:
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] (%d RESET)\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s = 0;
|
|
return 0; /* Data returned is arbitrary */
|
|
default:
|
|
/* We should never reach here, but if we do, there's
|
|
* nothing listening. */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[READ] [%08x] No card at cid=%d reg=%d\n",
|
|
R[NUM_PC], cid, reg);
|
|
csr_data |= CSRTIMO;
|
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Memory-mapped IO devices */
|
|
for (p = &iotable[0]; p->low != 0; p++) {
|
|
if ((pa >= p->low) && (pa < p->high) && p->read) {
|
|
return p->read(pa, size);
|
|
}
|
|
}
|
|
|
|
/* Not found. */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[%08x] [io_read] ADDR=%08x: No device found.\n",
|
|
R[NUM_PC], pa);
|
|
csr_data |= CSRTIMO;
|
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
|
return 0;
|
|
}
|
|
|
|
void io_write(uint32 pa, uint32 val, size_t size)
|
|
{
|
|
struct iolink *p;
|
|
uint8 cid, reg;
|
|
|
|
/* Feature Card Area */
|
|
if (pa >= CIO_BOTTOM && pa < CIO_TOP) {
|
|
cid = CID(pa);
|
|
reg = pa - CADDR(cid);
|
|
|
|
if (cio[cid].id == 0) {
|
|
/* Nothing lives here */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] No card at cid=%d reg=%d\n",
|
|
R[NUM_PC], cid, reg);
|
|
csr_data |= CSRTIMO;
|
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
|
return;
|
|
}
|
|
|
|
/* A normal SYSGEN sequence is: RESET -> INT0 -> INT1.
|
|
* However, there's a bug in the 3B2/400 DGMON test suite that
|
|
* runs on every startup. This diagnostic code performs a
|
|
* SYSGEN by calling RESET -> INT1 -> INT0. So, we must handle
|
|
* both orders. */
|
|
|
|
switch (reg) {
|
|
case IOF_ID:
|
|
case IOF_VEC:
|
|
switch(cio[cid].sysgen_s) {
|
|
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
|
case CIO_INT0: /* We've seen an INT0 but not an INT1. */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT0) ID\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT0;
|
|
break;
|
|
case CIO_INT1: /* We've seen an INT1 but not an INT0. Time to sysgen */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT0) SYSGEN\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT0;
|
|
cio_sysgen(cid);
|
|
break;
|
|
case CIO_SYSGEN: /* We've already sysgen'ed */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT0) EXPRESS JOB\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT0;
|
|
cio[cid].exp_handler(cid);
|
|
break;
|
|
default:
|
|
/* This should never happen */
|
|
stop_reason = STOP_ERR;
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT0) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
|
R[NUM_PC], cid, cio[cid].sysgen_s);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
case IOF_CTRL:
|
|
switch(cio[cid].sysgen_s) {
|
|
case CIO_INT_NONE: /* We've never seen an INT0 or INT1 */
|
|
case CIO_INT1: /* We've seen an INT1 but not an INT0 */
|
|
/* There's nothing to do in this instance */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT1) IGNORED\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT1;
|
|
break;
|
|
case CIO_INT0: /* We've seen an INT0 but not an INT1. Time to sysgen */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT1) SYSGEN\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT1;
|
|
cio_sysgen(cid);
|
|
break;
|
|
case CIO_SYSGEN: /* We've already sysgen'ed */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT1) FULL\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s |= CIO_INT1;
|
|
cio[cid].full_handler(cid);
|
|
break;
|
|
default:
|
|
/* This should never happen */
|
|
stop_reason = STOP_ERR;
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d INT1) ERROR IN STATE MACHINE sysgen_s=%02x\n",
|
|
R[NUM_PC], cid, cio[cid].sysgen_s);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
case IOF_STAT:
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] (%d RESET)\n",
|
|
R[NUM_PC], cid);
|
|
cio[cid].sysgen_s = 0;
|
|
return;
|
|
default:
|
|
/* We should never reach here, but if we do, there's
|
|
* nothing listening. */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[WRITE] [%08x] No card at cid=%d reg=%d\n",
|
|
R[NUM_PC], cid, reg);
|
|
csr_data |= CSRTIMO;
|
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Memory-mapped IO devices */
|
|
for (p = &iotable[0]; p->low != 0; p++) {
|
|
if ((pa >= p->low) && (pa < p->high) && p->write) {
|
|
p->write(pa, val, size);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Not found. */
|
|
sim_debug(IO_DBG, &cpu_dev,
|
|
"[%08x] [io_write] ADDR=%08x: No device found.\n",
|
|
R[NUM_PC], pa);
|
|
csr_data |= CSRTIMO;
|
|
cpu_abort(NORMAL_EXCEPTION, EXTERNAL_MEMORY_FAULT);
|
|
}
|
|
|
|
|
|
/* For debugging only */
|
|
void dump_entry(uint32 dbits, DEVICE *dev, CONST char *type,
|
|
uint16 esize, cio_entry *entry, uint8 *app_data)
|
|
{
|
|
sim_debug(dbits, dev,
|
|
"*** %s ENTRY: byte_count=%04x, subdevice=%02x, opcode=%d, address=%08x\n",
|
|
type, entry->byte_count, entry->subdevice,
|
|
entry->opcode, entry->address);
|
|
}
|