From 6d54a5fe0ae6742ce598759e1e028d526e027112 Mon Sep 17 00:00:00 2001 From: lephe Date: Tue, 27 Aug 2019 21:18:44 +0200 Subject: [PATCH] dma: add exception handler and dma_memset() --- include/gint/dma.h | 4 ++-- src/dma/dma.c | 52 ++++++++++++++++++++++++++++++++++-------- src/dma/inth.s | 18 +++++++++++++++ src/dma/memset.c | 11 +++++++++ src/render-cg/dclear.c | 34 +++------------------------ 5 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 src/dma/memset.c diff --git a/include/gint/dma.h b/include/gint/dma.h index f4e984e..2425050 100644 --- a/include/gint/dma.h +++ b/include/gint/dma.h @@ -55,7 +55,7 @@ typedef enum @dst Destination address, must be aligned with transfer size @dst_mode Destination address mode */ void dma_transfer(int channel, dma_size_t size, uint length, - void *src, dma_address_t src_mode, + void const *src, dma_address_t src_mode, void *dst, dma_address_t dst_mode); /* dma_transfer_wait() - Wait for a transfer on channel 0 to finish @@ -76,7 +76,7 @@ void dma_transfer_wait(int channel); Not using interrupts is a bad design idea for a majority of programs, and is only ever needed to display panic messages inside exception handlers. */ void dma_transfer_noint(int channel, dma_size_t size, uint blocks, - void *src, dma_address_t src_mode, + void const *src, dma_address_t src_mode, void *dst, dma_address_t dst_mode); #endif /* GINT_DMA */ diff --git a/src/dma/dma.c b/src/dma/dma.c index aa53149..8b86cf4 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -13,7 +13,7 @@ typedef volatile sh7305_dma_channel_t channel_t; -/* dma_channel(): returns address of the specified DMA channel */ +/* dma_channel(): Get address of a DMA channel */ static channel_t *dma_channel(int channel) { channel_t *addr[6] = { @@ -24,6 +24,36 @@ static channel_t *dma_channel(int channel) return ((uint)channel >= 6 ? NULL : addr[channel]); } +/* dma_translate(): Translate virtual address to DMA-suitable form */ +static uint32_t dma_translate(void const *address) +{ + uint32_t a = (uint32_t)address; + + /* Preserve RS addresses (as of SH7724 Reference, 11.2.2) */ + if(a >= 0xfd800000 && a < 0xfd800800) + return a; + + /* Translate virtual addresses to IL memory to physical addresses; the + same address is used (as of SH7724 Reference, 10.3.3) */ + if(a >= 0xe5200000 && a < 0xe5204000) + return a; + + /* First additional on-chip memory area (XRAM) */ + if(a >= 0xe5007000 && a < 0xE5009000) + return a; + + /* Second on-chip memory area (YRAM) */ + if(a >= 0xe5017000 && a < 0xe5019000) + return a; + + /* Translate P1 and P2 addresses to ROM and RAM to physical form */ + if(a >= 0x80000000 && a < 0xc0000000) + return a & 0x1fffffff; + + /* By default: I don't know what this is, let's preserve it */ + return a; +} + //--- // Driver interface //--- @@ -33,7 +63,7 @@ static channel_t *dma_channel(int channel) last parameter indicates whether interrupts should be used. Returns non-zero if the DMA is busy or a configuration error occurs. */ static int dma_setup(int channel, dma_size_t size, uint blocks, - void *src, dma_address_t src_mode, + void const *src, dma_address_t src_mode, void *dst, dma_address_t dst_mode, int interrupts) { @@ -48,8 +78,8 @@ static int dma_setup(int channel, dma_size_t size, uint blocks, DMA.OR.DME = 0; /* Set DMA source and target address */ - ch->SAR = (uint32_t)src & 0x1fffffff; - ch->DAR = (uint32_t)dst & 0x1fffffff; + ch->SAR = dma_translate(src); + ch->DAR = dma_translate(dst); /* Set the number of blocks to be transferred */ ch->TCR = blocks; @@ -74,7 +104,7 @@ static int dma_setup(int channel, dma_size_t size, uint blocks, /* dma_transfer(): Perform a data transfer */ void dma_transfer(int channel, dma_size_t size, uint blocks, - void *src, dma_address_t src_mode, + void const *src, dma_address_t src_mode, void *dst, dma_address_t dst_mode) { if(dma_setup(channel, size, blocks, src, src_mode, dst, dst_mode, 1)) @@ -97,7 +127,7 @@ void dma_transfer_wait(int channel) /* dma_transfer_noint(): Perform a data transfer without interruptions */ void dma_transfer_noint(int channel, dma_size_t size, uint blocks, - void *src, dma_address_t src_mode, + void const *src, dma_address_t src_mode, void *dst, dma_address_t dst_mode) { if(dma_setup(channel, size, blocks, src, src_mode, dst, dst_mode, 0)) @@ -130,7 +160,6 @@ static void init(void) if(isSH3()) return; /* Install the interrupt handler from dma/inth.s */ - /* TODO: DMA0 error gate is 0xbc0 */ int codes[] = { 0x800, 0x820, 0x840, 0x860, 0xb80, 0xba0 }; extern void inth_dma_te(void); @@ -146,15 +175,18 @@ static void init(void) ch->CHCR.DE = 0; } + /* Install the address error gate */ + extern void inth_dma_ae(void); + gint_inthandler(0xbc0, inth_dma_ae, 32); + /* Set interrupt priority to 3 (IPRE[15..12] for first three channels, IPRF[11..8] for last two and error gate */ gint_intlevel(16, 3); gint_intlevel(21, 3); - /* Unmask the channel interrupts */ - /* TODO: DMA0 error gate is IMR5 & 0x40 */ + /* Unmask the channel interrupts and the address error */ INTC.MSKCLR->IMR1 = 0x0f; - INTC.MSKCLR->IMR5 = 0x30; + INTC.MSKCLR->IMR5 = 0x70; /* Clear blocking flags and enable the master switch */ DMA.OR.AE = 0; diff --git a/src/dma/inth.s b/src/dma/inth.s index 2efaea1..19f5aeb 100644 --- a/src/dma/inth.s +++ b/src/dma/inth.s @@ -4,6 +4,7 @@ */ .global _inth_dma_te +.global _inth_dma_ae .section .gint.blocks, "ax" .align 4 @@ -29,3 +30,20 @@ _inth_dma_te: 1: .long 0 /* CHCR, set dynamically */ 2: .long 0xfe008060 /* DMA.OR */ + +/* DMA ADDRESS ERROR INTERRUPT HANDLER - 18 BYTES */ + +_inth_dma_ae: + /* Call a fixed function */ + sts.l pr, @-r15 + mov.l 1f, r1 + jsr @r1 + nop + lds.l @r15+, pr + + rte + nop + + .zero 14 + +1: .long _dma_address_error diff --git a/src/dma/memset.c b/src/dma/memset.c new file mode 100644 index 0000000..40ce5c9 --- /dev/null +++ b/src/dma/memset.c @@ -0,0 +1,11 @@ +#include + +/* dma_memset(): Fast memset for highly-aligned addresses */ +void dma_memset(void *dst, uint32_t l, size_t size) +{ + /* TODO: Use a proper IL memory allocation scheme */ + uint32_t *IL = (void *)0xe5200000; + for(int i = 0; i < 8; i++) IL[i] = l; + + dma_transfer_noint(1, DMA_32B, size >> 5, IL, DMA_FIXED, dst, DMA_INC); +} diff --git a/src/render-cg/dclear.c b/src/render-cg/dclear.c index b8877d6..939b2c5 100644 --- a/src/render-cg/dclear.c +++ b/src/render-cg/dclear.c @@ -1,38 +1,10 @@ #define GINT_NEED_VRAM #include +#include /* dclear() - fill the screen with a single color */ void dclear(uint16_t color) { - /* TOOD: render-cg: dclear(): Consider using the DMA */ - - /* Operate in longwords to clear faster. Also start at the end of the - VRAM to take advantage of pre-decrement writes. */ - uint32_t *v = (void *)(vram + 88704); - uint32_t op = (color << 16) | color; - - /* Split the VRAM into 2772 blocks of 16 longwords to reduce the - overhead of the loop */ - for(int blocks = 2772; blocks; blocks--) - { - *--v = op; - *--v = op; - *--v = op; - *--v = op; - - *--v = op; - *--v = op; - *--v = op; - *--v = op; - - *--v = op; - *--v = op; - *--v = op; - *--v = op; - - *--v = op; - *--v = op; - *--v = op; - *--v = op; - } + dma_memset(vram, (color << 16) | color, 396 * 224 * 2); + return; }