Overview
This page has a basic overview of the techniques used in the two main features of the demo, the mode7 style track and the xm playback engine.

The game in action
You can download the game here. It includes a basic map editor which you can use to fool around with the example track used in the game. Unfortunately I will not be releasing the source for the game at this time.
Tile editing suite
Mode7 Engine
When the demo was released it was the first non-commercial product to demonstrate hardware mode7 on the gba (I believe, please feel to let me know if I'm incorrect). It also used mode switching to show the parallax background scrolling around on the top.
XM Playback
The key part is keeping CPU usage as low as possible, so the inner mixing loops were coded in ARM assembler. Here is the main mixer routine, perhaps you can get some use out of it (or not ;).
C definition
typedef struct _sGxmChannel
{
u32 lowSpeed;
u32 count;
s8 *curSample;
s8 *loopEnd;
s8 *loopStart;
u16 highSpeed;
u16 pad;
s8 volume;
u8 active;
u8 flags; // ping-pong looping
u8 curPingPong;
};
void MixInLoop(u32 numBytes,sGxmChannel *channel,s16 *buffer);
ARM code
/* r0 is number of bytes to mix
r1 is pointer to the channel
r2 is the address of the mix buffer
sGxmChannel is as follows -
0 u32 lowSpeed;
4 u32 count;
8 s8 *curSample;
12 s8 *loopEnd;
16 s8 *loopStart;
20 u16 highSpeed;
22 u16 pad;
24 s8 volume;
25 u8 active;
26 u8 flags; // ping-pong looping
27 u8 curPingPong;
*/
MixInLoopFunc:
stmdb sp!,{r0-r12,lr}
stmdb sp!,{r1}
mov r8, r2 @ address of mix buffer
mov r2, r0 @ number of bytes to mix
mov r0, r1 @ base of structure
mov r1, #24 @ offset
ldrsb r3,[r0,r1] @ volume
mov r1, #0
ldr r4,[r0,r1] @ low speed
mov r1, #20
ldrh r5,[r0,r1] @ high speed
mov r1, #4
ldr r6,[r0,r1] @ count
mov r1, #8
ldr r7,[r0,r1] @ cur sample position
mov r1, #12
ldr r9,[r0,r1] @ loop end
mov r10,#1 @ sample active
mov r1, #26
ldrb r12,[r0,r1] @ flags
cmp r12,#0
beq MixLoopbackNoLoop
cmp r12,#1
beq MixLooping
b MixPingpong
/* r0 and r1 and temporaries
r2 = number of bytes to mix
r3 = volume of sample
r4 = low speed
r5 = high speed
r6 = count
r7 = pointer to 8 bit signed sample
r8 = mix buffer address
r9 = loop end
r10 = sample active*/
MixLoopbackNoLoop:
ldrsb r0,[r7]
adds r6,r6,r4
adc r7,r7,r5
cmp r7,r9
bge MixNoLoopOff @ break out of loop
ldrsh r1,[r8]
mla r1,r3,r0,r1
strh r1,[r8],#2
subs r2,r2,#1
bne MixLoopbackNoLoop
b MixDone
MixNoLoopOff:
mov r6,#0
mov r10,#0 @ set sample to inactive
b MixDone
/* r11 = loop length */
MixLooping:
mov r1,#16
ldr r11,[r0,r1] @ loop start
sub r11,r9,r11
MixLoopbackLooping:
ldrsb r0,[r7]
adds r6,r6,r4
adc r7,r7,r5
cmp r7,r9
subge r7,r7,r11 @ loopback to beginning
ldrsh r1,[r8]
mla r1,r3,r0,r1
strh r1,[r8],#2
subs r2,r2,#1
bne MixLoopbackLooping
b MixDone
MixPingpong:
b MixDone
MixDone:
/* Update the structure */
ldmia sp!,{r1}
mov r0, r1 @ base of structure
mov r1, #4 @ offset
str r6, [r0,r1] @ count
mov r1, #8
str r7, [r0,r1] @ current sample pos
mov r1, #25
strb r10, [r0,r1] @ sample active or not
/* Return */
ldmia sp!,{r0-r12,lr}
bx lr
.GLOBAL MixInLoop
MixInLoop:
stmdb sp!,{r7-r8,lr}
ldr r7,=MixInLoopFunc
mov lr,pc
add lr,lr,#4
bx r7
ldmia sp!,{r7-r8,lr}
bx lr
