initial g2 dma support

This commit is contained in:
Anthony Pesch 2016-10-01 22:44:43 -07:00
parent fdc4f3ae45
commit 1a761877c4
3 changed files with 204 additions and 211 deletions

View File

@ -6,7 +6,157 @@
struct reg_cb holly_cb[NUM_HOLLY_REGS];
static void holly_update_sh4_interrupts(struct holly *hl) {
static void holly_ch2_dma(struct holly *hl) {
// FIXME what are SB_LMMODE0 / SB_LMMODE1
struct sh4_dtr dtr = {0};
dtr.channel = 2;
dtr.rw = false;
dtr.addr = *hl->SB_C2DSTAT;
sh4_ddt(hl->sh4, &dtr);
*hl->SB_C2DLEN = 0;
*hl->SB_C2DST = 0;
holly_raise_interrupt(hl, HOLLY_INTC_DTDE2INT);
}
static void holly_gdrom_dma(struct holly *hl) {
if (!*hl->SB_GDEN) {
return;
}
struct gdrom *gd = hl->gdrom;
struct sh4 *sh4 = hl->sh4;
CHECK_EQ(*hl->SB_GDDIR, 1); // gdrom -> sh4
int transfer_size = *hl->SB_GDLEN;
int remaining = transfer_size;
uint32_t addr = *hl->SB_GDSTAR;
gdrom_dma_begin(gd);
while (remaining) {
// read a single sector at a time from the gdrom
uint8_t sector_data[SECTOR_SIZE];
int n = gdrom_dma_read(gd, sector_data, sizeof(sector_data));
struct sh4_dtr dtr = {0};
dtr.channel = 0;
dtr.rw = true;
dtr.data = sector_data;
dtr.addr = addr;
dtr.size = n;
sh4_ddt(sh4, &dtr);
remaining -= n;
addr += n;
}
gdrom_dma_end(gd);
*hl->SB_GDSTARD = addr;
*hl->SB_GDLEND = transfer_size;
*hl->SB_GDST = 0;
holly_raise_interrupt(hl, HOLLY_INTC_G1DEINT);
}
static void holly_g2_dma(struct holly *hl, int channel) {
// clang-format off
struct g2_channel_desc {
int STAG, STAR, LEN, DIR, TSEL, EN, ST, SUSP;
holly_interrupt_t INTR;
};
static struct g2_channel_desc channel_descs[] = {
{SB_ADSTAG, SB_ADSTAR, SB_ADLEN, SB_ADDIR, SB_ADTSEL, SB_ADEN, SB_ADST, SB_ADSUSP, HOLLY_INTC_G2DEAINT},
{SB_E1STAG, SB_E1STAR, SB_E1LEN, SB_E1DIR, SB_E1TSEL, SB_E1EN, SB_E1ST, SB_E1SUSP, HOLLY_INTC_G2DE1INT},
{SB_E2STAG, SB_E2STAR, SB_E2LEN, SB_E2DIR, SB_E2TSEL, SB_E2EN, SB_E2ST, SB_E2SUSP, HOLLY_INTC_G2DE2INT},
{SB_DDSTAG, SB_DDSTAR, SB_DDLEN, SB_DDDIR, SB_DDTSEL, SB_DDEN, SB_DDST, SB_DDSUSP, HOLLY_INTC_G2DEDINT},
};
// clang-format on
struct g2_channel_desc *desc = &channel_descs[channel];
uint32_t *STAG = &hl->reg[desc->STAG];
uint32_t *STAR = &hl->reg[desc->STAR];
uint32_t *LEN = &hl->reg[desc->LEN];
uint32_t *DIR = &hl->reg[desc->DIR];
uint32_t *TSEL = &hl->reg[desc->TSEL];
uint32_t *EN = &hl->reg[desc->EN];
uint32_t *ST = &hl->reg[desc->ST];
uint32_t *SUSP = &hl->reg[desc->SUSP];
holly_interrupt_t INTR = desc->INTR;
if (!*EN) {
return;
}
struct address_space *space = hl->sh4->memory_if->space;
int transfer_size = *LEN & 0x7fffffff;
int enable = *LEN >> 31;
int remaining = transfer_size;
uint32_t src = *STAR;
uint32_t dst = *STAG;
// only sh4 -> g2 supported for now
CHECK_EQ(*DIR, 0);
while (remaining) {
as_write32(space, dst, as_read32(space, src));
remaining -= 4;
src += 4;
dst += 4;
}
*STAR = src;
*STAG = dst;
*LEN = 0;
*EN = enable ? 1 : *EN;
*ST = 0;
holly_raise_interrupt(hl, INTR);
}
static void holly_maple_dma(struct holly *hl) {
if (!*hl->SB_MDEN) {
return;
}
struct maple *mp = hl->maple;
struct address_space *space = hl->sh4->memory_if->space;
uint32_t addr = *hl->SB_MDSTAR;
union maple_transfer desc;
struct maple_frame frame, res;
do {
desc.full = as_read64(space, addr);
addr += 8;
// read input
frame.header.full = as_read32(space, addr);
addr += 4;
for (uint32_t i = 0; i < frame.header.num_words; i++) {
frame.params[i] = as_read32(space, addr);
addr += 4;
}
// handle command and write response
if (maple_handle_command(mp, desc.port, &frame, &res)) {
as_write32(space, desc.result_addr, res.header.full);
desc.result_addr += 4;
for (uint32_t i = 0; i < res.header.num_words; i++) {
as_write32(space, desc.result_addr, res.params[i]);
desc.result_addr += 4;
}
} else {
as_write32(space, desc.result_addr, 0xffffffff);
}
} while (!desc.last);
*hl->SB_MDST = 0;
holly_raise_interrupt(hl, HOLLY_INTC_MDEINT);
}
static void holly_update_interrupts(struct holly *hl) {
// trigger the respective level-encoded interrupt on the sh4 interrupt
// controller
{
@ -38,6 +188,8 @@ static void holly_update_sh4_interrupts(struct holly *hl) {
sh4_clear_interrupt(hl->sh4, SH4_INTC_IRL_13);
}
}
// TODO check for hardware DMA initiation
}
#define define_reg_read(name, type) \
@ -92,14 +244,15 @@ void holly_raise_interrupt(struct holly *hl, holly_interrupt_t intr) {
enum holly_interrupt_type type = HOLLY_INTERRUPT_TYPE(intr);
uint32_t irq = HOLLY_INTERRUPT_IRQ(intr);
if (intr == HOLLY_INTC_PCVOINT) {
maple_vblank(hl->maple);
}
uint32_t *status = holly_interrupt_status(hl, type);
*status |= irq;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
// check for hardware dma initiation
if (intr == HOLLY_INTC_PCVOINT && *hl->SB_MDTSEL && *hl->SB_MDEN) {
holly_maple_dma(hl);
}
}
void holly_clear_interrupt(struct holly *hl, holly_interrupt_t intr) {
@ -109,7 +262,7 @@ void holly_clear_interrupt(struct holly *hl, holly_interrupt_t intr) {
uint32_t *status = holly_interrupt_status(hl, type);
*status &= ~irq;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
void holly_toggle_interrupt(struct holly *hl, holly_interrupt_t intr) {
@ -161,240 +314,136 @@ REG_W32(holly_cb, SB_ISTNRM) {
struct holly *hl = dc->holly;
// writing a 1 clears the interrupt
*hl->SB_ISTNRM &= ~value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_ISTEXT) {
struct holly *hl = dc->holly;
*hl->SB_ISTEXT &= ~value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_ISTERR) {
struct holly *hl = dc->holly;
*hl->SB_ISTERR &= ~value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML2NRM) {
struct holly *hl = dc->holly;
*hl->SB_IML2NRM = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML2EXT) {
struct holly *hl = dc->holly;
*hl->SB_IML2EXT = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML2ERR) {
struct holly *hl = dc->holly;
*hl->SB_IML2ERR = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML4NRM) {
struct holly *hl = dc->holly;
*hl->SB_IML4NRM = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML4EXT) {
struct holly *hl = dc->holly;
*hl->SB_IML4EXT = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML4ERR) {
struct holly *hl = dc->holly;
*hl->SB_IML4ERR = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML6NRM) {
struct holly *hl = dc->holly;
*hl->SB_IML6NRM = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML6EXT) {
struct holly *hl = dc->holly;
*hl->SB_IML6EXT = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_IML6ERR) {
struct holly *hl = dc->holly;
*hl->SB_IML6ERR = value;
holly_update_sh4_interrupts(hl);
holly_update_interrupts(hl);
}
REG_W32(holly_cb, SB_C2DST) {
struct holly *hl = dc->holly;
*hl->SB_C2DST = value;
if (!*hl->SB_C2DST) {
return;
if ((*hl->SB_C2DST = value)) {
holly_ch2_dma(hl);
}
// FIXME what are SB_LMMODE0 / SB_LMMODE1
struct sh4_dtr dtr = {0};
dtr.channel = 2;
dtr.rw = false;
dtr.addr = *hl->SB_C2DSTAT;
sh4_ddt(hl->sh4, &dtr);
*hl->SB_C2DLEN = 0;
*hl->SB_C2DST = 0;
holly_raise_interrupt(hl, HOLLY_INTC_DTDE2INT);
}
REG_W32(holly_cb, SB_SDST) {
struct holly *hl = dc->holly;
*hl->SB_SDST = value;
if (!*hl->SB_SDST) {
return;
if ((*hl->SB_SDST = value)) {
LOG_FATAL("Sort DMA not supported");
}
}
REG_W32(holly_cb, SB_MDST) {
struct holly *hl = dc->holly;
if ((*hl->SB_MDST = value)) {
holly_maple_dma(hl);
}
LOG_FATAL("Sort DMA not supported");
}
REG_W32(holly_cb, SB_GDST) {
struct gdrom *gd = dc->gdrom;
struct holly *hl = dc->holly;
// if a "0" is written to this register, it is ignored
*hl->SB_GDST |= value;
if (!*hl->SB_GDST) {
return;
if ((*hl->SB_GDST = value)) {
holly_gdrom_dma(hl);
}
CHECK_EQ(*hl->SB_GDEN, 1); // dma enabled
CHECK_EQ(*hl->SB_GDDIR, 1); // gd-rom -> system memory
int transfer_size = *hl->SB_GDLEN;
uint32_t start = *hl->SB_GDSTAR;
int remaining = transfer_size;
uint32_t addr = start;
gdrom_dma_begin(gd);
while (remaining) {
// read a single sector at a time from the gdrom
uint8_t sector_data[SECTOR_SIZE];
int n = gdrom_dma_read(gd, sector_data, sizeof(sector_data));
struct sh4_dtr dtr = {0};
dtr.channel = 0;
dtr.rw = true;
dtr.data = sector_data;
dtr.addr = addr;
dtr.size = n;
sh4_ddt(dc->sh4, &dtr);
remaining -= n;
addr += n;
}
gdrom_dma_end(gd);
*hl->SB_GDSTARD = start + transfer_size;
*hl->SB_GDLEND = transfer_size;
*hl->SB_GDST = 0;
holly_raise_interrupt(hl, HOLLY_INTC_G1DEINT);
}
REG_W32(holly_cb, SB_ADEN) {
struct holly *hl = dc->holly;
*hl->SB_ADEN = value;
if (!*hl->SB_ADEN) {
return;
}
LOG_WARNING("Ignored aica DMA request");
}
REG_W32(holly_cb, SB_ADST) {
struct holly *hl = dc->holly;
*hl->SB_ADST = value;
if (!*hl->SB_ADST) {
return;
if ((*hl->SB_ADST = value)) {
holly_g2_dma(hl, 0);
}
LOG_WARNING("Ignored aica DMA request");
}
REG_W32(holly_cb, SB_E1EN) {
struct holly *hl = dc->holly;
*hl->SB_E1EN = value;
if (!*hl->SB_E1EN) {
return;
}
LOG_WARNING("Ignored ext1 DMA request");
}
REG_W32(holly_cb, SB_E1ST) {
struct holly *hl = dc->holly;
*hl->SB_E1ST = value;
if (!*hl->SB_E1ST) {
return;
if ((*hl->SB_E1ST = value)) {
holly_g2_dma(hl, 1);
}
LOG_WARNING("Ignored ext1 DMA request");
}
REG_W32(holly_cb, SB_E2EN) {
struct holly *hl = dc->holly;
*hl->SB_E2EN = value;
if (!*hl->SB_E2EN) {
return;
}
LOG_WARNING("Ignored ext2 DMA request");
}
REG_W32(holly_cb, SB_E2ST) {
struct holly *hl = dc->holly;
*hl->SB_E2ST = value;
if (!*hl->SB_E2ST) {
return;
if ((*hl->SB_E2ST = value)) {
holly_g2_dma(hl, 2);
}
LOG_WARNING("Ignored ext2 DMA request");
}
REG_W32(holly_cb, SB_DDEN) {
struct holly *hl = dc->holly;
*hl->SB_DDEN = value;
if (!*hl->SB_DDEN) {
return;
}
LOG_WARNING("Ignored dev DMA request");
}
REG_W32(holly_cb, SB_DDST) {
struct holly *hl = dc->holly;
*hl->SB_DDST = value;
if (!*hl->SB_DDST) {
return;
if ((*hl->SB_DDST = value)) {
holly_g2_dma(hl, 3);
}
LOG_WARNING("Ignored dev DMA request");
}
REG_W32(holly_cb, SB_PDEN) {
struct holly *hl = dc->holly;
*hl->SB_PDEN = value;
if (!*hl->SB_PDEN) {
return;
}
LOG_WARNING("Ignored pvr DMA request");
}
REG_W32(holly_cb, SB_PDST) {
struct holly *hl = dc->holly;
*hl->SB_PDST = value;
if (!*hl->SB_PDST) {
return;
if ((*hl->SB_PDST = value)) {
LOG_WARNING("Ignored pvr DMA request");
}
LOG_WARNING("Ignored pvr DMA request");
}
// clang-format off

View File

@ -8,45 +8,6 @@ struct maple {
struct maple_device *devices[4];
};
static void maple_dma(struct maple *mp) {
struct address_space *space = mp->sh4->memory_if->space;
uint32_t start_addr = mp->holly->reg[SB_MDSTAR];
union maple_transfer desc;
struct maple_frame frame, res;
do {
desc.full = as_read64(space, start_addr);
start_addr += 8;
// read input
frame.header.full = as_read32(space, start_addr);
start_addr += 4;
for (uint32_t i = 0; i < frame.header.num_words; i++) {
frame.params[i] = as_read32(space, start_addr);
start_addr += 4;
}
// handle frame and write response
struct maple_device *dev = mp->devices[desc.port];
if (dev && dev->frame(dev, &frame, &res)) {
as_write32(space, desc.result_addr, res.header.full);
desc.result_addr += 4;
for (uint32_t i = 0; i < res.header.num_words; i++) {
as_write32(space, desc.result_addr, res.params[i]);
desc.result_addr += 4;
}
} else {
as_write32(space, desc.result_addr, 0xffffffff);
}
} while (!desc.last);
mp->holly->reg[SB_MDST] = 0;
holly_raise_interrupt(mp->holly, HOLLY_INTC_MDEINT);
}
static bool maple_init(struct device *dev) {
// struct maple *mp = (struct maple *)dev;
return true;
@ -61,19 +22,15 @@ static void maple_keydown(struct device *dev, enum keycode key, int16_t value) {
}
}
void maple_vblank(struct maple *mp) {
uint32_t enabled = mp->holly->reg[SB_MDEN];
uint32_t vblank_initiate = mp->holly->reg[SB_MDTSEL];
int maple_handle_command(struct maple *mp, int port, struct maple_frame *frame,
struct maple_frame *res) {
struct maple_device *dev = mp->devices[port];
// The controller can be started up by two methods: by software, or by
// hardware
// in synchronization with the V-BLANK signal. These methods are selected
// through the trigger selection register (SB_MDTSEL).
if (enabled && vblank_initiate) {
maple_dma(mp);
if (!dev) {
return 0;
}
// TODO maple vblank interrupt?
return dev->frame(dev, frame, res);
}
struct maple *maple_create(struct dreamcast *dc) {
@ -98,18 +55,3 @@ void maple_destroy(struct maple *mp) {
dc_destroy_window_interface(mp->window_if);
dc_destroy_device((struct device *)mp);
}
REG_W32(holly_cb, SB_MDST) {
struct maple *mp = dc->maple;
struct holly *hl = dc->holly;
*hl->SB_MDST = value;
if (*hl->SB_MDEN) {
if (*hl->SB_MDST) {
maple_dma(mp);
}
} else {
*hl->SB_MDST = 0;
}
}

View File

@ -18,7 +18,9 @@ struct maple_device {
struct maple_device *controller_create();
void maple_vblank(struct maple *mp);
int maple_handle_command(struct maple *mp, int port, struct maple_frame *frame,
struct maple_frame *res);
struct maple *maple_create(struct dreamcast *dc);
void maple_destroy(struct maple *mp);