From f8d69dd56a00eacfbf3c351e2c13b6e948f0aa6e Mon Sep 17 00:00:00 2001 From: lephe Date: Thu, 4 Jul 2019 11:46:26 -0400 Subject: [PATCH] display: add a dupdate_noint() for exceptions handlers --- include/gint/display.h | 18 +++++++++++++--- include/gint/dma.h | 13 +++++++++-- src/dma/dma.c | 48 ++++++++++++++++++++++++++++++++++++----- src/r61524/r61524.c | 15 ++++++++----- src/render-cg/dupdate.c | 10 +++++++-- src/render-fx/dupdate.c | 6 ++++++ 6 files changed, 93 insertions(+), 17 deletions(-) diff --git a/include/gint/display.h b/include/gint/display.h index 76d29f1..963ab0f 100644 --- a/include/gint/display.h +++ b/include/gint/display.h @@ -152,7 +152,7 @@ typedef struct gint's default font is used. On fx9860g, the default font is a 5x7 font very close to the system's. - On fxcg50, the default font is an original 10x12 font. + On fxcg50, the default font is an original 8x9 font. @font Font to use for subsequent text rendering calls */ void dfont(font_t const * font); @@ -192,10 +192,22 @@ void dsize(const char *str, font_t const * font, int *w, int *h); @str String to display @fg Text color fx9860g: white, black, none, invert - fxcg50: Any R5G6B6 color, or [color_none] + fxcg50: Any R5G6B6 color, or C_NONE @bg Background color fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color, or [color_none] */ + fxcg50: Any R5G6B5 color, or C_NONE */ void dtext(int x, int y, const char *str, int fg, int bg); +//--- +// Advanced functions +//--- + +/* dupdate_noint() - Push VRAM to the display without interrupts + This function does exactly as dupdate(), but does not use interrupts and + always returns when the transfer is finished. It actively waits for the end + of the transfer and is thus bad for any general-purpose use. In fact, it is + only needed to display panic messages in exception handlers. Don't use it + unless you know precisely why you're doing it. */ +void dupdate_noint(void); + #endif /* GINT_DISPLAY */ diff --git a/include/gint/dma.h b/include/gint/dma.h index 9917946..033bab9 100644 --- a/include/gint/dma.h +++ b/include/gint/dma.h @@ -42,7 +42,6 @@ typedef enum } dma_address_t; /* dma_transfer() - Start a data transfer on channel 0 - This function returns just when the transfer starts. The transfer will end later on and the DMA will be stopped by an interrupt. Call dma_transfer_wait() if you need to wait for the transfer to finish. Don't @@ -59,11 +58,21 @@ void dma_transfer(dma_size_t size, uint length, void *dst, dma_address_t dst_mode); /* dma_transfer_wait() - Wait for a transfer on channel 0 to finish - You should call this function when you need to transfer to be complete before continuing execution. If you are sure that the transfer is finished, this is not necessary (the only way to know is to look at the DMA registers or record interrupts). */ void dma_transfer_wait(void); +/* dma_transfer_noint() - Perform a data transfer without interrupts + This function performs a transfer much like dma_transfer(), but doesn't use + interrupts and *actively waits* for the transfer to finish, returning when + it's finished. Don't call dma_transfer_wait() after using this function. + + 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(dma_size_t size, uint blocks, + void *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 546b5a4..f768e0e 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -15,13 +15,17 @@ // Driver interface //--- -/* dma_transfer() - Perform a data transfer */ -void dma_transfer(dma_size_t size, uint blocks, +/* dma_setup() - Setup the DMA in interrupt or no-interrupt mode. + The first parameters are as for dma_transfer() and dma_transfer_noint(). The + 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(dma_size_t size, uint blocks, void *src, dma_address_t src_mode, - void *dst, dma_address_t dst_mode) + void *dst, dma_address_t dst_mode, + int interrupts) { /* Safety guard: only start a transfer if there's not one running */ - if(DMA.DMA0.CHCR.DE) return; + if(DMA.DMA0.CHCR.DE) return 1; /* Disable DMA0 and disable the master DMA switch */ DMA.DMA0.CHCR.DE = 0; @@ -41,7 +45,7 @@ void dma_transfer(dma_size_t size, uint blocks, DMA.DMA0.CHCR.TS_10 = (size & 3); DMA.DMA0.CHCR.DM = dst_mode; DMA.DMA0.CHCR.SM = src_mode; - DMA.DMA0.CHCR.IE = 1; + DMA.DMA0.CHCR.IE = !!interrupts; /* Prepare DMAOR by enabling the master switch and clearing the blocking flags. */ @@ -49,6 +53,16 @@ void dma_transfer(dma_size_t size, uint blocks, DMA.OR.AE = 0; DMA.OR.NMIF = 0; + return 0; +} + +/* dma_transfer() - Perform a data transfer */ +void dma_transfer(dma_size_t size, uint blocks, + void *src, dma_address_t src_mode, + void *dst, dma_address_t dst_mode) +{ + if(dma_setup(size, blocks, src, src_mode, dst, dst_mode, 1)) return; + /* Enable channel 0, starting the DMA transfer. */ DMA.DMA0.CHCR.DE = 1; } @@ -60,6 +74,30 @@ void dma_transfer_wait(void) while(DMA.OR.DME) sleep(); } +/* dma_transfer_noint() - Perform a data transfer without interruptions */ +void dma_transfer_noint(dma_size_t size, uint blocks, + void *src, dma_address_t src_mode, + void *dst, dma_address_t dst_mode) +{ + if(dma_setup(size, blocks, src, src_mode, dst, dst_mode, 0)) return; + + /* Enable channel 0, starting the DMA transfer. */ + DMA.DMA0.CHCR.DE = 1; + + /* Actively wait until the transfer is finished */ + while(!DMA.DMA0.CHCR.TE); + + /* Disable the channel and clear the TE flag. Disable the channel first + as clearing the TE flag will allow the transfer to restart */ + DMA.DMA0.CHCR.DE = 0; + DMA.DMA0.CHCR.TE = 0; + + /* Clear the AE and NMIF status flags and cut the master switch */ + DMA.OR.DME = 0; + DMA.OR.AE = 0; + DMA.OR.NMIF = 0; +} + //--- // Initialization //--- diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c index 9060699..969af31 100644 --- a/src/r61524/r61524.c +++ b/src/r61524/r61524.c @@ -202,9 +202,8 @@ void r61524_win_set(uint16_t HSA, uint16_t HEA, uint16_t VSA, uint16_t VEA) /* TODO: r61524: update, backlight, brightness, gamma */ -void r61524_display(uint16_t *vram, int start, int height) +void r61524_display(uint16_t *vram, int start, int height, int interrupts) { - /* Move the window to the desired region, then select address 0 */ r61524_win_set(0, 395, start, start + height - 1); select(ram_address_horizontal); @@ -224,9 +223,15 @@ void r61524_display(uint16_t *vram, int start, int height) int blocks = 99 * (height >> 2); /* Now roll! */ - dma_transfer(DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED); - /* Wait for any previous DMA transfer to finish */ - dma_transfer_wait(); + if(interrupts) + { + /* Usa a normal, interrupt-based transfer */ + dma_transfer(DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED); + /* Wait for it to finish */ + /* TODO: Allow r61524_display() to return early */ + dma_transfer_wait(); + } + else dma_transfer_noint(DMA_32B, blocks, src, DMA_INC, dst, DMA_FIXED); } //--- diff --git a/src/render-cg/dupdate.c b/src/render-cg/dupdate.c index a59a177..ec1bb23 100644 --- a/src/render-cg/dupdate.c +++ b/src/render-cg/dupdate.c @@ -2,8 +2,14 @@ #include //#include -/* dupdate() - push the video RAM to the display driver */ +/* dupdate() - Push the video RAM to the display driver */ void dupdate(void) { - r61524_display(vram, 0, 224); + r61524_display(vram, 0, 224, 1); +} + +/* dupdate_noint() - Push VRAM to the display without interrupts */ +void dupdate_noint(void) +{ + r61524_display(vram, 0, 224, 0); } diff --git a/src/render-fx/dupdate.c b/src/render-fx/dupdate.c index f673d79..8ff7297 100644 --- a/src/render-fx/dupdate.c +++ b/src/render-fx/dupdate.c @@ -13,3 +13,9 @@ void dupdate(void) { t6k11_display(vram, 0, 64, 16); } + +/* dupdate_noint() - Push VRAM to the display without interrupts */ +void dupdate_noint(void) +{ + dupdate(); +}