/** * \file dma.h * \brief DMA support * \author Stephane Dallongeville * \date 04/2015 * * This unit provides methods to use the hardware DMA capabilities. */ #include "config.h" #include "types.h" #ifndef _DMA_H_ #define _DMA_H_ /** * \brief * VRAM destination for DMA operation. */ #define DMA_VRAM 0 /** * \brief * CRAM destination for DMA operation. */ #define DMA_CRAM 1 /** * \brief * VSRAM destination for DMA operation. */ #define DMA_VSRAM 2 #define DMA_QUEUE_SIZE_DEFAULT 80 #define DMA_QUEUE_SIZE_MIN 32 #define DMA_TRANSFER_CAPACITY_NTSC 7200 #define DMA_TRANSFER_CAPACITY_PAL_LOW 8000 #define DMA_TRANSFER_CAPACITY_PAL_MAX 15000 // better to allocate a bit more than DMA capacity #define DMA_BUFFER_SIZE_NTSC 8192 #define DMA_BUFFER_SIZE_PAL_LOW 8192 #define DMA_BUFFER_SIZE_PAL_MAX (14 * 1024) #define DMA_BUFFER_SIZE_MIN (2 * 1024) /** * \brief * VRAM transfer method */ typedef enum { CPU = 0, /**< Transfer through the CPU immediately (slower.. useful for testing purpose mainly) */ DMA = 1, /**< Transfer through DMA immediately, using DMA is faster but can lock Z80 execution */ DMA_QUEUE = 2, /**< Put in the DMA queue so it will be transferred at next VBlank. Using DMA is faster but can lock Z80 execution */ DMA_QUEUE_COPY = 3 /**< Copy the buffer and put in the DMA queue so it will be transferred at next VBlank. Using DMA is faster but can lock Z80 execution */ } TransferMethod; /** * \brief * DMA transfer definition (used for DMA queue) */ typedef struct { u16 regLenL; // (newLen & 0xFF) | 0x9300; u16 regLenH; // ((newLen >> 8) & 0xFF) | 0x9400; u32 regAddrMStep; // (((addr << 7) & 0xFF0000) | 0x96008F00) + step; u32 regAddrHAddrL; // ((addr >> 1) & 0x7F00FF) | 0x97009500; u32 regCtrlWrite; // VDP_DMA_VRAMCOPY_ADDR(to) } DMAOpInfo; /** * \brief * DMA queue structure */ extern DMAOpInfo *dmaQueues; /** * \brief * DMA queue structure */ extern u16* dmaDataBuffer; /** * \brief * Initialize the DMA queue sub system with default settings. * * SGDK automatically call this method on hard reset so you don't need to call it again unless * you want to change the default parameters in which casse you should use #DMA_initEx(..) * * \see #DMA_initEx(..) */ void DMA_init(void); /** * \brief * Initialize the DMA queue sub system. * * \param size * The queue size (0 = default size = 64, min size = 20). * \param capacity * The maximum allowed size (in bytes) to transfer per #DMA_flushQueue() call (0 = default = no limit).
* Depending the current selected strategy, furthers transfers can be ignored (by default all transfers are done whatever is the limit). * See the #DMA_setIgnoreOverCapacity(..) method to change the strategy to adopt when capacity limit is reached. * \param bufferSize * Size of the buffer (in bytes) to store temporary data that will be transferred through the DMA queue (0 = default size = 8192 on NTSC and 14336 on PAL).
* The buffer should be big enough to contains all temporary that you need to store before next #DMA_flushQueue() call. * * SGDK automatically call this method on hard reset so you don't need to call it again unless * you want to change the default parameters. * * \see #DMA_setIgnoreOverCapacity(..) */ void DMA_initEx(u16 size, u16 capacity, u16 bufferSize); /** * \brief * Returns TRUE if the DMA_flushQueue() method is automatically called at VBlank to process DMA operations * pending in the queue. * * \see DMA_setAutoFlush() * \see DMA_flushQueue() */ bool DMA_getAutoFlush(void); /** * \brief * If set to TRUE (default) then the DMA_flushQueue() method is automatically called at VBlank * to process DMA operations pending in the queue otherwise you have to call the DMA_flushQueue() * method by yourself. * * \see DMA_flushQueue() */ void DMA_setAutoFlush(bool value); /** * \brief * Returns the maximum allowed number of pending transfer in the queue (allocated queue size). * * \see DMA_setMaxQueueSize() */ u16 DMA_getMaxQueueSize(void); /** * \brief * Sets the maximum allowed number of pending transfer in the queue (allocated queue size).
* WARNING: changing the queue size will clear the DMA queue. * * \param value * The queue size (minimum allowed size = 20) * * \see DMA_getMaxQueueSize() * \see DMA_setMaxQueueSizeToDefault() */ void DMA_setMaxQueueSize(u16 value); /** * \brief * Sets the maximum allowed number of pending transfer in the queue (allocated queue size) to default value (64).
* WARNING: changing the queue size will clear the DMA queue. * * \see DMA_setMaxQueueSize(..) */ void DMA_setMaxQueueSizeToDefault(void); /** * \brief * Returns the maximum allowed size (in bytes) to transfer per #DMA_flushQueue() call.
* A value of 0 means there is no DMA limit. * * \see DMA_setMaxTransferSize(..) */ u16 DMA_getMaxTransferSize(void); /** * \brief * Sets the maximum amount of data (in bytes) to transfer per #DMA_flushQueue() call.
* VBlank period allows to transfer up to 7.2 KB on NTSC system and 15 KB on PAL system.
* By default there is no size limit (0) * * \param value * The maximum amount of data (in bytes) to transfer during DMA_flushQueue() operation.
* Use 0 for no limit. * * \see DMA_flushQueue(void) */ void DMA_setMaxTransferSize(u16 value); /** * \brief * Sets the maximum amount of data (in bytes) to default value (7.2 KB on NTSC system and 15 KB on PAL system). * * \see DMA_setMaxTransferSize() */ void DMA_setMaxTransferSizeToDefault(void); /** * \brief * Returns the size (in bytes) of the temporary data buffer which can be used to store data * that will be transferred through the DMA queue. * * \see DMA_setBufferSize(void) */ u16 DMA_getBufferSize(void); /** * \brief * Sets the size (in bytes) of the temporary data buffer which can be used to store data * that will be transferred through the DMA queue.
* WARNING: changing the buffer size will clear the DMA queue. * * \param value * The size of the temporary data buffer (in bytes).
* Minimum allowed buffer size if 2048 (internals methods require a minimal buffer size) * * \see DMA_getBufferSize(void) * \see DMA_setBufferSizeToDefault(void) */ void DMA_setBufferSize(u16 value); /** * \brief * Sets the size (in bytes) of the temporary data buffer to default value (8 KB on NTSC system and 14 KB on PAL system). * * \see DMA_setBufferSize(void) */ void DMA_setBufferSizeToDefault(void); /** * \brief * Return TRUE means that we ignore future DMA operation when we reach the maximum capacity (see #DMA_setIgnoreOverCapacity(..) method). * * \see DMA_setIgnoreOverCapacity(void) */ bool DMA_getIgnoreOverCapacity(void); /** * \brief * Set the "over capacity" DMA queue strategy (default is FALSE). * * When set to TRUE any DMA operation queued after we reached the maximum defined transfer capacity * with #DMA_setMaxTransferSize(..) are ignored.
* When set to FALSE all DMA operations are done even when we are over the maximum capacity (which can lead to important slowdown). * * \see DMA_setMaxTransferSize() */ void DMA_setIgnoreOverCapacity(bool value); /** * \brief * Clears the DMA queue (all queued operations are lost).
* \see DMA_flushQueue(void) */ void DMA_clearQueue(void); /** * \brief * Transfer the content of the DMA queue to the VDP:
* Each pending DMA operation is sent to the VDP and processed as quickly as possible.
* This method returns when all DMA operations present in the queue has been transferred (or when maximum capacity has been reached).
* Note that this method is automatically called at VBlank time and you shouldn't call yourself except if * you want to process it before vblank (if you manually extend blank period with h-int for instance) in which case * you can disable the auto flush feature (see DMA_setAutoFlush(...)) * * \see DMA_queue(...) * \see DMA_setAutoFlush(...) */ void DMA_flushQueue(void); /** * \brief * Returns the number of transfer currently pending in the DMA queue. */ u16 DMA_getQueueSize(void); /** * \brief * Returns the size (in byte) of data to be transferred currently present in the DMA queue.
* NTSC frame allows about 7.6 KB of data to be transferred during VBlank (in H40) while * PAL frame allows about 17 KB (in H40). */ u16 DMA_getQueueTransferSize(void); /** * \brief * Tool method allowing to allocate memory from the DMA temporary buffer if you need a very temporary buffer.
* Don't forget to release memory using #DMA_releaseTemp(..).
* WARNING: it's very important to disable interrupts while using the temporary DMA buffer as DMA buffer can be flushed on interrupt. * * \param len * Number of word to allocate. * \return * The source buffer pointer if allocation succeeded,.
* Returns NULL if the buffer is full (or not big enough). * \see DMA_releaseTemp(..) */ void* DMA_allocateTemp(u16 len); /** * \brief * Tool method allowing to release memory previously allocated using #DMA_allocateTemp(..).
* WARNING: it's very important to disable interrupts while using the temporary DMA buffer as DMA buffer can be flushed on interrupt. * * \param len * Number of word to release. * \see DMA_allocateTemp(..) */ void DMA_releaseTemp(u16 len); /** * \brief * General method to transfer data to VDP memory. * * \param tm * Transfer method.
* Accepted values are:
* - CPU
* - DMA
* - DMA_QUEUE
* - DMA_QUEUE_COPY
* \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param from * Source buffer. * \param to * VRAM/CRAM/VSRAM destination address. * \param len * Number of word to allocate and transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (0 to 255).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation.
* \return * TRUE if the operation succeeded, FALSE otherwise (buffer or queue full). * \see DMA_queueDMA(..) */ bool DMA_transfer(TransferMethod tm, u8 location, void* from, u16 to, u16 len, u16 step); /** * \brief * Return TRUE if we have enough DMA capacity to transfer the given data block len (see #DMA_setMaxTransferSize(..)) * * \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param len * Number of word we want to transfer. * \return * TRUE if we have enough capacity, FALSE otherwise * \see DMA_getMaxTransferSize(..) * \see DMA_queueDma(..) */ bool DMA_canQueue(u8 location, u16 len); /** * \brief * Allocate temporary memory and queues the DMA transfer operation in the DMA queue.
* The idea of the DMA queue is to burst all DMA operations during VBLank to maximize bandwidth usage.
* IMPORTANT: You need to fill the returned data buffer before DMA occurs so it's a good practise to disable interrupts before * calling this method and re-enable them *after* you filled the buffer to avoid DMA queue flush operation happening in between. * * \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param to * VRAM/CRAM/VSRAM destination address. * \param len * Number of word to allocate and transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (0 to 255).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation.
* \return * The source buffer pointer that will be used for the DMA transfer so you can fill its content.
* Returns NULL if the buffer is full or if the DMA queue operation failed (queue is full). * \see DMA_queueDMA(..) * \see DMA_copyAndQueueDma(..) */ void* DMA_allocateAndQueueDma(u8 location, u16 to, u16 len, u16 step); /** * \brief * Same as #DMA_queueDma(..) method except that it first copies the data to transfer through DMA queue into a temporary buffer.
* This is useful when you know that source data may be modified before DMA acutally occurs and want to avoid that. * * \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param from * Source buffer. * \param to * VRAM/CRAM/VSRAM destination address. * \param len * Number of word to transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (0 to 255).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation.
* \return * FALSE if the operation failed (queue is full) * \see DMA_doDma(..) * \see DMA_queueDma(..) */ bool DMA_copyAndQueueDma(u8 location, void* from, u16 to, u16 len, u16 step); /** * \brief * Queues the specified DMA transfer operation in the DMA queue.
* The idea of the DMA queue is to burst all DMA operations during VBLank to maximize bandwidth usage.
* * \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param from * Source buffer address. * \param to * VRAM/CRAM/VSRAM destination address. * \param len * Number of word to transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (0 to 255).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation.
* \return * FALSE if the operation failed (queue is full) * \see DMA_doDma(..) */ bool DMA_queueDma(u8 location, void* from, u16 to, u16 len, u16 step); /** * \brief * Same as #DMA_queueDma(..) method except if doesn't check for 128 KB bank crossing on source * \see #DMA_queueDma(..) */ bool DMA_queueDmaFast(u8 location, void* from, u16 to, u16 len, u16 step); /** * \brief * Do DMA transfer operation immediately * * \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param from * Source buffer address. * \param to * VRAM/CRAM/VSRAM destination address. * \param len * Number of word to transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (-1 to keep current step).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation. * \see DMA_queue(..) */ void DMA_doDma(u8 location, void* from, u16 to, u16 len, s16 step); /** * \brief * Same as #DMA_doDma(..) method except if doesn't check for 128 KB bank crossing on source * \see #DMA_doDma(..) */ void DMA_doDmaFast(u8 location, void* from, u16 to, u16 len, s16 step); /** * \brief * Do software (CPU) copy to VDP memory (mainly for testing purpose as it's slower than using DMA) * * \param location * Destination location.
* Accepted values:
* - DMA_VRAM (for VRAM transfer).
* - DMA_CRAM (for CRAM transfer).
* - DMA_VSRAM (for VSRAM transfer).
* \param from * Source buffer. * \param to * VRAM/CRAM/VSRAM destination address. * \param len * Number of word to transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (-1 to keep current step).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation. */ void DMA_doCPUCopy(u8 location, void* from, u16 to, u16 len, s16 step); /** * \brief * Do software (CPU) copy to VDP memory (mainly for testing purpose as it's slower than using DMA) * * \param cmd * VDP packed control command (contains operation and destination address). * \param from * Source buffer. * \param len * Number of word to transfer. * \param step * destination (VRAM/VSRAM/CRAM) address increment step after each write (-1 to keep current step).
* By default you should set it to 2 for normal copy operation but you can use different value * for specific operation. */ void DMA_doCPUCopyDirect(u32 cmd, void* from, u16 len, s16 step); /** * \brief * Do VRAM DMA fill operation. * * \param to * VRAM destination address. * \param len * Number of byte to fill (minimum is 2 for even addr destination and 3 for odd addr destination).
* A value of 0 mean 0x10000. * \param value * Fill value (byte). * \param step * VRAM address increment step after each write (-1 to keep current step).
* should be 1 for a classic fill operation but you can use different value for specific operation. */ void DMA_doVRamFill(u16 to, u16 len, u8 value, s16 step); /** * \brief * Do VRAM DMA copy operation. * \param from * VRAM Source address. * \param to * VRAM destination address. * \param len * Number of byte to copy. * \param step * VRAM address increment step after each write (-1 to keep current step).
* should be 1 for a classic copy operation but you can use different value for specific operation. */ void DMA_doVRamCopy(u16 from, u16 to, u16 len, s16 step); /** * \brief * Wait current DMA fill/copy operation to complete (same as #VDP_waitDMACompletion()) */ void DMA_waitCompletion(void); #endif // _DMA_H_