nuclear@0: @******************************************************************** nuclear@0: @* crt0.S v1.28 by Jeff Frohwein * nuclear@0: @******************************************************************** nuclear@0: nuclear@0: @ v1.0 - Original release nuclear@0: @ v1.1 - Added proper .data section support nuclear@0: @ v1.2 - Added support for c++, overlays, interrupts, and nuclear@0: @ far calls (__FarFunction & __FarProcedure). nuclear@0: @ - Some ideas from Jason Wilkins & Mike Heckenbach. nuclear@0: @ v1.21- Killed the dumb test bug left in the code. nuclear@0: @ v1.22- Killed dumb bug "numero dos" in multiple interrupts routine. Thanks Mike H. :) nuclear@0: @ v1.23- Now correctly handles zero length .bss section. nuclear@0: @ v1.24- Loop back to start_vector now works if main {} exits. nuclear@0: @ v1.25- __FarProcedure now works. It was missing a .thumb_func directive. nuclear@0: @ v1.26- Added missing Serial Interrupt processing to __MultipleInterrupts section. nuclear@0: @ Added __FastInterrupt option for minimal interrupt processing. nuclear@0: @ Optimized __MultipleInterrupts section to save 4 bytes of stack space. nuclear@0: @ Added __ISRinIWRAM option that puts interrupt processing in IWRAM by default. nuclear@0: @ Options passed to main() or AgbMain() are now set to 0. (Thanks to DarkFader) nuclear@0: @ v1.27- Even though it might not cause any problems for anyone "as is", nuclear@0: @ changed .SECTION .iwram to .SECTION .iwram,"ax",%progbits nuclear@0: @ just to be safe. That is the more correct description/definition. nuclear@0: @ Added warning below about small default interrupt stack. nuclear@0: @ v1.28- Added force alignment (align 4) to CopyMem & ClearMem to nuclear@0: @ prevent infinite loops in cases where LD (buggy?) fails nuclear@0: @ to align(4). (Thanks to Mark Price & others.) nuclear@0: @ nuclear@0: @ This file is released into the public domain for commercial nuclear@0: @ or non-commercial usage with no restrictions placed upon it. nuclear@0: nuclear@0: .TEXT nuclear@0: nuclear@0: @ Comment out the next line ONLY if you plan to never support nuclear@0: @ multiboot mode and want to save a few bytes by removing nuclear@0: @ multiboot support code. Otherwise, leave it alone. It wont nuclear@0: @ disturb code designed to run only on flash carts. nuclear@0: @ nuclear@0: @ The normal way to enable generating code that works with nuclear@0: @ both multiboot and flash carts is to add the following to nuclear@0: @ your C code in your main project file AS A GLOBAL VARIABLE: nuclear@0: @ nuclear@0: @ #define MULTIBOOT int __gba_multiboot; nuclear@0: @ Then use it like this : MULTIBOOT nuclear@0: @ nuclear@0: @ IT MUST BE A GLOBAL VARIABLE OR IT WILL NOT WORK! nuclear@0: @ If this variable is not defined somewhere in your project nuclear@0: @ then code will be generated to run out of ROM instead of nuclear@0: @ EXRAM. The value of this variable is not important. nuclear@0: nuclear@0: .equ __MultiBootInclude, 1 nuclear@0: nuclear@0: @ If you are compiling for multiboot dedicated (will not nuclear@0: @ run in a cart) code then uncomment the following. Normally nuclear@0: @ you should leave this commented out so that a multiboot nuclear@0: @ image will run in a cart as well (by copying from ROM to RAM). nuclear@0: @ nuclear@0: @ This sets the maker code to "MB " which is a key that nuclear@0: @ some emulators look for to know to load the rom image nuclear@0: @ at 0x2000000 instead of the standard 0x8000000. nuclear@0: nuclear@0: @ .equ __MultibootDedicated, 1 nuclear@0: nuclear@0: @ There are two methods for clearing memory and nuclear@0: @ copying appropriate setup data. The fast & bulky nuclear@0: @ method is GBA DMA copy/clear but some emulators nuclear@0: @ do not accurately do DMA. If you have an inaccurate nuclear@0: @ emulator or want to conserve ROM space then comment nuclear@0: @ out the following line. There is not much advantage nuclear@0: @ gained by doing DMA copy/clear. nuclear@0: nuclear@0: @ .equ __DMACopyClear, 1 nuclear@0: nuclear@0: @ Uncomment the following line to support C++ development. nuclear@0: @ You also need to name your main C function the following: nuclear@0: @ int main (void) ...instead of... int AgbMain (void) nuclear@0: @ Doing so will cause ~5500 bytes of c++ support code to be nuclear@0: @ linked in with your project so do not enable c++ support nuclear@0: @ unless you plan to use it. nuclear@0: nuclear@0: .equ __CPPSupport, 1 nuclear@0: nuclear@0: @ Comment out the following line to disable interrupt support nuclear@0: @ in your code and to save some space in this file. nuclear@0: nuclear@0: @ .equ __InterruptSupport, 1 nuclear@0: nuclear@0: nuclear@0: @ Comment out the following line to put interrupt support in nuclear@0: @ ROM instead of IWRAM. Interrupt support in ROM will slow nuclear@0: @ down interrupt execution and has no advantage other than nuclear@0: @ saving a little bit of IWRAM. nuclear@0: nuclear@0: .equ __ISRinIWRAM, 1 nuclear@0: nuclear@0: @ NOTE: Only ONE of the following 3 interrupt options may be nuclear@0: @ uncommented. Also, __InterruptSupport above must be uncommented nuclear@0: @ for any of the following to have an effect. nuclear@0: @ If __InterruptSupport is uncommented then you MUST select ONE nuclear@0: @ AND ONLY ONE of the following 3 interrupt options. nuclear@0: @ nuclear@0: @ __FastInterrupts nuclear@0: @ Uncomment this line for minimal interrupt processing. nuclear@0: @ This allows 160 bytes of interrupt stack space with the nuclear@0: @ default lnkscript. nuclear@0: @ nuclear@0: @ __SingleInterrupts nuclear@0: @ Uncomment this line if you wish to use a table of function nuclear@0: @ pointers to process specific interrupts. This allows 160 nuclear@0: @ bytes of interrupt stack space with the default lnkscript. nuclear@0: @ nuclear@0: @ __MultipleInterrupts nuclear@0: @ Uncomment this line to allow multiple-interrupts-at-once nuclear@0: @ support. If you have several interrupts where one can nuclear@0: @ occur while another is being serviced then you need to nuclear@0: @ enable this option. This option uses the main stack instead nuclear@0: @ of the interrupt stack so you have access to a larger stack. nuclear@0: nuclear@0: .equ __FastInterrupts, 1 nuclear@0: @ .equ __SingleInterrupts, 1 nuclear@0: @ .equ __MultipleInterrupts, 1 nuclear@0: nuclear@0: nuclear@0: @ Uncomment the following line to disable sound and enter an nuclear@0: @ infinite loop if cart is removed during game play. You nuclear@0: @ must have the cart interrupt enabled for this to work and nuclear@0: @ __ISRinIWRAM, above, must be enabled (not commented out.) nuclear@0: nuclear@0: @ .equ __HandleCartInterrupt, 1 nuclear@0: nuclear@0: @ The following prevents IRQ stack overflow by switching to nuclear@0: @ System mode (User stack) when handling multiple interrupts. nuclear@0: @ To force use of IRQ stack only, comment out the following line. nuclear@0: nuclear@0: .equ __SwitchToUserStack, 1 nuclear@0: nuclear@0: @ !!!! NOTE: THE COPY ROUTINES IN THIS FILE WORK ON 4 BYTE nuclear@0: @ BOUNDARIES. YOUR LINKER SCRIPT MUST ALIGN SECTION STARTS nuclear@0: @ AND SECTION ENDS FOR SECTIONS THAT GET COPIED TO RAM WITH nuclear@0: @ ALIGN(4) !!!! nuclear@0: nuclear@0: .GLOBAL _start nuclear@0: _start: nuclear@0: .ALIGN nuclear@0: .CODE 32 nuclear@0: @ Start Vector nuclear@0: nuclear@0: b rom_header_end nuclear@0: nuclear@0: @ Nintendo Logo Character Data (8000004h) nuclear@0: @ .fill 156,1,0 nuclear@0: .long 0x51aeff24,0x21a29a69,0x0a82843d nuclear@0: .long 0xad09e484,0x988b2411,0x217f81c0,0x19be52a3 nuclear@0: .long 0x20ce0993,0x4a4a4610,0xec3127f8,0x33e8c758 nuclear@0: .long 0xbfcee382,0x94dff485,0xc1094bce,0xc08a5694 nuclear@0: .long 0xfca77213,0x734d849f,0x619acaa3,0x27a39758 nuclear@0: .long 0x769803fc,0x61c71d23,0x56ae0403,0x008438bf nuclear@0: .long 0xfd0ea740,0x03fe52ff,0xf130956f,0x85c0fb97 nuclear@0: .long 0x2580d660,0x03be63a9,0xe2384e01,0xff34a2f9 nuclear@0: .long 0x44033ebb,0xcb900078,0x943a1188,0x637cc065 nuclear@0: .long 0xaf3cf087,0x8be425d6,0x72ac0a38,0x07f8d421 nuclear@0: nuclear@0: @ Game Title (80000A0h) nuclear@0: .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 nuclear@0: .byte 0x00,0x00,0x00,0x00 nuclear@0: nuclear@0: .ifdef __MultibootDedicated nuclear@0: @ Game Code (80000ACh) nuclear@0: .ascii "MB " nuclear@0: .else nuclear@0: @ Game Code (80000ACh) nuclear@0: .byte 0x00,0x00,0x00,0x00 nuclear@0: .endif nuclear@0: nuclear@0: @ Maker Code (80000B0h) nuclear@0: .byte 0x30,0x31 nuclear@0: nuclear@0: @ Fixed Value (80000B2h) nuclear@0: .byte 0x96 nuclear@0: nuclear@0: @ Main Unit Code (80000B3h) nuclear@0: .byte 0x00 nuclear@0: nuclear@0: @ Device Type (80000B4h) nuclear@0: .byte 0x00 nuclear@0: nuclear@0: @ Unused Data (7Byte) (80000B5h) nuclear@0: .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00 nuclear@0: nuclear@0: @ Software Version No (80000BCh) nuclear@0: .byte 0x00 nuclear@0: nuclear@0: @ Complement Check (80000BDh) nuclear@0: .byte 0xf0 nuclear@0: nuclear@0: @ Checksum (80000BEh) nuclear@0: .byte 0x00,0x00 nuclear@0: nuclear@0: .ALIGN nuclear@0: .ARM @ ..or you can use CODE 32 here nuclear@0: nuclear@0: rom_header_end: nuclear@0: b start_vector @ This branch must be here for proper nuclear@0: @ positioning of the following header. nuclear@0: @ DO NOT REMOVE IT. nuclear@0: nuclear@0: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ nuclear@0: @ The following reserved bytes are used if the code is compiled for @ nuclear@0: @ multiboot mode. It does not hurt anything to leave this header in nuclear@0: @ even if the code is not compiled for multiboot. The GBA BIOS will nuclear@0: @ auto-patch the first two bytes with 0x03 and 0x01, respectively, nuclear@0: @ before running any code if it is executed as multiboot. nuclear@0: @ nuclear@0: nuclear@0: @ The following two bytes are included even for non-multiboot supporting nuclear@0: @ builds to guarantee that any generic library code that depends on them nuclear@0: @ will still be functional. nuclear@0: nuclear@0: .GLOBAL __boot_method, __slave_number nuclear@0: nuclear@0: __boot_method: nuclear@0: .byte 0 @ boot method (0=ROM boot, 3=Multiplay boot) nuclear@0: __slave_number: nuclear@0: .byte 0 @ slave # (1=slave#1, 2=slave#2, 3=slave#3) nuclear@0: nuclear@0: .ifdef __MultiBootInclude nuclear@0: nuclear@0: .byte 0 @ reserved nuclear@0: .byte 0 @ reserved nuclear@0: .word 0 @ reserved nuclear@0: .word 0 @ reserved nuclear@0: .word 0 @ reserved nuclear@0: .word 0 @ reserved nuclear@0: .word 0 @ reserved nuclear@0: .word 0 @ reserved nuclear@0: .endif nuclear@0: @ @ nuclear@0: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ nuclear@0: nuclear@0: nuclear@0: @@@@@@@@@@@@@@@@@@@@@@ nuclear@0: @ Reset @ nuclear@0: @@@@@@@@@@@@@@@@@@@@@@ nuclear@0: nuclear@0: .GLOBAL start_vector nuclear@0: .ALIGN nuclear@0: .ARM @ ..or you can use CODE 32 here nuclear@0: start_vector: nuclear@0: mov r0, #0x12 @ Switch to IRQ Mode nuclear@0: msr cpsr, r0 nuclear@0: ldr sp,=__sp_irq @ Set SP_irq nuclear@0: mov r0, #0x1f @ Switch to System Mode nuclear@0: msr cpsr, r0 nuclear@0: ldr sp,=__sp_usr @ Set SP_usr nuclear@0: nuclear@0: @ Enter Thumb mode nuclear@0: adr r0,1f + 1 @ add r0,pc,#1 also works here nuclear@0: @ for those that want to conserve labels. nuclear@0: bx r0 nuclear@0: nuclear@0: .THUMB @ ..or you can use .CODE 16 here nuclear@0: 1: nuclear@0: nuclear@0: .ifdef __InterruptSupport nuclear@0: ldr r1, =__intr_vector_buf @ Set Interrupt Address nuclear@0: ldr r0, =intr_main nuclear@0: str r0, [r1] nuclear@0: .endif nuclear@0: nuclear@0: .ifdef __MultiBootInclude nuclear@0: nuclear@0: @ *** Multiboot Copy Routine *** nuclear@0: @ Check the Program Counter to see if code is running nuclear@0: @ at 0x2000000 or 0x8000000. If it is running at 0x8000000 nuclear@0: @ then copy 256K bytes of it to 0x2000000 and then branch nuclear@0: @ to 0x2000000. nuclear@0: @ The reason for all this is to allow a program to be used nuclear@0: @ "as is" with an flash cart/emulator or with an MBV2-style nuclear@0: @ multiboot cable. nuclear@0: @ NOTE: You can also detect if this ROM is running from nuclear@0: @ 0x2000000 by checking the multiboot header above. nuclear@0: nuclear@0: ldr r0,=__text_start nuclear@0: lsl r0,#5 @ Was code compiled at 0x08000000 or higher? nuclear@0: bcs DoEWRAMClear @ yes, you can not run it in external WRAM nuclear@0: nuclear@0: @ Make sure we are in ExWRAM nuclear@0: nuclear@0: mov r0,pc nuclear@0: lsl r0,#5 @ Are we running from ROM (0x8000000 or higher) ? nuclear@0: bcc SkipEWRAMClear @ No, so no need to do a copy. nuclear@0: nuclear@0: @ We were started in ROM, silly emulators. :P nuclear@0: @ So we need to copy to ExWRAM. nuclear@0: nuclear@0: mov r3,#0x40 nuclear@0: lsl r3,#12 @ r3 = 0x40000 nuclear@0: lsl r2,r3,#7 @ r2 = 0x2000000 nuclear@0: mov r6,r2 @ r6 = 0x2000000 nuclear@0: lsl r1,r2,#2 @ r1 = 0x8000000 nuclear@0: nuclear@0: bl CopyMem nuclear@0: nuclear@0: @ Jump to the code to execute nuclear@0: nuclear@0: bx r6 nuclear@0: .endif nuclear@0: nuclear@0: DoEWRAMClear: nuclear@0: @ Clear External WRAM to 0x00 nuclear@0: nuclear@0: mov r1,#0x40 nuclear@0: lsl r1,#12 @ r1 = 0x40000 nuclear@0: lsl r0,r1,#7 @ r0 = 0x2000000 nuclear@0: bl ClearMem nuclear@0: nuclear@0: SkipEWRAMClear: nuclear@0: @ ldr r0,=AgbMain nuclear@0: @ bx r0 nuclear@0: nuclear@0: @ Clear Internal WRAM to 0x00 nuclear@0: mov r0,#3 nuclear@0: lsl r0,#24 @ r0 = 0x3000000 nuclear@0: ldr r1,=__sp_usr_offset - 16 nuclear@0: bl ClearMem nuclear@0: nuclear@0: .ifdef __MultiBootInclude nuclear@0: @ Clear BSS section to 0x00 nuclear@0: @ (Sometimes BSS may be in External WRAM) nuclear@0: ldr r0,=__bss_start nuclear@0: ldr r1,=__bss_end nuclear@0: sub r1,r0 nuclear@0: bl ClearMem nuclear@0: .endif nuclear@0: nuclear@0: @ Copy initialized data (data section) from LMA to VMA (ROM to RAM) nuclear@0: ldr r1,=__data_lma nuclear@0: ldr r2,=__data_start nuclear@0: ldr r4,=__data_end nuclear@0: bl CopyMemChk nuclear@0: nuclear@0: @ Copy internal work ram (iwram section) from LMA to VMA (ROM to RAM) nuclear@0: ldr r1,=__iwram_lma nuclear@0: ldr r2,=__iwram_start nuclear@0: ldr r4,=__iwram_end nuclear@0: bl CopyMemChk nuclear@0: nuclear@0: @ Copy internal work ram overlay 0 (iwram0 section) from LMA to VMA (ROM to RAM) nuclear@0: ldr r2,=__load_stop_iwram0 nuclear@0: ldr r1,=__load_start_iwram0 nuclear@0: sub r3,r2,r1 @ Is there any data to copy? nuclear@0: beq CIW0Skip @ no nuclear@0: nuclear@0: ldr r2,=__iwram_overlay_start nuclear@0: bl CopyMem nuclear@0: CIW0Skip: nuclear@0: nuclear@0: @ Copy external work ram (ewram section) from LMA to VMA (ROM to RAM) nuclear@0: ldr r1,=__ewram_lma nuclear@0: ldr r2,=__ewram_start nuclear@0: ldr r4,=__ewram_end nuclear@0: bl CopyMemChk nuclear@0: nuclear@0: @ Copy external work ram overlay 0 (ewram0 section) from LMA to VMA (ROM to RAM) nuclear@0: ldr r2,=__load_stop_ewram0 nuclear@0: ldr r1,=__load_start_ewram0 nuclear@0: sub r3,r2,r1 @ Is there any data to copy? nuclear@0: beq CEW0Skip @ no nuclear@0: nuclear@0: ldr r2,=__ewram_overlay_start nuclear@0: bl CopyMem nuclear@0: CEW0Skip: nuclear@0: nuclear@0: @ Jump to user code nuclear@0: nuclear@0: mov r0,#0 @ int argc nuclear@0: mov r1,#0 @ char *argv[] nuclear@0: nuclear@0: ldr r3,=start_vector nuclear@0: mov lr,r3 @ Set start_vector as return address nuclear@0: nuclear@0: .ifdef __CPPSupport nuclear@0: ldr r3,=main nuclear@0: .else nuclear@0: ldr r3,=AgbMain nuclear@0: .endif nuclear@0: bx r3 nuclear@0: nuclear@0: nuclear@0: .GLOBAL __FarFunction,__FarProcedure nuclear@0: .THUMB_FUNC nuclear@0: __FarFunction: nuclear@0: .THUMB_FUNC nuclear@0: __FarProcedure: nuclear@0: bx r0 nuclear@0: nop nuclear@0: nop @ This nop is here to allow unmapped memory to be used as nuclear@0: @ as a delay of almost 1 sec with a 1 cycle resolution. nuclear@0: @ Read this for technical info: nuclear@0: @ http://www.devrs.com/gba/files/gbadevfaqs.php#RepeatUses nuclear@0: nuclear@0: @ Clear memory to 0x00 if length != 0 nuclear@0: @ r0 = Start Address nuclear@0: @ r1 = Length nuclear@0: nuclear@0: ClearMem: nuclear@0: @ cmp r1,#0 @ Is length zero? nuclear@0: @ beq ClearMX @ yes, exit nuclear@0: nuclear@0: mov r2,#3 @ These commands are used in cases where nuclear@0: add r1,r2 @ the length is not a multiple of 4, nuclear@0: bic r1,r2 @ even though it should be. nuclear@0: nuclear@0: beq ClearMX @ Length is zero so exit nuclear@0: nuclear@0: .ifdef __DMACopyClear nuclear@0: ldr r2,reg_base nuclear@0: lsr r1,#2 @ r1 = (length/4) & 0xffff nuclear@0: nuclear@0: adr r3,fill_val nuclear@0: str r3,[r2,#0x4] @ Set source address (fill value) nuclear@0: str r0,[r2,#0x8] @ Set destination address (fill dest address) nuclear@0: strh r1,[r2,#0xc] @ Set DMA length nuclear@0: ldr r1,=0x8500 @ dma_clrb nuclear@0: strh r1,[r2,#0xe] @ Start DMA nuclear@0: .else nuclear@0: mov r2,#0 nuclear@0: ClrLoop: nuclear@0: stmia r0!,{r2} nuclear@0: sub r1,#4 nuclear@0: bne ClrLoop nuclear@0: .endif nuclear@0: ClearMX: nuclear@0: bx lr nuclear@0: nuclear@0: @ Copy memory if length != 0 nuclear@0: @ r1 = Source Address nuclear@0: @ r2 = Dest Address nuclear@0: @ r4 = Dest Address + Length nuclear@0: nuclear@0: CopyMemChk: nuclear@0: sub r3,r4,r2 @ Is there any data to copy? nuclear@0: @ beq CIDExit @ no nuclear@0: nuclear@0: @ Copy memory nuclear@0: @ r1 = Source Address nuclear@0: @ r2 = Dest Address nuclear@0: @ r3 = Length nuclear@0: nuclear@0: CopyMem: nuclear@0: mov r0,#3 @ These commands are used in cases where nuclear@0: add r3,r0 @ the length is not a multiple of 4, nuclear@0: bic r3,r0 @ even though it should be. nuclear@0: nuclear@0: beq CIDExit @ Length is zero so exit nuclear@0: nuclear@0: .ifdef __DMACopyClear nuclear@0: ldr r0,reg_base nuclear@0: lsr r3,#2 @ r3 = (length/4) & 0xffff nuclear@0: nuclear@0: str r1,[r0,#0x4] @ Set source address nuclear@0: str r2,[r0,#0x8] @ Set destination address nuclear@0: strh r3,[r0,#0xc] @ Set DMA length nuclear@0: ldr r3,=0x8400 @ dma_copy nuclear@0: strh r3,[r0,#0xe] @ Start DMA nuclear@0: .else nuclear@0: CIDLoop: nuclear@0: ldmia r1!,{r0} nuclear@0: stmia r2!,{r0} nuclear@0: sub r3,#4 nuclear@0: bne CIDLoop nuclear@0: .endif nuclear@0: CIDExit: nuclear@0: If_Undefined_Reference__rename_main_or_AgbMain_to_each_other_in_your_C_file: nuclear@0: bx lr nuclear@0: nuclear@0: .ALIGN nuclear@0: nuclear@0: .ifdef __DMACopyClear nuclear@0: fill_val: .word 0 nuclear@0: reg_base: .word 0x040000d0 nuclear@0: .endif nuclear@0: nuclear@0: .ALIGN nuclear@0: .POOL nuclear@0: nuclear@0: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ nuclear@0: @ Interrupt Processing @ nuclear@0: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ nuclear@0: nuclear@0: .ifdef __ISRinIWRAM nuclear@0: .SECTION .iwram,"ax",%progbits nuclear@0: .endif nuclear@0: nuclear@0: .EXTERN IntrTable nuclear@0: .EXTERN InterruptProcess nuclear@0: .GLOBAL intr_main nuclear@0: .ALIGN nuclear@0: .ARM nuclear@0: nuclear@0: @ NOTE: Ifyou copy the following code (start: intr_main - nuclear@0: @ end: intr_main_end) to internal WRAM then do not forget nuclear@0: @ to copy everything between these two labels. The .POOL nuclear@0: @ data must be copied since it is used by intr_main. nuclear@0: nuclear@0: @ NOTE2: If __ISRinIWRAM is defined then the copy to nuclear@0: @ IWRAM is done automatically for you. nuclear@0: nuclear@0: .ifdef __InterruptSupport nuclear@0: nuclear@0: .ifdef __FastInterrupts nuclear@0: intr_main: nuclear@0: ldr r0,=InterruptProcess nuclear@0: bx r0 nuclear@0: .endif nuclear@0: nuclear@0: .ifdef __SingleInterrupts nuclear@0: intr_main: nuclear@0: @ Single interrupts support nuclear@0: mov r3, #0x4000000 @ REG_BASE nuclear@0: ldr r2, [r3,#0x200]! @ Read REG_IE nuclear@0: and r1, r2, r2, lsr #16 @ r1 = IE & IF nuclear@0: ldr r2, =IntrTable nuclear@0: nuclear@0: ands r0, r1, #1 @ V-Blank Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #2 @ H-Blank Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #4 @ V Counter Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #8 @ Timer 0 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x10 @ Timer 1 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x20 @ Timer 2 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x40 @ Timer 3 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x80 @ Serial Communication Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x100 @ DMA0 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x200 @ DMA1 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x400 @ DMA2 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x800 @ DMA3 Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x1000 @ Key Interrupt nuclear@0: bne jump_intr nuclear@0: add r2, r2, #4 nuclear@0: ands r0, r1, #0x2000 @ Cart Interrupt nuclear@0: nuclear@0: .ifdef __HandleCartInterrupt nuclear@0: strneb r0, [r3, #0x84 - 0x200] @ Stop sound if cart removed (REG_SOUNDCNT_X) nuclear@0: loop: bne loop @ Infinite loop if cart removed nuclear@0: .endif nuclear@0: nuclear@0: jump_intr: nuclear@0: strh r0, [r3, #2] @ IF Clear nuclear@0: ldr r0, [r2] @ Jump to user IRQ process nuclear@0: bx r0 nuclear@0: .endif nuclear@0: nuclear@0: .ifdef __MultipleInterrupts nuclear@0: intr_main: nuclear@0: @ Multiple interrupts support nuclear@0: mov r2, #0x4000000 @ REG_BASE nuclear@0: ldr r3, [r2,#0x200]! @ r2 = IE : r3 = IF|IE nuclear@0: ldrh r1, [r2, #0x8] @ r1 = IME nuclear@0: mrs r0, spsr nuclear@0: stmfd sp!, {r0-r2,lr} @ {spsr, IME, REG_IE, lr} // IF|IE nuclear@0: nuclear@0: mov r0, #1 @ IME = 1 (To permit multiple interrupts if nuclear@0: @ an interrupt occurs) nuclear@0: strh r0, [r2, #0x8] nuclear@0: and r1, r3, r3, lsr #16 @ r1 = IE & IF nuclear@0: ldr r12, =IntrTable nuclear@0: nuclear@0: ands r0, r1, #1 @ V-blank interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #2 @ H-blank interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #4 @ V-counter interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #8 @ Timer 0 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x10 @ Timer 1 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x20 @ Timer 2 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x40 @ Timer 3 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x80 @ Serial Communication Interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x100 @ DMA 0 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x200 @ DMA 1 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x400 @ DMA 2 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x800 @ DMA 3 interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x1000 @ Key interrupt nuclear@0: bne jump_intr nuclear@0: add r12,r12, #4 nuclear@0: ands r0, r1, #0x2000 @ Cart interrupt nuclear@0: nuclear@0: .ifdef __HandleCartInterrupt nuclear@0: strneb r0, [r2, #0x84 - 0x200] @ Stop sound if cart removed (REG_SOUNDCNT_X) nuclear@0: loop: bne loop @ Infinite loop if cart removed nuclear@0: .endif nuclear@0: nuclear@0: jump_intr: nuclear@0: strh r0, [r2, #2] @ Clear IF nuclear@0: nuclear@0: @ Enable multiple interrupts & switch to system nuclear@0: @ mode if __SwitchToUserStack is defined. nuclear@0: nuclear@0: mrs r3, cpsr nuclear@0: .ifdef __SwitchToUserStack nuclear@0: bic r3, r3, #0xdf @ \__ nuclear@0: orr r3, r3, #0x1f @ / --> Enable IRQ & FIQ. Set CPU mode to System. nuclear@0: .else nuclear@0: bic r3, r3, #0xc0 @ Enable IRQ & FIQ nuclear@0: .endif nuclear@0: msr cpsr, r3 nuclear@0: nuclear@0: ldr r0, [r12] nuclear@0: nuclear@0: stmfd sp!, {lr} nuclear@0: adr lr, IntrRet nuclear@0: bx r0 nuclear@0: IntrRet: nuclear@0: ldmfd sp!, {lr} nuclear@0: nuclear@0: @ Disable multiple interrupts & switch to IRQ Mode nuclear@0: @ if __SwitchToUserStack is defined. nuclear@0: nuclear@0: mrs r3, cpsr nuclear@0: .ifdef __SwitchToUserStack nuclear@0: bic r3, r3, #0xdf @ \__ nuclear@0: orr r3, r3, #0x92 @ / --> Disable IRQ. Enable FIQ. Set CPU mode to IRQ. nuclear@0: .else nuclear@0: orr r3, r3, #0x80 @ Disable IRQ. nuclear@0: .endif nuclear@0: msr cpsr, r3 nuclear@0: nuclear@0: ldmfd sp!, {r0-r2,lr} @ {spsr, IME, REG_IE, lr} //IF|IE nuclear@0: @ strh r3, [r2] @ set IE nuclear@0: strh r1, [r2, #0x8] @ restore REG_IME nuclear@0: msr spsr, r0 @ restore spsr nuclear@0: bx lr nuclear@0: nuclear@0: .endif nuclear@0: nuclear@0: .ALIGN nuclear@0: .POOL @ Ifyou copy the intr_main routine, above, to internal nuclear@0: @ RAM then copy the pool data as well because IntrTable nuclear@0: @ address is stored here. Use intr_main_end as last address+1. nuclear@0: intr_main_end: nuclear@0: .endif nuclear@0: nuclear@0: .ALIGN nuclear@0: .POOL nuclear@0: nuclear@0: nuclear@0: .END nuclear@0: