/**
* \file sys.h
* \brief Entry point unit / Interrupt callback / System
* \author Stephane Dallongeville
* \date 08/2011
*
* This unit contains SGDK initialization / reset methods, IRQ callbacks and others system stuff.
*/
#ifndef _SYS_H_
#define _SYS_H_
#define PROCESS_PALETTE_FADING (1 << 0)
#define PROCESS_BITMAP_TASK (1 << 1)
#define PROCESS_DMA_TASK (1 << 2)
#define PROCESS_VDP_SCROLL_TASK (1 << 3)
#define ROM_ALIGN_BIT 17
#define ROM_ALIGN (1 << ROM_ALIGN_BIT)
#define ROM_ALIGN_MASK (ROM_ALIGN - 1)
#define ROM_START ROM
#define ROM_END (((u32) &_stext) + ((u32) &_sdata))
#define ROM_SIZE ((ROM_END + ROM_ALIGN_MASK) & (~ROM_ALIGN_MASK))
/**
* \brief
* To force method inlining (not sure that GCC does actually care of it)
*/
#define FORCE_INLINE inline __attribute__((always_inline))
/**
* \brief
* To force no inlining for this method
*/
#define NO_INLINE __attribute__((noinline))
/**
* \brief
* Put function in .data (RAM) instead of the default .text
*/
#define RAM_SECT __attribute__((section(".ramprog")))
/**
* \brief
* Declare function for the hint callback (generate a RTE to return from interrupt instead of RTS)
*/
#define HINTERRUPT_CALLBACK __attribute__((interrupt)) void
/**
* \brief
* Macro for packing structures and enumerates
*/
#define PACKED __attribute__((__packed__))
// exist through rom_head.c
typedef struct
{
char console[16]; /* Console Name (16) */
char copyright[16]; /* Copyright Information (16) */
char title_local[48]; /* Domestic Name (48) */
char title_int[48]; /* Overseas Name (48) */
char serial[14]; /* Serial Number (2, 12) */
u16 checksum; /* Checksum (2) */
char IOSupport[16]; /* I/O Support (16) */
u32 rom_start; /* ROM Start Address (4) */
u32 rom_end; /* ROM End Address (4) */
u32 ram_start; /* Start of Backup RAM (4) */
u32 ram_end; /* End of Backup RAM (4) */
char sram_sig[2]; /* "RA" for save ram (2) */
u16 sram_type; /* 0xF820 for save ram on odd bytes (2) */
u32 sram_start; /* SRAM start address - normally 0x200001 (4) */
u32 sram_end; /* SRAM end address - start + 2*sram_size (4) */
char modem_support[12]; /* Modem Support (24) */
char notes[40]; /* Memo (40) */
char region[16]; /* Country Support (16) */
} ROMHeader;
extern const ROMHeader rom_header;
// size of text segment --> start of initialized data (RO)
extern u32 _stext;
// size of initialized data segment
extern u32 _sdata;
/**
* \brief
* Define at which period to do VBlank process (see #SYS_doVBlankProcess() method)
*/
typedef enum
{
IMMEDIATELY, /** Start VBlank process immediately whatever we are in blanking period or not */
ON_VBLANK , /** Start VBlank process on VBlank period, start immediatly in we are already in VBlank */
ON_VBLANK_START /** Start VBlank process on VBlank *start* period, means that we wait the next *start* of VBlank period if we missed it */
} VBlankProcessTime;
#if LEGACY_ERROR_HANDLER
/**
* \brief
* Bus error interrupt callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *busErrorCB;
/**
* \brief
* Address error interrupt callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *addressErrorCB;
/**
* \brief
* Illegal instruction exception callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *illegalInstCB;
/**
* \brief
* Division by zero exception callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *zeroDivideCB;
/**
* \brief
* CHK instruction interrupt callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *chkInstCB;
/**
* \brief
* TRAPV instruction interrupt callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *trapvInstCB;
/**
* \brief
* Privilege violation exception callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *privilegeViolationCB;
/**
* \brief
* Trace interrupt callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *traceCB;
/**
* \brief
* Line 1x1x exception callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *line1x1xCB;
/**
* \brief
* Error exception callback.
*
* You can modify it to use your own callback (for debug purpose).
*/
extern VoidCallback *errorExceptionCB;
#endif
/**
* \brief
* Level interrupt callback.
*
* You can modify it to use your own callback.
*/
extern VoidCallback *intCB;
/**
* \brief
* Assert reset
*
* Assert reset pin on the 68000 CPU.
* This is needed to reset some attached hardware.
*/
void SYS_assertReset(void);
/**
* \brief
* Soft reset
*
* Software reset
*/
void SYS_reset(void);
/**
* \brief
* Hard reset
*
* Reset with forced hardware init and memory clear / reset operation.
*/
void SYS_hardReset(void);
/**
* \brief
* Wait for start of VBlank and do all the VBlank processing (DMA transfers, XGM driver tempo, Joypad pooling..)
* \return FALSE if process was canceled because the method was called from V-Int (vertical interrupt) callback
* in which case we exit the function as V-Int will be triggered immediately.
*
* Do all the SGDK VBlank process.
* Some specific processing should be done during the Vertical Blank period as the VDP is idle at this time.
* This is always where we should do all VDP data transfer (using the DMA preferably) but we can also do the processes which
* has to be done at a frame basis (joypad polling, sound driver sync/update..)
* In the case of SGDK, calling this method will actually do the following tasks:
* - flush the DMA queue
* - process asynchronous palette fading operation
* - joypad polling
*
* Note that VBlank process may be delayed to next VBlank if we missed the start of the VBlank period so that will cause a frame miss.
*/
bool SYS_doVBlankProcess(void);
/**
* \brief
* Do all the VBlank processing (DMA transfers, XGM driver tempo, Joypad pooling..)
* \param processTime
* Define at which period we start VBlank process, accepted values are:
* IMMEDIATELY Start VBlank process immediatly whatever we are in blanking period or not
* (*highly discouraged* unless you really know what you're doing !)
* ON_VBLANK Start VBlank process on VBlank period, if we already are in VBlank period
* it starts immediately (discouraged as VBlank period may be shortened and all
* processes cannot be completed in time)
* ON_VBLANK_START Start VBlank process on VBlank *start* period (recommanded as default value).
* That means that if #SYS_doVBlankProcess() is called too late (after the start
* of VBlank) then we force a passive wait for the next start of VBlank so we can
* align the processing with the beggining of VBlank period to ensure fast DMA
* transfert and avoid possible graphical glitches due to VRAM update during active display.
* \return FALSE if process was canceled because we forced Start VBlank process (time = ON_VBLANK_START)
* and the method was called from V-Int (vertical interrupt) callback in which case we exit the function
* as V-Int will be triggered immediately.
*
* Wait for Vblank and does all the SGDK VBlank process.
* Some specific processing should be done during the Vertical Blank period as the VDP is idle at this time.
* This is always where we should do all VDP data transfer (using the DMA preferably) but we can also do the processes which
* has to be done at a frame basis (joypad polling, sound driver sync/update..)
* In the case of SGDK, calling this method will actually do the following tasks:
* - flush the DMA queue
* - process asynchronous palette fading operation
* - joypad polling
*
* Note that depending the used time parameter, VBlank process may be delayed to next VBlank so that will wause a frame miss.
*/
bool SYS_doVBlankProcessEx(VBlankProcessTime processTime);
/**
* \brief
* End the current frame (alias for #SYS_doVBlankProcess(void)).
*
* End the current frame and does all the internal SGDK VBlank process (DMA flush, VDP data upload, async palette fade, scroll update..)
*
* \see SYS_doVBlankProcess(void)
*/
bool SYS_nextFrame(void);
/**
* \brief
* Returns the current value of the stack pointer register (A7)
*/
u32 SYS_getStackPointer();
/**
* \brief
* Return current interrupt mask level.
*
* See SYS_setInterruptMaskLevel() for more informations about interrupt mask level.
*/
u16 SYS_getInterruptMaskLevel(void);
/**
* \brief
* Set interrupt mask level.
*
* You can disable interrupt depending their level.
* Interrupt with level <= interrupt mask level are ignored.
* We have 3 different interrupts:
* Vertical interrupt (V-INT): level 6
* Horizontal interrupt (H-INT): level 4
* External interrupt (EX-INT): level 2
* Vertical interrupt has the highest level (and so priority) where external interrupt has lowest one.
* For instance to disable Vertical interrupt just use SYS_setInterruptMaskLevel(6).
*
* \see SYS_getInterruptMaskLevel()
* \see SYS_getAndSetInterruptMaskLevel()
* \see SYS_setVIntCallback()
* \see SYS_setHIntCallback()
*/
void SYS_setInterruptMaskLevel(u16 value);
/**
* \brief
* Set the interrupt mask level to given value and return previous level.
*
* You can disable interrupt depending their level.
* Interrupt with level <= interrupt mask level are ignored.
* We have 3 different interrupts:
* Vertical interrupt (V-INT): level 6
* Horizontal interrupt (H-INT): level 4
* External interrupt (EX-INT): level 2
* Vertical interrupt has the highest level (and so priority) where external interrupt has lowest one.
* For instance to disable Vertical interrupt just use SYS_setInterruptMaskLevel(6).
*
* \see SYS_getInterruptMaskLevel()
* \see SYS_setInterruptMaskLevel()
* \see SYS_setVIntCallback()
* \see SYS_setHIntCallback()
*/
u16 SYS_getAndSetInterruptMaskLevel(u16 value);
/**
* \brief
* Disable interrupts (Vertical, Horizontal and External).
*
*
* This method is used to temporary disable interrupts to protect some processes and should always be followed by SYS_enableInts().
* You need to protect against interrupts any processes than can be perturbed / corrupted by the interrupt callback code (IO ports access in general but not only).
* Now by default SGDK doesn't do anything armful in its interrupts handlers (except with the Bitmap engine) so it's not necessary to protect from interrupts by default
* but you may need it if your interrupts callback code does mess with VDP for instance.
* Note that you can nest #SYS_disableInts / #SYS_enableInts() calls.
*
* \see SYS_enableInts(void)
*/
void SYS_disableInts(void);
/**
* \brief
* Re-enable interrupts (Vertical, Horizontal and External).
*
* This method is used to reenable interrupts after a call to #SYS_disableInts().
* Note that you can nest #SYS_disableInts / #SYS_enableInts() calls.
*
* \see SYS_disableInts(void)
*/
void SYS_enableInts(void);
/**
* \brief
* Set user 'Vertical Blank' callback method.
*
* \param CB
* Pointer to the method to call on Vertical Blank period.
* You can remove current callback by passing a NULL pointer here.
*
* Vertical blank period starts right at the end of display period.
* This period is usually used to prepare next frame data (refresh sprites, scrolling ...).
* SGDK handle that in the #SYS_doVBlankProcess() method and will call the user 'Vertical Blank' from this method after all major tasks.
* It's recommended to use the 'Vertical Blank' callback instead of the 'VInt' callback if you need to do some VDP accesses.
*
* \see SYS_setVIntCallback(VoidCallback *CB);
*/
void SYS_setVBlankCallback(VoidCallback *CB);
/**
* \brief
* Set 'Vertical Interrupt' callback method, prefer #SYS_setVBlankCallback(..) when possible.
*
* \param CB
* Pointer to the method to call on Vertical Interrupt.
* You can remove current callback by passing a NULL pointer here.
*
* Vertical interrupt happen at the end of display period at the start of the vertical blank period.
* This period is usually used to prepare next frame data (refresh sprites, scrolling ...) though now
* SGDK handle most of these process using #SYS_doVBlankProcess() so you can control it manually (do it from main loop or put it in Vint callback).
* The only things that SGDK always handle from the vint callback is the XGM sound driver music tempo and Bitmap engine phase reset.
* It's recommended to keep your code as fast as possible as it will eat precious VBlank time, nor you should touch the VDP from your Vint callback
* otherwise you will need to protect any VDP accesses from your main loop (which is painful), use the SYS_setVIntCallback(..) instead for that.
*
* \see SYS_setVBlankCallback(VoidCallback *CB);
* \see SYS_setHIntCallback(VoidCallback *CB);
*/
void SYS_setVIntCallback(VoidCallback *CB);
/**
* \brief
* Set 'Horizontal Interrupt' callback method (need to be prefixed by HINTERRUPT_CALLBACK).
*
* \param CB
* Pointer to the method to call on Horizontal Interrupt.
* You can remove current callback by passing a NULL pointer here.
* You need to prefix your hint method with HINTERRUPT_CALLBACK:
*
HINTERRUPT_CALLBACK myHIntFunction() * { * ... * }
*