From 8ddd9bc9f55122919784c90d65151237b547bb22 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Wed, 28 Jun 2017 20:28:59 -0400 Subject: [PATCH] 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 --- src/guest/holly/holly.c | 181 +++++++++++++++++++++++++++------ src/guest/holly/holly_regs.inc | 16 +-- src/guest/holly/holly_types.h | 21 ++++ 3 files changed, 177 insertions(+), 41 deletions(-) diff --git a/src/guest/holly/holly.c b/src/guest/holly/holly.c index 28e468e5..d81caf98 100644 --- a/src/guest/holly/holly.c +++ b/src/guest/holly/holly.c @@ -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"); } } diff --git a/src/guest/holly/holly_regs.inc b/src/guest/holly/holly_regs.inc index 45da8c55..cad3313f 100644 --- a/src/guest/holly/holly_regs.inc +++ b/src/guest/holly/holly_regs.inc @@ -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) diff --git a/src/guest/holly/holly_types.h b/src/guest/holly/holly_types.h index cf8af5bf..74c481c5 100644 --- a/src/guest/holly/holly_types.h +++ b/src/guest/holly/holly_types.h @@ -4,6 +4,27 @@ #include /* 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"