update DMA status registers immediately, not when the interrupt timer ends.

this avoids games trying to suspend the DMA transfers due to a lack of
progress
This commit is contained in:
Anthony Pesch 2017-06-28 20:28:59 -04:00
parent fac45b83d1
commit 8ddd9bc9f5
3 changed files with 177 additions and 41 deletions

View File

@ -11,6 +11,10 @@ struct reg_cb holly_cb[NUM_HOLLY_REGS];
/*
* ch2 dma
*/
static void holly_ch2_dma_stop(struct holly *hl) {
/* nop as DMA is always performed synchronously */
}
static void holly_ch2_dma(struct holly *hl) {
/* FIXME what are SB_LMMODE0 / SB_LMMODE1 */
struct sh4_dtr dtr = {0};
@ -29,6 +33,7 @@ static void holly_ch2_dma(struct holly *hl) {
*/
static void holly_gdrom_dma(struct holly *hl) {
if (!*hl->SB_GDEN) {
*hl->SB_GDST = 0;
return;
}
@ -77,6 +82,7 @@ static void holly_gdrom_dma(struct holly *hl) {
*/
static void holly_maple_dma(struct holly *hl) {
if (!*hl->SB_MDEN) {
*hl->SB_MDST = 0;
return;
}
@ -125,7 +131,7 @@ static void holly_maple_dma(struct holly *hl) {
break;
default:
LOG_FATAL("Unhandled maple pattern 0x%x", desc.pattern);
LOG_FATAL("unhandled maple pattern 0x%x", desc.pattern);
break;
}
@ -159,11 +165,6 @@ static struct g2_channel_desc g2_channels[] = {
static void holly_g2_dma_timer_ch##channel(void *data) { \
struct holly *hl = data; \
struct g2_channel_desc *desc = &g2_channels[channel]; \
uint32_t len = hl->reg[desc->LEN]; \
int restart = len >> 31; \
hl->reg[desc->LEN] = 0; \
hl->reg[desc->EN] = restart; \
hl->reg[desc->ST] = 0; \
holly_raise_interrupt(hl, desc->INTR); \
}
@ -180,15 +181,27 @@ static void (*g2_timers[])(void *) = {
};
/* clang-format on */
static void holly_g2_dma_suspend(struct holly *hl, int channel) {
struct g2_channel_desc *desc = &g2_channels[channel];
if (!hl->reg[desc->EN] || !hl->reg[desc->ST]) {
return;
}
LOG_FATAL("holly_g2_dma_suspend not supported");
}
static void holly_g2_dma(struct holly *hl, int channel) {
struct g2_channel_desc *desc = &g2_channels[channel];
if (!hl->reg[desc->EN]) {
hl->reg[desc->ST] = 0;
return;
}
struct address_space *space = hl->sh4->memory_if->space;
uint32_t len = hl->reg[desc->LEN];
int restart = (len >> 31) == 0;
int transfer_size = len & 0x7fffffff;
int remaining = transfer_size;
uint32_t src = hl->reg[desc->STAR];
@ -197,10 +210,10 @@ static void holly_g2_dma(struct holly *hl, int channel) {
/* only sh4 -> g2 supported for now */
CHECK_EQ(hl->reg[desc->DIR], 0);
/* perform the DMA instantly, but don't update the DMA status registers or
raise the end of DMA interrupt until the DMA should actually end. this
hopefully fixes issues in games which break when DMAs end immediately,
without having to actually emulate the 16-bit x 25mhz g2 bus transfer */
/* perform the DMA immediately, but don't raise the end of DMA interrupt until
the DMA should actually end. this hopefully fixes issues in games which
break when DMAs end immediately, without having to actually emulate the
16-bit x 25mhz g2 bus transfer */
while (remaining) {
as_write32(space, dst, as_read32(space, src));
remaining -= 4;
@ -208,7 +221,12 @@ static void holly_g2_dma(struct holly *hl, int channel) {
dst += 4;
}
/* TODO read data into staging buffer from devices? */
/* the status registers need to be updated immediately as well. if they're not
updated until the interrupt is raised, the DMA functions used by games will
try to suspend the transfer due to a lack of progress */
hl->reg[desc->LEN] = 0;
hl->reg[desc->EN] = restart;
hl->reg[desc->ST] = 0;
int64_t end = CYCLES_TO_NANO(transfer_size / 2, UINT64_C(25000000));
scheduler_start_timer(hl->scheduler, g2_timers[channel], hl, end);
@ -294,7 +312,7 @@ static uint32_t *holly_interrupt_status(struct holly *hl,
case HOLLY_INT_ERR:
return hl->SB_ISTERR;
default:
LOG_FATAL("Invalid interrupt type");
LOG_FATAL("invalid interrupt type");
}
}
@ -475,94 +493,191 @@ REG_W32(holly_cb, SB_IML6ERR) {
REG_W32(holly_cb, SB_C2DST) {
struct holly *hl = dc->holly;
if ((*hl->SB_C2DST = value)) {
*hl->SB_C2DST = value;
if (*hl->SB_C2DST) {
holly_ch2_dma(hl);
} else {
holly_ch2_dma_stop(hl);
}
}
REG_W32(holly_cb, SB_SDST) {
struct holly *hl = dc->holly;
if ((*hl->SB_SDST = value)) {
LOG_FATAL("Sort DMA not supported");
/* can't write 0 */
*hl->SB_SDST |= value;
if (*hl->SB_SDST) {
LOG_FATAL("sort DMA not supported");
}
}
REG_W32(holly_cb, SB_MDST) {
struct holly *hl = dc->holly;
if ((*hl->SB_MDST = value)) {
/* can't write 0 */
*hl->SB_MDST |= value;
if (*hl->SB_MDST) {
holly_maple_dma(hl);
}
}
REG_W32(holly_cb, SB_GDST) {
struct holly *hl = dc->holly;
if ((*hl->SB_GDST = value)) {
/* can't write 0 */
*hl->SB_GDST |= value;
if (*hl->SB_GDST) {
holly_gdrom_dma(hl);
}
}
REG_W32(holly_cb, SB_ADST) {
struct holly *hl = dc->holly;
if ((*hl->SB_ADST = value)) {
/* can't write 0 */
*hl->SB_ADST |= value;
if (*hl->SB_ADST) {
holly_g2_dma(hl, 0);
}
}
REG_W32(holly_cb, SB_ADSUSP) {
struct holly *hl = dc->holly;
hl->SB_ADSUSP->full = value;
if (hl->SB_ADTSEL->susp && hl->SB_ADSUSP->susp) {
holly_g2_dma_suspend(hl, 0);
}
}
REG_W32(holly_cb, SB_ADTSEL) {
if ((value & 0x2) == 0x2) {
LOG_FATAL("Hardware DMA trigger not supported");
struct holly *hl = dc->holly;
hl->SB_ADTSEL->full = value;
if (hl->SB_ADTSEL->hw) {
LOG_FATAL("hardware DMA trigger not supported");
}
}
REG_W32(holly_cb, SB_E1ST) {
struct holly *hl = dc->holly;
if ((*hl->SB_E1ST = value)) {
/* can't write 0 */
*hl->SB_E1ST |= value;
if (*hl->SB_E1ST) {
holly_g2_dma(hl, 1);
}
}
REG_W32(holly_cb, SB_E1SUSP) {
struct holly *hl = dc->holly;
hl->SB_E1SUSP->full = value;
if (hl->SB_E1TSEL->susp && hl->SB_E1SUSP->susp) {
holly_g2_dma_suspend(hl, 1);
}
}
REG_W32(holly_cb, SB_E1TSEL) {
if ((value & 0x2) == 0x2) {
LOG_FATAL("Hardware DMA trigger not supported");
struct holly *hl = dc->holly;
hl->SB_E1TSEL->full = value;
if (hl->SB_E1TSEL->hw) {
LOG_FATAL("hardware DMA trigger not supported");
}
}
REG_W32(holly_cb, SB_E2ST) {
struct holly *hl = dc->holly;
if ((*hl->SB_E2ST = value)) {
/* can't write 0 */
*hl->SB_E2ST |= value;
if (*hl->SB_E2ST) {
holly_g2_dma(hl, 2);
}
}
REG_W32(holly_cb, SB_E2SUSP) {
struct holly *hl = dc->holly;
hl->SB_E2SUSP->full = value;
if (hl->SB_E2TSEL->susp && hl->SB_E2SUSP->susp) {
holly_g2_dma_suspend(hl, 2);
}
}
REG_W32(holly_cb, SB_E2TSEL) {
if ((value & 0x2) == 0x2) {
LOG_FATAL("Hardware DMA trigger not supported");
struct holly *hl = dc->holly;
hl->SB_E2TSEL->full = value;
if (hl->SB_E2TSEL->hw) {
LOG_FATAL("hardware DMA trigger not supported");
}
}
REG_W32(holly_cb, SB_DDST) {
struct holly *hl = dc->holly;
if ((*hl->SB_DDST = value)) {
/* can't write 0 */
*hl->SB_DDST |= value;
if (*hl->SB_DDST) {
holly_g2_dma(hl, 3);
}
}
REG_W32(holly_cb, SB_DDSUSP) {
struct holly *hl = dc->holly;
hl->SB_DDSUSP->full = value;
if (hl->SB_DDTSEL->susp && hl->SB_DDSUSP->susp) {
holly_g2_dma_suspend(hl, 3);
}
}
REG_W32(holly_cb, SB_DDTSEL) {
if ((value & 0x2) == 0x2) {
LOG_FATAL("Hardware DMA trigger not supported");
struct holly *hl = dc->holly;
hl->SB_DDTSEL->full = value;
if (hl->SB_DDTSEL->hw) {
LOG_FATAL("hardware DMA trigger not supported");
}
}
REG_W32(holly_cb, SB_PDST) {
struct holly *hl = dc->holly;
if ((*hl->SB_PDST = value)) {
LOG_FATAL("PVR DMA not supported");
/* can't write 0 */
*hl->SB_PDST |= value;
if (*hl->SB_PDST) {
LOG_FATAL("pvr DMA not supported");
}
}
REG_W32(holly_cb, SB_PDTSEL) {
if (value) {
LOG_FATAL("Hardware DMA trigger not supported");
struct holly *hl = dc->holly;
*hl->SB_PDTSEL = value;
if (*hl->SB_PDTSEL) {
LOG_FATAL("hardware DMA trigger not supported");
}
}

View File

@ -81,34 +81,34 @@ HOLLY_REG(0x005f7800, SB_ADSTAG, 0x00000000, uint32_t)
HOLLY_REG(0x005f7804, SB_ADSTAR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7808, SB_ADLEN, 0x00000000, uint32_t)
HOLLY_REG(0x005f780c, SB_ADDIR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7810, SB_ADTSEL, 0x00000000, uint32_t)
HOLLY_REG(0x005f7810, SB_ADTSEL, 0x00000000, union g2_tsel)
HOLLY_REG(0x005f7814, SB_ADEN, 0x00000000, uint32_t)
HOLLY_REG(0x005f7818, SB_ADST, 0x00000000, uint32_t)
HOLLY_REG(0x005f781c, SB_ADSUSP, 0x00000000, uint32_t)
HOLLY_REG(0x005f781c, SB_ADSUSP, 0x00000000, union g2_susp)
HOLLY_REG(0x005f7820, SB_E1STAG, 0x00000000, uint32_t)
HOLLY_REG(0x005f7824, SB_E1STAR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7828, SB_E1LEN, 0x00000000, uint32_t)
HOLLY_REG(0x005f782c, SB_E1DIR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7830, SB_E1TSEL, 0x00000000, uint32_t)
HOLLY_REG(0x005f7830, SB_E1TSEL, 0x00000000, union g2_tsel)
HOLLY_REG(0x005f7834, SB_E1EN, 0x00000000, uint32_t)
HOLLY_REG(0x005f7838, SB_E1ST, 0x00000000, uint32_t)
HOLLY_REG(0x005f783c, SB_E1SUSP, 0x00000000, uint32_t)
HOLLY_REG(0x005f783c, SB_E1SUSP, 0x00000000, union g2_susp)
HOLLY_REG(0x005f7840, SB_E2STAG, 0x00000000, uint32_t)
HOLLY_REG(0x005f7844, SB_E2STAR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7848, SB_E2LEN, 0x00000000, uint32_t)
HOLLY_REG(0x005f784c, SB_E2DIR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7850, SB_E2TSEL, 0x00000000, uint32_t)
HOLLY_REG(0x005f7850, SB_E2TSEL, 0x00000000, union g2_tsel)
HOLLY_REG(0x005f7854, SB_E2EN, 0x00000000, uint32_t)
HOLLY_REG(0x005f7858, SB_E2ST, 0x00000000, uint32_t)
HOLLY_REG(0x005f785c, SB_E2SUSP, 0x00000000, uint32_t)
HOLLY_REG(0x005f785c, SB_E2SUSP, 0x00000000, union g2_susp)
HOLLY_REG(0x005f7860, SB_DDSTAG, 0x00000000, uint32_t)
HOLLY_REG(0x005f7864, SB_DDSTAR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7868, SB_DDLEN, 0x00000000, uint32_t)
HOLLY_REG(0x005f786c, SB_DDDIR, 0x00000000, uint32_t)
HOLLY_REG(0x005f7870, SB_DDTSEL, 0x00000000, uint32_t)
HOLLY_REG(0x005f7870, SB_DDTSEL, 0x00000000, union g2_tsel)
HOLLY_REG(0x005f7874, SB_DDEN, 0x00000000, uint32_t)
HOLLY_REG(0x005f7878, SB_DDST, 0x00000000, uint32_t)
HOLLY_REG(0x005f787c, SB_DDSUSP, 0x00000000, uint32_t)
HOLLY_REG(0x005f787c, SB_DDSUSP, 0x00000000, union g2_susp)
HOLLY_REG(0x005f7880, SB_G2ID, 0x00000012, uint32_t)
HOLLY_REG(0x005f7890, SB_G2DSTO, 0x000003ff, uint32_t)
HOLLY_REG(0x005f7894, SB_G2TRTO, 0x000003ff, uint32_t)

View File

@ -4,6 +4,27 @@
#include <stdint.h>
/* registers */
union g2_tsel {
uint32_t full;
struct {
uint32_t ext : 1;
uint32_t hw : 1;
uint32_t susp : 1;
uint32_t : 29;
};
};
union g2_susp {
uint32_t full;
struct {
uint32_t susp : 1;
uint32_t : 3;
uint32_t transfer_state : 1;
uint32_t request_state : 1;
uint32_t : 26;
};
};
enum {
#define HOLLY_REG(addr, name, flags, default) name = (addr - 0x005f0000) >> 2,
#include "guest/holly/holly_regs.inc"