mirror of
https://github.com/open-simh/simh.git
synced 2026-05-02 06:25:43 +00:00
3b2: Update README.md and correct line endings
This change updates the 3B2 README.md file, and fixes all line endings on 3B2 source files.
This commit is contained in:
962
3B2/3b2_dmac.c
962
3B2/3b2_dmac.c
@@ -1,481 +1,481 @@
|
||||
/* 3b2_dmac.c: AM9517 DMA Controller
|
||||
|
||||
Copyright (c) 2021-2022, 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_dmac.h"
|
||||
|
||||
#if defined(REV2)
|
||||
#include "3b2_id.h"
|
||||
#endif
|
||||
|
||||
#include "3b2_cpu.h"
|
||||
#include "3b2_if.h"
|
||||
#include "3b2_iu.h"
|
||||
#include "3b2_mem.h"
|
||||
#include "3b2_stddev.h"
|
||||
#include "3b2_csr.h"
|
||||
|
||||
DMA_STATE dma_state;
|
||||
|
||||
UNIT dmac_unit[] = {
|
||||
{ UDATA (NULL, 0, 0), 0, 0 },
|
||||
{ UDATA (NULL, 0, 0), 0, 1 },
|
||||
{ UDATA (NULL, 0, 0), 0, 2 },
|
||||
{ UDATA (NULL, 0, 0), 0, 3 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
REG dmac_reg[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
DEVICE dmac_dev = {
|
||||
"DMAC", dmac_unit, dmac_reg, NULL,
|
||||
1, 16, 8, 4, 16, 32,
|
||||
NULL, NULL, &dmac_reset,
|
||||
NULL, NULL, NULL, NULL,
|
||||
DEV_DEBUG, 0, sys_deb_tab
|
||||
};
|
||||
|
||||
dmac_dma_handler device_dma_handlers[] = {
|
||||
#if defined(REV2)
|
||||
{DMA_ID_CHAN, IDBASE+ID_DATA_REG, &id_drq, dmac_generic_dma, id_after_dma},
|
||||
#endif
|
||||
{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_console, NULL},
|
||||
{DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_contty.drq, iu_dma_contty, NULL},
|
||||
{0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
uint32 dma_address(uint8 channel, uint32 offset) {
|
||||
uint32 addr, page;
|
||||
if (DMA_DECR(channel)) {
|
||||
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) - offset);
|
||||
} else {
|
||||
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset);
|
||||
}
|
||||
#if defined (REV3)
|
||||
page = (uint32)dma_state.channels[channel].page;
|
||||
#else
|
||||
/* In Rev 2, the top bit of the page address is a R/W bit, so
|
||||
we mask it here */
|
||||
page = (uint32)dma_state.channels[channel].page & 0x7f;
|
||||
#endif
|
||||
addr |= page << 16;
|
||||
return addr;
|
||||
}
|
||||
|
||||
t_stat dmac_reset(DEVICE *dptr)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(&dma_state, 0, sizeof(dma_state));
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
dma_state.channels[i].page = 0;
|
||||
dma_state.channels[i].addr = 0;
|
||||
dma_state.channels[i].mode = 0;
|
||||
dma_state.channels[i].wcount = 0;
|
||||
dma_state.channels[i].addr_c = 0;
|
||||
dma_state.channels[i].wcount_c = -1;
|
||||
dma_state.channels[i].ptr = 0;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
uint32 dmac_read(uint32 pa, size_t size)
|
||||
{
|
||||
uint8 reg, base, data;
|
||||
|
||||
base = (uint8) (pa >> 12);
|
||||
reg = pa & 0xff;
|
||||
|
||||
switch (base) {
|
||||
case DMA_C:
|
||||
switch (reg) {
|
||||
case 0: /* channel 0 current address reg */
|
||||
data = ((dma_state.channels[0].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 0 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 1: /* channel 0 current address reg */
|
||||
data = ((dma_state.channels[0].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 0 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 2: /* channel 1 current address reg */
|
||||
data = ((dma_state.channels[1].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 1 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 3: /* channel 1 current address reg */
|
||||
data = ((dma_state.channels[1].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 1 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 4: /* channel 2 current address reg */
|
||||
data = ((dma_state.channels[2].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 2 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 5: /* channel 2 current address reg */
|
||||
data = ((dma_state.channels[2].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 2 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 6: /* channel 3 current address reg */
|
||||
data = ((dma_state.channels[3].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 3 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 7: /* channel 3 current address reg */
|
||||
data = ((dma_state.channels[3].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 3 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 8:
|
||||
data = dma_state.status;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading DMAC Status %08x\n",
|
||||
data);
|
||||
dma_state.status = 0;
|
||||
break;
|
||||
default:
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"DMAC READ %lu B @ %08x\n",
|
||||
size, pa);
|
||||
data = 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
default:
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"[BASE: %08x] DMAC READ %lu B @ %08x\n",
|
||||
base, size, pa);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program the DMAC
|
||||
*/
|
||||
void dmac_program(uint8 reg, uint8 val)
|
||||
{
|
||||
uint8 channel_id, i, chan_num;
|
||||
dma_channel *channel;
|
||||
|
||||
#if defined(REV3)
|
||||
/* TODO: More general DMA interrupt clearing */
|
||||
CPU_CLR_INT(INT_UART_DMA);
|
||||
CLR_CSR(CSRDMA);
|
||||
#endif
|
||||
|
||||
if (reg < 8) {
|
||||
switch (reg) {
|
||||
case 0:
|
||||
case 1:
|
||||
chan_num = 0;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
chan_num = 1;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
chan_num = 2;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
chan_num = 3;
|
||||
break;
|
||||
default:
|
||||
chan_num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
channel = &dma_state.channels[chan_num];
|
||||
|
||||
switch (reg & 1) {
|
||||
case 0: /* Address */
|
||||
channel->addr &= ~(0xff << dma_state.bff * 8);
|
||||
channel->addr |= (val & 0xff) << (dma_state.bff * 8);
|
||||
channel->addr_c = channel->addr;
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Set address channel %d byte %d = %08x\n",
|
||||
chan_num, dma_state.bff, channel->addr);
|
||||
break;
|
||||
case 1: /* Word Count */
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Toggle the byte flip-flop */
|
||||
dma_state.bff ^= 1;
|
||||
|
||||
/* Handled. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* If it hasn't been handled, it must be one of the following
|
||||
registers. */
|
||||
|
||||
switch (reg) {
|
||||
case 8: /* Command */
|
||||
dma_state.command = val;
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Command: val=%02x\n",
|
||||
val);
|
||||
break;
|
||||
case 9: /* Request */
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Request set: val=%02x\n",
|
||||
val);
|
||||
dma_state.request = val;
|
||||
break;
|
||||
case 10: /* Write Single Mask Register Bit */
|
||||
channel_id = val & 3;
|
||||
|
||||
/* "Clear or Set" is bit 2 */
|
||||
if ((val >> 2) & 1) {
|
||||
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,
|
||||
"Write Single Mask Register Bit. channel=%d set/clear=%02x\n",
|
||||
channel_id, (val >> 2) & 1);
|
||||
break;
|
||||
case 11: /* Mode */
|
||||
channel_id = val & 3;
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Mode Set. channel=%d val=%02x\n",
|
||||
channel_id, val);
|
||||
dma_state.channels[channel_id].mode = val;
|
||||
break;
|
||||
case 12: /* Clear Byte Pointer Flip/Flop */
|
||||
dma_state.bff = 0;
|
||||
break;
|
||||
case 13: /* Master Clear */
|
||||
dma_state.bff = 0;
|
||||
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].addr_c = 0;
|
||||
dma_state.channels[i].wcount_c = -1;
|
||||
dma_state.channels[i].ptr = 0;
|
||||
}
|
||||
break;
|
||||
case 15: /* Write All Mask Register Bits */
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Write DMAC mask (all bits). Val=%02x\n",
|
||||
val);
|
||||
dma_state.mask = val & 0xf;
|
||||
break;
|
||||
case 16: /* Clear DMAC Interrupt */
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Clear DMA Interrupt in DMAC. val=%02x\n",
|
||||
val);
|
||||
break;
|
||||
default:
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Unhandled DMAC write. reg=%x val=%02x\n",
|
||||
reg, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dmac_page_update(uint8 base, uint8 reg, uint8 val)
|
||||
{
|
||||
uint8 shift = 0;
|
||||
|
||||
/* Sanity check */
|
||||
if (reg > 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(REV2)
|
||||
/* In Rev2 systems, the actual register is a 32-bit,
|
||||
byte-addressed register, so that address 4x000 is the highest
|
||||
byte, 4x003 is the lowest byte. */
|
||||
shift = -(reg - 3) * 8;
|
||||
#endif
|
||||
|
||||
switch (base) {
|
||||
#if defined (REV2)
|
||||
case DMA_ID:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 0 = %x\n", val);
|
||||
dma_state.channels[DMA_ID_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_ID_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
#endif
|
||||
case DMA_IF:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 1 = %x\n", val);
|
||||
dma_state.channels[DMA_IF_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_IF_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
case DMA_IUA:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 2 = %x\n", val);
|
||||
dma_state.channels[DMA_IUA_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_IUA_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
case DMA_IUB:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 3 = %x\n", val);
|
||||
dma_state.channels[DMA_IUB_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_IUB_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dmac_write(uint32 pa, uint32 val, size_t size)
|
||||
{
|
||||
uint8 reg, base;
|
||||
|
||||
base = (uint8) (pa >> 12);
|
||||
reg = pa & 0xff;
|
||||
|
||||
switch (base) {
|
||||
case DMA_C:
|
||||
dmac_program(reg, (uint8) val);
|
||||
break;
|
||||
#if defined (REV2)
|
||||
case DMA_ID:
|
||||
#endif
|
||||
case DMA_IUA:
|
||||
case DMA_IUB:
|
||||
case DMA_IF:
|
||||
dmac_page_update(base, reg, (uint8) val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dmac_generic_dma(uint8 channel, uint32 service_address)
|
||||
{
|
||||
uint8 data;
|
||||
int32 i;
|
||||
uint32 addr;
|
||||
dma_channel *chan = &dma_state.channels[channel];
|
||||
|
||||
i = chan->wcount_c;
|
||||
|
||||
/* TODO: This assumes every transfer is a block mode, which is not
|
||||
guaranteed to be valid, but is likely safe? */
|
||||
|
||||
switch (DMA_XFER(channel)) {
|
||||
case DMA_XFER_VERIFY:
|
||||
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||
"[dmac_generic_dma channel=%d] unhandled VERIFY request.\n",
|
||||
channel);
|
||||
break;
|
||||
case DMA_XFER_WRITE:
|
||||
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||
"[dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n",
|
||||
channel,
|
||||
chan->wcount + 1,
|
||||
dma_address(channel, 0),
|
||||
service_address,
|
||||
dma_state.channels[channel].page,
|
||||
dma_state.channels[channel].addr);
|
||||
for (; i >= 0; i--) {
|
||||
chan->wcount_c--;
|
||||
addr = dma_address(channel, chan->ptr++);
|
||||
chan->addr_c = addr;
|
||||
data = pread_b(service_address, BUS_PER);
|
||||
write_b(addr, data, BUS_PER);
|
||||
}
|
||||
break;
|
||||
case DMA_XFER_READ:
|
||||
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||
"[dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n",
|
||||
channel,
|
||||
chan->wcount + 1,
|
||||
dma_address(channel, 0),
|
||||
service_address);
|
||||
for (; i >= 0; i--) {
|
||||
chan->wcount_c = i;
|
||||
addr = dma_address(channel, chan->ptr++);
|
||||
chan->addr_c = addr;
|
||||
data = pread_b(addr, BUS_PER);
|
||||
write_b(service_address, data, BUS_PER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* End of Process must set the channel's mask bit */
|
||||
dma_state.mask |= (1 << channel);
|
||||
dma_state.status |= (1 << channel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Service pending DRQs
|
||||
*/
|
||||
void dmac_service_drqs()
|
||||
{
|
||||
volatile dmac_dma_handler *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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 3b2_dmac.c: AM9517 DMA Controller
|
||||
|
||||
Copyright (c) 2021-2022, 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_dmac.h"
|
||||
|
||||
#if defined(REV2)
|
||||
#include "3b2_id.h"
|
||||
#endif
|
||||
|
||||
#include "3b2_cpu.h"
|
||||
#include "3b2_if.h"
|
||||
#include "3b2_iu.h"
|
||||
#include "3b2_mem.h"
|
||||
#include "3b2_stddev.h"
|
||||
#include "3b2_csr.h"
|
||||
|
||||
DMA_STATE dma_state;
|
||||
|
||||
UNIT dmac_unit[] = {
|
||||
{ UDATA (NULL, 0, 0), 0, 0 },
|
||||
{ UDATA (NULL, 0, 0), 0, 1 },
|
||||
{ UDATA (NULL, 0, 0), 0, 2 },
|
||||
{ UDATA (NULL, 0, 0), 0, 3 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
REG dmac_reg[] = {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
DEVICE dmac_dev = {
|
||||
"DMAC", dmac_unit, dmac_reg, NULL,
|
||||
1, 16, 8, 4, 16, 32,
|
||||
NULL, NULL, &dmac_reset,
|
||||
NULL, NULL, NULL, NULL,
|
||||
DEV_DEBUG, 0, sys_deb_tab
|
||||
};
|
||||
|
||||
dmac_dma_handler device_dma_handlers[] = {
|
||||
#if defined(REV2)
|
||||
{DMA_ID_CHAN, IDBASE+ID_DATA_REG, &id_drq, dmac_generic_dma, id_after_dma},
|
||||
#endif
|
||||
{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_console, NULL},
|
||||
{DMA_IUB_CHAN, IUBASE+IUB_DATA_REG, &iu_contty.drq, iu_dma_contty, NULL},
|
||||
{0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
uint32 dma_address(uint8 channel, uint32 offset) {
|
||||
uint32 addr, page;
|
||||
if (DMA_DECR(channel)) {
|
||||
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) - offset);
|
||||
} else {
|
||||
addr = (PHYS_MEM_BASE + (uint32)(dma_state.channels[channel].addr) + offset);
|
||||
}
|
||||
#if defined (REV3)
|
||||
page = (uint32)dma_state.channels[channel].page;
|
||||
#else
|
||||
/* In Rev 2, the top bit of the page address is a R/W bit, so
|
||||
we mask it here */
|
||||
page = (uint32)dma_state.channels[channel].page & 0x7f;
|
||||
#endif
|
||||
addr |= page << 16;
|
||||
return addr;
|
||||
}
|
||||
|
||||
t_stat dmac_reset(DEVICE *dptr)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(&dma_state, 0, sizeof(dma_state));
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
dma_state.channels[i].page = 0;
|
||||
dma_state.channels[i].addr = 0;
|
||||
dma_state.channels[i].mode = 0;
|
||||
dma_state.channels[i].wcount = 0;
|
||||
dma_state.channels[i].addr_c = 0;
|
||||
dma_state.channels[i].wcount_c = -1;
|
||||
dma_state.channels[i].ptr = 0;
|
||||
}
|
||||
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
uint32 dmac_read(uint32 pa, size_t size)
|
||||
{
|
||||
uint8 reg, base, data;
|
||||
|
||||
base = (uint8) (pa >> 12);
|
||||
reg = pa & 0xff;
|
||||
|
||||
switch (base) {
|
||||
case DMA_C:
|
||||
switch (reg) {
|
||||
case 0: /* channel 0 current address reg */
|
||||
data = ((dma_state.channels[0].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 0 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 1: /* channel 0 current address reg */
|
||||
data = ((dma_state.channels[0].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 0 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 2: /* channel 1 current address reg */
|
||||
data = ((dma_state.channels[1].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 1 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 3: /* channel 1 current address reg */
|
||||
data = ((dma_state.channels[1].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 1 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 4: /* channel 2 current address reg */
|
||||
data = ((dma_state.channels[2].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 2 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 5: /* channel 2 current address reg */
|
||||
data = ((dma_state.channels[2].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 2 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 6: /* channel 3 current address reg */
|
||||
data = ((dma_state.channels[3].addr_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 3 Addr Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 7: /* channel 3 current address reg */
|
||||
data = ((dma_state.channels[3].wcount_c) >> (dma_state.bff * 8)) & 0xff;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading Channel 3 Addr Count Reg: %08x\n",
|
||||
data);
|
||||
dma_state.bff ^= 1;
|
||||
break;
|
||||
case 8:
|
||||
data = dma_state.status;
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"Reading DMAC Status %08x\n",
|
||||
data);
|
||||
dma_state.status = 0;
|
||||
break;
|
||||
default:
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"DMAC READ %lu B @ %08x\n",
|
||||
size, pa);
|
||||
data = 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
default:
|
||||
sim_debug(READ_MSG, &dmac_dev,
|
||||
"[BASE: %08x] DMAC READ %lu B @ %08x\n",
|
||||
base, size, pa);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program the DMAC
|
||||
*/
|
||||
void dmac_program(uint8 reg, uint8 val)
|
||||
{
|
||||
uint8 channel_id, i, chan_num;
|
||||
dma_channel *channel;
|
||||
|
||||
#if defined(REV3)
|
||||
/* TODO: More general DMA interrupt clearing */
|
||||
CPU_CLR_INT(INT_UART_DMA);
|
||||
CLR_CSR(CSRDMA);
|
||||
#endif
|
||||
|
||||
if (reg < 8) {
|
||||
switch (reg) {
|
||||
case 0:
|
||||
case 1:
|
||||
chan_num = 0;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
chan_num = 1;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
chan_num = 2;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
chan_num = 3;
|
||||
break;
|
||||
default:
|
||||
chan_num = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
channel = &dma_state.channels[chan_num];
|
||||
|
||||
switch (reg & 1) {
|
||||
case 0: /* Address */
|
||||
channel->addr &= ~(0xff << dma_state.bff * 8);
|
||||
channel->addr |= (val & 0xff) << (dma_state.bff * 8);
|
||||
channel->addr_c = channel->addr;
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Set address channel %d byte %d = %08x\n",
|
||||
chan_num, dma_state.bff, channel->addr);
|
||||
break;
|
||||
case 1: /* Word Count */
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Toggle the byte flip-flop */
|
||||
dma_state.bff ^= 1;
|
||||
|
||||
/* Handled. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* If it hasn't been handled, it must be one of the following
|
||||
registers. */
|
||||
|
||||
switch (reg) {
|
||||
case 8: /* Command */
|
||||
dma_state.command = val;
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Command: val=%02x\n",
|
||||
val);
|
||||
break;
|
||||
case 9: /* Request */
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Request set: val=%02x\n",
|
||||
val);
|
||||
dma_state.request = val;
|
||||
break;
|
||||
case 10: /* Write Single Mask Register Bit */
|
||||
channel_id = val & 3;
|
||||
|
||||
/* "Clear or Set" is bit 2 */
|
||||
if ((val >> 2) & 1) {
|
||||
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,
|
||||
"Write Single Mask Register Bit. channel=%d set/clear=%02x\n",
|
||||
channel_id, (val >> 2) & 1);
|
||||
break;
|
||||
case 11: /* Mode */
|
||||
channel_id = val & 3;
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Mode Set. channel=%d val=%02x\n",
|
||||
channel_id, val);
|
||||
dma_state.channels[channel_id].mode = val;
|
||||
break;
|
||||
case 12: /* Clear Byte Pointer Flip/Flop */
|
||||
dma_state.bff = 0;
|
||||
break;
|
||||
case 13: /* Master Clear */
|
||||
dma_state.bff = 0;
|
||||
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].addr_c = 0;
|
||||
dma_state.channels[i].wcount_c = -1;
|
||||
dma_state.channels[i].ptr = 0;
|
||||
}
|
||||
break;
|
||||
case 15: /* Write All Mask Register Bits */
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Write DMAC mask (all bits). Val=%02x\n",
|
||||
val);
|
||||
dma_state.mask = val & 0xf;
|
||||
break;
|
||||
case 16: /* Clear DMAC Interrupt */
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Clear DMA Interrupt in DMAC. val=%02x\n",
|
||||
val);
|
||||
break;
|
||||
default:
|
||||
sim_debug(WRITE_MSG, &dmac_dev,
|
||||
"Unhandled DMAC write. reg=%x val=%02x\n",
|
||||
reg, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dmac_page_update(uint8 base, uint8 reg, uint8 val)
|
||||
{
|
||||
uint8 shift = 0;
|
||||
|
||||
/* Sanity check */
|
||||
if (reg > 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(REV2)
|
||||
/* In Rev2 systems, the actual register is a 32-bit,
|
||||
byte-addressed register, so that address 4x000 is the highest
|
||||
byte, 4x003 is the lowest byte. */
|
||||
shift = -(reg - 3) * 8;
|
||||
#endif
|
||||
|
||||
switch (base) {
|
||||
#if defined (REV2)
|
||||
case DMA_ID:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 0 = %x\n", val);
|
||||
dma_state.channels[DMA_ID_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_ID_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
#endif
|
||||
case DMA_IF:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 1 = %x\n", val);
|
||||
dma_state.channels[DMA_IF_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_IF_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
case DMA_IUA:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 2 = %x\n", val);
|
||||
dma_state.channels[DMA_IUA_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_IUA_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
case DMA_IUB:
|
||||
sim_debug(WRITE_MSG, &dmac_dev, "Set page channel 3 = %x\n", val);
|
||||
dma_state.channels[DMA_IUB_CHAN].page &= ~(0xff << shift);
|
||||
dma_state.channels[DMA_IUB_CHAN].page |= ((uint16)val << shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dmac_write(uint32 pa, uint32 val, size_t size)
|
||||
{
|
||||
uint8 reg, base;
|
||||
|
||||
base = (uint8) (pa >> 12);
|
||||
reg = pa & 0xff;
|
||||
|
||||
switch (base) {
|
||||
case DMA_C:
|
||||
dmac_program(reg, (uint8) val);
|
||||
break;
|
||||
#if defined (REV2)
|
||||
case DMA_ID:
|
||||
#endif
|
||||
case DMA_IUA:
|
||||
case DMA_IUB:
|
||||
case DMA_IF:
|
||||
dmac_page_update(base, reg, (uint8) val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dmac_generic_dma(uint8 channel, uint32 service_address)
|
||||
{
|
||||
uint8 data;
|
||||
int32 i;
|
||||
uint32 addr;
|
||||
dma_channel *chan = &dma_state.channels[channel];
|
||||
|
||||
i = chan->wcount_c;
|
||||
|
||||
/* TODO: This assumes every transfer is a block mode, which is not
|
||||
guaranteed to be valid, but is likely safe? */
|
||||
|
||||
switch (DMA_XFER(channel)) {
|
||||
case DMA_XFER_VERIFY:
|
||||
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||
"[dmac_generic_dma channel=%d] unhandled VERIFY request.\n",
|
||||
channel);
|
||||
break;
|
||||
case DMA_XFER_WRITE:
|
||||
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||
"[dmac_generic_dma channel=%d] write: %d bytes to %08x from %08x (page=%04x addr=%08x)\n",
|
||||
channel,
|
||||
chan->wcount + 1,
|
||||
dma_address(channel, 0),
|
||||
service_address,
|
||||
dma_state.channels[channel].page,
|
||||
dma_state.channels[channel].addr);
|
||||
for (; i >= 0; i--) {
|
||||
chan->wcount_c--;
|
||||
addr = dma_address(channel, chan->ptr++);
|
||||
chan->addr_c = addr;
|
||||
data = pread_b(service_address, BUS_PER);
|
||||
write_b(addr, data, BUS_PER);
|
||||
}
|
||||
break;
|
||||
case DMA_XFER_READ:
|
||||
sim_debug(EXECUTE_MSG, &dmac_dev,
|
||||
"[dmac_generic_dma channel=%d] read: %d bytes from %08x to %08x\n",
|
||||
channel,
|
||||
chan->wcount + 1,
|
||||
dma_address(channel, 0),
|
||||
service_address);
|
||||
for (; i >= 0; i--) {
|
||||
chan->wcount_c = i;
|
||||
addr = dma_address(channel, chan->ptr++);
|
||||
chan->addr_c = addr;
|
||||
data = pread_b(addr, BUS_PER);
|
||||
write_b(service_address, data, BUS_PER);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* End of Process must set the channel's mask bit */
|
||||
dma_state.mask |= (1 << channel);
|
||||
dma_state.status |= (1 << channel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Service pending DRQs
|
||||
*/
|
||||
void dmac_service_drqs()
|
||||
{
|
||||
volatile dmac_dma_handler *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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user