summaryrefslogtreecommitdiff
path: root/game/src
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2020-12-30 19:07:31 +0000
committerJuan J. Martinez <jjm@usebox.net>2020-12-30 19:23:41 +0000
commit2682bc5d1d864341aaeb42a449db73c3ecd16d70 (patch)
tree9116764364b4ee0ce7f6037305077807b57776de /game/src
downloadubox-msx-lib-ca9b663c147340e92804979a96eee4113ab0b27f.tar.gz
ubox-msx-lib-ca9b663c147340e92804979a96eee4113ab0b27f.zip
Initial import1.0
Diffstat (limited to 'game/src')
-rw-r--r--game/src/Makefile48
-rw-r--r--game/src/akm.z8015
-rw-r--r--game/src/aux.c27
-rw-r--r--game/src/aux.h13
-rw-r--r--game/src/crt0.z8082
-rw-r--r--game/src/data.c9
-rw-r--r--game/src/effects.asm249
-rw-r--r--game/src/effects_playerconfig.asm14
-rw-r--r--game/src/game.c437
-rw-r--r--game/src/game.h130
-rw-r--r--game/src/main.c143
-rw-r--r--game/src/main.h50
-rw-r--r--game/src/song.asm650
-rw-r--r--game/src/song_playerconfig.asm17
14 files changed, 1884 insertions, 0 deletions
diff --git a/game/src/Makefile b/game/src/Makefile
new file mode 100644
index 0000000..7fa9f2f
--- /dev/null
+++ b/game/src/Makefile
@@ -0,0 +1,48 @@
+TARGET=game
+
+CODE=0x4000
+# leaves 199 bytes for AKM player buffer
+DATA=0xc0de
+
+# HEX, will fill with 0
+ROM_MAX=8000
+
+OUTPUT=../build
+OBJS = $(patsubst %.c,$(OUTPUT)/%.rel,$(wildcard *.c)) $(OUTPUT)/akm.rel
+LIBS = -lubox -lspman -lmplayer
+
+CC=sdcc
+AS=sdasz80
+AR=sdcclib
+CFLAGS=-mz80 --Werror -I../../include -I../generated --fsigned-char --std-sdcc99 --opt-code-speed
+LDFLAGS=-L../../lib -L. --no-std-crt0 --fomit-frame-pointer
+
+all: $(OUTPUT)/$(TARGET).rom
+ @../../tools/chksize 8000 4000 $(OUTPUT)/$(TARGET).map
+ cp ../bin/$(TARGET).rom ../../bin
+
+openmsx: all
+ openmsx -carta $(OUTPUT)/$(TARGET).rom -machine msx1
+
+$(OUTPUT)/%.rel: %.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -c $< -o $@
+
+$(OUTPUT)/%.rel: %.z80
+ $(AS) -g -o $@ $<
+
+$(OUTPUT)/akm.rel: akm.z80 song.asm effects.asm
+ ../../bin/rasm akm.z80 -o $(OUTPUT)/akm -s -sl -sq
+ Disark --sourceProfile sdcc --symbolFile $(OUTPUT)/akm.sym --src16bitsValuesInHex --src8bitsValuesInHex --undocumentedOpcodesToBytes $(OUTPUT)/akm.bin $(OUTPUT)/akm_sdcc.asm
+ $(AS) -g -o $@ $(OUTPUT)/akm_sdcc.asm
+
+$(OUTPUT)/$(TARGET).rom: $(OBJS) $(OUTPUT)/crt0.rel ../../lib/*.lib
+ $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) --code-loc $(CODE) --data-loc $(DATA) $(OUTPUT)/crt0.rel $(OBJS) -o $(OUTPUT)/$(TARGET).ihx
+ ../../bin/hex2bin -e bin -p 00 -l $(ROM_MAX) $(OUTPUT)/$(TARGET).ihx
+ mv $(OUTPUT)/$(TARGET).bin ../bin/$(TARGET).rom
+
+clean:
+ rm -f $(OUTPUT)/*
+
+.PHONY: all clean
+
+include Makefile.deps
diff --git a/game/src/akm.z80 b/game/src/akm.z80
new file mode 100644
index 0000000..97c8498
--- /dev/null
+++ b/game/src/akm.z80
@@ -0,0 +1,15 @@
+;
+; to build the custom AKM player with song + effects
+;
+
+include "song_playerconfig.asm"
+include "effects_playerconfig.asm"
+
+include "../../src/mplayer/akm/akm_ubox.asm"
+
+songDisarkGenerateExternalLabel:
+include "song.asm"
+
+effectsDisarkGenerateExternalLabel:
+include "effects.asm"
+
diff --git a/game/src/aux.c b/game/src/aux.c
new file mode 100644
index 0000000..15fc2a4
--- /dev/null
+++ b/game/src/aux.c
@@ -0,0 +1,27 @@
+#include <stdint.h>
+
+#include "ubox.h"
+
+#include "aux.h"
+
+/**
+ * Put a zero terminated string on the screen using tiles.
+ *
+ * The font starts on the tileset on tile 128 and the fist char in our has
+ * ASCII value 31, so it is adjusted so we can use ASCII *uppercase* directly
+ * in our C code.
+ */
+void put_text(uint8_t x, uint8_t y, const uint8_t *text)
+{
+ while (*text)
+ ubox_put_tile(x++, y, *text++ + 128 - 31);
+}
+
+/**
+ * Wait for `frames` frames.
+ */
+void wait_for(uint8_t frames)
+{
+ while (frames--)
+ ubox_wait();
+}
diff --git a/game/src/aux.h b/game/src/aux.h
new file mode 100644
index 0000000..bf62d1a
--- /dev/null
+++ b/game/src/aux.h
@@ -0,0 +1,13 @@
+#ifndef _AUX_H
+#define _AUX_H
+
+#include <stdint.h>
+
+/**
+ * Auxiliary functions.
+ */
+
+void put_text(uint8_t x, uint8_t y, const uint8_t *text);
+void wait_for(uint8_t frames);
+
+#endif // _AUX_H
diff --git a/game/src/crt0.z80 b/game/src/crt0.z80
new file mode 100644
index 0000000..81763d0
--- /dev/null
+++ b/game/src/crt0.z80
@@ -0,0 +1,82 @@
+.module crt0
+.globl _main
+
+.area _HOME
+.area _CODE
+.area _INITIALIZER
+.area _GSINIT
+.area _GSFINAL
+
+.area _DATA
+.area _INITIALIZED
+.area _BSEG
+.area _BSS
+.area _HEAP
+
+.area _CODE
+
+ENASLT = 0x0024
+RSLREG = 0x0138
+CLIKSW = 0xf3db
+
+ ; ROM header
+ .str "AB"
+ .dw _main_init
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+
+_main_init::
+
+ ; init the stack
+ di
+ ld sp, #0xf380
+ ei
+
+ ; setup memory
+ ; ref: https://www.msx.org/forum/msx-talk/development/memory-pages-again
+
+ call RSLREG
+ rrca
+ rrca
+ and #3
+ ld c, a
+ add a, #0xc1
+ ld l, a
+ ld h, #0xfc
+ ld a, (hl)
+ and #0x80
+ or c
+ ld c, a
+ inc l
+ inc l
+ inc l
+ inc l
+ ld a, (hl)
+ and #0x0c
+ or c
+ ld h, #0x80
+ call ENASLT
+
+ ; disable key click sound
+ xor a
+ ld (CLIKSW), a
+
+ call gsinit
+ call _main
+
+halt0:
+ halt
+ jr halt0
+
+.area _GSINIT
+gsinit::
+ ld bc, #l__INITIALIZER
+ ld a, b
+ or a, c
+ jr Z, gsinit_next
+ ld de, #s__INITIALIZED
+ ld hl, #s__INITIALIZER
+ ldir
+gsinit_next:
+
+.area _GSFINAL
+ ret
diff --git a/game/src/data.c b/game/src/data.c
new file mode 100644
index 0000000..67bec4e
--- /dev/null
+++ b/game/src/data.c
@@ -0,0 +1,9 @@
+/**
+ * Module storing the generated data.
+ */
+
+#define LOCAL
+#include "tiles.h"
+#include "player.h"
+#include "enemy.h"
+#include "map.h"
diff --git a/game/src/effects.asm b/game/src/effects.asm
new file mode 100644
index 0000000..0ff8465
--- /dev/null
+++ b/game/src/effects.asm
@@ -0,0 +1,249 @@
+; Sound Effects for song Green, version 2.0, generated by Arkos Tracker 2.
+
+Green_SoundEffects
+Green_SoundEffectsDisarkGenerateExternalLabel
+
+; The sound effects, starting at 1.
+Green_SoundEffectsDisarkPointerRegionStart0
+ dw Green_SoundEffects_Sound1 ; Sound effect 1.
+ dw Green_SoundEffects_Sound2 ; Sound effect 2.
+ dw Green_SoundEffects_Sound3 ; Sound effect 3.
+ dw Green_SoundEffects_Sound4 ; Sound effect 4.
+ dw Green_SoundEffects_Sound5 ; Sound effect 5.
+Green_SoundEffectsDisarkPointerRegionEnd0
+
+Green_SoundEffectsDisarkByteRegionStart1
+; Sound effect 1.
+Green_SoundEffects_Sound1
+ db 0 ; Speed
+
+Green_SoundEffects_Sound1_Loop db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 128 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 256 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 256 ; Software period.
+
+ db 37 ; Soft only. Volume: 9.
+ dw 128 ; Software period.
+
+ db 37 ; Soft only. Volume: 9.
+ dw 128 ; Software period.
+
+ db 29 ; Soft only. Volume: 7.
+ dw 128 ; Software period.
+
+ db 29 ; Soft only. Volume: 7.
+ dw 128 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 2.
+Green_SoundEffects_Sound2
+ db 0 ; Speed
+
+ db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 96 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 112 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 112 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 128 ; Software period.
+
+ db 1 ; Soft only. Volume: 0.
+ dw 128 ; Software period.
+
+ db 1 ; Soft only. Volume: 0.
+ dw 128 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 96 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 96 ; Software period.
+
+Green_SoundEffects_Sound2_Loop db 1 ; Soft only. Volume: 0.
+ dw 96 ; Software period.
+
+ db 1 ; Soft only. Volume: 0.
+ dw 96 ; Software period.
+
+ db 25 ; Soft only. Volume: 6.
+ dw 128 ; Software period.
+
+ db 25 ; Soft only. Volume: 6.
+ dw 128 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 3.
+Green_SoundEffects_Sound3
+ db 0 ; Speed
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 177 ; Soft only. Volume: 12.
+ db 1 ; Noise: 1.
+ dw 128 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 128 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 96 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 96 ; Software period.
+
+ db 49 ; Soft only. Volume: 12.
+ dw 256 ; Software period.
+
+Green_SoundEffects_Sound3_Loop db 49 ; Soft only. Volume: 12.
+ dw 256 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 4.
+Green_SoundEffects_Sound4
+ db 0 ; Speed
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 256 ; Software period.
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 256 ; Software period.
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 512 ; Software period.
+
+ db 185 ; Soft only. Volume: 14.
+ db 1 ; Noise: 1.
+ dw 512 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 640 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 640 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 608 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 608 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 768 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 768 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 1024 ; Software period.
+
+Green_SoundEffects_Sound4_Loop db 33 ; Soft only. Volume: 8.
+ dw 1024 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+; Sound effect 5.
+Green_SoundEffects_Sound5
+ db 0 ; Speed
+
+ db 57 ; Soft only. Volume: 14.
+ dw 512 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 512 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 608 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 608 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 800 ; Software period.
+
+ db 57 ; Soft only. Volume: 14.
+ dw 800 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 768 ; Software period.
+
+ db 53 ; Soft only. Volume: 13.
+ dw 1024 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 1280 ; Software period.
+
+ db 45 ; Soft only. Volume: 11.
+ dw 1536 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 2048 ; Software period.
+
+ db 41 ; Soft only. Volume: 10.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 33 ; Soft only. Volume: 8.
+ dw 2048 ; Software period.
+
+ db 25 ; Soft only. Volume: 6.
+ dw 2048 ; Software period.
+
+Green_SoundEffects_Sound5_Loop db 25 ; Soft only. Volume: 6.
+ dw 2048 ; Software period.
+
+ db 4 ; End of the sound effect.
+
+Green_SoundEffectsDisarkByteRegionEnd1
diff --git a/game/src/effects_playerconfig.asm b/game/src/effects_playerconfig.asm
new file mode 100644
index 0000000..f9d189c
--- /dev/null
+++ b/game/src/effects_playerconfig.asm
@@ -0,0 +1,14 @@
+; Configuration that can be included to Arkos Tracker 2 players.
+; It indicates what parts of code are useful to the song/sound effects, to save both memory and CPU.
+; The players may or may not take advantage of these flags, it is up to them.
+
+; You can either:
+; - Include this to the source that also includes the player (BEFORE the player is included) (recommended solution).
+; - Include this at the beginning of the player code.
+; - Copy/paste this directly in the player.
+; If you use one player but several songs, don't worry, these declarations will stack up.
+; If no configuration is used, the player will use default values (full code used).
+
+ PLY_CFG_SFX_ConfigurationIsPresent = 1
+ PLY_CFG_SFX_SoftOnly = 1
+ PLY_CFG_SFX_SoftOnly_Noise = 1
diff --git a/game/src/game.c b/game/src/game.c
new file mode 100644
index 0000000..bc52b2a
--- /dev/null
+++ b/game/src/game.c
@@ -0,0 +1,437 @@
+#include <stdint.h>
+#include <string.h>
+
+#include "ubox.h"
+#include "spman.h"
+#include "mplayer.h"
+
+#include "aux.h"
+#include "main.h"
+
+#define LOCAL
+#include "game.h"
+
+// generated
+#include "map.h"
+#include "player.h"
+#include "enemy.h"
+
+void init_map_entities()
+{
+ const uint8_t *m = cur_map;
+ uint8_t typ, last = 0;
+ uint16_t i;
+
+ // init sprite and patterns
+ spman_init();
+
+ // this sets everything to 0, which is useful as
+ // entity ET_UNUSED is 0
+ memset(entities, 0, sizeof(struct entity) * MAX_ENTITIES);
+
+ // get to the beginning of the entities:
+ // map size + 3 bytes of header (the map size and the entities size)
+ m += (uint16_t)(m[0] | m[1] << 8) + 3;
+
+ // the entity list ends with 255
+ while (*m != 0xff)
+ {
+ // first byte is type + direction flag on MSB
+ // remove MSB
+ typ = m[0] & (~DIR_FLAG);
+
+ entities[last].type = typ;
+ entities[last].x = m[1];
+ entities[last].y = m[2];
+ // in the map: param is 1 (int) to look left
+ entities[last].dir = m[0] & DIR_FLAG ? 1 : 0;
+
+ switch (typ)
+ {
+ // can be only one; always first entity
+ // because our entities are sorted by type!
+ case ET_PLAYER:
+ // 3 frames x 2 sprites = 6
+ entities[last].pat = spman_alloc_pat(PAT_PLAYER, player_sprite[0], 6, 0);
+ spman_alloc_pat(PAT_PLAYER_FLIP, player_sprite[0], 6, 1);
+ entities[last].update = update_player;
+ break;
+
+ case ET_ENEMY:
+ // 3 frames
+ entities[last].pat = spman_alloc_pat(PAT_ENEMY, enemy_sprite[0], 3, 0);
+ spman_alloc_pat(PAT_ENEMY_FLIP, enemy_sprite[0], 3, 1);
+ entities[last].update = update_enemy;
+ break;
+ }
+
+ // next entity
+ last++;
+
+ // all our entities are 3 bytes
+ m += 3;
+ }
+
+ // count how many batteries are in the map
+ batteries = 0;
+ for (i = 0; i < MAP_W * MAP_H; ++i)
+ if (cur_map_data[i] == BATTERY_TILE)
+ batteries++;
+
+}
+
+void draw_map()
+{
+ // draw the map!
+ //
+ // - is not compressed (which limits how many maps we can store)
+ // - our map is just the tile numbers
+ //
+ // so we do it in one call by coping our map to the backtround tile map
+ // addr in the VDP VRAM
+ ubox_wait_vsync();
+ ubox_write_vm((uint8_t *)0x1800, MAP_W * MAP_H, cur_map_data);
+}
+
+void draw_hud()
+{
+ uint8_t i;
+
+ put_text(0, 21, "LIVES");
+
+ for (i = 0; i < MAX_LIVES; ++i)
+ if (i < lives)
+ // our hearts tile
+ ubox_put_tile(1 + i, 22, 193);
+ else
+ ubox_put_tile(1 + i, 22, WHITESPACE_TILE);
+}
+
+// x and y in pixels
+void erase_battery(uint8_t x, uint8_t y)
+{
+ uint8_t t;
+ int8_t mod;
+
+ // find out the bg tile to use
+
+ // border of the map
+ if ((x >> 3) == 0)
+ mod = 1;
+ else
+ mod = -1;
+
+ switch (cur_map_data[mod + (x >> 3) + (y >> 3) * MAP_W])
+ {
+ case 12:
+ t = 13;
+ break;
+ case 13:
+ t = 12;
+ break;
+ default:
+ // this is next to a wall
+ if (mod == 1)
+ t = 13;
+ else
+ t = 12;
+ break;
+ }
+
+ // change the map data so we don't pick it up again
+ cur_map_data[(x >> 3) + (y >> 3) * MAP_W] = t;
+
+ // erase on the screen
+ ubox_put_tile(x >> 3, y >> 3, t);
+ ubox_put_tile(x >> 3, (y >> 3) - 1, t);
+}
+
+// x and y in pixels
+uint8_t is_map_blocked(uint8_t x, uint8_t y)
+{
+ return cur_map_data[(x >> 3) + (y >> 3) * MAP_W] < LAST_SOLID_TILE + 1;
+}
+
+// x and y in pixels
+uint8_t is_map_battery(uint8_t x, uint8_t y)
+{
+ return cur_map_data[(x >> 3) + (y >> 3) * MAP_W] == BATTERY_TILE;
+}
+
+// x and y in pixels; always check the bottom tile!
+uint8_t is_map_elevator_down(uint8_t x, uint8_t y)
+{
+ uint8_t t = cur_map_data[(x >> 3) + (y >> 3) * MAP_W];
+
+ // first check the elevator platform comparing tiles
+ if (t != 9 && t != 10)
+ return 0;
+
+ // then check the elevator tube comparing tiles
+ t = cur_map_data[(x >> 3) + ((y >> 3) - 1) * MAP_W];
+ return (t != 14 && t != 15);
+}
+
+// x and y in pixels; always check the bottom tile!
+uint8_t is_map_elevator_up(uint8_t x, uint8_t y)
+{
+ uint8_t t = cur_map_data[(x >> 3) + (y >> 3) * MAP_W];
+
+ // first check the elevator platform comparing tiles
+ if (t != 9 && t != 10)
+ return 0;
+
+ // then check the elevator tube comparing tiles
+ t = cur_map_data[(x >> 3) + ((y >> 3) - 1) * MAP_W];
+ return (t == 14 || t == 15);
+}
+
+void update_enemy()
+{
+ // check for the player; if alive and not invulnerable!
+ // we use small hit boxes
+ if (lives && !invuln
+ && entities[0].x + 6 < self->x + 10 && self->x + 6 < entities[0].x + 10
+ && self->y == entities[0].y)
+ {
+ // change direction
+ self->dir ^= 1;
+
+ // remove one life (is more like "hits")
+ lives--;
+ draw_hud();
+ invuln = INVUL_TIME;
+
+ if (!lives)
+ {
+ // different sound effects if is game over
+ mplayer_init(SONG, SONG_SILENCE);
+ mplayer_play_effect_p(EFX_DEAD, EFX_CHAN_NO, 0);
+ gameover_delay = GAMEOVER_DELAY;
+ }
+ else
+ mplayer_play_effect_p(EFX_HIT, EFX_CHAN_NO, 0);
+ }
+
+ // left or right?
+ if (self->dir)
+ {
+ // change direction
+ if (self->x == 2 || is_map_blocked(self->x, self->y + 15))
+ self->dir ^= 1;
+ else
+ self->x -= 1;
+ }
+ else
+ {
+ // change direction
+ if (self->x == 255 - 16 || is_map_blocked(self->x + 15, self->y + 15))
+ self->dir ^= 1;
+ else
+ self->x += 1;
+ }
+
+ // update the walking animation
+ if (self->delay++ == FRAME_WAIT)
+ {
+ self->delay = 0;
+ if (++self->frame == WALK_CYCLE)
+ self->frame = 0;
+ }
+
+ // allocate the sprites
+ sp.x = self->x;
+ // y on the screen starts in 255
+ sp.y = self->y - 1;
+ // find which pattern to show
+ sp.pattern = self->pat + (walk_frames[self->frame] + self->dir * 3) * 4;
+ // red
+ sp.attr = 9;
+ spman_alloc_sprite(&sp);
+}
+
+void update_player()
+{
+ // to know if we need to update the walk animation
+ uint8_t moved = 0;
+
+ // player is dead
+ if (!lives)
+ return;
+
+ // decrease counter if set
+ if (invuln)
+ invuln--;
+
+ if (control & UBOX_MSX_CTL_RIGHT)
+ {
+ self->dir = DIR_RIGHT;
+ moved = 1;
+
+ // wrap horizontally
+ if (self->x == 255 - 16)
+ self->x = 0;
+ // check if not solid, using bottom right
+ else if (!is_map_blocked(self->x + 15, self->y + 15))
+ self->x += 2;
+ }
+
+ if (control & UBOX_MSX_CTL_LEFT)
+ {
+ self->dir = DIR_LEFT;
+ moved = 1;
+
+ // wrap horizontally
+ if (self->x == 2)
+ self->x = (uint8_t)(255 - 16);
+ // check if not solid, using bottom left
+ else if (!is_map_blocked(self->x, self->y + 15))
+ self->x -= 2;
+ }
+
+ // are we touching a battery?
+ // use bottom center
+ if (is_map_battery(self->x + 8, self->y + 15))
+ {
+ mplayer_play_effect_p(EFX_BATTERY, EFX_CHAN_NO, 0);
+ batteries--;
+ erase_battery(self->x + 8, self->y + 15);
+ }
+
+ if (control & UBOX_MSX_CTL_FIRE1)
+ {
+ // use flags to prevent repeat: the player will
+ // have to release fire to use the elevator again
+ if (!self->flags)
+ {
+ self->flags = 1;
+
+ // check elevator down; using bottom middle
+ if (is_map_elevator_down(self->x + 8, self->y + 16))
+ {
+ mplayer_play_effect_p(EFX_ELEVATOR, EFX_CHAN_NO, 0);
+ self->y += 8 * 4;
+ }
+ // then elevator up; using bottom middle
+ else if (is_map_elevator_up(self->x + 8, self->y + 16))
+ {
+ mplayer_play_effect_p(EFX_ELEVATOR, EFX_CHAN_NO, 0);
+ self->y -= 8 * 4;
+ }
+ }
+ }
+ else
+ {
+ if (self->flags)
+ self->flags = 0;
+ }
+
+ // it moved, or at least pushed against a wall
+ if (moved)
+ {
+ // update the walking animation
+ if (self->delay++ == FRAME_WAIT)
+ {
+ self->delay = 0;
+ if (++self->frame == WALK_CYCLE)
+ self->frame = 0;
+ }
+ }
+ else
+ {
+ // just stand
+ if (self->frame)
+ {
+ self->frame = 0;
+ self->delay = 0;
+ }
+ }
+
+ // if we are invulnerable, don't draw odd frames
+ // and we get a nice blinking effect
+ if (invuln & 1)
+ return;
+
+ // allocate the player sprites; fixed so they never flicker
+ sp.x = self->x;
+ // y on the screen starts in 255
+ sp.y = self->y - 1;
+ // find which pattern to show
+ sp.pattern = self->pat + (walk_frames[self->frame] + self->dir * 3) * 8;
+ // green
+ sp.attr = 12;
+ spman_alloc_fixed_sprite(&sp);
+ // second one is 4 patterns away (16x16 sprites)
+ sp.pattern += 4;
+ // white
+ sp.attr = 15;
+ spman_alloc_fixed_sprite(&sp);
+}
+
+void run_game()
+{
+ // init some variables; look at game.h for description
+ lives = MAX_LIVES;
+ invuln = 0;
+ gameover_delay = 0;
+
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ // we only have one map, select it
+ cur_map = map[0];
+ // copy map data into RAM, we will modify it
+ // map data starts on byte 3 (skip map data size and entities size)
+ memcpy(cur_map_data, cur_map + 3, MAP_W * MAP_H);
+
+ // init entities before drawing
+ init_map_entities();
+ draw_map();
+
+ draw_hud();
+
+ ubox_enable_screen();
+
+ mplayer_init(SONG, SONG_IN_GAME);
+
+ // our game loop
+ while (1)
+ {
+ // exit the game
+ if (ubox_read_keys(7) == UBOX_MSX_KEY_ESC)
+ break;
+
+ // game completed!
+ if (!batteries)
+ break;
+
+ // we are in the gameover delay
+ if (gameover_delay)
+ {
+ // if finished, exit
+ if (--gameover_delay == 0)
+ break;
+ }
+
+ // read the selected control
+ control = ubox_read_ctl(ctl);
+
+ // update all the entities:
+ // - self is a pointer to THIS entity
+ // - because we don't create/destroy entities dynamically
+ // when we found one that is unused we are done
+ for (self = entities; self->type; self++)
+ self->update();
+
+ // ensure we wait to our desired update rate
+ ubox_wait();
+ // update sprites on screen
+ spman_update();
+ }
+
+ // stop the in game music
+ mplayer_init(SONG, SONG_IN_GAME);
+ // hide all the sprites before going back to the menu
+ spman_hide_all_sprites();
+}
diff --git a/game/src/game.h b/game/src/game.h
new file mode 100644
index 0000000..9227987
--- /dev/null
+++ b/game/src/game.h
@@ -0,0 +1,130 @@
+#ifndef _GAME_H
+#define _GAME_H
+
+#ifndef LOCAL
+#define LOCAL extern
+#else
+
+#define WALK_CYCLE 4
+
+// walk animation frames, private
+const uint8_t walk_frames[WALK_CYCLE] = { 0, 1, 0, 2 };
+
+#endif
+
+/**
+ * Game implementation and helpers.
+ */
+
+// map size in tiles
+#define MAP_W 32
+#define MAP_H 21
+
+// some useful tiles
+#define LAST_SOLID_TILE 10
+#define BATTERY_TILE 224
+
+#define MAX_LIVES 3
+
+// player + other entities
+#define MAX_ENTITIES 11
+
+// MSB in our entity data
+#define DIR_FLAG 128
+
+// sprite direction
+#define DIR_LEFT 1
+#define DIR_RIGHT 0
+
+// how long we wait between animation frames
+#define FRAME_WAIT 3
+
+// how long the player is invulnerable after death
+// time in frames
+#define INVUL_TIME 64
+
+// show some game frames before jumping to the game over
+// screen; this helps the player to know what happened
+#define GAMEOVER_DELAY 72
+
+// types for our pattern groups
+// used by spman
+enum pattern_type
+{
+ PAT_PLAYER = 0,
+ PAT_PLAYER_FLIP,
+ PAT_ENEMY,
+ PAT_ENEMY_FLIP,
+};
+
+// entity types in the same order
+// used in the map (see map_conf.json)
+enum entity_type
+{
+ ET_UNUSED = 0,
+ ET_PLAYER,
+ ET_ENEMY,
+};
+
+// notes:
+//
+// - x, y are the logic position on the map
+// - sp.x, sp.y are the position on screen
+struct entity
+{
+ uint8_t type;
+ uint8_t x;
+ uint8_t y;
+ uint8_t dir;
+ uint8_t pat;
+ uint8_t flags;
+ uint8_t delay;
+ uint8_t frame;
+ void (*update)();
+};
+
+void run_game();
+
+void update_player();
+void update_enemy();
+
+void draw_map();
+void draw_hud();
+
+void erase_battery(uint8_t x, uint8_t y);
+
+uint8_t is_map_blocked(uint8_t x, uint8_t y);
+uint8_t is_map_elevator_up(uint8_t x, uint8_t y);
+uint8_t is_map_elevator_down(uint8_t x, uint8_t y);
+
+// our entities; the player is 0
+LOCAL struct entity entities[MAX_ENTITIES];
+
+// used to read our control method
+LOCAL uint8_t control;
+
+// current map; we don't use ROM because we will modify it
+LOCAL const uint8_t *cur_map;
+// current map tile map (map data, not entities)
+LOCAL uint8_t cur_map_data[MAP_W * MAP_H];
+
+// player lives
+LOCAL uint8_t lives;
+// invulnerability time after death
+LOCAL uint8_t invuln;
+// batteries left
+LOCAL uint8_t batteries;
+// show dome delay once the lives run out
+LOCAL uint8_t gameover_delay;
+
+// used by all entities
+LOCAL struct sprite_attr sp;
+
+// current entity
+LOCAL struct entity *self;
+
+#ifdef LOCAL
+#undef LOCAL
+#endif
+
+#endif // _GAME_H
diff --git a/game/src/main.c b/game/src/main.c
new file mode 100644
index 0000000..1405356
--- /dev/null
+++ b/game/src/main.c
@@ -0,0 +1,143 @@
+#include <stdint.h>
+
+#include "ubox.h"
+#include "mplayer.h"
+
+#include "aux.h"
+#include "game.h"
+
+#define LOCAL
+#include "main.h"
+
+// generated
+#include "tiles.h"
+
+void draw_menu()
+{
+ uint8_t i;
+
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ // game logo; starts in tile 32 and it is 10 x 3 tiles
+ for (i = 0; i < 10; ++i)
+ {
+ ubox_put_tile(11 + i, 6, 32 + i);
+ ubox_put_tile(11 + i, 7, 64 + i);
+ ubox_put_tile(11 + i, 8, 96 + i);
+ }
+
+ put_text(11, 11, "PRESS FIRE");
+
+ put_text(7, 2, "UBOX MSX LIB DEMO!");
+ put_text(4, 16, "CODE, GRAPHICS AND SOUND");
+ put_text(8, 17, "JUAN J. MARTINEZ");
+ // 037 is ASCII 31 in octal, our Copyright sign
+ put_text(8, 21, "\0372020 USEBOX.NET");
+
+ ubox_enable_screen();
+}
+
+void draw_end_game()
+{
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ put_text(3, 9, "WELL DONE!");
+ put_text(3, 10, "YOU HAVE FINISHED THE DEMO.");
+ put_text(3, 12, "NOW GO AND MAKE GAMES :)");
+
+ put_text(3, 14, "(PRESS ESC)");
+
+ ubox_enable_screen();
+
+ // wait until ESC is pressed
+ while (1)
+ {
+ if (ubox_read_keys(7) == UBOX_MSX_KEY_ESC)
+ break;
+
+ ubox_wait();
+ }
+}
+
+void draw_game_over()
+{
+ ubox_disable_screen();
+
+ ubox_fill_screen(WHITESPACE_TILE);
+ put_text(11, 10, "GAME OVER");
+
+ ubox_enable_screen();
+
+ // play game over music
+ mplayer_init(SONG, SONG_GAME_OVER);
+
+ wait_for(128);
+}
+
+void main()
+{
+ // PAL: 50/2 = 25 FPS
+ // NTSC: 60/2 = 30 FPS
+ ubox_init_isr(2);
+
+ // set screen 2
+ ubox_set_mode(2);
+ // all black
+ ubox_set_colors(1, 1, 1);
+
+ // avoid showing garbage on the screen
+ // while setting up the tiles
+ ubox_disable_screen();
+
+ // upload our tileset
+ ubox_set_tiles(tiles);
+ // and the colour information
+ ubox_set_tiles_colors(tiles_colors);
+
+ // clear the screen
+ ubox_fill_screen(WHITESPACE_TILE);
+
+ ubox_enable_screen();
+
+ // reg 1: activate sprites, v-blank int on, 16x16 sprites
+ ubox_wvdp(1, 0xe2);
+
+ // init the player
+ mplayer_init(SONG, SONG_SILENCE);
+ mplayer_init_effects(EFFECTS);
+
+ // we don't need anything in our ISR
+ // other than the play function, so we
+ // use that!
+ ubox_set_user_isr(mplayer_play);
+
+redraw_menu:
+ draw_menu();
+
+ // whait until fire is pressed
+ // can be space (control will be cursors), or any fire button on a joystick
+ while (1)
+ {
+ ctl = ubox_select_ctl();
+ if (ctl != UBOX_MSX_CTL_NONE)
+ {
+ mplayer_play_effect_p(EFX_START, EFX_CHAN_NO, 0);
+ wait_for(16);
+
+ // play the game
+ run_game();
+
+ if (!lives)
+ draw_game_over();
+ else if (!batteries)
+ draw_end_game();
+
+ goto redraw_menu;
+ }
+ ubox_wait();
+ }
+}
diff --git a/game/src/main.h b/game/src/main.h
new file mode 100644
index 0000000..f8d5d77
--- /dev/null
+++ b/game/src/main.h
@@ -0,0 +1,50 @@
+#ifndef _MAIN_H
+#define _MAIN_H
+
+#include <stdint.h>
+
+#ifndef LOCAL
+#define LOCAL extern
+#endif
+
+#define WHITESPACE_TILE 129
+
+// sub-songs matching our Arkos song
+// configure the song to use MSX AY
+enum songs
+{
+ SONG_SILENCE = 0,
+ SONG_IN_GAME,
+ SONG_GAME_OVER,
+};
+
+// we will use 0 and 1 for the music
+#define EFX_CHAN_NO 2
+
+// sound effects matching our Arkos efc song
+// configure the song to use MSX AY
+enum effects
+{
+ EFX_NONE = 0,
+ EFX_START,
+ EFX_BATTERY,
+ EFX_ELEVATOR,
+ EFX_HIT,
+ EFX_DEAD,
+};
+
+void draw_end_game();
+void draw_menu();
+
+// store the selected control
+LOCAL uint8_t ctl;
+
+// Arkos data
+extern uint8_t SONG[];
+extern uint8_t EFFECTS[];
+
+#ifdef LOCAL
+#undef LOCAL
+#endif
+
+#endif // _MAIN_H
diff --git a/game/src/song.asm b/game/src/song.asm
new file mode 100644
index 0000000..de3ca50
--- /dev/null
+++ b/game/src/song.asm
@@ -0,0 +1,650 @@
+; Green, Song part, encoded in the AKM (minimalist) format V0.
+
+
+Green_Start
+Green_StartDisarkGenerateExternalLabel
+
+Green_DisarkPointerRegionStart0
+ dw Green_InstrumentIndexes ; Index table for the Instruments.
+Green_DisarkForceNonReferenceDuring2_1
+ dw 0 ; Index table for the Arpeggios.
+Green_DisarkForceNonReferenceDuring2_2
+ dw 0 ; Index table for the Pitches.
+
+; The subsongs references.
+ dw Green_Subsong0
+ dw Green_Subsong1
+ dw Green_Subsong2
+Green_DisarkPointerRegionEnd0
+
+; The Instrument indexes.
+Green_InstrumentIndexes
+Green_DisarkPointerRegionStart3
+ dw Green_Instrument0
+ dw Green_Instrument1
+ dw Green_Instrument2
+ dw Green_Instrument3
+ dw Green_Instrument4
+Green_DisarkPointerRegionEnd3
+
+; The Instrument.
+Green_DisarkByteRegionStart4
+Green_Instrument0
+ db 255 ; Speed.
+
+Green_Instrument0Loop db 0 ; Volume: 0.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart5
+ dw Green_Instrument0Loop ; Loops.
+Green_DisarkPointerRegionEnd5
+
+Green_Instrument1
+ db 0 ; Speed.
+
+ db 173 ; Volume: 11.
+ db 232 ; Arpeggio: -12.
+
+ db 45 ; Volume: 11.
+
+ db 45 ; Volume: 11.
+
+ db 45 ; Volume: 11.
+
+ db 45 ; Volume: 11.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart6
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd6
+
+Green_Instrument2
+ db 0 ; Speed.
+
+ db 173 ; Volume: 11.
+ db 232 ; Arpeggio: -12.
+
+ db 45 ; Volume: 11.
+
+ db 173 ; Volume: 11.
+ db 232 ; Arpeggio: -12.
+
+ db 45 ; Volume: 11.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart7
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd7
+
+Green_Instrument3
+ db 0 ; Speed.
+
+ db 177 ; Volume: 12.
+ db 27 ; Arpeggio: 13.
+ db 9 ; Noise: 9.
+
+ db 173 ; Volume: 11.
+ db 18 ; Arpeggio: 9.
+
+ db 169 ; Volume: 10.
+ db 12 ; Arpeggio: 6.
+
+ db 165 ; Volume: 9.
+ db 9 ; Arpeggio: 4.
+ db 5 ; Noise: 5.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart8
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd8
+
+Green_Instrument4
+ db 0 ; Speed.
+
+ db 113 ; Volume: 12.
+ dw 2 ; Pitch: 2.
+
+ db 45 ; Volume: 11.
+
+ db 41 ; Volume: 10.
+
+ db 37 ; Volume: 9.
+
+ db 33 ; Volume: 8.
+
+ db 29 ; Volume: 7.
+
+ db 25 ; Volume: 6.
+
+ db 21 ; Volume: 5.
+
+ db 4 ; End the instrument.
+Green_DisarkPointerRegionStart9
+ dw Green_Instrument0Loop ; Loop to silence.
+Green_DisarkPointerRegionEnd9
+
+Green_DisarkByteRegionEnd4
+Green_ArpeggioIndexes
+Green_DisarkPointerRegionStart10
+Green_DisarkPointerRegionEnd10
+
+Green_DisarkByteRegionStart11
+Green_DisarkByteRegionEnd11
+
+Green_PitchIndexes
+Green_DisarkPointerRegionStart12
+Green_DisarkPointerRegionEnd12
+
+Green_DisarkByteRegionStart13
+Green_DisarkByteRegionEnd13
+
+; Green, Subsong 0.
+; ----------------------------------
+
+Green_Subsong0
+Green_Subsong0DisarkPointerRegionStart0
+ dw Green_Subsong0_NoteIndexes ; Index table for the notes.
+ dw Green_Subsong0_TrackIndexes ; Index table for the Tracks.
+Green_Subsong0DisarkPointerRegionEnd0
+
+Green_Subsong0DisarkByteRegionStart1
+ db 6 ; Initial speed.
+
+ db 2 ; Most used instrument.
+ db 4 ; Second most used instrument.
+
+ db 1 ; Most used wait.
+ db 3 ; Second most used wait.
+
+ db 0 ; Default start note in tracks.
+ db 0 ; Default start instrument in tracks.
+ db 0 ; Default start wait in tracks.
+
+ db 13 ; Are there effects? 12 if yes, 13 if not. Don't ask.
+Green_Subsong0DisarkByteRegionEnd1
+
+; The Linker.
+Green_Subsong0DisarkByteRegionStart2
+; Pattern 0
+Green_Subsong0_Loop
+ db 170 ; State byte.
+ db 63 ; New height.
+ db 128 ; New track (0) for channel 1, as a reference (index 0).
+ db 128 ; New track (0) for channel 2, as a reference (index 0).
+ db 128 ; New track (0) for channel 3, as a reference (index 0).
+
+ db 1 ; End of the Song.
+ db 0 ; Speed to 0, meaning "end of song".
+Green_Subsong0DisarkByteRegionEnd2
+Green_Subsong0DisarkPointerRegionStart3
+ dw Green_Subsong0_Loop
+
+Green_Subsong0DisarkPointerRegionEnd3
+; The indexes of the tracks.
+Green_Subsong0_TrackIndexes
+Green_Subsong0DisarkPointerRegionStart4
+ dw Green_Subsong0_Track0 ; Track 0, index 0.
+Green_Subsong0DisarkPointerRegionEnd4
+
+Green_Subsong0DisarkByteRegionStart5
+Green_Subsong0_Track0
+ db 205 ; New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong0DisarkByteRegionEnd5
+; The note indexes.
+Green_Subsong0_NoteIndexes
+Green_Subsong0DisarkByteRegionStart6
+Green_Subsong0DisarkByteRegionEnd6
+
+; Green, Subsong 1.
+; ----------------------------------
+
+Green_Subsong1
+Green_Subsong1DisarkPointerRegionStart0
+ dw Green_Subsong1_NoteIndexes ; Index table for the notes.
+ dw Green_Subsong1_TrackIndexes ; Index table for the Tracks.
+Green_Subsong1DisarkPointerRegionEnd0
+
+Green_Subsong1DisarkByteRegionStart1
+ db 6 ; Initial speed.
+
+ db 2 ; Most used instrument.
+ db 4 ; Second most used instrument.
+
+ db 1 ; Most used wait.
+ db 3 ; Second most used wait.
+
+ db 57 ; Default start note in tracks.
+ db 0 ; Default start instrument in tracks.
+ db 0 ; Default start wait in tracks.
+
+ db 13 ; Are there effects? 12 if yes, 13 if not. Don't ask.
+Green_Subsong1DisarkByteRegionEnd1
+
+; The Linker.
+Green_Subsong1DisarkByteRegionStart2
+; Pattern 0
+Green_Subsong1_Loop
+ db 170 ; State byte.
+ db 63 ; New height.
+ db ((Green_Subsong1_Track0 - ($ + 2)) & #ff00) / 256 ; New track (0) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track0 - ($ + 1)) & 255)
+ db ((Green_Subsong1_Track1 - ($ + 2)) & #ff00) / 256 ; New track (1) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track1 - ($ + 1)) & 255)
+ db 128 ; New track (2) for channel 3, as a reference (index 0).
+
+; Pattern 1
+ db 32 ; State byte.
+ db ((Green_Subsong1_Track3 - ($ + 2)) & #ff00) / 256 ; New track (3) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track3 - ($ + 1)) & 255)
+
+; Pattern 2
+ db 40 ; State byte.
+ db ((Green_Subsong1_Track4 - ($ + 2)) & #ff00) / 256 ; New track (4) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track4 - ($ + 1)) & 255)
+ db ((Green_Subsong1_Track1 - ($ + 2)) & #ff00) / 256 ; New track (1) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track1 - ($ + 1)) & 255)
+
+; Pattern 3
+ db 40 ; State byte.
+ db ((Green_Subsong1_Track5 - ($ + 2)) & #ff00) / 256 ; New track (5) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track5 - ($ + 1)) & 255)
+ db ((Green_Subsong1_Track3 - ($ + 2)) & #ff00) / 256 ; New track (3) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong1_Track3 - ($ + 1)) & 255)
+
+ db 1 ; End of the Song.
+ db 0 ; Speed to 0, meaning "end of song".
+Green_Subsong1DisarkByteRegionEnd2
+Green_Subsong1DisarkPointerRegionStart3
+ dw Green_Subsong1_Loop
+
+Green_Subsong1DisarkPointerRegionEnd3
+; The indexes of the tracks.
+Green_Subsong1_TrackIndexes
+Green_Subsong1DisarkPointerRegionStart4
+ dw Green_Subsong1_Track2 ; Track 2, index 0.
+Green_Subsong1DisarkPointerRegionEnd4
+
+Green_Subsong1DisarkByteRegionStart5
+Green_Subsong1_Track0
+ db 222 ; Primary instrument (2). New escaped note: 69. New wait (11).
+ db 69 ; Escape note value.
+ db 11 ; Escape wait value.
+ db 95 ; Primary instrument (2). Same escaped note: 69. Primary wait (1).
+ db 94 ; Primary instrument (2). New escaped note: 60. Primary wait (1).
+ db 60 ; Escape note value.
+ db 30 ; Primary instrument (2). New escaped note: 62.
+ db 62 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 62. Primary wait (1).
+ db 62 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 67. New wait (15).
+ db 67 ; Escape note value.
+ db 15 ; Escape wait value.
+ db 95 ; Primary instrument (2). Same escaped note: 67. Primary wait (1).
+ db 95 ; Primary instrument (2). Same escaped note: 67. Primary wait (1).
+ db 95 ; Primary instrument (2). Same escaped note: 67. Primary wait (1).
+ db 94 ; Primary instrument (2). New escaped note: 65. Primary wait (1).
+ db 65 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 62. Secondary wait (3).
+ db 62 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 60. New wait (127).
+ db 60 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track1
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 142 ; New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 126 ; New instrument (1). New escaped note: 31. Primary wait (1).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 254 ; New instrument (1). New escaped note: 27. New wait (127).
+ db 27 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track2
+ db 205 ; New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track3
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 38. Primary wait (1).
+ db 38 ; Escape note value.
+ db 142 ; New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 24. Primary wait (1).
+ db 24 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 190 ; New instrument (1). New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 43. Primary wait (1).
+ db 43 ; Escape note value.
+ db 142 ; New escaped note: 31. Secondary wait (3).
+ db 31 ; Escape note value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 126 ; New instrument (3). New escaped note: 36. Primary wait (1).
+ db 36 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 143 ; Same escaped note: 36. Secondary wait (3).
+ db 207 ; Same escaped note: 36. New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track4
+ db 111 ; Secondary instrument (4). Same escaped note: 57. Primary wait (1).
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 70. Primary wait (1).
+ db 70 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 69. Secondary wait (3).
+ db 69 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 65. Secondary wait (3).
+ db 65 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 62. Secondary wait (3).
+ db 62 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 69. Secondary wait (3).
+ db 69 ; Escape note value.
+ db 95 ; Primary instrument (2). Same escaped note: 69. Primary wait (1).
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 65. Primary wait (1).
+ db 65 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 64. New wait (127).
+ db 64 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1_Track5
+ db 111 ; Secondary instrument (4). Same escaped note: 57. Primary wait (1).
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 70. Primary wait (1).
+ db 70 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 69. Primary wait (1).
+ db 69 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 69. Secondary wait (3).
+ db 69 ; Escape note value.
+ db 158 ; Primary instrument (2). New escaped note: 65. Secondary wait (3).
+ db 65 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 57. Primary wait (1).
+ db 57 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 50. Primary wait (1).
+ db 50 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 48. Primary wait (1).
+ db 48 ; Escape note value.
+ db 222 ; Primary instrument (2). New escaped note: 62. New wait (5).
+ db 62 ; Escape note value.
+ db 5 ; Escape wait value.
+ db 94 ; Primary instrument (2). New escaped note: 65. Primary wait (1).
+ db 65 ; Escape note value.
+ db 94 ; Primary instrument (2). New escaped note: 67. Primary wait (1).
+ db 67 ; Escape note value.
+ db 159 ; Primary instrument (2). Same escaped note: 67. Secondary wait (3).
+ db 222 ; Primary instrument (2). New escaped note: 65. New wait (127).
+ db 65 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong1DisarkByteRegionEnd5
+; The note indexes.
+Green_Subsong1_NoteIndexes
+Green_Subsong1DisarkByteRegionStart6
+Green_Subsong1DisarkByteRegionEnd6
+
+; Green, Subsong 2.
+; ----------------------------------
+
+Green_Subsong2
+Green_Subsong2DisarkPointerRegionStart0
+ dw Green_Subsong2_NoteIndexes ; Index table for the notes.
+ dw Green_Subsong2_TrackIndexes ; Index table for the Tracks.
+Green_Subsong2DisarkPointerRegionEnd0
+
+Green_Subsong2DisarkByteRegionStart1
+ db 6 ; Initial speed.
+
+ db 2 ; Most used instrument.
+ db 4 ; Second most used instrument.
+
+ db 1 ; Most used wait.
+ db 3 ; Second most used wait.
+
+ db 31 ; Default start note in tracks.
+ db 3 ; Default start instrument in tracks.
+ db 0 ; Default start wait in tracks.
+
+ db 13 ; Are there effects? 12 if yes, 13 if not. Don't ask.
+Green_Subsong2DisarkByteRegionEnd1
+
+; The Linker.
+Green_Subsong2DisarkByteRegionStart2
+; Pattern 0
+ db 170 ; State byte.
+ db 63 ; New height.
+ db ((Green_Subsong2_Track0 - ($ + 2)) & #ff00) / 256 ; New track (0) for channel 1, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong2_Track0 - ($ + 1)) & 255)
+ db ((Green_Subsong2_Track1 - ($ + 2)) & #ff00) / 256 ; New track (1) for channel 2, as an offset. Offset MSB, then LSB.
+ db ((Green_Subsong2_Track1 - ($ + 1)) & 255)
+ db 128 ; New track (2) for channel 3, as a reference (index 0).
+
+; Pattern 1
+Green_Subsong2_Loop
+ db 40 ; State byte.
+ db 128 ; New track (2) for channel 1, as a reference (index 0).
+ db 128 ; New track (2) for channel 2, as a reference (index 0).
+
+ db 1 ; End of the Song.
+ db 0 ; Speed to 0, meaning "end of song".
+Green_Subsong2DisarkByteRegionEnd2
+Green_Subsong2DisarkPointerRegionStart3
+ dw Green_Subsong2_Loop
+
+Green_Subsong2DisarkPointerRegionEnd3
+; The indexes of the tracks.
+Green_Subsong2_TrackIndexes
+Green_Subsong2DisarkPointerRegionStart4
+ dw Green_Subsong2_Track2 ; Track 2, index 0.
+Green_Subsong2DisarkPointerRegionEnd4
+
+Green_Subsong2DisarkByteRegionStart5
+Green_Subsong2_Track0
+ db 110 ; Secondary instrument (4). New escaped note: 55. Primary wait (1).
+ db 55 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 54. Primary wait (1).
+ db 54 ; Escape note value.
+ db 110 ; Secondary instrument (4). New escaped note: 53. Primary wait (1).
+ db 53 ; Escape note value.
+ db 46 ; Secondary instrument (4). New escaped note: 51.
+ db 51 ; Escape note value.
+ db 46 ; Secondary instrument (4). New escaped note: 49.
+ db 49 ; Escape note value.
+ db 174 ; Secondary instrument (4). New escaped note: 50. Secondary wait (3).
+ db 50 ; Escape note value.
+ db 175 ; Secondary instrument (4). Same escaped note: 50. Secondary wait (3).
+ db 238 ; Secondary instrument (4). New escaped note: 55. New wait (7).
+ db 55 ; Escape note value.
+ db 7 ; Escape wait value.
+ db 222 ; Primary instrument (2). New escaped note: 43. New wait (127).
+ db 43 ; Escape note value.
+ db 127 ; Escape wait value.
+
+Green_Subsong2_Track1
+ db 79 ; Same escaped note: 31. Primary wait (1).
+ db 126 ; New instrument (1). New escaped note: 30. Primary wait (1).
+ db 30 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 78 ; New escaped note: 29. Primary wait (1).
+ db 29 ; Escape note value.
+ db 78 ; New escaped note: 28. Primary wait (1).
+ db 28 ; Escape note value.
+ db 190 ; New instrument (3). New escaped note: 26. Secondary wait (3).
+ db 26 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 191 ; New instrument (1). Same escaped note: 26. Secondary wait (3).
+ db 1 ; Escape instrument value.
+ db 254 ; New instrument (3). New escaped note: 43. New wait (7).
+ db 43 ; Escape note value.
+ db 3 ; Escape instrument value.
+ db 7 ; Escape wait value.
+ db 254 ; New instrument (1). New escaped note: 31. New wait (127).
+ db 31 ; Escape note value.
+ db 1 ; Escape instrument value.
+ db 127 ; Escape wait value.
+
+Green_Subsong2_Track2
+ db 205 ; New wait (127).
+ db 127 ; Escape wait value.
+
+Green_Subsong2DisarkByteRegionEnd5
+; The note indexes.
+Green_Subsong2_NoteIndexes
+Green_Subsong2DisarkByteRegionStart6
+Green_Subsong2DisarkByteRegionEnd6
+
diff --git a/game/src/song_playerconfig.asm b/game/src/song_playerconfig.asm
new file mode 100644
index 0000000..9e7821f
--- /dev/null
+++ b/game/src/song_playerconfig.asm
@@ -0,0 +1,17 @@
+; Configuration that can be included to Arkos Tracker 2 players.
+; It indicates what parts of code are useful to the song/sound effects, to save both memory and CPU.
+; The players may or may not take advantage of these flags, it is up to them.
+
+; You can either:
+; - Include this to the source that also includes the player (BEFORE the player is included) (recommended solution).
+; - Include this at the beginning of the player code.
+; - Copy/paste this directly in the player.
+; If you use one player but several songs, don't worry, these declarations will stack up.
+; If no configuration is used, the player will use default values (full code used).
+
+ PLY_CFG_ConfigurationIsPresent = 1
+ PLY_CFG_NoSoftNoHard = 1
+ PLY_CFG_SoftOnly = 1
+ PLY_CFG_SoftOnly_Noise = 1
+ PLY_CFG_SoftOnly_SoftwareArpeggio = 1
+ PLY_CFG_SoftOnly_SoftwarePitch = 1