/**
* \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_