initial commit
authorJohn Tsiombikas <nuclear@member.fsf.org>
Wed, 27 Jul 2022 02:55:18 +0000 (05:55 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Wed, 27 Jul 2022 02:55:18 +0000 (05:55 +0300)
46 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
libs/maxmod/Makefile [new file with mode: 0644]
libs/maxmod/license.txt [new file with mode: 0644]
libs/maxmod/maxmod.h [new file with mode: 0644]
libs/maxmod/maxmod7.h [new file with mode: 0644]
libs/maxmod/mm_effect.S [new file with mode: 0644]
libs/maxmod/mm_init_default.S [new file with mode: 0644]
libs/maxmod/mm_main.S [new file with mode: 0644]
libs/maxmod/mm_mas.S [new file with mode: 0644]
libs/maxmod/mm_mas_arm.S [new file with mode: 0644]
libs/maxmod/mm_mixer_gba.S [new file with mode: 0644]
libs/maxmod/mm_types.h [new file with mode: 0644]
libs/maxmod/mp_defs.inc [new file with mode: 0644]
libs/maxmod/mp_format_mas.inc [new file with mode: 0644]
libs/maxmod/mp_macros.inc [new file with mode: 0644]
libs/maxmod/mp_mas.inc [new file with mode: 0644]
libs/maxmod/mp_mas_structs.inc [new file with mode: 0644]
libs/maxmod/mp_mixer_gba.inc [new file with mode: 0644]
libs/maxmod/swi_gba.inc [new file with mode: 0644]
src/asmutil.s [new file with mode: 0644]
src/debug.c [new file with mode: 0644]
src/debug.h [new file with mode: 0644]
src/dma.c [new file with mode: 0644]
src/dma.h [new file with mode: 0644]
src/font8x8.c [new file with mode: 0644]
src/game.h [new file with mode: 0644]
src/gamescr.c [new file with mode: 0644]
src/gbaregs.h [new file with mode: 0644]
src/input.c [new file with mode: 0644]
src/input.h [new file with mode: 0644]
src/intr.c [new file with mode: 0644]
src/intr.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/menuscr.c [new file with mode: 0644]
src/polyfill.c [new file with mode: 0644]
src/polyfill.h [new file with mode: 0644]
src/sprite.c [new file with mode: 0644]
src/sprite.h [new file with mode: 0644]
src/timer.c [new file with mode: 0644]
src/timer.h [new file with mode: 0644]
src/util.c [new file with mode: 0644]
src/util.h [new file with mode: 0644]
src/xgl.c [new file with mode: 0644]
src/xgl.h [new file with mode: 0644]
tools/lutgen.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..6244192
--- /dev/null
@@ -0,0 +1,19 @@
+*.o
+*.d
+*.swp
+cfg.mk
+*.gba
+*.elf
+*.a
+pngdump
+mmutil
+lutgen
+*.ppm
+*.png
+*.sav
+disasm
+data
+*.tar.gz
+*.zip
+pushdata
+link.map
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..db4dd95
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,102 @@
+src = $(wildcard src/*.c)
+ssrc = $(wildcard src/*.s) data/lut.s
+obj = $(src:.c=.o) $(ssrc:.s=.o)
+dep = $(src:.c=.d)
+name = gbajam22
+elf = $(name).elf
+bin = $(name).gba
+
+libs = libs/maxmod/libmm.a
+
+TCPREFIX = arm-none-eabi-
+
+CPP = $(TCPREFIX)cpp
+CC = $(TCPREFIX)gcc
+AS = $(TCPREFIX)as
+OBJCOPY = $(TCPREFIX)objcopy
+OBJDUMP = $(TCPREFIX)objdump
+
+opt = -O3 -fomit-frame-pointer -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork
+dbg = -g
+inc = -I. -Ilibs/maxmod
+
+CFLAGS = $(opt) $(dbg) -pedantic -Wall -MMD $(def) $(inc)
+ASFLAGS = -mthumb-interwork
+LDFLAGS = -mthumb -mthumb-interwork $(libs)
+
+-include cfg.mk
+
+.PHONY: all
+all: $(bin) $(bin_mb)
+
+$(bin): $(elf)
+       $(OBJCOPY) -O binary $(elf) $(bin)
+       gbafix -r0 $(bin)
+
+$(elf): $(obj) $(libs)
+       $(CC) -o $(elf) $(obj) -specs=gba.specs -Wl,-Map,link.map $(LDFLAGS)
+
+-include $(dep)
+
+src/data.o: src/data.s $(data)
+
+tools/pngdump/pngdump:
+       $(MAKE) -C tools/pngdump
+
+tools/lutgen: tools/lutgen.c
+       cc -o $@ $< -lm
+
+tools/mmutil/mmutil:
+       $(MAKE) -C tools/mmutil
+
+%.sraw: %.png tools/pngdump/pngdump
+       tools/pngdump/pngdump -o $@ -oc $(subst .sraw,.spal,$@) -os $(subst .sraw,.shade,$@) -s 8 $<
+
+%.raw: %.png tools/pngdump/pngdump
+       tools/pngdump/pngdump -o $@ -n $<
+
+%.pal: %.png tools/pngdump/pngdump
+       tools/pngdump/pngdump -o $@ -c $<
+
+data/lut.s: tools/lutgen
+       tools/lutgen >$@
+
+data/snd.bin: $(audata) tools/mmutil/mmutil
+       tools/mmutil/mmutil -o$@ -hdata/snd.h $(audata)
+
+data/snd.h: data/snd.bin
+
+.PHONY: clean
+clean:
+       rm -f $(obj) $(bin) $(bin_mb) $(elf) $(elf_mb)
+
+.PHONY: cleandep
+cleandep:
+       rm -f $(dep)
+
+.PHONY: cleanlibs
+cleanlibs:
+       $(MAKE) -C libs/maxmod clean
+
+.PHONY: install
+install: $(bin)
+       if2a -n -f -W $<
+
+.PHONY: run
+run: $(bin)
+       mgba -2 $(bin)
+
+.PHONY: debug
+debug: $(elf)
+       mgba -2 -g $(bin) &
+       $(TCPREFIX)gdb $<
+
+.PHONY: disasm
+disasm: $(elf)
+       $(OBJDUMP) -d $< >$@
+
+.PHONY: libs
+libs: $(libs)
+
+libs/maxmod/libmm.a:
+       $(MAKE) -C libs/maxmod
diff --git a/libs/maxmod/Makefile b/libs/maxmod/Makefile
new file mode 100644 (file)
index 0000000..f7a4356
--- /dev/null
@@ -0,0 +1,17 @@
+src = $(wildcard *.S)
+obj = $(src:.S=.o)
+lib_a = libmm.a
+
+TCPREFIX ?= arm-none-eabi-
+CC = $(TCPREFIX)gcc
+AS = $(TCPREFIX)as
+AR = $(TCPREFIX)ar
+
+ASFLAGS = -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork -DSYS_GBA -DUSE_IWRAM
+
+$(lib_a): $(obj)
+       $(AR) rcs $@ $(obj)
+
+.PHONY: clean
+clean:
+       rm -f $(obj) $(lib_a)
diff --git a/libs/maxmod/license.txt b/libs/maxmod/license.txt
new file mode 100644 (file)
index 0000000..265395b
--- /dev/null
@@ -0,0 +1,23 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *               Nintendo DS & Gameboy Advance Sound System                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
diff --git a/libs/maxmod/maxmod.h b/libs/maxmod/maxmod.h
new file mode 100644 (file)
index 0000000..f2ef840
--- /dev/null
@@ -0,0 +1,396 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                             GBA Definitions                              *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#ifndef MAXMOD_H
+#define MAXMOD_H
+
+#include <mm_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// precalculated mix buffer lengths (in bytes)
+#define MM_MIXLEN_8KHZ         544             // (8121 hz)
+#define MM_MIXLEN_10KHZ                704             // (10512 hz)   
+#define MM_MIXLEN_13KHZ                896             // (13379 hz)   
+#define MM_MIXLEN_16KHZ                1056    // (15768 hz)
+#define MM_MIXLEN_18KHZ        1216    // (18157 hz)
+#define MM_MIXLEN_21KHZ        1408    // (21024 hz)
+#define MM_MIXLEN_27KHZ                1792    // (26758 hz)
+#define MM_MIXLEN_31KHZ                2112    // (31536 hz)
+
+// measurements of channel types (bytes)
+#define MM_SIZEOF_MODCH                40
+#define MM_SIZEOF_ACTCH                28
+#define MM_SIZEOF_MIXCH                24
+
+/****************************************************************************
+ * mmInitDefault( mm_addr soundbank, mm_word number_of_channels )
+ *
+ * Initialize Maxmod with default settings.
+ ****************************************************************************/
+void mmInitDefault( mm_addr soundbank, mm_word number_of_channels );
+
+/****************************************************************************
+ * mmInit( mm_gba_system* setup )
+ *
+ * Initialize system. Call once at startup.
+ ****************************************************************************/
+void mmInit( mm_gba_system* setup );
+
+/****************************************************************************
+ * mmVBlank()
+ *
+ * Must be linked to the VBlank IRQ.
+ ****************************************************************************/
+void mmVBlank( void );
+
+/****************************************************************************
+ * mmSetVBlankHandler( void* function )
+ *
+ * Install user vblank handler
+ *
+ * function : Pointer to your VBlank handler.
+ ****************************************************************************/
+
+void mmSetVBlankHandler( void* function );
+
+/****************************************************************************
+ * mmSetEventHandler( mm_callback handler )
+ *
+ * Install handler to receive song events.
+ ****************************************************************************/
+
+void mmSetEventHandler( mm_callback handler );
+
+/****************************************************************************
+ * mmFrame()
+ *
+ * Work routine. _Must_ be called every frame.
+ ****************************************************************************/
+void mmFrame( void ) __attribute((long_call));
+
+
+
+/****************************************************************************
+ *
+ * Module Playback
+ *
+ ****************************************************************************/
+
+
+
+/****************************************************************************
+ * mmStart( mm_word id, mm_pmode mode )
+ *
+ * Start module playback.
+ *
+ * id : ID of module to play.
+ * mode : Playback mode (loop/once)
+ ****************************************************************************/
+
+void mmStart( mm_word id, mm_pmode mode );
+
+/****************************************************************************
+ * mmPause()
+ *
+ * Pause module playback, resume with mmResume().
+ ****************************************************************************/
+void mmPause( void );
+
+/****************************************************************************
+ * mmResume()
+ *
+ * Resume module playback, pause with mmPause().
+ ****************************************************************************/
+void mmResume( void );
+
+/****************************************************************************
+ * mmStop()
+ *
+ * Stop module playback. start again with mmStart().
+ ****************************************************************************/
+
+void mmStop( void );
+
+/****************************************************************************
+ * mmGetPosition()
+ *
+ * Get playback position.
+ ****************************************************************************/
+
+mm_word mmGetPosition( void );
+
+/****************************************************************************
+ * mmPosition()
+ *
+ * Set playback position.
+ *
+ * position: New position in the module sequence.
+ ****************************************************************************/
+
+void mmPosition( mm_word position );
+
+/****************************************************************************
+ * mmSetPosition()
+ *
+ * Set playback position (alias for mmPosition()).
+ *
+ * position: New position in the module sequence.
+ ****************************************************************************/
+
+inline void mmSetPosition( mm_word position )
+{
+    mmPosition( position );
+}
+
+/****************************************************************************
+ * mmActive()
+ *
+ * Returns nonzero if module is playing.
+ ****************************************************************************/
+
+int  mmActive( void );
+
+/****************************************************************************
+ * mmJingle( mm_word module_ID )
+ *
+ * Play module as jingle. Jingles are limited to 4 channels only.
+ *
+ * module_ID : ID of moudle (defined in soundbank header)
+ ****************************************************************************/
+void mmJingle( mm_word module_ID );
+
+/****************************************************************************
+ * mmActiveSub()
+ *
+ * Returns nonzero if a jingle is actively playing.
+ ****************************************************************************/
+
+int  mmActiveSub( void );
+
+/****************************************************************************
+ * mmSetModuleVolume( mm_word volume )
+ * mmSetJingleVolume( mm_word volume )
+ *
+ * Set volume scaler for music/jingles.
+ *
+ * volume : 0->1024 = silent..normal
+ ****************************************************************************/
+
+void mmSetModuleVolume( mm_word volume );
+void mmSetJingleVolume( mm_word volume );
+
+/*****************************************************************
+ * mmSetModuleTempo( mm_word tempo )
+ *
+ * Set tempo of playback.
+ *
+ * tempo : Fixed point (Q10) value representing tempo.
+ *         Range = 0x200 -> 0x800 = 0.5 -> 2.0
+ *****************************************************************/
+
+void mmSetModuleTempo( mm_word tempo );
+
+/*****************************************************************
+ * mmSetModulePitch( mm_word pitch )
+ *
+ * Set pitch of playback.
+ *
+ * pitch : Range = 0x200 -> 0x800 = 0.5 -> 2.0
+ *****************************************************************/
+void mmSetModulePitch( mm_word pitch );
+
+/****************************************************************************
+ * mmPlayModule( mm_word address, mm_word mode, mm_word layer )
+ *
+ * Play direct MAS file
+ ****************************************************************************/
+void mmPlayModule( mm_word address, mm_word mode, mm_word layer );
+
+
+
+/****************************************************************************
+ *
+ * Sound Effects
+ *
+ ****************************************************************************/
+
+
+
+/****************************************************************************
+ * mmEffect( mm_word sample_ID )
+ *
+ * Play a sound effect at its default frequency with full volume and
+ * centered panning.
+ *
+ * sample_ID : Sound effect ID. (defined in soundbank header)
+ ****************************************************************************/
+
+mm_sfxhand mmEffect( mm_word sample_ID );
+
+/****************************************************************************
+ * mmEffectEx( mm_sound_effect* sound )
+ *
+ * Play a sound effect with all parameters.
+ *
+ * sound : Sound effect attributes.
+ ****************************************************************************/
+
+mm_sfxhand mmEffectEx( mm_sound_effect* sound );
+
+/****************************************************************************
+ * mmEffectVolume( mm_sfxhand handle, mm_word volume )
+ *
+ * Set the volume of a sound effect.
+ *
+ * handle : Sound effect handle.
+ * volume : 0->65535
+ ****************************************************************************/
+void mmEffectVolume( mm_sfxhand handle, mm_word volume );
+
+/****************************************************************************
+ * mmEffectPanning( mm_sfxhand handle, mm_word panning )
+ *
+ * Set the panning of a sound effect.
+ *
+ * handle : Sound effect handle.
+ * panning : 0->255 = left..right
+ ****************************************************************************/
+void mmEffectPanning( mm_sfxhand handle, mm_byte panning );
+
+/****************************************************************************
+ * mmEffectRate( mm_sfxhand handle, mm_word rate )
+ *
+ * Set the playback rate of an effect.
+ *
+ * handle : Sound effect handle.
+ * rate: 6.10 factor
+ ****************************************************************************/
+void mmEffectRate( mm_sfxhand handle, mm_word rate );
+
+/****************************************************************************
+ * mmEffectScaleRate( mm_sfxhand handle, mm_word factor )
+ *
+ * Scale the playback rate of an effect.
+ *
+ * handle : Sound effect handle.
+ * factor : 6.10 fixed point factor.
+ ****************************************************************************/
+void mmEffectScaleRate( mm_sfxhand handle, mm_word factor );
+
+/****************************************************************************
+ * mmEffectActive( mm_sfxhand handle )
+ *
+ * Indicates if a sound effect is active or not.
+ *
+ * handle : Sound effect handle.
+ ****************************************************************************/
+
+mm_bool mmEffectActive( mm_sfxhand handle );
+
+/****************************************************************************
+ * mmEffectCancel( mm_sfxhand handle )
+ *
+ * Stop sound effect.
+ *
+ * handle : Sound effect handle.
+ ****************************************************************************/
+
+void mmEffectCancel( mm_sfxhand handle );
+
+/****************************************************************************
+ * mmEffectRelease( mm_sfxhand handle )
+ *
+ * Release sound effect (invalidate handle and allow interruption)
+ *
+ * handle : Sound effect handle.
+ ****************************************************************************/
+void mmEffectRelease( mm_sfxhand handle );
+
+/****************************************************************************
+ * mmSetEffectsVolume( mm_word volume )
+ *
+ * Set master volume scale for effect playback.
+ *
+ * volume : 0->1024 representing 0%->100% volume
+ ****************************************************************************/
+
+void mmSetEffectsVolume( mm_word volume );
+
+/****************************************************************************
+ * mmEffectCancelAll()
+ *
+ * Stop all sound effects
+ ****************************************************************************/
+void mmEffectCancelAll();
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/****************************************************************************
+ * Playback events
+ ****************************************************************************/
+
+//---------------------------------------------------------------------------
+// This happens when Maxmod reads a SFx (or mod/xm EFx) effect from a module
+// It will store 'x' in param_b
+//---------------------------------------------------------------------------
+#define MMCB_SONGMESSAGE       0x2A
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+// A module has finished playing
+// param == 0 if main module, ==1 otherwise
+//---------------------------------------------------------------------------
+#define MMCB_SONGFINISHED      0x2B
+//---------------------------------------------------------------------------
+
+/****************************************************************************
+ * etc...
+ ****************************************************************************/
+
+extern mm_byte mp_mix_seg;                     // current mixing segment
+extern mm_word mp_writepos;            // mixer's write position
+
+#endif
diff --git a/libs/maxmod/maxmod7.h b/libs/maxmod/maxmod7.h
new file mode 100644 (file)
index 0000000..a51b2d6
--- /dev/null
@@ -0,0 +1,483 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                            ARM7 Definitions                              *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#ifdef ARM9
+#error "maxmod7.h is for ARM7!"
+#endif
+
+//----------------------------------------------------------------
+#ifndef MAXMOD_H
+//----------------------------------------------------------------
+
+#define MAXMOD_H
+
+#include <mm_types.h>
+
+//----------------------------------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+//----------------------------------------------------------------
+
+/*****************************************************************
+ * mmInstall( channel )
+ *
+ * Install Maxmod.
+ *
+ * channel : IPC/FIFO channel to use, usually 7.
+ *****************************************************************/
+void mmInstall( int fifo_channel );
+
+/*****************************************************************
+ * mmLockChannels( mm_word bitmask )
+ *
+ * Lock audio channels from being used by Maxmod.
+ *
+ * bitmask : Bitmask of which channels should be locked.
+ * (&1 = ch0, &2 = ch1, &4 = ch2, &8 = ch4, etc...)
+ *****************************************************************/
+void mmLockChannels( mm_word bitmask );
+
+/*****************************************************************
+ * mmUnlockChannels( mm_word bitmask )
+ *
+ * Unlock audio channels so Maxmod can use them.
+ *
+ * bitmask : Bitmask of which channels should be unlocked.
+ * (&1 = ch0, &2 = ch1, &4 = ch2, &8 = ch4, etc...)
+ *****************************************************************/
+void mmUnlockChannels( mm_word bitmask );
+
+/*****************************************************************
+ * mmIsInitialize()
+ *
+ * Returns whether or not the system is ready for playback.
+ *****************************************************************/
+
+mm_bool mmIsInitialized( void );
+
+/*****************************************************************
+ * mmSelectMode( mm_mode_enum mode )
+ *
+ * Select audio mode
+ *
+ * mode:
+ *   MM_MODE_A : Hardware Mixing
+ *   MM_MODE_B : Interpolated Mixing
+ *   MM_MODE_C : Extended Mixing
+ *****************************************************************/
+void mmSelectMode( mm_mode_enum mode );
+
+/*****************************************************************
+ * mmFrame()
+ *
+ * Work routine (called automatically)
+ *****************************************************************/
+
+void mmFrame( void );
+
+// note about using sleep functions:
+// do not use during an interrupt!! it's not safe
+// and do not exit sleep unless it is already sleeping.
+
+
+
+/*****************************************************************
+ *
+ * Module Playback
+ *
+ *****************************************************************/
+
+
+
+/*****************************************************************
+ * mmStart( mm_word module_ID, mm_pmode mode )
+ *
+ * Start module playback
+ *
+ * module_ID : Index of module to play. (defined in soundbank header)
+ * mode : Mode of playback: MM_PLAY_ONCE or MM_PLAY_LOOP
+ *****************************************************************/
+void mmStart( mm_word module_ID, mm_pmode mode );
+
+/*****************************************************************
+ * mmPause()
+ *
+ * Pause module playback.
+ *****************************************************************/
+void mmPause( void );
+
+/*****************************************************************
+ * mmResume()
+ *
+ * Resume module playback
+ *****************************************************************/
+void mmResume( void );
+
+/*****************************************************************
+ * mmStop()
+ *
+ * Stop module playback. Start again (from the beginning)
+ * with mmStart(...).
+ *****************************************************************/
+void mmStop( void );
+
+/*****************************************************************
+ * mmPosition( mm_word position )
+ *
+ * Set playback position.
+ *
+ * position : New position in module sequence.
+ *****************************************************************/
+void mmPosition( mm_word position );
+
+/*****************************************************************
+ * mmActive()
+ *
+ * Returns nonzero if module is playing
+ *****************************************************************/
+mm_bool        mmActive( void );
+
+/*****************************************************************
+ * mmJingle( mm_word module_ID )
+ *
+ * Play module as jingle. Jingles are limited to 4 channels only.
+ *
+ * module_ID : ID of module (defined in soundbank header)
+ *****************************************************************/
+
+void mmJingle( mm_word module_ID );
+
+/*****************************************************************
+ * mmActiveSub()
+ *
+ * Returns nonzero if a jingle is actively playing.
+ *****************************************************************/
+mm_bool        mmActiveSub( void );
+
+/*****************************************************************
+ * mmSetModuleVolume( mm_word volume )
+ * mmSetJingleVolume( mm_word volume )
+ *
+ * Set volume scaler for music/jingles.
+ *
+ * volume : 0->1024 = silent..normal
+ *****************************************************************/
+
+void mmSetModuleVolume( mm_word volume );
+void mmSetJingleVolume( mm_word volume );
+
+/*****************************************************************
+ * mmPlayModule( mm_word address, mm_word mode, mm_word layer )
+ *
+ * Play direct MAS file
+ *****************************************************************/
+
+void mmPlayModule( mm_word address, mm_word mode, mm_word layer );
+
+
+
+/*****************************************************************
+ *
+ * Sound Effects
+ *
+ *****************************************************************/
+
+
+
+/*****************************************************************
+ * mmEffect( mm_word sample_ID )
+ *
+ * Play a sound effect at its default frequency with full volume
+ * and centered panning. Effect must be loaded first via
+ * mmLoadEffect.
+ *
+ * sample_ID : Sound effect ID. (defined in soundbank header)
+ *****************************************************************/
+mm_sfxhand mmEffect( mm_word sample_ID );
+
+/*****************************************************************
+ * mmEffectEx( mm_sound_effect* sound )
+ *
+ * Play a sound effect with the parameters supplied.
+ *
+ * sound : Sound effect attributes.
+ *****************************************************************/
+
+mm_sfxhand mmEffectEx( mm_sound_effect* sound );
+
+/*****************************************************************
+ * mmEffectVolume( mm_sfxhand handle, mm_word volume )
+ *
+ * Set the volume of a sound effect.
+ *
+ * handle : Sound effect handle.
+ * volume : 0->65535
+ *****************************************************************/
+void mmEffectVolume( mm_sfxhand handle, mm_word volume );
+
+/*****************************************************************
+ * mmEffectPanning( mm_sfxhand handle, mm_word panning )
+ *
+ * Set the panning of a sound effect.
+ *
+ * handle : Sound effect handle.
+ * panning : 0->255 = left..right
+ *****************************************************************/
+void mmEffectPanning( mm_sfxhand handle, mm_byte panning );
+
+/*****************************************************************
+ * mmEffectRate( mm_sfxhand handle, mm_word rate )
+ *
+ * Set the playback rate of an effect.
+ *
+ * handle : Sound effect handle.
+ * rate: 6.10 factor
+ *****************************************************************/
+void mmEffectRate( mm_sfxhand handle, mm_word rate );
+
+/*****************************************************************
+ * mmEffectScaleRate( mm_sfxhand handle, mm_word factor )
+ *
+ * Scale the playback rate of an effect.
+ *
+ * handle : Sound effect handle.
+ * factor : 6.10 fixed point factor.
+ *****************************************************************/
+void mmEffectScaleRate( mm_sfxhand handle, mm_word factor );
+
+/*****************************************************************
+ * mmEffectCancel( mm_sfxhand handle )
+ *
+ * Stop sound effect.
+ *
+ * handle : Sound effect handle.
+ *****************************************************************/
+
+void mmEffectCancel( mm_sfxhand handle );
+
+/*****************************************************************
+ * mmEffectRelease( mm_sfxhand handle )
+ *
+ * Release sound effect (invalidate handle and allow interruption)
+ *
+ * handle : Sound effect handle.
+ *****************************************************************/
+void mmEffectRelease( mm_sfxhand handle );
+
+/*****************************************************************
+ * mmSetEffectsVolume( mm_word volume )
+ *
+ * Set master volume scale for effect playback.
+ *
+ * volume : 0->1024 representing 0%->100% volume
+ *****************************************************************/
+
+void mmSetEffectsVolume( mm_word volume );
+
+/*****************************************************************
+ * mmEffectCancelAll()
+ *
+ * Stop all sound effects
+ *****************************************************************/
+void mmEffectCancelAll();
+
+
+
+/*****************************************************************
+ *
+ * Streaming
+ *
+ *****************************************************************/
+
+
+/*****************************************************************
+ * mmStreamOpen( mm_stream* stream, mm_addr wavebuffer, mm_addr workbuffer )
+ *
+ * Open audio stream.
+ *
+ * stream : Configuration struct
+ * wavebuffer : wave memory, must be aligned
+ * workbuffer : work memory, must be aligned
+ *****************************************************************/
+
+void mmStreamOpen( mm_stream* stream, mm_addr wavebuffer, mm_addr workbuffer );
+
+/*****************************************************************
+ * mmStreamUpdate()
+ *
+ * Fills the stream with wave data.
+ * This only needs to be called in 'manual' mode. It
+ * is called automatically in 'auto' mode.
+ *****************************************************************/
+
+void mmStreamUpdate( void );
+
+/*****************************************************************
+ * mmStreamClose()
+ *
+ * Close audio stream.
+ *****************************************************************/
+
+void mmStreamClose( void );
+
+/*****************************************************************
+ * mmStreamGetPosition()
+ *
+ * Get number of samples elapsed since the stream was opened.
+ * The 32-bit value will wrap every 36 hours or so (at 32khz)
+ *****************************************************************/
+mm_word mmStreamGetPosition();
+
+
+
+/*****************************************************************
+ *
+ * Reverb
+ *
+ *****************************************************************/
+
+/*****************************************************************
+ * mmReverbEnable()
+ *
+ * Enable reverb system. (use before configuring!)
+ *****************************************************************/
+void mmReverbEnable( void );
+/*****************************************************************
+ * mmReverbConfigure( mm_reverb_cfg* config )
+ *
+ * Configure reverb parameters.
+ * config : Configuration data.
+ *****************************************************************/
+void mmReverbConfigure( mm_reverb_cfg* config );
+
+/*****************************************************************
+ * mmReverbStart( mm_reverbch channels )
+ *
+ * Start reverb output.
+ *****************************************************************/
+
+void mmReverbStart( mm_reverbch channels );
+
+/*****************************************************************
+ * mmReverbStop( mm_reverch channels )
+ *
+ * Stop reverb output.
+ *****************************************************************/
+
+void mmReverbStop( mm_reverbch channels );
+
+/*****************************************************************
+ * mmReverbBufferSize( mm_word bit_depth, mm_word sampling_rate,
+ *                     mm_word delay )
+ *
+ * Calculate reverb buffer size based on bit depth, delay
+ * and sampling rate.
+ *
+ * bit_depth : Pass 8 or 16 for 8-bit/16-bit
+ * sampling_rate :
+ * delay : In milliseconds
+ *
+ * Returns size in WORDS.
+ *****************************************************************/
+
+static inline mm_word mmReverbBufferSize( mm_word bit_depth, mm_word sampling_rate, mm_word delay )
+{
+       if( bit_depth == 16 )
+       {
+               return ((((sampling_rate * delay * 2) / 1000) + 3) & (~3)) / 4;
+       }
+       else
+       {
+               return ((((sampling_rate * delay) / 1000) + 3) & (~3)) / 4;
+       }
+}
+
+/*****************************************************************
+ * mmReverbDisable()
+ *
+ * Disable reverb system.
+ *****************************************************************/
+
+void mmReverbDisable( void );
+
+
+
+//----------------------------------------------------------------
+#ifdef __cplusplus
+}
+#endif
+//----------------------------------------------------------------
+
+/*****************************************************************
+ * Playback events
+ *****************************************************************/
+
+//----------------------------------------------------------------
+// This happens when Maxmod reads an SFx (or mod/xm EFx) effect
+// from a module. It will pass 'x' in param.
+//----------------------------------------------------------------
+#define MMCB_SONGMESSAGE       0x2A
+//----------------------------------------------------------------
+
+//----------------------------------------------------------------
+// A module has finished playing.
+// param == 0 if main module, ==1 otherwise
+//----------------------------------------------------------------
+#define MMCB_SONGFINISHED      0x2B
+//----------------------------------------------------------------
+
+//=======================================================================================
+// misc definitions / references
+//=======================================================================================
+
+// main/sub layer attributes
+extern mm_modlayer mmLayerMain;
+extern mm_modlayer mmLayerSub;
+
+#endif
diff --git a/libs/maxmod/mm_effect.S b/libs/maxmod/mm_effect.S
new file mode 100644 (file)
index 0000000..5c92ffc
--- /dev/null
@@ -0,0 +1,767 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                           Sound Effect System                            *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#include       "mp_defs.inc"
+#include       "mp_mas.inc"
+#include       "mp_mas_structs.inc"
+#include       "mp_format_mas.inc"
+#include       "mp_macros.inc"
+
+#ifdef SYS_GBA
+#include       "mp_mixer_gba.inc"
+#endif
+
+#ifdef SYS_NDS
+#include       "mp_mixer_ds.inc"
+#endif
+
+/***********************************************************************
+ *
+ * Definitions
+ *
+ ***********************************************************************/
+
+.struct 0                                      // mm_sound_effect
+MM_SFX_SOURCE: .space 4                        // word: source
+MM_SFX_RATE:   .space 2                        // hword: rate
+MM_SFX_HANDLE: .space 2                        // byte:  handle
+MM_SFX_VOLUME: .space 1                        // byte:  volume
+MM_SFX_PANNING:        .space 1                        // byte:  panning
+MM_SFX_SIZE:                                   // 8 bytes total
+
+.equ   channelCount, 16
+.equ   releaseLevel, 200
+
+/***********************************************************************
+ *
+ * Memory
+ *
+ ***********************************************************************/
+
+       .BSS
+       .ALIGN 2
+       .GLOBAL mm_sfx_bitmask, mm_sfx_clearmask
+
+mm_sfx_mastervolume:   .space 4
+mm_sfx_channels:       .space 2*channelCount
+mm_sfx_bitmask:                .space 4
+mm_sfx_clearmask:      .space 4
+
+mm_sfx_counter:                .space 1
+
+/***********************************************************************
+ *
+ * Program
+ *
+ ***********************************************************************/
+       .TEXT
+       .THUMB
+       .ALIGN 2
+       
+/***********************************************************************
+ * mmResetEffects
+ ***********************************************************************/
+                                               .global mmResetEffects
+                                               .thumb_func
+mmResetEffects:
+       
+       mov     r0, #0
+       mov     r1, #channelCount
+       ldr     r2,=mm_sfx_channels
+       
+1:     strh    r0, [r2]
+       add     r2, #2
+       sub     r1, #1
+       bne     1b
+       
+       ldr     r2,=mm_sfx_bitmask
+       str     r0, [r2]
+       
+       bx      lr
+       
+/***********************************************************************
+ * mmGetFreeEffectChannel()
+ *
+ * Return index to free effect channel
+ ***********************************************************************/
+                                               .thumb_func
+mmGetFreeEffectChannel:
+       
+       ldr     r0,=mm_sfx_bitmask              // r0 = bitmask
+       ldr     r0, [r0]                        //
+       mov     r1, #1                          // r1 = channel counter
+       
+.channel_search:                               // shift out bits until we find a cleared one
+       lsr     r0, #1                          //
+       bcc     .found_channel                  //
+       add     r1, #1                          // r1 = index value
+       b       .channel_search                 //
+       
+.found_channel:
+       
+       cmp     r1, #channelCount+1             // if r1 == cc+1 then r1 = 0 (no handles avail.)
+       bne     .found_valid_channel            //
+       mov     r1, #0                          //
+.found_valid_channel:                          //
+       
+       mov     r0, r1                          // return value
+       bx      lr                              //
+
+/***********************************************************************
+ * mmEffect( id )
+ *
+ * Play sound effect with default parameters
+ ***********************************************************************/
+                                               .global mmEffect
+                                               .thumb_func
+mmEffect:
+       push    {lr}
+                                               // r0 = ssssssss
+       mov     r1, #1                          // r1 = hhhhrrrr
+       lsl     r1, #10                         //
+       ldr     r2,=0x000080FF                  // r2 = ----ppvv
+       
+       push    {r0-r2}                         // mmEffectEx( sound )
+       mov     r0, sp                          // 
+       bl      mmEffectEx                      //
+       add     sp, #12                         //
+       pop     {r3}                            //
+       bx      r3                              //
+       
+/***********************************************************************
+ * mmEffectEx( sound )
+ *
+ * Play sound effect with specified parameters
+ ***********************************************************************/
+                                               .global mmEffectEx
+                                               .thumb_func
+mmEffectEx:
+       
+       push    {r4-r6, lr}
+       
+       mov     r4, r0
+       ldrh    r5, [r4, #MM_SFX_HANDLE]        // test if handle was given
+       
+       cmp     r5, #255
+       bne     1f
+       mov     r5, #0
+       b       .got_handle
+       
+1:     cmp     r5, #0                          //
+       beq     .generate_new_handle            //
+       
+       lsl     r1, r5, #24                     // check if channel is in use
+       lsr     r1, #23                         //
+       sub     r1, #2                          //
+       ldr     r0,=mm_sfx_channels             //
+       ldrb    r0, [r0, r1]                    //
+       cmp     r0, #0                          //
+       beq     .got_handle                     // [valid handle otherwise]
+       
+       mov     r0, r5
+       bl      mmEffectCancel                  // attempt to stop active channel
+       cmp     r0, #0                          //
+       bne     .got_handle                     //
+                                               // failure: generate new handle
+.generate_new_handle:
+       
+       bl      mmGetFreeEffectChannel          // generate handle
+       mov     r5, r0                          //
+       beq     .got_handle                     //-(no available channels)
+                                               //
+       ldr     r0,=mm_sfx_counter              // (counter = ((counter+1) & 255))
+       ldrb    r1, [r0]                        //
+       add     r1, #1                          //
+       strb    r1, [r0]                        //
+       lsl     r1, #8
+       orr     r5, r1
+       lsl     r5, #16
+       lsr     r5, #16
+       
+.got_handle:
+       
+       ldr     r1,=mmAllocChannel              // allocate new channel
+       bl      mpp_call_r1                     //
+       cmp     r0, #255                        //
+       bge     .no_available_channels          //
+       
+       mov     r6, r0
+       
+       cmp     r5, #0
+       beq     1f 
+       
+       ldr     r1,=mm_sfx_channels             // register data
+       sub     r2, r5, #1                      // r3 = bit
+       lsl     r2, #24                         //
+       lsr     r2, #24                         //
+       mov     r3, #1                          //
+       lsl     r3, r2                          //
+       lsl     r2, #1                          //
+       add     r1, r2                          //
+       add     r2, r0, #1                      //
+       strb    r2, [r1, #0]                    //
+       lsr     r2, r5, #8                      //
+       strb    r2, [r1, #1]                    //
+       
+       ldr     r1,=mm_sfx_bitmask              // set bit
+       ldr     r2, [r1]                        //
+       orr     r2, r3                          //
+       str     r2, [r1]                        //
+       
+1:     
+
+       ldr     r1,=mm_achannels                // setup channel
+       ldr     r1, [r1]                        //
+       mov     r2, #MCA_SIZE                   //
+       mul     r2, r0                          //
+       add     r1, r2                          //
+                                               //
+       mov     r2, #releaseLevel               //
+       strb    r2, [r1, #MCA_FVOL]             //
+       
+       cmp     r5, #0                          //
+       bne     1f                              //
+       mov     r2, #ACHN_BACKGROUND            //
+       b       2f                              //
+1:     mov     r2, #ACHN_CUSTOM                //
+2:                                             //
+       strb    r2, [r1, #MCA_TYPE]             //
+       mov     r2, #MCAF_EFFECT                //
+       strb    r2, [r1, #MCA_FLAGS]            //
+       
+       GET_MIXCH r1                            // setup voice
+       mov     r2, #MIXER_CHN_SIZE             //
+       mul     r2, r0                          //
+       add     r3, r1, r2                      //
+       
+#ifdef SYS_GBA
+
+       ldr     r0,=mp_solution                 // set sample data address
+       ldr     r0, [r0]                        //
+       ldrh    r1, [r4, #MM_SFX_SOURCE]        //
+       lsl     r1, #2                          //
+       add     r1, #12                         //
+       ldr     r1, [r0, r1]                    //
+       add     r1, r0                          //
+       ldrh    r2, [r1, #8+C_SAMPLEC_DFREQ]    //
+       add     r1, #8+C_SAMPLE_DATA            //
+       str     r1, [r3, #MIXER_CHN_SRC]        //
+       
+       ldrh    r0, [r4, #MM_SFX_RATE]          // set pitch to original * pitch
+       mul     r2, r0                          //
+       lsr     r2, #10-2                       //
+       str     r2, [r3, #MIXER_CHN_FREQ]       //
+       
+       mov     r1, #0                          // reset read position
+       str     r1, [r3, #MIXER_CHN_READ]       //
+       
+       ldrb    r0, [r4, #MM_SFX_VOLUME]        // set volume
+       
+       ldr     r1,=mm_sfx_mastervolume
+       ldr     r1, [r1]
+       mul     r0, r1
+       lsr     r0, #10
+       
+       strb    r0, [r3, #MIXER_CHN_VOL]        //
+       
+       ldrb    r0, [r4, #MM_SFX_PANNING]       // set panning
+       strb    r0, [r3, #MIXER_CHN_PAN]        //
+       
+#else
+       ldr     r1, [r4, #MM_SFX_SOURCE]        // set sample address
+       lsr     r0, r1, #16                     // > 0x10000 = external sample
+       bne     1f                              //
+       ldr     r0,=mmSampleBank                // ID# otherwise
+       ldr     r0, [r0]                        //
+       lsl     r1, #2                          //
+       ldr     r1, [r0, r1]                    //
+       lsl     r1, #8                          //
+       lsr     r1, #8                          //
+       beq     .invalid_sample
+       add     r1, #8                          //
+       ldr     r0,=0x2000000                   //
+       add     r1, r0                          //
+1:                                             //
+       str     r1, [r3, #MIXER_CHN_SAMP]       //
+
+       
+       ldrh    r0, [r4, #MM_SFX_RATE]          // set pitch to original * pitch
+       ldrh    r1, [r1, #C_SAMPLEC_DFREQ]      //
+       mul     r1, r0                          //
+       lsr     r1, #10                         //
+       strh    r1, [r3, #MIXER_CHN_FREQ]       //
+       
+       mov     r1, #0                          // clear sample offset
+       str     r1, [r3, #MIXER_CHN_READ]       //
+       
+       ldrb    r1, [r4, #MM_SFX_PANNING]       // set panning + startbit
+       lsr     r1, #1                          //
+       add     r1, #0x80                       //
+       strb    r1, [r3, #MIXER_CHN_CNT]        //
+       
+       ldrb    r1, [r4, #MM_SFX_VOLUME]        // set volume
+       ldr     r0,=mm_sfx_mastervolume
+       ldr     r0, [r0]
+       mul     r1, r0
+//     lsr     r0, #10
+       lsr     r2, r1, #2
+//     lsl     r2, r1, #8
+
+       strh    r2, [r3, #MIXER_CHN_VOL]        //
+       
+       b       .valid_sample
+.invalid_sample:
+       mov     r0, #0
+       str     r0, [r3, #MIXER_CHN_SAMP]
+       
+.valid_sample:
+       
+#endif
+
+       mov     r0, r5                          // return handle
+       pop     {r4-r6}                         //
+       ret1                                    //
+       
+.no_available_channels:        
+       mov     r0, #0                          // return bad
+       pop     {r4-r6}                         //
+       ret1                                    //
+
+
+
+/***********************************************************************
+ * mme_get_channel_index
+ *
+ * Test handle and return mixing channel index
+ ***********************************************************************/
+                                               .thumb_func
+mme_get_channel_index:
+
+       lsl     r1, r0, #24                     // mask and test channel#
+       lsr     r1, #24-1                       //
+       cmp     r1, #0                          //
+       beq     .invalid_handle                 //
+       cmp     r1, #channelCount*2             //
+       bgt     .invalid_handle                 //
+       
+       ldr     r2,=mm_sfx_channels-2           // check if instances match
+       ldrh    r3, [r2, r1]                    //
+       lsr     r1, r3, #8                      //
+       lsr     r2, r0, #8                      //
+       cmp     r1, r2                          //
+       bne     .invalid_handle                 //
+       
+       lsl     r3, #24                         // mask channel#
+       lsr     r3, #24                         //
+       sub     r3, #1                          //
+       bx      lr                              //
+       
+.invalid_handle:                               // return invalid handle
+       mov     r3, #0                          //
+       mvn     r3, r3                          //
+       bx      lr                              //
+
+/***********************************************************************
+ * mmEffectActive( handle )
+ *
+ * Indicates if a sound effect is active or not
+ ***********************************************************************/
+                                               .global mmEffectActive
+                                               .thumb_func
+mmEffectActive:
+       push    {lr}                            //
+       bl      mme_get_channel_index   //
+       cmp     r3, #0                                  // if r3 >= 0, it is active
+       bge     .active                                 //
+       mov     r0, #0                                  // return false
+       pop     {r3}                                    //
+       bx      r3                                              //
+       
+.active:                                               // 
+       mov     r0, #1                                  // return true
+       pop     {r3}                                    //
+       bx      r3                                              //
+       
+/***********************************************************************
+ * mme_clear_channel(ch)
+ *
+ * Clear channel entry and bitmask
+ * r3 preserved
+ ***********************************************************************/
+                                               .thumb_func
+mme_clear_channel:
+       mov     r1, #0
+       ldr     r2,=mm_sfx_channels             // clear effect channel
+       lsl     r0, #1                          //
+       strh    r1, [r2, r0]                    //
+       
+       mov     r1, #1                          // clear effect bitmask
+       lsr     r0, #1                          //
+       lsl     r1, r0                          //
+       ldr     r2,=mm_sfx_bitmask              //
+       ldr     r0, [r2, #4]
+       orr     r0, r1
+       str     r0, [r2, #4]
+       ldr     r0, [r2]                        //
+       bic     r0, r1                          //
+       str     r0, [r2]                        //
+       bx      lr
+
+/***********************************************************************
+ * mmEffectVolume( handle, volume )
+ *
+ * Set effect volume
+ *
+ * volume 0..255
+ ***********************************************************************/
+                                               .global mmEffectVolume
+                                               .thumb_func
+mmEffectVolume:
+
+       push    {r1, lr}
+       
+       bl      mme_get_channel_index
+       pop     {r1}
+       bmi     1f
+       
+       ldr     r0,=mm_sfx_mastervolume
+       ldr     r0, [r0]
+       mul     r1, r0
+       
+       #ifdef SYS_NDS
+       
+       lsr     r1, #2
+       
+       #endif
+       
+       #ifdef SYS_GBA
+       
+       lsr     r1, #10
+       
+       #endif
+       
+       mov     r0, r3
+       
+       bl      mmMixerSetVolume
+
+1:     ret0
+
+/***********************************************************************
+ * mmEffectPanning( handle, panning )
+ *
+ * Set effect panning
+ *
+ * panning 0..255
+ ***********************************************************************/ 
+                                               .global mmEffectPanning
+                                               .thumb_func
+mmEffectPanning:
+
+       push    {r1, lr}
+       bl      mme_get_channel_index
+       pop     {r1}
+       bmi     1f
+       
+       mov     r0, r3
+       bl      mmMixerSetPan
+       
+1:     ret0
+
+/***********************************************************************
+ * mmEffectRate( handle, rate )
+ *
+ * Set effect playback rate
+ ***********************************************************************/
+                                               .global mmEffectRate
+                                               .thumb_func
+mmEffectRate:
+       
+       push    {r1, lr}
+       bl      mme_get_channel_index
+       pop     {r1}
+       bmi     1f
+       
+       mov     r0, r3
+       bl      mmMixerSetFreq
+       
+1:     ret0
+
+/***********************************************************************
+ * mmEffectCancel( handle )
+ *
+ * Stop sound effect
+ ***********************************************************************/
+                                               .global mmEffectCancel
+                                               .thumb_func
+mmEffectCancel:
+       
+       push    {r0, lr}
+       
+       bl      mme_get_channel_index
+       
+       pop     {r0}
+       
+       bmi     1f
+       
+       mov     r1, #MCA_SIZE                   // free achannel
+       mul     r1, r3                          //
+       ldr     r2,=mm_achannels                //
+       ldr     r2, [r2]                        //
+       add     r2, r1                          //
+       mov     r1, #ACHN_BACKGROUND            //
+       strb    r1, [r2, #MCA_TYPE]             //
+       mov     r1, #0                          //
+       strb    r1, [r2, #MCA_FVOL]             // clear volume for channel allocator
+       
+       lsl     r0, #24
+       lsr     r0, #24
+       sub     r0, #1
+       bl      mme_clear_channel
+       
+       mov     r1, #0                          // zero voice volume
+       mov     r0, r3                          //
+       bl      mmMixerSetVolume                //
+       
+       mov     r0, #1
+       ret1
+1:     
+       mov     r0, #0
+       ret1
+
+/***********************************************************************
+ * mmEffectRelease( channel )
+ *
+ * Release sound effect (allow interruption)
+ ***********************************************************************/
+                                               .global mmEffectRelease
+                                               .thumb_func
+mmEffectRelease:
+
+       push    {r0, lr}
+       
+       bl      mme_get_channel_index
+       pop     {r0}
+       
+       bmi     1f
+       
+       mov     r1, #MCA_SIZE                   // release achannel
+       mul     r1, r3                          //
+       ldr     r2,=mm_achannels                //
+       ldr     r2, [r2]                        //
+       add     r2, r1                          //
+       mov     r1, #ACHN_BACKGROUND            //
+       strb    r1, [r2, #MCA_TYPE]             //
+       
+       lsl     r0, #24
+       lsr     r0, #24
+       sub     r0, #1
+       bl      mme_clear_channel
+       
+1:     ret0
+
+/***********************************************************************
+ * mmEffectScaleRate( channel, factor )
+ *
+ * Scale sampling rate by 6.10 factor
+ ***********************************************************************/
+                                               .global mmEffectScaleRate
+                                               .thumb_func
+mmEffectScaleRate:
+       
+       push    {r1,lr}
+       
+       bl      mme_get_channel_index
+       pop     {r1}
+       
+       bmi     1f
+       
+       mov     r0, r3
+       bl      mmMixerMulFreq
+       
+1:     ret0
+
+/***********************************************************************
+ * mmSetEffectsVolume( volume )
+ *
+ * set master volume scale, 0->1024
+ ***********************************************************************/
+                                               .global mmSetEffectsVolume
+                                               .thumb_func
+mmSetEffectsVolume:
+
+       lsr     r1, r0, #10
+       beq     1f
+       mov     r0, #1
+       lsl     r0, #10
+       
+1:     ldr     r1,=mm_sfx_mastervolume
+       str     r0, [r1]
+       bx      lr
+       
+/***********************************************************************
+ * mmEffectCancelAll()
+ *
+ * Stop all sound effects
+ ***********************************************************************/
+                                               .global mmEffectCancelAll
+                                               .thumb_func
+mmEffectCancelAll:
+
+       push    {r4-r7,lr}
+       
+       ldr     r4,=mm_sfx_bitmask
+       ldr     r4, [r4]
+       ldr     r6,=mm_sfx_channels
+       mov     r5, #0
+       
+       
+       lsr     r4, #1
+       bcc     .mmeca_next
+.mmeca_process:
+
+       ldrb    r7, [r6, r5]
+       sub     r7, #1
+       bmi     .mmeca_next
+       
+       mov     r0, r7
+       mov     r1, #0
+       bl      mmMixerSetVolume
+       
+       ldr     r0,=mm_achannels                // free achannel
+       ldr     r0, [r0]                        //
+       mov     r1, #MCA_SIZE                   //
+       mul     r1, r7                          //
+       add     r0, r1                          //
+       mov     r1, #ACHN_BACKGROUND            //
+       strb    r1, [r0, #MCA_TYPE]             //
+       mov     r1, #0
+       strb    r1, [r0, #MCA_FVOL]             //
+       
+.mmeca_next:
+       add     r5, #2
+       lsr     r4, #1
+       bcs     .mmeca_process
+       bne     .mmeca_next
+       
+       bl      mmResetEffects
+       
+       POP     {r4-r7}
+       pop     {r3}
+       bx      r3
+
+/***********************************************************************
+ * mmUpdateEffects()
+ *
+ * Update sound effects
+ ***********************************************************************/
+                                               .global mmUpdateEffects
+                                               .thumb_func
+mmUpdateEffects:
+       
+       push    {r4-r6,lr}
+       
+       ldr     r4,=mm_sfx_bitmask
+       ldr     r4, [r4]
+       ldr     r6,=mm_sfx_channels
+       mov     r5, #0
+       
+       lsr     r4, #1
+       bcc     .next_channel
+.process_channel:
+
+       ldrb    r0, [r6, r5]                    // get channel index
+       sub     r0, #1                          //
+       bmi     .next_channel                   //
+       
+       GET_MIXCH r1
+       
+       mov     r2, #MIXER_CHN_SIZE             // get mixing channel pointer
+       mul     r2, r0                          //
+       add     r1, r2                          //
+       
+       #ifdef SYS_NDS                          // test if channel is still active
+                                               //
+       ldr     r2, [r1, #MIXER_CHN_SAMP]       //
+       lsl     r2, #8                          //
+       bne     .next_channel                   //
+                                               //
+       #else                                   //
+                                               //
+       ldr     r2, [r1, #MIXER_CHN_SRC]        //
+       asr     r2, #31                         //
+       beq     .next_channel                   //
+                                               //
+       #endif                                  //
+       
+       ldr     r1,=mm_achannels                // free achannel
+       ldr     r1, [r1]                        //
+       mov     r2, #MCA_SIZE                   //
+       mul     r2, r0                          //
+       add     r1, r2                          //
+       mov     r0, #0                          //
+       strb    r0, [r1, #MCA_TYPE]             //
+       strb    r0, [r1, #MCA_FLAGS]            //
+       strb    r0, [r6, r5]
+       
+.next_channel:
+       add     r5, #2                          // look for another set bit
+       lsr     r4, #1                          //
+       bcs     .process_channel                //
+       add     r5, #2                          //
+       lsr     r4, #1                          //
+       bcs     .process_channel                //
+       bne     .next_channel                   //
+       
+       mov     r4, #0
+       mov     r5, #1
+       lsl     r5, #32-channelCount
+       ldr     r6,=mm_sfx_channels
+        
+.build_new_bitmask:
+       ldrb    r0, [r6]
+       add     r6, #2
+       cmp     r0, #0
+       beq     1f
+       orr     r4, r5
+1:     lsl     r5, #1
+       bne     .build_new_bitmask
+       
+       lsr     r4, #32-channelCount
+       ldr     r0,=mm_sfx_bitmask
+       ldr     r1, [r0]                        // r1 = bits that change from 1->0
+       mov     r2, r1
+       eor     r1, r4
+       and     r1, r2
+
+       str     r4, [r0]
+       ldr     r4, [r0, #4]
+       orr     r4, r1
+       str     r4, [r0, #4]                    // write 1->0 mask
+       
+       pop     {r4-r6,pc}
+
+.pool
+
+.end
diff --git a/libs/maxmod/mm_init_default.S b/libs/maxmod/mm_init_default.S
new file mode 100644 (file)
index 0000000..d5b3cd0
--- /dev/null
@@ -0,0 +1,97 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *               Nintendo DS & Gameboy Advance Sound System                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+.global mmInitDefault
+
+@----------------------------------------------------------------------------
+
+#include "mp_defs.inc"
+
+.equ mixlen, 1056      // 16khz
+
+       .bss
+       .align 2
+
+__mixbuffer:
+       .space mixlen
+
+       .text
+       .thumb
+       .align 2
+
+#define MM_SIZEOF_MODCH                40
+#define MM_SIZEOF_ACTCH                28
+#define MM_SIZEOF_MIXCH                24
+       
+/****************************************************************************
+ * mmInitDefault( soundbank, #channels )
+ *
+ * Init maxmod with default settings.
+ ****************************************************************************/
+                                               .thumb_func
+mmInitDefault:
+       
+       push    {r0,r4,r5,r6,r7,lr}             // preserve regs, push soundbank
+       
+       //0 mode (3)
+       //1 mchcount (#channels)
+       //2 achcount (#channels)
+       //3 modch
+       //4 actch
+       //5 mixch
+       //6 mixmem (__mixbuffer)
+       //7 wavemem
+       
+       mov     r6, r1                          // r6=#channels
+       ldr     r0,=MM_SIZEOF_MODCH+MM_SIZEOF_ACTCH+MM_SIZEOF_MIXCH
+       mul     r0, r6
+       ldr     r4,=mixlen
+       add     r0, r4
+       bl      malloc
+       
+       mov     r7, r0                          // wavemem = beginning of buffer
+       add     r3, r0, r4                      // split up buffer into addresses [r3,r4,r5]
+       mov     r0, #MM_SIZEOF_MODCH            //
+       mul     r0, r6                          //
+       add     r4, r3, r0                      //
+       mov     r0, #MM_SIZEOF_ACTCH            //
+       mul     r0, r6                          //
+       add     r5, r4, r0                      //
+       mov     r0, #3                          //
+       mov     r1, r6                          //
+       mov     r2, r6                          //
+       ldr     r6,=__mixbuffer                 // r6 = mixbuffer (iwram)
+       
+       push    {r0-r7}
+       
+       mov     r0, sp                          // init maxmod, pass init struct
+       bl      mmInit                          //
+       
+       add     sp, #MM_GBA_SYSTEM_SIZE         // restore stack
+       
+       pop     {r4-r7}                         // return
+       pop     {r0}                            //
+       bx      r0                              //
+       
+.pool
diff --git a/libs/maxmod/mm_main.S b/libs/maxmod/mm_main.S
new file mode 100644 (file)
index 0000000..1f34067
--- /dev/null
@@ -0,0 +1,802 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+/******************************************************************************
+ *
+ * Definitions
+ *
+ ******************************************************************************/
+
+#include "mp_defs.inc"
+#include "mp_mas.inc"
+#include "mp_mas_structs.inc"
+#include "mp_macros.inc"
+
+#ifdef SYS_GBA
+#include "mp_mixer_gba.inc"
+#endif
+
+#ifdef SYS_NDS
+#include "mp_mixer_ds.inc"
+#endif
+
+/******************************************************************************
+ *
+ * Memory
+ *
+ ******************************************************************************/
+
+       .BSS
+       .ALIGN 2
+
+/******************************************************************************
+ * mmCallback
+ *
+ * Function pointer to user event handler
+ ******************************************************************************/
+                                       .global mmCallback
+mmCallback:    .space 4
+
+/******************************************************************************
+ * mmModuleCount
+ *
+ * Number of modules in soundbank
+ ******************************************************************************/
+                                       .global mmModuleCount
+mmModuleCount: .space 4
+
+/******************************************************************************
+ * mmModuleBank
+ *
+ * Address of module bank
+ ******************************************************************************/
+                                       .global mmModuleBank
+mmModuleBank:  .space 4
+
+/******************************************************************************
+ * mmSampleBank
+ *
+ * Address of sample bank
+ ******************************************************************************/
+                                       .global mmSampleBank
+mmSampleBank:  .space 4
+
+/******************************************************************************
+ * mm_ch_mask
+ *
+ * Bitmask to select which hardware/software channels can be used
+ ******************************************************************************/
+                                       .global mm_ch_mask
+mm_ch_mask:    .space 4
+
+/******************************************************************************
+ * mm_vblank_function
+ *
+ * Pointer to a user function to be called during the vblank irq
+ ******************************************************************************/
+                                       .global mm_vblank_function
+mm_vblank_function:    .space 4
+
+/******************************************************************************
+ * mm_rds_pchannels, mm_rds_achannels
+ *
+ * Memory for module/active channels for NDS system
+ ******************************************************************************/
+#ifdef SYS_NDS
+mm_rds_pchannels:      .space MCH_SIZE*32
+mm_rds_achannels:      .space MCA_SIZE*32
+#endif
+
+/******************************************************************************
+ * mmInitialized
+ *
+ * Variable that will be 'true' if we are ready for playback
+ ******************************************************************************/
+                                       .global mmInitialized
+mmInitialized:         .space 1
+
+
+
+
+/******************************************************************************
+ *
+ * Program
+ *
+ ******************************************************************************/
+
+
+
+
+/******************************************************************************
+ * mpp_call_*
+ *
+ * Functions to branch to a register
+ ******************************************************************************/
+
+#ifdef SYS_GBA
+.section ".iwram", "ax", %progbits
+.thumb
+.align 2
+
+.global mpp_call_r7i, mpp_call_r2i, mpp_call_r1i
+
+       .thumb_func
+//-----------------------------------------------------------------------------
+mpp_call_r7i: bx r7
+//-----------------------------------------------------------------------------
+
+       .thumb_func
+//-----------------------------------------------------------------------------
+mpp_call_r2i: bx r2
+//-----------------------------------------------------------------------------
+
+       .thumb_func
+//-----------------------------------------------------------------------------
+mpp_call_r1i: bx r1
+//-----------------------------------------------------------------------------
+
+#endif
+
+//-----------------------------------------------------------------------------
+       .TEXT
+       .THUMB
+       .ALIGN 2
+//-----------------------------------------------------------------------------
+
+.global mpp_call_r7, mpp_call_r1, mpp_call_r2, mpp_call_r3
+
+       .thumb_func
+@------------------------------------------------------------------------------
+mpp_call_r7: bx r7
+@------------------------------------------------------------------------------
+
+       .thumb_func
+@------------------------------------------------------------------------------
+mpp_call_r1: bx r1
+@------------------------------------------------------------------------------
+
+       .thumb_func
+@------------------------------------------------------------------------------
+mpp_call_r2: bx r2
+@------------------------------------------------------------------------------
+       
+       .thumb_func
+@------------------------------------------------------------------------------
+mpp_call_r3: bx r3
+@------------------------------------------------------------------------------
+
+/******************************************************************************
+ * mmSetEventHandler
+ *
+ * Set function for handling playback events
+ ******************************************************************************/
+                                               .global mmSetEventHandler
+                                               .thumb_func
+mmSetEventHandler:
+
+       ldr     r1,=mmCallback
+       str     r0, [r1]
+       bx      lr
+
+#ifdef SYS_NDS
+/******************************************************************************
+ * mmLockChannels( mask )
+ *
+ * Lock audio channels to prevent the sequencer from using them.
+ ******************************************************************************/
+                                               .global mmLockChannels
+                                               .thumb_func
+mmLockChannels:
+       push    {r4, r5, lr}
+       ldr     r1,=mm_ch_mask                  // clear bits
+       ldr     r2, [r1]                        //
+       bic     r2, r0                          //
+       str     r2, [r1]                        //
+       
+       mov     r4, r0
+       mov     r5, #0
+       
+2:     lsr     r4, #1
+       bcc     1f
+       mov     r0, r5
+       bl      StopActiveChannel
+1:     add     r5, #1
+       cmp     r4, #0
+       bne     2b
+       
+       pop     {r4,r5}
+       pop     {r3}
+       bx      r3
+       
+/******************************************************************************
+ * StopActiveChannel( index )
+ *
+ * Stop active channel
+ ******************************************************************************/
+                                               .thumb_func
+StopActiveChannel:
+       push    {r4}
+       
+       GET_MIXCH r1                            // stop mixing channel
+       mov     r2, #MIXER_CHN_SIZE             //
+       mul     r2, r0                          //
+       add     r1, r2                          //
+                                               //
+                                               //
+       #ifdef SYS_GBA                          //
+                                               //
+       mov     r2, #0                          //
+       sub     r2, #1                          //
+       str     r2, [r1, #MIXER_CHN_SRC]        //
+                                               //
+       #endif                                  //
+                                               //
+       #ifdef SYS_NDS                          //
+                                               //
+       mov     r2, #0                          //
+       str     r2, [r1, #MIXER_CHN_SAMP]       //
+       strh    r2, [r1, #MIXER_CHN_CVOL]       //
+       strh    r2, [r1, #MIXER_CHN_VOL]        //
+                                               //
+       #endif                                  //
+       
+       ldr     r1,=0x4000400                   // stop hardware channel
+       lsl     r2, r0, #4                      //
+       mov     r3, #0                          //
+       str     r3, [r1, r2]                    //
+       
+       ldr     r1,=mm_achannels                // disable achn
+       ldr     r1, [r1]                        //
+       mov     r2, #MCA_SIZE                   //
+       mul     r2, r0                          //
+       add     r1, r2                          //
+       mov     r2, #0                          //
+       ldrb    r4, [r1, #MCA_FLAGS]            //
+       strb    r2, [r1, #MCA_FLAGS]            //
+       strb    r2, [r1, #MCA_TYPE]             //
+       
+       lsr     r1, r4, #8
+       bcs     .iseffect
+       
+       lsr     r4, #7
+       bcs     .issub
+       
+       ldr     r1,=mm_pchannels                // stop hooked pchannel
+       ldr     r1, [r1]                        //
+       ldr     r2,=mm_num_mch                  //
+       ldr     r2, [r2]                        //
+                                               //
+2:     ldrb    r3, [r1, #MCH_ALLOC]            //
+       cmp     r3, r0                          //
+       bne     1f                              //
+       mov     r3, #255                        //
+       strb    r3, [r1, #MCH_ALLOC]            //
+       b       .iseffect                       //
+1:     sub     r2, #1                          //
+       bne     2b                              //
+       
+       b       .iseffect                       //
+       
+.issub:
+       // stop sub pchannel
+       ldr     r1,=mm_schannels
+       mov     r2, #4
+       
+2:     ldrb    r3, [r1, #MCH_ALLOC]            //
+       cmp     r3, r0                          //
+       bne     1f                              //
+       mov     r3, #255                        //
+       strb    r3, [r1, #MCH_ALLOC]            //
+       b       .iseffect                       //
+1:     sub     r2, #1                          //
+       bne     2b                              //
+       
+.iseffect:
+
+       // hope it works out for effects...
+       
+       pop     {r4}
+       bx      lr
+       
+
+/******************************************************************************
+ * mmUnlockChannels( mask )
+ *
+ * Unlock audio channels so they can be used by the sequencer.
+ ******************************************************************************/
+                                               .global mmUnlockChannels
+                                               .thumb_func
+mmUnlockChannels:
+
+#ifdef SYS_NDS
+       ldr     r1,=mm_mixing_mode              // can NOT unlock channels in mode b
+       ldrb    r1, [r1]                        //
+       cmp     r1, #1                          //
+       beq     1f                              //
+#endif
+       
+       ldr     r1,=mm_ch_mask
+       ldr     r2, [r1]
+       orr     r2, r0
+       str     r2, [r1]
+1:     bx      lr
+#endif
+
+
+
+/******************************************************************************
+ *
+ * GBA
+ *
+ ******************************************************************************/
+
+
+
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_GBA
+//-----------------------------------------------------------------------------
+
+       .BSS
+       .ALIGN 2
+
+/******************************************************************************
+ * mp_solution
+ *
+ * Address of soundbank in memory/rom
+ ******************************************************************************/
+                                               .global mp_solution
+mp_solution:   .space 4
+
+       .TEXT
+       .THUMB
+       .ALIGN 2
+       
+/******************************************************************************
+ * mmInit(system)
+ *
+ * Initialize maxmod
+ ******************************************************************************/
+                                               .global mmInit
+                                               .thumb_func
+mmInit:
+       push    {lr}
+       
+       ldr     r2,=mp_solution
+       mov     r1, #MM_GBA_SYSTEM_SOUNDBANK
+       ldr     r1, [r0,r1]
+       str     r1, [r2]
+
+       ldr     r2,=mm_achannels
+       ldr     r1, [r0,#MM_GBA_SYSTEM_ACTCH]
+       str     r1, [r2]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_MODCH]
+       str     r1, [r2,#4]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_MCH_COUNT]
+       str     r1, [r2,#8]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_ACH_COUNT]
+       str     r1, [r2,#12]
+       
+       bl      mmMixerInit     @ initialize software/hardware mixer
+
+       ldr     r1,=mm_num_ach
+       ldr     r1,[r1]
+       mov     r0,#1
+       lsl     r0, r1
+       sub     r0,#1
+       
+       ldr     r1,=mm_ch_mask
+       str     r0, [r1]
+       
+       ldr     r0,=0x400                       //
+       bl      mmSetModuleVolume
+       ldr     r0,=0x400                       //
+       bl      mmSetJingleVolume       
+       ldr     r0,=0x400                       //
+       bl      mmSetEffectsVolume              //
+       
+       
+       ldr     r0,=0x400
+       bl      mmSetModuleTempo
+       
+       ldr     r0,=0x400
+       bl      mmSetModulePitch
+       
+       bl      mmResetEffects
+       
+       ret0
+
+/******************************************************************************
+ * mmSetVBlankHandler
+ *
+ * Set function to be called during the vblank IRQ
+ ******************************************************************************/
+                                               .global mmSetVBlankHandler
+                                               .thumb_func
+mmSetVBlankHandler:
+
+       ldr     r1,=mm_vblank_function
+       str     r0, [r1]
+       bx      lr
+
+/******************************************************************************
+ * mmFrame()
+ *
+ * Work routine, user _must_ call this every frame.
+ ******************************************************************************/
+                                               .global mmFrame
+                                               .thumb_func
+mmFrame:
+       
+       push    {lr}
+       push    {r4-r7}
+
+@ update effects
+       
+       ldr     r7,=mmUpdateEffects
+       bl      _call_via_r7
+       
+@ update sub layer
+@ sub layer has 60hz accuracy
+       
+       ldr     r7,=mppUpdateSub
+       bl      _call_via_r7
+       
+@ update main layer and mix samples.
+@ main layer is sample-accurate.
+
+       ldr     r0,=mpp_channels        @ copy channels
+       ldr     r1,=mm_pchannels
+       ldr     r1,[r1]
+       str     r1, [r0]
+       ldr     r0,=mpp_nchannels       @ copy #channels
+       ldr     r1,=mm_num_mch
+       ldr     r1,[r1]
+       strb    r1, [r0] 
+       ldr     r0,=mpp_clayer          @ layer=0 (main)
+       mov     r1, #0
+       strb    r1, [r0]
+
+       ldr     r0,=mmLayerMain @mpp_layerA             @ copy layer pointer
+       ldr     r1,=mpp_layerp
+       str     r0, [r1]
+       
+       ldr     r4,=mm_mixlen
+       ldr     r4,[r4]
+                                       @ mixlen is divisible by 2
+
+       ldrb    r1, [r0, #MPL_ISPLAYING]        @ check if main layer is active
+       cmp     r1, #0
+       beq     .mpf_no_mainlayer       @ skip processing if disabled (and just mix samples)
+       
+.mpf_mix_advr:
+       
+       ldr     r0,=mpp_layerp          @ get layer
+       ldr     r0, [r0]
+       
+       mov     r1, #MPL_TICKRATE       @ get samples/tick
+       ldrh    r5, [r0, r1]
+       
+       mov     r1, #MPL_SAMPCOUNT      @ get sample count
+       ldrh    r6, [r0,r1]
+       
+       sub     r5, r6                  @ calc tickrate-counter
+       cmp     r5, #0
+       bge     1f
+       mov     r5, #0
+1:     cmp     r5, r4                  @ > mixlen?
+       blt     .mpf_mix_adv            @ no, mix and process tick
+       b       .mpf_mix                @ yes, mix the rest of samples
+       
+.mpf_mix_adv:
+       
+       mov     r1, #MPL_SAMPCOUNT      @ reset sample counter
+       mov     r7, #0                  @
+       strh    r7, [r0,r1]             @
+       sub     r4, r5                  @ subtract from #samples to mix
+       
+       PROF_START
+       
+       mov     r0, r5
+       ldr     r7,=mmMixerMix  @ mix samples
+       bl      _call_via_r7
+       
+       PROF_END 0
+               
+       ldr     r7,=mppProcessTick
+       bl      _call_via_r7
+
+       b       .mpf_mix_advr   @ process more samples
+.mpf_mix:
+
+@ add samples remaining to SAMPCOUNT
+@ and mix more samples
+       
+       mov     r1, #MPL_SAMPCOUNT
+       add     r6, r4
+       strh    r6, [r0, r1]
+       mov     r0, r4
+       PROF_START
+       ldr     r1,=mmMixerMix
+       bl      _call_via_r1
+       PROF_END 0
+
+       pop     {r4-r7}
+       ret1                    @ return to user
+
+.mpf_no_mainlayer:
+
+@ main layer isn't active,
+@ mix full amount
+
+       mov     r0, r4
+       
+       PROF_START
+       ldr     r1,=mmMixerMix
+       bl      _call_via_r1
+       PROF_END 0
+
+       pop     {r4-r7}
+       ret1
+
+.pool
+
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+
+
+/******************************************************************************
+ *
+ * NDS
+ *
+ ******************************************************************************/
+
+
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_NDS
+//-----------------------------------------------------------------------------
+
+       .TEXT
+       .THUMB
+       .ALIGN 2
+
+/******************************************************************************
+ * mmSuspendIRQ_t
+ *
+ * Function to disable interrupts via the status register
+ ******************************************************************************/
+                                               .global mmSuspendIRQ_t
+                                               .thumb_func
+mmSuspendIRQ_t:
+       ldr     r0,=1f
+       bx      r0
+
+.arm
+.align 2
+1:     mrs     r0, cpsr
+       and     r1, r0, #0x80
+       orr     r0, #0x80
+       msr     cpsr, r0
+       str     r1, previous_irq_state
+       bx      lr
+.thumb
+
+/******************************************************************************
+ * mmRestoreIRQ_t
+ *
+ * Function to enable interrupts via the status register
+ ******************************************************************************/       
+                                               .global mmRestoreIRQ_t
+                                               .thumb_func
+mmRestoreIRQ_t:
+       ldr     r0,=1f
+       bx      r0
+
+.arm
+.align 2
+1:     mrs     r0, cpsr
+       ldr     r1, previous_irq_state
+       bic     r0, #0x80
+       orr     r0, r1
+       msr     cpsr, r0
+       bx      lr
+       
+.thumb
+
+previous_irq_state:
+       .space  4
+
+       .thumb_func
+/******************************************************************************
+ * mmIsInitialized()
+ *
+ * Returns true if the system is ready for playback
+ ******************************************************************************/
+                                               .global mmIsInitialized
+                                               .thumb_func
+mmIsInitialized:
+       ldr     r0,=mmInitialized
+       ldrb    r0, [r0]
+       bx      lr
+       
+/******************************************************************************
+ * mmInit7()
+ *
+ * Initialize system
+ ******************************************************************************/
+                                               .global mmInit7
+                                               .thumb_func
+mmInit7:
+       push    {lr}
+       mov     r0, #0x08
+       ldr     r1,=mmFrame
+       bl      irqSet
+       
+       mov     r0, #0x08
+       bl      irqEnable
+       
+       ldr     r0,=0x400                       // set volumes
+       bl      mmSetModuleVolume               //
+       ldr     r0,=0x400                       //
+       bl      mmSetJingleVolume               //
+       ldr     r0,=0x400                       //
+       bl      mmSetEffectsVolume              //
+       
+       ldr     r0,=mmInitialized               // set initialized flag
+       mov     r1, #42                         //
+       strb    r1, [r0]                        //
+       
+       ldr     r0,=0xFFFF                      // select all hardware channels
+       bl      mmUnlockChannels                //
+       
+       ldr     r0,=mm_achannels                // setup channel addresses 
+       ldr     r1,=mm_rds_achannels            //
+       str     r1, [r0]                        //
+       ldr     r1,=mm_rds_pchannels            //
+       str     r1, [r0,#4]                     //
+       mov     r1, #32                         // 32 channels
+       str     r1, [r0,#8]                     //
+       str     r1, [r0,#12]                    //
+       
+       ldr     r0,=0x400
+       bl      mmSetModuleTempo
+       
+       ldr     r0,=0x400
+       bl      mmSetModulePitch
+       
+       bl      mmResetEffects
+       
+       bl      mmMixerInit                     // setup mixer
+       
+       ldr     r0,=mmEventForwarder            // forward events
+       bl      mmSetEventHandler
+       
+       ldr     r0,=mmInitialized               // set initialized flag
+       mov     r1, #42                         //
+       strb    r1, [r0]                        //
+       
+.exit_r3:
+       pop     {r3}
+       bx      r3
+
+/******************************************************************************
+ * mmInstall( channel )
+ *
+ * Install ARM7 system
+ ******************************************************************************/
+                                               .global mmInstall
+                                               .thumb_func
+mmInstall:
+       push    {lr}
+       
+       ldr     r1,=mmInitialized               // not initialized until we get soundbank data
+       mov     r2, #0                          //
+       strb    r2, [r1]                        //
+       
+       bl      mmSetupComms                    // setup communication
+       
+       b       .exit_r3
+
+/******************************************************************************
+ * mmEventForwarder( msg, param )
+ *
+ * Forward event to arm9
+ ******************************************************************************/
+                                               .thumb_func
+mmEventForwarder:
+       
+       push    {lr}
+       lsl     r1, #8
+       orr     r0, r1
+       mov     r1, #1
+       lsl     r1, #20
+       orr     r0, r1
+       bl      mmARM9msg
+       pop     {pc}
+       
+/******************************************************************************
+ * mmGetSoundBank( n_songs, bank )
+ *
+ * Load sound bank address
+ ******************************************************************************/
+                                               .global mmGetSoundBank
+                                               .thumb_func
+mmGetSoundBank:
+       ldr     r2,=mmModuleCount               // save data
+       stmia   r2!, {r0,r1}                    //
+       
+       lsl     r0, #2                          // also sample bank address
+       add     r1, r0                          //      
+       stmia   r2!, {r1}                       //
+       
+//------------------------------------------------
+// initialize system
+//------------------------------------------------
+
+       b       mmInit7
+
+/******************************************************************************
+ * mmFrame()
+ *
+ * Routine function
+ ******************************************************************************/
+                                               .global mmFrame
+                                               .thumb_func
+mmFrame:
+       
+       push    {lr}
+       
+       ldr     r0,=mmInitialized       // catch not-initialized
+       ldrb    r0, [r0]                //
+       cmp     r0, #42                 //
+       bne     1f                      //
+
+       bl      mmMixerPre              // <-- critical timing
+       
+       ldr     r0,=0x4000208           // enable irq
+       mov     r1, #1                  //
+       strh    r1, [r0]                //
+       
+       bl      mmUpdateEffects         // update sound effects
+       bl      mmPulse                 // update module playback
+       bl      mmMixerMix              // update audio
+       
+       
+       bl      mmSendUpdateToARM9
+       
+1:     bl      mmProcessComms          // process communications
+       
+       ret1
+       
+.pool
+
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+.end
+//-----------------------------------------------------------------------------
+
diff --git a/libs/maxmod/mm_mas.S b/libs/maxmod/mm_mas.S
new file mode 100644 (file)
index 0000000..9cc9c9e
--- /dev/null
@@ -0,0 +1,4960 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                            Module Processing                             *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#include "mp_format_mas.inc"
+#include "mp_mas_structs.inc"
+#include "mp_defs.inc"
+#include "mp_macros.inc"
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_GBA
+//-----------------------------------------------------------------------------
+
+#include "mp_mixer_gba.inc"
+#include "swi_gba.inc"
+
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_NDS
+//-----------------------------------------------------------------------------
+#include "mp_mixer_ds.inc"
+#include "swi_nds.inc"
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+
+.equ   S3M_FREQ_DIVIDER        ,57268224       // (s3m,xm,it)
+.equ   MOD_FREQ_DIVIDER_PAL    ,56750314       // (mod)
+.equ   MOD_FREQ_DIVIDER_NTSC   ,57272724       // (---)
+
+
+
+/******************************************************************************
+ *
+ * Memory
+ *
+ ******************************************************************************/
+
+
+
+       .BSS
+       .ALIGN 2
+
+/******************************************************************************
+ * mmLayerMain
+ *
+ * Layer data for module playback.
+ ******************************************************************************/
+                                                       .global mmLayerMain
+mmLayerMain:           .space MPL_SIZE
+
+/******************************************************************************
+ * mmLayerSub
+ *
+ * Layer data for jingle playback.
+ ******************************************************************************/
+                                                       .global mmLayerSub
+mmLayerSub:            .space MPL_SIZE
+
+/******************************************************************************
+ * mpp_vars
+ *
+ * Holds intermediate data during the module processing.
+ ******************************************************************************/
+                                                       .global mpp_vars
+mpp_vars:              .space MPV_SIZE
+
+       .ALIGN 2
+
+/******************************************************************************
+ * mpp_layerp
+ *
+ * Pointer to layer data during processing.
+ ******************************************************************************/
+                                                       .global mpp_layerp
+mpp_layerp:            .space 4
+
+/******************************************************************************
+ * mpp_channels
+ *
+ * Pointer to channel array during processing
+ ******************************************************************************/
+                                                       .global mpp_channels
+mpp_channels:          .space 4
+
+/******************************************************************************
+ * mpp_resolution
+ *
+ * Speed divider for DS timing.
+ ******************************************************************************/
+mpp_resolution:                .space 4
+
+/******************************************************************************
+ * mm_mastertempo
+ *
+ * Master tempo scaler.
+ ******************************************************************************/
+mm_mastertempo:                .space 4
+
+/******************************************************************************
+ * mm_masterpitch
+ * 
+ * Master pitch scaler.
+ ******************************************************************************/
+mm_masterpitch:                .space 4
+
+/******************************************************************************
+ * mpp_nchannels
+ *
+ * Number of channels in layer
+ ******************************************************************************/
+                                                       .global mpp_nchannels
+mpp_nchannels:         .space 1
+
+/******************************************************************************
+ * mpp_clayer
+ *
+ * Layer selection, 0 = main, 1 = sub
+ ******************************************************************************/
+                                                       .global mpp_clayer
+mpp_clayer:            .space 1
+
+/******************************************************************************
+ * mm_achannels, mm_pchannels, mm_num_mch, mm_num_ach, mm_schannels
+ *
+ * Channel data/sizes, don't move these around--see mmInit first
+ ******************************************************************************/
+                                                       .ALIGN 2
+                                                       
+.global mm_achannels, mm_pchannels, mm_num_mch, mm_num_ach, mm_schannels
+                                               
+mm_achannels:          .space 4
+mm_pchannels:          .space 4
+mm_num_mch:            .space 4
+mm_num_ach:            .space 4
+mm_schannels:          .space MP_SCHANNELS*MCH_SIZE
+
+
+
+/******************************************************************************
+ * 
+ * Macros
+ *
+ ******************************************************************************/
+
+
+/******************************************************************************
+ * mpp_InstrumentPointer
+ *
+ * Calculate instrument address.
+ * Requires r8 = layer
+ * Returns in r0
+ * Trashes r1, r2
+ ******************************************************************************/
+.macro mpp_InstrumentPointer
+       mov     r1, r8  
+       ldr     r2,[r1,#MPL_SONGADR]    
+       ldr     r1,[r1,#MPL_INSTTABLE]  
+       lsl     r0, #2                  
+       ldr     r0,[r1,r0]              
+       add     r0, r2
+.endm
+
+/******************************************************************************
+ * mpp_SamplePointer
+ *
+ * Calculate sample address.
+ * Requires r8 = layer
+ * Returns in r0
+ * Trashes r1, r2
+ ******************************************************************************/
+.macro mpp_SamplePointer       
+       mov     r1, r8          
+       ldr     r2,[r1,#MPL_SONGADR]    
+       ldr     r1,[r1,#MPL_SAMPTABLE]  
+       lsl     r0, #2                  
+       ldr     r0,[r1,r0]              
+       add     r0, r2
+.endm
+
+
+
+/******************************************************************************
+ *
+ * Program
+ *
+ ******************************************************************************/
+
+
+
+       .TEXT
+       .ALIGN 2
+
+/******************************************************************************
+ * mpp_resetchannels(...)
+ *
+ * Reset channel data, and any active channels linked to the layer.
+ * Requires r5 = layer, r6 = channels, r7 = #channels
+ ******************************************************************************/
+                                                       .thumb_func
+mpp_resetchannels:
+
+       push    {r4-r6}
+
+       mov     r0, r6                                  // clear channel data to 0
+       mov     r1, #MCH_SIZE/4                         //
+       mul     r1, r7                                  //
+       mov     r2, #0                                  //
+1:     stmia   r0!, {r2}                               //
+       sub     r1, #1                                  //
+       bne     1b                                      //
+       
+       mov     r0, r6                                  // reset channel indexes
+       sub     r0, #MCH_SIZE-MCH_ALLOC                 //
+       mov     r1, #MCH_SIZE                           //
+       mul     r1, r7                                  //
+       mov     r2, #255                                //
+1:     strb    r2, [r0, r1]                            //
+       sub     r1, #MCH_SIZE                           //
+       bne     1b                                      //
+
+       GET_MIXCH r4                                    // reset active channels linked to this layer
+       ldr     r6,=mpp_clayer                          //
+       ldrb    r6, [r6]                                //
+
+#ifdef SYS_GBA                                         // disabled status differs between systems
+       ldr     r5,=1<<31                               //
+#endif                                                 //
+#ifdef SYS_NDS                                         //
+       mov     r5, #0                                  //
+#endif                                                 //
+
+       ldr     r0,=mm_achannels                        // r0 = achannels
+       ldr     r0, [r0]                                //
+       ldr     r1,=mm_num_ach                          // r1 = #achannels
+       ldr     r1, [r1]                                //
+       mov     r2, #0                                  // r2 = 0 (for clearing)
+       
+.mpic_loop3:
+       ldrb    r3, [r0, #MCA_FLAGS]                    // test if layer matches
+       lsr     r3, #6                                  //
+       cmp     r3, r6                                  //
+       bne     .mpic_l3_skip                           //
+
+       mov     r3, #MCA_SIZE-4                         // clear achannel data to zero
+.mpic_loop4:                                           //
+       str     r2, [r0, r3]                            //
+       sub     r3, r3, #4                              //
+       bpl     .mpic_loop4                             //
+
+       str     r5, [r4]                                // disable mixer channel
+       
+.mpic_l3_skip:
+       
+       add     r0, #MCA_SIZE                           // increment stuff and loop
+       add     r4, #MIXER_CHN_SIZE                     //
+                                                       //
+       sub     r1, #1                                  //
+       bne     .mpic_loop3                             //
+       
+       pop     {r4-r6}
+       bx      lr
+
+#ifdef SYS_NDS
+
+/******************************************************************************
+ * mm_reset_channels()
+ *
+ * Reset all channels.
+ ******************************************************************************/
+                                               .global mm_reset_channels
+                                               .thumb_func
+mm_reset_channels:
+
+       push    {lr}
+       
+       ldr     r0,=mm_achannels                // clear active channel data
+       ldr     r0, [r0]                        //
+       mov     r1, #0                          //
+       ldr     r2,=MCA_SIZE*32/4               //
+                                               //
+1:     stmia   r0!, {r1}                       //
+       sub     r2, #1                          //
+       bne     1b                              //
+       
+       ldr     r0,=mm_pchannels                // reset channel allocation
+       ldr     r0, [r0]                        //
+       mov     r1, #255                        //
+       mov     r2, #32                         //
+                                               //
+1:     strb    r1, [r0, #MCH_ALLOC]            //
+       add     r0, #MCH_SIZE                   //
+       sub     r2, #1                          //
+       bne     1b                              //
+       
+       ldr     r0,=mm_schannels                // reset channel allocation
+       mov     r2, #4                          //
+                                               //
+1:     strb    r1, [r0, #MCH_ALLOC]            //
+       add     r0, #MCH_SIZE                   //
+       sub     r2, #1                          //
+       bne     1b                              //
+       
+       bl      mmResetEffects
+       
+       pop     {r3}
+       bx      r3
+       
+#endif
+
+/******************************************************************************
+ * mpp_suspend()
+ *
+ * Suspend main module and associated channels.
+ ******************************************************************************/
+                                               .thumb_func
+mpp_suspend:
+
+       push    {r4,lr}
+       ldr     r0,=mm_achannels
+       ldr     r0, [r0]
+       GET_MIXCH r1
+       ldr     r2,=mm_num_ach
+       ldr     r2, [r2]
+       mov     r4, #0
+.mpps_loop:
+       ldrb    r3, [r0, #MCA_FLAGS]
+       lsr     r3, #6
+       bne     .mpps_next
+#ifdef SYS_GBA 
+       str     r4, [r1, #MIXER_CHN_FREQ]
+#else
+       strh    r4, [r1, #MIXER_CHN_FREQ]
+       strh    r4, [r1, #MIXER_CHN_VOL]
+#endif
+
+.mpps_next:
+       add     r0, #MCA_SIZE
+       add     r1, #MIXER_CHN_SIZE
+       sub     r2, #1
+       bne     .mpps_loop
+       
+       pop     {r4, pc}
+
+/******************************************************************************
+ *
+ * NDS System
+ *
+ ******************************************************************************/
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_NDS
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mmStart( module_ID, mode )
+ *
+ * Start module playback.
+ *
+ * module_ID : Index of module.
+ * mode : Playback mode.
+ ******************************************************************************/
+                                                       .global mmStart
+                                                       .thumb_func
+mmStart:
+       
+       mov     r2, #0
+.mpps_backdoor:
+       lsl     r0, #2
+       ldr     r3,=mmModuleBank
+       ldr     r3, [r3]
+       ldr     r0, [r3, r0]
+       
+       cmp     r0, #0
+       beq     1f
+       add     r0, #8
+       b       mmPlayModule
+1:     bx      lr
+
+/******************************************************************************
+ * mmJingle( module_ID )
+ *
+ * Play module as jingle.
+ *
+ * module_ID : Index of module
+ ******************************************************************************/
+                                                       .global mmJingle
+                                                       .thumb_func
+mmJingle:
+       
+       mov     r2, #1
+       mov     r1, #MPP_PLAY_ONCE
+       b       .mpps_backdoor
+       
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ *
+ * GBA System
+ *
+ ******************************************************************************/
+//-----------------------------------------------------------------------------
+#ifdef SYS_GBA
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mmStart( module_ID, mode )
+ *
+ * Start module playback
+ *
+ * module_ID : id of module
+ * mode : mode of playback
+ ******************************************************************************/
+                                                       .global mmStart
+                                                       .thumb_func
+mmStart:
+
+       mov     r2, #0
+.mpps_backdoor:
+       push    {r2}
+       ldr     r2,=mp_solution         @ resolve song address
+       ldr     r2, [r2]
+       ldrh    r3, [r2, #0]
+       lsl     r3, #2
+       add     r3, #12
+       add     r3, r2
+       lsl     r0, #2
+       add     r0, r3
+       ldr     r0, [r0]
+       add     r0, r2
+       
+       pop     {r2}
+       
+       add     r0, #8
+
+       b       mmPlayModule
+1:     bx      lr
+
+/******************************************************************************
+ * mmJingle( module_ID )
+ *
+ * Start jingle playback
+ *
+ * module_ID : index of module
+ ******************************************************************************/
+                                                       .global mmJingle
+                                                       .thumb_func
+mmJingle:
+
+       mov     r2, #1
+       mov     r1, #MPP_PLAY_ONCE
+       b       .mpps_backdoor
+
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mmPlayModule( address, mode, layer )
+ *
+ * Start playing module.
+ ******************************************************************************/
+                                                       .global mmPlayModule
+                                                       .thumb_func
+mmPlayModule:
+       
+       push    {lr}
+       push    {r4-r7}
+       
+       ldr     r3,=mpp_clayer
+       strb    r2, [r3]
+       
+       cmp     r2, #0
+       bne     1f
+       ldr     r5,=mmLayerMain
+       @ldr    r6,=mpp_pchannels
+       ldr     r6,=mm_pchannels
+       ldr     r6,[r6]
+       @mov    r7, #MP_MCHANNELS
+       ldr     r7,=mm_num_mch
+       ldr     r7,[r7]
+       b       2f
+1:     ldr     r5,=mmLayerSub
+       ldr     r6,=mm_schannels
+       mov     r7, #MP_SCHANNELS
+2:
+//     push    {r2}
+       
+       mov     r2, #MPL_MODE
+       strb    r1, [r5, r2]
+
+       mov     r4, r0
+       str     r4, [r5, #MPL_SONGADR]
+       
+       bl      mpp_resetchannels
+       
+       ldrb    r3, [r4, #C_MAS_INSTN]
+       ldrb    r2, [r4, #C_MAS_SAMPN]
+       lsl     r3, #2
+       lsl     r2, #2
+       
+       mov     r0, r4
+       add     r0, #255
+       add     r0, #C_MAS_TABLES-255
+       
+       str     r0, [r5, #MPL_INSTTABLE]        @ setup instrument table
+       add     r0, r3
+       str     r0, [r5, #MPL_SAMPTABLE]        @ setup sample table
+       add     r0, r2
+       str     r0, [r5, #MPL_PATTTABLE]        @ setup pattern table
+
+       mov     r0, #0          @ set pattern to 0
+       
+       @ldr    r1,=mpp_setposition
+       @bl     mpp_call_r1
+       bl      mpp_setposition
+       
+       ldrb    r0, [r4, #C_MAS_TEMPO]  @ load initial tempo
+       
+       bl      mpp_setbpm
+       
+       ldrb    r0, [r4, #C_MAS_GV]     @ load initial global volume
+       
+
+       strb    r0, [r5, #MPL_GV]
+
+       ldrb    r0, [r4, #C_MAS_FLAGS]  @ read song flags
+       strb    r0, [r5, #MPL_FLAGS]    @ save
+       
+       lsl     r0, #32-2
+       lsr     r0, #32-2+1
+       strb    r0, [r5, #MPL_OLDEFFECTS]
+
+       ldrb    r0, [r4, #C_MAS_SPEED]  @ speed
+       strb    r0, [r5, #MPL_SPEED]            @ and set
+       
+       mov     r0, #1                          // mpp_playing=true
+       strb    r0, [r5, #MPL_ISPLAYING]
+       
+       mov     r1, #MPL_VALID                  // set valid flag
+       strb    r0, [r5, r1]                    //
+
+       bl      mpp_resetvars
+       
+       @ setup channel volumes
+       mov     r0, r6
+       add     r0, #MCH_CVOLUME
+       mov     r3, r7
+       add     r4, #C_MAS_CHANVOL
+.cvol_setup:
+       ldrb    r1, [r4]
+       strb    r1, [r0]
+       add     r0, #MCH_SIZE
+       add     r4, #1
+       sub     r3, #1
+       bne     .cvol_setup
+       
+       add     r4, #32
+       sub     r4, r7
+       
+       mov     r0, r6
+       add     r0, #MCH_PANNING
+       mov     r3, r7
+.cpan_setup:
+       ldrb    r1, [r4]
+       strb    r1, [r0]
+       add     r0, #MCH_SIZE
+       add     r4, #1
+       sub     r3, #1
+       bne     .cpan_setup
+
+//     pop     {r2}            @ <-- FIX.2 WHY WAS THIS PRESERVED
+       
+       pop     {r4-r7}
+       pop     {r0}            @ return
+       
+       bx      r0              @
+.pool
+
+/******************************************************************************
+ * mmPause()
+ *
+ * Pause module playback.
+ ******************************************************************************/
+                                                       .global mmPause
+                                                       .thumb_func
+mmPause:
+       
+       push    {lr}
+       push    {r5}
+       
+       ldr     r5,=mmLayerMain
+       
+       mov     r0, #MPL_VALID
+       ldrb    r0, [r5, r0]
+       cmp     r0, #0
+       beq     1f
+       
+       mov     r0, #0
+       strb    r0, [r5, #MPL_ISPLAYING]
+       
+       bl      mpp_suspend
+       
+1:     pop     {r5}
+       ret0
+
+/******************************************************************************
+ * mmResume()
+ *
+ * Resume module playback.
+ ******************************************************************************/
+                                                       .global mmResume
+                                                       .thumb_func
+mmResume:
+
+       ldr     r1,=mmLayerMain
+       mov     r0, #MPL_VALID
+       ldrb    r0, [r1, r0]
+       cmp     r0, #0
+       beq     1f
+       
+       mov     r0, #1
+       strb    r0, [r1, #MPL_ISPLAYING]
+1:     bx      lr
+
+/******************************************************************************
+ * mmActive()
+ *
+ * Returns true if module is playing.
+ ******************************************************************************/
+                                                       .global mmActive
+                                                       .thumb_func
+mmActive:
+
+       ldr     r0,=mmLayerMain
+       ldrb    r0, [r0, #MPL_ISPLAYING]
+       bx      lr
+
+/******************************************************************************
+ * mmActiveSub()
+ *
+ * Returns true if a jingle is playing.
+ ******************************************************************************/
+                                                       .global mmActiveSub
+                                                       .thumb_func
+mmActiveSub:
+       
+       ldr     r0,=mmLayerSub
+       ldrb    r0, [r0, #MPL_ISPLAYING]
+       bx      lr
+
+/******************************************************************************
+ * mmSetModuleVolume( volume )
+ *
+ * Set master module volume.
+ *
+ * volume : 0->1024
+ ******************************************************************************/
+                                                       .global mmSetModuleVolume
+                                                       .thumb_func
+mmSetModuleVolume:
+
+@ clamp volume 0->1024
+       lsr     r1, r0, #10
+       beq     1f
+       mov     r0, #1
+       lsl     r0, #10
+
+@ set volume
+1:     ldr     r1,=mmLayerMain
+       mov     r2, #MPL_VOLUME
+       strh    r0, [r1, r2]
+       bx      lr
+
+/******************************************************************************
+ * mmSetJingleVolume( volume )
+ *
+ * Set master jingle volume.
+ *
+ * volume : 0->1024
+ ******************************************************************************/
+                                                       .global mmSetJingleVolume
+                                                       .thumb_func
+mmSetJingleVolume:
+
+@ clamp volume 0->1024
+       lsr     r1, r0, #10
+       beq     1f
+       mov     r0, #1
+       lsl     r0, #10
+
+@ set volume
+1:     ldr     r1,=mmLayerSub @mpp_layerB
+       mov     r2, #MPL_VOLUME
+       strh    r0, [r1, r2]
+       bx      lr
+
+/******************************************************************************
+ * mppStop() [[internal function]]
+ *
+ * Stop module playback.
+ ******************************************************************************/
+                                                       .thumb_func
+mppStop:
+
+       push    {lr}
+
+       ldr     r0,=mpp_clayer
+       ldrb    r0, [r0]
+       
+       cmp     r0, #0
+       beq     1f
+       ldr     r5,=mmLayerSub
+       ldr     r6,=mm_schannels
+       mov     r7, #MP_SCHANNELS
+       b       2f
+1:     ldr     r5,=mmLayerMain
+       @ldr    r6,=mpp_pchannels
+       ldr     r6,=mm_pchannels
+       ldr     r6,[r6]
+       @mov    r7, #MP_MCHANNELS
+       ldr     r7,=mm_num_mch
+       ldr     r7,[r7]
+2:
+       
+       mov     r0, #0
+       strb    r0, [r5, #MPL_ISPLAYING]
+       
+       mov     r1, #MPL_VALID
+       strb    r0, [r5, r1]
+       
+       bl      mpp_resetchannels
+       pop     {r0}
+       bx      r0
+
+/******************************************************************************
+ * mmGetPosition()
+ *
+ * Get playback position
+ ******************************************************************************/
+                                                       .global mmGetPosition
+                                                       .thumb_func
+mmGetPosition:
+       
+       ldr     r1,=mmLayerMain
+       ldrb    r0, [r1, #MPL_POSITION]
+       bx      lr
+       
+/******************************************************************************
+ * mmPosition( position )
+ *
+ * Set playback position
+ ******************************************************************************/
+                                                       .global mmPosition
+                                                       .thumb_func
+mmPosition:
+       
+       push    {r4-r7,lr}
+       ldr     r5,=mmLayerMain
+       ldr     r6,=mm_pchannels
+       ldr     r6,[r6]
+       ldr     r7,=mm_num_mch
+       ldr     r7, [r7]
+       
+       push    {r0}
+//     bl      mpp_resetchannels
+       
+       pop     {r0}
+       bl      mpp_setposition
+
+       pop     {r4-r7}
+       pop     {r3}
+       bx      r3
+       
+/******************************************************************************
+ * mmSetModuleTempo( tempo )
+ *
+ * Set master tempo
+ *
+ * tempo : x.10 fixed point tempo, 0.5->2.0
+ ******************************************************************************/
+                                                       .global mmSetModuleTempo
+                                                       .thumb_func
+mmSetModuleTempo:
+       
+       push    {r5,lr}
+       
+       mov     r1, #1                          // clamp value: 512->2048
+       lsl     r1, #11                         //
+       cmp     r0, r1                          //
+       ble     1f                              //
+       mov     r0, r1                          //
+1:     mov     r1, #1                          //
+       lsl     r1, #9                          //
+       cmp     r0, r1                          //
+       bge     1f                              //
+       mov     r0, r1                          //
+1:
+       
+       ldr     r1,=mm_mastertempo
+       str     r0, [r1]
+       
+       ldr     r5,=mmLayerMain
+       ldr     r0,=mpp_clayer
+       mov     r1, #0
+       strb    r1, [r0]
+       
+       ldrb    r0, [r5, #MPL_BPM]
+       cmp     r0, #0
+       beq     1f
+       bl      mpp_setbpm
+       
+1:     pop     {r5}
+       pop     {r3}
+       bx      r3
+       
+/******************************************************************************
+ * mmSetModulePitch( pitch )
+ *
+ * Set master pitch
+ *
+ * pitch : x.10 fixed point value, range = 0.5->2.0
+ ******************************************************************************/
+                                                       .global mmSetModulePitch
+                                                       .thumb_func
+mmSetModulePitch:
+       push    {r5,lr}
+       
+       mov     r1, #1                          // clamp value: 512->2048
+       lsl     r1, #11                         //
+       cmp     r0, r1                          //
+       ble     1f                              //
+       mov     r0, r1                          //
+1:     mov     r1, #1                          //
+       lsl     r1, #9                          //
+       cmp     r0, r1                          //
+       bge     1f                              //
+       mov     r0, r1                          //
+1:
+       
+       ldr     r1,=mm_masterpitch
+       str     r0, [r1]
+       
+1:     pop     {r5}
+       pop     {r3}
+       bx      r3
+       
+.pool
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_NDS7
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mmSetResolution( divider )
+ *
+ * Set update resolution
+ ******************************************************************************/
+                                                       .global mmSetResolution
+                                                       .thumb_func
+mmSetResolution:
+
+       push    {r5, lr}
+       
+       ldr     r1,=mpp_resolution
+       str     r0, [r1]
+       
+       ldr     r5,=mmLayerMain
+       
+       ldr     r0,=mpp_clayer
+       mov     r1, #0
+       strb    r1, [r0]
+       
+       ldrb    r0, [r5, #MPL_BPM]
+       cmp     r0, #0
+       beq     1f
+       bl      mpp_setbpm
+1:     ldr     r5,=mmLayerSub
+
+       ldr     r0,=mpp_clayer
+       mov     r1, #1
+       strb    r1, [r0]
+       
+       ldrb    r0, [r5, #MPL_BPM]
+       cmp     r0, #0
+       beq     1f
+       bl      mpp_setbpm
+1:     pop     {r5}
+       pop     {r3}
+       bx      r3
+
+#endif
+
+/******************************************************************************
+ * mmStop()
+ *
+ * Stop module playback.
+ ******************************************************************************/
+                                                       .global mmStop
+                                                       .thumb_func
+mmStop:
+
+       push    {r4-r7,lr}
+       ldr     r1,=mpp_clayer
+       mov     r0, #0
+       strb    r0, [r1]
+       bl      mppStop
+       pop     {r4-r7}
+       ret3
+
+/******************************************************************************
+ * mpp_resetvars()
+ *
+ * Reset pattern variables
+ * Input r5 = layer
+ ******************************************************************************/
+                                                       .thumb_func
+mpp_resetvars:
+
+       mov     r0, #255
+       strb    r0, [r5, #MPL_PATTJUMP]
+       mov     r0, #0
+       strb    r0, [r5, #MPL_PATTJUMP_ROW]
+       bx      lr
+
+/******************************************************************************
+ * mpp_setbpm( bpm )
+ *
+ * Set BPM. bpm = 32..255
+ * Input r5 = layer
+ ******************************************************************************/
+                                                       .thumb_func
+mpp_setbpm:
+       
+       strb    r0, [r5, #MPL_BPM]
+       
+#ifdef SYS_GBA
+
+       ldr     r1,=mpp_clayer
+       ldrb    r1, [r1]
+       cmp     r1, #0
+       bne     1f
+       
+       ldr     r1,=mm_mastertempo      // multiply by master tempo
+       ldr     r1, [r1]                //
+       mul     r1, r0                  //
+       lsr     r1, #10                 //
+       
+       ldr     r0,=mm_bpmdv            @ samples per tick ~= mixfreq / (bpm/2.5) ~= mixfreq*2.5/bpm
+       ldr     r0,[r0]
+       
+       swi     SWI_DIVIDE              @ SWI 07h, divide r1/r0
+       lsr     r0, #1                  @ multiple of two
+       lsl     r0, #1                  @ ---------------
+       mov     r1, #MPL_TICKRATE
+       strh    r0, [r5, r1]            @ 
+       mov     r1, #MPL_SAMPCOUNT
+//     mov     r0, #0
+//     strh    r0, [r5, r1]
+       bx      lr                      @ return
+       
+1:     @ SUB LAYER, time using vsync (rate = bpm/2.5 / 59.7)
+       
+       lsl     r0, #15
+       mov     r1, #149
+       swi     SWI_DIVIDE
+       mov     r1, #MPL_TICKRATE
+       strh    r0, [r5, r1]
+       bx      lr
+       
+#endif
+
+#ifdef SYS_NDS
+
+@ vsync = ~59.8261 HZ (says GBATEK)
+@ divider = hz * 2.5 * 64
+
+       ldr     r1,=mpp_clayer
+       ldrb    r1, [r1]
+       cmp     r1, #0
+       bne     1f
+       ldr     r1,=mm_mastertempo      // multiply by master tempo
+       ldr     r1, [r1]                //
+       mul     r0, r1                  //
+//     lsr     r1, #10                 //
+
+       lsl     r0, #16+6-10
+       b       2f
+1:
+       lsl     r0, #16+6
+2:
+       @ using 60hz vsync for timing
+//     lsl     r0, #16+6
+       ldr     r1,=mpp_resolution
+       ldr     r1, [r1]
+       swi     SWI_DIVIDE
+       lsr     r0, #1
+       mov     r1, #MPL_TICKRATE
+       strh    r0, [r5, r1]
+       bx      lr
+       
+#endif
+
+.pool
+
+/******************************************************************************
+ * mpp_setposition( position )
+ *
+ * Set sequence position.
+ * Input r5 = layer
+ ******************************************************************************/
+                                                       .thumb_func
+mpp_setposition:
+       
+       push    {lr}
+       
+mpp_setpositionA:
+       
+       strb    r0, [r5, #MPL_POSITION]
+       
+       ldr     r1, [r5, #MPL_SONGADR]
+       mov     r3, r1
+       add     r1, #C_MAS_ORDER        @ get sequence entry
+       ldrb    r1, [r1, r0]            @
+       
+       cmp     r1, #254
+       bne     .mpsp_skippatt
+       add     r0, #1
+       b       mpp_setpositionA
+.mpsp_skippatt:
+       
+       cmp     r1, #255
+       bne     .mpsp_endsong
+       
+       @ END OF SONG!!! WOOPHEE!!!!
+       
+
+       mov     r0, #MPL_MODE @mpp_playmode
+       ldrb    r0, [r5, r0]
+       
+       cmp     r0, #MPP_PLAY_ONCE
+       bge     1f
+@      @ its looping:
+       b       3f
+
+1:     @ its playing once:
+
+       bl      mppStop
+       mov     r0, #MPCB_SONGFINISHED
+       ldr     r2,=mmCallback
+       ldr     r2,[r2]
+       cmp     r2, #0
+       beq     3f
+       ldr     r1,=mpp_clayer
+       ldrb    r1, [r1]
+       bl      mpp_call_r2
+       
+3:     
+       
+       ldrb    r0, [r5, #MPL_ISPLAYING]
+       cmp     r0, #0
+       bne     1f
+
+       pop     {pc}
+
+1:     
+       ldr     r0, [r5, #MPL_SONGADR]  @ set position to 'restart'
+       ldrb    r0, [r0, #C_MAS_REP]
+       b       mpp_setpositionA
+.mpsp_endsong:
+       
+       mov     r0, r1
+       
+       ldr     r1, [r5, #MPL_PATTTABLE]
+       lsl     r0, #2
+       
+       @ r1 = pattern address( in table )
+       
+       ldr     r1, [r1, r0]
+       add     r1, r3          @ add song address
+       
+       @ r1 = pattern address
+       
+       ldrb    r2, [r1]                @ set pattern size
+       strb    r2, [r5, #MPL_NROWS]    @
+       
+       mov     r2, #0                  @ reset tick/row
+       strh    r2, [r5, #MPL_TICK]
+       strb    r2, [r5, #MPL_FPATTDELAY]
+       strb    r2, [r5, #MPL_PATTDELAY]
+
+       mov     r0, #MPL_PATTREAD
+       add     r1, #1
+       str     r1, [r5, r0]            @ store pattern data address
+
+       mov     r0, #MPL_PLOOP_ADR              @ reset pattern loop
+       str     r1, [r5, r0]
+       mov     r0, #0
+       strb    r0, [r5, #MPL_PLOOP_ROW]
+       strb    r0, [r5, #MPL_PLOOP_TIMES]
+       
+       pop     {pc}
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_NDS
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mppUpdateLayer( layer )
+ *
+ * Update module layer
+ ******************************************************************************/
+                                                       .thumb_func
+mppUpdateLayer:
+
+       push    {lr}
+       ldr     r1,=mpp_layerp
+       str     r0, [r1]
+       mov     r1, #MPL_TICKRATE
+       ldrh    r1, [r0, r1]    
+       mov     r2, #MPL_TICKFRAC
+       ldrh    r3, [r0, r2]
+       lsl     r1, #1
+       add     r3, r1
+       strh    r3, [r0, r2]
+       lsr     r3, #16
+       beq     1f
+2:     push    {r3}
+       
+       bl      mppProcessTick
+       pop     {r3}
+       sub     r3, #1
+       bne     2b
+1:     pop     {pc}
+
+/******************************************************************************
+ * mmPulse()
+ *
+ * NDS Work Routine
+ ******************************************************************************/
+                                               .global mmPulse
+                                               .thumb_func
+mmPulse:
+               
+       push    {lr}
+       
+       ldr     r0,=mpp_channels                // update main layer
+       ldr     r1,=mm_pchannels                //
+       ldr     r1,[r1]                         //
+       str     r1, [r0]                        //
+       ldr     r0,=mpp_nchannels               //
+       ldr     r1,=mm_num_mch                  //
+       ldr     r1,[r1]                         //
+       strb    r1, [r0]                        //
+       ldr     r0,=mpp_clayer                  //
+       mov     r1, #0                          //
+       strb    r1, [r0]                        //
+                                               //
+       ldr     r0,=mmLayerMain                 //
+       bl      mppUpdateLayer                  //
+
+       ldr     r0,=mpp_channels                // update sub layer
+       ldr     r1,=mm_schannels                //
+       str     r1, [r0]                        //
+       ldr     r0,=mpp_nchannels               //
+       mov     r1, #MP_SCHANNELS               //
+       strb    r1, [r0]                        //
+       ldr     r0,=mpp_clayer                  //
+       mov     r1, #1                          //
+       strb    r1, [r0]                        //
+                                               //
+       ldr     r0,=mmLayerSub                  //
+       bl      mppUpdateLayer                  //
+       
+       pop     {pc}
+       
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+#ifdef SYS_GBA
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mppUpdateSub()
+ *
+ * Update sub-module/jingle, this is bad for some reason...
+ ******************************************************************************/
+                                               .global mppUpdateSub
+                                               .thumb_func
+mppUpdateSub:
+       
+       ldr     r0,=mmLayerSub
+       ldrb    r0, [r0, #MPL_ISPLAYING]
+       cmp     r0, #0
+       bne     .mppus_update
+       bx      lr
+.mppus_update:
+       
+       ldr     r0,=mpp_channels
+       ldr     r1,=mm_schannels
+       str     r1, [r0]
+       ldr     r0,=mpp_nchannels
+       mov     r1, #MP_SCHANNELS
+       strb    r1, [r0]
+       ldr     r0,=mpp_clayer
+       mov     r1, #1
+       strb    r1, [r0]
+       
+       push    {lr}
+       ldr     r0,=mmLayerSub
+       ldr     r1,=mpp_layerp
+       str     r0, [r1]
+       
+       mov     r1, #MPL_TICKRATE
+       ldrh    r1, [r0, r1]
+       mov     r2, #MPL_TICKFRAC
+       ldrh    r3, [r0, r2]
+       lsl     r1, #1
+       add     r3, r1
+       strh    r3, [r0, r2]
+       lsr     r3, #16
+       beq     1f
+2:     push    {r3}
+       ldr     r1,=mppProcessTick
+       bl      mpp_call_r1
+       pop     {r3}
+       sub     r3, #1
+       bgt     2b
+1:     pop     {pc}
+       
+//-----------------------------------------------------------------------------
+#endif
+//-----------------------------------------------------------------------------
+
+.pool
+
+//-----------------------------------------------------------------------------
+// IWRAM CODE
+//-----------------------------------------------------------------------------
+#ifdef USE_IWRAM
+       .SECTION ".iwram", "ax", %progbits
+       .ALIGN 2
+#endif
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mppProcessTick()
+ *
+ * Process module tick.
+ ******************************************************************************/
+.global mppProcessTick                         @@@@@@@@@@@@@@@@@@
+.thumb_func                                    @@@@     @@@     @@
+                                               @@@  @@  @@@@@@  @@
+mppProcessTick:                                        @@      @@@ @    @@
+                                               @@  @@@@@@@    @@@
+                                               @@@@@@@@@@@@@@@@@
+
+       add     r0,pc,#0                        // switch to ARM to preserve regs
+       bx      r0                              //
+.arm                                           //
+       stmfd   sp!, {lr}                       //
+       stmfd   sp!, {r4-r10}                   //
+       
+       add     r0,pc,#1                        // switch back to THUMB
+       bx      r0
+.thumb
+
+       ldr     r0,=mpp_layerp                  // test if module is playing
+       ldr     r0, [r0]                        //
+       mov     r8, r0                          //
+       ldrb    r1, [r0, #MPL_ISPLAYING]        //
+       cmp     r1, #0                          //
+       bne     1f                              //
+       ldr     r1,=.mppt_exit                  //
+       mov     pc,r1                           //
+1:
+
+@---------------------------------------------------
+@ read pattern data
+@---------------------------------------------------
+
+       ldrb    r1, [r0, #MPL_PATTDELAY]
+       cmp     r1, #0
+       bne     .mpp_pt_skippatternread
+       ldrb    r0, [r0, #MPL_TICK]
+       cmp     r0, #0
+       bne     .mpp_pt_skippatternread
+       
+       PROF_START
+       fjump2 mmReadPattern
+       //bl    mpp_ReadPattern
+       PROF_END 4
+
+@-----------------------------------------     
+.mpp_pt_skippatternread:
+       
+       mov     r0, r8
+       mov     r4, #MPL_MCH_UPDATE
+       ldr     r4, [r0, r4]
+       
+@---------------------------------------------------
+@ loop through module channels
+@---------------------------------------------------
+       
+       ldr     r7,=mpp_channels
+       ldr     r7, [r7]
+       mov     r0, #0
+       mov     r10, r0 @ use r10 as channel counter
+       mov     r0, r8
+       ldrb    r0, [r0, #MPL_TICK]
+       cmp     r0, #0
+       bne     pchannel_loop_other
+       
+@---------------------------------------------------
+pchannel_loop_first:
+@---------------------------------------------------   
+       lsr     r4, #1
+       bcc     pchannel_empty
+       fjump2  mmUpdateChannel_T0
+pchannel_empty:
+       mov     r0, #1
+       add     r10, r0
+       add     r7, #MCH_SIZE
+       cmp     r4, #0
+       bne     pchannel_loop_first
+       b       pchannel_loop_finished
+       
+@---------------------------------------------------
+pchannel_loop_other:
+@---------------------------------------------------
+       lsr     r4, #1
+       bcc     pchannel_empty2
+       #ifdef FOO_UC
+       bl      mpp_Update_Channel
+       #else
+       fjump2  mmUpdateChannel_TN
+       #endif
+       
+pchannel_empty2:
+       mov     r0, #1
+       add     r10, r0
+       add     r7, #MCH_SIZE
+       cmp     r4, #0
+       bne     pchannel_loop_other
+       
+pchannel_loop_finished:
+       
+@---------------------------------------------------
+@ loop through active channels
+@---------------------------------------------------
+       PROF_START
+       ldr     r6,=mm_achannels
+       ldr     r6, [r6]
+       @ldr    r6,=mpp_achannels
+       mov     r4, #0
+       
+@---------------------------------------------------   
+.mpp_pt_achn_loop:
+@---------------------------------------------------
+
+       ldrb    r0, [r6, #MCA_TYPE]
+       cmp     r0, #ACHN_DISABLED
+       beq     .mpp_pt_achn_disabled
+       ldr     r0,=mpp_clayer
+       ldrb    r0, [r0]
+       ldrb    r1, [r6, #MCA_FLAGS]
+       lsr     r1, #6
+       cmp     r1, r0
+       bne     .mpp_pt_achn_next
+       
+       ldr     r1,=mpp_vars
+       ldrb    r0, [r6, #MCA_VOLUME]
+       strb    r0, [r1, #MPV_AFVOL]
+       mov     r0, #0
+       strh    r0, [r1, #MPV_PANPLUS]
+       
+       ldr     r5, [r6, #MCA_PERIOD]
+       
+       bl      mpp_Update_ACHN
+       
+       b       .mpp_pt_achn_next
+       
+@---------------------------------------------------   
+.mpp_pt_achn_disabled:
+@      mov     r0, r4
+@      bl      mp_Mixer_StopChannel
+       
+@---------------------------------------------------
+.mpp_pt_achn_next:
+@---------------------------------------------------
+
+       ldrb    r0, [r6, #MCA_FLAGS]
+       mov     r1, #MCAF_UPDATED
+       bic     r0, r1
+       strb    r0, [r6, #MCA_FLAGS]
+       add     r6, #MCA_SIZE
+       add     r4, #1
+       ldr     r0,=mm_num_ach
+       ldr     r0,[r0]
+       
+       cmp     r4, r0
+@      cmp     r4, #MP_NCHANNELS
+       bne     .mpp_pt_achn_loop
+@---------------------------------------------------
+       PROF_END 6
+       
+       
+       
+       ldr     r1,=mppProcessTick_incframe
+       mov     pc,r1
+
+.pool
+
+//-----------------------------------------------------------------------------
+// TEXT Code
+//-----------------------------------------------------------------------------
+       .TEXT
+       .THUMB
+       .ALIGN 2
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mppProcessTick_incframe [[internal]]
+ ******************************************************************************/
+                                               .thumb_func
+mppProcessTick_incframe:
+
+@---------------------------------------------------
+@ update tick/row/position
+@---------------------------------------------------
+       
+       mov     r5, r8                          @ get tick#
+       ldrb    r1, [r5, #MPL_TICK]             @ ..
+       add     r1, #1                          @ increment
+       
+       ldrb    r2, [r5, #MPL_SPEED]            @ compare with speed
+       cmp     r1, r2                          @ ..
+       blt     .mppt_continuerow               @ if less than, continue this row
+       
+       ldrb    r2, [r5, #MPL_FPATTDELAY]
+       cmp     r2, #0
+       beq     .mppt_nofpd
+       sub     r2, #1
+       strb    r2, [r5, #MPL_FPATTDELAY]
+       b       .mppt_continuerow
+.mppt_nofpd:
+       
+       mov     r1, #0                          @  .. otherwise clear tick count
+       b       .mppt_nextrow                   @  and advance to next row
+       
+.mppt_continuerow:                             @ continue current row:
+       strb    r1, [r5, #MPL_TICK]             @ save tick#
+       b       .mppt_exit                      @ exit
+       
+.mppt_nextrow:                                         @ advance row
+       
+       ldrb    r2, [r5, #MPL_PATTDELAY]
+       cmp     r2, #0
+       beq     .mppt_nopd
+       sub     r2, #1
+       strb    r2, [r5, #MPL_PATTDELAY]
+       beq     .mppt_nopd
+       b       .mppt_continuerow
+.mppt_nopd:
+       
+       strb    r1, [r5, #MPL_TICK]                     @ save tick# (0)
+       ldrb    r1, [r5, #MPL_PATTJUMP]
+       cmp     r1, #255
+       beq     .mppt_no_pj
+       
+       mov     r2, #255
+       strb    r2, [r5, #MPL_PATTJUMP]
+       mov     r0, r1
+       bl      mpp_setposition
+       
+       ldrb    r1, [r5, #MPL_PATTJUMP_ROW]
+       cmp     r1, #0
+       beq     .mppt_pj_no_offset
+       mov     r2, #0
+       strb    r2, [r5, #MPL_PATTJUMP_ROW]
+       bl      mpph_FastForward
+.mppt_pj_no_offset:
+       
+       b       .mppt_exit
+       
+.mppt_no_pj:
+       
+       mov     r3, #MPL_PLOOP_JUMP
+       ldrb    r1, [r5, r3]
+       cmp     r1, #0
+       beq     .mppt_no_ploop
+       mov     r1, #0
+       strb    r1, [r5, r3]
+       ldrb    r1, [r5, #MPL_PLOOP_ROW]
+       strb    r1, [r5, #MPL_ROW]
+       mov     r3, #MPL_PLOOP_ADR
+       ldr     r1, [r5, r3]
+
+       mov     r3, #MPL_PATTREAD
+       str     r1, [r5, r3]
+       b       .mppt_exit
+.mppt_no_ploop:
+       ldrb    r1, [r5, #MPL_ROW]                      @ ..
+       add     r1, #1                                  @ increment
+       ldrb    r2, [r5, #MPL_NROWS]                    @ 
+       add     r2, #1
+       cmp     r1, r2                                  @ check with #rows for pattern
+       bne     .mppt_continuepattern                   @ if !=, then continue playing this pattern
+       
+.mppt_nextposition:                                    @ advance position
+       
+       ldrb    r0, [r5, #MPL_POSITION]                 @ increment position
+       add     r0, #1
+       
+       bl      mpp_setposition
+       b       .mppt_exit
+       
+.mppt_continuepattern:
+       
+       strb    r1, [r5, #MPL_ROW]                      @ save row count
+       
+.thumb_func
+.mppt_exit:
+       
+       @ switch to arm
+       ldr     r0,=.mppt_exita
+       bx      r0
+.arm
+.align 2
+.mppt_exita:
+
+       ldmfd   sp!, {r4-r10}
+       ldmfd   sp!, {r0}
+       bx      r0
+.thumb
+
+.pool
+
+//-----------------------------------------------------------------------------
+// IWRAM CODE
+//-----------------------------------------------------------------------------
+#ifdef USE_IWRAM
+       .SECTION ".iwram", "ax", %progbits
+       .ALIGN 2
+#endif
+//-----------------------------------------------------------------------------
+
+/******************************************************************************
+ * mpp_Channel_NewNote()
+ *
+ * Process new note.
+ * Input r7 = pchannel address
+ ******************************************************************************/
+                                               .global mpp_Channel_NewNote
+                                               .thumb_func
+mpp_Channel_NewNote:
+       
+       @ r7 = pchannel address
+       push    {r4,lr}
+
+       ldrb    r0, [r7, #MCH_INST]             @ get instrument#
+       sub     r0, #1
+       bcc     .mppt_skipnna
+
+       bl      mpp_Channel_GetACHN
+       cmp     r6, #0
+       beq     .mppt_alloc_channel     
+       
+       ldrb    r0, [r7, #MCH_INST]             @ get instrument#
+       sub     r0, #1
+       
+       mpp_InstrumentPointer
+       
+       ldrb    r1, [r7, #MCH_BFLAGS]           @ fetch NNA
+       lsr     r1, #6
+       
+       beq     .mppt_NNA_CUT                   @ skip if zero
+       
+       ldrb    r1, [r0, #C_MASI_DCT]           @ otherwise check duplicate check type
+       lsl     r1, #32-2
+       lsr     r1, #32-2
+       lsl     r1, #1                          @ jump to mppt_DCT_TABLE[dct]
+       add     r1, pc
+       mov     pc, r1
+
+.mppt_DCT_TABLE:
+b      .mppt_DCNA
+b      .mppt_DCT_NOTE
+b      .mppt_DCT_SAMP
+b      .mppt_DCT_INST
+.mppt_DCT_NOTE:                                        @ DCT note ---------------------
+       ldrb    r1, [r7, #MCH_PNOTE]            @ get pattern note
+       lsl     r1, #1                          @ translate to real note
+       add     r1, #C_MASI_MAP                 @ with note/sample map
+       ldrb    r1, [r0, r1]                    @ r1 = real note
+       ldrb    r2, [r7, #MCH_NOTE]             @ compare with last note
+       cmp     r1, r2                          @
+       beq     .mppt_DCA                       @ equal? perform DCA
+       b       .mppt_DCNA                      @ otherwise skip
+       
+.mppt_DCT_SAMP:                                        @ DCT sample -------------------
+       
+       // **WARNING: code not functional with new instrument table
+       ldrb    r1, [r7, #MCH_PNOTE]            @ get pattern note
+       lsl     r1, #1                          @ translate to sample#
+       add     r1, #C_MASI_MAP+1               @ with note/sample map
+       ldrb    r1, [r0, r1]                    @ r1 = sample#
+       ldrb    r2, [r6, #MCA_SAMPLE]           @ compare with achn's sample
+       cmp     r1, r2                          @
+       beq     .mppt_DCA                       @ equal? perform DCA
+       b       .mppt_DCNA                      @ otherwise skip
+       
+.mppt_DCT_INST:                                        @ DCT instrument ---------------
+       ldrb    r1, [r7, #MCH_INST]             @ load instrument
+       ldrb    r2, [r6, #MCA_INST]             @ compare with active inst
+       cmp     r1, r2                          @
+       bne     .mppt_DCNA                      @ not equal? skip DCA
+       
+.mppt_DCA:                                     @ DUPLICATE CHECK ACTION -------
+       ldrb    r1, [r0, #C_MASI_DCA]           @ read type
+       cmp     r1, #IT_DCA_CUT                 @ cut?
+       beq     .mppt_NNA_CUT                   @   branch
+       cmp     r1, #IT_DCA_OFF                 @ note-off?
+       beq     .mppt_NNA_OFF                   @   branch
+       b       .mppt_NNA_FADE                  @ note-fade otherwise
+       
+.mppt_DCNA:
+       
+       ldrb    r1, [r7, #MCH_BFLAGS]
+       lsr     r1, #6                          @ get NNA
+       lsl     r1, #1                          @ and jump to
+       add     r1, pc                          @ NNA_TABLE[NNA]
+       mov     pc, r1
+.mppt_NNA_TABLE:
+b      .mppt_NNA_CUT
+b      .mppt_NNA_CONTINUE
+b      .mppt_NNA_OFF
+b      .mppt_NNA_FADE
+       
+@---------------------------------------------------------------------------------
+.mppt_NNA_CUT:
+@---------------------------------------------------------------------------------
+       
+       #ifdef SYS_NDS                          // nds supports volume ramping
+       
+       ldrb    r1, [r6, #MCA_TYPE]
+       cmp     r1, #0
+       BEQ     .mppt_samechannel
+       mov     r1, #ACHN_BACKGROUND
+       strb    r1, [r6, #MCA_TYPE]
+       mov     r1, #0
+       strb    r1, [r6, #MCA_VOLUME]
+       b       .mppt_NNA_FINISHED
+       
+       #else
+       b       .mppt_samechannel
+       #endif
+       
+@---------------------------------------------------------------------------------
+.mppt_NNA_CONTINUE:
+@---------------------------------------------------------------------------------
+       
+       mov     r1, #ACHN_BACKGROUND    @ use different channel
+       strb    r1, [r6, #MCA_TYPE]     @ set active channel to "background"
+       b       .mppt_NNA_FINISHED      @ finished
+
+@---------------------------------------------------------------------------------
+.mppt_NNA_OFF:
+@---------------------------------------------------------------------------------
+
+       ldrb    r1, [r6, #MCA_FLAGS]    @ clear KEYON in flags byte
+       mov     r2, #MCAF_KEYON
+       bic     r1, r2
+
+       strb    r1, [r6, #MCA_FLAGS]
+       mov     r1, #ACHN_BACKGROUND    @ set type to "background"
+       strb    r1, [r6, #MCA_TYPE]
+       b       .mppt_NNA_FINISHED      @ finished
+
+@---------------------------------------------------------------------------------
+.mppt_NNA_FADE:
+@---------------------------------------------------------------------------------
+
+       ldrb    r1, [r6, #MCA_FLAGS]    @ set NOTE FADE in flags byte
+       mov     r2, #MCAF_FADE
+       orr     r1, r2
+       strb    r1, [r6, #MCA_FLAGS]
+       mov     r1, #ACHN_BACKGROUND    @ set type to "background"
+       strb    r1, [r6, #MCA_TYPE]     @
+       
+.mppt_NNA_FINISHED:
+       
+.mppt_alloc_channel:
+
+       mov     r4, r6
+
+       ldr     r1,=mmAllocChannel
+       jump1                           @ find new active channel
+       strb    r0, [r7, #MCH_ALLOC]    @ save number
+       
+       #ifdef SYS_NDS
+       
+       cmp     r4, #0
+       beq     .mppt_samechannel
+
+       mov     r1, #MCA_SIZE           @ copy data from previous channel
+       mul     r0, r1                  @ (for volume ramping wierdness)
+       ldr     r1,=mm_achannels        @
+       ldr     r1,[r1]                 @
+       add     r0, r1                  @
+                                       @
+       mov     r2, #MCA_SIZE/4
+1:     ldmia   r4!, {r1}
+       stmia   r0!, {r1}
+       sub     r2, #1
+       bne     1b
+       
+/*     ldr     r1, [r4, #MCA_FADE]     @
+       str     r1, [r0, #MCA_FADE]     @
+       ldr     r1, [r4, #MCA_ENVC_PAN] @
+       str     r1, [r0, #MCA_ENVC_PAN] @
+       ldr     r1, [r4, #MCA_AVIB_DEP] @
+       str     r1, [r0, #MCA_AVIB_DEP] @
+       ldrb    r1, [r4, #MCA_FLAGS]    @
+       strb    r1, [r0, #MCA_FLAGS]    @
+       ldrb    r1, [r4, #MCA_VOLUME]
+       strb    r1, [r0, #MCA_VOLUME]
+       ldrb    r1, [r4, #MCA_PANNING]
+       strb    r1, [r0, #MCA_PANNING]
+       ldrb    r1, [r4, #MCA_SAMPLE]
+       strb    r1, [r0, #MCA_SAMPLE]
+       ldrb    r1, [r4, #MCA_INST]
+       strb    r1, [r0, #MCA_INST]*/
+       
+       #endif
+       
+.mppt_samechannel:
+       
+.mppt_skipnna:
+       pop     {r4}
+       pop     {r3}
+       bx      r3
+       
+.pool
+
+.align 2
+.thumb_func
+@------------------------------------------------------------------------------------------------------
+mpp_Channel_GetACHN:
+@------------------------------------------------------------------------------------------------------
+       
+       @ gets the active channel pointer
+       @ and stores in r6
+       @ gives 0 if N/A
+       
+       ldrb    r0, [r7, #MCH_ALLOC]
+       cmp     r0, #255
+       bge     1f
+       ldr     r6,=mm_achannels
+       ldr     r6,[r6]
+       mov     r1, #MCA_SIZE
+       mul     r0, r1
+       add     r6, r0
+       bx      lr
+       
+1:     mov     r6, #0
+       bx      lr
+.pool
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------------------
+mpp_Update_ACHN:
+@----------------------------------------------------------------------------------------------------
+               
+@ r5 = affected period
+@ r6 = achannel address
+
+       push    {lr}                    @ enter subroutine
+       
+@ check updated flag & exit if already updated
+       
+       ldrb    r0, [r6, #MCA_FLAGS]
+       mov     r1, #MCAF_UPDATED
+       tst     r0, r1
+       beq     .mpp_achn_update
+       pop     {pc}
+
+@--------------------------------------------
+
+       .global mpp_Update_ACHN_notest
+       .thumb_func
+@----------------------------------------------------------------------------------------------------
+mpp_Update_ACHN_notest:
+@----------------------------------------------------------------------------------------------------
+       push    {lr}
+       
+.mpp_achn_update:
+       
+@------------------------------------------------------------------------
+@ Envelope Processing
+@------------------------------------------------------------------------
+       
+       ldrb    r0, [r6, #MCA_INST]
+       sub     r0, #1
+       bCS     1f
+       b       .mppt_achn_noinst
+1:     mpp_InstrumentPointer
+
+@ get envelope flags
+       
+       mov     r1, r0
+       ldrb    r2, [r1, #C_MASI_ENVFLAGS]
+       add     r1, #C_MASI_ENVELOPES
+       
+       lsr     r2, #1                          @ shift out volume envelope bit
+       bcc     .mppt_no_volenv
+
+       ldrb    r3, [r6, #MCA_FLAGS]
+       lsr     r3, #6
+       
+       bcs     .mppt_achn_ve_enabled
+       ldrb    r0, [r1]
+       add     r1, r0
+       b       .mppt_no_volenv
+.mppt_achn_ve_enabled:
+       
+       push    {r1, r2}
+       
+       ldrh    r0, [r6, #MCA_ENVC_VOL]
+       mov     r2, r1
+       ldrb    r1, [r6, #MCA_ENVN_VOL]
+       bl      mpph_ProcessEnvelope
+       strb    r1, [r6, #MCA_ENVN_VOL]
+       strh    r0, [r6, #MCA_ENVC_VOL]
+       mov     r1, r3
+       
+       cmp     r2, #1
+       bne     .mpph_volenv_notend
+       ldrb    r0, [r6, #MCA_FLAGS]
+       
+       mov     r3, r8                          @ stupid xm doesn't fade out at envelope end
+       ldrb    r3, [r3, #MPL_FLAGS]
+       lsr     r3, #C_FLAGS_XS
+       
+       mov     r3, #MCAF_ENVEND
+       bcs     1f
+       mov     r3, #MCAF_ENVEND+MCAF_FADE
+1:
+       orr     r0, r3
+       strb    r0, [r6, #MCA_FLAGS]
+.mpph_volenv_notend:
+
+       cmp     r2, #1
+       blt     .mpph_volenv_normal
+       
+       @ check keyon and turn on fade...
+       ldrb    r0, [r6, #MCA_FLAGS]
+       mov     r2, #MCAF_KEYON
+       tst     r0, r2
+       bne     .mpph_volenv_normal
+.mpph_volenv_notefade:
+       mov     r2, #MCAF_FADE
+       orr     r0, r2
+       strb    r0, [r6, #MCA_FLAGS]
+       
+.mpph_volenv_normal:
+       
+       ldr     r0,=mpp_vars
+       ldrb    r2, [r0, #MPV_AFVOL]
+       mul     r2, r1
+       lsr     r2, #6+6
+       strb    r2, [r0, #MPV_AFVOL]
+       pop     {r1, r2}
+       ldrb    r0, [r1]
+       add     r1, r0
+       b       .mppt_has_volenv
+.mppt_no_volenv:
+       
+       ldrb    r0, [r6, #MCA_FLAGS]
+       mov     r3, #MCAF_ENVEND
+       orr     r0, r3
+       mov     r3, #MCAF_KEYON
+       tst     r0, r3
+       bne     .mppt_has_volenv
+       mov     r3, #MCAF_FADE
+       orr     r0, r3
+       strb    r0, [r6, #MCA_FLAGS]
+       
+       mov     r0, r8          @ check XM MODE and cut note
+       ldrb    r0, [r0, #MPL_FLAGS]
+       lsr     r0, #C_FLAGS_XS
+       bcc     .mppt_has_volenv
+       mov     r0, #0
+       strh    r0, [r6, #MCA_FADE]
+.mppt_has_volenv:
+       
+       lsr     r2, #1
+       bcc     .mppt_no_panenv
+       push    {r1, r2}
+       ldrh    r0, [r6, #MCA_ENVC_PAN]
+       mov     r2, r1
+       ldrb    r1, [r6, #MCA_ENVN_PAN]
+       bl      mpph_ProcessEnvelope
+       strb    r1, [r6, #MCA_ENVN_PAN]
+       strh    r0, [r6, #MCA_ENVC_PAN]
+       mov     r1, r3
+
+       ldr     r0,=mpp_vars
+       mov     r3, #MPV_PANPLUS
+       ldrsh   r2, [r0,r3]
+       lsr     r1, #4
+       sub     r1, #128
+       add     r2, r1
+       strh    r2, [r0,r3]
+       pop     {r1, r2}
+.mppt_no_panenv:
+       
+       lsr     r2, #1
+       bcc     .mppt_no_pitchenv
+       ldrb    r0, [r1, #C_MASIE_FILTER]
+       cmp     r0, #0
+       bne     .mppt_no_pitchenv
+       push    {r1, r2}
+       ldrh    r0, [r6, #MCA_ENVC_PIC]
+       mov     r2, r1
+       ldrb    r1, [r6, #MCA_ENVN_PIC]
+       bl      mpph_ProcessEnvelope
+       strb    r1, [r6, #MCA_ENVN_PIC]
+       strh    r0, [r6, #MCA_ENVC_PIC]
+       mov     r1, r3
+       lsr     r1, #3
+       sub     r1, #255
+       mov     r0, r5
+       sub     r1, #1
+       bmi     .mppt_pitchenv_minus
+#ifdef USE_IWRAM
+       ldr     r2,=mpph_LinearPitchSlide_Up
+       jump2
+#else
+       bl      mpph_LinearPitchSlide_Up
+#endif
+       b       .mppt_pitchenv_plus
+.mppt_pitchenv_minus:
+       neg     r1, r1
+#ifdef USE_IWRAM
+       ldr     r2,=mpph_LinearPitchSlide_Down
+       jump2
+#else
+       bl      mpph_LinearPitchSlide_Down
+#endif
+.mppt_pitchenv_plus:
+       mov     r5, r0
+       pop     {r1, r2}
+.mppt_no_pitchenv:
+       
+       ldrb    r0, [r6, #MCA_FLAGS]
+       mov     r1, #MCAF_FADE
+       tst     r0, r1
+       beq     .mppt_achn_nofade
+       ldrb    r0, [r6, #MCA_INST]
+       sub     r0, #1
+       
+       mpp_InstrumentPointer
+       ldrb    r0, [r0, #C_MASI_FADE]
+
+       ldrh    r1, [r6, #MCA_FADE]
+
+       sub     r1, r0
+       bcs     .mppt_achn_fadeout_clip
+       mov     r1, #0
+.mppt_achn_fadeout_clip:
+       strh    r1, [r6, #MCA_FADE]
+
+.mppt_achn_nofade:
+       
+.mppt_achn_keyon:
+       
+
+@----------------------------------------------------------------------------------
+@ *** PROCESS AUTO VIBRATO
+@----------------------------------------------------------------------------------
+       
+       ldrb    r0, [r6, #MCA_SAMPLE]
+       sub     r0, #1
+       bcc     .mppt_achn_nostart      @ no sample!!
+       
+       @bl     mpp_SamplePointer
+       mpp_SamplePointer
+       ldrh    r1, [r0, #C_MASS_VIR]   @ get av-rate
+       cmp     r1, #0                  @ 0?
+       beq     .mppt_av_disabled       @  if 0 then its disabled
+       ldrh    r2, [r6, #MCA_AVIB_DEP] @ get depth counter
+       add     r2, r1                  @ add rate
+       lsr     r1, r2, #15             @ check for 15-bit overflow
+       beq     .mppt_av_depclip        @ ..
+       
+       ldr     r2,=32768               @ and clamp to 32768
+.mppt_av_depclip:                      
+       strh    r2, [r6, #MCA_AVIB_DEP] @ save depth counter
+       ldrb    r1, [r0, #C_MASS_VID]   @ get av-depth
+       mul     r1, r2                  @ multiply
+
+       ldrb    r3, [r6, #MCA_AVIB_POS] @ get table position
+       ldrb    r2, [r0, #C_MASS_VIS]   @ get av-speed
+       add     r3, r2                  @ add to position
+       lsl     r3, #32-8               @ wrap position to 0->255
+       lsr     r3, #32-8               @ ..
+       strb    r3, [r6, #MCA_AVIB_POS]         @ save position
+       ldr     r2,=mpp_TABLE_FineSineData      @ get table pointer
+       ldrsb   r2, [r2, r3]                    @ load table value at position
+       mul     r2, r1                          @ multiply with depth
+       asr     r2, #23                         @ shift value
+       bmi     .mppt_av_minus                  @ and perform slide...
+.mppt_av_plus:                                 @ --slide up
+       mov     r1, r2                          @ r1 = slide value
+       mov     r0, r5                          @ r0 = frequency
+#ifdef USE_IWRAM
+       fjump2  mpph_PitchSlide_Up
+#else
+       bl      mpph_PitchSlide_Up              @ pitch slide
+#endif
+       b       .mppt_av_finished               @
+.mppt_av_minus:                                        @ --slide down
+       neg     r1, r2                          @ r1 = slide value
+       mov     r0, r5                          @ r0 = frequency
+#ifdef USE_IWRAM
+       ldr     r2,=mpph_PitchSlide_Down
+       jump2
+#else
+       bl      mpph_PitchSlide_Down            @ pitch slide
+#endif
+
+.mppt_av_finished:
+       mov     r5, r0                          @ affect frequency
+.mppt_av_disabled:
+       
+@---------------------------------------------------------------------------------
+       
+.mppt_achn_noinst:
+       
+       push    {r4}
+       mov     r0, #MIXER_CHN_SIZE
+       mul     r4, r0
+       @ldr    r0,=mp_channels
+@      ldr     r0,=mm_mixchannels
+@      ldr     r0,[r0]
+       GET_MIXCH r0
+       add     r4, r0
+       
+       @ *** UPDATE MIXING INFORMATION
+       
+       ldrb    r0, [r6, #MCA_FLAGS]            @ read flags
+       mov     r1, #MCAF_START                 @ test start bit
+       tst     r0, r1                          @ ..
+       beq     .mppt_achn_nostart              @
+.mppt_achn_start:                              @ START NOTE
+       bic     r0, r1                          @ clear bit
+       strb    r0, [r6, #MCA_FLAGS]            @ save flags
+       ldrb    r0, [r6, #MCA_SAMPLE]           @ get sample #
+
+       sub     r0, #1                          @ ..
+       bcc     .mppt_achn_nostart              @ quit if invalid
+       @bl     mpp_SamplePointer               @ get sample address
+       mpp_SamplePointer
+       
+       ldrh    r3, [r0, #C_MASS_MSLID]
+       
+       add     r1,r3,#1                        @ msl id == 0xFFFF?
+       lsr     r1, #17
+       
+       bcc     .mppt_achn_msl_sample 
+
+.mppt_achn_direct_sample:                      @ then sample follows
+       
+       add     r0, #12
+       
+//------------------------------------------------
+#ifdef SYS_GBA
+//     ldr     r1, [r0,#C_SAMPLE_LEN]  @ setup mixer (GBA)
+//     lsl     r1, #MP_SAMPFRAC
+//     str     r1, [r4,#MIXER_CHN_LEN]
+//     ldr     r1, [r0,#C_SAMPLE_LOOP]
+//     str     r1, [r4,#MIXER_CHN_LOOP]
+       add     r0, #C_SAMPLE_DATA
+       str     r0, [r4,#MIXER_CHN_SRC]
+       
+#else
+//-------------------------------------------
+
+       ldr     r1,=0x2000000
+       sub     r0, r1
+       str     r0, [r4, #MIXER_CHN_SAMP]
+       ldrb    r1, [r4, #MIXER_CHN_CNT]
+       mov     r0, #MIXER_CF_START
+       orr     r1, r0
+       strb    r1, [r4, #MIXER_CHN_CNT]
+       
+#endif
+//-------------------
+       
+       b       .mppt_achn_gotsample
+.mppt_achn_msl_sample:                         @ otherwise get from solution
+
+       #ifdef SYS_GBA
+
+       ldr     r2,=mp_solution
+       ldr     r2, [r2]
+       mov     r1, r2
+       add     r1, #12
+       lsl     r3, #2
+       ldr     r1, [r1, r3]
+       add     r1, #8
+       add     r0, r1, r2
+       
+       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ NOTICE, USE LDM HERE
+       
+//     ldr     r1, [r0,#C_SAMPLE_LEN]  @ setup mixer (GBA)
+//     lsl     r1, #MP_SAMPFRAC
+//     str     r1, [r4,#MIXER_CHN_LEN]
+//     ldr     r1, [r0,#C_SAMPLE_LOOP]
+//     str     r1, [r4,#MIXER_CHN_LOOP]
+       add     r0, #C_SAMPLE_DATA
+       str     r0, [r4,#MIXER_CHN_SRC]
+
+       #endif
+
+       #ifdef SYS_NDS
+                       
+       ldr     r2,=mmSampleBank        @ get samplebank pointer
+       ldr     r2, [r2]
+       lsl     r3, #2          @ add msl_id *4
+       ldr     r1, [r2, r3]
+       
+       lsl     r1, #8          @ mask out counter value
+       lsr     r1, #8
+       add     r1, #8
+
+       str     r1, [r4,#MIXER_CHN_SAMP]
+       ldrb    r1, [r4,#MIXER_CHN_CNT]         // read control         **CNT was cleared, no need to read it
+       mov     r0, #MIXER_CF_START             // set start bit
+       orr     r1, r0
+       strb    r1, [r4,#MIXER_CHN_CNT]         // save control
+
+       #endif
+       
+.mppt_achn_gotsample:
+       ldr     r1,=mpp_vars
+       ldrb    r1, [r1, #MPV_SAMPOFF]
+#ifdef SYS_GBA
+       lsl     r1, #MP_SAMPFRAC+8
+       str     r1, [r4, #MIXER_CHN_READ]
+#else
+       str     r1, [r4, #MIXER_CHN_READ]
+#endif
+       
+.mppt_achn_nostart:
+       @ set pitch
+       
+       ldrb    r0, [r6, #MCA_SAMPLE]           @ get sample#
+       sub     r0, #1                          @ ..
+       bcc     .mppt_achn_setvolumeA
+       @bl     mpp_SamplePointer               @ quit if invalid
+       mpp_SamplePointer
+       push    {r0}
+       
+       mov     r1, r8
+       ldrb    r1, [r1, #MPL_FLAGS]
+       lsr     r1, #C_FLAGS_SS
+       bcc     .mppt_achn_amigafreqs
+.mppt_achn_linearfreqs:
+       
+       ldrh    r0, [r0, #C_MASS_FREQ]          @ get C5SPEED
+       LSL     R0, #2
+       lsr     r1, r5, #8                      @ do some stuff...
+       mul     r1, r0                          @ ... period * freq?
+       
+       lsr     r1, #8
+       
+       ldr     r0,=mpp_clayer
+       ldrb    r0, [r0]
+       cmp     r0, #0
+       bne     1f
+       ldr     r0,=mm_masterpitch
+       ldr     r0, [r0]
+       mul     r1, r0
+       lsr     r1, #10
+1:
+
+       #ifdef SYS_GBA
+       
+       
+//     ldr     r0,=mm_freqscalar
+//     ldr     r0, [r0]
+       ldr     r0,=(4096*65536)/15768
+       mul     r0, r1
+       lsr     r0, #16
+       str     r0, [r4, #MIXER_CHN_FREQ]
+       
+       #else
+
+       ldr     r0,=MIXER_SCALE
+       mul     r0, r1
+       lsr     r0, #16+1
+       strh    r0, [r4, #MIXER_CHN_FREQ]
+       
+       //strh  r1, [r4, #MIXER_CHN_FREQ]
+       #endif
+       
+       b       .mppt_achn_setvolume
+       
+.mppt_achn_amigafreqs:
+       ldr     r0,=MOD_FREQ_DIVIDER_PAL
+       movs    r1, r5
+       
+       beq     .mppt_achn_setvolume            @ 0 is a bad period
+       swi     SWI_DIVIDE
+       
+       ldr     r1,=mpp_clayer
+       ldrb    r1, [r1]
+       cmp     r1, #0
+       bne     1f
+       ldr     r1,=mm_masterpitch
+       ldr     r1, [r1]
+       mul     r0, r1
+       lsr     r0, #10
+1:
+
+       #ifdef SYS_GBA
+
+//     ldr     r1,=mm_freqscalar
+//     ldr     r1,[r1]
+       ldr     r1,=(4096*65536)/15768
+       mul     r0, r1
+       lsr     r0, #16
+
+       str     r0, [r4, #MIXER_CHN_FREQ]
+       #else
+//     mov     r1, r0
+//     ldr     r0,=16756991                    @ calculate ds mixer timing
+//     swi     SWI_DIVIDE
+//     neg     r0,r0
+       //lsr   r0, #5
+       ldr     r1,=MIXER_SCALE
+       mul     r0, r1
+       lsr     r0, #16+1
+       strh    r0, [r4, #MIXER_CHN_FREQ]
+       #endif
+
+@----------------------------------------------------------------------------------------------------
+.mppt_achn_setvolume:
+@----------------------------------------------------------------------------------------------------
+       
+       @ set volume
+       
+       pop     {r0}
+                                               @ <-- stepped oct 28, 3:27pm 
+       ldrb    r3, [r0, #C_MASS_GV]            @ SV, 6-bit
+       ldrb    r0, [r6, #MCA_INST]
+       sub     r0, #1
+       bcs     1f
+       
+.thumb_func
+.mppt_achn_setvolumeA: 
+       mov     r1, #0
+       b       .mppt_achn_badinstrument
+1:     
+       mpp_InstrumentPointer
+       ldrb    r0, [r0, #C_MASI_GVOL]  @ IV, 7-bit
+       mul     r3, r0
+       
+       ldr     r1,=mpp_vars
+       ldrb    r0, [r1, #MPV_AFVOL]    @ ((CV*VOL)/32*VEV/64) 7-bit
+       
+       mul     r3, r0
+       
+       mov     r1, r8                  @ get global vollume
+       ldrb    r0, [r1, #MPL_FLAGS]
+       lsr     r0, #4
+       ldrb    r0, [r1, #MPL_GV]       @ ..                 7-bit
+       
+       bcc     1f
+       lsl     r0, #1                  @ xm mode global volume is only 0->64, shift to 0->128
+       
+1:     mul     r3, r0                  @ multiply
+       
+       lsr     r3, #10
+       
+       ldrh    r0, [r6, #MCA_FADE]
+       
+       
+       mul     r3, r0
+       
+       lsr     r3, r3, #10
+       
+       mov     r0, r8
+       mov     r1, #MPL_VOLUME
+       ldrh    r0, [r0, r1]
+       mul     r3, r0
+       
+//------------------------------------------------
+#ifdef SYS_NDS
+       lsr     r1, r3, #19-3-5 ///#19-3        (new 16-bit levels!)
+       ldr     r3,=65535  //2047
+       cmp     r1, r3                          @ clip values over 255
+       blt     1f
+       mov     r1, r3
+1:
+.mppt_achn_badinstrument:
+//     lsr     r3, r1, #3              (new 16-bit levels!)
+       lsr     r3, r1, #8              
+       strb    r3, [r6, #MCA_FVOL]
+
+#else
+
+       lsr     r1, r3, #19
+       cmp     r1, #255                        @ clip values over 255
+       blt     1f
+       mov     r1, #255
+1:
+.mppt_achn_badinstrument:
+       strb    r1, [r6, #MCA_FVOL]
+
+#endif
+
+       cmp     r1, #0
+       bne     .mppt_achn_audible
+       
+       #ifdef SYS_NDS                  // nds has volume ramping!
+       
+       ldrb    r3, [r6, #MCA_TYPE]
+       cmp     r3, #ACHN_BACKGROUND
+       bne     1f
+       ldrb    r3, [r6, #MCA_VOLUME]
+       cmp     r3, #0
+       bne     1f
+       ldrh    r3, [r4, #MIXER_CHN_CVOL]
+       cmp     r3, #0
+       beq     .mppt_achn_not_audible
+       #endif
+       
+1:     ldrb    r3, [r6, #MCA_FLAGS]
+       mov     r2, #MCAF_ENVEND
+       tst     r3, r2
+       beq     .mppt_achn_audible
+       mov     r2, #MCAF_KEYON
+       tst     r3, r2
+       bne     .mppt_achn_audible
+       
+//     #ifdef SYS_NDS
+//     ldrh    r3, [r4, #MIXER_CHN_CVOL]       // nds has volume ramping!!
+//     cmp     r3, #0
+//     bne     .mppt_achn_audible
+//     #endif
+
+.mppt_achn_not_audible:
+       @ STOP CHANNEL
+
+#ifdef SYS_GBA
+       ldr     r0,=1<<31
+       str     r0,[r4,#MIXER_CHN_SRC]  @ stop mixer channel
+#else
+       mov     r0,#0
+       str     r0,[r4,#MIXER_CHN_SAMP] @ stop mixer channel
+#endif
+
+       ldrb    r3, [r6, #MCA_TYPE]
+       cmp     r3, #ACHN_FOREGROUND
+       bne     .mpp_achn_noparent
+       ldrb    r1, [r6, #MCA_PARENT]
+       mov     r3, #MCH_SIZE
+       mul     r1, r3
+       ldr     r0,=mpp_channels
+       ldr     r0, [r0]
+       add     r0, r1
+       mov     r1, #255
+       strb    r1, [r0, #MCH_ALLOC]
+.mpp_achn_noparent:
+       
+       mov     r1, #ACHN_DISABLED
+       strb    r1, [r6, #MCA_TYPE]
+       b       .mpp_achn_updated
+.mppt_achn_audible:
+       
+#ifdef SYS_NDS
+       
+       strh    r1, [r4, #MIXER_CHN_VOL]
+       
+#else
+       
+       strb    r1, [r4, #MIXER_CHN_VOL]
+       
+#endif
+       
+#ifdef SYS_GBA                                         // check if mixer channel has ended
+
+       ldr     r0, [r4, #MIXER_CHN_SRC]
+       asr     r0, #31
+       beq     1f
+
+#else
+
+       ldr     r0, [r4, #MIXER_CHN_SAMP]
+       lsl     r0, #8
+       bne     1f
+       
+#endif
+
+       ldrb    r3, [r6, #MCA_TYPE]
+       cmp     r3, #ACHN_FOREGROUND
+       bne     2f
+
+       ldrb    r1, [r6, #MCA_PARENT]                   // stop channel if channel ended
+       mov     r3, #MCH_SIZE
+       mul     r1, r3
+       ldr     r0,=mpp_channels
+       ldr     r0, [r0]
+       add     r0, r1
+       mov     r1, #255
+       strb    r1, [r0, #MCH_ALLOC]
+2:
+
+#ifdef SYS_GBA
+       ldr     r0,=1<<31
+       str     r0,[r4,#MIXER_CHN_SRC]  @ stop mixer channel
+#else
+       mov     r0,#0
+       str     r0,[r4,#MIXER_CHN_SAMP] @ stop mixer channel
+#endif
+
+       mov     r1, #ACHN_DISABLED
+       strb    r1, [r6, #MCA_TYPE]
+       b       .mpp_achn_updated
+
+       @ set panning
+1:     ldr     r1,=mpp_vars
+       mov     r3, #MPV_PANPLUS
+       ldrsh   r0, [r1,r3]
+       ldrb    r1, [r6, #MCA_PANNING]
+       
+       add     r1, r0
+
+       cmp     r1, #0
+       bge     .mpp_achn_clippan1
+       mov     r1, #0
+.mpp_achn_clippan1:
+       cmp     r1, #255
+       ble     .mpp_achn_clippan2
+       mov     r1, #255
+.mpp_achn_clippan2:
+
+       #ifdef SYS_NDS
+       lsr     r1, #1
+       ldrb    r0, [r4, #MIXER_CHN_CNT]
+       lsr     r0, #7
+       lsl     r0, #7
+       orr     r0, r1
+       strb    r0, [r4, #MIXER_CHN_CNT]
+       #endif
+       
+       #ifdef SYS_GBA
+       strb    r1, [r4, #MIXER_CHN_PAN]
+       #endif
+       
+       
+.mpp_achn_updated:
+       pop     {r4}
+       pop     {r0}
+       bx      r0
+       //pop   {pc}                            @ exit
+.pool
+
+.align 2
+.thumb_func
+@-------------------------------------------------------------------------
+mpph_ProcessEnvelope:                  @ params={count,node,address}
+@-------------------------------------------------------------------------
+
+@ processes the envelope at <address>
+@ returns:
+@ r0=count
+@ r1=node
+@ r2=exit_code
+@ r3=value*64
+       
+       push    {r4,r5}
+
+@ get node and base
+       lsl     r4, r1, #2
+       add     r4, #C_MASIE_NODES
+       add     r4, r2
+       
+       ldrh    r3, [r4, #2]
+       lsl     r3, #32-7
+       lsr     r3, #32-7-6
+
+@ check for zero count
+       
+       cmp     r0, #0
+       bne     .mpph_pe_between
+.mpph_pe_new:
+
+@ process envelope loop
+       
+       ldrb    r5, [r2, #C_MASIE_LEND]
+       cmp     r1, r5
+       bne     1f
+       ldrb    r1, [r2, #C_MASIE_LSTART]
+       mov     r2, #2
+       b       .mpph_pe_exit
+
+@ process envelope sustain loop
+
+1:     ldrb    r5, [r6, #MCA_FLAGS]
+       lsr     r5, #1  @ locked
+       bcc     1f
+       ldrb    r5, [r2, #C_MASIE_SEND]
+       cmp     r1, r5
+       bne     1f
+       ldrb    r1, [r2, #C_MASIE_SSTART]
+       mov     r2, #0
+       b       .mpph_pe_exit
+
+@ check for end
+
+1:     ldrb    r5, [r2, #C_MASIE_NODEC]
+       sub     r5, #1
+       cmp     r1, r5
+       bne     .mpph_count
+       mov     r2, #2
+       b       .mpph_pe_exit
+
+.mpph_pe_between:
+
+@                            delta * count 
+@ formula : y = base*2^6 + -----------------
+@                                 2^3
+       mov     r5, #0
+       ldrsh   r5, [r4,r5]
+       mul     r5, r0
+       asr     r5, #3
+       add     r3, r5
+       
+.mpph_count:
+
+@ increment count and check if == read count
+
+       add     r0, #1
+       ldrh    r5, [r4, #2]
+       lsr     r5, #7
+       cmp     r0, r5
+       bne     .mpph_pe_exit
+
+@ increment node and reset counter
+
+       mov     r0, #0
+       add     r1, #1
+
+.mpph_pe_exit:
+       
+       pop     {r4,r5}
+       bx      lr
+.pool
+
+/*
+.align 2
+.thumb_func
+@--------------------------------------------------------------------------------------------
+mpp_Alloc_Channel:
+@--------------------------------------------------------------------------------------------
+       
+       @ find a channel to use
+       @ returns invalid channel if none available
+       push    {r4,r5}                         @ preserve reg(s)
+       ldr     r5,=mm_ch_mask
+       ldr     r5, [r5]
+
+       @ldr    r1,=mpp_achannels               @ load pointer
+       ldr     r1,=mm_achannels
+       ldr     r1,[r1]
+       mov     r0, #0                          @ load counter
+       mov     r2, #255                        @ r2 = MAXVOL+1 (highest)
+       add     r2, #1
+       mov     r3, #255                        @ r3 = 255 (none found)
+       b       .mppac_start
+
+.mppac_next:
+       add     r1, #MCA_SIZE           @ change pointer
+       add     r0, #1                  @ count
+.mppac_start:
+       lsr     r5, #1
+       bcs     .mppac_check
+       bne     .mppac_next
+.mppac_end:
+       mov     r0, r3                  @ if no disabled channels are found, use lowest volume channel
+.mppac_found:
+
+       pop     {r4,r5}
+       bx      lr
+       
+.mppac_check:
+       ldrb    r4, [r1, #MCA_TYPE]     @ check active channel type
+       cmp     r4, #ACHN_DISABLED      @ disabled?
+       beq     .mppac_found            @ if so, use this channel
+       cmp     r4, #ACHN_BACKGROUND    @ check if its a background channel
+       bne     .mppac_next
+       
+       ldrb    r4, [r1, #MCA_FVOL]     @ compare volumes
+       cmp     r4, r2                  @
+       bge     .mppac_next
+       mov     r3, r0                  @ save channel#
+       mov     r2, r4                  @ and volume
+       b       .mppac_next
+*/
+
+.pool
+
+@........................................................................................
+
+.thumb_func
+mpp_PatternPointer:
+       mov     r1, r8
+       ldr     r2,[r1,#MPL_SONGADR]
+       ldr     r1,[r1,#MPL_PATTTABLE]
+       lsl     r0, #2
+       ldr     r0,[r1,r0]
+       add     r0, r2
+       bx      lr
+.pool
+#ifdef FOO_UC
+.align 2
+.thumb_func
+mpp_GetPeriod:
+       @ r0 = note
+       @ r2 = tuning
+       @ CLOBBERS R1,R3
+       @ RETURN
+       @ r0 = IT/S3M PERIOD
+
+       mov     r1, r8
+       ldrb    r1, [r1, #MPL_FLAGS]
+       lsr     r1, #C_FLAGS_SS
+       bcs     .mpp_gp_linear
+.mpp_gp_amiga:
+       
+       mov     r3, r0
+       ldr     r0,=note_table_mod
+       ldrb    r1, [r0, r3]
+       sub     r0, #3*10
+       lsr     r3, #2
+       ldrb    r0, [r0, r3]
+       
+       @ r0 = octave
+       @ r1 = note
+       lsl     r1, #1
+       ldr     r3,=ST3_FREQTABLE
+       ldrh    r1, [r3, r1]
+       
+       ldr     r3,=133808
+       mul     r1, r3
+       lsr     r1, r0
+       
+       lsr     r0, r1, #3
+       mov     r1, r2
+       
+       swi     SWI_DIVIDE
+       lsl     r0, #3
+       
+       bx      lr
+
+.mpp_gp_linear:
+       ldr     r1,=IT_PitchTable
+       lsl     r0, #2
+       ldr     r0, [r1, r0]
+       
+       bx      lr
+
+#endif
+
+@=============================================================================
+@                                EFFECT MEMORY
+@=============================================================================
+
+.text
+
+mpp_effect_memmap_xm:
+.byte  0,0,0,0,2,3,4,0,0,5,0,6,7,0,0,8,9,10,11,0,0,0,0,12,0,0,0,0,0,0,13
+@      /,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q, R, S,T,U,V,W, X,Y,Z,0,1,2,3
+
+.equ   MPP_XM_VFX_MEM_VS,      12  @ $ud
+.equ   MPP_XM_VFX_MEM_FVS,     13  @ $ud
+.equ   MPP_XM_VFX_MEM_GLIS,    14  @ $0x
+.equ   MPP_XM_VFX_MEM_PANSL,   7   @ $lr
+
+mpp_effect_memmap_it:
+.byte  0,0,0,0,2,3,3,0,0,4,5,2,2,0,6,7,8,9,10,11,12,0,0,13,0,14,0
+@      /,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R, S, T, U,V,W, X,Y, Z
+mpp_veffect_memmap_it:
+
+.equ   MPP_IT_VFX_MEM,         14
+.equ   MPP_GLIS_MEM,           0
+.equ   MPP_IT_PORTAMEM,        2
+
+
+@ 0 means custom behavior, or disabled
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------
+mpp_Channel_ExchangeMemory:
+@-----------------------------------------------------------------------------
+       
+       @ r0 = effect#
+       @ r1 = param
+
+@ check flags for XM mode
+       
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_XS
+       bcs     1f
+
+@ IT effects 
+       
+       ldr     r2,=mpp_effect_memmap_it
+       b       2f
+       
+1:
+@ XM effects
+
+       ldr     r2,=mpp_effect_memmap_xm
+2:     ldrb    r2, [r2, r0]
+       sub     r2, #1
+       bcc     3f
+       
+@ if param=0 then load memory value, otherwise save param to memory
+       
+       add     r2, #MCH_MEMORY
+       cmp     r1, #0
+       bne     1f
+       ldrb    r1, [r7, r2]
+       strb    r1, [r7, #MCH_PARAM]
+1:     strb    r1, [r7, r2]
+       
+3:     bx      lr
+
+@----------------------------------------------------------
+mpp_Channel_ExchangeGxx:
+@----------------------------------------------------------
+
+       @ r1 = param
+
+
+
+/******************************************************************************
+ *
+ * Volume Commands
+ *
+ ******************************************************************************/
+
+
+
+/******************************************************************************
+ * mpp_Process_VolumeCommand()
+ *
+ * Process volume command
+ ******************************************************************************/
+                                       .global mpp_Process_VolumeCommand
+                                       .thumb_func
+mpp_Process_VolumeCommand:
+       
+       mov     r0, r8
+       ldrb    r2, [r0, #MPL_TICK]
+       ldr     r0, [r0, #MPL_SONGADR]
+       ldrb    r0, [r0, #C_MAS_FLAGS]
+       lsr     r0, #4
+       ldrb    r0, [r7, #MCH_VOLCMD]
+       bcc     .mppuv_it
+       b       .mppuv_xm
+.mppuv_it:
+
+@ determine which command to use 
+
+       cmp     r0, #64
+       ble     .mppuv_setvol
+       cmp     r0, #84
+       ble     .mppuv_fvol
+       cmp     r0, #104
+       ble     .mppuv_volslide
+       cmp     r0, #124
+       ble     .mppuv_porta
+       cmp     r0, #192
+       ble     .mppuv_panning
+       cmp     r0, #202
+       ble     .mppuv_glissando
+       cmp     r0, #212
+       ble     .mppuv_vibrato
+.mppuv_exit1:
+       bx      lr
+
+.align 2
+@-----------------------------------------------------------------------------------
+.mppuv_setvol:                                 @ SET VOLUME
+@-----------------------------------------------------------------------------------
+
+@ sets volume on tick0
+
+       cmp     r2, #0
+       bne     .mppuv_setvol_exit
+       strb    r0, [r7, #MCH_VOLUME]
+.mppuv_setvol_exit:
+       bx      lr                              @ exit
+
+.align 2
+@-----------------------------------------------------------------------------------
+.mppuv_fvol:                                   @ FINE VOLUME SLIDE UP/DOWN
+@-----------------------------------------------------------------------------------
+
+       cmp     r2, #0                          @ only slide on tick0
+       bne     .mppuv_exit1                    @ ..
+       ldrb    r1, [r7, #MCH_VOLUME]           @ load channel volume
+       mov     r2, #MCH_MEMORY+MPP_IT_VFX_MEM
+       cmp     r0, #75                         @ check slide direction
+       bge     .mppuv_fvoldown                 @ jump to slide down if value is 75+ (75-84 is slide down)
+.mppuv_fvolup:                                 @ ------ slide up ----------
+       sub     r0, #65                         @ 65->74 , 0->9
+.mppuv_volup:                                  @ ** entry for volume slide up
+       bne     1f                              @ is value 0?
+       ldrb    r0, [r7, r2]                    @  then fetch value from memory
+1:     strb    r0, [r7, r2]                    @ save value
+.mppuv_volupA:
+       add     r1, r0                          @ add to volume
+       cmp     r1, #64                         @ clamp to 0->64
+       blt     .mppuv_fvol_exit                @ ..
+       mov     r1, #64                         @ ..
+       b       .mppuv_fvol_exit                @ ..
+.mppuv_fvoldown:                               @ ------ slide down --------
+       sub     r0, #75                         @ 75->84 , 0->9
+.mppuv_voldown:                                        @ ** entry for volume slide down
+       bne     1f                              @ is value 0?
+       ldrb    r0, [r7, r2]                    @  then fetch value from memory
+1:     strb    r0, [r7, r2]                    @ save value
+.mppuv_voldownA:
+       sub     r1, r0                          @ subtract from volume
+       bcs     .mppuv_fvol_exit                @ check overflow and clamp
+       mov     r1, #0                          @ ..
+.mppuv_fvol_exit:                              @ ..
+       strb    r1, [r7, #MCH_VOLUME]           @ store volume
+.mppuv_exit2:
+       bx      lr                              @ exit function
+       
+.align 2
+@----------------------------------------------------------------------------------
+.mppuv_volslide:                               @ VOLUME SLIDE UP/DOWN
+@----------------------------------------------------------------------------------
+
+       cmp     r2, #0                          @ only slide on other ticks
+       beq     .mppuv_exit1                    @ ..
+       ldrb    r1, [r7, #MCH_VOLUME]           @ get volume
+       cmp     r0, #95                         @ check slide direction
+       bge     .mppuv_vs_down                  
+.mppuv_vs_up:                                  @ slide up...
+       mov     r2, #MCH_MEMORY+MPP_IT_VFX_MEM
+       sub     r0, #85                         @ 85->94 , 0->9
+       b       .mppuv_volup                    @ branch to function (use fvol code)
+
+.mppuv_vs_down:                                        @ slide down...
+       mov     r2, #MCH_MEMORY+MPP_IT_VFX_MEM
+       sub     r0, #95                         @ 95->104 , 0->9
+       b       .mppuv_voldown                  @ branch to function (use fvol code)
+
+.align 2
+@---------------------------------------------------------------------------------------
+.mppuv_porta:                                  @ PORTAMENTO UP/DOWN
+@---------------------------------------------------------------------------------------
+
+       cmp     r2, #0                          @ only slide on other ticks
+       beq     .mppuv_exit2
+       
+       push    {lr}                            @ save return address
+       mov     r1, r0                          @ get period value
+       mov     r0, #MCH_PERIOD
+       ldr     r0, [r7, r0]
+       
+       cmp     r1, #115                        @ check slide direction
+       bge     .mppuv_porta_up
+.mppuv_porta_down:
+       sub     r1, #105                        @ map value 0->9
+       lsl     r1, #2                          @ volume command slides are
+       bne     1f
+       ldrb    r1, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+1:     strb    r1, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+       bl      mpph_PitchSlide_Down            @ equal to normal slides *4
+       b       .mppuv_porta_set
+       
+.mppuv_porta_up:
+       sub     r1, #115                        @ slide up...
+       lsl     r1, #2
+       bne     1f
+       ldrb    r1, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+1:     strb    r1, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+       bl      mpph_PitchSlide_Up
+
+.mppuv_porta_set:
+       mov     r2, #MCH_PERIOD                 @ store new period
+       ldr     r1, [r7, r2]
+       str     r0, [r7, r2]
+       sub     r0, r1                          @ and edit temp period
+       add     r5, r0
+       
+       pop     {r0}
+       bx      r0
+//     pop     {pc}                            @ exit
+
+.align 2
+@---------------------------------------------------------------------------------------
+.mppuv_panning:                                        @ SET PANNING
+@---------------------------------------------------------------------------------------
+
+       cmp     r2, #0                          @ only set on tick 0
+       bne     .mppuv_exit1                    @ ..
+       sub     r0, #128                        @ map to 0->64
+       lsl     r0, #2
+       cmp     r0, #255
+       blt     .mppuv_p_store
+       mov     r0, #255
+.mppuv_p_store:
+       strb    r0, [r7, #MCH_PANNING]          @ save to active channel
+.mppuv_p_exit:
+       bx      lr                              @ exit
+
+.align 2
+@---------------------------------------------------------------------------------------
+.mppuv_glissando:                              @ GLISSANDO
+@---------------------------------------------------------------------------------------
+
+       cmp     r2, #0
+       beq     .mppuv_p_exit
+
+       sub     r0, #193
+       ldr     r1,=vcmd_glissando_table
+       ldrb    r0, [r1, r0]
+
+       mov     r1, r8
+       ldrb    r1, [r1, #MPL_FLAGS]
+       lsr     r1, #C_FLAGS_GS
+       bcs     2f
+@ single gxx
+
+       cmp     r0, #0
+       beq     1f
+       ldrb    r0, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+1:     strb    r0, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+       b       .mppe_glis_ot
+
+2: @ shared gxx
+
+       cmp     r0, #0
+       beq     1f
+       ldrb    r0, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+1:     strb    r0, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+       strb    r0, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+       
+       b       .mppe_glis_ot
+.pool
+
+vcmd_glissando_table:
+.byte  0,1,4,8,16,32,64,96,128,255
+
+.align 2
+@---------------------------------------------------------------------------------------
+.mppuv_vibrato:                                        @ VIBRATO (SPEED)
+@---------------------------------------------------------------------------------------
+       
+       @ vibrato... sets speed
+       cmp     r2, #0
+       beq     .mppuv_vib_exit
+       sub     r0, #203
+       beq     1f
+       lsl     r0, #2
+       strb    r0, [r7, #MCH_VIBSPD]
+1:     b       mppe_DoVibrato
+.mppuv_vib_exit:
+       bx      lr
+
+
+
+/******************************************************************************
+ *
+ * XM Volume Commands
+ *
+ ******************************************************************************/
+
+
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------------
+.mppuv_xm:
+@---------------------------------------------------------------------------------------
+
+@ determine command type 
+       
+       cmp     r0, #0          @ 0 = none
+       beq     .mppuv_exit4
+       cmp     r0, #0x50
+       ble     .mppuv_xm_setvol
+       cmp     r0, #0x80
+       blt     .mppuv_xm_volslide
+       cmp     r0, #0xA0
+       blt     .mppuv_xm_fvolslide
+       cmp     r0, #0xC0
+       blt     .mppuv_xm_vibrato
+       cmp     r0, #0xD0
+       blt     .mppuv_xm_panning
+       cmp     r0, #0xF0
+       blt     .mppuv_xm_panslide
+       b       .mppuv_xm_toneporta
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+.mppuv_xm_setvol:                                      @ Set Volume
+@----------------------------------------------------------------------------------------
+
+       cmp     R2, #0
+       bne     .mppuv_exit4
+       sub     r0, #0x10
+       strb    r0, [r7, #MCH_VOLUME]
+.mppuv_exit4:
+       bx      lr
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+.mppuv_xm_volslide:                                    @ Volume Slide
+@----------------------------------------------------------------------------------------
+
+       cmp     r2, #0
+       beq     .mppuv_exit2
+       ldrb    r1, [r7, #MCH_VOLUME]
+       mov     r3, #MCH_MEMORY+MPP_XM_VFX_MEM_VS
+       ldrb    r2, [r7, r3]
+       cmp     r0, #0x70
+       bge     .mppuv_xm_volslide_up
+       sub     r0, #0x60
+.mppuv_xm_volslide_dn_check:
+       bne     1f
+       mov     r0, r2
+       lsl     r0, #32-4
+       lsr     r0, #32-4
+       b       2f
+1:     lsr     r2, #4
+       lsl     r2, #4
+       orr     r2, r0
+       strb    r2, [r7, r3]    
+2:     b       .mppuv_voldownA
+
+.mppuv_xm_volslide_up:
+       sub     r0, #0x70
+.mppuv_xm_volslide_up_check:
+       bne     1f
+       mov     r0, r2
+       lsr     r0, #4
+       b       2f
+1:     lsl     r2, #32-4
+       lsr     r2, #32-4
+       lsl     r0, #4
+       orr     r2, r0
+       lsr     r0, #4
+       strb    r2, [r7, r3]
+2:     b       .mppuv_volupA
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+.mppuv_xm_fvolslide:                                   @ Fine Volume Slide
+@----------------------------------------------------------------------------------------
+
+       cmp     r2, #0
+       bne     .mppuv_exit4
+       ldrb    r1, [r7, #MCH_VOLUME]
+       mov     r3, #MCH_MEMORY+MPP_XM_VFX_MEM_FVS
+       ldrb    r2, [r7, r3]
+       cmp     r0, #0x90
+       bge     .mppuv_xm_fvolslide_up
+       sub     r0, #0x80
+       b       .mppuv_xm_volslide_dn_check
+.mppuv_xm_fvolslide_up:
+       sub     r0, #0x90
+       b       .mppuv_xm_volslide_up_check
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+.mppuv_xm_vibrato:                                     @ Vibrato
+@----------------------------------------------------------------------------------------
+       
+       @ xm vibrato
+       @ sets speed or depth
+       
+       cmp     r2, #0
+       beq     .mppuv_xm_vibexit
+       cmp     r0, #0xB0
+       bge     .mppuv_xm_vibdepth
+
+.mppuv_xm_vibspd:
+       sub     r0, #0xA0
+       lsl     r0, #2
+       beq     1f
+       strb    r0, [r7, #MCH_VIBSPD]
+1:     b       mppe_DoVibrato
+       
+.mppuv_xm_vibdepth:
+       sub     r0, #0xB0
+       lsl     r0, #3
+       beq     1f
+       strb    r0, [r7, #MCH_VIBDEP]
+1:     b       mppe_DoVibrato
+       
+.mppuv_xm_vibexit:
+       bx      lr
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+.mppuv_xm_panning:                                     @ Panning
+@----------------------------------------------------------------------------------------
+       
+       cmp     r2, #0
+       bne     .mppuv_exit3
+       sub     r0, #0xC0
+       lsl     r0, #4
+       cmp     r0, #240
+       beq     .mppuv_xm_panhack
+       strb    r0, [r7, #MCH_PANNING]
+       bx      lr
+.mppuv_xm_panhack:
+       mov     r0, #255
+       strb    r0, [r7, #MCH_PANNING]
+.mppuv_exit3:
+       bx      lr
+       
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+.mppuv_xm_panslide:                                    @ Panning Slide
+@----------------------------------------------------------------------------------------
+       
+       cmp     r2, #0
+       beq     .mppuv_exit3
+       ldrb    r2, [r7, #MCH_PANNING]
+       ldrb    r3, [r7, #MCH_MEMORY + MPP_XM_VFX_MEM_PANSL]
+       cmp     r0, #0xE0
+       bge     .mppuv_xm_panslide_right
+       sub     r0, #0xD0
+       bne     1f
+       lsr     r0, r3, #4
+       b       2f
+1:     lsl     r3, #32-4
+       lsr     r3, #32-4
+       lsl     r0, #4
+       orr     r3, r0
+       lsr     r0, #4
+       strb    r3, [r7, #MCH_MEMORY + MPP_XM_VFX_MEM_PANSL]
+
+2:     lsl     r0, #2
+       sub     r2, r0
+       bcs     .mppuv_xm_panslide_set
+       mov     r2, #0
+       b       .mppuv_xm_panslide_set
+.mppuv_xm_panslide_right:
+       sub     r0, #0xE0
+       bne     1f
+       lsl     r0, r3, #32-4
+       lsr     r0, #32-4
+       b       2f
+1:     lsr     r3, #4
+       lsl     r3, #4
+       orr     r3, r0
+       strb    r3, [r7, #MCH_MEMORY + MPP_XM_VFX_MEM_PANSL]
+2:     lsl     r0, #2
+       add     r2, r0
+       cmp     r2, #255
+       blt     .mppuv_xm_panslide_set
+       mov     r2, #255
+.mppuv_xm_panslide_set:
+       strb    r2, [r7, #MCH_PANNING]
+       bx      lr
+
+.align 2
+.thumb_func
+@-------------------------------------------------------------------------------------
+.mppuv_xm_toneporta:                                   @ Glissando
+@-------------------------------------------------------------------------------------
+
+       @ glissando...
+       @ on nonzero ticks, do a regular glissando slide at speed * 16
+       cmp     r2, #0
+       beq     1f
+       
+       sub     r0, #0xF0
+       lsl     r0, #4
+       beq     2f
+       mov     r1, #MCH_MEMORY+MPP_XM_VFX_MEM_GLIS
+       strb    r0, [r7, r1]
+2:     ldrb    r0, [r7, r1]
+       mov     r1, r0
+       
+       b       .mppe_glis_backdoor
+       
+1:     bx      lr
+
+
+
+/******************************************************************************
+ *
+ * Module Effects
+ *
+ ******************************************************************************/
+
+
+
+/******************************************************************************
+ * mpp_ProcessEffect()
+ *
+ * Process pattern effect.
+ ******************************************************************************/
+                                               .global mpp_Process_Effect
+                                               .thumb_func
+mpp_Process_Effect:
+
+       push    {lr}
+       ldrb    r0, [r7, #MCH_EFFECT]   @ get effect#
+       ldrb    r1, [r7, #MCH_PARAM]    @ r1 = param
+       bl      mpp_Channel_ExchangeMemory
+
+       lsl     r0, #1
+
+       pop     {r2}
+       mov     lr, r2
+
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_TICK]     @ r2 = tick#
+
+       cmp     r2, #0                  @ Z flag = tick0 :)
+       add     r0, pc
+       mov     pc, r0
+       
+       b       mppe_todo
+       b       mppe_SetSpeed
+       b       mppe_PositionJump
+       b       mppe_PatternBreak
+       b       mppe_VolumeSlide
+       b       mppe_Portamento
+       b       mppe_Portamento
+       b       mppe_Glissando
+       b       mppe_Vibrato
+       b       mppe_todo
+       b       mppe_Arpeggio
+       b       mppe_VibratoVolume
+       b       mppe_PortaVolume
+       b       mppe_ChannelVolume
+       b       mppe_ChannelVolumeSlide
+       b       mppe_SampleOffset
+       b       mppe_todo
+       b       mppe_Retrigger
+       b       mppe_Tremolo            @ tremolo
+       b       mppe_Extended
+       b       mppe_SetTempo
+       b       mppe_FineVibrato
+       b       mppe_SetGlobalVolume
+       b       mppe_GlobalVolumeSlide
+       b       mppe_SetPanning
+       b       mppe_Panbrello
+       b       mppe_ZXX
+       b       mppe_SetVolume
+       b       mppe_KeyOff
+       b       mppe_EnvelopePos
+       b       mppe_OldTremor
+.pool
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_SetSpeed:                         @ EFFECT Axy: SET SPEED
+@---------------------------------------------------------------------------------
+
+       bne     .mppe_ss_exit           @ dont set on nonzero ticks
+       cmp     r1, #0
+       beq     .mppe_ss_exit
+       mov     r0, r8
+       
+       strb    r1, [r0, #MPL_SPEED]
+.mppe_ss_exit:
+.thumb_func
+mppe_todo:
+       bx      lr                      @ exit
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_PositionJump:                     @ EFFECT Bxy: SET POSITION
+@---------------------------------------------------------------------------------
+
+       bne     .mppe_pj_exit           @ skip nonzero ticks
+       mov     r0, r8
+       strb    r1, [r0, #MPL_PATTJUMP]
+.mppe_pj_exit:
+       bx      lr                      @ exit
+.pool
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_PatternBreak:                             @ EFFECT Cxy: PATTERN BREAK
+@---------------------------------------------------------------------------------
+       bne     .mppe_pb_exit                   @ skip nonzero ticks
+       mov     r0, r8                          @ get variables
+       strb    r1, [r0, #MPL_PATTJUMP_ROW]     @ save param to row value
+       ldrb    r1, [r0, #MPL_PATTJUMP]         @ check if pattjump=empty
+       cmp     r1, #255                        @ 255=empty
+       bne     .mppe_pb_exit                   @ ...
+       ldrb    r1, [r0, #MPL_POSITION]         @ if empty, set pattjump=position+1
+       add     r1, #1
+       strb    r1, [r0, #MPL_PATTJUMP]
+.mppe_pb_exit:
+       bx      lr                              @ finished
+.pool
+
+.align 2
+.thumb_func
+@------------------------------------------------------------------------------------------
+mppe_VolumeSlide:                              @ EFFECT Dxy: VOLUME SLIDE
+@------------------------------------------------------------------------------------------
+
+       push    {lr}
+       ldrb    r0, [r7, #MCH_VOLUME]           @ load volume
+       
+       bl      mpph_VolumeSlide64
+       
+       strb    r0, [r7, #MCH_VOLUME]           @ save volume
+.mppe_vs_zt:
+       pop     {r0}
+       bx      r0
+//     pop     {pc}                            @ exit
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------
+mppe_Portamento:                               @ EFFECT Exy/Fxy: Portamento
+@----------------------------------------------------------------------------------
+
+       push    {lr}
+       
+.mppe_pd_ot:
+       mov     r3, #0
+       mov     r0, r1
+       lsr     r0, #4                          @ test for Ex param (Extra fine slide)
+       cmp     r0, #0xE                        @ ..
+.mppe_pd_checkE:                               @ ..
+       bne     .mppe_pd_checkF                 @ ..
+       cmp     r2, #0                          @ Extra fine slide: only slide on tick0
+       bne     .mppe_pd_exit                   @ ..
+       lsl     r1, #32-4                       @ mask out slide value
+       lsr     r1, #32-4                       @ ..
+       mov     r3, #1
+       b       .mppe_pd_otherslide             @ skip the *4 multiplication
+.mppe_pd_checkF:                               @ ------------------------------------
+       cmp     r0, #0xF                        @ test for Fx param (Fine slide)
+       bne     .mppe_pd_regslide               @ ..
+       cmp     r2, #0                          @ Fine slide: only slide on tick0
+       bne     .mppe_pd_exit                   @ ..
+       lsl     r1, #32-4                       @ mask out slide value
+       lsr     r1, #32-4                       @ ..
+       b       .mppe_pd_otherslide
+.mppe_pd_regslide:
+       cmp     r2, #0
+       beq     .mppe_pd_exit
+.mppe_pd_otherslide:
+       
+       ldrb    r0, [r7, #MCH_EFFECT]           @ check slide direction
+       mov     r2, #MCH_PERIOD
+       cmp     r0, #5                          @ .. (5 = portamento down)
+       ldr     r0, [r7, r2]                    @ get period
+       
+       bne     .mppe_pd_slideup                @ branch to function
+.mppe_pd_slidedown:                            @ -------SLIDE DOWN-------
+       
+       cmp     r3, #0
+       bne     .mppe_pd_fineslidedown
+       bl      mpph_PitchSlide_Down
+       
+       b       .mppe_pd_store                  @ store & exit
+
+.mppe_pd_fineslidedown:
+       bl      mpph_FinePitchSlide_Down
+       b       .mppe_pd_store
+       
+.mppe_pd_slideup:                              @ ---------SLIDE UP---------
+       
+       cmp     r3, #0
+       bne     .mppe_pd_fineslideup
+       bl      mpph_PitchSlide_Up
+       b       .mppe_pd_store
+.mppe_pd_fineslideup:
+       bl      mpph_FinePitchSlide_Up
+
+.mppe_pd_store:
+       mov     r2, #MCH_PERIOD
+       ldr     r1, [r7, #MCH_PERIOD]
+       str     r0, [r7, #MCH_PERIOD]
+       sub     r0, r1
+       add     r5, r0
+.mppe_pd_exit:
+       pop     {r0}
+       bx      r0                              @ exit
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Glissando:                                        @ EFFECT Gxy: Glissando
+@---------------------------------------------------------------------------------
+
+       bne     .mppe_glis_ot
+       
+       mov     r0, r8
+       ldrb    r0, [r0, #MPL_FLAGS]
+       lsr     r0, #C_FLAGS_GS
+       bcc     2f
+       @ gxx is shared, IT MODE ONLY!!
+       cmp     r1, #0
+       bne     3f
+       ldrb    r1, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+       strb    r1, [r7, #MCH_PARAM]
+3:     strb    r1, [r7, #MCH_MEMORY+MPP_IT_PORTAMEM]
+       strb    r1, [r7, #MCH_MEMORY+MPP_GLIS_MEM]      @ for simplification later
+       b       .mppe_glis_ot
+       
+2:     @ gxx is separate
+       cmp     r1, #0
+       bne     3f
+       
+       ldrb    r1, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+       strb    r1, [r7, #MCH_PARAM]
+3:     strb    r1, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+
+       bx      lr
+//     b       .mppe_glis_exit
+.mppe_glis_ot:
+       
+       push    {lr}                                    // save return address
+
+       ldrb    r1, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+
+.mppe_glis_backdoor:
+
+       push    {r1}
+       
+       cmp     r6, #0                                  // exit if no active channel
+       bne     1f                                      //
+       pop     {r1,r3}                                 //
+       bx      r3                                      //
+1:                                                     //
+       
+       ldrb    r0, [r6, #MCA_SAMPLE]                   // get target period
+       sub     r0, #1                                  //
+       mpp_SamplePointer                               //
+       ldrh    r1, [r0, #C_MASS_FREQ]                  //
+       LSL     R1, #2                                  //
+       ldrb    r2, [r7, #MCH_NOTE]                     //
+       ldr     r3,=mmGetPeriod                         //
+       bl      mpp_call_r3                             //
+       
+       pop     {r1}                                    // r1 = parameter
+       push    {r0}                                    // 
+       
+       mov     r3, r0                                  // r3 = target period
+       mov     r2, #MCH_PERIOD                         // r0 = current period
+       ldr     r0, [r7, r2]                            //
+       mov     r2, r8                                  // test S flag
+       ldrb    r2, [r2, #MPL_FLAGS]                    //
+       lsr     r2, #C_FLAGS_SS                         //
+       bCC     .mppe_glis_amiga
+       cmp     r0, r3
+       blt     .mppe_glis_slideup
+       bgt     .mppe_glis_slidedown
+.mppe_glis_noslide:
+       pop     {r0}
+       pop     {r3}
+       bx      r3
+
+.mppe_glis_slideup:
+       bl      mpph_PitchSlide_Up
+       pop     {r1}
+       
+       cmp     r0, r1
+       blt     .mppe_glis_store
+       mov     r0, r1
+       b       .mppe_glis_store
+.mppe_glis_slidedown:
+       bl      mpph_PitchSlide_Down
+       pop     {r1}
+       cmp     r0, r1
+       bgt     .mppe_glis_store
+       mov     r0, r1
+.mppe_glis_store:
+       
+       mov     r2, #MCH_PERIOD
+       ldr     r1, [r7, r2] @#MCA_PERIOD]
+       str     r0, [r7, r2] @#MCA_PERIOD]
+       sub     r0, r1
+       add     r5, r0
+       
+.mppe_glis_exit:
+       pop     {r3}
+       bx      r3
+
+
+       //bx    lr
+
+.mppe_glis_amiga:
+
+       cmp     r0, r3
+       blt     .mppe_glis_amiga_up
+       bgt     .mppe_glis_amiga_down
+       pop     {r0}
+       pop     {r3}
+       bx      r3
+
+.mppe_glis_amiga_down:
+       bl      mpph_PitchSlide_Up
+       pop     {r1}
+       cmp     r0, r1
+       bgt     .mppe_glis_store
+       mov     r0, r1
+       b       .mppe_glis_store
+.mppe_glis_amiga_up:
+       bl      mpph_PitchSlide_Down
+       pop     {r1}
+       cmp     r0, r1
+       blt     .mppe_glis_store
+       mov     r0, r1
+       b       .mppe_glis_store
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Vibrato:                                  @ EFFECT Hxy: Vibrato
+@---------------------------------------------------------------------------------
+
+       bne     .mppe_v_ot
+       lsr     r0, r1, #4                      @ if (x != 0) {
+       beq     .mppe_v_nospd                   @   speed = 4*x;
+       lsl     r0, #2                          @   ..
+       strb    r0, [r7, #MCH_VIBSPD]           @   ..
+.mppe_v_nospd:
+       
+       lsl     r0, r1, #32-4                   @ if (y != 0) {
+       beq     .mppe_v_nodep                   @ ..
+       lsr     r0, #32-6                       @   depth = y * 4;
+       mov     r1, r8
+       ldrb    r1, [r1, #MPL_OLDEFFECTS]       @   if(OldEffects)
+       lsl     r0, r1                          @      depth <<= 1;
+       strb    r0, [r7, #MCH_VIBDEP]           @
+       b       mppe_DoVibrato
+.mppe_v_nodep:
+       BX      LR
+
+.align 2
+.thumb_func
+@-------------------------------------------------------
+mppe_DoVibrato:
+@-------------------------------------------------------
+
+.mppe_v_ot:
+       mov     r0, r8
+       ldrb    r1, [r0, #MPL_TICK]
+       ldrb    r0, [r0, #MPL_OLDEFFECTS]
+       
+       cmp     r0, #0
+       beq     .mppe_dv_update
+       cmp     r1, #0
+       bne     .mppe_dv_update
+       
+       push    {lr}
+       ldrb    r1, [r7, #MCH_VIBPOS]
+       b       .mppe_dv_notupdate
+.mppe_dv_update:
+       push    {lr}
+       ldrb    r0, [r7, #MCH_VIBSPD]
+       ldrb    r1, [r7, #MCH_VIBPOS]
+       add     r1, r0
+       lsl     r1, #32-8
+       lsr     r1, #32-8
+       strb    r1, [r7, #MCH_VIBPOS]
+.mppe_dv_notupdate:
+       ldr     r2,=mpp_TABLE_FineSineData
+       ldrsb   r1, [r2, r1]
+       ldrb    r0, [r7, #MCH_VIBDEP]
+       mul     r1, r0
+       asr     r1, #8
+       mov     r0, r5
+       cmp     r1, #0
+       blt     .mppe_dv_negative
+       bl      mpph_PitchSlide_Up
+       b       .mppe_dv_store
+.mppe_dv_negative:
+       neg     r1, r1
+       bl      mpph_PitchSlide_Down
+.mppe_dv_store:
+       mov     r5, r0
+       pop     {r0}
+       bx      r0
+//     pop     {pc}            @ return THUMB
+.pool
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Tremor:                                   @ EFFECT Ixy: Tremor
+@---------------------------------------------------------------------------------
+       bx      lr
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Arpeggio:                                 @ EFFECT Jxy: Arpeggio
+@---------------------------------------------------------------------------------
+
+       bne     .mppe_arp_ot
+
+       mov     r0, #0
+       strb    r0, [r7, #MCH_FXMEM]
+.mppe_arp_ot:
+       cmp     r6, #0
+       beq     1f
+       ldrb    r0, [r7, #MCH_FXMEM]
+//     ldrb    r3, [r6, #MCA_SAMPLE]   ?               ???     
+       cmp     r0, #1
+       bgt     .mppe_arp_2
+       beq     .mppe_arp_1
+.mppe_arp_0:
+       mov     r0, #1
+       strb    r0, [r7, #MCH_FXMEM]
+       @ do nothing! :)
+1:     bx      lr
+       
+.mppe_arp_1:
+       
+       mov     r0, #2                  @ set next tick to '2'
+       strb    r0, [r7, #MCH_FXMEM]    @ save...
+       mov     r0, r5
+       lsr     r1, #4                  @ mask out high nibble of param
+.mppe_arp_others:
+       mov     r2, r5
+       cmp     r1, #12                 @ see if its >= 12
+       blt     .mppea1_12              @ ..
+       add     r2, r5                  @  add period if so... (octave higher)
+.mppea1_12:                            @  ..
+       lsl     r1, #4                  @ *16*hword
+       
+       mov     r0, r5
+       push    {lr}
+       bl      mpph_LinearPitchSlide_Up
+
+       mov     r5, r0
+       pop     {r0}
+       bx      r0
+//     pop     {pc}
+       
+.mppe_arp_2:
+       mov     r0, #0
+       strb    r0, [r7, #MCH_FXMEM]
+       mov     r0, r5
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       b       .mppe_arp_others
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_VibratoVolume:                    @ EFFECT Kxy: Vibrato+Volume Slide
+@---------------------------------------------------------------------------------
+
+       push    {lr}
+       push    {r1,r2}
+       bl      mppe_DoVibrato
+       pop     {r1,r2}
+       cmp     r2, #0
+       bl      mppe_VolumeSlide
+       pop     {r0}
+       bx      r0
+//     pop     {pc}
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_PortaVolume:                      @ EFFECT Lxy: Portamento+Volume Slide
+@---------------------------------------------------------------------------------
+
+       push    {lr}
+       push    {r1,r2}
+       ldrb    r1, [r7, #MCH_MEMORY+MPP_GLIS_MEM]
+       bl      mppe_Glissando
+       pop     {r1, r2}
+       cmp     r2, #0
+       bl      mppe_VolumeSlide
+       pop     {r0}
+       bx      r0
+//     pop     {pc}
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_ChannelVolume:                            @ EFFECT Mxy: Set Channel Volume
+@---------------------------------------------------------------------------------
+
+       bne     .mppe_cv_exit                   @ quite simple...
+       cmp     r1, #0x40
+       bgt     .mppe_cv_exit                   @ ignore command if parameter is > 0x40
+       strb    r1, [r7, #MCH_CVOLUME]
+.mppe_cv_exit:
+       bx      lr
+       
+.align 2
+.thumb_func
+@------------------------------------------------------------------------------------
+mppe_ChannelVolumeSlide:                       @ EFFECT Nxy: Channel Volume Slide
+@------------------------------------------------------------------------------------
+
+       push    {lr}
+
+       ldrb    r0, [r7, #MCH_CVOLUME]          @ load volume
+       bl      mpph_VolumeSlide64
+
+       strb    r0, [r7, #MCH_CVOLUME]          @ save volume
+       pop     {r0}
+       bx      r0
+//     pop     {pc}                            @ exit
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------
+mppe_SampleOffset:                             @ EFFECT Oxy Sample Offset
+@----------------------------------------------------------------------------------
+
+       bne     .mppe_so_exit                   @ skip on other ticks
+
+       ldr     r0,=mpp_vars
+       strb    r1, [r0, #MPV_SAMPOFF]          @ set offset
+.mppe_so_exit:
+       bx      lr
+.pool
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_PanningSlide:                             @ EFFECT Pxy Panning Slide
+@---------------------------------------------------------------------------------
+
+       push    {lr}
+
+       mov     r0, #255
+       push    {r0}
+       ldrb    r0, [r7, #MCH_PANNING]          @ load panning
+       
+       bl      mpph_VolumeSlide
+
+       strb    r0, [r7, #MCH_PANNING]          @ save panning
+       pop     {r0}
+       bx      r0
+//     pop     {pc}                            @ exit
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Retrigger:                                        @ EFFECT Qxy Retrigger Note
+@---------------------------------------------------------------------------------
+
+       ldrb    r0, [r7, #MCH_FXMEM]
+       cmp     r0, #0
+       bne     .mppe_retrig_refillN
+.mppe_retrig_refill:
+       lsl     r0, r1, #32-4
+       lsr     r0, #32-4
+       add     r0, #1
+.mppe_retrig_exitcount:
+       strb    r0, [r7, #MCH_FXMEM]
+       bx      lr
+.mppe_retrig_refillN:
+       sub     r0, #1
+       cmp     r0, #1
+       bne     .mppe_retrig_exitcount
+.mppe_retrig_fire:
+       
+       ldrb    r2, [r7, #MCH_VOLUME]
+       lsr     r0, r1, #4
+       beq     .mppe_retrig_v_change0
+       cmp     r0, #5
+       ble     .mppe_retrig_v_change_sub
+       cmp     r0, #6
+       beq     .mppe_retrig_v_change_23
+       cmp     r0, #7
+       beq     .mppe_retrig_v_change_12
+       cmp     r0, #8
+       beq     .mppe_retrig_v_change0
+       cmp     r0, #0xD
+       ble     .mppe_retrig_v_change_add
+       cmp     r0, #0xE
+       beq     .mppe_retrig_v_change_32
+       cmp     r0, #0xF
+       beq     .mppe_retrig_v_change_21
+.mppe_retrig_v_change_21:
+       lsl     r2, #1
+       cmp     r2, #64
+       blt     .mppe_retrig_finish
+       mov     r2, #64
+.mppe_retrig_v_change0:
+       b       .mppe_retrig_finish
+.mppe_retrig_v_change_sub:
+       sub     r0, #1
+       mov     r3, #1
+       lsl     r3, r0
+       sub     r2, r3
+       bcs     .mppe_retrig_finish
+       mov     r2, #0
+       b       .mppe_retrig_finish
+.mppe_retrig_v_change_23:
+       mov     r0, #171
+       mul     r2, r0
+       lsr     r2, #8
+       b       .mppe_retrig_finish
+.mppe_retrig_v_change_12:
+       lsr     r2, #1
+       b       .mppe_retrig_finish
+.mppe_retrig_v_change_add:
+       sub     r0, #9
+       mov     r3, #1
+       lsl     r3, r0
+       add     r2, r3
+       cmp     r2, #64
+       blt     .mppe_retrig_finish
+       mov     r2, #64
+       b       .mppe_retrig_finish
+.mppe_retrig_v_change_32:
+       mov     r0, #192
+       mul     r2, r0
+       lsr     r2, #7
+.mppe_retrig_finish:
+       strb    r2, [r7, #MCH_VOLUME]
+       cmp     r6, #0
+       beq     .mppe_retrig_refill
+       ldrb    r0, [r6, #MCA_FLAGS]
+       mov     r2, #MCAF_START
+       orr     r0, r2
+       strb    r0, [r6, #MCA_FLAGS]
+       b       .mppe_retrig_refill
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Tremolo:                                  @ EFFECT Rxy: Tremolo
+@---------------------------------------------------------------------------------
+
+@ r1 = param
+@ Z = tick0
+
+       beq     .mppe_trem_zt                   @ skip this part on tick0
+.mppe_trem_ot:
+       @ X = speed, Y = depth
+       ldrb    r0, [r7, #MCH_FXMEM]            @ get sine position
+       lsr     r3, r1, #4                      @ mask out SPEED
+       lsl     r3, #2                          @ speed*4 to compensate for larger sine table
+       add     r0, r3                          @ add to position
+       strb    r0, [r7, #MCH_FXMEM]            @ save (value & 255)
+.mppe_trem_zt:
+       ldrb    r0, [r7, #MCH_FXMEM]            @ get sine position
+       ldr     r3,=mpp_TABLE_FineSineData      @ load sine table value
+       ldrsb   r0, [r3, r0]
+       lsl     r1, #32-4                       @ mask out DEPTH
+       lsr     r1, #32-4
+       mul     r0, r1                          @ SINE*DEPTH / 64
+       asr     r0, #6
+       mov     r1, r8
+       ldrb    r1, [r1, #MPL_FLAGS]
+       lsr     r1, #C_FLAGS_XS
+       bcs     1f
+       asr     r0, #1
+1:     ldr     r1,=mpp_vars                    @ set volume addition variable
+       strb    r0, [r1, #MPV_VOLPLUS]
+       bx      lr
+.pool
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_Extended:                         @ EFFECT Sxy: Extended Effects
+@---------------------------------------------------------------------------------
+       
+       lsr     r0, r1, #4
+       lsl     r0, #1
+       cmp     r2, #0
+       add     r0, pc
+       mov     pc, r0
+       
+       @ branch table...
+       b       mppex_XM_FVolSlideUp    @ S0x
+       b       mppex_XM_FVolSlideDown  @ S1x
+       b       mppex_OldRetrig         @ S2x
+       b       mppex_VibForm           @ S3x
+       b       mppex_TremForm          @ S4x
+       b       mppex_PanbForm          @ S5x
+       b       mppex_FPattDelay        @ S6x
+       b       mppex_InstControl       @ S7x
+       b       mppex_SetPanning        @ S8x
+       b       mppex_SoundControl      @ S9x
+       b       mppex_HighOffset        @ SAx
+       b       mppex_PatternLoop       @ SBx
+       b       mppex_NoteCut           @ SCx
+       b       mppex_NoteDelay         @ SDx
+       b       mppex_PatternDelay      @ SEy
+       b       mppex_SongMessage       @ SFx
+
+.mppe_ex_quit:
+
+@-------------------------------------------
+.thumb_func
+mppex_Unused:
+       bx      lr
+
+@-------------------------------------------
+
+.thumb_func
+mppex_XM_FVolSlideUp:
+       bne     2f
+       ldrb    r0, [r7, #MCH_VOLUME]
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       add     r0, r1
+       cmp     r0, #64
+       blt     1f
+       mov     r0, #64
+1:     strb    r0, [r7, #MCH_VOLUME]
+2:     bx      lr
+
+.thumb_func
+mppex_XM_FVolSlideDown:
+       bne     2f
+       ldrb    r0, [r7, #MCH_VOLUME]
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       sub     r0, r1
+       bcs     1f
+       mov     r0, #0
+1:     strb    r0, [r7, #MCH_VOLUME]
+2:     bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_OldRetrig:
+       bne     1f
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       strb    r1, [r7, #MCH_FXMEM]
+       bx      lr
+       
+1:     ldrb    r0, [r7, #MCH_FXMEM]
+       sub     r0, #1
+
+       bne     1f
+       
+       lsl     r0, r1, #32-4
+       lsr     r0, #32-4
+       strb    r0, [r7, #MCH_FXMEM]
+
+       cmp     r6, #0
+       beq     1f
+       ldrb    r1, [r6, #MCA_FLAGS]
+       mov     r2, #MCAF_START
+       
+       orr     r1, r2
+       strb    r1, [r6, #MCA_FLAGS]
+       
+1:     strb    r0, [r7, #MCH_FXMEM]
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_VibForm:
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_TremForm:
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_PanbForm:
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_FPattDelay:
+       bne     .mppex_fpd_exit
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       mov     r0, r8
+       strb    r1, [r0, #MPL_FPATTDELAY]
+.mppex_fpd_exit:
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_InstControl:
+       bne     .mppex_ic_exit
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       cmp     r1, #2
+       ble     .mppex_ic_pastnotes
+       cmp     r1, #6
+       ble     .mppex_ic_nna
+       cmp     r1, #8
+       ble     .mppex_ic_envelope
+       bx      lr
+.mppex_ic_pastnotes:
+       @ todo...
+       bx      lr
+.mppex_ic_nna:
+       @ overwrite NNA
+       sub     r1, #3
+       ldrb    r2, [r7, #MCH_BFLAGS]
+       lsl     r2, #32-6
+       lsr     r2, #32-6
+       lsl     r1, #6
+       orr     r2, r1
+       strb    r2, [r7, #MCH_BFLAGS]
+       bx      lr
+.mppex_ic_envelope:
+       cmp     r6, #0
+       beq     .mppex_ic_exit
+       ldrb    r2, [r6, #MCA_FLAGS]
+       mov     r0, #32
+       bic     r2, r0
+       sub     r1, #7
+       lsl     r1, #5
+       orr     r2, r1
+       strb    r2, [r6, #MCA_FLAGS]
+
+.mppex_ic_exit:
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_SetPanning:
+       lsl     r1, #4
+       strb    r1, [r7, #MCH_PANNING]
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_SoundControl:
+       cmp     r1, #0x91
+       beq     .mppex_sc_surround
+       bx      lr
+.mppex_sc_surround:
+       @ set surround
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_HighOffset:
+       @ todo...
+       bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_PatternLoop:
+       bne     .mppex_pl_exit                          @ dont update on nonzero ticks
+       mov     r2, r8
+
+       lsl     r1, #32-4                               @ mask low nibble of parameter
+       lsr     r1, #32-4                               @ ...
+       bne     .mppex_pl_not0                          @ is zero?
+       
+       ldrb    r1, [r2, #MPL_ROW]                      @   ...
+       strb    r1, [r2, #MPL_PLOOP_ROW]                @    ...
+       ldr     r1,=mpp_vars                            @    ....
+       ldr     r1, [r1, #MPV_PATTREAD_P]               @   .. ...
+       mov     r3, #MPL_PLOOP_ADR
+       str     r1, [r2, r3]                            @  ...  ...
+       bx      lr                                      @ ...    ...
+.mppex_pl_not0:                                                @ otherwise...
+       ldrb    r0, [r2, #MPL_PLOOP_TIMES]              @   get pattern loop counter
+       cmp     r0, #0                                  @   zero?
+       bne     .mppex_pl_active                        @   if not then its already active
+       strb    r1, [r2, #MPL_PLOOP_TIMES]              @     zero: save parameter to counter
+       b       .mppex_pl_exit_enable                   @     exit & enable jump
+.mppex_pl_active:                                      @  nonzero:
+       sub     r0, #1                                  @    decrement counter
+       strb    r0, [r2, #MPL_PLOOP_TIMES]              @    save
+       beq     .mppex_pl_exit                          @    enable jump if not 0
+.mppex_pl_exit_enable:
+       mov     r0, #1                                  @    enable jump
+       mov     r3, #MPL_PLOOP_JUMP
+       strb    r0, [r2, r3]                            @    ..
+.mppex_pl_exit:                                                @    exit
+       bx      lr                                      @    ....
+.pool
+
+@-------------------------------------------
+.thumb_func
+mppex_NoteCut:
+       lsl     r1, #32-4                       @ mask parameter
+       lsr     r1, #32-4                       @ ..
+       cmp     r1, r2                          @ compare with tick#
+       bne     .mppex_nc_exit                  @ if equal:
+       mov     r0, #0                          @   cut volume
+       strb    r0, [r7, #MCH_VOLUME]           @   ..
+.mppex_nc_exit:                                        @ exit
+       bx      lr                              @ ..
+
+@-------------------------------------------
+.thumb_func
+mppex_NoteDelay:
+       
+       mov     r0, r8
+       ldrb    r2, [r0, #MPL_TICK]
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       cmp     r2, r1
+       bge     1f
+       ldr     r0,=mpp_vars
+       strb    r1, [r0, #MPV_NOTEDELAY]
+1:     bx      lr
+
+@-------------------------------------------
+.thumb_func
+mppex_PatternDelay:
+       bne     .mppex_pd_quit                  @ update on tick0
+       lsl     r1, #32-4                       @ mask parameter
+       lsr     r1, #32-4                       @ ..
+       mov     r0, r8
+       ldrb    r2, [r0, #MPL_PATTDELAY]        @ get patterndelay
+       cmp     r2, #0                          @ only update if it's 0
+       bne     .mppex_pd_quit                  @ ..
+       add     r1, #1                          @ set to param+1
+       strb    r1, [r0, #MPL_PATTDELAY]        @ ..
+.mppex_pd_quit:                                        @ exit
+       bx      lr                              @ ..
+
+@-------------------------------------------
+.thumb_func
+mppex_SongMessage:
+
+       bne     .mppex_pd_quit                  @ update on tick0
+       push    {lr}                            @ save return address
+       lsl     r1, #32-4                       @ mask parameter
+       lsr     r1, #32-4                       @ ..
+       ldr     r2,=mmCallback
+       ldr     r2, [r2]
+       cmp     r2, #0
+       beq     1f
+       mov     r0, #MPCB_SONGMESSAGE
+       
+       bl      mpp_call_r2
+       @jump2
+1:     //pop   {pc}
+       pop     {r0}
+       bx      r0
+.pool
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+mppe_SetTempo:                                 @ EFFECT Txy: Set Tempo / Tempo Slide
+@----------------------------------------------------------------------------------------
+
+       @ 0x  = slide down
+       @ 1x  = slide up
+       @ 2x+ = set
+       
+       // BUGGED???
+       // not using setbpm for slides???
+
+       cmp     r1, #0x20
+       bge     .mppe_st_set
+       cmp     r2, #0
+       beq     .mppe_st_exit
+
+       mov     r0, r8
+       ldrb    r2, [r0, #MPL_BPM]
+
+       cmp     r1, #0x10
+       bge     .mppe_st_slideup
+.mppe_st_slidedown:
+       sub     r2, r1
+       cmp     r2, #32
+       bge     .mppe_st_save
+       mov     r2, #32
+.mppe_st_save:
+       mov     r0, r2
+       b       .mppe_st_set2
+.mppe_st_slideup:
+       
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       
+       add     r2, r1
+       cmp     r2, #255
+       blt     .mppe_st_save
+       mov     r2, #255
+       b       .mppe_st_save
+.mppe_st_set:
+       cmp     r2, #0
+       bne     .mppe_st_exit
+       mov     r0, r1
+.mppe_st_set2:
+       push    {r5,lr}
+       mov     r5, r8
+       ldr     r1,=mpp_setbpm
+       bl      mpp_call_r1
+       @jump1
+       pop     {r5}
+       pop     {r3}
+       bx      r3
+.mppe_st_exit:
+       bx      lr
+.pool
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------------
+mppe_FineVibrato:                              @ EFFECT Uxy: Fine Vibrato
+@----------------------------------------------------------------------------------------
+
+       bne     .mppe_fv_ot
+       lsr     r0, r1, #4
+       beq     .mppe_fv_nospd
+       lsl     r0, #2
+       strb    r0, [r7, #MCH_VIBSPD]
+.mppe_fv_nospd:
+       
+       lsl     r0, r1, #32-4
+       beq     .mppe_fv_nodep
+//     lsr     r0, #32                 heh...
+       lsr     r0, #32-4
+       mov     r1, r8
+       ldrb    r1, [r1, #MPL_OLDEFFECTS]
+       lsl     r0, r1
+       strb    r0, [r7, #MCH_VIBDEP]
+.mppe_fv_nodep:
+
+.mppe_fv_ot:
+       b       mppe_DoVibrato
+.pool
+
+.align 2
+.thumb_func
+@------------------------------------------------------------------------------------
+mppe_SetGlobalVolume:                  @ EFFECT Vxy: Set Global Volume
+@------------------------------------------------------------------------------------
+
+       bne     .mppe_sgv_exit          @ on tick0:
+       mov     r0, r8
+       ldrb    r2, [r0, #MPL_FLAGS]
+       mov     r3, #(1<<(C_FLAGS_XS-1))+(1<<(C_FLAGS_LS-1))
+       tst     r2, r3
+       beq     1f
+       mov     r2, #0x40
+       b       2f
+1:     mov     r2, #0x80
+2:
+       cmp     r1, r2
+       blt     1f
+       mov     r1, r2
+1:     strb    r1, [r0, #MPL_GV]       @ save param to global volume
+.mppe_sgv_exit:
+       bx      lr
+.pool
+
+
+.align 2
+.thumb_func
+@----------------------------------------------------------------------------------
+mppe_GlobalVolumeSlide:                                @ EFFECT Wxy: Global Volume Slide
+@----------------------------------------------------------------------------------
+
+       push    {lr}
+
+.mppe_gvs_ot:
+       mov     r0, r8
+       ldrb    r0, [r0, #MPL_FLAGS]
+       lsr     r0, #C_FLAGS_XS
+       bcs     1f
+       
+       mov     r0, #128
+       b       2f
+
+1:     mov     r0, #64
+
+2:     push    {r0}
+       mov     r0, r8
+       ldrb    r0, [r0, #MPL_GV]               @ load global volume
+       
+       bl      mpph_VolumeSlide                @ slide..       
+       
+       mov     r1, r8
+       strb    r0, [r1, #MPL_GV]               @ save global volume
+       pop     {r0}
+       bx      r0
+//     pop     {pc}                            @ exit
+.pool
+
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------------
+mppe_SetPanning:                               @ EFFECT Xxy: Set Panning
+@---------------------------------------------------------------------------------------
+
+       bne     .mppe_sp_exit                   @ on tick0:
+       strb    r1, [r7, #MCH_PANNING]          @ set panning=param
+.mppe_sp_exit:
+       bx      lr
+.pool
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------------
+mppe_Panbrello:                                        @ EFFECT Yxy: Panbrello
+@---------------------------------------------------------------------------------------
+
+       @ todo
+       bx      lr
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------------
+mppe_ZXX:                              @ EFFECT Zxy: Set Filter
+@---------------------------------------------------------------------------------
+
+@      ZXX IS NOT SUPPORTED
+       bx      lr                      @     exit
+.pool
+
+@=======================================================================================
+@                                     OLD EFFECTS
+@=======================================================================================
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------------
+mppe_SetVolume:                                @ EFFECT 0xx: Set Volume
+@-----------------------------------------------------------------------------------
+
+       bne     .mppe_sv_exit           @ on tick0:
+       strb    r1, [r7, #MCH_VOLUME]   @ set volume=param
+.mppe_sv_exit:
+       bx      lr
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------------
+mppe_KeyOff:                           @ EFFECT 1xx: Key Off
+@-----------------------------------------------------------------------------------
+       cmp     r1, r2                  @ if tick=param:
+       bne     .mppe_ko_exit           @ 
+       cmp     r6, #0
+       beq     .mppe_ko_exit
+       ldrb    r0, [r6, #MCA_FLAGS]    @   clear keyon from flags
+       mov     r1, #MCAF_KEYON         @
+       bic     r0, r1                  @
+       strb    r0, [r6, #MCA_FLAGS]    @
+.mppe_ko_exit:
+       bx      lr                      @ finished
+.pool
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------------
+mppe_EnvelopePos:                      @ EFFECT 1xx: Envelope Position
+@-----------------------------------------------------------------------------------
+       bne     .mppe_ep_ot             @ on tick0:
+       cmp     r6, #0
+       beq     .mppe_ep_ot
+
+       @ - NOT SUPPORTED ANYMORE -
+
+@      strh    r1, [r6, #MCA_ENVP_VOL] @ set volume envelope position
+@      strh    r1, [r6, #MCA_ENVP_PAN] @ set panning envelope positin
+                                       @ pitch envelope wasn't invented yet
+.mppe_ep_ot:
+       bx      lr                      @ finished
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------------
+mppe_OldTremor:                                @ EFFECT 3xy: Old Tremor
+@-----------------------------------------------------------------------------------
+       
+       bne     .mppe_ot_ot
+       bx      lr
+.mppe_ot_ot:
+       ldrb    r0, [r7, #MCH_FXMEM]
+       cmp     r0, #0
+       bne     .mppe_ot_old
+.mppe_ot_new:
+       ldrb    r0, [r7, #MCH_BFLAGS+1]
+       mov     r3, #0b110
+       eor     r0, r3
+       strb    r0, [r7, #MCH_BFLAGS+1]
+       lsr     r0, #3
+       bcc     .mppe_ot_low
+       lsr     r1, #4
+       add     r1, #1
+       strb    r1, [r7, #MCH_FXMEM]
+       b       .mppe_ot_apply
+.mppe_ot_low:
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       add     r1, #1
+       strb    r1, [r7, #MCH_FXMEM]
+       b       .mppe_ot_apply
+       
+.mppe_ot_old:
+       sub     r0, #1
+       strb    r0, [r7, #MCH_FXMEM]
+
+.mppe_ot_apply:
+       ldrb    r2, [r7, #MCH_BFLAGS+1]
+       
+       lsr     r2, #3
+       bcs     .mppe_ot_cut
+       mov     r1, #-64&255 
+       ldr     r2,=mpp_vars
+       strb    r1, [r2, #MPV_VOLPLUS]
+.mppe_ot_cut:
+       bx      lr
+
+@===============================================================================
+@                                    FUNCTIONS!!
+@===============================================================================
+
+.align 2
+.thumb_func
+@--------------------------------------------------------------------------
+mpph_PitchSlide_Down:                          @ Linear/Amiga slide down
+@--------------------------------------------------------------------------
+
+       @ r0 = period
+       @ r1 = slide value (/4)
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_SS
+       bcc     .mpph_psd_amiga
+       b       .mpph_psd
+
+.thumb_func
+@--------------------------------------------------------------------------
+mpph_LinearPitchSlide_Down:                    @ Linear slide down
+@--------------------------------------------------------------------------
+
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_SS
+       bcc     .mpph_psu
+.mpph_psd:
+       ldr     r2,=mpp_TABLE_LinearSlideDownTable
+.mpph_psd_fine:
+       lsl     r1, #1
+       ldrh    r1, [r2, r1]
+       lsr     r0, #5
+       mul     r0, r1
+       lsr     r0, #16 -5
+.mpph_psd_clip:
+       cmp     r0, #0
+       bge     .mpph_psd_clipdone
+       mov     r0, #0
+.mpph_psd_clipdone:
+       bx      lr
+.mpph_psd_amiga:
+       lsl     r1, #4
+.mpph_psd_amiga_fine:
+       add     r0, r1
+       lsr     r1, r0, #16+5
+       beq     .mpph_psd_clipdone
+       mov     r0, #1
+       lsl     r0, #16+5
+       b       .mpph_psd_clip
+
+.align 2
+.thumb_func
+@--------------------------------------------------------------------------
+mpph_PitchSlide_Up:                    @ Linear/Amiga slide up
+@--------------------------------------------------------------------------
+
+       @ r0 = period
+       @ r1 = slide value (/4)
+
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_SS
+       bcc     .mpph_psu_amiga
+       b       .mpph_psu
+.thumb_func
+mpph_LinearPitchSlide_Up:              @ Linear slide up
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_SS
+       bcc     .mpph_psd
+.mpph_psu:
+       ldr     r2,=mpp_TABLE_LinearSlideUpTable
+       mov     r3, r0
+       cmp     r1, #192
+       blt     .mpph_psu_notdouble
+       add     r3, r3
+.mpph_psu_notdouble:
+.mpph_psu_fine:
+       lsl     r1, #1
+       ldrh    r1, [r2, r1]
+       lsr     r0, #5
+       mul     r0, r1
+       lsr     r0, #16-5
+       add     r0, r3
+.mpph_psu_clip:
+       mov     r1, r0
+       lsr     r1, #16+5
+       beq     .mpph_psu_clipped
+       mov     r0, #1
+       lsl     r0, #16+5
+.mpph_psu_clipped:
+       bx      lr
+.mpph_psu_amiga:
+       lsl     r1, #4
+.mpph_psu_amiga_fine:
+       sub     r0, r1
+       bcs     .mpph_psu_clipped
+       mov     r0, #0
+       bx      lr
+
+.align 2
+.thumb_func
+@---------------------------------------------------------------------------
+mpph_FinePitchSlide_Up:
+@---------------------------------------------------------------------------
+
+       @ r0 = period
+       @ r1 = slide value (0-15)
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_SS
+       bcc     .mpph_fpsu_amiga
+       ldr     r2,=mpp_TABLE_FineLinearSlideUpTable
+       mov     r3, r0
+       b       .mpph_psu_fine
+.mpph_fpsu_amiga:
+       lsl     r1, #2
+       b       .mpph_psu_amiga_fine
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------------
+mpph_FinePitchSlide_Down:
+@-----------------------------------------------------------------------------------
+
+       @ r0 = period
+       @ r1 = slide value (0-15)
+       
+       mov     r2, r8
+       ldrb    r2, [r2, #MPL_FLAGS]
+       lsr     r2, #C_FLAGS_SS
+       bcc     .mpph_fpsd_amiga
+       ldr     r2,=mpp_TABLE_FineLinearSlideDownTable
+       b       .mpph_psd_fine
+.mpph_fpsd_amiga:
+       lsl     r1, #2
+       b       .mpph_psd_amiga_fine
+.pool
+
+@-----------------------------------------------------------------------------------
+
+.text
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------------
+mpph_FastForward:
+@-----------------------------------------------------------------------------------
+
+       @ r1 = #rows to skip
+       cmp     r1, #0
+       bne     .mpph_ff
+.mpph_ff_exitf:
+       bx      lr
+.mpph_ff:
+       
+       mov     r0, r8
+       ldrb    r2, [r0, #MPL_NROWS]
+       add     r2, #1
+       cmp     r1, r2
+       bge     .mpph_ff_exitf
+       strb    r1, [r0, #MPL_ROW]
+       push    {r7,lr}
+       ldr     r7,=mmReadPattern //mpp_ReadPattern
+.mpph_ff_loop:
+       push    {r1,r7}
+       bl      mpp_call_r7
+       pop     {r1,r7}
+       sub     r1, #1
+       bne     .mpph_ff_loop
+
+       pop     {r7}
+       pop     {r0}
+       bx      r0
+//     pop     {pc}
+.pool
+
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@          IWRAM CODE          @
+@                              @
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+.align 2
+.thumb_func
+@-----------------------------------------------------------------------------
+mpph_VolumeSlide64:
+@-----------------------------------------------------------------------------
+
+       mov     r3, #64
+       push    {r3}
+
+.thumb_func
+@-----------------------------------------------------------------------------
+mpph_VolumeSlide:
+@-----------------------------------------------------------------------------
+
+       @ r0 = volume
+       @ r1 = paramter
+       @ r2 = tick#
+       @ stack:1 = max volume
+       
+       mov     r3, r8
+       ldrb    r3, [r3, #MPL_FLAGS]
+       lsr     r3, #C_FLAGS_XS
+       bcs     .mpph_vs_XM
+       
+       cmp     r1, #0x0F                       @  is value 15?
+       bne     .mpph_vs_hack1                  @    then only slide on tick0
+       b       .mpph_vs_fsub
+.mpph_vs_hack1:
+       cmp     r1, #0xF0                       @  is value 15?
+       bne     .mpph_vs_hack2                  @    then only slide on tick0
+       cmp     r2, #0                          @    ..
+       bne     .mpph_vs_exit                   @    ..
+       b       .mpph_vs_fadd
+.mpph_vs_hack2:
+
+       mov     r3, r1                          @ test for Dx0
+       lsl     r3, #32-4                       @ ..
+       bne     .mpph_vs_next1                  @ ..
+.mpph_vs_add:                                  @ Dx0:                  (used for DxF too)
+       cmp     r2, #0
+       beq     .mpph_vs_exit
+.mpph_vs_fadd:
+       lsr     r1, #4                          @  fix value
+       add     r0, r1                          @  add to volume
+       pop     {r1}
+       cmp     r0, r1                          @  clip values past 64
+       blt     .mpph_vs_exit2                  @  ..
+       mov     r0, r1                          @  ..
+       b       .mpph_vs_exit2                  @  ..
+.mpph_vs_next1:                                        @---------------------
+       mov     r3, r1                          @ test for D0x
+       lsr     r3, #4                          @ ..
+       bne     .mpph_vs_next2                  @ ..
+.mpph_vs_sub:                                  @ D0x:
+       cmp     r2, #0
+       beq     .mpph_vs_exit
+.mpph_vs_fsub:
+       lsl     r1, #32-4                       @  mask value
+       lsr     r1, #32-4                       @  ..
+       
+       sub     r0, r1                          @  subtract from volume
+       bcs     .mpph_vs_exit                   @  clip values under 0
+       mov     r0, #0                          @  ..
+       b       .mpph_vs_exit                   @  ..
+.mpph_vs_next2:                                        @---------------------
+       cmp     r2, #0                          @ fine slides now... only slide on tick0
+       bne     .mpph_vs_exit                   @ ..
+       
+       mov     r3, r1                          @ test for DxF
+       lsl     r3, #32-4                       @ ..
+       lsr     r3, #32-4
+       cmp     r3, #0x0F                       @ ..
+       beq     .mpph_vs_fadd                   @ branch
+       
+       mov     r3, r1                          @ test for DFx
+       lsr     r3, #4                          @ ..
+       cmp     r3, #0x0F                       @ ..
+       beq     .mpph_vs_fsub                   @ branch
+.mpph_vs_exit:
+
+       pop     {r1}
+
+.mpph_vs_exit2:
+       bx      lr                              @ exit if all fail
+       
+.mpph_vs_XM:
+       cmp     r2, #0
+       beq     .mpph_vs_exit
+       
+       lsr     r3, r1, #4
+       lsl     r1, #32-4
+       lsr     r1, #32-4
+       sub     r3, r1
+       add     r0, r3
+       pop     {r1}
+       cmp     r0, r1
+       blt     .mpph_vsxm_testlow
+       mov     r0, r1
+.mpph_vsxm_testlow:
+       cmp     r0, #0
+       bgt     .mpph_vs_exit2
+       mov     r0, #0
+       bx      lr
+.pool
+
+@==========================================================================================
+@                                          TABLES
+@==========================================================================================
+
+.TEXT
+
+.align 2
+/******************************************************************************
+ * ST3_FREQTABLE
+ *
+ * LUT for amiga periods.
+ ******************************************************************************/
+                                               .global ST3_FREQTABLE
+ST3_FREQTABLE:
+
+.hword 1712*8, 1616*8, 1524*8, 1440*8, 1356*8, 1280*8, 1208*8, 1140*8, 1076*8, 1016*8, 960*8, 907*8    @ MORE ACCURACY SCALARS
+
+@middle octave is 4.
+@      
+@                       133808 * ( period(NOTE) >> octave )
+@      note_st3period = --------------------------------------------
+@                       middle_c_finetunevalue(INSTRUMENT)
+@
+
+.align 2       
+@-------------------------------------------------------------------------------------------
+mpp_TABLE_LinearSlideUpTable:                  @ value = 2^(val/192), 16.16 fixed
+@-------------------------------------------------------------------------------------------
+
+.hword 0,     237,   475,   714,   953   @ 0->4                @ ADD 1.0
+.hword 1194,  1435,  1677,  1920,  2164  @ 5->9
+.hword 2409,  2655,  2902,  3149,  3397  @ 10->14
+.hword 3647,  3897,  4148,  4400,  4653  @ 15->19
+.hword 4907,  5157,  5417,  5674,  5932  @ 20->24
+.hword 6190,  6449,  6710,  6971,  7233  @ 25->29
+.hword 7496,  7761,  8026,  8292,  8559  @ 30->34
+.hword 8027,  9096,  9366,  9636,  9908  @ 35->39
+.hword 10181, 10455, 10730, 11006, 11283 @ 40->44
+.hword 11560, 11839, 12119, 12400, 12682 @ 45->49
+.hword 12965, 13249, 13533, 13819, 14106 @ 50->54
+.hword 14394, 14684, 14974, 15265, 15557 @ 55->59
+.hword 15850, 16145, 16440, 16737, 17034 @ 60->64
+.hword 17333, 17633, 17933, 18235, 18538 @ 65->69
+.hword 18842, 19147, 19454, 19761, 20070 @ 70->74
+.hword 20379, 20690, 21002, 21315, 21629 @ 75->79
+.hword 21944, 22260, 22578, 22897, 23216 @ 80->84
+.hword 23537, 23860, 24183, 24507, 24833 @ 85->89
+.hword 25160, 25488, 25817, 26148, 26479 @ 90->94
+.hword 26812, 27146, 27481, 27818, 28155 @ 95->99
+.hword 28494, 28834, 29175, 29518, 29862 @ 100->104
+.hword 30207, 30553, 30900, 31248, 31599 @ 105->109
+.hword 31951, 32303, 32657, 33012, 33369 @ 110->114
+.hword 33726, 34085, 34446, 34807, 35170 @ 115->119
+.hword 35534, 35900, 36267, 36635, 37004 @ 120->124
+.hword 37375, 37747, 38121, 38496, 38872 @ 125->129
+.hword 39250, 39629, 40009, 40391, 40774 @ 130->134
+.hword 41158, 41544, 41932, 42320, 42710 @ 135->139
+.hword 43102, 43495, 43889, 44285, 44682 @ 140->144
+.hword 45081, 45481, 45882, 46285, 46690 @ 145->149
+.hword 47095, 47503, 47917, 48322, 48734 @ 150->154
+.hword 49147, 49562, 49978, 50396, 50815 @ 155->159
+.hword 51236, 51658, 52082, 52507, 52934 @ 160->164
+.hword 53363, 53793, 54224, 54658, 55092 @ 165->169
+.hword 55529, 55966, 56406, 56847, 57289 @ 170->174
+.hword 57734, 58179, 58627, 59076, 59527 @ 175->179
+.hword 59979, 60433, 60889, 61346, 61805 @ 180->184
+.hword 62265, 62727, 63191, 63657, 64124 @ 185->189
+.hword 64593, 65064, 0,     474,   950   @ 190->194            @ ADD 2.0 w/ 192+
+.hword 1427,  1906,  2387,  2870,  3355  @ 195->199
+.hword 3841,  4327,  4818,  5310,  5803  @ 200->204
+.hword 6298,  6795,  7294,  7794,  8296  @ 205->209
+.hword 8800,  9306,  9814,  10323, 10835 @ 210->214
+.hword 11348, 11863, 12380, 12899, 13419 @ 215->219
+.hword 13942, 14467, 14993, 15521, 16051 @ 220->224
+.hword 16583, 17117, 17653, 18191, 18731 @ 225->229
+.hword 19273, 19817, 20362, 20910, 21460 @ 230->234
+.hword 22011, 22565, 23121, 23678, 24238 @ 235->239
+.hword 24800, 25363, 25929, 25497, 27067 @ 240->244
+.hword 27639, 28213, 28789, 29367, 29947 @ 245->249
+.hword 30530, 31114, 31701, 32289, 32880 @ 250->254
+.hword 33473, 34068                      @ 255->256
+
+.align 2
+@-------------------------------------------------------------------------------------
+mpp_TABLE_LinearSlideDownTable:                        @ value = 2^(-val/192), 16.16 fixed
+@-------------------------------------------------------------------------------------
+
+.hword 65535, 65300, 65065, 64830, 64596, 64364, 64132, 63901 @ 0->7
+.hword 63670, 63441, 63212, 62984, 62757, 62531, 62306, 62081 @ 8->15
+.hword 61858, 61635, 61413, 61191, 60971, 60751, 60532, 60314 @ 16->23
+.hword 60097, 59880, 59664, 59449, 59235, 59022, 58809, 58597 @ 24->31
+.hword 58386, 58176, 57966, 57757, 57549, 57341, 57135, 56929 @ 32->39
+.hword 56724, 56519, 56316, 56113, 55911, 55709, 55508, 55308 @ 40->47
+.hword 55109, 54910, 54713, 54515, 54319, 54123, 53928, 53734 @ 48->55
+.hword 53540, 53347, 53155, 52963, 52773, 52582, 52393, 52204 @ 56->63
+.hword 52016, 51829, 51642, 51456, 51270, 51085, 50901, 50718 @ 64->71
+.hword 50535, 50353, 50172, 49991, 49811, 49631, 49452, 49274 @ 72->79
+.hword 49097, 48920, 48743, 48568, 48393, 48128, 48044, 47871 @ 80->87
+.hword 47699, 47527, 47356, 47185, 47015, 46846, 46677, 46509 @ 88->95
+.hword 46341, 46174, 46008, 45842, 45677, 45512, 45348, 45185 @ 96->103
+.hword 45022, 44859, 44698, 44537, 44376, 44216, 44057, 43898 @104->111
+.hword 43740, 43582, 43425, 43269, 43113, 42958, 42803, 42649 @112->119
+.hword 42495, 42342, 42189, 42037, 41886, 41735, 41584, 41434 @120->127
+.hword 41285, 41136, 40988, 40840, 40639, 40566, 40400, 40253 @128->135
+.hword 40110, 39965, 39821, 39678, 39535, 39392, 39250, 39109 @136->143
+.hword 38968, 38828, 38688, 38548, 38409, 38271, 38133, 37996 @144->151
+.hword 37859, 37722, 37586, 37451, 37316, 37181, 37047, 36914 @152->159
+.hword 36781, 36648, 36516, 36385, 36254, 36123, 35993, 35863 @160->167
+.hword 35734, 35605, 35477, 35349, 35221, 35095, 34968, 34842 @168->175
+.hword 34716, 34591, 34467, 34343, 34219, 34095, 33973, 33850 @176->183
+.hword 33728, 33607, 33486, 33365, 33245, 33125, 33005, 32887 @184->191
+.hword 32768, 32650, 32532, 32415, 32298, 32182, 32066, 31950 @192->199
+.hword 31835, 31720, 31606, 31492, 31379, 31266, 31153, 31041 @200->207
+.hword 30929, 30817, 30706, 30596, 30485, 30376, 30226, 30157 @208->215
+.hword 30048, 29940, 29832, 29725, 29618, 29511, 29405, 29299 @216->223
+.hword 29193, 29088, 28983, 28879, 28774, 28671, 28567, 28464 @224->231
+.hword 28362, 28260, 28158, 28056, 27955, 27855, 27754, 27654 @232->239
+.hword 27554, 27455, 27356, 27258, 27159, 27062, 26964, 26867 @240->247
+.hword 26770, 26674, 26577, 26482, 26386, 26291, 26196, 26102 @248->255
+.hword 26008                                                  @ 256
+
+/******************************************************************************
+ * IT_PitchTable
+ *
+ * LUT for linear periods.
+ ******************************************************************************/
+                                               .global IT_PitchTable
+                                               .align 2
+IT_PitchTable:
+
+.hword 2048, 0,   2170, 0,   2299, 0,   2435, 0,   2580, 0,   2734, 0 @ C-0
+.hword 2896, 0,   3069, 0,   3251, 0,   3444, 0,   3649, 0,   3866, 0 @>B-0
+
+.hword 4096, 0,   4340, 0,   4598, 0,   4871, 0,   5161, 0,   5468, 0 @ C-1
+.hword 5793, 0,   6137, 0,   6502, 0,   6889, 0,   7298, 0,   7732, 0 @>B-1
+
+.hword 8192, 0,   8679, 0,   9195, 0,   9742, 0,   10321, 0,  10935, 0 @ octave 2
+.hword 11585, 0,  12274, 0,  13004, 0,  13777, 0,  14596, 0,  15464, 0
+
+.hword 16384, 0,  17358, 0,  18390, 0,  19484, 0,  20643, 0,  21870, 0 @ octave 3
+.hword 23170, 0,  24548, 0,  26008, 0,  27554, 0,  29193, 0,  30929, 0
+
+.hword 32768, 0,  34716, 0,  36781, 0,  38968, 0,  41285, 0,  43740, 0 @ octave 4
+.hword 46341, 0,  49097, 0,  52016, 0,  55109, 0,  58386, 0,  61858, 0
+
+.hword 0, 1,      3897, 1,   8026, 1,   12400, 1,  17034, 1,  21944, 1 @ octave 5
+.hword 27146, 1,  32657, 1,  38496, 1,  44682, 1,  51236, 1,  58179, 1
+
+.hword 0, 2,      7794, 2,   16051, 2,  24800, 2,  34068, 2,  43888, 2 @ octave 6
+.hword 54292, 2,  65314, 2,  11456, 3,  23828, 3,  36936, 3,  50823, 3
+
+.hword 0, 4,      15588, 4,  32103, 4,  49600, 4,  2601, 5,   22240, 5 @ octave 7
+.hword 43048, 5,  65092, 5,  22912, 6,  47656, 6,  8336, 7,   36110, 7
+
+.hword 0, 8,      31176, 8,  64205, 8,  33663, 9,  5201, 10,  44481, 10 @ octave 8
+.hword 20559, 11, 64648, 11, 45823, 12, 29776, 13, 16671, 14, 6684, 15
+
+.hword 0, 16,     62352, 16, 62875, 17, 1790,  19, 10403, 20, 23425, 21 @ octave 9
+.hword 41118, 22, 63761, 23, 26111, 25, 59552, 26, 33342, 28, 13368, 30
+
+.align 2
+@-------------------------------------------------------------------------------------
+mpp_TABLE_FineLinearSlideUpTable:
+@-------------------------------------------------------------------------------------
+
+.hword 0,      59,     118,    178,    237    @ 0->4           ADD 1x
+.hword 296,    356,    415,    475,    535    @ 5->9
+.hword 594,    654,    714,    773,    833    @ 10->14
+.hword 893                                    @ 15
+
+.align 2
+@-------------------------------------------------------------------------------------
+mpp_TABLE_FineLinearSlideDownTable:
+@-------------------------------------------------------------------------------------
+
+.hword 65535, 65477, 65418, 65359, 65300, 65241, 65182, 65359 @ 0->7
+.hword 65065, 65006, 64947, 64888, 64830, 64772, 64713, 64645 @ 8->15
+
+@-------------------------------------------------------------------------------------
+mpp_TABLE_FineSineData:
+@-------------------------------------------------------------------------------------
+
+.byte    0,  2,  3,  5,  6,  8,  9, 11, 12, 14, 16, 17, 19, 20, 22, 23
+.byte   24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44
+.byte   45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59
+.byte   59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64
+.byte   64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60
+.byte   59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46
+.byte   45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26
+.byte   24, 23, 22, 20, 19, 17, 16, 14, 12, 11,  9,  8,  6,  5,  3,  2
+.byte    0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23
+.byte  -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44
+.byte  -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59
+.byte  -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64
+.byte  -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60
+.byte  -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46
+.byte  -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26
+.byte  -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2
+
+.end
diff --git a/libs/maxmod/mm_mas_arm.S b/libs/maxmod/mm_mas_arm.S
new file mode 100644 (file)
index 0000000..13df41e
--- /dev/null
@@ -0,0 +1,612 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                    Module Processing (ARM segment)                       *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+@=============================================================
+@                         DEFINITIONS
+@=============================================================
+
+#include "mp_format_mas.inc"
+#include "mp_mas_structs.inc"
+#include "mp_defs.inc"
+#include "mp_macros.inc"
+
+#ifdef SYS_GBA
+#include "mp_mixer_gba.inc"
+#include "swi_gba.inc"
+#endif
+
+#ifdef SYS_NDS
+#include "mp_mixer_ds.inc"
+#include "swi_nds.inc"
+#endif
+
+__SECTION_IWRAM
+
+.arm
+.align 2
+
+.global        mmAllocChannel
+@********************************************************
+mmAllocChannel:
+@********************************************************
+// finds a channel to use
+// returns invalid channel [255] if none available
+       push    {r4,r5,r6}                              // preserve regs
+       ldr     r5,=mm_ch_mask                  // read channel mask
+       ldr     r5, [r5]
+       ldr     r1,=mm_achannels                // pointer to active channels
+       ldr     r1, [r1]
+       add     r1, #MCA_FVOL
+       mov     r0, #0                          // r0 = counter
+       mov     r2, #0x80000000                 // r2 = min vol
+       mov     r3, #255                        // r3 = best channel [255=none]
+       mov     r6, #ACHN_BACKGROUND
+       b       .mppac_start
+.mppac_skip:
+       add     r1, #MCA_SIZE
+.mppac_next_test:
+       cmp     r5, #0
+.mppac_next_notest:
+       beq     .mppac_finished
+       add     r0, #1
+.mppac_start:
+       movs    r5, r5, lsr#1                   // 1    shift out channel bit
+       bcc     .mppac_skip                     // 1/3  skip if cleared
+       ldrh    r4, [r1], #MCA_SIZE             // 5    read type setting & increment pointer
+       cmp     r6, r4, lsr #8                  // 1    compare background/disabled
+       blt     .mppac_next_test                // 1/3  if > background then its important, dont use this channel
+       bgt     .mppac_found                    // 1/3  if < background then its disabled, use this channel
+       
+       cmp     r2, r4, lsl#23                  // 1    compare volume with our record
+       bls     .mppac_next_test                // 1/3  goto next if not less
+       mov     r3, r0                          // 1    otherwise save this channel
+       movs    r2, r4, lsl#23                  // 1    and volume level
+//     beq     .mppac_finished                 // 1/3  exit immediately if volume is zero
+       b       .mppac_next_test                // 3    loop
+                                               // 17
+.mppac_finished:
+       mov     r0, r3
+//     cmp     r0, #255        //trap
+//     5: bge 5b
+.mppac_found:
+       pop     {r4,r5,r6}
+       bx      lr
+
+.global mmReadPattern
+@****************************************************
+mmReadPattern:
+@****************************************************
+       push    {r10-r12}
+       ldr     r11, [r8, #MPL_SONGADR]
+       ldrb    r12, [r11, #C_MAS_INSTN]        // read instr count
+       ldrb    r11, [r8, #MPL_FLAGS]           // read flags
+       mov     r10, #1                         // for various things
+       
+       ldr     r0,=mpp_channels
+       ldr     r9, [r0]
+       ldr     r7, [r8, #MPL_PATTREAD]
+       ldr     r0,=mpp_vars
+       str     r7, [r0, #MPV_PATTREAD_P]
+       
+       mov     r1, #0
+
+@----------------------------------------------------------------
+readpattern:
+@----------------------------------------------------------------
+
+       mov     r5, #0                  // clear flags
+       ldrb    r3, [r7], #1            // read pattern byte
+       movs    r3, r3, lsl#32-7        // mask channel#
+       beq     end_of_row              // 0 = end of row
+       rsb     r3, r10, r3, lsr#32-7   // get channel number
+       orr     r1, r10, lsl r3
+       mov     r0, #MCH_SIZE
+       mla     r6, r3, r0, r9          // get channel pointer
+       ldrcsb  r2, [r7], #1            // read new maskvariable if bit was set
+       strcsb  r2, [r6, #MCH_CFLAGS]   // and save it
+       ldrccb  r2, [r6, #MCH_CFLAGS]   // otherwise read previous flags
+       
+       tst     r2, #1                  // test note bit
+       beq     no_note
+       ldrb    r0, [r7], #1            // read note value
+       cmp     r0, #254                // test if < 254
+       strltb  r0, [r6, #MCH_PNOTE]    // [most common result]
+       blt     no_note
+       orreq   r5, #MF_NOTECUT         // 254 is note-cut
+       orrgt   r5, #MF_NOTEOFF         // 255 is note-off
+no_note:
+       
+       tst     r2, #2                  // shift out instrument bit
+       beq     no_instrument
+       ldrb    r0, [r7], #1            // read instrument value
+       tst     r5, #MF_NOTEOFF|MF_NOTECUT
+       bne     no_instrument           // skip if note-off or cut is set
+       cmp     r0, r12                 // check if value > max instruments
+       movgt   r0, #0                  // zero if so
+       ldrb    r3, [r6, #MCH_INST]     // test if instrument is the same
+       cmp     r0, r3
+       beq     same_instrument
+       movs    r3, r11, lsr#C_FLAGS_LS // shift out 'mod/s3m' flag
+       orrcs   r5, #MF_START           // set start flag if old
+       orr     r5, #MF_NEWINSTR        // set new instrument flag
+same_instrument:
+       strb    r0, [r6, #MCH_INST]
+no_instrument:
+       
+       tst     r10, r2, lsr#3          // test volume & effect bits
+       ldrcsb  r0, [r7], #1            // copy vcmd
+       strcsb  r0, [r6, #MCH_VOLCMD]
+no_vcmd:
+       beq     no_effect
+       ldrb    r0, [r7], #1            // copy effect
+       ldrb    r3, [r7], #1            // copy param
+       orr     r0, r3, lsl#8
+       strh    r0, [r6, #MCH_EFFECT]   // write effect+param
+no_effect:
+       orr     r5, r2, lsr#4           // orr in the new flags
+       strb    r5, [r6, #MCH_FLAGS]    // save flags
+       b       readpattern             // loop
+end_of_row:
+       str     r7, [r8, #MPL_PATTREAD] // save read position
+       str     r1, [r8, #MPL_MCH_UPDATE] // save update bits
+       pop     {r10-r12}
+       bx      lr                      // return
+       
+.macro get_channel branch
+       ldrb    r0, [r7, #MCH_ALLOC]    // get channel
+       cmp     r0, #255                //
+       bge     \branch         // no channel!
+       ldr     r6,=mm_achannels        //
+       ldr     r6, [r6]                //
+       mov     r1, #MCA_SIZE           //
+       mla     r6, r0, r1, r6          //
+.endm
+
+.global mmUpdateChannel_T0, mmUpdateChannel_TN
+@***************************************************************************
+mmUpdateChannel_T0:                    // for tick 0
+@***************************************************************************
+       push    {lr}
+       
+       ldrb    r5, [r7, #MCH_FLAGS]    // read channel flags
+       tst     r5, #MF_START           // test start flag
+       beq     dont_start_channel      // skip section if cleared
+       
+       tst     r5, #MF_HASFX           // test effect flag
+       beq     no_channel_effect
+       ldrh    r0, [r7, #MCH_EFFECT]   // read effect
+       and     r1, r0, #0xFF
+//     cmp     r1, #19                 // test for 'SDx' (note delay)
+//     lsreq   r2, r0, #12
+//     cmpeq   r2, #0xD
+//     beq     dont_start_channel      // dont start channel if delayed
+       
+                                       // test for glissando
+       tst     r5, #MF_NEWINSTR
+       bne     start_channel           // always start channel if it's a new instrument
+       cmp     r1, #0x7                // test if effect is Gxx
+       beq     glissando_affected
+       
+no_channel_effect:
+       tst     r5, #MF_NEWINSTR        // copeee
+       bne     start_channel           // always start channel if it's a new instrument
+       
+       tst     r5, #MF_HASVCMD         // test for volume commmand
+       beq     start_channel           // none = start channel
+       ldrb    r0, [r8, #MPL_FLAGS]    // read mod flags
+       movs    r0, r0, lsr#C_FLAGS_XS  // test for XM mode
+       ldrb    r0, [r7, #MCH_VOLCMD]   // read volume command
+       bcs     xm_vcmd                 // branch
+it_vcmd:                               // using IT effects:
+       cmp     r0, #193                // glissando is 193..202
+       blt     start_channel
+       cmp     r0, #202
+       bgt     start_channel
+       b       glissando_affected
+xm_vcmd:                               // using XM effects:
+       cmp     r0, #0xF0               // glissando is Fx
+       bge     glissando_affected
+       b       start_channel
+no_channel_vcmd:
+
+glissando_affected:
+       
+       get_channel start_channel
+/*     ldrb    r0, [r7, #MCH_ALLOC]    // if channel is invalid
+       cmp     r0, #255                // then skip this
+       bge     start_channel
+       
+       ldr     r6,=mm_achannels        // get channel
+       ldr     r6, [r6]                //
+       mov     r1, #MCA_SIZE           //
+       mla     r6, r0, r1, r6          //
+*/     
+       bl      mmChannelStartACHN      // start achn
+       bic     r5, #MF_START           // clear start flag
+       strb    r5, [r7, #MCH_FLAGS]
+       b       dont_start_channel
+       
+start_channel:                         // ok start channel...
+       
+       ldr     r2,=mpp_Channel_NewNote
+       mov     lr, pc
+       bx      r2
+       get_channel     mmUpdateChannel_TN_
+       
+//     cmp     r6, #0
+//     beq     mmUpdateChannel_TN_
+       
+       bl      mmChannelStartACHN      // start achn
+//----------------------------------------------
+       // r2 = note, calculate period
+       ldrb    r0, [r6, #MCA_SAMPLE]
+       subs    r0, #1
+       bcc     no_sample_to_make_period
+       
+       ldrb    r1, [r8, #MPL_FLAGS]
+       lsrs    r1, #C_FLAGS_SS
+       bcs     linear_periods
+       
+       ldr     r3, [r8, #MPL_SONGADR]  // read song addr
+       ldr     r1, [r8, #MPL_SAMPTABLE]// get sample table
+       ldr     r0, [r1, r0, lsl#2]     // get sample address
+       add     r0, r3                  // add module base
+       
+       ldrh    r1, [r0, #C_MASS_FREQ]  // read tuning freq
+       lsl     r1, #2                  // shift ...
+//     mov     r0, r2                  // move this.
+       bl      get_amiga_period
+       b       got_period
+linear_periods:
+       LDR     R1,=IT_PitchTable
+       LDR     R0, [R1, R2, LSL#2]
+       //bl    get_linear_period
+got_period:
+//--------------------------------
+       
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+       str     r0, [r7, #MCH_PERIOD]   // save period
+       ldrb    r0, [r6, #MCA_FLAGS]    // set start flag
+       orr     r0, #MCAF_START
+       strb    r0, [r6, #MCA_FLAGS]
+no_sample_to_make_period:
+//----------------------------------------------------
+       
+       b       channel_started
+       
+dont_start_channel:
+       
+       get_channel     mmUpdateChannel_TN___
+       
+channel_started:
+       tst     r5, #MF_DVOL
+       beq     dvol_skip
+       ldrb    r0, [r7, #MCH_INST]
+       subs    r0, #1
+       bcc     dvol_no_instrument
+       
+       ldr     r2, [r8, #MPL_SONGADR]  // get instrument pointer
+       ldr     r1, [r8, #MPL_INSTTABLE]
+       ldr     r0, [r1, r0, lsl#2]
+       add     r0, r2
+       
+       ldr     r1, [r0, #C_MASI_NNA]   // read nna,envflags,pan
+       ldrb    r2, [r7, #MCH_BFLAGS]   // read bflags
+       bic     r2, #0b11000000         // clear old nna
+       orr     r2, r1, lsl#6           // set new nna
+       strb    r2, [r7, #MCH_BFLAGS]
+       
+       ldrb    r2, [r6, #MCA_FLAGS]    // read achn flags
+       bic     r2, #MCAF_VOLENV
+       tst     r1, #ENVFLAG_A<<8
+       orrne   r2, #MCAF_VOLENV
+       strb    r2, [r6, #MCA_FLAGS]
+       
+       movs    r1, r1, lsl#8+1         // shift out panning MSB
+       movcs   r1, r1, lsr#24          // if set, use new panning value
+       strcsb  r1, [r7, #MCH_PANNING]
+dvol_no_instrument:
+       
+       ldrb    r0, [r6, #MCA_SAMPLE]   // read sample#
+       subs    r0, #1
+       bcc     dvol_no_sample          // exit if invalid
+       
+       ldr     r2, [r8, #MPL_SONGADR]  // get sample address
+       ldr     r1, [r8, #MPL_SAMPTABLE]
+       ldr     r0, [r1, r0, lsl#2]
+       //add   r0, r2
+       
+       ldrh    r1, [r0, r2]
+       //ldrh  r1, [r0, #C_MASS_DV]    // read dvol & pan
+       strb    r1, [r7, #MCH_VOLUME]   // copy volume
+       movs    r1, r1, lsl#24-7
+       mov     r1, r1, lsr#24
+       strcsb  r1, [r7, #MCH_PANNING]
+dvol_skip:
+dvol_no_sample:
+
+       tst     r5, #MF_START|MF_DVOL
+       beq     dont_reset_volume
+       
+       ldrb    r0, [r8, #MPL_FLAGS]
+       tst     r0, #C_FLAGS_X
+       beq     1f
+       tst     r5, #MF_DVOL
+       beq     dont_reset_volume       // xm stuff
+1:
+reset_volume:
+       mov     r0, #(1<<10)            // [1]
+       mov     r1, #0                  // [1]
+       mov     r2, #0                  // [1]
+       add     r12, r6, #MCA_FADE      // [1]
+       stmia   r12, {r0-r2}            // [19]
+                                       // [23]
+       strh    r1, [r6, #MCA_ENVN_VOL]
+       strb    r1, [r6, #MCA_ENVN_PIC]
+       
+       // the last bit of code sets fade to (1<<10)
+       // and clears ENVC_VOL,ENVC_PAN,ENVC_PIC,
+       // AVIB_DEP, and AVIB_POS
+       // second bit clears ENVN vars
+       
+       strb    r1, [r7, #MCH_FXMEM]    // clear fx memory
+       
+       ldrb    r1, [r6, #MCA_FLAGS]    // set keyon
+       orr     r1, #MCAF_KEYON         // and clear envend+fade
+       bic     r1, #MCAF_ENVEND | MCAF_FADE
+       strb    r1, [r6, #MCA_FLAGS]
+       
+dont_reset_volume:
+
+       tst     r5, #MF_NOTEOFF         // test noteoff bit
+       beq     skip_noteoff
+       
+       ldrb    r1, [r6, #MCA_FLAGS]    // read flags
+       bic     r1, #MCAF_KEYON         // clear key-on
+       ldrb    r0, [r8, #MPL_FLAGS]    // read mod flags
+       tst     r0, #C_FLAGS_X          // test xm mode
+       orrne   r1, #MCAF_FADE          // XM starts fade immediately on note-off
+       strb    r1, [r6, #MCA_FLAGS]
+skip_noteoff:
+
+       tst     r5, #MF_NOTECUT         // test notecut bit
+       movne   r0, #0                  // clear volume
+       strneb  r0, [r7, #MCH_VOLUME]   // on note-cut
+       
+       bic     r5, #MF_START           // clear start flag
+       strb    r5, [r7, #MCH_FLAGS]    // save flags
+       b       mmUpdateChannel_TN_
+@************************************************************
+mmUpdateChannel_TN:                    // for other ticks
+@************************************************************
+       push    {lr}
+       
+mmUpdateChannel_TN_:
+       ldrb    r0, [r7, #MCH_ALLOC]    // get channel
+       cmp     r0, #255                //
+       bge     mmUpdateChannel_TN___   // no channel!
+       ldr     r6,=mm_achannels        //
+       ldr     r6, [r6]                //
+       mov     r1, #MCA_SIZE           //
+       mla     r6, r0, r1, r6          //
+       
+mmUpdateChannel_TN___:
+       
+       movge   r6, #0
+
+       ldr     r5, [r7, #MCH_PERIOD]           // r5 will be used to hold the period
+       ldr     r1,=mpp_vars
+       mov     r0, #0
+       strb    r0, [r1, #MPV_SAMPOFF]          // clear variables
+       strb    r0, [r1, #MPV_VOLPLUS]
+       strb    r0, [r1, #MPV_PANPLUS]
+       strb    r0, [r1, #MPV_NOTEDELAY]
+       
+//---------------------------------------------
+// Update Volume Commands
+//---------------------------------------------
+
+       ldrb    r0, [r7, #MCH_FLAGS]
+       tst     r0, #MF_HASVCMD
+       ldrne   r1,=mpp_Process_VolumeCommand
+       movne   lr, pc
+       bxne    r1
+       //blne  mpp_Process_VolumeCommand
+       
+//---------------------------------------------
+// Update Effects
+//---------------------------------------------        
+       
+       ldrb    r0, [r7, #MCH_FLAGS]
+       tst     r0, #MF_HASFX
+       ldrne   r1,=mpp_Process_Effect
+       movne   lr, pc
+       bxne    r1
+       //blne  mpp_Process_Effect
+       
+//---------------------------------------------
+
+       cmp     r6, #0
+       beq     no_achn
+       ldrh    r0, [r7, #MCH_VOLUME]   // read volume & cvolume
+       and     r1, r0, #255            // mask volume
+       mov     r0, r0, lsr#8           // mask cvolume
+       mul     r0, r1, r0              // multiply together
+       mov     r0, r0, lsr#5           // shift
+       strb    r0, [r6, #MCA_VOLUME]   // save in achn volume
+       
+       ldr     r1,=mpp_vars            // 
+       ldrsb   r2, [r1, #MPV_VOLPLUS]  // read volume addition
+       adds    r0, r2, lsl#3           // add to volume
+       movmi   r0, #0
+       cmp     r0, #129
+       movcs   r0, #128
+       strb    r0, [r1, #MPV_AFVOL]    // store in immediate vol
+       
+       ldrb    r0, [r1, #MPV_NOTEDELAY]// read note delay
+       cmp     r0, #0                  // dont update if nonzero
+       beq     channel_update_achn
+       
+       ldrb    r0, [r6, #MCA_FLAGS]
+       orr     r0, #MCAF_UPDATED
+       strb    r0, [r6, #MCA_FLAGS]
+       b       no_achn
+       
+channel_update_achn:
+
+       ldrb    r0, [r7, #MCH_PANNING]  // copy panning
+       strb    r0, [r6, #MCA_PANNING]
+       ldr     r0, [r7, #MCH_PERIOD]   // copy period
+       str     r0, [r6, #MCA_PERIOD]
+       mov     r0, #0
+       strh    r0, [r1, #MPV_PANPLUS]  // WHAT IS THIS??? "@ <---- RESERVED FOR LATER USE, CLEAR TO ZERO TEMPORARILY"
+       
+       ldrb    r0, [r6, #MCA_FLAGS]
+       orr     r0, #MCAF_UPDATED
+       strb    r0, [r6, #MCA_FLAGS]
+       
+       push    {r4}
+       ldrb    r4, [r7, #MCH_ALLOC]
+       ldr     r1,=mpp_Update_ACHN_notest
+       mov     lr, pc
+       bx      r1
+       
+       pop     {r4}
+no_achn:
+       pop     {lr}
+       bx      lr
+
+@***********************************************************
+mmChannelStartACHN:            // returns r2=note
+@***********************************************************
+       ldrb    r2, [r7, #MCH_BFLAGS+1]         // clear tremor/cutvol
+       bic     r2, #0b110
+       strb    r2, [r7, #MCH_BFLAGS+1]
+       
+       cmp     r6, #0                          // achn==0?
+       beq     1f                              // then skip this part
+       
+       mov     r0, #ACHN_FOREGROUND            // set foreground type
+       strb    r0, [r6, #MCA_TYPE]
+       
+       ldrb    r0, [r6, #MCA_FLAGS]            // read flags
+       bic     r0, #0b11000000                 // clear SUB, EFFECT
+       ldr     r1,=mpp_clayer                  // get layer
+       ldrb    r1, [r1]
+       orr     r0, r1, lsl#6                   // orr into flags
+       orr     r0, r10, r0, lsl#8              // orr PARENT
+       strh    r0, [r6, #MCA_PARENT]           // store parent/flags
+       ldrb    r0, [r7, #MCH_INST]             // copy instrument
+       strb    r0, [r6, #MCA_INST]
+1:     ldreqb  r0, [r7, #MCH_INST]
+       subs    r0, #1
+       bcc     invalid_instrument
+       
+       ldr     r2, [r8, #MPL_SONGADR]          // get instrument pointer
+       ldr     r1, [r8, #MPL_INSTTABLE]
+       ldr     r0, [r1, r0, lsl#2]
+       add     r0, r2
+       
+       ldrb    r2, [r7, #MCH_PNOTE]            // get pattern note
+       
+       ldrh    r1, [r0, #C_MASI_MAP]           // read notemap offset
+       tst     r1, #0x8000                     // test MSB
+       beq     full_notemap                    // if set: notemap doesnt exist!
+                                               // use single entry
+       cmp     r6, #0                          // if channel is valid
+       strneb  r1, [r6, #MCA_SAMPLE]           //   write sample value
+       strb    r2, [r7, #MCH_NOTE]             // write note value (without notemap, all entries == PNOTE)
+       bx      lr                              // return
+       
+full_notemap:
+       
+       add     r0, r2, lsl#1                   // add note offset
+       ldrh    r2, [r0, r1]                    // read notemap entry [instr+note*2+notemap_offset]
+       strb    r2, [r7, #MCH_NOTE]             // write note value
+       cmp     r6, #0                          // if channel is valid
+       mov     r0, r2, lsr#8                   //   write sample value
+       strneb  r0, [r6, #MCA_SAMPLE]           //   ..
+       and     r2, #255
+invalid_instrument:
+       bx      lr                              // return
+       
+/**********
+       
+       cmp     r6, #0                          // read notemap [sample]
+       ldrneb  r2, [r0, #C_MASI_MAP+1]
+       strneb  r2, [r6, #MCA_SAMPLE]
+       
+//invalid_instrument:                          // BUG???
+       ldrb    r2, [r0, #C_MASI_MAP]           // read notemap [note]
+       strb    r2, [r7, #MCH_NOTE]
+invalid_instrument:
+       
+       bx      lr
+**************/
+       
+.global mmGetPeriod
+@***********************************************************
+mmGetPeriod:
+@***********************************************************
+
+       ldrb    r0, [r8, #MPL_FLAGS]
+       lsrs    r0, #C_FLAGS_SS
+       bcs     get_linear_period       // tuning not used here with linear periods
+       
+get_amiga_period:
+       adr     r3, note_table_mod      // get octave/mod from note
+       ldrb    r0, [r3, r2]
+       sub     r3, #3*10
+       ldrb    r2, [r3, r2, lsr#2]
+       
+       // r0 = (note mod 12) << 1
+       // r1 = tuning
+       // r2 = (note / 12)
+       
+       ldr     r3,=ST3_FREQTABLE       // read st3 table entry
+       ldrh    r0, [r3, r0]
+       
+       mov     r3, #0x00AB0            // r3 = 133808
+       orr     r3, #0x20000
+       mul     r0, r3, r0              // multiply by magic number...
+       mov     r0, r0, lsr r2          // shift by octave
+       cmp     r1, #0
+       beq     1f
+       swi     SWI_DIVIDE<<16          // divide value / tuning
+1:     bx      lr
+       
+.global note_table_oct // remove this, make this static
+.global        note_table_mod  // remove this, make this staic
+note_table_oct:
+.byte  0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5, 6,6,6 ,7,7,7,8,8,8,9,9,9
+note_table_mod:
+.byte  0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0,2,4,6,8,10,12,14,16,18,20,22,0
+.align 2
+
+get_linear_period:
+       ldr     r1,=IT_PitchTable
+       ldr     r0, [r1, r2, lsl#2]
+       bx      lr
+
+.pool
diff --git a/libs/maxmod/mm_mixer_gba.S b/libs/maxmod/mm_mixer_gba.S
new file mode 100644 (file)
index 0000000..16bdeb7
--- /dev/null
@@ -0,0 +1,1367 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                            GBA Audio System                              *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+@ DEFINITIONS
+
+#include "mp_macros.inc"
+#include "mp_format_mas.inc"
+#include "mp_defs.inc"
+
+@ timer freq = 2^24 / mixfreq
+@ mixlen ~= mixfreq * 0.01673
+@ recommended mixing frequencies: 5734,7884,10512,13379,15768,18157,21024,26758,31536,36314,40137,42048
+@ other mixing frequencies may cause clicks
+@ mixlen must be divisible by 2
+
+@==================================================================
+@                          GLOBAL SYMBOLS
+@==================================================================
+
+
+       .global mmVBlank
+       .type   mmVBlank STT_FUNC
+       .global mmMixerMix
+       .type   mmMixerMix STT_FUNC
+
+       .global mmMixerSetSource
+       .type   mmMixerSetSource STT_FUNC
+       .global mmMixerSetRead
+       .type   mmMixerSetRead STT_FUNC
+       .global mmMixerSetFreq
+       .type   mmMixerSetFreq STT_FUNC
+       .global mmMixerStopChannel
+       .type   mmMixerStopChannel STT_FUNC
+       .global mmMixerInit
+       .type   mmMixerInit STT_FUNC
+       .global mmMixerChannelActive
+       .type   mmMixerChannelActive STT_FUNC
+       .global mmMixerMulFreq
+       .type   mmMixerMulFreq STT_FUNC
+       .global mmMixerSetVolume
+       .type   mmMixerSetVolume STT_FUNC
+       .global mmMixerSetPan
+       .type   mmMixerSetPan STT_FUNC
+
+//     .global mm_freqscalar
+       .global mm_mixlen
+       .global mm_bpmdv
+       .global mp_mix_seg
+       .global mp_writepos
+       .global mm_mixchannels
+
+@===============================================
+@ more definitions
+@===============================================
+
+.EQU   REG_SOUNDCNT_L, 0x4000080
+.EQU   REG_SOUNDCNT_H, 0x4000082
+.EQU   REG_SOUNDCNT_X, 0x4000084
+
+.EQU   REG_TM0CNT,     0x4000100
+
+.EQU   REG_DMA1SAD,    0x40000BC
+.EQU   REG_DMA1DAD,    0x40000C0
+.EQU   REG_DMA1CNT,    0x40000C4
+
+.EQU   REG_DMA2SAD,    0x40000C8
+.EQU   REG_DMA2DAD,    0x40000CC
+.EQU   REG_DMA2CNT,    0x40000D0
+
+.EQU   REG_DMA3SAD,    0x40000D4
+.EQU   REG_DMA3DAD,    0x40000D8
+.EQU   REG_DMA3CNT,    0x40000DC
+
+.EQU   REG_SGFIFOA,    0x40000A0
+.EQU   REG_SGFIFOB,    0x40000A4
+.EQU   REG_VCOUNT,     0x4000006
+
+@ MIXER CHANNEL FORMAT
+
+.equ   CHN_SIZE, 24
+
+.equ   CHN_SRC,0
+.equ   CHN_READ,4
+.equ   CHN_VOL,8
+.equ   CHN_PAN,9
+// 10
+// 11
+.equ   CHN_FREQ,12
+.equ   CHN_SIZE,16
+
+/////////////////////.equ      CHN_LOOP,8
+/////////////////////.equ      CHN_LEN,16
+
+@-------------------------------------
+
+.equ   FETCH_SIZE, 384
+.equ   FETCH_THRESHOLD, (6016)//7040           // frequency threshold (dont use fetch for high freqs!)
+
+@======================================================================
+@                               MEMORY
+@======================================================================
+
+.section .bss
+.align 2
+
+mp_writepos:   .space 4        @ wavebuffer write position
+
+mm_mixbuffer:  .space 4        @ dont move (see init)
+mm_mixchannels:        .space 4        @ dont move
+mm_wavebuffer: .space 4        @ dont move
+mm_mixlen:     .space 4        @ dont move
+mm_mixch_count:        .space 4        @ dont move
+mm_mixch_end:  .space 4        @ dont move
+mm_ratescale:  .space 4        @ dont move
+mm_timerfreq:  .space 4        @ dont move
+
+mm_bpmdv:      .space 4
+
+mp_mix_seg:    .space 1        @ mixing segment select
+
+.align 2
+
+mm_fetch:      .space FETCH_SIZE+16
+
+@ 11-bit mixed sample buffer
+@ data is interleaved
+@ left,left,right,right,left,left,etc...
+
+@===========================================================================
+@                                  PROGRAM
+@===========================================================================
+
+.section .iwram, "ax", %progbits
+
+.ARM
+.ALIGN 2
+
+mpm_nullsample:
+.byte  128
+
+.align 2
+@-----------------------------------------------------------------------------------------------------
+mmVBlank:                              @ vblank wrapper, used to reset dma...HIGH PRIORITY PLEASE!
+@-----------------------------------------------------------------------------------------------------
+       
+       b       .mpvb_disabled          @ disable until ready.. (overwrite this area with NOP)
+
+       ldr     r0,=mp_mix_seg          @ swap mixing segmentl
+       ldrsb   r1, [r0]
+       mvns    r1, r1
+       strb    r1, [r0]
+       beq     .mpvb_exit
+       
+       ldr     r1,=0x040000c6          @ dma control   RESTART DMA
+       ldr     r0,=0x0440              @ disable dma
+       strh    r0, [r1]
+       strh    r0, [r1,#12]
+       ldr     r0,=0xB600              @ restart dma
+       strh    r0, [r1]
+       strh    r0, [r1,#12]
+       
+       b       .mpvb_dontreset
+.mpvb_exit:
+       ldr     r0,=mp_writepos         @ restart write position
+@      ldr     r1,=mp_playbuffer_l     @ ...
+       ldr     r1,=mm_wavebuffer
+       ldr     r1, [r1]
+       str     r1, [r0]                @ ...
+.mpvb_dontreset:
+
+.mpvb_disabled:
+       ldr     r0,=0x3007FF8           @ acknowledge interrupt
+       ldrh    r2, [r0]
+       orr     r2, r2, #0x1
+       strh    r2, [r0]
+       
+       ldr     r0,=mm_vblank_function
+       ldr     r0, [r0]
+       cmp     r0, #0
+       bxeq    lr
+       bx      r0
+.pool
+
+.align 2
+@-------------------------------------------------------------------------
+mmMixerMix:                            @ params={ samples_count }
+@-------------------------------------------------------------------------
+       
+@ exit function if samples == 0
+@ it will malfunction.
+       
+       cmp     r0, #0
+       bne     .mp_zerocheck
+       bx      lr
+
+.mp_zerocheck:
+
+@ preserve registers
+
+       stmfd   sp!, {r4-r11,lr}
+
+       stmfd   sp!, {r0}               @ preserve mixing count
+
+@------------------------------------------------------------------------
+@ SECTOR 0, INITIALIZATION
+@------------------------------------------------------------------------
+
+@ clear mixing buffers
+
+       and     r10, r0, #7
+       mov     r2, r0, lsr#3           @ clearing samps*2*2 bytes (hword*stereo) 32 bytes at a time
+       ldr     r0,=mm_mixbuffer
+       ldr     r0,[r0]
+       mov     r1, #0                  @ zero variable
+       mov     r3, r1
+       mov     r4, r1
+       mov     r5, r1
+       mov     r6, r1
+       mov     r7, r1
+       mov     r8, r1
+       mov     r9, r1
+
+       cmp     r2, #0
+       beq     2f
+
+@ clear 32 bytes/write
+
+1:     stmia   r0!, {r1,r3-r9}
+       subs    r2, r2, #1
+       bne     1b
+2:
+       
+@ clear remainder
+       cmp     r10, #0
+       beq     2f
+
+1:     str     r1, [r0], #4
+       subs    r10, r10, #1
+       bne     1b
+2:
+
+@----------------------------------------------------------------------------------
+@ BEGIN MIXING ROUTINE
+@----------------------------------------------------------------------------------
+
+       ldr     r12,=mm_mixchannels
+       ldr     r12,[r12]
+       mov     r11, #0                 @ volume addition
+       
+@--------------------
+.mpm_cloop:
+@--------------------
+
+@----------------------------------------------------------------------
+@ SECTOR 1, CALCULATIONS
+@----------------------------------------------------------------------
+
+@ aliases
+#define rchan  r12
+#define rvolA  r11
+#define rsrc   r10
+#define        rfreq   r9
+#define rmixb  r8
+#define rread  r7
+#define rvolL  r5
+#define rvolR  r6
+#define rmixc  r4
+#define rmixcc r3
+
+@ read source address
+
+       ldr     rsrc, [rchan, #CHN_SRC]
+       cmp     rsrc, #0
+       bmi     .mpm_next               @ EXIT if MSB is set ------------>
+
+@ read frequency value
+       
+       ldr     rfreq, [rchan, #CHN_FREQ]
+       cmp     rfreq, #0
+       beq     .mpm_next               @ EXIT if zero ----------------=->
+       
+       ldr     r0,=mm_ratescale
+       ldr     r0, [r0]
+       mul     rfreq, r0
+       lsr     rfreq, #14
+       
+@ load mixing buffers
+
+       ldr     rmixb,=mm_mixbuffer
+       ldr     rmixb,[rmixb]
+
+@ get read position
+       
+       ldr     rread, [rchan, #CHN_READ]
+
+@ calculate volume
+
+       ldrb    rvolR,[rchan, #CHN_VOL] @ volume = 0-255
+       ldrb    r0, [rchan, #CHN_PAN]   @ pan = 0-255
+       
+       rsb     r0, r0, #256
+       mul     rvolL, r0, rvolR        @ (vol*pan) = right volume
+       mov     rvolL, rvolL, lsr#8
+       add     rvolA, rvolA, rvolL     @ add to volume counter
+       rsb     r0, r0, #256
+       mul     rvolR, r0, rvolR        @ calc left volume (256-pan)*vol
+       mov     rvolR, rvolR, lsr#8
+       add     rvolA, rvolA, rvolR, lsl#16
+       
+       ldr     rmixc, [sp]             @ get mix count
+       
+@****************************************************************
+.mpm_remix_test:
+@****************************************************************
+       
+       mov     r2, #0
+       mul     r1, rmixc, rfreq        // get number of samples that will be read
+       
+       cmp     rfreq, #FETCH_THRESHOLD
+       bge     1f
+       
+       cmp     r1, #FETCH_SIZE<<12     // check if its > fetch size
+       movhi   r1, #FETCH_SIZE<<12     // if so: clamp to fetch size and set flag
+       movhi   r2, #1
+       
+1:
+//     ldr     r0, [rchan, #CHN_LEN]   // now subtract length - read to get # samples remaining
+       ldr     r0, [rsrc, #-C_SAMPLE_DATA+C_SAMPLE_LEN]
+       rsb     r0, rread, r0, lsl#SAMPFRAC
+//     sub     r0, r0, rread           // in the source
+       cmp     r1, r0                  // clamp mix count
+       movhi   r1, r0
+       bhi     .calc_mix
+       cmp     r2, #0
+       beq     .mpm_mix_full
+       
+.calc_mix:
+       
+       
+       push    {r1}            // preserve sample count
+
+       
+//--------------------------------
+.macro DIV_ITER shift
+//--------------------------------
+       cmp     r0, rfreq, lsl#\shift
+       subcs   r0, rfreq, lsl#\shift
+       addcs   r2, #1<<\shift
+.endm
+//--------------------------------
+.macro DIVIDER shift
+//--------------------------------
+       DIV_ITER \shift
+.if \shift != 0
+       DIVIDER (\shift-1)
+.endif
+.endm
+//--------------------------------
+       
+       mov     r0, r1                  // divide samples / frequency (24bit/16bit)
+       mov     r2, #0
+       
+1:     subs    r0, rfreq, lsl#16       // divide top part
+       addcs   r2, #1<<16
+       bcs     1b
+       add     r0, rfreq, lsl#16
+       
+       DIVIDER 15                      // divide the rest...
+       
+       cmp     r0, #1                  // round up result
+       adc     r0, r2, #0
+       
+//     mov     r0,r1
+//     mov     r1,rfreq
+//     swi     0x060000
+//     cmp     r1, #0
+//     addne   r0, #1
+       
+       pop     {r1}            // restore sample count
+       sub     rmixc, r0       // subtract from mixcount
+       mov     rmixcc, r0
+       b       .mpm_mix_short
+
+@------------------------------------------------------------------------
+@ SECTOR 2, MIXING
+@------------------------------------------------------------------------
+       
+@-----------------------------------------
+.mpm_mix_full:
+@-----------------------------------------
+
+@ mix all samples
+       
+       mov     rmixcc, rmixc           @ <-- move mixing count
+       mov     rmixc, #0               @ clear mixing count
+.mpm_mix_short:
+       
+@------------------------------------------------------
+.mpm_remix:
+@------------------------------------------------------
+
+@ mix samples...
+@ preserve registers
+       stmfd   sp!, {rmixc,rvolA,rchan}
+
+@ zero mixing count??
+       cmp     rmixcc, #0
+       beq     .mpm_mix_complete       @ exit -------->
+       
+       cmp     rfreq, #FETCH_THRESHOLD
+       bge     .dont_use_fetch
+       
+       /*
+       ldr     r0,=mm_fetch            // transfer samples from ROM to RAM
+       add     r1, #2<<14              // add threshold for safety
+       ldr     r2,=REG_DMA3SAD
+       str     r0, [r2, #4]
+       add     r0, r10, rread, lsr#12
+       bic     r0, #0b11
+       str     r0, [r2, #0]
+       mov     r0, #(1<<31)+(1<<26)
+       add     r0, r1, lsr#14
+       str     r0, [r2, #8]
+       b       fooo
+       */
+       
+       // [cycle timings assume 3,1 ROM waitstates]
+       
+       push    {r3-r12}
+       // r10 is SRC!
+       ldr     r0,=mm_fetch            // destination
+       add     r10, r10, rread, lsr#12 // add read offset to source
+       bic     r10, #0b11              // align to 32 bits
+       add     r1, #4<<12              // add safety threshold
+       subs    r1, #40<<12             // subtract 36
+       
+       bcc     .exit_fetch             // skip large fetch if negative
+.fetch:        ldmia   r10!, {r2-r9,r11,r14}   // read 40 samples      [44 cycles]
+       stmia   r0!, {r2-r9,r11,r14}    // write 40 samples     [11 cycles]
+       subs    r1, #40<<12             // count                [1  cycle ]
+       bcc     .exit_fetch             // exit if done         [1  cycle ]
+       ldmia   r10!, {r2-r9,r11,r14}   // read 40 samples      [44 cycles]
+       stmia   r0!, {r2-r9,r11,r14}    // write 40 samples     [11 cycles]
+       subs    r1, #40<<12             // count                [1  cycle ]
+       bcc     .exit_fetch             // exit if done         [1  cycle ]
+       ldmia   r10!, {r2-r9,r11,r14}   // read 40 samples      [44 cycles]
+       stmia   r0!, {r2-r9,r11,r14}    // write 40 samples     [11 cycles]
+       subs    r1, #40<<12             // count                [1  cycle ]
+       bcs     .fetch                  // loop if remaining    [3  cycles]
+                                       //                      [173 cycles/120 samples]
+.exit_fetch:
+
+       adds    r1, #(40<<12)-(24<<12)
+       bmi     .end_medfetch
+.medfetch:
+       ldmia   r10!, {r2-r7}           // read 24 samples      [26]
+       stmia   r0!, {r2-r7}            // write 24 samples     [7 ]
+       subs    r1, #24<<12             // count                [1 ]
+       bcc     .end_medfetch           // exit if done         [1 ]
+       ldmia   r10!, {r2-r7}           // read 24 samples      [26]
+       stmia   r0!, {r2-r7}            // write 24 samples     [7 ]
+       subs    r1, #24<<12             // count                [1 ]
+       bcc     .end_medfetch           // exit if done         [1 ]
+       ldmia   r10!, {r2-r7}           // read 24 samples      [26]
+       stmia   r0!, {r2-r7}            // write 24 samples     [7 ]
+       subs    r1, #24<<12             // count                [1 ]
+       bcs     .medfetch               // loop                 [3 ]
+.end_medfetch:                         //                      [107]
+
+       adds    r1, #24<<12             // add 24 back
+       bmi     .end_fetch              // exit if <= 0
+.fetchsmall:
+       ldr     r2, [r10], #4           // read 4 samples       [8]
+       str     r2, [r0], #4            // write 4 samples      [2]
+       subs    r1, #4<<12              // count                [1]
+       ble     .end_fetch              // exit maybe           [1]
+       ldr     r2, [r10], #4           // read 4 samples       [8]
+       str     r2, [r0], #4            // write 4 samples      [2]
+       subs    r1, #4<<12              // count                [1]
+       bgt     .fetchsmall             // exit maybe           [3]
+.end_fetch:
+       
+       pop     {r3-r12}                // restore regs
+       
+fooo:
+       
+       mov     r0, rread, lsr#12       // get read integer
+       push    {r0, rsrc}              // preserve regs
+       bic     rread, r0, lsl#12       // clear integer
+       and     r0, #0b11               // mask low bits
+       ldr     rsrc,=mm_fetch          //
+       add     rsrc, rsrc, r0          // offset source (fetch is word aligned!)
+       
+.dont_use_fetch:
+       
+       tst     rmixb, #0b11            // test alignment of work output
+       beq     .mpm_aligned
+       
+#define rsamp1 r1 
+#define        rsampa  r0
+#define rsampb r2
+                                                       @ routine to WORD align mixing sector
+       ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ load sample
+       add     rread, rread, rfreq                     @ add frequency
+       mul     rsampb, rsampa, rvolL                   @ multiply by left volume
+       ldrh    rsamp1, [rmixb]                         @ add to mixing buffer (left)
+       add     rsamp1, rsamp1, rsampb, lsr#5           
+       strh    rsamp1, [rmixb], #4
+       mul     rsampb, rsampa, rvolR                   @ multiply by right volume
+       ldrh    rsamp1, [rmixb]                         @ add to mixing buffer (right)
+       add     rsamp1, rsamp1, rsampb, lsr#5
+       strh    rsamp1, [rmixb], #2
+       sub     rmixcc, rmixcc, #1                      @ decrement mix count
+       
+#undef rsamp1
+#undef rsampa
+#undef rsampb
+
+.mpm_aligned:
+                                       // determine mixing mode
+       cmp     rvolL, rvolR            // if volume_left == volume_right then assume 'center' mixing
+       beq     .mpm_mix_ac             
+       cmp     rvolL, #0               // if left volume is zero, do 'right' mixing
+       beq     .mpm_mix_ar
+       cmp     rvolR, #0               // if right volume is zero, do 'left' mixing
+       beq     .mpm_mix_al
+       b       mmMix_ArbPanning        // otherwise do arb mixing
+.mpm_mix_al:
+       b       mmMix_HardLeft
+.mpm_mix_ar:
+       b       mmMix_HardRight
+
+@ center mixing------------
+.mpm_mix_ac:
+       cmp     rvolL, #0               // check if volume is zero
+       bne     mmMix_CenteredPanning   // mix samples if not zero
+       b       mmMix_Skip              // skip samples if zero
+.mpm_mix_complete:
+
+       cmp     rfreq, #FETCH_THRESHOLD
+       poplt   {r0, rsrc}                      // restore regs
+       addlt   rread, rread, r0, lsl#12        // add old integer to read
+       ldmfd   sp!, {rmixc,rvolA,rchan}        // restore more regs
+       
+       ldr     r1, [rsrc, #-C_SAMPLE_DATA+C_SAMPLE_LEN]
+       lsl     r1, #SAMPFRAC
+       //ldr   r1, [rchan, #CHN_LEN]           // check length against position
+       cmp     r1, rread
+       bgt     .mpm_channelfinished
+       
+//     ldr     r1, [rchan, #CHN_LOOP]          // read channel loop
+       
+       ldr     r1, [rsrc, #-C_SAMPLE_DATA+C_SAMPLE_LOOP]
+       cmp     r1, #0
+//     movs    r1, r1, lsl#8                   // mask out high byte (that is VOLUME)
+       bmi     .mpm_channel_stop               // MSB = no loop, stop channel ->
+       
+
+       sub     rread,rread,r1,lsl#(SAMPFRAC)   // subtract loop length (<<SAMPFRAC) from position
+
+       cmp     rmixc, #0                       // mix more samples?    
+       ble     .mpm_channelfinished            // no ->
+       b       .mpm_remix_test                 // yes: loop
+
+@----------------------------------------------------------------
+.mpm_channel_stop:
+@----------------------------------------------------------------
+
+@ *** END OF SAMPLE
+       mov     r1, #1<<31                      // disable channel
+       str     r1, [rchan, #CHN_SRC]
+
+@ mix zero into the rest of the buffer
+       ldr     rsrc,=mpm_nullsample            // mix zero into the rest of the buffer
+       mov     rfreq, #0                       // zero freq
+       mov     rread, #0                       // zero freq
+       movs    rmixcc, rmixc                   // mix remaining amount
+       ble     .mpm_channelfinished            // (exit if zero)
+       mov     rmixc, #0
+       mov     r1, #0
+       b       .mpm_remix                      // mix 'zero' into the rest of the data
+       
+@---------------------------------------------------------------
+.mpm_channelfinished:
+@---------------------------------------------------------------
+       
+       cmp     rmixc, #0                       // mix more samples?
+       bne     .mpm_remix_test                 // yes: loop
+       
+@  *** END OF MIXING ***
+
+       str     rread, [rchan, #CHN_READ]       // save read position
+       
+@-----------------------
+.mpm_next:
+@-----------------------
+
+       add     rchan, rchan, #CHN_SIZE         // seek to next channel
+       ldr     r0,=mm_mixch_end                // compare with ending
+       ldr     r0,[r0]                         
+       cmp     rchan, r0                       
+       bne     .mpm_cloop                      // loop if !=
+
+@----------------------------------------------------------------------------------
+@ SECTOR 3, POST-PROCESSING
+@----------------------------------------------------------------------------------
+
+#define        prmixl          r0
+#define        prwritel        r2
+#define        prwriter        r3
+#define prcount                r4
+#define prvolR         r12
+#define prvolL         r11
+#define        prsamp1         r6
+#define prsamp2                r5
+#define prsamp3                r7
+       
+       ldr     prmixl,=mm_mixbuffer
+       ldr     prmixl,[prmixl]
+       
+       ldr     prwritel,=mp_writepos
+       ldr     prwritel, [prwritel]
+       ldr     prwriter,=mm_mixlen
+       ldr     prwriter, [prwriter]
+       add     prwriter, prwritel, prwriter, lsl#1 @#MP_MIXLEN*2
+       ldmfd   sp!, {prcount}
+
+@ get volume accumulators
+       
+       mov     prvolR, rvolA, lsr#16+1
+       mov     prvolR, prvolR, lsl#3
+       
+       mov     prvolL, rvolA, lsl#16
+       mov     prvolL, prvolL, lsr#16+1
+       mov     prvolL, prvolL, lsl#3
+       
+       subs    prcount, prcount, #1
+       ble     .mpm_copy2_end
+
+@--------------------------------------------------
+.mpm_copy2:
+@--------------------------------------------------
+
+@ ***************** LEFT OUTPUT ******************
+
+       ldr     prsamp1, [prmixl], #4           @ get 2 mixed samples
+       sub     prsamp2, prsamp1, prvolL        @ convert to signed
+
+       mov     prsamp2, prsamp2, lsl#16        @ mask low hword with sign extension
+       movs    prsamp2, prsamp2, asr#16+3      @ and convert 11-bit to 8-bit
+
+       cmp     prsamp2, #-128                  @ clamp
+       movlt   prsamp2, #-128                  @
+       cmp     prsamp2, #127                   @
+       movgt   prsamp2, #127                   @
+       
+                                               @ next sample...
+       rsbs    prsamp3, prvolL, prsamp1,lsr#16 @ convert to signed
+       movs    prsamp3, prsamp3, asr#3         @ convert 11-bit to 8-bit
+
+       cmp     prsamp3, #-128                  @ clamp
+       movlt   prsamp3, #-128                  @
+       cmp     prsamp3, #127                   @
+       movgt   prsamp3, #127                   @
+       
+       and     prsamp2, prsamp2, #255          @ write to output
+       orr     prsamp2, prsamp3, lsl#8         @
+       strh    prsamp2, [prwritel], #2         @
+
+@ **************** RIGHT OUTPUT ******************
+       
+       ldr     prsamp1, [prmixl], #4           @ get 2 mixed samples
+       sub     prsamp2, prsamp1, prvolR        @ convert to signed
+       
+       mov     prsamp2, prsamp2, lsl#16        @ mask low hword and convert 11-bit to 8-bit
+       movs    prsamp2, prsamp2, asr#16+3      @
+       
+       cmp     prsamp2, #-128                  @ clamp value
+       movlt   prsamp2, #-128                  @
+       cmp     prsamp2, #127                   @
+       movgt   prsamp2, #127                   @
+       
+                                               @ next sample...
+       rsbs    prsamp3, prvolR, prsamp1,lsr#16 @ convert to signed
+       movs    prsamp3, prsamp3, asr#3         @ convert 11-bit to 8-bit
+       
+       cmp     prsamp3, #-128                  @ clamp value
+       movlt   prsamp3, #-128                  @
+       cmp     prsamp3, #127                   @
+       movgt   prsamp3, #127                   @
+       
+       and     prsamp2, prsamp2, #255          @ write to output
+       orr     prsamp2, prsamp3, lsl#8         @
+       strh    prsamp2, [prwriter], #2         @
+
+       subs    prcount, prcount, #2            @ loop
+       bgt     .mpm_copy2                      @
+
+@------------------------------------------------------------------
+
+.mpm_copy2_end:
+       ldr     r0,=mp_writepos                 @ store new write position
+       str     prwritel, [r0]
+
+@------------------------------------------------------------------
+
+       ldmfd   sp!, {r4-r11,lr}                // restore registers
+       bx      lr                              // phew!
+
+.pool
+
+@================================================================================
+@                                 MIXING ROUTINES
+@================================================================================
+
+.macro READ_AND_INCREMENT reg
+       ldrb    \reg, [rsrc, rread, lsr#SAMPFRAC]
+       add     rread, rread, rfreq
+.endm
+
+.macro READ_D reg, tmp
+       READ_AND_INCREMENT \reg
+       READ_AND_INCREMENT \tmp
+       orr     \reg, \tmp, lsl#16
+.endm
+
+.macro MIX_D vol, tmp1, tmp2
+       READ_D  \tmp1, \tmp2                    // load&combine 2 samples
+       mul     \tmp2, \tmp1, \vol              // multiply by volume
+       bic     \tmp2, \tmp2, #0x1F0000         // prepare for shift
+.endm
+
+.macro MIX_DA vol, target, tmp1, tmp2
+       MIX_D   \vol, \tmp1, \tmp2
+       add     \target, \target, \tmp2, lsr#5  // add 11-bit values
+.endm
+
+.macro MIX_DC vol, target_a, target_b, tmp1, tmp2
+       MIX_D   \vol, \tmp1, \tmp2
+       add     \target_a, \target_a, \tmp2, lsr#5      // add 11-bit values
+       add     \target_b, \target_b, \tmp2, lsr#5
+.endm
+
+.macro MIX_DB  vol_l, vol_r, target_a, target_b, tmp1, tmp2
+       READ_D  \tmp1, \tmp2
+       
+.if \target_a != 0
+       mul     \tmp2, \tmp1, \vol_l                    // multiply by volume
+       bic     \tmp2, \tmp2, #0x1F0000                 // prepare for shift
+       add     \target_a, \target_a, \tmp2, lsr#5      // add 11-bit values
+.endif
+.if \target_b != 0
+       mul     \tmp2, \tmp1, \vol_r                    // multiply by volume
+       bic     \tmp2, \tmp2, #0x1F0000                 // prepare for shift
+       add     \target_b, \target_b, \tmp2, lsr#5      // add 11-bit values
+.endif
+.endm
+
+@-----------------------------------------------------------------------------------
+mmMix_Skip:
+@-----------------------------------------------------------------------------------
+
+@ mix nothing
+       mul     r0, rmixcc, rfreq       @ read += samples * frequency
+       add     rread, rread, r0
+       b       .mpm_mix_complete
+
+@-----------------------------------------------------------------------------------
+mmMix_HardLeft:
+@-----------------------------------------------------------------------------------
+@ mix hard panned left
+
+       bl      mmMix_SingleChannel     // mix left channel
+       bgt     mmMix_Remainder         // mix remaining amount
+       b       .mpm_mix_complete       // return
+
+@-----------------------------------------------------------------------------------
+mmMix_HardRight:
+@-----------------------------------------------------------------------------------
+@ hard panned right
+
+       mov     rvolL, rvolR            // move volume
+       add     rmixb, rmixb, #4        // offset to 'right' data
+       bl      mmMix_SingleChannel     // mix routine
+       mov     rvolL, #0               // clear left volume again
+       sub     rmixb, rmixb, #4        // remove offet
+       bgt     mmMix_Remainder         // mix remaining count
+       b       .mpm_mix_complete       // return
+
+@----------------------------------------
+mmMix_SingleChannel:
+@----------------------------------------
+
+#define rsamp1 r1 
+#define rsamp2 r2
+#define rsamp3 r11
+#define        rsamp4  r12
+#define        rsampa  r0
+#define rsampb r4
+
+@ hard panned mixing (single channel mono)
+@ interleaving really cuts this method's effectiveness :(
+
+@ mix 8 samples/loop
+
+       subs    rmixcc, rmixcc, #8
+       bmi     .mpmah_8e
+.mpmah_8:
+       ldmia   rmixb, {rsamp1, rsamp2, rsamp3}         // load data [2nd word not used]
+       MIX_DA  rvolL, rsamp1, rsampa, rsampb           // mix data
+       str     rsamp1, [rmixb], #8                     // store mixed word
+       MIX_DA  rvolL, rsamp3, rsampa, rsampb           // mix data
+       str     rsamp3, [rmixb], #8                     // store mixed word
+       ldmia   rmixb, {rsamp1, rsamp2, rsamp3}         // load data [2nd word not used]
+       MIX_DA  rvolL, rsamp1, rsampa, rsampb           // mix data
+       str     rsamp1, [rmixb], #8                     // store mixed word
+       MIX_DA  rvolL, rsamp3, rsampa, rsampb           // mix data
+       str     rsamp3, [rmixb], #8                     // store mixed word
+       subs    rmixcc, rmixcc, #8                      // decrement 8 samples
+       bpl     .mpmah_8                                // loop if >= 0
+.mpmah_8e:
+
+@ mix remainder samples
+
+       adds    rmixcc, rmixcc, #8                      // fix mixing count
+       bx      lr                                      // return
+
+#undef rsamp1
+#undef rsamp2
+#undef rsamp3
+#undef rsamp4
+#undef rsampa
+#undef rsampb
+
+@----------------------------------------------------------
+mmMix_CenteredPanning:
+@----------------------------------------------------------
+       
+#define rsamp1 r1 
+#define rsamp2 r2
+#define rsamp3 r4
+#define rsamp4 r6
+#define        rsamp5  r11
+#define rsamp6 r12
+#define        rsampa  r0
+#define rsampb lr
+
+@ mix center panning (double channel mono)
+       
+       subs    rmixcc, rmixcc, #6
+       bmi     .mpmac_6e
+.mpmac_6:
+       
+       ldmia   rmixb, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5,rsamp6}      // read words
+       MIX_DC  rvolL, rsamp1, rsamp2, rsampa, rsampb                   // mix data
+       MIX_DC  rvolL, rsamp3, rsamp4, rsampa, rsampb
+       MIX_DC  rvolL, rsamp5, rsamp6, rsampa, rsampb
+       stmia   rmixb!, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5,rsamp6}     // write words
+       subs    rmixcc, rmixcc, #6                                      // count
+       bpl     .mpmac_6                                                // loop
+.mpmac_6e:
+
+       mov     rvolR, rvolL                    // restore right volume (same as left)
+       adds    rmixcc, rmixcc, #6              // fix mix count
+       bgt     mmMix_Remainder                 // mix remaining amount
+       b       .mpm_mix_complete               // finished mixing segment
+       
+#undef rsamp1
+#undef rsamp2
+#undef rsamp3
+#undef rsamp4
+#undef rsamp5
+#undef rsamp6
+#undef rsampa
+#undef rsampb
+
+@---------------------------------------------------
+mmMix_ArbPanning:              // SLOWEST!
+@---------------------------------------------------
+
+#define rsamp1 r1 
+#define rsamp2 r2
+#define rsamp3 r4
+#define rsamp4 r11
+#define        rsampa  r0
+#define rsampb r12
+#define rsamp5 r14
+
+       subs    r3, r3, #10
+       bmi     .mpmaa_10e
+.mpmaa_10:
+       
+       ldmia   rmixb, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}     // load bufferdata
+       MIX_DB  rvolL, rvolR, rsamp1, rsamp2, rsampa, rsampb    // mix
+       MIX_DB  rvolL, rvolR, rsamp3, rsamp4, rsampa, rsampb    // mix
+       MIX_DB  rvolL, rvolR, rsamp5, 0, rsampa, rsampb         // mix half
+       stmia   rmixb!, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}    // store bufferdata
+       ldmia   rmixb, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}     // load bufferdata
+       mul     rsampb, rsampa, rvolR                           // mix other half
+       bic     rsampb, rsampb, #0x1F0000                       // ..
+       add     rsamp1, rsamp1, rsampb, lsr#5                   // ..
+       MIX_DB  rvolL, rvolR, rsamp2, rsamp3, rsampa, rsampb    // mix
+       MIX_DB  rvolL, rvolR, rsamp4, rsamp5, rsampa, rsampb    // mix
+       stmia   rmixb!, {rsamp1,rsamp2,rsamp3,rsamp4,rsamp5}    // store bufferdata
+       subs    rmixcc, rmixcc, #10                             // count
+       bpl     .mpmaa_10                                       // loop
+.mpmaa_10e:
+       
+       adds    rmixcc, rmixcc, #10
+       bgt     mmMix_Remainder
+       b       .mpm_mix_complete
+
+.pool
+
+#undef rsamp1  
+#undef rsamp2 
+#undef rsamp3
+#undef rsamp4
+#undef rsampa
+#undef rsampb
+
+//---------------------------------------------------------------------------
+mmMix_Remainder:
+//---------------------------------------------------------------------------
+// (slow function to mix remaining amount of samples)
+// assumes mix count isn't zero!
+
+#define rsamp1 r1
+#define rsamp2 r2
+#define rvol   r11
+#define rsampa r0
+#define rsampb r4
+
+       orr     rvol, rvolL, rvolR, lsl#16
+       
+.mix_remaining:
+/*     ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ 3 load sample
+       add     rread, rread, rfreq                     @ 1 add frequency
+       mul     rsampb, rsampa, rvolL                   @ 2 multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ 3 load mix buffer entry
+       add     rsamp1, rsamp1, rsampb, lsr#5           @ 1 add
+       strh    rsamp1, [rmixb], #2                     @ 2 store
+       ldrh    rsamp1, [rmixb, #2]                     @ 3
+       mul     rsampb, rsampa, rvolR                   @ 2
+       add     rsamp1, rsamp1, rsampb, lsr#5           @ 1
+       strh    rsamp1, [rmixb, #2]                     @ 2
+*/
+       
+       ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ 3 load sample
+       add     rread, rread, rfreq                     @ 1 add frequency
+       mul     rsampb, rvol, rsampa                    @ 2 multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ 3 load mix buffer entry (left)
+       bic     rsamp2, rsampb, #0xFF0000               @ 1 prep for shift
+       add     rsamp1, rsamp1, rsamp2, lsr#5           @ 1 add
+       strh    rsamp1, [rmixb], #2                     @ 2 store (left)
+       ldrh    rsamp1, [rmixb, #2]                     @ 3 load (right)
+       add     rsamp1, rsamp1, rsampb, lsr#16+5        @ 1 add values
+       strh    rsamp1, [rmixb, #2]                     @ 2 store (right)
+       
+       subs    rmixcc, rmixcc, #2                      @ 2
+       blt     .end_mix_remaining                      @ 1/exit
+
+/*     ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ load sample
+       add     rread, rread, rfreq                     @ add frequency
+       mul     rsampb, rsampa, rvolL                   @ multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ load mix buffer entry
+       add     rsamp1, rsamp1, rsampb, lsr#5           @ add
+       strh    rsamp1, [rmixb], #4                     @ store
+       ldrh    rsamp1, [rmixb]
+       mul     rsampb, rsampa, rvolR
+       add     rsamp1, rsamp1, rsampb, lsr#5
+       strh    rsamp1, [rmixb], #2
+*/
+
+       ldrb    rsampa, [rsrc, rread, lsr#SAMPFRAC]     @ 3 load sample
+       add     rread, rread, rfreq                     @ 1 add frequency
+       mul     rsampb, rvol, rsampa                    @ 2 multiply by volume
+       ldrh    rsamp1, [rmixb]                         @ 3 load mix buffer entry (left)
+       bic     rsamp2, rsampb, #0xFF0000               @ 1 prep for shift
+       add     rsamp1, rsamp1, rsamp2, lsr#5           @ 1 add
+       strh    rsamp1, [rmixb], #4                     @ 2 store (left)
+       ldrh    rsamp1, [rmixb]                         @ 3 load (right)
+       add     rsamp1, rsamp1, rsampb, lsr#16+5        @ 1 add values
+       strh    rsamp1, [rmixb], #2                     @ 2 store (right)
+
+       bgt     .mix_remaining
+.end_mix_remaining:
+       
+       b       .mpm_mix_complete
+       
+#undef rsamp1
+#undef rsamp2
+#undef rvol
+#undef rsampa
+#undef rsampb
+
+@============================================================================
+@                                 END OF MIXER
+@============================================================================
+
+       .TEXT
+       .THUMB
+       .ALIGN 2
+
+/****************************************************************************
+ * mmMixerSetSource( channel, p_sample )
+ *
+ * Set channel source
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetSource: 
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2, [r2]                //
+       add     r0, r2                  //
+       
+       add     r1, #C_SAMPLE_DATA      // set sample data address
+       str     r1, [r0, #CHN_SRC]      //
+       
+       mov     r1, #0                  // reset read position
+       str     r1, [r0, #CHN_READ]     //
+       
+       bx      lr
+
+/****************************************************************************
+ * mmMixerSetRead( channel, value )
+ *
+ * Set channel read position
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetRead:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r2                  //
+       
+       str     r1, [r0, #CHN_READ]     // store new offset
+       bx      lr                      //
+
+/****************************************************************************
+ * mmMixerSetFreq
+ *
+ * Set channel mixing rate
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetFreq:
+
+//     push    {r3, r4}                // FIXME: why would you preserve r3?
+       
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2, r0              // 
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r0, r2              //
+       
+       lsl     r1, #2 // ...
+       
+       strh    r1, [r0, #CHN_FREQ]
+       bx      lr
+       
+/*
+       ldr     r4,=mm_freqscalar       // r4=scale
+       ldr     r4,[r4]                 //
+
+       add     r2, pc, #0              // switch to arm for a nice long multiply
+       bx      r2
+       
+       .ARM
+       
+       //------------------------------------
+       // fix frequency to match mixing rate
+       // a = specified frequency
+       // hz = a*2y13 / pr
+       //------------------------------------
+       
+       umull   r3, r2, r1, r4          // long multiply
+       add     r3, r3, #32768          // shift, etc..
+       mov     r3, r3, lsr#16          //
+       orr     r3, r3, r2, lsl#16      //
+       
+       str     r3, [r0, #CHN_FREQ]     // set chan frequency
+       
+       ldmfd   sp!, {r3,r4}            // pop registers
+       bx      lr                      // return
+       */
+       .THUMB
+
+/****************************************************************************
+ * mmMixerMulFreq( channel, value )
+ *
+ * Scale mixing frequency
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerMulFreq:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2, [r2]                //
+       add     r0, r2                  //
+
+       ldr     r3, [r0, #CHN_FREQ]     // scale
+       mul     r3, r1                  //
+       lsr     r3, #10                 //
+       str     r3, [r0, #CHN_FREQ]     //
+       bx      lr
+
+/****************************************************************************
+ * mmMixerStopChannel( channel )
+ *
+ * Stop mixing channel
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerStopChannel:
+
+       mov     r1, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r1                  //
+       ldr     r1,=mm_mixchannels      //
+       ldr     r1,[r1]                 //
+       add     r0, r1                  //
+               
+       mov     r1, #1                  // set MSB (disable) of source
+       lsl     r1, #31                 //
+       str     r1, [r0]                //
+       bx      lr
+
+/****************************************************************************
+ * mmMixerChannelActive( channel )
+ *
+ * Test if mixing channel is active
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerChannelActive:
+
+       mov     r1, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r1                  //
+       ldr     r1,=mm_mixchannels      //
+       ldr     r1,[r1]                 //
+       add     r0, r1                  //
+       
+mp_Mixer_ChannelEnabledA:
+       ldr     r0, [r0, #CHN_SRC]      // nonzero (-1) = enabled
+       asr     r0, #31                 // zero         = disabled
+       mvn     r0, r0                  //
+       bx      lr
+
+/****************************************************************************
+ * mmMixerSetVolume( channel, volume )
+ * 
+ * Set channel volume
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetVolume:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r2                  //
+       
+       strb    r1, [r0, #CHN_VOL]      // set volume
+       
+       bx      lr
+       
+/****************************************************************************
+ * mmMixerSetPan( channel, panning )
+ *
+ * Set channel panning
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerSetPan:
+
+       mov     r2, #CHN_SIZE           // get channel pointer from index
+       mul     r0, r2                  //
+       ldr     r2,=mm_mixchannels      //
+       ldr     r2,[r2]                 //
+       add     r0, r2                  //
+       
+       strb    r1, [r0, #CHN_PAN]      // set panning
+       bx      lr
+       
+/****************************************************************************
+ * mmMixerInit( system )
+ *
+ * Initialize mixer
+ ****************************************************************************/
+                                       .thumb_func
+mmMixerInit:
+
+       ldr     r2,=mm_mixbuffer
+       ldr     r1, [r0,#MM_GBA_SYSTEM_ACH_COUNT]
+       str     r1, [r2,#16]
+       
+       mov     r3, #CHN_SIZE
+       mul     r1, r3
+       ldr     r3, [r0,#MM_GBA_SYSTEM_MIXCH]
+       str     r3, [r2,#4]
+       add     r3, r1
+       str     r3, [r2,#20]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_MIXMEM]
+       str     r1, [r2,#0]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_WAVEMEM]
+       str     r1, [r2,#8]
+       ldr     r1, [r0,#MM_GBA_SYSTEM_MODE]
+       lsl     r1, #1
+       adr     r3, mp_mixing_lengths
+       ldrh    r3, [r3,r1]
+       str     r3, [r2,#12]
+       adr     r3, mp_rate_scales
+       ldrh    r3, [r3, r1]
+       str     r3, [r2, #24]
+       adr     r3, mp_timing_sheet
+       ldrh    r3, [r3, r1]
+       str     r3, [r2, #28]
+       adr     r3, mp_bpm_divisors
+       lsl     r1, #1
+       ldr     r3, [r3,r1]
+
+       ldr     r2,=mm_bpmdv
+       str     r3, [r2,#0]
+
+       
+       ldr     r0,=mm_wavebuffer               @ clear wave buffer
+       ldr     r0,[r0]
+       ldr     r1,=mm_mixlen
+       ldr     r1, [r1]
+       mov     r2, #0                          @ ..
+.mpi_loop1:                                    @ ..
+       stmia   r0!, {r2}                       @ ..
+       sub     r1, r1, #1                      @ ..
+       bne     .mpi_loop1                      @ ..
+       
+       ldr     r0,=mp_mix_seg                  @ reset mixing segment
+       strb    r2, [r0]                        @ ..
+       
+       ldr     r0,=mm_mixchannels              @ disable mixing channels
+       ldr     r1,[r0,#12]@ nchannels
+       ldr     r0,[r0]
+       ldr     r3,=1<<31
+.mpi_loop2:
+       str     r3, [r0, #CHN_SRC]
+       add     r0, #CHN_SIZE
+       sub     r1, #1
+       bne     .mpi_loop2
+
+       ldr     r0,=mmVBlank                    @ enable vblank routine
+       ldr     r1,=0xE1A00000                  @ ..
+       str     r1, [r0]                        @ ..
+       
+       ldr     r0,=REG_SGFIFOA                 @ clear fifo data
+       str     r2, [r0]                        @ ..
+       str     r2, [r0, #4]                    @ ..
+       ldr     r0,=REG_SOUNDCNT_H              @ reset direct sound
+       strh    r2, [r0]                        @ ..
+       ldr     r1,=0x9A0C                      @ setup sound [DIRECT SOUND A/B reset,timer0,A=left,B=right,volume=100%]
+       strh    r1, [r0]                        @ ..
+       ldr     r0,=REG_DMA1SAD                 @ setup DMA source addresses (playback buffers)
+       ldr     r1,=mm_wavebuffer
+       ldr     r2, [r1, #4]@mixlen
+       ldr     r1, [r1]
+       
+       @ldr    r1,=mp_playbuffer_l             @ ..
+       str     r1, [r0]                        @ ..
+
+       add     r1,r2
+       add     r1,r2
+
+@      ldr     r1,=mp_playbuffer_r             @ ..
+       str     r1, [r0, #12]                   @ ..
+       
+       ldr     r1,=REG_SGFIFOA                 @ setup DMA destination (sound fifo)
+       str     r1, [r0, #4]                    @ ..
+       add     r1, #4                          @ ..
+       str     r1, [r0, #16]                   @ ..
+       
+       ldr     r1,=0xB6000000                  @ enable DMA (enable,fifo request,32-bit,repeat)
+       str     r1, [r0, #8]                    @ ..
+       str     r1, [r0, #20]                   @ ..
+       
+       ldr     r0,=REG_SOUNDCNT_X              @ master sound enable
+       mov     r1, #0x80                       @ ..
+       strh    r1, [r0]                        @ ..
+       
+       ldr     r0,=REG_VCOUNT                  @ wait for new frame
+.mpi_vsync:                                    @ ..
+       ldrh    r1, [r0]                        @ skip current vblank period
+       cmp     r1, #160                        @ ..
+       bge     .mpi_vsync                      @ ..
+.mpi_vsync2:
+       ldrh    r1, [r0]                        @ wait for new one
+       cmp     r1, #160                        @ ..
+       blt     .mpi_vsync2                     @ ..
+       
+.mpi_vsync_2:                                  @ pass#2
+       ldrh    r1, [r0]                        @ skip current vblank period
+       cmp     r1, #160                        @ ..
+       bge     .mpi_vsync_2                    @ ..
+.mpi_vsync2_2:
+       ldrh    r1, [r0]                        @ wait for new one
+       cmp     r1, #160                        @ ..
+       blt     .mpi_vsync2_2                   @ ..
+       
+       ldr     r0,=REG_TM0CNT                          @ enable sampling timer
+       ldr     r1,=mm_timerfreq
+       ldr     r1,[r1]
+       mov     r2, #0x80
+       lsl     r2, #16
+       orr     r1, r2
+       @ldr    r1,=(-MP_TIMERFREQ&0xFFFF) | (0x80<<16) @ ..
+       str     r1, [r0]                                @ ..
+       bx      lr                                      @ finished
+       
+
+// round(rate / 59.737)
+.align 2
+mp_mixing_lengths:
+       .hword  136,  176,  224,  264,  304,  352,  448,  528
+       @      8khz,10khz,13khz,16khz,18khz,21khz,27khz,32khz
+
+.align 2
+//mp_freq_scales:              @ (16khz -> real)
+//     .hword 33056, 25536, 20064, 17024, 14784, 12768
+
+// 15768*16384 / rate
+mp_rate_scales:
+       .hword 31812, 24576, 19310, 16384, 14228, 12288,  9655,  8192
+       @       8khz, 10khz, 13khz, 16khz, 18khz, 21khz, 27khz, 32khz
+       @       8121, 10512, 13379, 15768, 18157, 21024, 26758, 31536,
+
+.align 2
+// gbaclock / rate
+mp_timing_sheet:
+       .hword -2066,-1596,-1254,-1064, -924, -798, -627, -532
+       @       8khz,10khz,13khz,16khz,18khz,21khz,27khz,32khz
+
+.align 2
+// rate * 2.5
+mp_bpm_divisors:
+       .word 20302,26280,33447,39420,45393,52560,66895,78840
+
+       
+.end
diff --git a/libs/maxmod/mm_types.h b/libs/maxmod/mm_types.h
new file mode 100644 (file)
index 0000000..222ff5e
--- /dev/null
@@ -0,0 +1,327 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+// TYPE DEFINITIONS
+
+#ifndef MM_TYPES_H
+#define MM_TYPES_H
+
+typedef unsigned int   mm_word;        // 32 bits
+typedef unsigned short mm_hword;       // 16 bits
+typedef unsigned char  mm_byte;        // 8 bits
+
+typedef unsigned short mm_sfxhand;     // sound effect handle
+
+typedef unsigned char  mm_bool;        // boolean value
+
+typedef void*                  mm_addr;        // pointer
+typedef void*                  mm_reg;         // hardware register
+
+typedef enum
+{
+       MM_MODE_A,
+       MM_MODE_B,
+       MM_MODE_C
+} mm_mode_enum;
+
+typedef enum
+{
+       MM_STREAM_8BIT_MONO                                     = 0x0,  // 000b
+       MM_STREAM_8BIT_STEREO                           = 0x1,  // 001b
+       
+       MM_STREAM_16BIT_MONO                            = 0x2,  // 010b
+       MM_STREAM_16BIT_STEREO                          = 0x3,  // 011b
+       
+//     MM_STREAM_ADPCM_MONO                            = 0x4,  // 100b
+//     MM_STREAM_ADPCM_STEREO                          = 0x5,  // 101b
+
+//     adpcm streaming is not supported by the ds hardware
+//     (the loop point data gets recorded so ring buffers are not possible)
+
+//-----------------------------------------------------------------------------
+} mm_stream_formats;
+//-----------------------------------------------------------------------------
+
+typedef mm_word (*mm_callback)( mm_word msg, mm_word param );
+typedef mm_word (*mm_stream_func)( mm_word length, mm_addr dest, mm_stream_formats format );
+
+typedef enum
+{
+       MMRF_MEMORY             =0x01,
+       MMRF_DELAY              =0x02,
+       MMRF_RATE               =0x04,
+       MMRF_FEEDBACK   =0x08,
+       MMRF_PANNING    =0x10,
+       MMRF_LEFT               =0x20,
+       MMRF_RIGHT              =0x40,
+       MMRF_BOTH               =0x60,
+       
+       MMRF_INVERSEPAN =0x80,
+       MMRF_NODRYLEFT  =0x100,
+       MMRF_NODRYRIGHT =0x200,
+       MMRF_8BITLEFT   =0x400,
+       MMRF_16BITLEFT  =0x800,
+       MMRF_8BITRIGHT  =0x1000,
+       MMRF_16BITRIGHT =0x2000,
+       MMRF_DRYLEFT    =0x4000,
+       MMRF_DRYRIGHT   =0x8000
+//-----------------------------------------------------------------------------
+} mm_reverbflags;
+//-----------------------------------------------------------------------------
+
+typedef enum
+{
+       MMRC_LEFT = 1,
+       MMRC_RIGHT = 2,
+       MMRC_BOTH = 3
+//-----------------------------------------------------------------------------
+} mm_reverbch;
+//-----------------------------------------------------------------------------
+
+
+typedef struct mmreverbcfg
+{
+       mm_word                         flags;
+       mm_addr                         memory;
+       mm_hword                        delay;
+       mm_hword                        rate;
+       mm_hword                        feedback;
+       mm_byte                         panning;
+//-----------------------------------------------------------------------------
+} mm_reverb_cfg;
+//-----------------------------------------------------------------------------
+
+typedef enum
+{
+       MM_PLAY_LOOP,
+       MM_PLAY_ONCE
+//-----------------------------------------------------------------------------
+} mm_pmode;
+//-----------------------------------------------------------------------------
+
+typedef enum
+{
+       MM_MIX_8KHZ,
+       MM_MIX_10KHZ,
+       MM_MIX_13KHZ,
+       MM_MIX_16KHZ,
+       MM_MIX_18KHZ,
+       MM_MIX_21KHZ,
+       MM_MIX_27KHZ,
+       MM_MIX_31KHZ
+//-----------------------------------------------------------------------------
+} mm_mixmode;
+//-----------------------------------------------------------------------------
+
+typedef enum
+{
+       MM_TIMER0,      // hardware timer 0
+       MM_TIMER1,      // hardware timer 1
+       MM_TIMER2,      // hardware timer 2
+       MM_TIMER3       // hardware timer 3
+
+//-----------------------------------------------------------------------------
+} mm_stream_timer;
+//-----------------------------------------------------------------------------
+
+typedef struct t_mmdssample
+{
+       mm_word         loop_start;
+       union {
+       mm_word         loop_length;
+       mm_word         length;
+       };
+       mm_byte         format;
+       mm_byte         repeat_mode;
+       mm_hword        base_rate;
+       mm_addr         data;
+//-----------------------------------------------------------------------------
+} mm_ds_sample;
+//-----------------------------------------------------------------------------
+
+typedef struct t_mmsoundeffect
+{
+       union {
+// sample ID (defined in soundbank header)
+       mm_word id;
+       
+// external sample address, not valid on GBA system
+       mm_ds_sample* sample;
+       };
+       
+// playback rate
+       mm_hword rate;
+       
+// sound handle
+       mm_sfxhand handle;
+       
+// volume, 0..255
+       mm_byte volume;
+       
+// panning, 0..255
+       mm_byte panning;
+       
+//-----------------------------------------------------------------------------
+} mm_sound_effect;
+//-----------------------------------------------------------------------------
+
+typedef struct t_mmgbasystem 
+{
+       mm_mixmode      mixing_mode;
+       mm_word         mod_channel_count;
+       mm_word         mix_channel_count;
+       mm_addr         module_channels;
+       mm_addr         active_channels;
+       mm_addr         mixing_channels;
+       mm_addr         mixing_memory;
+       mm_addr         wave_memory;
+       mm_addr         soundbank;
+//-----------------------------------------------------------------------------
+} mm_gba_system;
+//-----------------------------------------------------------------------------
+
+typedef struct t_mmdssystem
+{
+
+// give MSL_NSONGS
+       mm_word         mod_count;
+
+// pass MSL_NSAMPS
+       mm_word         samp_count;
+       
+// pass pointer to memory buffer
+// (mm_word mem_bank[MSL_BANKSIZE])
+       mm_word*        mem_bank;
+       
+// fifo channel to use (usually 7)
+       mm_word         fifo_channel;
+
+//-----------------------------------------------------------------------------        
+} mm_ds_system;
+//-----------------------------------------------------------------------------
+
+typedef struct t_mmstream
+{      
+// sampling rate. 1024->32768 (HZ)
+       mm_word sampling_rate;
+       
+// number of samples to buffer
+       mm_word buffer_length;
+       
+// pointer to filling routine
+       mm_stream_func callback;
+
+// stream format (mm_stream_formats)
+       mm_word format;
+       
+// hardware timer selection (mm_stream_timers)
+       mm_word timer;
+
+// if set, user must call mmStreamUpdate manually
+       mm_bool manual;
+       
+//-----------------------------------------------------------------------------
+} mm_stream;
+//-----------------------------------------------------------------------------
+
+typedef struct t_mmlayer
+{
+
+// current tick count
+       mm_byte tick;
+
+// current row being played
+       mm_byte row;
+       
+// module sequence position
+       mm_byte position;
+       
+// number of rows in current pattern
+       mm_byte nrows;
+
+// global volume multiplier
+       mm_byte global_volume;
+
+// speed of module (ticks/row)
+       mm_byte speed;
+
+// module is active
+       mm_byte active;
+
+// tempo of module
+       mm_byte bpm;
+
+//-----------------------------------------------------------------------------
+} mm_modlayer;
+//-----------------------------------------------------------------------------
+
+typedef struct tmm_voice
+{
+
+// data source information
+       mm_addr         source;         // address to sample data
+       mm_word         length;         // length of sample data OR loop length (expressed in WORDS)
+       mm_hword        loop_start;     // loop start position (expressed in WORDS)
+
+// frequency divider
+       mm_hword        timer;
+
+// update flags
+       mm_byte         flags;
+
+// source format
+       mm_byte         format;         // 0: 8-bit
+                                                       // 1: 16-bit
+                                                       // 2: adpcm
+// repeat mode
+       mm_byte         repeat;         // 0: manual
+                                                       // 1: forward loop
+                                                       // 2: one shot
+
+// volume + divider setting
+       mm_byte         volume;         // 0->127
+       mm_byte         divider;        // 0->3 = /1, /2, /4, /16
+
+// panning setting
+       mm_byte         panning;        // 0->127
+
+// index of voice
+       mm_byte         index;          // 0->15
+
+// pad to 20 bytes
+       mm_byte         reserved[1];
+
+//-----------------------------------------------------------------------------
+} mm_voice;
+//-----------------------------------------------------------------------------
+
+enum {
+       MMVF_FREQ               =2,             // update frequency when this flag is set
+       MMVF_VOLUME             =4,             // update volume
+       MMVF_PANNING    =8,             // update panning
+       MMVF_SOURCE             =16,    // update source and start note
+       MMVF_STOP               =32             // stop voice (cut sound)
+};
+
+//-----------------------------------------------------------------------------
+
+#endif
diff --git a/libs/maxmod/mp_defs.inc b/libs/maxmod/mp_defs.inc
new file mode 100644 (file)
index 0000000..7716ef9
--- /dev/null
@@ -0,0 +1,117 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#ifndef MP_DEFS_INC
+#define MP_DEFS_INC
+
+@ song 'mode' can be one of the following:
+.equ           MPP_PLAY_LOOP   ,0
+.equ           MPP_PLAY_ONCE   ,1
+.equ           MPP_PLAY_JINGLE ,2
+
+@ other definitions
+#ifdef SYS_GBA
+
+.struct 0
+MM_GBA_SYSTEM_MODE:            .space 4
+MM_GBA_SYSTEM_MCH_COUNT:       .space 4
+MM_GBA_SYSTEM_ACH_COUNT:       .space 4
+MM_GBA_SYSTEM_MODCH:           .space 4
+MM_GBA_SYSTEM_ACTCH:           .space 4
+MM_GBA_SYSTEM_MIXCH:           .space 4
+MM_GBA_SYSTEM_MIXMEM:          .space 4
+MM_GBA_SYSTEM_WAVEMEM:         .space 4
+MM_GBA_SYSTEM_SOUNDBANK:       .space 4
+MM_GBA_SYSTEM_SIZE:
+
+.equ           SAMPFRAC,               12      @ # of bits used in fractional part of sample reading
+#endif
+
+.equ           MP_SCHANNELS    ,4
+
+@ callback parameters
+
+.equ   MMCB_SONGREQUEST        ,0x1A   @ nds9
+.equ   MMCB_SAMPREQUEST        ,0x1B   @ nds9
+.equ   MMCB_DELETESONG         ,0x1C   @ nds9
+.equ   MMCB_DELETESAMPLE       ,0x1D   @ nds9
+
+@.equ  MPCB_SAMPMEMORY         ,0x1E   @ ---
+@.equ  MPCB_SONGMEMORY         ,0x1F   @ ---
+.equ   MMCB_BANKMEMORY         ,0x1E   @ nds9
+
+.equ   MPCB_SONGMESSAGE        ,0x2A   @ gba/nds7 song playback
+.equ   MPCB_SONGFINISHED       ,0x2B   @ gba/nds7
+
+.struct 0
+mms_rate:      .space 4
+mms_len:       .space 4
+mms_function:  .space 4
+mms_format:    .space 4
+mms_timer:     .space 4
+mms_manual:    .space 1
+mms_size:
+
+.struct 0                              // reverb cfg struct
+mmrc_flags:            .space 4
+mmrc_memory:           .space 4
+mmrc_delay:            .space 2
+mmrc_rate:             .space 2
+mmrc_feedback:         .space 2
+mmrc_panning:          .space 1
+
+.equ   MMRFS_MEMORY,           0
+.equ   MMRFS_DELAY,            1
+.equ   MMRFS_RATE,             2
+.equ   MMRFS_FEEDBACK,         3
+.equ   MMRFS_PANNING,          4
+.equ   MMRFS_LEFT,             5
+.equ   MMRFS_RIGHT,            6
+.equ   MMRFS_INVERSEPAN,       7
+.equ   MMRFS_NODRYLEFT,        8
+.equ   MMRFS_NODRYRIGHT,       9
+.equ   MMRFS_8BITLEFT,         10
+.equ   MMRFS_16BITLEFT,        11
+.equ   MMRFS_8BITRIGHT,        12
+.equ   MMRFS_16BITRIGHT,       13
+.equ   MMRFS_DRYLEFT,          14
+.equ   MMRFS_DRYRIGHT,         15
+
+// reverbcfg flags
+.equ   MMRF_MEMORY,    1<<MMRFS_MEMORY
+.equ   MMRF_DELAY,     1<<MMRFS_DELAY
+.equ   MMRF_RATE,      1<<MMRFS_RATE
+.equ   MMRF_FEEDBACK,  1<<MMRFS_FEEDBACK
+.equ   MMRF_PANNING,   1<<MMRFS_PANNING
+.equ   MMRF_LEFT,      1<<MMRFS_LEFT
+.equ   MMRF_RIGHT,     1<<MMRFS_RIGHT
+.equ   MMRF_INVERSEPAN,1<<MMRFS_INVERSEPAN
+.equ   MMRF_NODRYLEFT, 1<<MMRFS_DRYLEFT
+.equ   MMRF_NODRYRIGHT,1<<MMRFS_DRYRIGHT
+.equ   MMRF_8BITLEFT,  1<<MMRFS_8BITLEFT
+.equ   MMRF_16BITLEFT, 1<<MMRFS_16BITLEFT
+.equ   MMRF_8BITRIGHT, 1<<MMRFS_8BITRIGHT
+.equ   MMRF_16BITRIGHT,1<<MMRFS_16BITRIGHT
+.equ   MMRF_DRYLEFT,   1<<MMRFS_DRYLEFT
+.equ   MMRF_DRYRIGHT,  1<<MMRFS_DRYRIGHT
+
+#endif
diff --git a/libs/maxmod/mp_format_mas.inc b/libs/maxmod/mp_format_mas.inc
new file mode 100644 (file)
index 0000000..750c2bd
--- /dev/null
@@ -0,0 +1,108 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+.ifndef MP_FORMAT_MAS_INC
+.equ   MP_FORMAT_MAS_INC, 1
+
+@ MAS header structure..................................
+.equ   C_MAS_LEN       ,0
+.equ   C_MAS_INSTN     ,1
+.equ   C_MAS_SAMPN     ,2      
+.equ   C_MAS_PATTN     ,3
+.equ   C_MAS_FLAGS     ,4
+.equ   C_MAS_GV        ,5
+.equ   C_MAS_SPEED     ,6
+.equ   C_MAS_TEMPO     ,7
+.equ   C_MAS_REP       ,8
+
+.equ   C_MAS_CHANVOL   ,12
+.equ   C_MAS_CHANPAN   ,44
+.equ   C_MAS_ORDER     ,76
+.equ   C_MAS_TABLES    ,276
+
+.equ   C_FLAGS_GS      ,1
+.equ   C_FLAGS_OS      ,2
+.equ   C_FLAGS_SS      ,3
+.equ   C_FLAGS_XS      ,4
+.equ   C_FLAGS_DS      ,5
+///////.equ    C_FLAGS_LS      ,5              HUH???
+.equ   C_FLAGS_LS      ,6
+
+.equ   C_FLAGS_X,      (1<<3)
+
+@ instrument struct.....................................
+.equ   C_MASI_GVOL     ,0
+.equ   C_MASI_FADE     ,1
+.equ   C_MASI_RANDVOL  ,2
+.equ   C_MASI_DCT      ,3
+.equ   C_MASI_NNA      ,4
+.equ   C_MASI_ENVFLAGS ,5
+.equ   C_MASI_PAN      ,6
+.equ   C_MASI_DCA      ,7
+.equ   C_MASI_MAP      ,8
+.equ   C_MASI_ENVELOPES,12
+//.equ C_MASI_ENVELOPES,248
+
+.equ   C_MASIE_SIZE    ,0
+.equ   C_MASIE_LSTART  ,1
+.equ   C_MASIE_LEND    ,2
+.equ   C_MASIE_SSTART  ,3
+.equ   C_MASIE_SEND    ,4
+.equ   C_MASIE_NODEC   ,5
+.equ   C_MASIE_FILTER  ,6
+.equ   C_MASIE_NODES   ,8
+
+.EQU   ENVFLAG_A,      0b1000
+
+@ sample structure......................................
+.equ   C_MASS_DV       ,0
+.equ   C_MASS_PAN      ,1
+.equ   C_MASS_FREQ     ,2
+.equ   C_MASS_VIT      ,4
+.equ   C_MASS_VID      ,5
+.equ   C_MASS_VIS      ,6
+.equ   C_MASS_VIR      ,8
+.equ   C_MASS_GV       ,7
+
+.equ   C_MASS_MSLID    ,10
+
+@ pattern structure.....................................
+.equ   C_MASP_NROWS    ,0
+.equ   C_MASP_DATA     ,1
+
+@ sample structure......................................
+.equ   C_SAMPLE_LEN    ,0
+.equ   C_SAMPLE_LOOP   ,4
+.equ   C_SAMPLE_POINT  ,12
+.equ   C_SAMPLE_DATA   ,16
+
+.equ   C_SAMPLEN_LSTART,0
+.equ   C_SAMPLEN_LEN   ,4
+.equ   C_SAMPLEN_FORMAT,8
+.equ   C_SAMPLEN_REP   ,9
+.equ   C_SAMPLEN_POINT ,12
+.equ   C_SAMPLEN_DATA  ,16
+
+.equ   C_SAMPLEC_DFREQ ,10
+
+
+.endif
diff --git a/libs/maxmod/mp_macros.inc b/libs/maxmod/mp_macros.inc
new file mode 100644 (file)
index 0000000..e178107
--- /dev/null
@@ -0,0 +1,195 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *                       MACROS TO MAKE LIFE EASY                           *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#ifdef SYS_NDS9
+
+.macro ret0            @ arm9 pop {pc} has mode change
+       pop     {pc}
+.endm
+.macro ret1            @ arm9 pop {pc} has mode change
+       pop     {pc}
+.endm
+.macro ret2            @ arm9 pop {pc} has mode change
+       pop     {pc}
+.endm
+.macro ret3            @ arm9 pop {pc} has mode change
+       pop     {pc}
+.endm
+
+.macro jump7
+       blx     r7
+.endm
+
+
+.macro jump2
+       blx     r2
+.endm
+
+.macro jump1
+       blx     r1
+.endm
+
+.macro fjump2 function
+       ldr     r2,=function
+       blx     r2
+.endm
+
+#else
+
+.macro ret0            @ arm7 requires pop/bx combo
+       pop     {r0}
+       bx      r0
+.endm
+.macro ret1
+       pop     {r1}
+       bx      r1
+.endm
+.macro ret2
+       pop     {r2}
+       bx      r2
+.endm
+.macro ret3
+       pop     {r3}
+       bx      r3
+.endm
+
+
+.macro jump7
+#ifdef USE_IWRAM
+       bl      mpp_call_r7i
+#else
+       bl      mpp_call_r7
+#endif
+.endm
+
+.macro jump2
+#ifdef USE_IWRAM
+       bl      mpp_call_r2i
+#else
+       bl      mpp_call_r2
+#endif
+.endm
+
+.macro fjump2 function
+       ldr     r2,=\function
+       jump2
+.endm
+
+.macro jump1
+#ifdef USE_IWRAM
+       bl      mpp_call_r1i
+#else
+       bl      mpp_call_r1
+#endif
+.endm
+
+#endif
+
+.macro break9
+9: b 9b
+.endm
+
+.macro breakp
+mov    r11,r11
+.endm
+
+.macro GET_MIXCH reg
+#ifdef SYS_NDS
+       ldr     \reg,=mm_mix_channels
+#endif
+#ifdef SYS_GBA
+       ldr     \reg,=mm_mixchannels
+       ldr     \reg, [\reg]
+#endif
+.endm
+
+//#define ENABLE_PROFILE
+
+.macro PROF_START mode
+
+#ifdef ENABLE_PROFILE
+
+       push    {r0-r3}
+       push    {lr}
+       ldr     r1,=profile_start
+       mov     r2, pc
+       add     r2, #5
+       mov     lr, r2
+       bx      r1
+       pop     {r0}
+       mov     lr, r0
+       pop     {r0-r3}
+#endif 
+
+.endm
+
+.macro PROF_END mode
+
+#ifdef ENABLE_PROFILE
+       push    {r0-r3}
+       push    {lr}
+       mov     r0, #\mode
+       ldr     r1,=profile_take
+       mov     r2, pc
+       add     r2, #5
+       mov     lr, r2
+       bx      r1
+       pop     {r0}
+       mov     lr, r0
+       pop     {r0-r3}
+       
+#endif
+
+.endm
+
+.macro __SECTION_IWRAM
+#ifdef SYS_GBA
+       .section ".iwram", "ax", %progbits
+#else
+       .text
+#endif
+.endm
+
+//------------------------------
+.macro nocash_msg msg
+//------------------------------
+       mov     r12,r12
+       b       9f
+       .hword  0x6464
+       .hword  0
+       .string "\msg"
+       .align 2
+9:
+.endm
+//------------------------------
+.macro nocash_reset_clks
+//------------------------------
+       nocash_msg %zeroclks%
+.endm
+//------------------------------
+.macro nocash_print_clks
+//------------------------------
+       nocash_msg %lastclks%
+.endm
+//------------------------------
diff --git a/libs/maxmod/mp_mas.inc b/libs/maxmod/mp_mas.inc
new file mode 100644 (file)
index 0000000..9f63b13
--- /dev/null
@@ -0,0 +1,40 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#ifndef        MP_MAS_INC
+#define        MP_MAS_INC
+
+@#ifdef SYS_NDS
+@.equ  MP_MCHANNELS    ,16
+@#else
+@.equ  MP_MCHANNELS    ,32
+@#endif
+
+.extern mpp_vars
+.extern mpp_pattread
+.extern mpp_sm_handle
+
+.extern mpp_resetchannels
+.extern mpp_PlaySong
+.extern mpp_processtick
+
+#endif
diff --git a/libs/maxmod/mp_mas_structs.inc b/libs/maxmod/mp_mas_structs.inc
new file mode 100644 (file)
index 0000000..539ea17
--- /dev/null
@@ -0,0 +1,189 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+@ Layer Information
+@ -----------------
+
+.equ   MPL_TICK        ,0
+.equ   MPL_ROW         ,1
+.equ   MPL_POSITION    ,2
+.equ   MPL_NROWS       ,3
+.equ   MPL_GV          ,4
+.equ   MPL_SPEED       ,5
+.equ   MPL_ISPLAYING   ,6
+.equ   MPL_BPM         ,7
+.equ   MPL_INSTTABLE   ,8
+.equ   MPL_SAMPTABLE   ,12
+.equ   MPL_PATTTABLE   ,16
+.equ   MPL_SONGADR     ,20
+.equ   MPL_FLAGS       ,24
+.equ   MPL_OLDEFFECTS  ,25
+.equ   MPL_PATTJUMP    ,26
+.equ   MPL_PATTJUMP_ROW,27
+.equ   MPL_FPATTDELAY  ,28
+.equ   MPL_PATTDELAY   ,29
+
+.equ   MPL_PLOOP_ROW   ,30
+.equ   MPL_PLOOP_TIMES ,31
+.equ   MPL_PLOOP_ADR   ,32
+.equ   MPL_PATTREAD    ,36
+.equ   MPL_PLOOP_JUMP  ,40
+.equ   MPL_VALID               ,41
+
+.equ   MPL_TICKRATE    ,42     @ 1.15 fixed point OR sample count
+.equ   MPL_SAMPCOUNT   ,44     @ sample timing
+.equ   MPL_TICKFRAC    ,44     @ vsync  timing 0.16 fixed point
+
+.equ   MPL_MODE        ,46
+.equ   mpl_reserved2   ,47
+.equ   MPL_MCH_UPDATE  ,48
+.equ   MPL_VOLUME      ,52
+.equ   mpl_reserved3   ,54
+.equ   MPL_SIZE        ,56
+
+@ active information
+@--------------------
+
+.equ   MPV_PATTREAD_P  ,4
+.equ   MPV_AFVOL       ,8
+.equ   MPV_SAMPOFF     ,9
+.equ   MPV_VOLPLUS     ,10
+.equ   MPV_NOTEDELAY   ,11
+.equ   MPV_PANPLUS     ,12
+.equ   MPV_SIZE        ,14
+
+@ Module Channel
+@ --------------
+
+.EQU   MCH_ALLOC,      0 @ ALLOCATED ACTIVE CHANNEL#
+.EQU   MCH_CFLAGS,     1 @ PATTERN COMRESSION FLAGS, called "maskvariable" in ITTECH.TXT
+.EQU   MCH_PANNING,    2
+.EQU   MCH_VOLCMD,     3 @ VOLUME COLUMN COMMAND
+.EQU   MCH_EFFECT,     4 @ EFFECT#                             } COMBINED
+.EQU   MCH_PARAM,      5 @ EFFECT PARAMETER    }
+.EQU   MCH_FXMEM,      6 @ EFFECT MEMORY
+.EQU   MCH_NOTE,       7 @ TRANSLATED NOTE
+.EQU   MCH_FLAGS,      8 @ CHANNEL FLAGS
+.EQU   MCH_INST,       9 @ INSTRUMENT#
+//.EQU MCH_PFLAGS,     10 @ PLAYBACK FLAGS (???)
+.EQU   MCH_VIBDEP,     11
+.EQU   MCH_VIBSPD,     12
+.EQU   MCH_VIBPOS,     13
+.EQU   MCH_VOLUME,     14 //}combined
+.EQU   MCH_CVOLUME,    15 //}
+.EQU   MCH_PERIOD,     16
+.EQU   MCH_BFLAGS,     20
+.EQU   MCH_PNOTE,      22 @ PATTERN NOTE
+.EQU   MCH_MEMORY,     23 @ 15 bytes
+.EQU   MCH_SIZE,       40 @ sizeof( module_channel ) @ 2 extra bytes
+
+@ Active Channel
+@ --------------
+
+.EQU   MCA_PERIOD,     0       @ internal period
+.EQU   MCA_FADE,       4       //   }
+.EQU   MCA_ENVC_VOL,   6       //   }
+.EQU   MCA_ENVC_PAN,   8       //   } COMBINED
+.EQU   MCA_ENVC_PIC,   10      //   } during volume reset
+.EQU   MCA_AVIB_DEP,   12      //   } AND NEWNOTE, CHECK NEWNOTE ON CHANGES
+.EQU   MCA_AVIB_POS,   14      //   }
+.EQU   MCA_FVOL,       16      // } COMBINED for SPEED
+.EQU   MCA_TYPE,       17      // }
+.EQU   MCA_INST,       18
+.EQU   MCA_PANNING,    19
+.EQU   MCA_VOLUME,     20
+.EQU   MCA_SAMPLE,     21
+.EQU   MCA_PARENT,     22 // } COMBINED
+.EQU   MCA_FLAGS,      23 // }
+.EQU   MCA_ENVN_VOL,   24
+.EQU   MCA_ENVN_PAN,   25
+.EQU   MCA_ENVN_PIC,   26
+.EQU   MCA_SFX,        27      @ can store this anywhere
+.EQU   MCA_SIZE,       28
+
+@ Active Channel Flags
+@ --------------------
+
+.EQU   MCAF_KEYON      ,1      @ key is on... LOCKED
+.EQU   MCAF_FADE       ,2      @ note-fade is activated
+.EQU   MCAF_START      ,4      @ [re]start sample
+.EQU   MCAF_UPDATED    ,8      @ already updated by pchannel routine
+.EQU   MCAF_ENVEND     ,16     @ end of envelope
+.EQU   MCAF_VOLENV     ,32     @ volume envelope enabled
+.EQU   MCAF_SUB        ,64     @ sublayer.................locked..
+.EQU   MCAF_EFFECT     ,128    @ subsublayer.............................LOCKED (mpEffect)
+
+@ Active Channel Types
+@ --------------------
+
+.EQU   ACHN_DISABLED   ,0      @ LOCKED (multiple routines)
+.EQU   ACHN_RESERVED   ,1      @ (can't be used [alloc channel])
+.EQU   ACHN_BACKGROUND ,2      @ LOCKED (alloc channel)
+.EQU   ACHN_FOREGROUND ,3
+.EQU   ACHN_CUSTOM     ,4
+
+@ Module Channel Flags
+@ --------------------
+
+.equ   MF_START        ,1
+.equ   MF_DVOL         ,2
+.equ   MF_HASVCMD      ,4
+.equ   MF_HASFX        ,8
+.equ   MF_NEWINSTR     ,16
+
+.EQU   MF_NOTEOFF      ,64     @ LOCKED
+.EQU   MF_NOTECUT      ,128    @ LOCKED
+
+@.equ  MF_NEWINSTR     ,1      @ new instrument
+@.EQU  MF_DVOL         ,2
+@.EQU  MF_START        ,4
+@.EQU  MF_HASFX        ,8
+@.EQU  MF_HASVCMD      ,16
+@.EQU  MF_NOTEOFF      ,64     @ LOCKED
+@.EQU  MF_NOTECUT      ,128    @ LOCKED
+
+@ Other Definitions
+@ -----------------
+
+.equ   IT_NNA_CUT      ,0      @ New note actions
+.equ   IT_NNA_CONT     ,1
+.equ   IT_NNA_OFF      ,2
+.equ   IT_NNA_FADE     ,3
+
+.equ   IT_DCA_CUT      ,0      @ Duplicate check actions
+.equ   IT_DCA_OFF      ,1
+.equ   IT_DCA_FADE     ,2
+
+@ Misc Reference
+@ --------------
+
+@ BFLAGS:
+@ /////ctv nnppttvv
+@ nn...............new note action
+@ pp...............panbrello waveform
+@ tt...............tremolo waveform
+@ vv...............vibrato waveform
+@ dd...............duplicate check type
+@ v................volume envelope enabled
+@ t................tremor variable...
+@ c................cut channel volume
+@ //////...........reserved
diff --git a/libs/maxmod/mp_mixer_gba.inc b/libs/maxmod/mp_mixer_gba.inc
new file mode 100644 (file)
index 0000000..4ba54fe
--- /dev/null
@@ -0,0 +1,42 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+#ifndef MP_MIXER_GBA_INC
+#define MP_MIXER_GBA_INC
+
+@ definitions
+
+.equ   MP_SAMPFRAC             ,12
+
+.equ   MIXER_CHN_SRC           ,0
+.equ   MIXER_CHN_READ          ,4
+.equ   MIXER_CHN_VOL           ,8      
+.equ   MIXER_CHN_PAN           ,9
+// 10
+// 11
+.equ   MIXER_CHN_FREQ          ,12
+.equ   MIXER_CHN_SIZE          ,16
+
+//////////////////////////////////.equ MIXER_CHN_LOOP          ,8
+/////////////////////////////////////.equ      MIXER_CHN_LEN           ,16
+
+#endif
diff --git a/libs/maxmod/swi_gba.inc b/libs/maxmod/swi_gba.inc
new file mode 100644 (file)
index 0000000..53d7737
--- /dev/null
@@ -0,0 +1,23 @@
+/****************************************************************************
+ *                                                          __              *
+ *                ____ ___  ____ __  ______ ___  ____  ____/ /              *
+ *               / __ `__ \/ __ `/ |/ / __ `__ \/ __ \/ __  /               *
+ *              / / / / / / /_/ />  </ / / / / / /_/ / /_/ /                *
+ *             /_/ /_/ /_/\__,_/_/|_/_/ /_/ /_/\____/\__,_/                 *
+ *                                                                          *
+ *         Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org)         *
+ *                                                                          *
+ * Permission to use, copy, modify, and/or distribute this software for any *
+ * purpose with or without fee is hereby granted, provided that the above   *
+ * copyright notice and this permission notice appear in all copies.        *
+ *                                                                          *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
+ ****************************************************************************/
+
+.equ   SWI_DIVIDE,     0x06
diff --git a/src/asmutil.s b/src/asmutil.s
new file mode 100644 (file)
index 0000000..b88a7a1
--- /dev/null
@@ -0,0 +1,38 @@
+       .text
+       .thumb
+
+       .globl fillblock_16byte
+fillblock_16byte:
+       push {r4-r6}
+       mov r3, r1
+       mov r4, r1
+       mov r5, r1
+       mov r6, r1
+0:     stmia r0!, {r3, r4, r5, r6}
+       sub r2, #1
+       bne 0b
+       pop {r4-r6}
+       bx lr
+
+       .globl get_pc
+get_pc:
+       mov r0, lr
+       bx lr
+
+       .globl get_sp
+get_sp:
+       mov r0, sp
+       bx lr
+
+       .arm
+       .extern panic_regs
+       .globl get_panic_regs
+       .type get_panic_regs, %function
+get_panic_regs:
+       stmfd sp!, {sp, lr}
+       ldr lr, =panic_regs
+       stm lr, {r0-r15}
+       ldmfd sp!, {r0, lr}
+       ldr r0, =panic_regs + 13 * 4
+       stm r0, {sp, lr}
+       bx lr
diff --git a/src/debug.c b/src/debug.c
new file mode 100644 (file)
index 0000000..e983ff6
--- /dev/null
@@ -0,0 +1,154 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include "gbaregs.h"
+#include "intr.h"
+#include "debug.h"
+#include "util.h"
+
+uint16_t vblperf_color[] = {
+       /* grn  blue   cyan  yellow  orng    red     purple  d.green purple ... */
+       /* 60    30     20     15     12      10      8.5     7.5    ... */
+       0x3e0, 0xf863, 0xffc0, 0x3ff, 0x1ff, 0x001f, 0xf81f, 0x1e0, 0xf81f, 0xf81f, 0xf81f
+};
+
+void vblperf_setcolor(int palidx)
+{
+       vblperf_palptr = (uint16_t*)CRAM_BG_ADDR + palidx;
+}
+
+
+uint32_t panic_regs[16];
+void get_panic_regs(void);
+
+void panic(void *pc, const char *fmt, ...)
+{
+       int y;
+       va_list ap;
+       uint32_t *reg;
+
+       get_panic_regs();
+
+       intr_disable();
+       REG_DISPCNT = 4 | DISPCNT_BG2;
+
+       set_bg_color(0, 31, 0, 0);
+       set_bg_color(0xff, 31, 31, 31);
+
+       fillblock_16byte((void*)VRAM_LFB_FB0_ADDR, 0, 240 * 160 / 16);
+
+       fillblock_16byte((unsigned char*)VRAM_LFB_FB0_ADDR + 240 * 3, 0xffffffff, 240 / 16);
+       dbg_drawstr(44, 0, " Panic at %p ", pc);
+
+       va_start(ap, fmt);
+       y = dbg_vdrawstr(0, 12, fmt, ap) + 8;
+       va_end(ap);
+
+       fillblock_16byte((unsigned char*)VRAM_LFB_FB0_ADDR + 240 * (y + 4), 0xffffffff, 240 / 16);
+       y += 8;
+
+       reg = panic_regs;
+       y = dbg_drawstr(0, y, " r0 %08x  r1 %08x\n r2 %08x  r3 %08x\n r4 %08x  r5 %08x\n r6 %08x  r7 %08x\n",
+                       reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7]);
+       y = dbg_drawstr(0, y, " r8 %08x  r9 %08x\nr10 %08x r11 %08x\n ip %08x  sp %08x\n lr %08x  pc %08x\n",
+                       reg[8], reg[9], reg[10], reg[11], reg[12], reg[13], reg[14], reg[15]);
+
+       /* stop any sound/music playback */
+       REG_SOUNDCNT_H = SCNT_DSA_CLRFIFO | SCNT_DSB_CLRFIFO;
+       REG_TMCNT_H(1) &= ~TMCNT_EN;
+       REG_DMA1CNT_H = 0;
+       REG_DMA2CNT_H = 0;
+
+       for(;;);
+}
+
+void dbg_drawglyph(int x, int y, int c)
+{
+       int i;
+       uint16_t pp;
+       unsigned char row;
+       uint16_t *ptr = (uint16_t*)VRAM_LFB_FB0_ADDR + (y << 7) - (y << 3) + (x >> 1);
+       unsigned char *fnt = font_8x8 + ((c & 0xff) << 3);
+
+       for(i=0; i<8; i++) {
+               row = *fnt++;
+               pp = row & 0x80 ? 0xff : 0;
+               *ptr++ = pp | (row & 0x40 ? 0xff00 : 0);
+               pp = row & 0x20 ? 0xff : 0;
+               *ptr++ = pp | (row & 0x10 ? 0xff00 : 0);
+               pp = row & 0x08 ? 0xff : 0;
+               *ptr++ = pp | (row & 0x04 ? 0xff00 : 0);
+               pp = row & 0x02 ? 0xff : 0;
+               *ptr++ = pp | (row & 0x01 ? 0xff00 : 0);
+               ptr += 120 - 4;
+       }
+}
+
+int dbg_vdrawstr(int x, int y, const char *fmt, va_list ap)
+{
+       int startx, c;
+       char buf[128];
+       char *ptr = buf;
+
+       vsnprintf(buf, sizeof buf, fmt, ap);
+
+       startx = x;
+       while(*ptr) {
+               if(y >= 160) break;
+
+               c = *ptr++;
+               switch(c) {
+               case '\n':
+                       y += 8;
+               case '\r':
+                       x = startx;
+                       break;
+
+               default:
+                       dbg_drawglyph(x, y, c);
+                       x += 8;
+                       if(x >= 240 - 8) {
+                               while(*ptr && isspace(*ptr)) ptr++;
+                               x = 0;
+                               y += 8;
+                       }
+               }
+       }
+
+       return y;
+}
+
+int dbg_drawstr(int x, int y, const char *fmt, ...)
+{
+       int res;
+       va_list ap;
+
+       va_start(ap, fmt);
+       res = dbg_vdrawstr(x, y, fmt, ap);
+       va_end(ap);
+       return res;
+}
+
+#ifdef EMUBUILD
+__attribute__((target("arm")))
+void emuprint(const char *fmt, ...)
+{
+       char buf[128];
+       va_list ap;
+
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof buf, fmt, ap);
+       va_end(ap);
+
+       asm volatile(
+               "mov r0, %0\n\t"
+               "swi 0xff0000\n\t" :
+               : "r" (buf)
+               : "r0"
+       );
+}
+#else
+void emuprint(const char *fmt, ...)
+{
+}
+#endif
diff --git a/src/debug.h b/src/debug.h
new file mode 100644 (file)
index 0000000..921fd12
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef DEBUG_H_
+#define DEBUG_H_
+
+#include <stdarg.h>
+#include "util.h"
+
+extern unsigned char font_8x8[];
+
+extern uint16_t vblperf_color[];
+uint16_t *vblperf_palptr;
+volatile int vblperf_count;
+
+void vblperf_setcolor(int palidx);
+
+#ifdef VBLBAR
+#define vblperf_begin()        \
+       do { \
+               *vblperf_palptr = 0; \
+               vblperf_count = 0; \
+       } while(0)
+
+#define vblperf_end() \
+       do { \
+               *vblperf_palptr = vblperf_color[vblperf_count]; \
+       } while(0)
+#else
+#define vblperf_begin()
+#define vblperf_end()
+#endif
+
+void panic(void *pc, const char *fmt, ...) __attribute__((noreturn));
+
+void dbg_drawglyph(int x, int y, int c);
+int dbg_drawstr(int x, int y, const char *fmt, ...);
+int dbg_vdrawstr(int x, int y, const char *fmt, va_list ap);
+
+void emuprint(const char *fmt, ...);
+
+#endif /* DEBUG_H_ */
diff --git a/src/dma.c b/src/dma.c
new file mode 100644 (file)
index 0000000..ba23ab7
--- /dev/null
+++ b/src/dma.c
@@ -0,0 +1,62 @@
+#include "dma.h"
+
+/* DMA Options */
+#define DMA_ENABLE                             0x80000000
+#define DMA_INT_ENABLE                 0x40000000
+#define DMA_TIMING_IMMED               0x00000000
+#define DMA_TIMING_VBLANK              0x10000000
+#define DMA_TIMING_HBLANK              0x20000000
+#define DMA_TIMING_DISPSYNC            0x30000000
+#define DMA_16                                 0x00000000
+#define DMA_32                                 0x04000000
+#define DMA_REPEAT                             0x02000000
+#define DMA_SRC_INC                            0x00000000
+#define DMA_SRC_DEC                            0x00800000
+#define DMA_SRC_FIX                            0x01000000
+#define DMA_DST_INC                            0x00000000
+#define DMA_DST_DEC                            0x00200000
+#define DMA_DST_FIX1                   0x00400000
+#define DMA_DST_RELOAD                 0x00600000
+
+/* DMA Register Parts */
+#define DMA_SRC                0
+#define DMA_DST                1
+#define DMA_CTRL       2
+
+static volatile uint32_t *reg_dma[4] = {(void*)0x040000b0, (void*)0x040000bc, (void*)0x040000c8, (void*)0x040000d4 };
+
+/* --- perform a copy of words or halfwords using DMA --- */
+
+void dma_copy32(int channel, void *dst, void *src, int words, unsigned int flags)
+{
+       reg_dma[channel][DMA_SRC] = (uint32_t)src;
+       reg_dma[channel][DMA_DST] = (uint32_t)dst;
+       reg_dma[channel][DMA_CTRL] = words | flags | DMA_32 | DMA_ENABLE;
+}
+
+void dma_copy16(int channel, void *dst, void *src, int halfwords, unsigned int flags)
+{
+       reg_dma[channel][DMA_SRC] = (uint32_t)src;
+       reg_dma[channel][DMA_DST] = (uint32_t)dst;
+       reg_dma[channel][DMA_CTRL] = halfwords | flags | DMA_16 | DMA_ENABLE;
+}
+
+/* --- fill a buffer with an ammount of words and halfwords using DMA --- */
+
+static uint32_t fill[4];
+
+void dma_fill32(int channel, void *dst, uint32_t val, int words)
+{
+       fill[channel] = val;
+       reg_dma[channel][DMA_SRC] = (uint32_t)(fill + channel);
+       reg_dma[channel][DMA_DST] = (uint32_t)dst;
+       reg_dma[channel][DMA_CTRL] = words | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_32 | DMA_ENABLE;
+}
+
+void dma_fill16(int channel, void *dst, uint16_t val, int halfwords)
+{
+       fill[channel] = val;
+       reg_dma[channel][DMA_SRC] = (uint32_t)(fill + channel);
+       reg_dma[channel][DMA_DST] = (uint32_t)dst;
+       reg_dma[channel][DMA_CTRL] = halfwords | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_16 | DMA_ENABLE;
+}
diff --git a/src/dma.h b/src/dma.h
new file mode 100644 (file)
index 0000000..7ff7606
--- /dev/null
+++ b/src/dma.h
@@ -0,0 +1,12 @@
+#ifndef DMA_H_
+#define DMA_H_
+
+#include <stdint.h>
+
+void dma_copy32(int channel, void *dst, void *src, int words, unsigned int flags);
+void dma_copy16(int channel, void *dst, void *src, int halfwords, unsigned int flags);
+
+void dma_fill32(int channel, void *dst, uint32_t val, int words);
+void dma_fill16(int channel, void *dst, uint16_t val, int halfwords);
+
+#endif /* DMA_H_ */
diff --git a/src/font8x8.c b/src/font8x8.c
new file mode 100644 (file)
index 0000000..365044f
--- /dev/null
@@ -0,0 +1,2565 @@
+/* Font data derived from Linux 2.6.7: drivers/video/console/font_8x8.c */
+
+unsigned char font_8x8[] = {
+
+       /* 0 0x00 '^@' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 1 0x01 '^A' */
+       0x7e, /* 01111110 */
+       0x81, /* 10000001 */
+       0xa5, /* 10100101 */
+       0x81, /* 10000001 */
+       0xbd, /* 10111101 */
+       0x99, /* 10011001 */
+       0x81, /* 10000001 */
+       0x7e, /* 01111110 */
+
+       /* 2 0x02 '^B' */
+       0x7e, /* 01111110 */
+       0xff, /* 11111111 */
+       0xdb, /* 11011011 */
+       0xff, /* 11111111 */
+       0xc3, /* 11000011 */
+       0xe7, /* 11100111 */
+       0xff, /* 11111111 */
+       0x7e, /* 01111110 */
+
+       /* 3 0x03 '^C' */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+
+       /* 4 0x04 '^D' */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+
+       /* 5 0x05 '^E' */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xd6, /* 11010110 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+
+       /* 6 0x06 '^F' */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+
+       /* 7 0x07 '^G' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 8 0x08 '^H' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xe7, /* 11100111 */
+       0xc3, /* 11000011 */
+       0xc3, /* 11000011 */
+       0xe7, /* 11100111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 9 0x09 '^I' */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x42, /* 01000010 */
+       0x42, /* 01000010 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 10 0x0a '^J' */
+       0xff, /* 11111111 */
+       0xc3, /* 11000011 */
+       0x99, /* 10011001 */
+       0xbd, /* 10111101 */
+       0xbd, /* 10111101 */
+       0x99, /* 10011001 */
+       0xc3, /* 11000011 */
+       0xff, /* 11111111 */
+
+       /* 11 0x0b '^K' */
+       0x0f, /* 00001111 */
+       0x07, /* 00000111 */
+       0x0f, /* 00001111 */
+       0x7d, /* 01111101 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x78, /* 01111000 */
+
+       /* 12 0x0c '^L' */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+
+       /* 13 0x0d '^M' */
+       0x3f, /* 00111111 */
+       0x33, /* 00110011 */
+       0x3f, /* 00111111 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x70, /* 01110000 */
+       0xf0, /* 11110000 */
+       0xe0, /* 11100000 */
+
+       /* 14 0x0e '^N' */
+       0x7f, /* 01111111 */
+       0x63, /* 01100011 */
+       0x7f, /* 01111111 */
+       0x63, /* 01100011 */
+       0x63, /* 01100011 */
+       0x67, /* 01100111 */
+       0xe6, /* 11100110 */
+       0xc0, /* 11000000 */
+
+       /* 15 0x0f '^O' */
+       0x18, /* 00011000 */
+       0xdb, /* 11011011 */
+       0x3c, /* 00111100 */
+       0xe7, /* 11100111 */
+       0xe7, /* 11100111 */
+       0x3c, /* 00111100 */
+       0xdb, /* 11011011 */
+       0x18, /* 00011000 */
+
+       /* 16 0x10 '^P' */
+       0x80, /* 10000000 */
+       0xe0, /* 11100000 */
+       0xf8, /* 11111000 */
+       0xfe, /* 11111110 */
+       0xf8, /* 11111000 */
+       0xe0, /* 11100000 */
+       0x80, /* 10000000 */
+       0x00, /* 00000000 */
+
+       /* 17 0x11 '^Q' */
+       0x02, /* 00000010 */
+       0x0e, /* 00001110 */
+       0x3e, /* 00111110 */
+       0xfe, /* 11111110 */
+       0x3e, /* 00111110 */
+       0x0e, /* 00001110 */
+       0x02, /* 00000010 */
+       0x00, /* 00000000 */
+
+       /* 18 0x12 '^R' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+
+       /* 19 0x13 '^S' */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+
+       /* 20 0x14 '^T' */
+       0x7f, /* 01111111 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0x7b, /* 01111011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x00, /* 00000000 */
+
+       /* 21 0x15 '^U' */
+       0x3e, /* 00111110 */
+       0x61, /* 01100001 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x86, /* 10000110 */
+       0x7c, /* 01111100 */
+
+       /* 22 0x16 '^V' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 23 0x17 '^W' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+
+       /* 24 0x18 '^X' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 25 0x19 '^Y' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 26 0x1a '^Z' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0xfe, /* 11111110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 27 0x1b '^[' */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xfe, /* 11111110 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 28 0x1c '^\' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 29 0x1d '^]' */
+       0x00, /* 00000000 */
+       0x24, /* 00100100 */
+       0x66, /* 01100110 */
+       0xff, /* 11111111 */
+       0x66, /* 01100110 */
+       0x24, /* 00100100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 30 0x1e '^^' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 31 0x1f '^_' */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 32 0x20 ' ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 33 0x21 '!' */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 34 0x22 '"' */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x24, /* 00100100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 35 0x23 '#' */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+
+       /* 36 0x24 '$' */
+       0x18, /* 00011000 */
+       0x3e, /* 00111110 */
+       0x60, /* 01100000 */
+       0x3c, /* 00111100 */
+       0x06, /* 00000110 */
+       0x7c, /* 01111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 37 0x25 '%' */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xcc, /* 11001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x66, /* 01100110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 38 0x26 '&' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 39 0x27 ''' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 40 0x28 '(' */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x00, /* 00000000 */
+
+       /* 41 0x29 ')' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+
+       /* 42 0x2a '*' */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0xff, /* 11111111 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 43 0x2b '+' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 44 0x2c ',' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+
+       /* 45 0x2d '-' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 46 0x2e '.' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 47 0x2f '/' */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0x80, /* 10000000 */
+       0x00, /* 00000000 */
+
+       /* 48 0x30 '0' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+
+       /* 49 0x31 '1' */
+       0x18, /* 00011000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 50 0x32 '2' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x06, /* 00000110 */
+       0x1c, /* 00011100 */
+       0x30, /* 00110000 */
+       0x66, /* 01100110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 51 0x33 '3' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x06, /* 00000110 */
+       0x3c, /* 00111100 */
+       0x06, /* 00000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 52 0x34 '4' */
+       0x1c, /* 00011100 */
+       0x3c, /* 00111100 */
+       0x6c, /* 01101100 */
+       0xcc, /* 11001100 */
+       0xfe, /* 11111110 */
+       0x0c, /* 00001100 */
+       0x1e, /* 00011110 */
+       0x00, /* 00000000 */
+
+       /* 53 0x35 '5' */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xfc, /* 11111100 */
+       0x06, /* 00000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 54 0x36 '6' */
+       0x38, /* 00111000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0xfc, /* 11111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 55 0x37 '7' */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+
+       /* 56 0x38 '8' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 57 0x39 '9' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7e, /* 01111110 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+
+       /* 58 0x3a ':' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 59 0x3b ';' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+
+       /* 60 0x3c '<' */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+
+       /* 61 0x3d '=' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 62 0x3e '>' */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x00, /* 00000000 */
+
+       /* 63 0x3f '?' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 64 0x40 '@' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xde, /* 11011110 */
+       0xde, /* 11011110 */
+       0xde, /* 11011110 */
+       0xc0, /* 11000000 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+
+       /* 65 0x41 'A' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 66 0x42 'B' */
+       0xfc, /* 11111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xfc, /* 11111100 */
+       0x00, /* 00000000 */
+
+       /* 67 0x43 'C' */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 68 0x44 'D' */
+       0xf8, /* 11111000 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+
+       /* 69 0x45 'E' */
+       0xfe, /* 11111110 */
+       0x62, /* 01100010 */
+       0x68, /* 01101000 */
+       0x78, /* 01111000 */
+       0x68, /* 01101000 */
+       0x62, /* 01100010 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 70 0x46 'F' */
+       0xfe, /* 11111110 */
+       0x62, /* 01100010 */
+       0x68, /* 01101000 */
+       0x78, /* 01111000 */
+       0x68, /* 01101000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+
+       /* 71 0x47 'G' */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xce, /* 11001110 */
+       0x66, /* 01100110 */
+       0x3a, /* 00111010 */
+       0x00, /* 00000000 */
+
+       /* 72 0x48 'H' */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 73 0x49 'I' */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 74 0x4a 'J' */
+       0x1e, /* 00011110 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+
+       /* 75 0x4b 'K' */
+       0xe6, /* 11100110 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0x78, /* 01111000 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+
+       /* 76 0x4c 'L' */
+       0xf0, /* 11110000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x62, /* 01100010 */
+       0x66, /* 01100110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 77 0x4d 'M' */
+       0xc6, /* 11000110 */
+       0xee, /* 11101110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xd6, /* 11010110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 78 0x4e 'N' */
+       0xc6, /* 11000110 */
+       0xe6, /* 11100110 */
+       0xf6, /* 11110110 */
+       0xde, /* 11011110 */
+       0xce, /* 11001110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 79 0x4f 'O' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 80 0x50 'P' */
+       0xfc, /* 11111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+
+       /* 81 0x51 'Q' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xce, /* 11001110 */
+       0x7c, /* 01111100 */
+       0x0e, /* 00001110 */
+
+       /* 82 0x52 'R' */
+       0xfc, /* 11111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+
+       /* 83 0x53 'S' */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 84 0x54 'T' */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x5a, /* 01011010 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 85 0x55 'U' */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 86 0x56 'V' */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+
+       /* 87 0x57 'W' */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+
+       /* 88 0x58 'X' */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 89 0x59 'Y' */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 90 0x5a 'Z' */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x8c, /* 10001100 */
+       0x18, /* 00011000 */
+       0x32, /* 00110010 */
+       0x66, /* 01100110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 91 0x5b '[' */
+       0x3c, /* 00111100 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 92 0x5c '\' */
+       0xc0, /* 11000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x06, /* 00000110 */
+       0x02, /* 00000010 */
+       0x00, /* 00000000 */
+
+       /* 93 0x5d ']' */
+       0x3c, /* 00111100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 94 0x5e '^' */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 95 0x5f '_' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+
+       /* 96 0x60 '`' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 97 0x61 'a' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 98 0x62 'b' */
+       0xe0, /* 11100000 */
+       0x60, /* 01100000 */
+       0x7c, /* 01111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+
+       /* 99 0x63 'c' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 100 0x64 'd' */
+       0x1c, /* 00011100 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 101 0x65 'e' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 102 0x66 'f' */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x60, /* 01100000 */
+       0xf8, /* 11111000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+
+       /* 103 0x67 'g' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x7c, /* 01111100 */
+       0x0c, /* 00001100 */
+       0xf8, /* 11111000 */
+
+       /* 104 0x68 'h' */
+       0xe0, /* 11100000 */
+       0x60, /* 01100000 */
+       0x6c, /* 01101100 */
+       0x76, /* 01110110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+
+       /* 105 0x69 'i' */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 106 0x6a 'j' */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+
+       /* 107 0x6b 'k' */
+       0xe0, /* 11100000 */
+       0x60, /* 01100000 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0x78, /* 01111000 */
+       0x6c, /* 01101100 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+
+       /* 108 0x6c 'l' */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 109 0x6d 'm' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xec, /* 11101100 */
+       0xfe, /* 11111110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0x00, /* 00000000 */
+
+       /* 110 0x6e 'n' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+
+       /* 111 0x6f 'o' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 112 0x70 'p' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+
+       /* 113 0x71 'q' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x7c, /* 01111100 */
+       0x0c, /* 00001100 */
+       0x1e, /* 00011110 */
+
+       /* 114 0x72 'r' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x76, /* 01110110 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+
+       /* 115 0x73 's' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x06, /* 00000110 */
+       0xfc, /* 11111100 */
+       0x00, /* 00000000 */
+
+       /* 116 0x74 't' */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0xfc, /* 11111100 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x36, /* 00110110 */
+       0x1c, /* 00011100 */
+       0x00, /* 00000000 */
+
+       /* 117 0x75 'u' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 118 0x76 'v' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+
+       /* 119 0x77 'w' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+
+       /* 120 0x78 'x' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 121 0x79 'y' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7e, /* 01111110 */
+       0x06, /* 00000110 */
+       0xfc, /* 11111100 */
+
+       /* 122 0x7a 'z' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x4c, /* 01001100 */
+       0x18, /* 00011000 */
+       0x32, /* 00110010 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 123 0x7b '{' */
+       0x0e, /* 00001110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x70, /* 01110000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x0e, /* 00001110 */
+       0x00, /* 00000000 */
+
+       /* 124 0x7c '|' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 125 0x7d '}' */
+       0x70, /* 01110000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x0e, /* 00001110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+
+       /* 126 0x7e '~' */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 127 0x7f '\7f' */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 128 0x80 '\80' */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x0c, /* 00001100 */
+       0x78, /* 01111000 */
+
+       /* 129 0x81 '\81' */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 130 0x82 '\82' */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 131 0x83 '\83' */
+       0x7c, /* 01111100 */
+       0x82, /* 10000010 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 132 0x84 '\84' */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 133 0x85 '\85' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 134 0x86 '\86' */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 135 0x87 '\87' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x7e, /* 01111110 */
+       0x0c, /* 00001100 */
+       0x38, /* 00111000 */
+
+       /* 136 0x88 '\88' */
+       0x7c, /* 01111100 */
+       0x82, /* 10000010 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 137 0x89 '\89' */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 138 0x8a '\8a' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 139 0x8b '\8b' */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 140 0x8c '\8c' */
+       0x7c, /* 01111100 */
+       0x82, /* 10000010 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 141 0x8d '\8d' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 142 0x8e '\8e' */
+       0xc6, /* 11000110 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 143 0x8f '\8f' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 144 0x90 '\90' */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xf8, /* 11111000 */
+       0xc0, /* 11000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 145 0x91 '\91' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0xd8, /* 11011000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 146 0x92 '\92' */
+       0x3e, /* 00111110 */
+       0x6c, /* 01101100 */
+       0xcc, /* 11001100 */
+       0xfe, /* 11111110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xce, /* 11001110 */
+       0x00, /* 00000000 */
+
+       /* 147 0x93 '\93' */
+       0x7c, /* 01111100 */
+       0x82, /* 10000010 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 148 0x94 '\94' */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 149 0x95 '\95' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 150 0x96 '\96' */
+       0x78, /* 01111000 */
+       0x84, /* 10000100 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 151 0x97 '\97' */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 152 0x98 '\98' */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7e, /* 01111110 */
+       0x06, /* 00000110 */
+       0xfc, /* 11111100 */
+
+       /* 153 0x99 '\99' */
+       0xc6, /* 11000110 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+
+       /* 154 0x9a '\9a' */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 155 0x9b '\9b' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 156 0x9c '\9c' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x64, /* 01100100 */
+       0xf0, /* 11110000 */
+       0x60, /* 01100000 */
+       0x66, /* 01100110 */
+       0xfc, /* 11111100 */
+       0x00, /* 00000000 */
+
+       /* 157 0x9d '\9d' */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 158 0x9e '\9e' */
+       0xf8, /* 11111000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xfa, /* 11111010 */
+       0xc6, /* 11000110 */
+       0xcf, /* 11001111 */
+       0xc6, /* 11000110 */
+       0xc7, /* 11000111 */
+
+       /* 159 0x9f '\9f' */
+       0x0e, /* 00001110 */
+       0x1b, /* 00011011 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0xd8, /* 11011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+
+       /* 160 0xa0 ' ' */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 161 0xa1 '¡' */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 162 0xa2 '¢' */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+
+       /* 163 0xa3 '£' */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 164 0xa4 '¤' */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+
+       /* 165 0xa5 '¥' */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0xe6, /* 11100110 */
+       0xf6, /* 11110110 */
+       0xde, /* 11011110 */
+       0xce, /* 11001110 */
+       0x00, /* 00000000 */
+
+       /* 166 0xa6 '¦' */
+       0x3c, /* 00111100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x3e, /* 00111110 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 167 0xa7 '§' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 168 0xa8 '¨' */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x63, /* 01100011 */
+       0x3e, /* 00111110 */
+       0x00, /* 00000000 */
+
+       /* 169 0xa9 '©' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 170 0xaa 'ª' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 171 0xab '«' */
+       0x63, /* 01100011 */
+       0xe6, /* 11100110 */
+       0x6c, /* 01101100 */
+       0x7e, /* 01111110 */
+       0x33, /* 00110011 */
+       0x66, /* 01100110 */
+       0xcc, /* 11001100 */
+       0x0f, /* 00001111 */
+
+       /* 172 0xac '¬' */
+       0x63, /* 01100011 */
+       0xe6, /* 11100110 */
+       0x6c, /* 01101100 */
+       0x7a, /* 01111010 */
+       0x36, /* 00110110 */
+       0x6a, /* 01101010 */
+       0xdf, /* 11011111 */
+       0x06, /* 00000110 */
+
+       /* 173 0xad '­' */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 174 0xae '®' */
+       0x00, /* 00000000 */
+       0x33, /* 00110011 */
+       0x66, /* 01100110 */
+       0xcc, /* 11001100 */
+       0x66, /* 01100110 */
+       0x33, /* 00110011 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 175 0xaf '¯' */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0x66, /* 01100110 */
+       0x33, /* 00110011 */
+       0x66, /* 01100110 */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 176 0xb0 '°' */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+       0x22, /* 00100010 */
+       0x88, /* 10001000 */
+
+       /* 177 0xb1 '±' */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+
+       /* 178 0xb2 '²' */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+
+       /* 179 0xb3 '³' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 180 0xb4 '´' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 181 0xb5 'µ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 182 0xb6 '¶' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf6, /* 11110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 183 0xb7 '·' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 184 0xb8 '¸' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 185 0xb9 '¹' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf6, /* 11110110 */
+       0x06, /* 00000110 */
+       0xf6, /* 11110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 186 0xba 'º' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 187 0xbb '»' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x06, /* 00000110 */
+       0xf6, /* 11110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 188 0xbc '¼' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf6, /* 11110110 */
+       0x06, /* 00000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 189 0xbd '½' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 190 0xbe '¾' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 191 0xbf '¿' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 192 0xc0 'À' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 193 0xc1 'Á' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 194 0xc2 'Â' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 195 0xc3 'Ã' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 196 0xc4 'Ä' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 197 0xc5 'Å' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 198 0xc6 'Æ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 199 0xc7 'Ç' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x37, /* 00110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 200 0xc8 'È' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x37, /* 00110111 */
+       0x30, /* 00110000 */
+       0x3f, /* 00111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 201 0xc9 'É' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3f, /* 00111111 */
+       0x30, /* 00110000 */
+       0x37, /* 00110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 202 0xca 'Ê' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf7, /* 11110111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 203 0xcb 'Ë' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xf7, /* 11110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 204 0xcc 'Ì' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x37, /* 00110111 */
+       0x30, /* 00110000 */
+       0x37, /* 00110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 205 0xcd 'Í' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 206 0xce 'Î' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf7, /* 11110111 */
+       0x00, /* 00000000 */
+       0xf7, /* 11110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 207 0xcf 'Ï' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 208 0xd0 'Ð' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 209 0xd1 'Ñ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 210 0xd2 'Ò' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 211 0xd3 'Ó' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x3f, /* 00111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 212 0xd4 'Ô' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 213 0xd5 'Õ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 214 0xd6 'Ö' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3f, /* 00111111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 215 0xd7 '×' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xff, /* 11111111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 216 0xd8 'Ø' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 217 0xd9 'Ù' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 218 0xda 'Ú' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 219 0xdb 'Û' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 220 0xdc 'Ü' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 221 0xdd 'Ý' */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+
+       /* 222 0xde 'Þ' */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+
+       /* 223 0xdf 'ß' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 224 0xe0 'à' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0xc8, /* 11001000 */
+       0xdc, /* 11011100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+
+       /* 225 0xe1 'á' */
+       0x78, /* 01111000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xd8, /* 11011000 */
+       0xcc, /* 11001100 */
+       0xc6, /* 11000110 */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+
+       /* 226 0xe2 'â' */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+
+       /* 227 0xe3 'ã' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+
+       /* 228 0xe4 'ä' */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+
+       /* 229 0xe5 'å' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+
+       /* 230 0xe6 'æ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0xc0, /* 11000000 */
+
+       /* 231 0xe7 'ç' */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+
+       /* 232 0xe8 'è' */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+
+       /* 233 0xe9 'é' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+
+       /* 234 0xea 'ê' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0xee, /* 11101110 */
+       0x00, /* 00000000 */
+
+       /* 235 0xeb 'ë' */
+       0x0e, /* 00001110 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x3e, /* 00111110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 236 0xec 'ì' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 237 0xed 'í' */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x7e, /* 01111110 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0x7e, /* 01111110 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+
+       /* 238 0xee 'î' */
+       0x1e, /* 00011110 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x7e, /* 01111110 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x1e, /* 00011110 */
+       0x00, /* 00000000 */
+
+       /* 239 0xef 'ï' */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+
+       /* 240 0xf0 'ð' */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 241 0xf1 'ñ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 242 0xf2 'ò' */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 243 0xf3 'ó' */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+
+       /* 244 0xf4 'ô' */
+       0x0e, /* 00001110 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 245 0xf5 'õ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0x70, /* 01110000 */
+
+       /* 246 0xf6 'ö' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 247 0xf7 '÷' */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 248 0xf8 'ø' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 249 0xf9 'ù' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 250 0xfa 'ú' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 251 0xfb 'û' */
+       0x0f, /* 00001111 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0xec, /* 11101100 */
+       0x6c, /* 01101100 */
+       0x3c, /* 00111100 */
+       0x1c, /* 00011100 */
+
+       /* 252 0xfc 'ü' */
+       0x6c, /* 01101100 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 253 0xfd 'ý' */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 254 0xfe 'þ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 255 0xff 'ÿ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+};
diff --git a/src/game.h b/src/game.h
new file mode 100644 (file)
index 0000000..b290634
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef GAME_H_
+#define GAME_H_
+
+void (*screen_vblank)(void);
+
+void menuscr(void);
+void gamescr(void);
+
+#endif /* GAME_H_ */
diff --git a/src/gamescr.c b/src/gamescr.c
new file mode 100644 (file)
index 0000000..cacb40d
--- /dev/null
@@ -0,0 +1,69 @@
+#include <stdlib.h>
+#include <string.h>
+#include "gbaregs.h"
+#include "game.h"
+#include "dma.h"
+#include "util.h"
+#include "intr.h"
+#include "input.h"
+#include "sprite.h"
+#include "debug.h"
+
+static void draw(void);
+static void vblank(void);
+
+static int nframes, num_vbl, backbuf;
+static uint16_t *vram[] = { (uint16_t*)VRAM_LFB_FB0_ADDR, (uint16_t*)VRAM_LFB_FB1_ADDR };
+static uint16_t bnstate;
+
+
+void gamescr(void)
+{
+       int i;
+
+       REG_DISPCNT = 4 | DISPCNT_BG2 | DISPCNT_OBJ | DISPCNT_FB1;
+
+       vblperf_setcolor(0xff);//192);
+
+       fillblock_16byte(vram[0], 0xffffffff, 240 * 160 / 16);
+       fillblock_16byte(vram[1], 0xffffffff, 240 * 160 / 16);
+
+       mask(INTR_VBLANK);
+       screen_vblank = vblank;
+       unmask(INTR_VBLANK);
+
+       nframes = 0;
+       for(;;) {
+               backbuf = ++nframes & 1;
+
+               bnstate = ~REG_KEYINPUT;
+
+               draw();
+
+               vblperf_end();
+               wait_vblank();
+               present(backbuf);
+               vblperf_begin();
+       }
+}
+
+static void draw(void)
+{
+       int i, j;
+       uint16_t pix;
+       uint16_t *fb = vram[backbuf];
+
+       for(i=0; i<160; i++) {
+               for(j=0; j<240/2; j++) {
+                       pix = ((i^j) << 1) & 0xff;
+                       pix |= (i^j) << 9;
+                       *fb++ = pix;
+               }
+       }
+}
+
+__attribute__((noinline, target("arm"), section(".iwram")))
+static void vblank(void)
+{
+       num_vbl++;
+}
diff --git a/src/gbaregs.h b/src/gbaregs.h
new file mode 100644 (file)
index 0000000..3396cc9
--- /dev/null
@@ -0,0 +1,365 @@
+#ifndef GBAREGS_H_
+#define GBAREGS_H_
+
+#include <stdint.h>
+
+#define VRAM_START_ADDR                0x6000000
+#define VRAM_BG_ADDR           VRAM_START_ADDR
+#define VRAM_OBJ_ADDR          0x6010000
+#define VRAM_LFB_OBJ_ADDR      0x6014000
+#define VRAM_LFB_FB0_ADDR      VRAM_START_ADDR
+#define VRAM_LFB_FB1_ADDR      0x600a000
+
+/* address of character data block x (4 possible blocks, 16k each) */
+#define VRAM_CHR_BLOCK_ADDR(x) (VRAM_START_ADDR + ((x) << 14))
+/* address of screen data block x (32 possible blocks, 2k each) */
+#define VRAM_SCR_BLOCK_ADDR(x) (VRAM_START_ADDR + ((x) << 11))
+
+/* fields of a background tile in screen memory */
+#define BGTILE_HFLIP   0x0400
+#define BGTILE_VFLIP   0x0800
+#define BGTILE_PAL(x)  ((uint16_t)(x) << 12)
+
+/* color palette ram addresses for backgrounds and sprites */
+#define CRAM_BG_ADDR   0x5000000
+#define CRAM_OBJ_ADDR  0x5000200
+
+/* OAM table */
+#define OAM_ADDR               0x7000000
+
+/* interrupt handler */
+#define INTR_VECTOR            (*(volatile uint32_t*)0x3007ffc)
+
+/* battery backed RAM address */
+#define SRAM_ADDR              0xe000000
+
+/* I/O space */
+
+#define REG_BASE               0x4000000
+#define REG8(x)                        (*(volatile uint8_t*)(REG_BASE + (x)))
+#define REG16(x)               (*(volatile uint16_t*)(REG_BASE + (x)))
+#define REG32(x)               (*(volatile uint32_t*)(REG_BASE + (x)))
+
+/* ---- display registers ---- */
+#define REG_DISPCNT            REG16(0x00)
+#define REG_GREENSWAP  REG16(0x02)
+#define REG_DISPSTAT   REG16(0x04)
+#define REG_VCOUNT             REG16(0x06)
+#define REG_BG0CNT             REG16(0x08)
+#define REG_BG1CNT             REG16(0x0a)
+#define REG_BG2CNT             REG16(0x0c)
+#define REG_BG3CNT             REG16(0x0e)
+/* scrolling registers */
+#define REG_BG0HOFS            REG16(0x10)
+#define REG_BG0VOFS            REG16(0x12)
+#define REG_BG1HOFS            REG16(0x14)
+#define REG_BG1VOFS            REG16(0x16)
+#define REG_BG2HOFS            REG16(0x18)
+#define REG_BG2VOFS            REG16(0x1a)
+#define REG_BG3HOFS            REG16(0x1c)
+#define REG_BG3VOFS            REG16(0x1e)
+/* BG rotation and scaling registers */
+#define REG_BG2PA              REG16(0x20)
+#define REG_BG2PB              REG16(0x22)
+#define REG_BG2PC              REG16(0x24)
+#define REG_BG2PD              REG16(0x26)
+#define REG_BG2X               REG32(0x28)
+#define REG_BG2Y               REG32(0x2c)
+#define REG_BG3PA              REG16(0x30)
+#define REG_BG3PB              REG16(0x32)
+#define REG_BG3PC              REG16(0x34)
+#define REG_BG3PD              REG16(0x36)
+#define REG_BG3X               REG32(0x38)
+#define REG_BG3Y               REG32(0x3c)
+/* window registers */
+#define REG_WIN0H              REG16(0x40)
+#define REG_WIN1H              REG16(0x42)
+#define REG_WIN0V              REG16(0x44)
+#define REG_WIN1V              REG16(0x46)
+#define REG_WININ              REG16(0x48)
+#define REG_WINOUT             REG16(0x4a)
+
+#define REG_MOSAIC             REG16(0x4c)
+/* color effects */
+#define REG_BLDCNT             REG16(0x50)
+#define REG_BLDALPHA   REG16(0x52)
+#define REG_BLDY               REG16(0x54)
+
+/* ---- sound registers ---- */
+#define REG_SOUND1CNT_L        REG16(0x60)
+#define REG_SOUND1CNT_H        REG16(0x62)
+#define REG_SOUND1CNT_X        REG16(0x64)
+#define REG_SOUND2CNT_L        REG16(0x68)
+#define REG_SOUND2CNT_H        REG16(0x6c)
+#define REG_SOUND3CNT_L        REG16(0x70)
+#define REG_SOUND3CNT_H        REG16(0x72)
+#define REG_SOUND3CNT_X        REG16(0x74)
+#define REG_SOUND4CNT_L        REG16(0x78)
+#define REG_SOUND4CNT_H        REG16(0x7c)
+#define REG_SOUNDCNT_L REG16(0x80)
+#define REG_SOUNDCNT_H REG16(0x82)
+#define REG_SOUNDCNT_X REG16(0x84)
+#define REG_SOUNDBIAS  REG16(0x88)
+#define WAVE_RAM_PTR   ((unsigned char*)(REG_BASE + 0x90))
+#define REG_FIFO_A             REG32(0xa0)
+#define REG_FIFO_B             REG32(0xa4)
+#define FIFO_A_PTR             ((unsigned char*)(REG_BASE + 0xa0))
+#define FIFO_B_PTR             ((unsigned char*)(REG_BASE + 0xa4))
+
+/* ---- DMA registers ---- */
+#define REG_DMA0SAD            REG32(0xb0)
+#define REG_DMA0DAD            REG32(0xb4)
+#define REG_DMA0CNT            REG32(0xb8)
+#define REG_DMA0CNT_L  REG16(0xb8)
+#define REG_DMA0CNT_H  REG16(0xba)
+#define REG_DMA1SAD            REG32(0xbc)
+#define REG_DMA1DAD            REG32(0xc0)
+#define REG_DMA1CNT            REG32(0xc4)
+#define REG_DMA1CNT_L  REG16(0xc4)
+#define REG_DMA1CNT_H  REG16(0xc6)
+#define REG_DMA2SAD            REG32(0xc8)
+#define REG_DMA2DAD            REG32(0xcc)
+#define REG_DMA2CNT            REG32(0xd0)
+#define REG_DMA2CNT_L  REG16(0xd0)
+#define REG_DMA2CNT_H  REG16(0xd2)
+#define REG_DMA3SAD            REG32(0xd4)
+#define REG_DMA3DAD            REG32(0xd8)
+#define REG_DMA3CNT            REG32(0xdc)
+#define REG_DMA3CNT_L  REG16(0xdc)
+#define REG_DMA3CNT_H  REG16(0xde)
+
+/* ---- timer registers ---- */
+#define REG_TM0CNT_L   REG16(0x100)
+#define REG_TM0CNT_H   REG16(0x102)
+#define REG_TM1CNT_L   REG16(0x104)
+#define REG_TM1CNT_H   REG16(0x106)
+#define REG_TM2CNT_L   REG16(0x108)
+#define REG_TM2CNT_H   REG16(0x10a)
+#define REG_TM3CNT_L   REG16(0x10c)
+#define REG_TM3CNT_H   REG16(0x10e)
+
+#define REG_TMCNT_L(x) REG16(0x100 + ((x) << 2))
+#define REG_TMCNT_H(x) REG16(0x102 + ((x) << 2))
+
+/* ---- communication registers (serial/joybus/gpio) ---- */
+#define REG_SIODATA32  REG32(0x120)
+#define REG_SIOMULTI0  REG16(0x120)
+#define REG_SIOMULTI1  REG16(0x122)
+#define REG_SIOMULTI2  REG16(0x124)
+#define REG_SIOMULTI3  REG16(0x126)
+#define REG_SIOCNT             REG16(0x128)
+#define REG_SIOMLT_SEND        REG16(0x12a)
+#define REG_SIODATA8   REG16(0x12a)
+#define REG_RCNT               REG16(0x134)
+#define REG_JOYCNT             REG16(0x140)
+#define REG_JOY_RECV   REG32(0x150)
+#define REG_JOY_TRANS  REG32(0x154)
+#define REG_JOYSTAT            REG16(0x158)
+
+/* ---- keypad registers ---- */
+#define REG_KEYINPUT   REG16(0x130)
+#define REG_KEYCNT             REG16(0x132)
+
+/* ---- interrupts ---- */
+#define REG_IE                 REG16(0x200)
+#define REG_IF                 REG16(0x202)
+#define REG_WAITCNT            REG16(0x204)
+#define REG_IME                        REG16(0x208)
+
+#define REG_POSTFLG            REG8(0x300)
+#define REG_HALTCNT            REG8(0x301)
+#define REG_INTMEMCNT  REG32(0x800)
+
+/* REG_DISPSTAT bits */
+#define DISPSTAT_VBLANK                        0x01
+#define DISPSTAT_HBLANK                        0x02
+#define DISPSTAT_VMATCH                        0x04
+#define DISPSTAT_IEN_VBLANK            0x08
+#define DISPSTAT_IEN_HBLANK            0x10
+#define DISPSTAT_IEN_VMATCH            0x20
+#define DISPSTAT_VCOUNT(x)             ((uint16_t)(x) << 8)
+
+/* REG_DISPCNT bits */
+#define DISPCNT_MODE(x)                        (x)
+#define DISPCNT_FB1                            0x0010
+#define DISPCNT_HBLANK_OBJPROC 0x0020
+#define DISPCNT_OBJMAP_1D              0x0040
+#define DISPCNT_FORCE_BLANK            0x0080
+#define DISPCNT_BG0                            0x0100
+#define DISPCNT_BG1                            0x0200
+#define DISPCNT_BG2                            0x0400
+#define DISPCNT_BG3                            0x0800
+#define DISPCNT_OBJ                            0x1000
+#define DISPCNT_WIN0                   0x2000
+#define DISPCNT_WIN1                   0x4000
+#define DISPCNT_WINOBJ                 0x8000
+
+/* REG_BGXCNT bits */
+#define BGCNT_PRIO(x)                  ((uint16_t)(x))
+#define BGCNT_CHR_BASE(x)              ((uint16_t)(x) << 2)
+#define BGCNT_MOSAIC                   0x0040
+#define BGCNT_256COL                   0x0080
+#define BGCNT_SCR_BASE(x)              ((uint16_t)(x) << 8)
+#define BGCNT_WRAP                             0x2000
+
+#define BGCNT_SZ(x)                            ((uint16_t)(x) << 14)
+#define BGCNT_SZ_TX_256X256            BGCNT_SZ(0)
+#define BGCNT_SZ_RS_128X128            BGCNT_SZ(0)
+#define BGCNT_SZ_TX_512X256            BGCNT_SZ(1)
+#define BGCNT_SZ_RS_256X256            BGCNT_SZ(1)
+#define BGCNT_SZ_TX_256X512            BGCNT_SZ(2)
+#define BGCNT_SZ_RS_512X512            BGCNT_SZ(2)
+#define BGCNT_SZ_TX_512X512            BGCNT_SZ(3)
+#define BGCNT_SZ_RS_1024X1024  BGCNT_SZ(3)
+
+/* REG_BLDCNT bits */
+#define BLDCNT_A_BG0           0x0001
+#define BLDCNT_A_BG1           0x0002
+#define BLDCNT_A_BG2           0x0004
+#define BLDCNT_A_BG3           0x0008
+#define BLDCNT_A_OBJ           0x0010
+#define BLDCNT_A_BACKDROP      0x0020
+#define BLDCNT_B_BG0           0x0100
+#define BLDCNT_B_BG1           0x0200
+#define BLDCNT_B_BG2           0x0400
+#define BLDCNT_B_BG3           0x0800
+#define BLDCNT_B_OBJ           0x1000
+#define BLDCNT_B_BACKDROP      0x2000
+
+#define BLDCNT_ALPHA           0x0040
+#define BLDCNT_BRIGHTEN                0x0080
+#define BLDCNT_DARKEN          0x00c0
+
+/* REG_IF bits */
+#define IF_VBLANK              0x0001
+#define IF_HBLANK              0x0002
+#define IF_VMATCH              0x0004
+#define IF_TIMER0              0x0008
+#define IF_TIMER1              0x0010
+#define IF_TIMER2              0x0020
+#define IF_TIMER3              0x0040
+#define IF_COMM                        0x0080
+#define IF_DMA0                        0x0100
+#define IF_DMA1                        0x0200
+#define IF_DMA2                        0x0400
+#define IF_DMA3                        0x0800
+#define IF_KEY                 0x1000
+#define IF_GPAK                        0x2000
+
+/* REG_TMXCNT bits */
+#define TMCNT_PRESCL_CLK1              0
+#define TMCNT_PRESCL_CLK64             1
+#define TMCNT_PRESCL_CLK256            2
+#define TMCNT_PRESCL_CLK1024   3
+
+#define TMCNT_CASCADE                  0x04
+#define TMCNT_IE                               0x40
+#define TMCNT_EN                               0x80
+
+/* REG_KEY* bits */
+#define KEY_A                  0x0001
+#define KEY_B                  0x0002
+#define KEY_SELECT             0x0004
+#define KEY_START              0x0008
+#define KEY_RIGHT              0x0010
+#define KEY_LEFT               0x0020
+#define KEY_UP                 0x0040
+#define KEY_DOWN               0x0080
+#define KEY_RT                 0x0100
+#define KEY_LT                 0x0200
+
+#define KEYCNT_IE              0x4000
+#define KEYCNT_IAND            0x8000
+
+/* REG_SOUNDCNT_L bits */
+#define SCNT_SS_LVOL(x)                ((x) & 7)
+#define SCNT_SS_RVOL(x)                (((x) & 7) << 4)
+#define SCNT_SS_VOL(x)         (SCNT_SS_LVOL(x) | SCNT_SS_RVOL(x))
+#define SCNT_SS1_EN_R          0x0100
+#define SCNT_SS2_EN_R          0x0200
+#define SCNT_SS3_EN_R          0x0400
+#define SCNT_SS4_EN_R          0x0800
+#define SCNT_SS_EN_R(x)                (SCNT_SS1_EN_R << (x))
+#define SCNT_SS1_EN_L          0x1000
+#define SCNT_SS2_EN_L          0x2000
+#define SCNT_SS3_EN_L          0x4000
+#define SCNT_SS4_EN_L          0x8000
+#define SCNT_SS_EN_L(x)                (SCNT_SS1_EN_L << (x))
+#define SCNT_SS1_EN                    (SCNT_SS1_EN_R | SCNT_SS1_EN_L)
+#define SCNT_SS2_EN                    (SCNT_SS2_EN_R | SCNT_SS2_EN_L)
+#define SCNT_SS3_EN                    (SCNT_SS3_EN_R | SCNT_SS3_EN_L)
+#define SCNT_SS4_EN                    (SCNT_SS4_EN_R | SCNT_SS4_EN_L)
+#define SCNT_SS_EN(x)          (SCNT_SS_EN_L(x) | SCNT_SS_EN_R(x))
+
+#define SCNT_SS1               0
+#define SCNT_SS2               1
+#define SCNT_SS3               2
+#define SCNT_SS4               3
+
+/* REG_SOUNDCNT_X bits */
+#define SCNT_MASTER_EN         0x0080
+
+/* REG_SOUNDCNT_H bits */
+#define SCNT_SS_VOL_QRT                0x0000
+#define SCNT_SS_VOL_HALF       0x0001
+#define SCNT_SS_VOL_FULL       0x0002
+#define SCNT_DSA_VOL_HALF      0
+#define SCNT_DSA_VOL_FULL      0x0004
+#define SCNT_DSB_VOL_HALF      0
+#define SCNT_DSB_VOL_FULL      0x0008
+#define SCNT_DSA_EN_R          0x0100
+#define SCNT_DSA_EN_L          0x0200
+#define SCNT_DSA_TIMER0                0
+#define SCNT_DSA_TIMER1                0x0400
+#define SCNT_DSA_CLRFIFO       0x0800
+#define SCNT_DSB_EN_R          0x1000
+#define SCNT_DSB_EN_L          0x2000
+#define SCNT_DSB_TIMER0                0
+#define SCNT_DSB_TIMER1                0x4000
+#define SCNT_DSB_CLRFIFO       0x8000
+
+/* REG_DMAxCNT_H bits */
+#define DMACNTH_DST_INC                0
+#define DMACNTH_DST_DEC                0x0020
+#define DMACNTH_DST_FIXED      0x0040
+#define DMACNTH_INC_RELOAD     0x0060
+#define DMACNTH_SRC_INC                0
+#define DMACNTH_SRC_DEC                0x0080
+#define DMACNTH_SRC_FIXED      0x0100
+#define DMACNTH_REPEAT         0x0200
+#define DMACNTH_16BIT          0
+#define DMACNTH_32BIT          0x0400
+#define DMACNTH_VBLANK         0x1000
+#define DMACNTH_HBLANK         0x2000
+#define DMACNTH_SOUND          0x3000
+#define DMACNTH_IEN                    0x4000
+#define DMACNTH_EN                     0x8000
+
+#define DMACNT_DST_INC         0
+#define DMACNT_DST_DEC         0x00200000
+#define DMACNT_DST_FIXED       0x00400000
+#define DMACNT_INC_RELOAD      0x00600000
+#define DMACNT_SRC_INC         0
+#define DMACNT_SRC_DEC         0x00800000
+#define DMACNT_SRC_FIXED       0x01000000
+#define DMACNT_REPEAT          0x02000000
+#define DMACNT_16BIT           0
+#define DMACNT_32BIT           0x04000000
+#define DMACNT_VBLANK          0x10000000
+#define DMACNT_HBLANK          0x20000000
+#define DMACNT_SOUND           0x30000000
+#define DMACNT_IEN                     0x40000000
+#define DMACNT_EN                      0x80000000
+
+/* REG_WAITCNT bits */
+#define WAITCNT_ROM_4_2                0x0000
+#define WAITCNT_ROM_3_2                0x0004
+#define WAITCNT_ROM_2_2                0x0008
+#define WAITCNT_ROM_8_2                0x000c
+#define WAITCNT_ROM_4_1                0x0010
+#define WAITCNT_ROM_3_1                0x0014
+#define WAITCNT_ROM_2_1                0x0018
+#define WAITCNT_ROM_8_1                0x001c
+#define WAITCNT_PREFETCH       0x4000
+
+#endif /* GBAREGS_H_ */
diff --git a/src/input.c b/src/input.c
new file mode 100644 (file)
index 0000000..6a66dd1
--- /dev/null
@@ -0,0 +1,39 @@
+#include "input.h"
+#include "gbaregs.h"
+#include "intr.h"
+
+static void keyintr(void);
+
+static uint16_t bnstate;
+
+void select_input(uint16_t bmask)
+{
+       bnstate = 0;
+
+       mask(INTR_KEY);
+       if(bmask) {
+               REG_KEYCNT = bmask | KEYCNT_IE;
+               interrupt(INTR_KEY, keyintr);
+               unmask(INTR_KEY);
+       } else {
+               REG_KEYCNT = 0;
+               interrupt(INTR_KEY, 0);
+       }
+}
+
+uint16_t get_input(void)
+{
+       uint16_t s;
+
+       mask(INTR_KEY);
+       s = bnstate;
+       bnstate = 0;
+       unmask(INTR_KEY);
+
+       return s;
+}
+
+static void keyintr(void)
+{
+       bnstate |= ~REG_KEYINPUT;
+}
diff --git a/src/input.h b/src/input.h
new file mode 100644 (file)
index 0000000..3ed22a0
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef INPUT_H_
+#define INPUT_H_
+
+#include <stdint.h>
+
+enum {
+       BN_A            = 0x0001,
+       BN_B            = 0x0002,
+       BN_SELECT       = 0x0004,
+       BN_START        = 0x0008,
+       BN_RIGHT        = 0x0010,
+       BN_LEFT         = 0x0020,
+       BN_UP           = 0x0040,
+       BN_DOWN         = 0x0080,
+       BN_RT           = 0x0100,
+       BN_LT           = 0x0200
+};
+
+#define BN_DPAD        (BN_RIGHT | BN_LEFT | BN_UP | BN_DOWN)
+
+void select_input(uint16_t bmask);
+uint16_t get_input(void);
+
+#endif /* INPUT_H_ */
diff --git a/src/intr.c b/src/intr.c
new file mode 100644 (file)
index 0000000..5858bf1
--- /dev/null
@@ -0,0 +1,31 @@
+#include "intr.h"
+
+#define MAX_INTR       14
+static void (*intr_table[MAX_INTR])(void);
+
+__attribute__ ((target("arm"), section(".iwram")))
+static void intr_handler(void)
+{
+       int i;
+       uint16_t iflags;
+
+       iflags = REG_IF;
+
+       for(i=0; i<MAX_INTR; i++) {
+               if((iflags & (1 << i)) && intr_table[i]) {
+                       intr_table[i]();
+               }
+       }
+
+       REG_IF = iflags;        /* ack intr */
+}
+
+void intr_init(void)
+{
+       INTR_VECTOR = (uint32_t)intr_handler;
+}
+
+void interrupt(int intr, void (*handler)(void))
+{
+       intr_table[intr] = handler;
+}
diff --git a/src/intr.h b/src/intr.h
new file mode 100644 (file)
index 0000000..859b9a7
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef INTR_H_
+#define INTR_H_
+
+#include "gbaregs.h"
+
+/* interrupts */
+enum {
+       INTR_VBLANK,
+       INTR_HBLANK,
+       INTR_VCOUNT,
+       INTR_TIMER0,
+       INTR_TIMER1,
+       INTR_TIMER2,
+       INTR_TIMER3,
+       INTR_COMM,
+       INTR_DMA0,
+       INTR_DMA1,
+       INTR_DMA2,
+       INTR_DMA3,
+       INTR_KEY,
+       INTR_GPAK
+};
+
+void intr_init(void);
+
+/* set/clear interrupts */
+#define intr_enable()  \
+       do { REG_IME |= 0x0001; } while(0)
+#define intr_disable() \
+       do { REG_IME &= 0xfffe; } while(0)
+
+/* set an interrupt handler */
+void interrupt(int intr, void (*handler)(void));
+
+/* mask/unmask an interrupt */
+#define mask(intr)             do {REG_IE &= ~(1 << (intr));} while(0)
+#define unmask(intr)   do {REG_IE |= 1 << (intr);} while(0)
+
+#endif /* INTR_H_ */
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..068f3d1
--- /dev/null
@@ -0,0 +1,59 @@
+#include <math.h>
+#include "gbaregs.h"
+#include "intr.h"
+#include "debug.h"
+#include "game.h"
+#include "maxmod.h"
+
+static void vblank(void);
+static void nopfunc(void);
+
+int main(void)
+{
+       int i;
+       volatile uint16_t *cptr;
+
+       intr_init();
+
+       REG_WAITCNT = WAITCNT_PREFETCH | WAITCNT_ROM_2_1;
+
+       cptr = (uint16_t*)CRAM_BG_ADDR;
+       for(i=0; i<256; i++) {
+               int c = i >> 3;
+               *cptr++ = c | ((c >> 1) << 10);
+       }
+
+#ifndef NOSOUND
+       mmInitDefault(sound_data, 8);
+       mmStart(MOD_POPCORN, MM_PLAY_LOOP);
+#endif
+
+       screen_vblank = nopfunc;
+
+       intr_disable();
+       interrupt(INTR_VBLANK, vblank);
+       REG_DISPSTAT |= DISPSTAT_IEN_VBLANK;
+       unmask(INTR_VBLANK);
+
+       intr_enable();
+       gamescr();
+
+       for(;;);
+       return 0;
+}
+
+static void vblank(void)
+{
+       vblperf_count++;
+
+       screen_vblank();
+
+#ifndef NOSOUND
+       mmVBlank();
+       mmFrame();
+#endif
+}
+
+static void nopfunc(void)
+{
+}
diff --git a/src/menuscr.c b/src/menuscr.c
new file mode 100644 (file)
index 0000000..dfbc8cb
--- /dev/null
@@ -0,0 +1,5 @@
+#include "gbaregs.h"
+
+void menuscr(void)
+{
+}
diff --git a/src/polyfill.c b/src/polyfill.c
new file mode 100644 (file)
index 0000000..18294f5
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include "polyfill.h"
+#include "debug.h"
+
+static unsigned char *fb;
+static int fbwidth, fbheight;
+static short scantab[2][160] __attribute__((section(".iwram")));
+
+void polyfill_framebuffer(unsigned char *ptr, int w, int h)
+{
+       fb = ptr;
+       fbwidth = w;
+       fbheight = h;
+}
+
+#define VNEXT(p)       (((p) == vlast) ? varr : (p) + 1)
+#define VPREV(p)       ((p) == varr ? vlast : (p) - 1)
+#define VSUCC(p, side) ((side) == 0 ? VNEXT(p) : VPREV(p))
+
+void polyfill_flat(struct pvertex *varr, int vnum, unsigned char col)
+{
+       int i, line, top, bot;
+       struct pvertex *vlast, *v, *vn;
+       int32_t x, y0, y1, dx, dy, slope, fx, fy;
+       short *tab, start, len;
+       unsigned char *fbptr;
+       uint16_t *pptr, pcol = ((uint16_t)col << 8) | (uint16_t)col;
+
+       vlast = varr + vnum - 1;
+       top = fbheight;
+       bot = 0;
+
+       for(i=0; i<vnum; i++) {
+               v = varr + i;
+               vn = VNEXT(v);
+
+               if(vn->y == v->y) continue;
+
+               if(vn->y > v->y) {
+                       tab = scantab[0];
+               } else {
+                       tab = scantab[1];
+                       v = vn;
+                       vn = varr + i;
+               }
+
+               dx = vn->x - v->x;
+               dy = vn->y - v->y;
+               slope = (dx << 8) / dy;
+
+               y0 = (v->y + 0x100) & 0xffffff00;       /* start from the next scanline */
+               fy = y0 - v->y;                                         /* fractional part before the next scanline */
+               fx = (fy * slope) >> 8;                         /* X adjust for the step to the next scanline */
+               x = v->x + fx;                                          /* adjust X */
+               y1 = vn->y & 0xffffff00;                        /* last scanline of the edge <= vn->y */
+
+               line = y0 >> 8;
+               if(line < top) top = line;
+               if((y1 >> 8) > bot) bot = y1 >> 8;
+
+               if(line > 0) tab += line;
+
+               while(line <= (y1 >> 8) && line < fbheight) {
+                       if(line >= 0) {
+                               int val = x < 0 ? 0 : x >> 8;
+                               *tab++ = val < fbwidth ? val : fbwidth - 1;
+                       }
+                       x += slope;
+                       line++;
+               }
+       }
+
+       fbptr = fb + top * fbwidth;
+       for(i=top; i<=bot; i++) {
+               start = scantab[0][i];
+               len = scantab[1][i] - start;
+
+               if(len > 0) {
+                       if(start & 1) {
+                               pptr = (uint16_t*)(fbptr + (start & 0xfffe));
+                               *pptr = (*pptr & 0xff) | ((uint16_t)col << 8);
+                               len--;
+                               start++;
+                       }
+                       pptr = (uint16_t*)(fbptr + start);
+                       while(len > 1) {
+                               *pptr++ = pcol;
+                               len -= 2;
+                       }
+                       if(len) {
+                               *pptr = (*pptr & 0xff00) | col;
+                       }
+               }
+               fbptr += fbwidth;
+       }
+}
+
+
+/* ----- line drawing and clipping ------ */
+enum {
+       IN              = 0,
+       LEFT    = 1,
+       RIGHT   = 2,
+       TOP             = 4,
+       BOTTOM  = 8
+};
+
+static int outcode(int x, int y, int xmin, int ymin, int xmax, int ymax)
+{
+       int code = 0;
+
+       if(x < xmin) {
+               code |= LEFT;
+       } else if(x > xmax) {
+               code |= RIGHT;
+       }
+       if(y < ymin) {
+               code |= TOP;
+       } else if(y > ymax) {
+               code |= BOTTOM;
+       }
+       return code;
+}
+
+#define FIXMUL(a, b)   (((a) * (b)) >> 8)
+#define FIXDIV(a, b)   (((a) << 8) / (b))
+
+#define LERP(a, b, t)  ((a) + FIXMUL((b) - (a), (t)))
+
+int clip_line(int *x0, int *y0, int *x1, int *y1, int xmin, int ymin, int xmax, int ymax)
+{
+       int oc_out;
+
+       int oc0 = outcode(*x0, *y0, xmin, ymin, xmax, ymax);
+       int oc1 = outcode(*x1, *y1, xmin, ymin, xmax, ymax);
+
+       long fx0, fy0, fx1, fy1, fxmin, fymin, fxmax, fymax;
+
+       if(!(oc0 | oc1)) return 1;      /* both points are inside */
+
+       fx0 = *x0 << 8;
+       fy0 = *y0 << 8;
+       fx1 = *x1 << 8;
+       fy1 = *y1 << 8;
+       fxmin = xmin << 8;
+       fymin = ymin << 8;
+       fxmax = xmax << 8;
+       fymax = ymax << 8;
+
+       for(;;) {
+               long x, y, t;
+
+               if(oc0 & oc1) return 0;         /* both have points with the same outbit, not visible */
+               if(!(oc0 | oc1)) break;         /* both points are inside */
+
+               oc_out = oc0 ? oc0 : oc1;
+
+               if(oc_out & TOP) {
+                       t = FIXDIV(fymin - fy0, fy1 - fy0);
+                       x = LERP(fx0, fx1, t);
+                       y = fymin;
+               } else if(oc_out & BOTTOM) {
+                       t = FIXDIV(fymax - fy0, fy1 - fy0);
+                       x = LERP(fx0, fx1, t);
+                       y = fymax;
+               } else if(oc_out & LEFT) {
+                       t = FIXDIV(fxmin - fx0, fx1 - fx0);
+                       x = fxmin;
+                       y = LERP(fy0, fy1, t);
+               } else {/*if(oc_out & RIGHT) {*/
+                       t = FIXDIV(fxmax - fx0, fx1 - fx0);
+                       x = fxmax;
+                       y = LERP(fy0, fy1, t);
+               }
+
+               if(oc_out == oc0) {
+                       fx0 = x;
+                       fy0 = y;
+                       oc0 = outcode(fx0 >> 8, fy0 >> 8, xmin, ymin, xmax, ymax);
+               } else {
+                       fx1 = x;
+                       fy1 = y;
+                       oc1 = outcode(fx1 >> 8, fy1 >> 8, xmin, ymin, xmax, ymax);
+               }
+       }
+
+       *x0 = fx0 >> 8;
+       *y0 = fy0 >> 8;
+       *x1 = fx1 >> 8;
+       *y1 = fy1 >> 8;
+       return 1;
+}
+
+#ifdef ALT_LCLIP
+#define PUTPIXEL(ptr) \
+       do { \
+               if(x0 >= 0 && x0 < fbwidth && y0 >= 0 && y0 < fbheight) { \
+                       uint16_t *pptr = (uint16_t*)((uint32_t)ptr & 0xfffffffe); \
+                       if((uint32_t)ptr & 1) { \
+                               *pptr = (*pptr & 0xff) | (color << 8); \
+                       } else { \
+                               *pptr = (*pptr & 0xff00) | color; \
+                       } \
+               } \
+       } while(0)
+#else  /* !ALT_LCLIP */
+#define PUTPIXEL(ptr) \
+       do { \
+               uint16_t *pptr = (uint16_t*)((uint32_t)ptr & 0xfffffffe); \
+               if((uint32_t)ptr & 1) { \
+                       *pptr = (*pptr & 0xff) | (color << 8); \
+               } else { \
+                       *pptr = (*pptr & 0xff00) | color; \
+               } \
+       } while(0)
+#endif
+
+void draw_line(int x0, int y0, int x1, int y1, unsigned short color)
+{
+       int i, dx, dy, x_inc, y_inc, error;
+#ifdef ALT_LCLIP
+       int y0inc;
+#endif
+       unsigned char *fbptr = fb;
+
+       fbptr += y0 * fbwidth + x0;
+
+       dx = x1 - x0;
+       dy = y1 - y0;
+
+       if(dx >= 0) {
+               x_inc = 1;
+       } else {
+               x_inc = -1;
+               dx = -dx;
+       }
+       if(dy >= 0) {
+               y_inc = fbwidth;
+#ifdef ALT_LCLIP
+               y0inc = 1;
+#endif
+       } else {
+               y_inc = -fbwidth;
+#ifdef ALT_LCLIP
+               y0inc = -1;
+#endif
+               dy = -dy;
+       }
+
+       if(dx > dy) {
+               error = dy * 2 - dx;
+               for(i=0; i<=dx; i++) {
+                       PUTPIXEL(fbptr);
+                       if(error >= 0) {
+                               error -= dx * 2;
+                               fbptr += y_inc;
+#ifdef ALT_LCLIP
+                               y0 += y0inc;
+#endif
+                       }
+                       error += dy * 2;
+                       fbptr += x_inc;
+#ifdef ALT_LCLIP
+                       x0 += x_inc;
+#endif
+               }
+       } else {
+               error = dx * 2 - dy;
+               for(i=0; i<=dy; i++) {
+                       PUTPIXEL(fbptr);
+                       if(error >= 0) {
+                               error -= dy * 2;
+                               fbptr += x_inc;
+#ifdef ALT_LCLIP
+                               x0 += x_inc;
+#endif
+                       }
+                       error += dx * 2;
+                       fbptr += y_inc;
+#ifdef ALT_LCLIP
+                       y0 += y0inc;
+#endif
+               }
+       }
+}
+
diff --git a/src/polyfill.h b/src/polyfill.h
new file mode 100644 (file)
index 0000000..88166cf
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef POLYFILL_H_
+#define POLYFILL_H_
+
+#include <stdint.h>
+
+struct pvertex {
+       int32_t x, y;
+};
+
+void polyfill_framebuffer(unsigned char *fb, int width, int height);
+void polyfill_flat(struct pvertex *v, int vnum, unsigned char col);
+
+int clip_line(int *x0, int *y0, int *x1, int *y1, int xmin, int ymin, int xmax, int ymax);
+void draw_line(int x0, int y0, int x1, int y1, unsigned short color);
+
+#endif /* POLYFILL_H_ */
diff --git a/src/sprite.c b/src/sprite.c
new file mode 100644 (file)
index 0000000..c77ae90
--- /dev/null
@@ -0,0 +1,77 @@
+#include "sprite.h"
+#include "gbaregs.h"
+
+
+void spr_setup(int xtiles, int ytiles, unsigned char *pixels, unsigned char *cmap)
+{
+       int i, j, num_tiles;
+       uint16_t *cptr, *src, *dst;
+
+       num_tiles = xtiles * ytiles;
+       dst = (uint16_t*)VRAM_LFB_OBJ_ADDR;
+       src = (uint16_t*)pixels;
+       for(i=0; i<num_tiles; i++) {
+               /* copy every row of tile i (8x8) */
+               for(j=0; j<8; j++) {
+                       *dst++ = src[j * 64];
+                       *dst++ = src[j * 64 + 1];
+               }
+               src += 2;
+
+               if((i & 31) == 31) {
+                       src += 7 * 64;  /* skip to the next row of tiles (skip 7 rows of pixels) */
+               }
+       }
+
+       cptr = (uint16_t*)CRAM_OBJ_ADDR;
+       for(i=0; i<16; i++) {
+               unsigned char r = *cmap++ >> 3;
+               unsigned char g = *cmap++ >> 3;
+               unsigned char b = *cmap++ >> 3;
+               *cptr++ = r | ((uint16_t)g << 5) | ((uint16_t)b << 10);
+       }
+}
+
+void spr_clear(void)
+{
+       int i;
+
+       for(i=0; i<128; i++) {
+               spr_oam_clear(0, i);
+       }
+}
+
+void spr_oam(uint16_t *oam, int idx, int spr, int x, int y, unsigned int flags)
+{
+       if(!oam) oam = (uint16_t*)OAM_ADDR;
+
+       oam += idx << 2;
+
+       oam[0] = (y & 0xff) | (flags & 0xff00);
+       oam[1] = (x & 0x1ff) | ((flags >> 8) & 0xfe00);
+       oam[2] = (spr & 0x3ff) | ((flags & 3) << 10);
+}
+
+void spr_spr_oam(uint16_t *oam, int idx, struct sprite *spr)
+{
+       int i;
+       struct hwsprite *s;
+
+       s = spr->hwspr;
+       for(i=0; i<spr->num_hwspr; i++) {
+               spr_oam(oam, idx, s->id, spr->x + s->x, spr->y + s->y, s->flags);
+               s++;
+       }
+}
+
+void spr_transform(uint16_t *oam, int idx, int16_t *mat)
+{
+       if(!oam) oam = (uint16_t*)OAM_ADDR;
+
+       oam += (idx << 4) + 3;
+
+       oam[0] = *mat++;
+       oam[4] = *mat++;
+       oam[8] = *mat++;
+       oam[12] = *mat;
+}
diff --git a/src/sprite.h b/src/sprite.h
new file mode 100644 (file)
index 0000000..27d16ff
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef SPRITE_H_
+#define SPRITE_H_
+
+#include <stdint.h>
+
+enum {
+       SPR_ROTSCL      = 0x0100,
+       SPR_DBLSZ       = 0x0200,
+       SPR_BLEND       = 0x0400,
+       SPR_OBJWIN      = 0x0800,
+       SPR_MOSAIC      = 0x1000,
+       SPR_256COL      = 0x2000,
+       SPR_HRECT       = 0x4000,
+       SPR_VRECT       = 0x8000,
+
+       SPR_HFLIP       = 0x100000,
+       SPR_VFLIP       = 0x200000,
+       SPR_SZ16        = 0x400000,
+       SPR_SZ32        = 0x800000,
+       SPR_SZ64        = 0xc00000
+};
+#define SPR_SZ8                0
+#define SPR_ROTSCL_SEL(x)      ((unsigned int)(x) << 17)
+#define SPR_PRIO(x)                    ((unsigned int)(x) & 3)
+
+
+struct hwsprite {
+       short id;
+       short width, height;
+       short x, y;
+       unsigned int flags;
+};
+
+struct sprite {
+       short x, y;
+       struct hwsprite hwspr[8];
+       short num_hwspr;
+};
+
+void spr_setup(int xtiles, int ytiles, unsigned char *pixels, unsigned char *cmap);
+void spr_clear(void);
+
+#define spr_oam_clear(oam, idx) spr_oam(oam, idx, 0, 0, 160, 0)
+void spr_oam(uint16_t *oam, int idx, int spr, int x, int y, unsigned int flags);
+void spr_spr_oam(uint16_t *oam, int idx, struct sprite *spr);
+
+/* idx is the rotation/scale parameter index (0-31), not the sprite index */
+void spr_transform(uint16_t *oam, int idx, int16_t *mat);
+
+
+#endif /* SPRITE_H_ */
diff --git a/src/timer.c b/src/timer.c
new file mode 100644 (file)
index 0000000..f4b0c7a
--- /dev/null
@@ -0,0 +1,55 @@
+#include "intr.h"
+#include "timer.h"
+
+#define F_CLK  16780000
+/* clock is 16.78MHz
+ * - no prescale: 59.595ns
+ * - prescale 64: 3.814us
+ * - prescale 256: 15.256us
+ * - prescale 1024: 61.025us
+ */
+
+static void timer_intr(void);
+
+void init_timer(int tm, unsigned long rate_hz, void (*intr)(void))
+{
+       static const unsigned long clk[] = {F_CLK, F_CLK / 64, F_CLK / 256, F_CLK / 1024};
+       unsigned long count;
+       int pscl = 0;
+
+       do {
+               count = clk[pscl] / rate_hz;
+       } while(count >= 65536 && ++pscl < 4);
+
+       if(pscl >= 4) return;   /* impossible rate */
+
+       REG_TMCNT_H(tm) = 0;
+       REG_TMCNT_L(tm) = 65536 - count;
+       if(intr) {
+               interrupt(INTR_TIMER0 + tm, intr);
+               unmask(INTR_TIMER0 + tm);
+               REG_TMCNT_H(tm) = TMCNT_IE;
+       }
+       REG_TMCNT_H(tm) |= TMCNT_EN | pscl;
+}
+
+void reset_msec_timer(void)
+{
+       REG_TM0CNT_H &= ~TMCNT_EN;
+       interrupt(INTR_TIMER0, timer_intr);
+       timer_msec = 0;
+       REG_TM0CNT_L = 65535 - 16779;
+       REG_TM0CNT_H |= TMCNT_IE | TMCNT_EN;
+       unmask(INTR_TIMER0);
+}
+
+void delay(unsigned long ms)
+{
+       unsigned long end = timer_msec + ms;
+       while(timer_msec < end);
+}
+
+static void timer_intr(void)
+{
+       timer_msec++;
+}
diff --git a/src/timer.h b/src/timer.h
new file mode 100644 (file)
index 0000000..3daed47
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#include "gbaregs.h"
+
+#define enable_timer(x) \
+       do { REG_TMCNT_H(x) |= TMCNT_EN; } while(0)
+
+#define disable_timer(x) \
+       do { REG_TMCNT_H(x) &= ~TMCNT_EN; } while(0)
+
+volatile unsigned long timer_msec;
+
+void init_timer(int tm, unsigned long rate_hz, void (*intr)(void));
+
+void reset_msec_timer(void);
+
+void delay(unsigned long ms);
+
+#ifdef __thumb__
+#define udelay(x)  asm volatile ( \
+       "0: sub %0, %0, #1\n\t" \
+       "bne 0b\n\t" \
+       :: "r"(x) : "cc")
+#else
+#define udelay(x)  asm volatile ( \
+       "0: subs %0, %0, #1\n\t" \
+       "bne 0b\n\t" \
+       :: "r"(x) : "cc")
+#endif
+
+#endif /* TIMER_H_ */
diff --git a/src/util.c b/src/util.c
new file mode 100644 (file)
index 0000000..9bba42f
--- /dev/null
@@ -0,0 +1,25 @@
+#include "util.h"
+#include "debug.h"
+
+extern char __iheap_start;
+static char *top = &__iheap_start;
+
+int iwram_brk(void *addr)
+{
+       if((char*)addr < &__iheap_start) {
+               addr = &__iheap_start;
+       }
+       if(addr > get_sp()) {
+               /*return -1;*/
+               panic(get_pc(), "iwram_brk (%p) >= sp", addr);
+       }
+       top = addr;
+       return 0;
+}
+
+void *iwram_sbrk(intptr_t delta)
+{
+       void *prev = top;
+       iwram_brk(top + delta);
+       return prev;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644 (file)
index 0000000..789e63e
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef UTIL_H_
+#define UTIL_H_
+
+#include <stdint.h>
+
+#define wait_vblank() \
+       do { \
+               while(REG_DISPSTAT & DISPSTAT_VBLANK); \
+               while(!(REG_DISPSTAT & DISPSTAT_VBLANK)); \
+       } while(0)
+
+#define present(x) \
+       do { \
+               REG_DISPCNT = DISPCNT_BG2 | DISPCNT_OBJ | 4 | ((x) << 4); \
+       } while(0)
+
+
+#define set_bg_color(idx, r, g, b) \
+       do { \
+               ((uint16_t*)CRAM_BG_ADDR)[idx] = (uint16_t)(r) | ((uint16_t)(g) << 5) | ((uint16_t)(b) << 10); \
+       } while(0)
+
+extern int16_t sinlut[];
+
+#define SIN(x) sinlut[(x) & 0xff]
+#define COS(x) sinlut[((x) + 64) & 0xff]
+
+int iwram_brk(void *addr);
+void *iwram_sbrk(intptr_t delta);
+
+void fillblock_16byte(void *dest, uint32_t val, int count);
+
+void *get_pc(void);
+void *get_sp(void);
+
+#endif /* UTIL_H_ */
diff --git a/src/xgl.c b/src/xgl.c
new file mode 100644 (file)
index 0000000..deccdea
--- /dev/null
+++ b/src/xgl.c
@@ -0,0 +1,294 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include <math.h>
+#include "xgl.h"
+#include "polyfill.h"
+#include "debug.h"
+
+#define MAT_STACK_SIZE 4
+
+static int vp[4];
+static int32_t mat[MAT_STACK_SIZE][16];
+static int mtop;
+static unsigned int opt;
+static int32_t ldir[3];
+
+static void draw_ptlines(int prim, const struct xvertex *varr, int vcount);
+
+
+void xgl_init(void)
+{
+       xgl_viewport(0, 0, 240, 160);
+       xgl_load_identity();
+
+       ldir[0] = ldir[1] = 0;
+       ldir[2] = -0x100;
+}
+
+void xgl_enable(unsigned int o)
+{
+       opt |= o;
+}
+
+void xgl_disable(unsigned int o)
+{
+       opt &= ~o;
+}
+
+void xgl_viewport(int x, int y, int w, int h)
+{
+       vp[0] = x;
+       vp[1] = y;
+       vp[2] = w;
+       vp[3] = h;
+}
+
+void xgl_push_matrix(void)
+{
+       int prev;
+
+       if(mtop >= MAT_STACK_SIZE - 1) return;
+
+       prev = mtop++;
+       memcpy(mat[mtop], mat[prev], sizeof mat[0]);
+}
+
+void xgl_pop_matrix(void)
+{
+       if(mtop > 0) mtop--;
+}
+
+static int32_t id[] = {
+       0x10000, 0, 0, 0,
+       0, 0x10000, 0, 0,
+       0, 0, 0x10000, 0,
+       0, 0, 0, 0x10000
+};
+
+void xgl_load_identity(void)
+{
+       memcpy(mat[mtop], id, sizeof mat[0]);
+}
+
+void xgl_load_matrix(const int32_t *m)
+{
+       memcpy(mat[mtop], m, sizeof mat[0]);
+}
+
+void xgl_get_matrix(int32_t *m)
+{
+       memcpy(m, mat[mtop], sizeof mat[0]);
+}
+
+#define M(i,j) (((i) << 2) + (j))
+#define XMUL(a, b)     (((a) >> 8) * ((b) >> 8))
+void xgl_mult_matrix(const int32_t *m2)
+{
+       int i, j;
+       int32_t m1[16];
+       int32_t *dest = mat[mtop];
+
+       memcpy(m1, dest, sizeof m1);
+
+       for(i=0; i<4; i++) {
+               for(j=0; j<4; j++) {
+                       *dest++ = XMUL(m1[M(0, j)], m2[M(i, 0)]) +
+                               XMUL(m1[M(1, j)], m2[M(i, 1)]) +
+                               XMUL(m1[M(2, j)], m2[M(i, 2)]) +
+                               XMUL(m1[M(3, j)], m2[M(i, 3)]);
+               }
+       }
+}
+
+#define XSIN(x)                (int32_t)(sin(x / 65536.0f) * 65536.0f)
+#define XCOS(x)                (int32_t)(cos(x / 65536.0f) * 65536.0f)
+
+void xgl_translate(int32_t x, int32_t y, int32_t z)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       m[12] = x;
+       m[13] = y;
+       m[14] = z;
+       xgl_mult_matrix(m);
+}
+
+void xgl_rotate_x(int32_t angle)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       int32_t sa = XSIN(angle);
+       int32_t ca = XCOS(angle);
+       m[5] = ca;
+       m[6] = sa;
+       m[9] = -sa;
+       m[10] = ca;
+       xgl_mult_matrix(m);
+}
+
+void xgl_rotate_y(int32_t angle)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       int32_t sa = XSIN(angle);
+       int32_t ca = XCOS(angle);
+       m[0] = ca;
+       m[2] = -sa;
+       m[8] = sa;
+       m[10] = ca;
+       xgl_mult_matrix(m);
+}
+
+void xgl_rotate_z(int32_t angle)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       int32_t sa = XSIN(angle);
+       int32_t ca = XCOS(angle);
+       m[0] = ca;
+       m[1] = sa;
+       m[4] = -sa;
+       m[5] = ca;
+       xgl_mult_matrix(m);
+}
+
+void xgl_scale(int32_t x, int32_t y, int32_t z)
+{
+       int32_t m[16] = {0};
+       m[0] = x;
+       m[5] = y;
+       m[10] = z;
+       m[15] = 0x10000;
+       xgl_mult_matrix(m);
+}
+
+static void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
+{
+       out->x = XMUL(m[0], in->x) + XMUL(m[4], in->y) + XMUL(m[8], in->z) + m[12];
+       out->y = XMUL(m[1], in->x) + XMUL(m[5], in->y) + XMUL(m[9], in->z) + m[13];
+       out->z = XMUL(m[2], in->x) + XMUL(m[6], in->y) + XMUL(m[10], in->z) + m[14];
+}
+
+static void xform_norm(struct xvertex *out, const struct xvertex *in, const int32_t *m)
+{
+       out->nx = XMUL(m[0], in->nx) + XMUL(m[4], in->ny) + XMUL(m[8], in->nz);
+       out->ny = XMUL(m[1], in->nx) + XMUL(m[5], in->ny) + XMUL(m[9], in->nz);
+       out->nz = XMUL(m[2], in->nx) + XMUL(m[6], in->ny) + XMUL(m[10], in->nz);
+}
+
+/* d = 1.0 / tan(fov/2) */
+#define PROJ_D 0x20000
+
+void xgl_draw(int prim, const struct xvertex *varr, int vcount)
+{
+       int i, cidx;
+       struct xvertex xv[4];
+       struct pvertex pv[4];
+       int32_t ndotl;
+
+       if(prim < 3) {
+               draw_ptlines(prim, varr, vcount);
+               return;
+       }
+
+       while(vcount >= prim) {
+               cidx = varr->cidx;
+
+               xform(xv, varr, mat[mtop]);
+               xform_norm(xv, varr, mat[mtop]);
+
+               if(xv->nz > 0) {
+                       /* backface */
+                       varr += prim;
+                       vcount -= prim;
+                       continue;
+               }
+
+               if(opt & XGL_LIGHTING) {
+                       ndotl = (xv->nx >> 8) * ldir[0] + (xv->ny >> 8) * ldir[1] + (xv->nz >> 8) * ldir[2];
+                       if(ndotl < 0) ndotl = 0;
+                       cidx = 128 + (ndotl >> 9);
+                       if(cidx > 255) cidx = 255;
+               }
+
+               xv->x = (xv->x << 1) / (xv->z >> 8);    /* assume aspect: ~2 */
+               xv->y = (xv->y << 2) / (xv->z >> 8);    /* the shift is * PROJ_D */
+               /* projection result is 24.8 */
+               /* viewport */
+               pv->x = (((xv->x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
+               pv->y = (((0x100 - xv->y) >> 1) * vp[3]) + (vp[1] << 8);
+               varr++;
+
+               for(i=1; i<prim; i++) {
+                       xform(xv + i, varr, mat[mtop]);
+
+                       xv[i].x = (xv[i].x << 1) / (xv[i].z >> 8);      /* assume aspect: ~2 */
+                       xv[i].y = (xv[i].y << 2) / (xv[i].z >> 8);      /* the shift is * PROJ_D */
+                       /* projection result is 24.8 */
+                       /* viewport */
+                       pv[i].x = (((xv[i].x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
+                       pv[i].y = (((0x100 - xv[i].y) >> 1) * vp[3]) + (vp[1] << 8);
+                       varr++;
+               }
+               vcount -= prim;
+
+               polyfill_flat(pv, prim, cidx);
+       }
+}
+
+void xgl_transform(const struct xvertex *vin, int *x, int *y)
+{
+       struct xvertex v;
+       xform(&v, vin, mat[mtop]);
+
+       v.x = (v.x << 1) / (v.z >> 8);  /* assume aspect: ~2 */
+       v.y = (v.y << 2) / (v.z >> 8);  /* the shift is * PROJ_D */
+       /* projection result is 24.8 */
+       /* viewport */
+       *x = ((((v.x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
+       *y = ((((0x100 - v.y) >> 1) * vp[3]) >> 8) + vp[1];
+}
+
+static void draw_ptlines(int prim, const struct xvertex *varr, int vcount)
+{
+       int i;
+       struct xvertex xv[2];
+
+       while(vcount >= prim) {
+               for(i=0; i<prim; i++) {
+                       xform(xv + i, varr, mat[mtop]);
+
+                       xv[i].x = (xv[i].x << 1) / (xv[i].z >> 8);      /* assume aspect: ~2 */
+                       xv[i].y = (xv[i].y << 2) / (xv[i].z >> 8);      /* the shift is * PROJ_D */
+                       /* projection result is 24.8 */
+                       /* viewport */
+                       xv[i].x = ((((xv[i].x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
+                       xv[i].y = ((((0x100 - xv[i].y) >> 1) * vp[3]) >> 8) + vp[1];
+                       varr++;
+               }
+               vcount -= prim;
+
+               /* line clipping */
+#ifndef ALT_LCLIP
+               clip_line((int*)&xv[0].x, (int*)&xv[0].y, (int*)&xv[1].x, (int*)&xv[1].y, vp[0], vp[1], vp[2] - 1, vp[3] - 1);
+#endif
+               draw_line(xv[0].x, xv[0].y, xv[1].x, xv[1].y, varr[-2].cidx);
+       }
+}
+
+void xgl_xyzzy(void)
+{
+       mat[mtop][12] = mat[mtop][13] = 0;
+}
diff --git a/src/xgl.h b/src/xgl.h
new file mode 100644 (file)
index 0000000..eba42b5
--- /dev/null
+++ b/src/xgl.h
@@ -0,0 +1,68 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef XGL_H_
+#define XGL_H_
+
+#define X_PI   0x3243f
+#define X_2PI  0x6487f
+#define X_HPI  0x19220
+#define X_QPI  0xc910
+
+enum {
+       XGL_LIGHTING    = 1,
+       XGL_DEPTH_TEST  = 2,
+};
+
+enum {
+       XGL_LINES               = 2,
+       XGL_TRIANGLES   = 3,
+       XGL_QUADS               = 4
+};
+
+struct xvertex {
+       int32_t x, y, z;
+       int32_t nx, ny, nz;
+       unsigned char cidx;
+};
+
+void xgl_init(void);
+
+void xgl_enable(unsigned int opt);
+void xgl_disable(unsigned int opt);
+
+void xgl_viewport(int x, int y, int w, int h);
+
+void xgl_push_matrix(void);
+void xgl_pop_matrix(void);
+void xgl_load_identity(void);
+void xgl_load_matrix(const int32_t *m);
+void xgl_get_matrix(int32_t *m);
+void xgl_mult_matrix(const int32_t *m);
+
+void xgl_translate(int32_t x, int32_t y, int32_t z);
+void xgl_rotate_x(int32_t angle);
+void xgl_rotate_y(int32_t angle);
+void xgl_rotate_z(int32_t angle);
+void xgl_scale(int32_t x, int32_t y, int32_t z);
+
+void xgl_draw(int prim, const struct xvertex *varr, int vcount);
+void xgl_transform(const struct xvertex *vin, int *x, int *y);
+
+void xgl_xyzzy(void);
+
+#endif /* XGL_H_ */
diff --git a/tools/lutgen.c b/tools/lutgen.c
new file mode 100644 (file)
index 0000000..70d7641
--- /dev/null
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <math.h>
+
+int main(void)
+{
+       int i;
+
+       puts("\t.data");
+       puts("\t.globl sinlut");
+       puts("sinlut:");
+       for(i=0; i<256; i++) {
+               float x = sin((float)i / 128.0f * M_PI);
+               printf("\t.short %d\n", (int)(x * 256.0f));
+       }
+       return 0;
+}