├── Makefile ├── Makefile.win ├── README.md ├── example.prg ├── example.sid ├── fileio.c ├── fileio.h ├── gcommon.h ├── gt2mini.c ├── musicdata.s ├── musicmodule.s ├── mw4title.sng ├── player.s ├── prgexample.s └── sidexample.s /Makefile: -------------------------------------------------------------------------------- 1 | all: gt2mini example.prg example.sid 2 | 3 | clean: 4 | rm -f gt2mini 5 | rm -f *.prg 6 | rm -f *.sid 7 | 8 | gt2mini: gt2mini.c fileio.c 9 | gcc gt2mini.c fileio.c -o gt2mini 10 | 11 | example.prg: gt2mini mw4title.sng prgexample.s player.s 12 | ./gt2mini mw4title.sng musicmodule.s -s1000 13 | dasm musicmodule.s -omusicmodule.bin -p3 -f3 14 | dasm prgexample.s -oexample.prg -p3 15 | 16 | example.sid: gt2mini mw4title.sng sidexample.s player.s 17 | ./gt2mini mw4title.sng musicdata.s -b 18 | dasm sidexample.s -oexample.sid -p3 -f3 -------------------------------------------------------------------------------- /Makefile.win: -------------------------------------------------------------------------------- 1 | all: gt2mini.exe example.prg example.sid 2 | 3 | clean: 4 | del *.exe 5 | del *.prg 6 | del *.sid 7 | 8 | gt2mini.exe: gt2mini.c fileio.c 9 | gcc gt2mini.c fileio.c -ogt2mini.exe 10 | 11 | example.prg: gt2mini.exe mw4title.sng prgexample.s player.s 12 | gt2mini mw4title.sng musicmodule.s -s1000 13 | dasm musicmodule.s -omusicmodule.bin -p3 -f3 14 | dasm prgexample.s -oexample.prg -p3 15 | 16 | example.sid: gt2mini.exe mw4title.sng sidexample.s player.s 17 | gt2mini mw4title.sng musicdata.s -b 18 | dasm sidexample.s -oexample.sid -p3 -f3 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cadaver/miniplayer/702e28d60f94397f6f0be76fe71566458af67906/README.md -------------------------------------------------------------------------------- /example.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cadaver/miniplayer/702e28d60f94397f6f0be76fe71566458af67906/example.prg -------------------------------------------------------------------------------- /example.sid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cadaver/miniplayer/702e28d60f94397f6f0be76fe71566458af67906/example.sid -------------------------------------------------------------------------------- /fileio.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void fwrite8(FILE *file, unsigned data) 4 | { 5 | unsigned char bytes[1]; 6 | 7 | bytes[0] = data; 8 | fwrite(bytes, 1, 1, file); 9 | } 10 | 11 | void fwritele16(FILE *file, unsigned data) 12 | { 13 | unsigned char bytes[2]; 14 | 15 | bytes[0] = data; 16 | bytes[1] = data >> 8; 17 | fwrite(bytes, 2, 1, file); 18 | } 19 | 20 | void fwritele32(FILE *file, unsigned data) 21 | { 22 | unsigned char bytes[4]; 23 | 24 | bytes[0] = data; 25 | bytes[1] = data >> 8; 26 | bytes[2] = data >> 16; 27 | bytes[3] = data >> 24; 28 | fwrite(bytes, 4, 1, file); 29 | } 30 | 31 | unsigned fread8(FILE *file) 32 | { 33 | unsigned char bytes[1]; 34 | 35 | fread(bytes, 1, 1, file); 36 | return bytes[0]; 37 | } 38 | 39 | unsigned freadle16(FILE *file) 40 | { 41 | unsigned char bytes[2]; 42 | 43 | fread(bytes, 2, 1, file); 44 | return (bytes[0]) | (bytes[1] << 8); 45 | } 46 | 47 | unsigned freadle32(FILE *file) 48 | { 49 | unsigned char bytes[4]; 50 | 51 | fread(bytes, 4, 1, file); 52 | return (bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); 53 | } 54 | 55 | unsigned freadhe16(FILE *file) 56 | { 57 | unsigned char bytes[2]; 58 | 59 | fread(bytes, 2, 1, file); 60 | return (bytes[1]) | (bytes[0] << 8); 61 | } 62 | 63 | unsigned freadhe32(FILE *file) 64 | { 65 | unsigned char bytes[4]; 66 | 67 | fread(bytes, 4, 1, file); 68 | return (bytes[3]) | (bytes[2] << 8) | (bytes[1] << 16) | (bytes[0] << 24); 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /fileio.h: -------------------------------------------------------------------------------- 1 | #ifndef FILEIO_H 2 | #define FILEIO_H 3 | 4 | void fwrite8(FILE *file, unsigned data); 5 | void fwritele16(FILE *file, unsigned data); 6 | void fwritele32(FILE *file, unsigned data); 7 | unsigned fread8(FILE *file); 8 | unsigned freadle16(FILE *file); 9 | unsigned freadle32(FILE *file); 10 | unsigned freadhe16(FILE *file); 11 | unsigned freadhe32(FILE *file); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /gcommon.h: -------------------------------------------------------------------------------- 1 | #ifndef GCOMMON_H 2 | #define GCOMMON_H 3 | 4 | #define CMD_DONOTHING 0 5 | #define CMD_PORTAUP 1 6 | #define CMD_PORTADOWN 2 7 | #define CMD_TONEPORTA 3 8 | #define CMD_VIBRATO 4 9 | #define CMD_SETAD 5 10 | #define CMD_SETSR 6 11 | #define CMD_SETWAVE 7 12 | #define CMD_SETWAVEPTR 8 13 | #define CMD_SETPULSEPTR 9 14 | #define CMD_SETFILTERPTR 10 15 | #define CMD_SETFILTERCTRL 11 16 | #define CMD_SETFILTERCUTOFF 12 17 | #define CMD_SETMASTERVOL 13 18 | #define CMD_FUNKTEMPO 14 19 | #define CMD_SETTEMPO 15 20 | 21 | #define WTBL 0 22 | #define PTBL 1 23 | #define FTBL 2 24 | #define STBL 3 25 | 26 | #define MAX_FILT 64 27 | #define MAX_STR 32 28 | #define MAX_INSTR 64 29 | #define MAX_CHN 3 30 | #define MAX_PATT 208 31 | #define MAX_TABLES 4 32 | #define MAX_TABLELEN 255 33 | #define MAX_INSTRNAMELEN 16 34 | #define MAX_PATTROWS 128 35 | #define MAX_SONGLEN 254 36 | #define MAX_SONGS 32 37 | #define MAX_NOTES 96 38 | 39 | #define REPEAT 0xd0 40 | #define TRANSDOWN 0xe0 41 | #define TRANSUP 0xf0 42 | #define LOOPSONG 0xff 43 | 44 | #define ENDPATT 0xff 45 | #define INSTRCHG 0x00 46 | #define FX 0x40 47 | #define FXONLY 0x50 48 | #define FIRSTNOTE 0x60 49 | #define LASTNOTE 0xbc 50 | #define REST 0xbd 51 | #define KEYOFF 0xbe 52 | #define KEYON 0xbf 53 | #define OLDKEYOFF 0x5e 54 | #define OLDREST 0x5f 55 | 56 | #define WAVEDELAY 0x1 57 | #define WAVELASTDELAY 0xf 58 | #define WAVESILENT 0xe0 59 | #define WAVELASTSILENT 0xef 60 | #define WAVECMD 0xf0 61 | #define WAVELASTCMD 0xfe 62 | 63 | typedef struct 64 | { 65 | unsigned char ad; 66 | unsigned char sr; 67 | unsigned char ptr[MAX_TABLES]; 68 | unsigned char vibdelay; 69 | unsigned char gatetimer; 70 | unsigned char firstwave; 71 | unsigned char name[MAX_INSTRNAMELEN]; 72 | } INSTR; 73 | 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /gt2mini.c: -------------------------------------------------------------------------------- 1 | // Converter tool from GoatTracker2 song format to minimal player 2 | // Cadaver (loorni@gmail.com) 4/2019 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "fileio.h" 10 | #include "gcommon.h" 11 | 12 | #define MST_NOFINEVIB 0 13 | #define MST_FINEVIB 1 14 | #define MST_FUNKTEMPO 2 15 | #define MST_PORTAMENTO 3 16 | #define MST_RAW 4 17 | 18 | #define WTBL 0 19 | #define PTBL 1 20 | #define FTBL 2 21 | #define STBL 3 22 | 23 | #define MAX_MPSONGS 16 24 | #define MAX_MPPATT 127 25 | #define MAX_MPCMD 127 26 | #define MAX_MPPATTLEN 256 27 | #define MAX_MPSONGLEN 256 28 | #define MAX_MPTBLLEN 255 29 | 30 | #define MP_ENDPATT 0x00 31 | #define MP_FIRSTNOTE 0x02 32 | #define MP_LASTNOTE 0x7a 33 | #define MP_WAVEPTR 0x7c 34 | #define MP_KEYOFF 0x7e 35 | #define MP_REST 0x7f 36 | #define MP_NOCMD 0x1; 37 | #define MP_MAXDUR 128 38 | 39 | INSTR instr[MAX_INSTR]; 40 | unsigned char ltable[MAX_TABLES][MAX_TABLELEN]; 41 | unsigned char rtable[MAX_TABLES][MAX_TABLELEN]; 42 | unsigned char songorder[MAX_SONGS][MAX_CHN][MAX_SONGLEN+2]; 43 | unsigned char pattern[MAX_PATT][MAX_PATTROWS*4+4]; 44 | unsigned char patttempo[MAX_PATT][MAX_PATTROWS+1]; 45 | unsigned char pattinstr[MAX_PATT][MAX_PATTROWS+1]; 46 | unsigned char pattkeyon[MAX_PATT][MAX_PATTROWS+1]; 47 | unsigned char pattbasetrans[MAX_PATT]; 48 | unsigned char pattremaptempo[MAX_PATT]; 49 | unsigned char pattremapsrc[MAX_PATT]; 50 | unsigned char pattremapdest[MAX_PATT]; 51 | 52 | char songname[MAX_STR]; 53 | char authorname[MAX_STR]; 54 | char copyrightname[MAX_STR]; 55 | int pattlen[MAX_PATT]; 56 | int songlen[MAX_SONGS][MAX_CHN]; 57 | int tbllen[MAX_TABLES]; 58 | int highestusedpatt; 59 | int highestusedinstr; 60 | int highestusedsong; 61 | int defaultpatternlength = 64; 62 | int remappedpatterns = 0; 63 | int maxdur = MP_MAXDUR; 64 | 65 | unsigned char mpwavetbl[MAX_MPTBLLEN+1]; 66 | unsigned char mpnotetbl[MAX_MPTBLLEN+1]; 67 | unsigned char mpwavenexttbl[MAX_MPTBLLEN+1]; 68 | unsigned char mppulselimittbl[MAX_MPTBLLEN+1]; 69 | unsigned char mppulsespdtbl[MAX_MPTBLLEN+1]; 70 | unsigned char mppulsenexttbl[MAX_MPTBLLEN+1]; 71 | unsigned char mpfiltlimittbl[MAX_MPTBLLEN+1]; 72 | unsigned char mpfiltspdtbl[MAX_MPTBLLEN+1]; 73 | unsigned char mpfiltnexttbl[MAX_MPTBLLEN+1]; 74 | 75 | unsigned char mppatterns[MAX_MPPATT][MAX_MPPATTLEN]; 76 | unsigned char mppattlen[MAX_MPPATT]; 77 | unsigned char mppattnext[MAX_MPPATT]; 78 | unsigned char mppattprev[MAX_MPPATT]; 79 | 80 | unsigned char mptracks[MAX_MPSONGS][MAX_MPSONGLEN]; 81 | unsigned char mpsongstart[MAX_MPSONGS][3]; 82 | unsigned char mpsongtotallen[MAX_MPSONGS]; 83 | 84 | unsigned char mpinsad[MAX_MPCMD]; 85 | unsigned char mpinssr[MAX_MPCMD]; 86 | unsigned char mpinsfirstwave[MAX_MPCMD]; 87 | unsigned char mpinswavepos[MAX_MPCMD]; 88 | unsigned char mpinspulsepos[MAX_MPCMD]; 89 | unsigned char mpinsfiltpos[MAX_MPCMD]; 90 | 91 | unsigned char instrmap[256]; 92 | unsigned char instrpulseused[256]; 93 | unsigned char instrfirstwavepos[256]; 94 | unsigned char instrlastwavepos[256]; 95 | unsigned char legatoinstrmap[256]; 96 | unsigned char legatostepmap[256]; 97 | unsigned char waveposmap[256]; 98 | unsigned char pulseposmap[256]; 99 | unsigned char filtposmap[256]; 100 | unsigned char slidemap[65536]; 101 | unsigned char vibratomap[65536]; 102 | 103 | int mpinssize = 0; 104 | int mplegatoinssize = 0; 105 | int mpwavesize = 0; 106 | int mppulsesize = 0; 107 | int mpfiltsize = 0; 108 | 109 | FILE* out = 0; 110 | int baremode = 0; 111 | int startaddress = -1; 112 | int endaddress = -1; 113 | 114 | unsigned short freqtbl[] = { 115 | 0x022d,0x024e,0x0271,0x0296,0x02be,0x02e8,0x0314,0x0343,0x0374,0x03a9,0x03e1,0x041c, 116 | 0x045a,0x049c,0x04e2,0x052d,0x057c,0x05cf,0x0628,0x0685,0x06e8,0x0752,0x07c1,0x0837, 117 | 0x08b4,0x0939,0x09c5,0x0a5a,0x0af7,0x0b9e,0x0c4f,0x0d0a,0x0dd1,0x0ea3,0x0f82,0x106e, 118 | 0x1168,0x1271,0x138a,0x14b3,0x15ee,0x173c,0x189e,0x1a15,0x1ba2,0x1d46,0x1f04,0x20dc, 119 | 0x22d0,0x24e2,0x2714,0x2967,0x2bdd,0x2e79,0x313c,0x3429,0x3744,0x3a8d,0x3e08,0x41b8, 120 | 0x45a1,0x49c5,0x4e28,0x52cd,0x57ba,0x5cf1,0x6278,0x6853,0x6e87,0x751a,0x7c10,0x8371, 121 | 0x8b42,0x9389,0x9c4f,0xa59b,0xaf74,0xb9e2,0xc4f0,0xd0a6,0xdd0e,0xea33,0xf820,0xffff 122 | }; 123 | 124 | void loadsong(const char* songfilename); 125 | void clearsong(int cs, int cp, int ci, int cf, int cn); 126 | void clearinstr(int num); 127 | void clearpattern(int num); 128 | void countpatternlengths(void); 129 | int makespeedtable(unsigned data, int mode, int makenew); 130 | void primpsonginfo(void); 131 | void clearmpsong(void); 132 | void convertsong(void); 133 | void getpatttempos(void); 134 | void getpattbasetrans(void); 135 | unsigned char copywavetable(unsigned char instr); 136 | unsigned char getlegatoinstr(unsigned char instr); 137 | unsigned char getvibrato(unsigned char delay, unsigned char pos); 138 | unsigned char getslide(unsigned short speed); 139 | void savempsong(const char* songfilename); 140 | void writeblock(FILE* out, const char* blockname, unsigned char* data, int len); 141 | 142 | int main(int argc, const char** argv) 143 | { 144 | int c; 145 | 146 | if (argc < 3) 147 | { 148 | printf("Converter from GT2 format to minimal player. Outputs source code.\n" 149 | "Usage: gt2mini [options]\n" 150 | "-b Bare mode; do not add header for music module operation\n" 151 | "-sxxxx Include hexadecimal start address & Dasm processor statement\n" 152 | "-exxxx Calculate start address from specified hexadecimal end address\n"); 153 | return 1; 154 | } 155 | 156 | for (c = 3; c < argc; ++c) 157 | { 158 | if (argv[c][0] == '-') 159 | { 160 | switch(argv[c][1]) 161 | { 162 | case 'b': 163 | baremode = 1; 164 | break; 165 | case 's': 166 | startaddress = strtoul(&argv[c][2], 0, 16); 167 | break; 168 | case 'e': 169 | endaddress = strtoul(&argv[c][2], 0, 16); 170 | break; 171 | } 172 | } 173 | } 174 | 175 | loadsong(argv[1]); 176 | primpsonginfo(); 177 | clearmpsong(); 178 | convertsong(); 179 | savempsong(argv[2]); 180 | return 0; 181 | } 182 | 183 | void loadsong(const char* songfilename) 184 | { 185 | int c; 186 | int ok = 0; 187 | char ident[4]; 188 | FILE *handle; 189 | 190 | handle = fopen(songfilename, "rb"); 191 | 192 | if (!handle) 193 | { 194 | printf("Could not open input song %s\n", songfilename); 195 | exit(1); 196 | } 197 | 198 | fread(ident, 4, 1, handle); 199 | if ((!memcmp(ident, "GTS3", 4)) || (!memcmp(ident, "GTS4", 4)) || (!memcmp(ident, "GTS5", 4))) 200 | { 201 | int d; 202 | int length; 203 | int amount; 204 | int loadsize; 205 | clearsong(1,1,1,1,1); 206 | ok = 1; 207 | 208 | // Read infotexts 209 | fread(songname, sizeof songname, 1, handle); 210 | fread(authorname, sizeof authorname, 1, handle); 211 | fread(copyrightname, sizeof copyrightname, 1, handle); 212 | 213 | // Read songorderlists 214 | amount = fread8(handle); 215 | highestusedsong = amount - 1; 216 | for (d = 0; d < amount; d++) 217 | { 218 | for (c = 0; c < MAX_CHN; c++) 219 | { 220 | length = fread8(handle); 221 | loadsize = length; 222 | loadsize++; 223 | fread(songorder[d][c], loadsize, 1, handle); 224 | } 225 | } 226 | // Read instruments 227 | amount = fread8(handle); 228 | for (c = 1; c <= amount; c++) 229 | { 230 | instr[c].ad = fread8(handle); 231 | instr[c].sr = fread8(handle); 232 | instr[c].ptr[WTBL] = fread8(handle); 233 | instr[c].ptr[PTBL] = fread8(handle); 234 | instr[c].ptr[FTBL] = fread8(handle); 235 | instr[c].ptr[STBL] = fread8(handle); 236 | instr[c].vibdelay = fread8(handle); 237 | instr[c].gatetimer = fread8(handle); 238 | instr[c].firstwave = fread8(handle); 239 | fread(&instr[c].name, MAX_INSTRNAMELEN, 1, handle); 240 | } 241 | // Read tables 242 | for (c = 0; c < MAX_TABLES; c++) 243 | { 244 | loadsize = fread8(handle); 245 | tbllen[c] = loadsize; 246 | fread(ltable[c], loadsize, 1, handle); 247 | fread(rtable[c], loadsize, 1, handle); 248 | } 249 | // Read patterns 250 | amount = fread8(handle); 251 | for (c = 0; c < amount; c++) 252 | { 253 | length = fread8(handle) * 4; 254 | fread(pattern[c], length, 1, handle); 255 | } 256 | countpatternlengths(); 257 | } 258 | 259 | // Goattracker v2.xx (3-table) import 260 | if (!memcmp(ident, "GTS2", 4)) 261 | { 262 | int d; 263 | int length; 264 | int amount; 265 | int loadsize; 266 | clearsong(1,1,1,1,1); 267 | ok = 1; 268 | 269 | // Read infotexts 270 | fread(songname, sizeof songname, 1, handle); 271 | fread(authorname, sizeof authorname, 1, handle); 272 | fread(copyrightname, sizeof copyrightname, 1, handle); 273 | 274 | // Read songorderlists 275 | amount = fread8(handle); 276 | highestusedsong = amount - 1; 277 | for (d = 0; d < amount; d++) 278 | { 279 | for (c = 0; c < MAX_CHN; c++) 280 | { 281 | length = fread8(handle); 282 | loadsize = length; 283 | loadsize++; 284 | fread(songorder[d][c], loadsize, 1, handle); 285 | } 286 | } 287 | 288 | // Read instruments 289 | amount = fread8(handle); 290 | for (c = 1; c <= amount; c++) 291 | { 292 | instr[c].ad = fread8(handle); 293 | instr[c].sr = fread8(handle); 294 | instr[c].ptr[WTBL] = fread8(handle); 295 | instr[c].ptr[PTBL] = fread8(handle); 296 | instr[c].ptr[FTBL] = fread8(handle); 297 | instr[c].vibdelay = fread8(handle); 298 | instr[c].ptr[STBL] = makespeedtable(fread8(handle), MST_FINEVIB, 0) + 1; 299 | instr[c].gatetimer = fread8(handle); 300 | instr[c].firstwave = fread8(handle); 301 | fread(&instr[c].name, MAX_INSTRNAMELEN, 1, handle); 302 | } 303 | // Read tables 304 | for (c = 0; c < MAX_TABLES-1; c++) 305 | { 306 | loadsize = fread8(handle); 307 | tbllen[c] = loadsize; 308 | fread(ltable[c], loadsize, 1, handle); 309 | fread(rtable[c], loadsize, 1, handle); 310 | } 311 | // Read patterns 312 | amount = fread8(handle); 313 | for (c = 0; c < amount; c++) 314 | { 315 | int d; 316 | length = fread8(handle) * 4; 317 | fread(pattern[c], length, 1, handle); 318 | 319 | // Convert speedtable-requiring commands 320 | for (d = 0; d < length; d++) 321 | { 322 | switch (pattern[c][d*4+2]) 323 | { 324 | case CMD_FUNKTEMPO: 325 | pattern[c][d*4+3] = makespeedtable(pattern[c][d*4+3], MST_FUNKTEMPO, 0) + 1; 326 | break; 327 | 328 | case CMD_PORTAUP: 329 | case CMD_PORTADOWN: 330 | case CMD_TONEPORTA: 331 | pattern[c][d*4+3] = makespeedtable(pattern[c][d*4+3], MST_PORTAMENTO, 0) + 1; 332 | break; 333 | 334 | case CMD_VIBRATO: 335 | pattern[c][d*4+3] = makespeedtable(pattern[c][d*4+3], MST_FINEVIB, 0) + 1; 336 | break; 337 | } 338 | } 339 | } 340 | countpatternlengths(); 341 | } 342 | // Goattracker 1.xx 343 | if (!memcmp(ident, "GTS!", 4)) 344 | { 345 | printf("GT1 songs are not supported. Please re-save the song in GT2.\n"); 346 | exit(1); 347 | } 348 | 349 | // Convert pulsemodulation speed of < v2.4 songs 350 | if (ident[3] < '4') 351 | { 352 | for (c = 0; c < MAX_TABLELEN; c++) 353 | { 354 | if ((ltable[PTBL][c] < 0x80) && (rtable[PTBL][c])) 355 | { 356 | int speed = ((signed char)rtable[PTBL][c]); 357 | speed <<= 1; 358 | if (speed > 127) speed = 127; 359 | if (speed < -128) speed = -128; 360 | rtable[PTBL][c] = speed; 361 | } 362 | } 363 | } 364 | 365 | // Convert old legato/nohr parameters 366 | if (ident[3] < '5') 367 | { 368 | for (c = 1; c < MAX_INSTR; c++) 369 | { 370 | if (instr[c].firstwave >= 0x80) 371 | { 372 | instr[c].gatetimer |= 0x80; 373 | instr[c].firstwave &= 0x7f; 374 | } 375 | if (!instr[c].firstwave) instr[c].gatetimer |= 0x40; 376 | } 377 | } 378 | } 379 | 380 | void clearsong(int cs, int cp, int ci, int ct, int cn) 381 | { 382 | int c; 383 | 384 | if (!(cs | cp | ci | ct | cn)) return; 385 | 386 | for (c = 0; c < MAX_CHN; c++) 387 | { 388 | int d; 389 | if (cs) 390 | { 391 | for (d = 0; d < MAX_SONGS; d++) 392 | { 393 | memset(&songorder[d][c][0], 0, MAX_SONGLEN+2); 394 | if (!d) 395 | { 396 | songorder[d][c][0] = c; 397 | songorder[d][c][1] = LOOPSONG; 398 | } 399 | else 400 | { 401 | songorder[d][c][0] = LOOPSONG; 402 | } 403 | } 404 | } 405 | } 406 | if (cn) 407 | { 408 | memset(songname, 0, sizeof songname); 409 | memset(authorname, 0, sizeof authorname); 410 | memset(copyrightname, 0, sizeof copyrightname); 411 | } 412 | if (cp) 413 | { 414 | for (c = 0; c < MAX_PATT; c++) 415 | clearpattern(c); 416 | } 417 | if (ci) 418 | { 419 | for (c = 0; c < MAX_INSTR; c++) 420 | clearinstr(c); 421 | } 422 | if (ct == 1) 423 | { 424 | for (c = MAX_TABLES-1; c >= 0; c--) 425 | { 426 | memset(ltable[c], 0, MAX_TABLELEN); 427 | memset(rtable[c], 0, MAX_TABLELEN); 428 | } 429 | } 430 | countpatternlengths(); 431 | } 432 | 433 | void clearpattern(int p) 434 | { 435 | int c; 436 | 437 | memset(pattern[p], 0, MAX_PATTROWS*4); 438 | for (c = 0; c < defaultpatternlength; c++) pattern[p][c*4] = REST; 439 | for (c = defaultpatternlength; c <= MAX_PATTROWS; c++) pattern[p][c*4] = ENDPATT; 440 | } 441 | 442 | void clearinstr(int num) 443 | { 444 | memset(&instr[num], 0, sizeof(INSTR)); 445 | if (num) 446 | { 447 | instr[num].gatetimer = 2; 448 | instr[num].firstwave = 0x9; 449 | } 450 | } 451 | 452 | void countpatternlengths(void) 453 | { 454 | int c, d, e; 455 | 456 | highestusedpatt = 0; 457 | highestusedinstr = 0; 458 | for (c = 0; c < MAX_PATT; c++) 459 | { 460 | for (d = 0; d <= MAX_PATTROWS; d++) 461 | { 462 | if (pattern[c][d*4] == ENDPATT) break; 463 | if ((pattern[c][d*4] != REST) || (pattern[c][d*4+1]) || (pattern[c][d*4+2]) || (pattern[c][d*4+3])) 464 | highestusedpatt = c; 465 | if (pattern[c][d*4+1] > highestusedinstr) highestusedinstr = pattern[c][d*4+1]; 466 | } 467 | pattlen[c] = d; 468 | } 469 | 470 | for (e = 0; e < MAX_SONGS; e++) 471 | { 472 | for (c = 0; c < MAX_CHN; c++) 473 | { 474 | for (d = 0; d < MAX_SONGLEN; d++) 475 | { 476 | if (songorder[e][c][d] >= LOOPSONG) break; 477 | if ((songorder[e][c][d] < REPEAT) && (songorder[e][c][d] > highestusedpatt)) 478 | highestusedpatt = songorder[e][c][d]; 479 | } 480 | songlen[e][c] = d; 481 | } 482 | } 483 | } 484 | 485 | int makespeedtable(unsigned data, int mode, int makenew) 486 | { 487 | int c; 488 | unsigned char l = 0, r = 0; 489 | 490 | if (!data) return -1; 491 | 492 | switch (mode) 493 | { 494 | case MST_NOFINEVIB: 495 | l = (data & 0xf0) >> 4; 496 | r = (data & 0x0f) << 4; 497 | break; 498 | 499 | case MST_FINEVIB: 500 | l = (data & 0x70) >> 4; 501 | r = ((data & 0x0f) << 4) | ((data & 0x80) >> 4); 502 | break; 503 | 504 | case MST_FUNKTEMPO: 505 | l = (data & 0xf0) >> 4; 506 | r = data & 0x0f; 507 | break; 508 | 509 | case MST_PORTAMENTO: 510 | l = (data << 2) >> 8; 511 | r = (data << 2) & 0xff; 512 | break; 513 | 514 | case MST_RAW: 515 | r = data & 0xff; 516 | l = data >> 8; 517 | break; 518 | } 519 | 520 | if (makenew == 0) 521 | { 522 | for (c = 0; c < MAX_TABLELEN; c++) 523 | { 524 | if ((ltable[STBL][c] == l) && (rtable[STBL][c] == r)) 525 | return c; 526 | } 527 | } 528 | 529 | for (c = 0; c < MAX_TABLELEN; c++) 530 | { 531 | if ((!ltable[STBL][c]) && (!rtable[STBL][c])) 532 | { 533 | ltable[STBL][c] = l; 534 | rtable[STBL][c] = r; 535 | return c; 536 | } 537 | } 538 | return -1; 539 | } 540 | 541 | void primpsonginfo(void) 542 | { 543 | printf("Songs: %d Patterns: %d Instruments: %d\n", highestusedsong+1, highestusedpatt+1, highestusedinstr); 544 | } 545 | 546 | void clearmpsong(void) 547 | { 548 | memset(mpwavetbl, 0, sizeof mpwavetbl); 549 | memset(mpnotetbl, 0, sizeof mpnotetbl); 550 | memset(mpwavenexttbl, 0, sizeof mpwavenexttbl); 551 | memset(mppulselimittbl, 0, sizeof mppulselimittbl); 552 | memset(mppulsespdtbl, 0, sizeof mppulsespdtbl); 553 | memset(mppulsenexttbl, 0, sizeof mppulsenexttbl); 554 | memset(mpfiltlimittbl, 0, sizeof mpfiltlimittbl); 555 | memset(mpfiltspdtbl, 0, sizeof mpfiltspdtbl); 556 | memset(mpfiltnexttbl, 0, sizeof mpfiltnexttbl); 557 | memset(mppatterns, 0, sizeof mppatterns); 558 | memset(mptracks, 0, sizeof mptracks); 559 | memset(mpinsad, 0, sizeof mpinsad); 560 | memset(mpinssr, 0, sizeof mpinssr); 561 | memset(mpinsfirstwave, 0, sizeof mpinsfirstwave); 562 | memset(mpinswavepos, 0, sizeof mpinswavepos); 563 | memset(mpinspulsepos, 0, sizeof mpinspulsepos); 564 | memset(mpinsfiltpos, 0, sizeof mpinsfiltpos); 565 | mpinssize = 0; 566 | mplegatoinssize = 0; 567 | mpwavesize = 0; 568 | mppulsesize = 0; 569 | mpfiltsize = 0; 570 | } 571 | 572 | void convertsong(void) 573 | { 574 | int e,c,f; 575 | int mergepatt; 576 | 577 | memset(instrmap, 0, sizeof instrmap); 578 | memset(instrpulseused, 0, sizeof instrpulseused); 579 | memset(instrfirstwavepos, 0, sizeof instrfirstwavepos); 580 | memset(instrlastwavepos, 0, sizeof instrlastwavepos); 581 | memset(legatoinstrmap, 0, sizeof legatoinstrmap); 582 | memset(legatostepmap, 0, sizeof legatostepmap); 583 | memset(slidemap, 0, sizeof slidemap); 584 | memset(vibratomap, 0, sizeof vibratomap); 585 | memset(waveposmap, 0, sizeof waveposmap); 586 | memset(pulseposmap, 0, sizeof pulseposmap); 587 | memset(filtposmap, 0, sizeof filtposmap); 588 | 589 | if (highestusedsong > 15) 590 | { 591 | printf("More than 16 songs not supported\n"); 592 | exit(1); 593 | } 594 | if (highestusedpatt > 126) 595 | { 596 | printf("More than 127 patterns not supported\n"); 597 | exit(1); 598 | } 599 | 600 | getpatttempos(); 601 | getpattbasetrans(); 602 | 603 | // Convert trackdata 604 | for (e = 0; e <= highestusedsong; e++) 605 | { 606 | printf("Converting trackdata for song %d\n", e+1); 607 | 608 | int dest = 0; 609 | 610 | for (c = 0; c < MAX_CHN; c++) 611 | { 612 | int sp = 0; 613 | int len = 0; 614 | unsigned char positionmap[256]; 615 | 616 | int trans = 0; 617 | int lasttrans = -1; // Make sure transpose resets on song loop, even if GT2 song doesn't include it 618 | 619 | mpsongstart[e][c] = dest; 620 | 621 | while (1) 622 | { 623 | int rep = 1; 624 | int patt = 0; 625 | 626 | if (songorder[e][c][sp] >= LOOPSONG) 627 | break; 628 | while (songorder[e][c][sp] >= TRANSDOWN) 629 | { 630 | positionmap[sp] = dest + 1; 631 | trans = songorder[e][c][sp++] - TRANSUP; 632 | } 633 | while ((songorder[e][c][sp] >= REPEAT) && (songorder[e][c][sp] < TRANSDOWN)) 634 | { 635 | positionmap[sp] = dest + 1; 636 | rep = songorder[e][c][sp++] - REPEAT + 1; 637 | } 638 | patt = songorder[e][c][sp]; 639 | positionmap[sp] = dest + 1; 640 | 641 | if (trans + pattbasetrans[patt] != lasttrans) 642 | { 643 | mptracks[e][dest++] = ((trans + pattbasetrans[patt]) & 0x7f) | 0x80; 644 | lasttrans = trans + pattbasetrans[patt]; 645 | } 646 | 647 | while (rep--) 648 | { 649 | mptracks[e][dest++] = patt + 1; 650 | } 651 | sp++; 652 | } 653 | sp++; 654 | mptracks[e][dest++] = 0; 655 | mptracks[e][dest++] = positionmap[songorder[e][c][sp]]; 656 | } 657 | if (dest > 255) 658 | { 659 | printf("Song %d's trackdata does not fit in 255 bytes\n", e+1); 660 | exit(1); 661 | } 662 | mpsongtotallen[e] = dest; 663 | } 664 | 665 | printf("Converting wavetable\n"); 666 | 667 | { 668 | int sp = 0; 669 | 670 | while (sp < 256 && mpwavesize < 255) 671 | { 672 | unsigned char wave = ltable[WTBL][sp]; 673 | unsigned char note = rtable[WTBL][sp]; 674 | 675 | if (sp > 0 && wave == 0x00 && ltable[WTBL][sp-1] == 0xff) 676 | break; 677 | 678 | waveposmap[sp+1] = mpwavesize + 1; 679 | sp++; 680 | 681 | if (wave < 0xf0 && note == 0x80) 682 | printf("Warning: 'keep frequency unchanged' in wavetable is unsupported\n"); 683 | if (wave < 0xf0 && note >= 0x81 && note < 0x8c) 684 | { 685 | printf("Wavetable has octave 0, can not be converted correctly\n"); 686 | exit(1); 687 | } 688 | if (wave >= 0xf0 && wave < 0xff) 689 | printf("Warning: wavetable commands are unsupported\n"); 690 | 691 | if (wave == 0xff) 692 | { 693 | waveposmap[sp] = mpwavesize; 694 | continue; 695 | } 696 | else if (wave >= 0x10 && wave <= 0x8f) 697 | { 698 | mpwavetbl[mpwavesize] = wave; 699 | mpnotetbl[mpwavesize] = note < 0x80 ? note : note-11; 700 | mpwavenexttbl[mpwavesize] = mpwavesize+1+1; 701 | mpwavesize++; 702 | } 703 | else if (wave >= 0xe1 && wave <= 0xef) 704 | { 705 | mpwavetbl[mpwavesize] = wave - 0xe0; 706 | mpnotetbl[mpwavesize] = note < 0x80 ? note : note-11; 707 | mpwavenexttbl[mpwavesize] = mpwavesize+1+1; 708 | mpwavesize++; 709 | } 710 | else if (wave < 0x10) 711 | { 712 | mpwavetbl[mpwavesize] = 0xff - wave; 713 | mpnotetbl[mpwavesize] = note < 0x80 ? note : note-11; 714 | mpwavenexttbl[mpwavesize] = mpwavesize+1+1; 715 | mpwavesize++; 716 | } 717 | } 718 | 719 | // Fix jumps 720 | sp = 0; 721 | while (sp < 256) 722 | { 723 | unsigned char wave = ltable[WTBL][sp]; 724 | unsigned char note = rtable[WTBL][sp]; 725 | 726 | if (sp > 0 && wave == 0x00 && ltable[WTBL][sp-1] == 0xff) 727 | break; 728 | if (wave == 0xff) 729 | { 730 | mpwavenexttbl[waveposmap[sp+1]-1] = note ? waveposmap[note] : 0; 731 | } 732 | ++sp; 733 | } 734 | } 735 | 736 | printf("Converting pulsetable\n"); 737 | 738 | // Create the "stop pulse" optimization step 739 | mppulselimittbl[mppulsesize] = 0; 740 | mppulsespdtbl[mppulsesize] = 0; 741 | mppulsenexttbl[mppulsesize] = 0; 742 | mppulsesize++; 743 | 744 | { 745 | int sp = 0; 746 | int pulsevalue = 0; 747 | 748 | while (sp < 256 && mppulsesize < 127) 749 | { 750 | unsigned char time = ltable[PTBL][sp]; 751 | unsigned char spd = rtable[PTBL][sp]; 752 | 753 | if (sp > 0 && time == 0x00 && ltable[PTBL][sp-1] == 0xff) 754 | break; 755 | 756 | pulseposmap[sp+1] = (mppulsesize + 1) | (time & 0x80); 757 | sp++; 758 | 759 | if (time != 0xff && spd & 0xf) 760 | { 761 | printf("Warning: lowest 4 bits of pulse aren't supported\n"); 762 | } 763 | 764 | if (time == 0xff) 765 | { 766 | pulseposmap[sp] = mppulsesize; 767 | continue; 768 | } 769 | else if (time & 0x80) 770 | { 771 | mppulselimittbl[mppulsesize] = (time & 0xf) | (spd & 0xf0); 772 | mppulsespdtbl[mppulsesize] = 0; 773 | mppulsenexttbl[mppulsesize] = mppulsesize+1+1; 774 | if (mppulsesize > 1 && mppulsenexttbl[mppulsesize-1] == mppulsesize+1) 775 | mppulsenexttbl[mppulsesize-1] |= 0x80; 776 | pulsevalue = ((time & 0xf) << 8) | spd; 777 | ++mppulsesize; 778 | } 779 | else 780 | { 781 | if (spd < 0x80) 782 | pulsevalue += time*spd; 783 | else 784 | pulsevalue += time*((int)spd-0x100); 785 | mppulselimittbl[mppulsesize] = (pulsevalue >> 8) | (pulsevalue & 0xf0); 786 | if (spd < 0x80) 787 | mppulsespdtbl[mppulsesize] = spd & 0xf0; 788 | else 789 | mppulsespdtbl[mppulsesize] = (spd & 0xf0) - 1; 790 | mppulsenexttbl[mppulsesize] = mppulsesize+1+1; 791 | ++mppulsesize; 792 | } 793 | } 794 | 795 | // Fix jumps 796 | sp = 0; 797 | while (sp < 256) 798 | { 799 | unsigned char time = ltable[PTBL][sp]; 800 | unsigned char spd = rtable[PTBL][sp]; 801 | 802 | if (sp > 0 && time == 0x00 && ltable[PTBL][sp-1] == 0xff) 803 | break; 804 | if (time == 0xff) 805 | { 806 | mppulsenexttbl[(pulseposmap[sp+1]&0x7f)-1] = spd ? pulseposmap[spd] : 0; 807 | } 808 | ++sp; 809 | } 810 | } 811 | 812 | printf("Converting filtertable\n"); 813 | 814 | { 815 | int sp = 0; 816 | unsigned char cutoffvalue = 0; 817 | 818 | while (sp < 256 && mpfiltsize < 127) 819 | { 820 | unsigned char time = ltable[FTBL][sp]; 821 | unsigned char spd = rtable[FTBL][sp]; 822 | 823 | if (sp > 0 && time == 0x00 && ltable[FTBL][sp-1] == 0xff) 824 | break; 825 | 826 | filtposmap[sp+1] = (mpfiltsize + 1) | (time & 0x80); 827 | sp++; 828 | 829 | if (time == 0xff) 830 | { 831 | filtposmap[sp] = mpfiltsize; 832 | continue; 833 | } 834 | else if (time & 0x80) 835 | { 836 | mpfiltspdtbl[mpfiltsize] = (time & 0x70) | (spd & 0x8f); 837 | mpfiltnexttbl[mpfiltsize] = mpfiltsize+1+1; 838 | if (ltable[FTBL][sp] != 0) 839 | printf("Warning: filter init-step not followed by set cutoff-step\n"); 840 | if (mpfiltsize > 1 && mpfiltnexttbl[mpfiltsize-1] == mpfiltsize+1) 841 | mpfiltnexttbl[mpfiltsize-1] |= 0x80; 842 | ++mpfiltsize; 843 | } 844 | else if (time == 0 && mpfiltsize > 0) 845 | { 846 | // Fill in the initial cutoff value of the init step above 847 | mpfiltlimittbl[mpfiltsize-1] = spd; 848 | cutoffvalue = spd; 849 | } 850 | else 851 | { 852 | if (spd < 0x80) 853 | cutoffvalue += time*spd; 854 | else 855 | cutoffvalue += time*((int)spd-0x100); 856 | mpfiltlimittbl[mpfiltsize] = cutoffvalue; 857 | mpfiltspdtbl[mpfiltsize] = spd; 858 | mpfiltnexttbl[mpfiltsize] = mpfiltsize+1+1; 859 | ++mpfiltsize; 860 | } 861 | } 862 | 863 | // Fix jumps 864 | sp = 0; 865 | while (sp < 256) 866 | { 867 | unsigned char time = ltable[FTBL][sp]; 868 | unsigned char spd = rtable[FTBL][sp]; 869 | 870 | if (sp > 0 && time == 0x00 && ltable[FTBL][sp-1] == 0xff) 871 | break; 872 | if (time == 0xff) 873 | { 874 | mpfiltnexttbl[(filtposmap[sp+1]&0x7f)-1] = spd ? filtposmap[spd] : 0; 875 | } 876 | ++sp; 877 | } 878 | } 879 | 880 | printf("Converting instruments\n"); 881 | 882 | for (e = 1; e <= highestusedinstr; e++) 883 | { 884 | int pulseused = 0; 885 | 886 | // If instrument has no wavetable pointer, assume it is not used 887 | if (!instr[e].ptr[WTBL]) 888 | continue; 889 | mpinsad[mpinssize] = instr[e].ad; 890 | mpinssr[mpinssize] = instr[e].sr; 891 | mpinsfirstwave[mpinssize] = instr[e].firstwave; 892 | mpinswavepos[mpinssize] = instrfirstwavepos[e] = waveposmap[instr[e].ptr[WTBL]]; 893 | 894 | // Find out last wavestep for legato, also find out if pulse is used 895 | { 896 | int wp = instrfirstwavepos[e]; 897 | while (wp < 255) 898 | { 899 | if (mpwavetbl[wp-1] & 0x40) 900 | pulseused = 1; 901 | if (mpwavenexttbl[wp-1] != wp+1) 902 | break; 903 | ++wp; 904 | } 905 | instrlastwavepos[e] = wp; 906 | } 907 | 908 | if (!instr[e].ptr[PTBL]) 909 | { 910 | if (pulseused) 911 | mpinspulsepos[mpinssize] = 0; // Keep existing pulse going 912 | else 913 | mpinspulsepos[mpinssize] = 1; // Stop pulse-step, for saving rastertime for triangle/sawtooth/noise only instruments 914 | } 915 | else 916 | mpinspulsepos[mpinssize] = pulseposmap[instr[e].ptr[PTBL]]; 917 | 918 | if (!instr[e].ptr[FTBL]) 919 | mpinsfiltpos[mpinssize] = 0; 920 | else 921 | mpinsfiltpos[mpinssize] = filtposmap[instr[e].ptr[FTBL]]; 922 | 923 | instrmap[e] = mpinssize+1; 924 | mpinssize++; 925 | } 926 | mplegatoinssize = mpinssize; 927 | 928 | for (e = 1; e <= highestusedinstr; e++) 929 | { 930 | // Add instrument vibratos 931 | if (instr[e].ptr[STBL] && instr[e].ptr[WTBL] && instr[e].vibdelay > 0) 932 | { 933 | int i = instrmap[e]; 934 | int newvibwavepos = 0; 935 | int needcopy = 0; 936 | int waveends = 0; 937 | int wavejumppos = 0; 938 | int f; 939 | 940 | for (f = 1; f <= highestusedinstr; f++) 941 | { 942 | if (f != e && instr[f].ptr[WTBL] == instr[e].ptr[WTBL]) 943 | { 944 | needcopy = 1; 945 | break; 946 | } 947 | } 948 | 949 | if (!needcopy) 950 | mpwavenexttbl[instrlastwavepos[e]-1] = getvibrato(instr[e].vibdelay, instr[e].ptr[STBL]); 951 | else 952 | { 953 | int jumppos = 0; 954 | int copystart = mpwavesize+1; 955 | mpinswavepos[instrmap[e]-1] = copystart; 956 | for (f = instrfirstwavepos[e]; f <= instrlastwavepos[e]; ++f) 957 | { 958 | mpwavetbl[mpwavesize] = mpwavetbl[f-1]; 959 | mpnotetbl[mpwavesize] = mpnotetbl[f-1]; 960 | mpwavenexttbl[mpwavesize] = mpwavesize+1+1; 961 | ++mpwavesize; 962 | } 963 | instrfirstwavepos[e] = copystart; 964 | instrlastwavepos[e] = mpwavesize; 965 | jumppos = mpwavesize-1; 966 | mpwavenexttbl[jumppos] = getvibrato(instr[e].vibdelay, instr[e].ptr[STBL]); 967 | } 968 | } 969 | } 970 | 971 | // Convert patterns 972 | printf("Converting patterns\n"); 973 | 974 | for (e = 0; e <= highestusedpatt; e++) 975 | { 976 | // Reserve more space due to possible step splitting 977 | unsigned char notecolumn[MAX_PATTROWS*2]; 978 | unsigned char cmdcolumn[MAX_PATTROWS*2]; 979 | unsigned char durcolumn[MAX_PATTROWS*2]; 980 | memset(cmdcolumn, 0, sizeof cmdcolumn); 981 | memset(durcolumn, 0, sizeof durcolumn); 982 | int pattlen = 0; 983 | int lastnoteins = -1; 984 | int lastnotempins = -1; 985 | int lastdur; 986 | int lastwaveptr = 0; 987 | int d = 0; 988 | unsigned short freq = 0; 989 | int targetfreq = 0; 990 | int tptargetnote = 0; 991 | int tpstepsleft = 0; 992 | 993 | for (c = 0; c < MAX_PATTROWS+1; c++) 994 | { 995 | int note = pattern[e][c*4]; 996 | int gtcmd = pattern[e][c*4+2]; 997 | int gtcmddata = pattern[e][c*4+3]; 998 | if (note == ENDPATT) 999 | { 1000 | notecolumn[d] = MP_ENDPATT; 1001 | break; 1002 | } 1003 | int instr = pattinstr[e][c]; 1004 | int mpinstr = instrmap[instr]; 1005 | int dur = patttempo[e][c]; 1006 | int waveptr = 0; 1007 | 1008 | // If tempo not determined, use default 6 1009 | if (!dur) 1010 | dur = 6; 1011 | 1012 | // If is not a toneportamento with speed, reset frequency before processing effects 1013 | if (note >= FIRSTNOTE+12 && note <= LASTNOTE) 1014 | { 1015 | if (gtcmd != 0x3 || gtcmddata == 0) 1016 | { 1017 | freq = freqtbl[note-FIRSTNOTE-12]; 1018 | } 1019 | } 1020 | 1021 | if (gtcmd != 0) 1022 | { 1023 | if (gtcmd == 0xf) 1024 | { 1025 | // No-op, tempo already accounted for 1026 | } 1027 | else if (gtcmd == 0x3) 1028 | { 1029 | if (gtcmddata == 0 && note >= FIRSTNOTE+12 && note <= LASTNOTE) // Legato note start 1030 | { 1031 | mpinstr = getlegatoinstr(instr); 1032 | tptargetnote = 0; 1033 | tpstepsleft = 0; 1034 | } 1035 | else if (gtcmddata > 0 && note >= FIRSTNOTE+12 && note <= LASTNOTE) 1036 | { 1037 | targetfreq = freqtbl[note-FIRSTNOTE-12]; 1038 | unsigned short speed = 0; 1039 | if (gtcmddata != 0) 1040 | speed = (ltable[STBL][gtcmddata-1] << 8) | rtable[STBL][gtcmddata-1]; 1041 | tpstepsleft = speed > 0 ? abs(freq-targetfreq) / speed : MAX_PATTROWS; 1042 | tpstepsleft += 1; // Assume 1 step gets lost 1043 | 1044 | if (targetfreq > freq) 1045 | { 1046 | waveptr = getslide(speed); 1047 | } 1048 | else 1049 | { 1050 | speed = -speed; 1051 | waveptr = getslide(speed); 1052 | } 1053 | tptargetnote = note; 1054 | note = REST; // The actual toneportamento target note is just discarded, as there is no "stop at note" functionality in the player 1055 | } 1056 | } 1057 | else if (gtcmd == 0x1) 1058 | { 1059 | unsigned short speed = 0; 1060 | if (gtcmddata != 0) 1061 | speed = (ltable[STBL][gtcmddata-1] << 8) | rtable[STBL][gtcmddata-1]; 1062 | 1063 | waveptr = getslide(speed); 1064 | freq += (dur-3) * speed; // Assume 3 steps get lost for toneportamento purposes 1065 | } 1066 | else if (gtcmd == 0x2) 1067 | { 1068 | unsigned short speed = 0; 1069 | if (gtcmddata != 0) 1070 | speed = -((ltable[STBL][gtcmddata-1] << 8) | rtable[STBL][gtcmddata-1]); 1071 | 1072 | waveptr = getslide(speed); 1073 | freq += (dur-3) * speed; // Assume 3 steps get lost for toneportamento purposes 1074 | } 1075 | else if (gtcmd == 0x4) 1076 | { 1077 | waveptr = getvibrato(0, gtcmddata); 1078 | } 1079 | else 1080 | { 1081 | printf("Warning: unsupported command %x in pattern %d step %d\n", gtcmd, e, c); 1082 | } 1083 | } 1084 | 1085 | durcolumn[d] = dur; 1086 | 1087 | // Check if toneportamento ends now 1088 | if (tptargetnote) 1089 | { 1090 | if (tpstepsleft < dur) 1091 | { 1092 | mpinstr = getlegatoinstr(lastnoteins); 1093 | 1094 | if (tpstepsleft == 0) 1095 | { 1096 | notecolumn[d] = (tptargetnote-pattbasetrans[e]-FIRSTNOTE-12)*2+MP_FIRSTNOTE; 1097 | cmdcolumn[d] = mpinstr; 1098 | } 1099 | else 1100 | { 1101 | if (waveptr && waveptr != lastwaveptr) 1102 | { 1103 | notecolumn[d] = MP_WAVEPTR; 1104 | cmdcolumn[d] = waveptr; 1105 | durcolumn[d] = tpstepsleft; 1106 | } 1107 | else 1108 | { 1109 | notecolumn[d] = MP_REST; 1110 | cmdcolumn[d] = 0; 1111 | durcolumn[d] = tpstepsleft; 1112 | } 1113 | ++d; 1114 | notecolumn[d] = (tptargetnote-pattbasetrans[e]-FIRSTNOTE-12)*2+MP_FIRSTNOTE; 1115 | cmdcolumn[d] = mpinstr; 1116 | durcolumn[d] = dur-tpstepsleft; 1117 | } 1118 | ++d; 1119 | tptargetnote = 0; 1120 | lastwaveptr = 0; // TP ended, consider next waveptr command individually again 1121 | lastnotempins = mpinstr; 1122 | continue; 1123 | } 1124 | else 1125 | { 1126 | tpstepsleft -= dur; 1127 | } 1128 | } 1129 | 1130 | if (note >= FIRSTNOTE+12 && note <= LASTNOTE) 1131 | { 1132 | lastwaveptr = 0; // If triggering a note, forget the last waveptr 1133 | 1134 | notecolumn[d] = (note-pattbasetrans[e]-FIRSTNOTE-12)*2+MP_FIRSTNOTE; 1135 | if (mpinstr != lastnotempins) 1136 | { 1137 | cmdcolumn[d] = mpinstr; 1138 | lastnoteins = instr; 1139 | lastnotempins = mpinstr; 1140 | } 1141 | // If waveptr in combination with note, must split step 1142 | if (waveptr && waveptr != lastwaveptr) 1143 | { 1144 | int requireddur = instrlastwavepos[instr]-instrfirstwavepos[instr] + 3; 1145 | if (dur > requireddur) 1146 | { 1147 | durcolumn[d] = requireddur; 1148 | ++d; 1149 | notecolumn[d] = MP_WAVEPTR; 1150 | cmdcolumn[d] = waveptr; 1151 | durcolumn[d] = dur-requireddur; 1152 | } 1153 | } 1154 | } 1155 | else 1156 | { 1157 | notecolumn[d] = note == KEYOFF ? MP_KEYOFF : MP_REST; 1158 | if (waveptr && waveptr != lastwaveptr) 1159 | { 1160 | notecolumn[d] = MP_WAVEPTR; 1161 | cmdcolumn[d] = waveptr; 1162 | } 1163 | } 1164 | 1165 | ++d; 1166 | lastwaveptr = waveptr; 1167 | } 1168 | 1169 | for (c = 0; c < MAX_PATTROWS*2;) 1170 | { 1171 | int merge = 0; 1172 | 1173 | if (notecolumn[c] == MP_ENDPATT || notecolumn[c+1] == MP_ENDPATT) 1174 | break; 1175 | 1176 | if ((durcolumn[c] + durcolumn[c+1]) <= maxdur && cmdcolumn[c+1] == 0) 1177 | { 1178 | if (notecolumn[c] == MP_KEYOFF && notecolumn[c+1] == MP_KEYOFF) 1179 | merge = 1; 1180 | else if (notecolumn[c] == MP_REST && notecolumn[c+1] == MP_REST) 1181 | merge = 1; 1182 | else if (notecolumn[c] == MP_KEYOFF && notecolumn[c+1] == MP_REST) 1183 | merge = 1; 1184 | else if (notecolumn[c] == MP_WAVEPTR && notecolumn[c+1] == MP_REST) 1185 | merge = 1; 1186 | else if (notecolumn[c] >= MP_FIRSTNOTE && notecolumn[c] <= MP_LASTNOTE && notecolumn[c+1] == MP_REST) 1187 | merge = 1; 1188 | } 1189 | 1190 | if (merge) 1191 | { 1192 | int d; 1193 | durcolumn[c] += durcolumn[c+1]; 1194 | for (d = c+1; d < MAX_PATTROWS*2-1; d++) 1195 | { 1196 | notecolumn[d] = notecolumn[d+1]; 1197 | cmdcolumn[d] = cmdcolumn[d+1]; 1198 | durcolumn[d] = durcolumn[d+1]; 1199 | } 1200 | } 1201 | else 1202 | c++; 1203 | } 1204 | 1205 | // Check if two consecutive durations can be averaged 1206 | for (c = 0; c < MAX_PATTROWS*2; c++) 1207 | { 1208 | int sum = durcolumn[c] + durcolumn[c+1]; 1209 | int average = 0; 1210 | 1211 | if (notecolumn[c] == MP_ENDPATT || notecolumn[c+1] == MP_ENDPATT) 1212 | break; 1213 | 1214 | if (!(sum & 1) && durcolumn[c] != durcolumn[c+1] && cmdcolumn[c+1] == 0 && (notecolumn[c+2] == MP_ENDPATT || durcolumn[c+2] != durcolumn[c+1])) 1215 | { 1216 | if (notecolumn[c] == MP_KEYOFF && notecolumn[c+1] == MP_KEYOFF) 1217 | average = 1; 1218 | else if (notecolumn[c] == MP_REST && notecolumn[c+1] == MP_REST) 1219 | average = 1; 1220 | else if (notecolumn[c] == MP_KEYOFF && notecolumn[c+1] == MP_REST) 1221 | average = 1; 1222 | else if (notecolumn[c] == MP_WAVEPTR && notecolumn[c+1] == MP_REST) 1223 | average = 1; 1224 | else if (notecolumn[c] >= MP_FIRSTNOTE && notecolumn[c] <= MP_LASTNOTE && notecolumn[c+1] == MP_REST) 1225 | average = 1; 1226 | } 1227 | if (average == 1) 1228 | { 1229 | durcolumn[c] = durcolumn[c+1] = sum/2; 1230 | c++; 1231 | } 1232 | } 1233 | 1234 | // Remove 1-2-1 duration changes if possible 1235 | for (c = 1; c < MAX_PATTROWS*2; c++) 1236 | { 1237 | if (notecolumn[c] == MP_ENDPATT || notecolumn[c+1] == MP_ENDPATT) 1238 | break; 1239 | 1240 | if (durcolumn[c+1] == durcolumn[c-1] && durcolumn[c] == 2*durcolumn[c-1] && notecolumn[c] >= MP_FIRSTNOTE && notecolumn[c] <= MP_LASTNOTE) 1241 | { 1242 | int d; 1243 | for (d = MAX_PATTROWS - 1; d > c; d--) 1244 | { 1245 | notecolumn[d+1] = notecolumn[d]; 1246 | cmdcolumn[d+1] = cmdcolumn[d]; 1247 | durcolumn[d+1]= durcolumn[d]; 1248 | } 1249 | notecolumn[c+1] = MP_REST; 1250 | cmdcolumn[c+1] = 0; 1251 | durcolumn[c] = durcolumn[c+1] = durcolumn[c-1]; 1252 | } 1253 | } 1254 | 1255 | // Fix if has too short durations 1256 | for (c = 0; c < MAX_PATTROWS*2; c++) 1257 | { 1258 | if (notecolumn[c] == MP_ENDPATT) 1259 | break; 1260 | if (durcolumn[c] > 0 && durcolumn[c] < 3) 1261 | { 1262 | int orig = durcolumn[c]; 1263 | int inc = 3-durcolumn[c]; 1264 | durcolumn[c] += inc; 1265 | durcolumn[c+1] -= inc; 1266 | printf("Warning: adjusting too short duration in pattern %d step %d to %d (next step adjusted to %d)\n", e, c, durcolumn[c], durcolumn[c+1]); 1267 | } 1268 | } 1269 | 1270 | // Clear unneeded durations 1271 | for (c = 0; c < MAX_PATTROWS*2; c++) 1272 | { 1273 | if (notecolumn[c] == MP_ENDPATT) 1274 | break; 1275 | if (c && durcolumn[c] == lastdur) 1276 | durcolumn[c] = 0; 1277 | if (durcolumn[c]) 1278 | lastdur = durcolumn[c]; 1279 | } 1280 | 1281 | // Build the final patterndata 1282 | for (c = 0; c < MAX_PATTROWS*2; c++) 1283 | { 1284 | if (notecolumn[c] == MP_ENDPATT) 1285 | { 1286 | mppatterns[e][pattlen++] = MP_ENDPATT; 1287 | break; 1288 | } 1289 | 1290 | if (notecolumn[c] >= MP_FIRSTNOTE && notecolumn[c] <= MP_LASTNOTE) 1291 | { 1292 | if (cmdcolumn[c]) 1293 | { 1294 | mppatterns[e][pattlen++] = notecolumn[c]; 1295 | mppatterns[e][pattlen++] = cmdcolumn[c]; 1296 | } 1297 | else 1298 | mppatterns[e][pattlen++] = notecolumn[c] + MP_NOCMD; 1299 | } 1300 | else if (notecolumn[c] == MP_WAVEPTR) 1301 | { 1302 | mppatterns[e][pattlen++] = notecolumn[c]; 1303 | mppatterns[e][pattlen++] = cmdcolumn[c]; 1304 | } 1305 | else 1306 | { 1307 | mppatterns[e][pattlen++] = notecolumn[c]; 1308 | } 1309 | if (durcolumn[c]) 1310 | { 1311 | mppatterns[e][pattlen++] = 0x101 - durcolumn[c]; 1312 | } 1313 | } 1314 | 1315 | if (pattlen > MAX_MPPATTLEN) 1316 | { 1317 | printf("Pattern %d does not fit in 256 bytes when compressed\n", e); 1318 | exit(1); 1319 | } 1320 | mppattlen[e] = pattlen; 1321 | } 1322 | 1323 | // Check if some pattern is always followed by another, and combine 1324 | do 1325 | { 1326 | memset(mppattnext, 0, sizeof mppattnext); 1327 | memset(mppattprev, 0, sizeof mppattprev); 1328 | mergepatt = 0; 1329 | for (e = 0; e <= highestusedsong; e++) 1330 | { 1331 | int f; 1332 | for (f = 0; f < mpsongtotallen[e]-1; ++f) 1333 | { 1334 | int patt = mptracks[e][f]; 1335 | if (patt >= 1 && patt < 128) 1336 | { 1337 | int nextpatt = mptracks[e][f+1]; 1338 | if (nextpatt >= 1 && nextpatt < 128) 1339 | { 1340 | if (mppattnext[patt] == 0) 1341 | mppattnext[patt] = nextpatt; 1342 | else if (mppattnext[patt] != nextpatt) 1343 | mppattnext[patt] = 0xff; 1344 | } 1345 | else 1346 | mppattnext[patt] = 0xff; 1347 | } 1348 | if (f > 0) 1349 | { 1350 | int prevpatt = mptracks[e][f-1]; 1351 | if (prevpatt >= 1 && prevpatt < 128) 1352 | { 1353 | if (mppattprev[patt] == 0) 1354 | mppattprev[patt] = prevpatt; 1355 | else if (mppattprev[patt] != prevpatt) 1356 | mppattprev[patt] = 0xff; 1357 | } 1358 | else 1359 | mppattprev[patt] = 0xff; 1360 | } 1361 | } 1362 | } 1363 | for (e = 1; e <= highestusedpatt+1; ++e) 1364 | { 1365 | if (mppattnext[e] > 0 && mppattnext[e] < 128 && mppattprev[mppattnext[e]] == e && mppattlen[e-1] + mppattlen[mppattnext[e]-1] <= 256) 1366 | { 1367 | int mergedest = e-1; 1368 | int mergesrc = mppattnext[e]-1; 1369 | int f,g; 1370 | 1371 | printf("Joining pattern %d into %d\n", mergesrc, mergedest); 1372 | mergepatt = 1; 1373 | 1374 | memcpy(&mppatterns[mergedest][mppattlen[mergedest]-1], &mppatterns[mergesrc][0], mppattlen[mergesrc]); 1375 | mppattlen[mergedest] = mppattlen[mergedest] + mppattlen[mergesrc] - 1; 1376 | for (f = mergesrc; f < highestusedpatt; ++f) 1377 | { 1378 | memcpy(&mppatterns[f][0], &mppatterns[f+1][0], MAX_MPPATTLEN); 1379 | mppattlen[f] = mppattlen[f+1]; 1380 | } 1381 | --highestusedpatt; 1382 | 1383 | for (f = 0; f <= highestusedsong; ++f) 1384 | { 1385 | for (g = 0; g < mpsongtotallen[f];) 1386 | { 1387 | if (mptracks[f][g] == mergesrc+1) 1388 | { 1389 | int h; 1390 | memmove(&mptracks[f][g], &mptracks[f][g+1], mpsongtotallen[f]-g-1); 1391 | --mpsongtotallen[f]; 1392 | if (g < mpsongstart[f][1]) 1393 | --mpsongstart[f][1]; 1394 | if (g < mpsongstart[f][2]) 1395 | --mpsongstart[f][2]; 1396 | // Adjust jump points 1397 | for (h = 0; h < mpsongtotallen[f]-1; ++h) 1398 | { 1399 | if (mptracks[f][h] == 0 && mptracks[f][h+1] > g) 1400 | --mptracks[f][h+1]; 1401 | } 1402 | } 1403 | else if (mptracks[f][g] == 0) 1404 | { 1405 | // Jump over the jump point 1406 | g += 2; 1407 | } 1408 | else 1409 | { 1410 | if (mptracks[f][g] > mergesrc+1 && mptracks[f][g] < 128) 1411 | --mptracks[f][g]; 1412 | ++g; 1413 | } 1414 | } 1415 | } 1416 | break; 1417 | } 1418 | } 1419 | } 1420 | while (mergepatt); 1421 | } 1422 | 1423 | unsigned char getlegatoinstr(unsigned char instr) 1424 | { 1425 | if (legatoinstrmap[instr]) 1426 | return legatoinstrmap[instr]; 1427 | 1428 | if (!legatostepmap[instrlastwavepos[instr]]) 1429 | { 1430 | if (mpwavetbl[instrlastwavepos[instr]-1] == 0x00 || mpwavetbl[instrlastwavepos[instr]-1] >= 0x90) 1431 | legatostepmap[instrlastwavepos[instr]] = instrlastwavepos[instr]; 1432 | else 1433 | { 1434 | if (mpwavesize >= 255) 1435 | { 1436 | printf("Out of wavetable space while converting legato instrument\n"); 1437 | exit(1); 1438 | } 1439 | mpwavetbl[mpwavesize] = 0xff; 1440 | mpnotetbl[mpwavesize] = mpnotetbl[instrlastwavepos[instr]-1]; 1441 | mpwavenexttbl[mpwavesize] = mpwavenexttbl[instrlastwavepos[instr]-1]; 1442 | legatostepmap[instrlastwavepos[instr]] = mpwavesize + 1; 1443 | ++mpwavesize; 1444 | } 1445 | } 1446 | 1447 | mpinswavepos[mplegatoinssize] = legatostepmap[instrlastwavepos[instr]]; 1448 | legatoinstrmap[instr] = mplegatoinssize+0x81; 1449 | ++mplegatoinssize; 1450 | 1451 | return legatoinstrmap[instr]; 1452 | } 1453 | 1454 | unsigned char getvibrato(unsigned char delay, unsigned char param) 1455 | { 1456 | if (delay == 0) 1457 | delay = 1; 1458 | 1459 | if (vibratomap[(delay<<8) + param]) 1460 | return vibratomap[(delay<<8) + param]; 1461 | 1462 | vibratomap[(delay<<8) + param] = mpwavesize+1; 1463 | 1464 | if (delay > 1 && mpwavesize < 254) 1465 | { 1466 | mpwavetbl[mpwavesize] = 0x100 - delay; 1467 | mpnotetbl[mpwavesize] = 0; 1468 | mpwavenexttbl[mpwavesize] = mpwavesize+1+1; 1469 | mpwavesize++; 1470 | } 1471 | 1472 | if (mpwavesize < 255) 1473 | { 1474 | mpwavetbl[mpwavesize] = 0; 1475 | mpnotetbl[mpwavesize] = 0xff - ltable[STBL][param-1]; 1476 | mpwavenexttbl[mpwavesize] = rtable[STBL][param-1]; 1477 | mpwavesize++; 1478 | } 1479 | 1480 | return vibratomap[(delay<<8) + param]; 1481 | } 1482 | 1483 | unsigned char getslide(unsigned short speed) 1484 | { 1485 | if (slidemap[speed]) 1486 | return slidemap[speed]; 1487 | 1488 | mpwavetbl[mpwavesize] = 0x90; 1489 | mpnotetbl[mpwavesize] = (speed-1) & 0xff; 1490 | mpwavenexttbl[mpwavesize] = (speed-1) >> 8; 1491 | slidemap[speed] = mpwavesize+1; 1492 | ++mpwavesize; 1493 | 1494 | return slidemap[speed]; 1495 | } 1496 | 1497 | void getpattbasetrans(void) 1498 | { 1499 | int c,e; 1500 | memset(pattbasetrans, 0, sizeof pattbasetrans); 1501 | 1502 | for (e = 0; e <= highestusedpatt; e++) 1503 | { 1504 | int basetrans = 0; 1505 | for (c = 0; c < MAX_PATTROWS+1; c++) 1506 | { 1507 | int note = pattern[e][c*4]; 1508 | if (note >= FIRSTNOTE && note < FIRSTNOTE+12) 1509 | { 1510 | printf("Pattern %d: octave 0 is not supported\n", e); 1511 | exit(1); 1512 | } 1513 | if (note >= FIRSTNOTE && note <= LASTNOTE) 1514 | { 1515 | if (note >= FIRSTNOTE+6*12) 1516 | { 1517 | while (note - basetrans >= FIRSTNOTE+6*12) 1518 | basetrans += 12; 1519 | } 1520 | } 1521 | } 1522 | for (c = 0; c < MAX_PATTROWS+1; c++) 1523 | { 1524 | int note = pattern[e][c*4]; 1525 | if (note >= FIRSTNOTE && note <= LASTNOTE) 1526 | { 1527 | if (note - basetrans < FIRSTNOTE+12) 1528 | { 1529 | printf("Pattern %d has too wide note range\n", e); 1530 | exit(1); 1531 | } 1532 | } 1533 | } 1534 | if (basetrans > 0) 1535 | printf("Pattern %d basetranspose %d\n", e, basetrans); 1536 | pattbasetrans[e] = basetrans; 1537 | } 1538 | } 1539 | 1540 | void getpatttempos(void) 1541 | { 1542 | int e,c; 1543 | 1544 | memset(patttempo, 0, sizeof patttempo); 1545 | memset(pattinstr, 0, sizeof pattinstr); 1546 | 1547 | // Simulates playroutine going through the songs 1548 | for (e = 0; e <= highestusedsong; e++) 1549 | { 1550 | printf("Determining tempo & instruments for song %d\n", e+1); 1551 | int sp[3] = {-1,-1,-1}; 1552 | int pp[3] = {0xff,0xff,0xff}; 1553 | int pn[3] = {0,0,0}; 1554 | int rep[3] = {0,0,0}; 1555 | int stop[3] = {0,0,0}; 1556 | int instr[3] = {1,1,1}; 1557 | int tempo[3] = {6,6,6}; 1558 | int tick[3] = {0,0,0}; 1559 | int keyon[3] = {0,0,0}; 1560 | 1561 | while (!stop[0] || !stop[1] || !stop[2]) 1562 | { 1563 | for (c = 0; c < MAX_CHN; c++) 1564 | { 1565 | if (!stop[c] && !tick[c]) 1566 | { 1567 | if (pp[c] == 0xff || pattern[pn[c]][pp[c]*4] == ENDPATT) 1568 | { 1569 | if (!rep[c]) 1570 | { 1571 | sp[c]++; 1572 | if (songorder[e][c][sp[c]] >= LOOPSONG) 1573 | { 1574 | stop[c] = 1; 1575 | break; 1576 | } 1577 | while (songorder[e][c][sp[c]] >= TRANSDOWN) 1578 | sp[c]++; 1579 | while ((songorder[e][c][sp[c]] >= REPEAT) && (songorder[e][c][sp[c]] < TRANSDOWN)) 1580 | { 1581 | rep[c] = songorder[e][c][sp[c]] - REPEAT; 1582 | sp[c]++; 1583 | } 1584 | } 1585 | else 1586 | rep[c]--; 1587 | 1588 | pn[c] = songorder[e][c][sp[c]]; 1589 | pp[c] = 0; 1590 | } 1591 | 1592 | if (!stop[c]) 1593 | { 1594 | int note = pattern[pn[c]][pp[c]*4]; 1595 | if (note >= FIRSTNOTE && note <= LASTNOTE && pattern[pn[c]][pp[c]*4+2] != CMD_TONEPORTA) 1596 | keyon[c] = 1; 1597 | if (note == KEYON) 1598 | keyon[c] = 1; 1599 | if (note == KEYOFF) 1600 | keyon[c] = 0; 1601 | 1602 | instr[c] = pattern[pn[c]][pp[c]*4+1]; 1603 | 1604 | if (pattern[pn[c]][pp[c]*4+2] == 0xf) 1605 | { 1606 | int newtempo = pattern[pn[c]][pp[c]*4+3]; 1607 | if (newtempo < 0x80) 1608 | { 1609 | tempo[0] = newtempo; 1610 | tempo[1] = newtempo; 1611 | tempo[2] = newtempo; 1612 | } 1613 | else 1614 | tempo[c] = newtempo & 0x7f; 1615 | } 1616 | } 1617 | } 1618 | } 1619 | 1620 | for (c = 0; c < MAX_CHN; c++) 1621 | { 1622 | if (!stop[c]) 1623 | { 1624 | if (pp[c] < MAX_PATTROWS) 1625 | { 1626 | if (patttempo[pn[c]][pp[c]] != 0 && patttempo[pn[c]][pp[c]] != tempo[c]) 1627 | { 1628 | // We have detected pattern playback with multiple tempos 1629 | int f; 1630 | int pattfound = 0; 1631 | 1632 | for (f = 0; f < remappedpatterns; f++) 1633 | { 1634 | if (pattremaptempo[f] == tempo[c] && pattremapsrc[f] == pn[c]) 1635 | { 1636 | pattfound = 1; 1637 | pn[c] = pattremapdest[f]; 1638 | songorder[e][c][sp[c]] = pn[c]; 1639 | break; 1640 | } 1641 | } 1642 | 1643 | if (!pattfound) 1644 | { 1645 | if (highestusedpatt >= MAX_PATT-1) 1646 | { 1647 | printf("Not enough patterns free to perform tempo remapping"); 1648 | exit(1); 1649 | } 1650 | printf("Remapping pattern %d to %d as it's played at multiple tempos\n", pn[c], highestusedpatt+1); 1651 | memcpy(&pattern[highestusedpatt+1][0], &pattern[pn[c]][0], MAX_PATTROWS*4+4); 1652 | pattremaptempo[remappedpatterns] = tempo[c]; 1653 | pattremapsrc[remappedpatterns] = pn[c]; 1654 | pattremapdest[remappedpatterns] = highestusedpatt+1; 1655 | remappedpatterns++; 1656 | pn[c] = highestusedpatt+1; 1657 | songorder[e][c][sp[c]] = pn[c]; 1658 | highestusedpatt++; 1659 | } 1660 | } 1661 | 1662 | if (!tick[c]) 1663 | { 1664 | patttempo[pn[c]][pp[c]] = tempo[c]; 1665 | pattinstr[pn[c]][pp[c]] = instr[c]; 1666 | pattkeyon[pn[c]][pp[c]] = keyon[c]; 1667 | } 1668 | } 1669 | 1670 | tick[c]++; 1671 | if (tick[c] >= tempo[c]) 1672 | { 1673 | tick[c] = 0; 1674 | pp[c]++; 1675 | } 1676 | } 1677 | } 1678 | } 1679 | } 1680 | } 1681 | 1682 | void savempsong(const char* songfilename) 1683 | { 1684 | int c; 1685 | int totalsize = 0; 1686 | 1687 | if (!baremode) 1688 | totalsize += 7; 1689 | totalsize += (highestusedsong+1) * 5; 1690 | totalsize += (highestusedpatt+1) * 2; 1691 | totalsize += mpinssize * 5; 1692 | totalsize += mplegatoinssize; 1693 | totalsize += mpwavesize * 3; 1694 | totalsize += mppulsesize * 3; 1695 | totalsize += mpfiltsize * 3; 1696 | for (c = 0; c <= highestusedsong; ++c) 1697 | totalsize += mpsongtotallen[c]; 1698 | for (c = 0; c <= highestusedpatt; ++c) 1699 | totalsize += mppattlen[c]; 1700 | 1701 | out = fopen(songfilename, "wt"); 1702 | if (!out) 1703 | { 1704 | printf("Could not open destination file %s\n", songfilename); 1705 | exit(1); 1706 | } 1707 | 1708 | if (endaddress >= 0) 1709 | startaddress = endaddress - totalsize; 1710 | 1711 | if (startaddress >= 0) 1712 | { 1713 | fprintf(out, " org $%04x\n", startaddress); 1714 | fprintf(out, " processor 6502\n"); 1715 | fprintf(out, "\n"); 1716 | } 1717 | 1718 | if (!baremode) 1719 | { 1720 | fprintf(out, "musicHeader:\n"); 1721 | fprintf(out, " dc.b %d\n", (highestusedsong+1)*5); 1722 | fprintf(out, " dc.b %d\n", highestusedpatt+1); 1723 | fprintf(out, " dc.b %d\n", mpinssize); 1724 | fprintf(out, " dc.b %d\n", mplegatoinssize); 1725 | fprintf(out, " dc.b %d\n", mpwavesize); 1726 | fprintf(out, " dc.b %d\n", mppulsesize); 1727 | fprintf(out, " dc.b %d\n", mpfiltsize); 1728 | fprintf(out, "\n"); 1729 | } 1730 | 1731 | fprintf(out, "songTbl:\n"); 1732 | for (c = 0; c <= highestusedsong; ++c) 1733 | { 1734 | fprintf(out, " dc.w song%d-1\n", c); 1735 | fprintf(out, " dc.b $%02x\n", mpsongstart[c][0]+1); 1736 | fprintf(out, " dc.b $%02x\n", mpsongstart[c][1]+1); 1737 | fprintf(out, " dc.b $%02x\n", mpsongstart[c][2]+1); 1738 | } 1739 | fprintf(out, "\n"); 1740 | 1741 | fprintf(out, "pattTblLo:\n"); 1742 | for (c = 0; c <= highestusedpatt; ++c) 1743 | fprintf(out, " dc.b patt%d\n", c); 1748 | fprintf(out, "\n"); 1749 | 1750 | writeblock(out, "insAD", mpinsad, mpinssize); 1751 | writeblock(out, "insSR", mpinssr, mpinssize); 1752 | writeblock(out, "insFirstWave", mpinsfirstwave, mpinssize); 1753 | writeblock(out, "insPulsePos", mpinspulsepos, mpinssize); 1754 | writeblock(out, "insFiltPos", mpinsfiltpos, mpinssize); 1755 | writeblock(out, "insWavePos", mpinswavepos, mplegatoinssize); 1756 | writeblock(out, "waveTbl", mpwavetbl, mpwavesize); 1757 | writeblock(out, "noteTbl", mpnotetbl, mpwavesize); 1758 | writeblock(out, "waveNextTbl", mpwavenexttbl, mpwavesize); 1759 | writeblock(out, "pulseLimitTbl", mppulselimittbl, mppulsesize); 1760 | writeblock(out, "pulseSpdTbl", mppulsespdtbl, mppulsesize); 1761 | writeblock(out, "pulseNextTbl", mppulsenexttbl, mppulsesize); 1762 | writeblock(out, "filtLimitTbl", mpfiltlimittbl, mpfiltsize); 1763 | writeblock(out, "filtSpdTbl", mpfiltspdtbl, mpfiltsize); 1764 | writeblock(out, "filtNextTbl", mpfiltnexttbl, mpfiltsize); 1765 | 1766 | for (c = 0; c <= highestusedsong; ++c) 1767 | { 1768 | char namebuf[80]; 1769 | sprintf(namebuf, "song%d", c); 1770 | writeblock(out, namebuf, &mptracks[c][0], mpsongtotallen[c]); 1771 | } 1772 | 1773 | for (c = 0; c <= highestusedpatt; ++c) 1774 | { 1775 | char namebuf[80]; 1776 | sprintf(namebuf, "patt%d", c); 1777 | writeblock(out, namebuf, &mppatterns[c][0], mppattlen[c]); 1778 | } 1779 | } 1780 | 1781 | void writeblock(FILE* out, const char* name, unsigned char* data, int len) 1782 | { 1783 | int c; 1784 | fprintf(out, "%s:\n", name); 1785 | for (c = 0; c < len; ++c) 1786 | { 1787 | if (c == 0) 1788 | fprintf(out, " dc.b $%02x", data[c]); 1789 | else if ((c & 7) == 0) 1790 | fprintf(out, "\n dc.b $%02x", data[c]); 1791 | else 1792 | fprintf(out, ",$%02x", data[c]); 1793 | } 1794 | fprintf(out, "\n\n"); 1795 | } -------------------------------------------------------------------------------- /musicdata.s: -------------------------------------------------------------------------------- 1 | songTbl: 2 | dc.w song0-1 3 | dc.b $01 4 | dc.b $0b 5 | dc.b $14 6 | 7 | pattTblLo: 8 | dc.b patt0 24 | dc.b >patt1 25 | dc.b >patt2 26 | dc.b >patt3 27 | dc.b >patt4 28 | dc.b >patt5 29 | dc.b >patt6 30 | dc.b >patt7 31 | dc.b >patt8 32 | dc.b >patt9 33 | dc.b >patt10 34 | dc.b >patt11 35 | dc.b >patt12 36 | 37 | insAD: 38 | dc.b $cc,$00,$00,$00,$02,$00,$06,$00 39 | dc.b $07,$01,$01,$22,$02,$02,$02,$00 40 | dc.b $00,$00,$01,$01,$01 41 | 42 | insSR: 43 | dc.b $ba,$67,$67,$ba,$3c,$e9,$9a,$5b 44 | dc.b $a8,$9c,$5c,$7a,$3c,$3c,$3c,$5b 45 | dc.b $5b,$5b,$5c,$5c,$5c 46 | 47 | insFirstWave: 48 | dc.b $09,$09,$09,$09,$09,$09,$09,$09 49 | dc.b $09,$09,$09,$09,$09,$09,$09,$09 50 | dc.b $09,$09,$09,$09,$09 51 | 52 | insPulsePos: 53 | dc.b $82,$86,$00,$82,$8b,$92,$93,$96 54 | dc.b $92,$00,$00,$9a,$8b,$8b,$8b,$96 55 | dc.b $96,$96,$00,$00,$00 56 | 57 | insFiltPos: 58 | dc.b $81,$00,$00,$81,$00,$84,$84,$00 59 | dc.b $84,$00,$00,$00,$00,$00,$00,$00 60 | dc.b $00,$00,$00,$00,$00 61 | 62 | insWavePos: 63 | dc.b $01,$02,$04,$06,$07,$08,$0d,$10 64 | dc.b $11,$1b,$1d,$1e,$20,$24,$28,$2c 65 | dc.b $30,$34,$38,$3c,$40,$45,$49 66 | 67 | waveTbl: 68 | dc.b $41,$81,$41,$81,$41,$41,$41,$81 69 | dc.b $41,$40,$80,$80,$81,$41,$41,$41 70 | dc.b $81,$41,$41,$40,$40,$40,$40,$40 71 | dc.b $40,$40,$41,$21,$41,$51,$41,$41 72 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 73 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 74 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 75 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 76 | dc.b $fe,$fe,$fe,$90,$ff,$90,$90,$90 77 | dc.b $ff,$00,$90,$90,$90,$00,$00,$90 78 | 79 | noteTbl: 80 | dc.b $00,$b9,$00,$bd,$00,$00,$00,$bd 81 | dc.b $a1,$99,$bf,$b9,$b9,$99,$00,$00 82 | dc.b $bd,$14,$10,$12,$0c,$0a,$08,$06 83 | dc.b $04,$02,$00,$00,$00,$00,$00,$00 84 | dc.b $03,$07,$00,$00,$03,$08,$00,$00 85 | dc.b $04,$07,$00,$00,$04,$07,$00,$00 86 | dc.b $03,$07,$00,$00,$03,$08,$00,$00 87 | dc.b $03,$07,$00,$00,$04,$07,$00,$00 88 | dc.b $03,$08,$00,$07,$00,$03,$ef,$1f 89 | dc.b $00,$fa,$4f,$47,$7f,$fb,$fb,$df 90 | 91 | waveNextTbl: 92 | dc.b $00,$03,$00,$05,$00,$00,$00,$09 93 | dc.b $0a,$0b,$0c,$00,$0e,$0f,$00,$00 94 | dc.b $12,$13,$14,$15,$16,$17,$18,$19 95 | dc.b $1a,$00,$1c,$00,$00,$1f,$00,$21 96 | dc.b $22,$23,$21,$25,$26,$27,$25,$29 97 | dc.b $2a,$2b,$29,$2d,$2e,$2f,$2d,$31 98 | dc.b $32,$33,$31,$35,$36,$37,$35,$39 99 | dc.b $3a,$3b,$39,$3d,$3e,$3f,$3d,$41 100 | dc.b $42,$43,$41,$00,$00,$00,$ff,$00 101 | dc.b $00,$10,$00,$00,$00,$10,$20,$ff 102 | 103 | pulseLimitTbl: 104 | dc.b $00,$02,$0f,$01,$02,$04,$0a,$12 105 | dc.b $02,$04,$40,$38,$0e,$16,$01,$f8 106 | dc.b $0e,$08,$06,$21,$06,$04,$0a,$01 107 | dc.b $04,$40,$f7,$01,$f7 108 | 109 | pulseSpdTbl: 110 | dc.b $00,$00,$20,$df,$20,$00,$10,$ef 111 | dc.b $ef,$10,$00,$10,$10,$ef,$ef,$10 112 | dc.b $10,$00,$00,$cf,$30,$00,$40,$bf 113 | dc.b $40,$00,$30,$cf,$30 114 | 115 | pulseNextTbl: 116 | dc.b $00,$03,$04,$05,$03,$07,$08,$09 117 | dc.b $0a,$07,$0c,$0d,$0e,$0f,$10,$11 118 | dc.b $0e,$00,$14,$15,$14,$17,$18,$19 119 | dc.b $17,$1b,$1c,$1d,$1c 120 | 121 | filtLimitTbl: 122 | dc.b $40,$bf,$40,$f8 123 | 124 | filtSpdTbl: 125 | dc.b $91,$01,$ff,$91 126 | 127 | filtNextTbl: 128 | dc.b $02,$03,$02,$00 129 | 130 | song0: 131 | dc.b $80,$01,$04,$05,$07,$05,$0b,$07 132 | dc.b $00,$01,$80,$02,$02,$08,$02,$0c 133 | dc.b $08,$00,$0b,$80,$03,$06,$09,$03 134 | dc.b $0d,$09,$00,$14 135 | 136 | patt0: 137 | dc.b $06,$01,$99,$7f,$7c,$44,$d1,$16 138 | dc.b $96,$81,$7f,$00 139 | 140 | patt1: 141 | dc.b $36,$02,$f9,$1e,$03,$1f,$37,$1f 142 | dc.b $1f,$37,$1f,$1f,$37,$1f,$1f,$3b 143 | dc.b $37,$33,$7f,$37,$1f,$1f,$37,$1f 144 | dc.b $1f,$37,$1f,$1f,$2d,$e9,$2f,$f1 145 | dc.b $3d,$36,$02,$f9,$1e,$03,$1f,$37 146 | dc.b $1f,$1f,$37,$1f,$1f,$37,$1f,$1f 147 | dc.b $3b,$37,$33,$7f,$37,$1f,$1f,$37 148 | dc.b $1f,$1f,$37,$1f,$1f,$2c,$02,$e9 149 | dc.b $2f,$f1,$3d,$36,$02,$f9,$28,$03 150 | dc.b $29,$37,$29,$29,$37,$29,$29,$37 151 | dc.b $29,$29,$3b,$37,$33,$7f,$37,$29 152 | dc.b $29,$37,$29,$29,$37,$29,$29,$2d 153 | dc.b $e9,$2f,$f1,$3d,$3a,$02,$f9,$2c 154 | dc.b $03,$2d,$3b,$2d,$2d,$3b,$2d,$2d 155 | dc.b $3b,$2d,$2d,$3d,$3b,$37,$7f,$3b 156 | dc.b $2d,$2d,$3b,$2d,$2d,$41,$e1,$3d 157 | dc.b $f1,$3b,$f9,$33,$e9,$00 158 | 159 | patt2: 160 | dc.b $4e,$0d,$81,$7e,$4e,$0e,$7e,$4e 161 | dc.b $0d,$81,$7e,$4f,$7e,$d1,$4a,$0f 162 | dc.b $b1,$00 163 | 164 | patt3: 165 | dc.b $10,$04,$91,$7f,$7c,$46,$e1,$14 166 | dc.b $96,$81,$7f,$d1,$1a,$01,$7c,$47 167 | dc.b $e1,$00 168 | 169 | patt4: 170 | dc.b $06,$07,$f9,$07,$1e,$06,$06,$07 171 | dc.b $7f,$07,$06,$06,$06,$07,$7f,$07 172 | dc.b $06,$06,$06,$07,$7f,$07,$06,$06 173 | dc.b $0a,$07,$07,$07,$06,$06,$06,$07 174 | dc.b $7f,$07,$06,$06,$06,$07,$7f,$07 175 | dc.b $06,$06,$0a,$07,$0d,$0b,$06,$06 176 | dc.b $02,$07,$17,$17,$16,$06,$16,$07 177 | dc.b $7f,$17,$16,$06,$16,$07,$7f,$17 178 | dc.b $16,$06,$16,$07,$17,$17,$16,$06 179 | dc.b $1a,$07,$17,$17,$16,$06,$16,$07 180 | dc.b $7f,$17,$16,$06,$16,$07,$7f,$17 181 | dc.b $16,$06,$16,$07,$17,$1b,$1e,$06 182 | dc.b $16,$07,$10,$07,$f9,$11,$10,$06 183 | dc.b $10,$07,$7f,$11,$10,$06,$10,$07 184 | dc.b $7f,$11,$10,$06,$10,$07,$7f,$11 185 | dc.b $10,$06,$0c,$07,$11,$11,$10,$06 186 | dc.b $10,$07,$7f,$11,$10,$06,$10,$07 187 | dc.b $7f,$11,$10,$06,$10,$07,$11,$15 188 | dc.b $16,$06,$1a,$07,$15,$15,$14,$06 189 | dc.b $14,$07,$7f,$15,$14,$06,$14,$07 190 | dc.b $7f,$15,$14,$06,$14,$07,$11,$15 191 | dc.b $1a,$06,$7f,$14,$07,$15,$14,$06 192 | dc.b $14,$07,$7f,$15,$14,$06,$1a,$07 193 | dc.b $7f,$1b,$1a,$06,$7f,$1b,$1b,$1e 194 | dc.b $09,$17,$00 195 | 196 | patt5: 197 | dc.b $4a,$10,$f9,$4e,$11,$f1,$4f,$d9 198 | dc.b $7e,$a1,$7f,$4a,$10,$f9,$47,$f1 199 | dc.b $47,$d9,$7e,$a1,$7f,$4a,$10,$f9 200 | dc.b $4e,$11,$f1,$4f,$d9,$7e,$a1,$7f 201 | dc.b $4a,$10,$f9,$4e,$11,$f1,$4f,$b9 202 | dc.b $7e,$e1,$52,$12,$a1,$7e,$e1,$00 203 | 204 | patt6: 205 | dc.b $06,$07,$f9,$07,$06,$06,$06,$07 206 | dc.b $7f,$07,$06,$06,$06,$07,$7f,$07 207 | dc.b $06,$06,$06,$07,$7f,$07,$06,$06 208 | dc.b $02,$07,$0d,$0d,$0c,$06,$0c,$07 209 | dc.b $7f,$0d,$0c,$06,$0c,$07,$7f,$0d 210 | dc.b $0c,$06,$0c,$07,$7f,$0d,$0c,$06 211 | dc.b $10,$07,$15,$15,$14,$06,$14,$07 212 | dc.b $7f,$15,$14,$06,$14,$07,$7f,$15 213 | dc.b $14,$06,$14,$07,$7f,$15,$14,$06 214 | dc.b $14,$07,$17,$17,$16,$06,$16,$07 215 | dc.b $7f,$17,$0c,$06,$0c,$07,$7f,$0d 216 | dc.b $1a,$06,$1a,$07,$7f,$1b,$1a,$06 217 | dc.b $1b,$06,$07,$f9,$07,$06,$06,$06 218 | dc.b $07,$7f,$07,$06,$06,$06,$07,$7f 219 | dc.b $07,$06,$06,$06,$07,$7f,$07,$06 220 | dc.b $06,$02,$07,$0d,$0d,$0c,$06,$0c 221 | dc.b $07,$7f,$0d,$0c,$06,$0c,$07,$7f 222 | dc.b $0d,$0c,$06,$0c,$07,$7f,$0d,$0c 223 | dc.b $06,$10,$07,$15,$15,$14,$06,$14 224 | dc.b $07,$7f,$15,$14,$06,$14,$07,$7f 225 | dc.b $15,$14,$06,$14,$07,$7f,$15,$14 226 | dc.b $06,$14,$07,$17,$17,$16,$06,$16 227 | dc.b $07,$7f,$17,$16,$06,$16,$07,$15 228 | dc.b $15,$14,$06,$14,$07,$7f,$14,$06 229 | dc.b $1a,$09,$17,$00 230 | 231 | patt7: 232 | dc.b $7f,$e9,$44,$0a,$f9,$4f,$53,$f1 233 | dc.b $53,$fd,$7c,$48,$f7,$54,$97,$52 234 | dc.b $0a,$f1,$4f,$45,$f9,$7c,$4a,$7e 235 | dc.b $e9,$4b,$f9,$55,$59,$f1,$59,$fd 236 | dc.b $7c,$4b,$f7,$5c,$97,$58,$0a,$f1 237 | dc.b $55,$59,$f9,$7c,$4a,$7e,$e9,$4b 238 | dc.b $f9,$53,$55,$f1,$55,$fd,$7c,$4b 239 | dc.b $f8,$58,$97,$f6,$54,$0a,$f1,$53 240 | dc.b $4b,$4f,$7c,$4a,$55,$59,$fd,$7c 241 | dc.b $4b,$f7,$5c,$97,$ef,$58,$0a,$f1 242 | dc.b $7c,$4a,$7e,$7c,$4a,$e9,$44,$0a 243 | dc.b $f9,$4f,$53,$f1,$53,$fd,$7c,$48 244 | dc.b $f7,$54,$97,$52,$0a,$f1,$4f,$45 245 | dc.b $f9,$7c,$4a,$7e,$e9,$4b,$f9,$55 246 | dc.b $59,$f1,$59,$fd,$7c,$4c,$f6,$5c 247 | dc.b $97,$f8,$58,$0a,$f1,$55,$67,$67 248 | dc.b $fd,$7c,$4d,$f8,$6a,$97,$f6,$7c 249 | dc.b $4a,$f1,$7e,$f9,$62,$0a,$f1,$7c 250 | dc.b $4a,$e1,$7e,$f1,$5d,$f9,$63,$67 251 | dc.b $fd,$5e,$97,$55,$4f,$47,$4f,$55 252 | dc.b $5f,$6d,$67,$5f,$55,$4f,$55,$5f 253 | dc.b $67,$62,$0a,$5c,$97,$53,$4b,$45 254 | dc.b $4b,$53,$5d,$6b,$63,$5d,$53,$4b 255 | dc.b $53,$5d,$63,$00 256 | 257 | patt8: 258 | dc.b $4e,$0d,$f9,$4e,$13,$89,$54,$0f 259 | dc.b $f9,$54,$14,$89,$5c,$0d,$f9,$5c 260 | dc.b $13,$89,$4e,$0e,$f9,$4e,$15,$d9 261 | dc.b $54,$0f,$f9,$54,$14,$e9,$4a,$0f 262 | dc.b $f9,$4a,$14,$d9,$4e,$0d,$f9,$4e 263 | dc.b $13,$89,$54,$0f,$f9,$54,$14,$89 264 | dc.b $5c,$0d,$f9,$5c,$13,$89,$4e,$0e 265 | dc.b $f9,$4e,$15,$c9,$5c,$0d,$f9,$5c 266 | dc.b $13,$c9,$00 267 | 268 | patt9: 269 | dc.b $7f,$d1,$00 270 | 271 | patt10: 272 | dc.b $1e,$07,$f1,$1e,$06,$f9,$1e,$07 273 | dc.b $7f,$1f,$1a,$06,$1e,$07,$7f,$1b 274 | dc.b $1a,$06,$1a,$07,$1f,$1f,$1e,$06 275 | dc.b $1a,$07,$1f,$7f,$1e,$06,$1e,$07 276 | dc.b $7f,$1f,$1a,$06,$1e,$07,$7f,$1b 277 | dc.b $1a,$06,$1a,$07,$1f,$1f,$1e,$06 278 | dc.b $1a,$07,$15,$7f,$14,$06,$14,$07 279 | dc.b $7f,$15,$10,$06,$14,$07,$7f,$11 280 | dc.b $10,$06,$10,$07,$15,$15,$14,$06 281 | dc.b $10,$07,$15,$7f,$14,$06,$14,$07 282 | dc.b $7f,$15,$10,$06,$14,$07,$7f,$11 283 | dc.b $10,$06,$10,$07,$15,$15,$14,$06 284 | dc.b $10,$07,$28,$07,$f1,$28,$06,$f9 285 | dc.b $28,$07,$7f,$29,$24,$06,$28,$07 286 | dc.b $7f,$25,$24,$06,$24,$07,$29,$29 287 | dc.b $28,$06,$24,$07,$29,$7f,$28,$06 288 | dc.b $28,$07,$7f,$29,$24,$06,$28,$07 289 | dc.b $7f,$25,$24,$06,$24,$07,$29,$29 290 | dc.b $28,$06,$24,$07,$1f,$7f,$1e,$06 291 | dc.b $1e,$07,$7f,$1f,$1a,$06,$1e,$07 292 | dc.b $7f,$1b,$1a,$06,$1a,$07,$1f,$1f 293 | dc.b $1e,$06,$1a,$07,$1f,$7f,$1e,$06 294 | dc.b $1e,$07,$7f,$1f,$1a,$06,$1e,$07 295 | dc.b $7f,$1b,$1a,$06,$1a,$07,$1f,$1e 296 | dc.b $06,$1f,$1a,$09,$00 297 | 298 | patt11: 299 | dc.b $66,$0c,$f9,$55,$4f,$55,$67,$55 300 | dc.b $63,$55,$4f,$55,$67,$7c,$4e,$e9 301 | dc.b $63,$f1,$67,$f9,$55,$4f,$55,$67 302 | dc.b $55,$6b,$55,$4f,$55,$63,$7c,$4e 303 | dc.b $e9,$61,$f1,$63,$f9,$5d,$53,$5d 304 | dc.b $63,$5d,$61,$59,$4f,$59,$63,$7c 305 | dc.b $4e,$e9,$61,$f1,$63,$f9,$5d,$53 306 | dc.b $5d,$63,$5d,$61,$59,$4f,$59,$63 307 | dc.b $7c,$4f,$f1,$67,$f9,$69,$6d,$70 308 | dc.b $0c,$f9,$5f,$59,$5f,$71,$5f,$6d 309 | dc.b $5f,$59,$5f,$6b,$7c,$4e,$e9,$67 310 | dc.b $f1,$71,$f9,$5f,$59,$5f,$71,$5f 311 | dc.b $75,$5f,$59,$5f,$6d,$7c,$4e,$e9 312 | dc.b $6b,$f1,$6d,$f9,$67,$5d,$67,$6d 313 | dc.b $67,$6b,$63,$59,$63,$6d,$7c,$4e 314 | dc.b $e9,$6b,$f1,$6d,$f9,$67,$5d,$67 315 | dc.b $6d,$67,$6b,$63,$59,$63,$6d,$7c 316 | dc.b $4e,$e9,$7e,$f1,$00 317 | 318 | patt12: 319 | dc.b $5c,$05,$d1,$59,$e1,$55,$53,$f1 320 | dc.b $5d,$d1,$59,$e1,$53,$53,$f1,$53 321 | dc.b $d1,$4f,$e1,$53,$4f,$f1,$53,$d1 322 | dc.b $4f,$e1,$53,$e9,$55,$f9,$59,$5d 323 | dc.b $4e,$05,$d1,$4b,$e1,$4f,$4b,$f1 324 | dc.b $4f,$d1,$55,$e1,$5d,$59,$f1,$5d 325 | dc.b $d1,$59,$e1,$5d,$59,$f1,$5d,$d1 326 | dc.b $59,$e1,$5d,$e9,$2c,$08,$fe,$7c 327 | dc.b $50,$ec,$00 328 | 329 | -------------------------------------------------------------------------------- /musicmodule.s: -------------------------------------------------------------------------------- 1 | org $1000 2 | processor 6502 3 | 4 | musicHeader: 5 | dc.b 5 6 | dc.b 13 7 | dc.b 21 8 | dc.b 23 9 | dc.b 80 10 | dc.b 29 11 | dc.b 4 12 | 13 | songTbl: 14 | dc.w song0-1 15 | dc.b $01 16 | dc.b $0b 17 | dc.b $14 18 | 19 | pattTblLo: 20 | dc.b patt0 36 | dc.b >patt1 37 | dc.b >patt2 38 | dc.b >patt3 39 | dc.b >patt4 40 | dc.b >patt5 41 | dc.b >patt6 42 | dc.b >patt7 43 | dc.b >patt8 44 | dc.b >patt9 45 | dc.b >patt10 46 | dc.b >patt11 47 | dc.b >patt12 48 | 49 | insAD: 50 | dc.b $cc,$00,$00,$00,$02,$00,$06,$00 51 | dc.b $07,$01,$01,$22,$02,$02,$02,$00 52 | dc.b $00,$00,$01,$01,$01 53 | 54 | insSR: 55 | dc.b $ba,$67,$67,$ba,$3c,$e9,$9a,$5b 56 | dc.b $a8,$9c,$5c,$7a,$3c,$3c,$3c,$5b 57 | dc.b $5b,$5b,$5c,$5c,$5c 58 | 59 | insFirstWave: 60 | dc.b $09,$09,$09,$09,$09,$09,$09,$09 61 | dc.b $09,$09,$09,$09,$09,$09,$09,$09 62 | dc.b $09,$09,$09,$09,$09 63 | 64 | insPulsePos: 65 | dc.b $82,$86,$00,$82,$8b,$92,$93,$96 66 | dc.b $92,$00,$00,$9a,$8b,$8b,$8b,$96 67 | dc.b $96,$96,$00,$00,$00 68 | 69 | insFiltPos: 70 | dc.b $81,$00,$00,$81,$00,$84,$84,$00 71 | dc.b $84,$00,$00,$00,$00,$00,$00,$00 72 | dc.b $00,$00,$00,$00,$00 73 | 74 | insWavePos: 75 | dc.b $01,$02,$04,$06,$07,$08,$0d,$10 76 | dc.b $11,$1b,$1d,$1e,$20,$24,$28,$2c 77 | dc.b $30,$34,$38,$3c,$40,$45,$49 78 | 79 | waveTbl: 80 | dc.b $41,$81,$41,$81,$41,$41,$41,$81 81 | dc.b $41,$40,$80,$80,$81,$41,$41,$41 82 | dc.b $81,$41,$41,$40,$40,$40,$40,$40 83 | dc.b $40,$40,$41,$21,$41,$51,$41,$41 84 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 85 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 86 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 87 | dc.b $fe,$fe,$fe,$41,$fe,$fe,$fe,$41 88 | dc.b $fe,$fe,$fe,$90,$ff,$90,$90,$90 89 | dc.b $ff,$00,$90,$90,$90,$00,$00,$90 90 | 91 | noteTbl: 92 | dc.b $00,$b9,$00,$bd,$00,$00,$00,$bd 93 | dc.b $a1,$99,$bf,$b9,$b9,$99,$00,$00 94 | dc.b $bd,$14,$10,$12,$0c,$0a,$08,$06 95 | dc.b $04,$02,$00,$00,$00,$00,$00,$00 96 | dc.b $03,$07,$00,$00,$03,$08,$00,$00 97 | dc.b $04,$07,$00,$00,$04,$07,$00,$00 98 | dc.b $03,$07,$00,$00,$03,$08,$00,$00 99 | dc.b $03,$07,$00,$00,$04,$07,$00,$00 100 | dc.b $03,$08,$00,$07,$00,$03,$ef,$1f 101 | dc.b $00,$fa,$4f,$47,$7f,$fb,$fb,$df 102 | 103 | waveNextTbl: 104 | dc.b $00,$03,$00,$05,$00,$00,$00,$09 105 | dc.b $0a,$0b,$0c,$00,$0e,$0f,$00,$00 106 | dc.b $12,$13,$14,$15,$16,$17,$18,$19 107 | dc.b $1a,$00,$1c,$00,$00,$1f,$00,$21 108 | dc.b $22,$23,$21,$25,$26,$27,$25,$29 109 | dc.b $2a,$2b,$29,$2d,$2e,$2f,$2d,$31 110 | dc.b $32,$33,$31,$35,$36,$37,$35,$39 111 | dc.b $3a,$3b,$39,$3d,$3e,$3f,$3d,$41 112 | dc.b $42,$43,$41,$00,$00,$00,$ff,$00 113 | dc.b $00,$10,$00,$00,$00,$10,$20,$ff 114 | 115 | pulseLimitTbl: 116 | dc.b $00,$02,$0f,$01,$02,$04,$0a,$12 117 | dc.b $02,$04,$40,$38,$0e,$16,$01,$f8 118 | dc.b $0e,$08,$06,$21,$06,$04,$0a,$01 119 | dc.b $04,$40,$f7,$01,$f7 120 | 121 | pulseSpdTbl: 122 | dc.b $00,$00,$20,$df,$20,$00,$10,$ef 123 | dc.b $ef,$10,$00,$10,$10,$ef,$ef,$10 124 | dc.b $10,$00,$00,$cf,$30,$00,$40,$bf 125 | dc.b $40,$00,$30,$cf,$30 126 | 127 | pulseNextTbl: 128 | dc.b $00,$03,$04,$05,$03,$07,$08,$09 129 | dc.b $0a,$07,$0c,$0d,$0e,$0f,$10,$11 130 | dc.b $0e,$00,$14,$15,$14,$17,$18,$19 131 | dc.b $17,$1b,$1c,$1d,$1c 132 | 133 | filtLimitTbl: 134 | dc.b $40,$bf,$40,$f8 135 | 136 | filtSpdTbl: 137 | dc.b $91,$01,$ff,$91 138 | 139 | filtNextTbl: 140 | dc.b $02,$03,$02,$00 141 | 142 | song0: 143 | dc.b $80,$01,$04,$05,$07,$05,$0b,$07 144 | dc.b $00,$01,$80,$02,$02,$08,$02,$0c 145 | dc.b $08,$00,$0b,$80,$03,$06,$09,$03 146 | dc.b $0d,$09,$00,$14 147 | 148 | patt0: 149 | dc.b $06,$01,$99,$7f,$7c,$44,$d1,$16 150 | dc.b $96,$81,$7f,$00 151 | 152 | patt1: 153 | dc.b $36,$02,$f9,$1e,$03,$1f,$37,$1f 154 | dc.b $1f,$37,$1f,$1f,$37,$1f,$1f,$3b 155 | dc.b $37,$33,$7f,$37,$1f,$1f,$37,$1f 156 | dc.b $1f,$37,$1f,$1f,$2d,$e9,$2f,$f1 157 | dc.b $3d,$36,$02,$f9,$1e,$03,$1f,$37 158 | dc.b $1f,$1f,$37,$1f,$1f,$37,$1f,$1f 159 | dc.b $3b,$37,$33,$7f,$37,$1f,$1f,$37 160 | dc.b $1f,$1f,$37,$1f,$1f,$2c,$02,$e9 161 | dc.b $2f,$f1,$3d,$36,$02,$f9,$28,$03 162 | dc.b $29,$37,$29,$29,$37,$29,$29,$37 163 | dc.b $29,$29,$3b,$37,$33,$7f,$37,$29 164 | dc.b $29,$37,$29,$29,$37,$29,$29,$2d 165 | dc.b $e9,$2f,$f1,$3d,$3a,$02,$f9,$2c 166 | dc.b $03,$2d,$3b,$2d,$2d,$3b,$2d,$2d 167 | dc.b $3b,$2d,$2d,$3d,$3b,$37,$7f,$3b 168 | dc.b $2d,$2d,$3b,$2d,$2d,$41,$e1,$3d 169 | dc.b $f1,$3b,$f9,$33,$e9,$00 170 | 171 | patt2: 172 | dc.b $4e,$0d,$81,$7e,$4e,$0e,$7e,$4e 173 | dc.b $0d,$81,$7e,$4f,$7e,$d1,$4a,$0f 174 | dc.b $b1,$00 175 | 176 | patt3: 177 | dc.b $10,$04,$91,$7f,$7c,$46,$e1,$14 178 | dc.b $96,$81,$7f,$d1,$1a,$01,$7c,$47 179 | dc.b $e1,$00 180 | 181 | patt4: 182 | dc.b $06,$07,$f9,$07,$1e,$06,$06,$07 183 | dc.b $7f,$07,$06,$06,$06,$07,$7f,$07 184 | dc.b $06,$06,$06,$07,$7f,$07,$06,$06 185 | dc.b $0a,$07,$07,$07,$06,$06,$06,$07 186 | dc.b $7f,$07,$06,$06,$06,$07,$7f,$07 187 | dc.b $06,$06,$0a,$07,$0d,$0b,$06,$06 188 | dc.b $02,$07,$17,$17,$16,$06,$16,$07 189 | dc.b $7f,$17,$16,$06,$16,$07,$7f,$17 190 | dc.b $16,$06,$16,$07,$17,$17,$16,$06 191 | dc.b $1a,$07,$17,$17,$16,$06,$16,$07 192 | dc.b $7f,$17,$16,$06,$16,$07,$7f,$17 193 | dc.b $16,$06,$16,$07,$17,$1b,$1e,$06 194 | dc.b $16,$07,$10,$07,$f9,$11,$10,$06 195 | dc.b $10,$07,$7f,$11,$10,$06,$10,$07 196 | dc.b $7f,$11,$10,$06,$10,$07,$7f,$11 197 | dc.b $10,$06,$0c,$07,$11,$11,$10,$06 198 | dc.b $10,$07,$7f,$11,$10,$06,$10,$07 199 | dc.b $7f,$11,$10,$06,$10,$07,$11,$15 200 | dc.b $16,$06,$1a,$07,$15,$15,$14,$06 201 | dc.b $14,$07,$7f,$15,$14,$06,$14,$07 202 | dc.b $7f,$15,$14,$06,$14,$07,$11,$15 203 | dc.b $1a,$06,$7f,$14,$07,$15,$14,$06 204 | dc.b $14,$07,$7f,$15,$14,$06,$1a,$07 205 | dc.b $7f,$1b,$1a,$06,$7f,$1b,$1b,$1e 206 | dc.b $09,$17,$00 207 | 208 | patt5: 209 | dc.b $4a,$10,$f9,$4e,$11,$f1,$4f,$d9 210 | dc.b $7e,$a1,$7f,$4a,$10,$f9,$47,$f1 211 | dc.b $47,$d9,$7e,$a1,$7f,$4a,$10,$f9 212 | dc.b $4e,$11,$f1,$4f,$d9,$7e,$a1,$7f 213 | dc.b $4a,$10,$f9,$4e,$11,$f1,$4f,$b9 214 | dc.b $7e,$e1,$52,$12,$a1,$7e,$e1,$00 215 | 216 | patt6: 217 | dc.b $06,$07,$f9,$07,$06,$06,$06,$07 218 | dc.b $7f,$07,$06,$06,$06,$07,$7f,$07 219 | dc.b $06,$06,$06,$07,$7f,$07,$06,$06 220 | dc.b $02,$07,$0d,$0d,$0c,$06,$0c,$07 221 | dc.b $7f,$0d,$0c,$06,$0c,$07,$7f,$0d 222 | dc.b $0c,$06,$0c,$07,$7f,$0d,$0c,$06 223 | dc.b $10,$07,$15,$15,$14,$06,$14,$07 224 | dc.b $7f,$15,$14,$06,$14,$07,$7f,$15 225 | dc.b $14,$06,$14,$07,$7f,$15,$14,$06 226 | dc.b $14,$07,$17,$17,$16,$06,$16,$07 227 | dc.b $7f,$17,$0c,$06,$0c,$07,$7f,$0d 228 | dc.b $1a,$06,$1a,$07,$7f,$1b,$1a,$06 229 | dc.b $1b,$06,$07,$f9,$07,$06,$06,$06 230 | dc.b $07,$7f,$07,$06,$06,$06,$07,$7f 231 | dc.b $07,$06,$06,$06,$07,$7f,$07,$06 232 | dc.b $06,$02,$07,$0d,$0d,$0c,$06,$0c 233 | dc.b $07,$7f,$0d,$0c,$06,$0c,$07,$7f 234 | dc.b $0d,$0c,$06,$0c,$07,$7f,$0d,$0c 235 | dc.b $06,$10,$07,$15,$15,$14,$06,$14 236 | dc.b $07,$7f,$15,$14,$06,$14,$07,$7f 237 | dc.b $15,$14,$06,$14,$07,$7f,$15,$14 238 | dc.b $06,$14,$07,$17,$17,$16,$06,$16 239 | dc.b $07,$7f,$17,$16,$06,$16,$07,$15 240 | dc.b $15,$14,$06,$14,$07,$7f,$14,$06 241 | dc.b $1a,$09,$17,$00 242 | 243 | patt7: 244 | dc.b $7f,$e9,$44,$0a,$f9,$4f,$53,$f1 245 | dc.b $53,$fd,$7c,$48,$f7,$54,$97,$52 246 | dc.b $0a,$f1,$4f,$45,$f9,$7c,$4a,$7e 247 | dc.b $e9,$4b,$f9,$55,$59,$f1,$59,$fd 248 | dc.b $7c,$4b,$f7,$5c,$97,$58,$0a,$f1 249 | dc.b $55,$59,$f9,$7c,$4a,$7e,$e9,$4b 250 | dc.b $f9,$53,$55,$f1,$55,$fd,$7c,$4b 251 | dc.b $f8,$58,$97,$f6,$54,$0a,$f1,$53 252 | dc.b $4b,$4f,$7c,$4a,$55,$59,$fd,$7c 253 | dc.b $4b,$f7,$5c,$97,$ef,$58,$0a,$f1 254 | dc.b $7c,$4a,$7e,$7c,$4a,$e9,$44,$0a 255 | dc.b $f9,$4f,$53,$f1,$53,$fd,$7c,$48 256 | dc.b $f7,$54,$97,$52,$0a,$f1,$4f,$45 257 | dc.b $f9,$7c,$4a,$7e,$e9,$4b,$f9,$55 258 | dc.b $59,$f1,$59,$fd,$7c,$4c,$f6,$5c 259 | dc.b $97,$f8,$58,$0a,$f1,$55,$67,$67 260 | dc.b $fd,$7c,$4d,$f8,$6a,$97,$f6,$7c 261 | dc.b $4a,$f1,$7e,$f9,$62,$0a,$f1,$7c 262 | dc.b $4a,$e1,$7e,$f1,$5d,$f9,$63,$67 263 | dc.b $fd,$5e,$97,$55,$4f,$47,$4f,$55 264 | dc.b $5f,$6d,$67,$5f,$55,$4f,$55,$5f 265 | dc.b $67,$62,$0a,$5c,$97,$53,$4b,$45 266 | dc.b $4b,$53,$5d,$6b,$63,$5d,$53,$4b 267 | dc.b $53,$5d,$63,$00 268 | 269 | patt8: 270 | dc.b $4e,$0d,$f9,$4e,$13,$89,$54,$0f 271 | dc.b $f9,$54,$14,$89,$5c,$0d,$f9,$5c 272 | dc.b $13,$89,$4e,$0e,$f9,$4e,$15,$d9 273 | dc.b $54,$0f,$f9,$54,$14,$e9,$4a,$0f 274 | dc.b $f9,$4a,$14,$d9,$4e,$0d,$f9,$4e 275 | dc.b $13,$89,$54,$0f,$f9,$54,$14,$89 276 | dc.b $5c,$0d,$f9,$5c,$13,$89,$4e,$0e 277 | dc.b $f9,$4e,$15,$c9,$5c,$0d,$f9,$5c 278 | dc.b $13,$c9,$00 279 | 280 | patt9: 281 | dc.b $7f,$d1,$00 282 | 283 | patt10: 284 | dc.b $1e,$07,$f1,$1e,$06,$f9,$1e,$07 285 | dc.b $7f,$1f,$1a,$06,$1e,$07,$7f,$1b 286 | dc.b $1a,$06,$1a,$07,$1f,$1f,$1e,$06 287 | dc.b $1a,$07,$1f,$7f,$1e,$06,$1e,$07 288 | dc.b $7f,$1f,$1a,$06,$1e,$07,$7f,$1b 289 | dc.b $1a,$06,$1a,$07,$1f,$1f,$1e,$06 290 | dc.b $1a,$07,$15,$7f,$14,$06,$14,$07 291 | dc.b $7f,$15,$10,$06,$14,$07,$7f,$11 292 | dc.b $10,$06,$10,$07,$15,$15,$14,$06 293 | dc.b $10,$07,$15,$7f,$14,$06,$14,$07 294 | dc.b $7f,$15,$10,$06,$14,$07,$7f,$11 295 | dc.b $10,$06,$10,$07,$15,$15,$14,$06 296 | dc.b $10,$07,$28,$07,$f1,$28,$06,$f9 297 | dc.b $28,$07,$7f,$29,$24,$06,$28,$07 298 | dc.b $7f,$25,$24,$06,$24,$07,$29,$29 299 | dc.b $28,$06,$24,$07,$29,$7f,$28,$06 300 | dc.b $28,$07,$7f,$29,$24,$06,$28,$07 301 | dc.b $7f,$25,$24,$06,$24,$07,$29,$29 302 | dc.b $28,$06,$24,$07,$1f,$7f,$1e,$06 303 | dc.b $1e,$07,$7f,$1f,$1a,$06,$1e,$07 304 | dc.b $7f,$1b,$1a,$06,$1a,$07,$1f,$1f 305 | dc.b $1e,$06,$1a,$07,$1f,$7f,$1e,$06 306 | dc.b $1e,$07,$7f,$1f,$1a,$06,$1e,$07 307 | dc.b $7f,$1b,$1a,$06,$1a,$07,$1f,$1e 308 | dc.b $06,$1f,$1a,$09,$00 309 | 310 | patt11: 311 | dc.b $66,$0c,$f9,$55,$4f,$55,$67,$55 312 | dc.b $63,$55,$4f,$55,$67,$7c,$4e,$e9 313 | dc.b $63,$f1,$67,$f9,$55,$4f,$55,$67 314 | dc.b $55,$6b,$55,$4f,$55,$63,$7c,$4e 315 | dc.b $e9,$61,$f1,$63,$f9,$5d,$53,$5d 316 | dc.b $63,$5d,$61,$59,$4f,$59,$63,$7c 317 | dc.b $4e,$e9,$61,$f1,$63,$f9,$5d,$53 318 | dc.b $5d,$63,$5d,$61,$59,$4f,$59,$63 319 | dc.b $7c,$4f,$f1,$67,$f9,$69,$6d,$70 320 | dc.b $0c,$f9,$5f,$59,$5f,$71,$5f,$6d 321 | dc.b $5f,$59,$5f,$6b,$7c,$4e,$e9,$67 322 | dc.b $f1,$71,$f9,$5f,$59,$5f,$71,$5f 323 | dc.b $75,$5f,$59,$5f,$6d,$7c,$4e,$e9 324 | dc.b $6b,$f1,$6d,$f9,$67,$5d,$67,$6d 325 | dc.b $67,$6b,$63,$59,$63,$6d,$7c,$4e 326 | dc.b $e9,$6b,$f1,$6d,$f9,$67,$5d,$67 327 | dc.b $6d,$67,$6b,$63,$59,$63,$6d,$7c 328 | dc.b $4e,$e9,$7e,$f1,$00 329 | 330 | patt12: 331 | dc.b $5c,$05,$d1,$59,$e1,$55,$53,$f1 332 | dc.b $5d,$d1,$59,$e1,$53,$53,$f1,$53 333 | dc.b $d1,$4f,$e1,$53,$4f,$f1,$53,$d1 334 | dc.b $4f,$e1,$53,$e9,$55,$f9,$59,$5d 335 | dc.b $4e,$05,$d1,$4b,$e1,$4f,$4b,$f1 336 | dc.b $4f,$d1,$55,$e1,$5d,$59,$f1,$5d 337 | dc.b $d1,$59,$e1,$5d,$59,$f1,$5d,$d1 338 | dc.b $59,$e1,$5d,$e9,$2c,$08,$fe,$7c 339 | dc.b $50,$ec,$00 340 | 341 | -------------------------------------------------------------------------------- /mw4title.sng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cadaver/miniplayer/702e28d60f94397f6f0be76fe71566458af67906/mw4title.sng -------------------------------------------------------------------------------- /player.s: -------------------------------------------------------------------------------- 1 | ; Minimal feature-limited C64 music player 2 | ; Written by Cadaver (loorni@gmail.com) 2/2018 3 | 4 | ; Configuration: 5 | ; 6 | ; PLAYER_ZPBASE is the zeropage base address. 4 consecutive locations are needed, 7 | ; or 5 with sound effect support. 8 | ; 9 | ; PLAYER_SFX is whether to compile in the sound FX player. Three possible values: 10 | ; 0 No sound FX support 11 | ; 1 Overrides the music channel on which sounds are played, music can not 12 | ; continue underneath 13 | ; 2 Music is able to continue underneath and resume when the sound stops. 14 | ; This has the effect of requiring extra channel variables and potentially 15 | ; more rastertime 16 | ; 17 | ; PLAYER_MODULES is whether the player can be pointed to new music modules during 18 | ; runtime (e.g. loadable music.) In this mode, the SetMusicData routine should be 19 | ; called before playback to set the music module address. Note that the music data 20 | ; itself needs to be assembled to a fixed known address, so that trackdata & pattern 21 | ; addresses are correct within their respective tables, and it needs to include the 22 | ; header information. 23 | ; 0 No module support 24 | ; 1 Include module support 25 | 26 | trackPtrLo = PLAYER_ZPBASE+0 27 | trackPtrHi = PLAYER_ZPBASE+1 28 | pattPtrLo = PLAYER_ZPBASE+2 29 | pattPtrHi = PLAYER_ZPBASE+3 30 | if PLAYER_SFX > 0 31 | sfxTemp = PLAYER_ZPBASE+4 32 | endif 33 | 34 | ; Music module header format for SetMusicData: (written out first by the converter) 35 | ; 36 | ; - Song table size 37 | ; - Number of patterns 38 | ; - Number of instruments 39 | ; - Number of instruments including legato 40 | ; - Number of wave table steps 41 | ; - Number of pulse table steps 42 | ; - Number of filter table steps 43 | 44 | MUSICHEADERSIZE = 7 45 | 46 | FIXUP_SONGSIZE = 0 47 | FIXUP_PATTSIZE = 4 48 | FIXUP_INSSIZE = 8 49 | FIXUP_LEGATOINSSIZE = 12 50 | FIXUP_WAVESIZE = 16 51 | FIXUP_PULSESIZE = 20 52 | FIXUP_FILTSIZE = 24 53 | FIXUP_NOSIZE = 28 54 | 55 | FIXUP_ZERO = 0 56 | FIXUP_MINUS1 = 1 57 | FIXUP_MINUS81 = 2 58 | 59 | NUMFIXUPS = 30 60 | 61 | ; Track-data format: 62 | ; [transpose], pattern, [end] 63 | ; 64 | ; $00 Song end, followed by loop position 65 | ; $01-$7f Patterns 66 | ; $80-$ff Signed transpose, $80 being neutral 67 | ; 68 | ; All tracks of subtune must fit into 255 bytes. Start indices into trackdata are 1-based, 69 | ; as index 0 is used for PLAYER_SFX mode 1. 70 | 71 | TRANS = $80 72 | SONGJUMP = 0 73 | 74 | ; Pattern-data format: 75 | ; note, [instrument], [duration], [end] 76 | ; 77 | ; Note values: 78 | ; $00 Pattern end 79 | ; $02-$7a Notes with instrument byte following 80 | ; $03-$7b Notes without instrument change 81 | ; $7c Waveptr change command, followed by new wave pointer 82 | ; $7e Gate off 83 | ; $7f Rest 84 | ; 85 | ; Duration values are negative from $80-$ff, with $fe representing the shortest legal 86 | ; duration (3) 87 | 88 | ENDPATT = 0 89 | INS = -1 90 | DUR = $101 91 | C0 = 1*2+1 92 | CS0 = 2*2+1 93 | D0 = 3*2+1 94 | DS0 = 4*2+1 95 | E0 = 5*2+1 96 | F0 = 6*2+1 97 | FS0 = 7*2+1 98 | G0 = 8*2+1 99 | GS0 = 9*2+1 100 | A0 = 10*2+1 101 | AS0 = 11*2+1 102 | H0 = 12*2+1 103 | C1 = 13*2+1 104 | CS1 = 14*2+1 105 | D1 = 15*2+1 106 | DS1 = 16*2+1 107 | E1 = 17*2+1 108 | F1 = 18*2+1 109 | FS1 = 19*2+1 110 | G1 = 20*2+1 111 | GS1 = 21*2+1 112 | A1 = 22*2+1 113 | AS1 = 23*2+1 114 | H1 = 24*2+1 115 | C2 = 25*2+1 116 | CS2 = 26*2+1 117 | D2 = 27*2+1 118 | DS2 = 28*2+1 119 | E2 = 29*2+1 120 | F2 = 30*2+1 121 | FS2 = 31*2+1 122 | G2 = 32*2+1 123 | GS2 = 33*2+1 124 | A2 = 34*2+1 125 | AS2 = 35*2+1 126 | H2 = 36*2+1 127 | C3 = 37*2+1 128 | CS3 = 38*2+1 129 | D3 = 39*2+1 130 | DS3 = 40*2+1 131 | E3 = 41*2+1 132 | F3 = 42*2+1 133 | FS3 = 43*2+1 134 | G3 = 44*2+1 135 | GS3 = 45*2+1 136 | A3 = 46*2+1 137 | AS3 = 47*2+1 138 | H3 = 48*2+1 139 | C4 = 49*2+1 140 | CS4 = 50*2+1 141 | D4 = 51*2+1 142 | DS4 = 52*2+1 143 | E4 = 53*2+1 144 | F4 = 54*2+1 145 | FS4 = 55*2+1 146 | G4 = 56*2+1 147 | GS4 = 57*2+1 148 | A4 = 58*2+1 149 | AS4 = 59*2+1 150 | H4 = 60*2+1 151 | WAVEPTR = $7c 152 | KEYOFF = $7e 153 | REST = $7f 154 | 155 | ; Instruments have ADSR, 1st frame waveform and wave/pulse/filterpointers 156 | ; 157 | ; Instruments $01-$7f use gateoff before note and ADSR init. 158 | ; Instruments $81-$ff are the same instruments in legato mode: no gateoff, no ADSR/pulse/filter change 159 | ; 160 | ; Use 0 in pulse & filterpointers to skip initialization. 161 | 162 | FIRSTINSTR = $01 163 | FIRSTLEGATOINSTR = $81 164 | SKIPTABLEINIT = $00 165 | 166 | ; Tables are based on having 3 colums, like NinjaTracker 1. 167 | ; The right side column is the "next" position, 0 to stop wave/pulse/filter 168 | ; 169 | ; Wavetable left column values: 170 | ; $00 Vibrato, mid column is negative width, and right column speed 171 | ; $01-$8f Waveform values, mid column is note ($00-$7f relative, $80-$ff absolute) 172 | ; $90 Slide, mid+right columns are 16bit speed-1, optimization due to carry being set 173 | ; in slide routine 174 | ; $91-$ff Delayed wavetable step without wavechange, delay is negative ($ff = one frame) 175 | 176 | VIBRATO = $00 177 | FIRSTWAVE = $01 178 | LASTWAVE = $8f 179 | SLIDE = $90 180 | WAVEDELAY = $100 181 | 182 | ; If pulse or filterpointer has the high bit ($80) set, the next step will be interpreted 183 | ; as an init-step: 184 | ; 185 | ; For pulse: nybble-reversed initial pulse value in left (limit) column 186 | ; For filter: initial cutoff value in left column, mid column contains control bits: 187 | ; $01,$02,$04 channels to filter 188 | ; $10,$20 lowpass or bandpass (highpass not supported) 189 | ; $40,$80,$c0 resonance control 190 | ; 191 | ; Otherwise pulse & filter steps include the cutoff/pulse target in the left column 192 | ; (for pulse, nybbles reversed) and speed (nybble reversed also) in the mid column. 193 | ; Negative pulse speed values must have one subtracted from them, ie. if you have speed 194 | ; $40 up, use $bf for down with same speed. 195 | 196 | TABLEINITSTEP = $80 197 | FILTERCHN1 = $01 198 | FILTERCHN2 = $02 199 | FILTERCHN4 = $04 200 | LOWPASS = $10 201 | BANDPASS = $20 202 | 203 | if PLAYER_MODULES > 0 204 | 205 | ; Point player to new music module 206 | ; 207 | ; A = music data address lowbyte 208 | ; X = music data address highbyte 209 | ; 210 | ; Playroutine should not be executed while this routine is executing, as they share the 211 | ; same zeropage locations. 212 | 213 | SetMusicData: sta SetMusicData_HeaderLda+1 214 | clc 215 | adc #MUSICHEADERSIZE 216 | sta trackPtrLo 217 | stx SetMusicData_HeaderLda+2 218 | txa 219 | adc #$00 220 | sta trackPtrHi 221 | ldx #$00 222 | SetMusicData_FixupLoop: 223 | lda fixupDestLoTbl,x 224 | sta pattPtrLo 225 | lda fixupDestHiTbl,x 226 | pha 227 | and #$03 228 | adc #>PlayRoutine 229 | sta pattPtrHi 230 | pla 231 | lsr 232 | lsr 233 | pha 234 | lsr 235 | lsr 236 | cmp #FIXUP_NOSIZE/4 237 | bcs SetMusicData_AddDone 238 | tay 239 | SetMusicData_HeaderLda: 240 | lda musicHeader,y 241 | adc trackPtrLo 242 | sta trackPtrLo 243 | bcc SetMusicData_AddDone 244 | inc trackPtrHi 245 | SetMusicData_AddDone: 246 | pla 247 | and #$03 248 | tay 249 | lda trackPtrLo 250 | sec 251 | sbc fixupSubTbl,y 252 | ldy #$01 253 | sta (pattPtrLo),y 254 | iny 255 | lda trackPtrHi 256 | sbc #$00 257 | sta (pattPtrLo),y 258 | inx 259 | cpx #NUMFIXUPS 260 | bcc SetMusicData_FixupLoop 261 | rts 262 | 263 | endif 264 | 265 | if PLAYER_SFX > 0 266 | 267 | ; Sound effect playback init 268 | ; 269 | ; X = channel variable index (0,7,14) 270 | ; A = sound data address lowbyte 271 | ; Y = sound data address highbyte 272 | ; 273 | ; Note: you must devise your own sound effect priority / interruption mechanism. 274 | ; Check the channel variable chnSfxPtrHi whether a sound is still playing (nonzero if is) 275 | ; 276 | ; Sound effect playback does not potentially work right if the call is interrupted by 277 | ; the playroutine. Therefore either protect it with sei/cli or call from the same 278 | ; interrupt that calls the playroutine (recommended.) 279 | ; 280 | ; Sound effect data consists of control bytes followed by data, delays and endmark. 281 | ; Each control byte takes 1 frame to execute. The bits are evaluated from LSB first. 282 | ; A sound always begins with a gateoff, so this does not need to be specified. 283 | ; 284 | ; $00 Endmark 285 | ; $01-$1f Control byte, bits: 286 | ; $01 First frame init (sets fixed waveform $09), followed by AD,SR bytes 287 | ; $02 Set pulse, followed by nybble-reversed pulsewidth 288 | ; $04 Set wave, followed by waveform byte 289 | ; $08 Set frequency, followed by freq highbyte (also set to lowbyte) 290 | ; $10 Set freqmod, followed by 8-bit freqmod speed, which affects only highbyte 291 | ; $80-$ff Delay, is negative similar to wavetable ($ff = one frame) 292 | 293 | SFXEND = $00 294 | SFXINIT = $01 295 | SFXPULSE = $02 296 | SFXWAVE = $04 297 | SFXFREQ = $08 298 | SFXFREQMOD = $10 299 | SFXDELAY = $100 300 | 301 | PlaySfx: sta chnSfxPtrLo,x 302 | tya 303 | sta chnSfxPtrHi,x 304 | lda #$00 305 | sta chnSfxPos,x 306 | if PLAYER_SFX = 1 ;Disable the channel for music playback until 307 | sta chnSongPos,x ;new subtune initialized 308 | endif 309 | rts 310 | 311 | endif 312 | 313 | ; Playroutine entrypoint. 314 | ; 315 | ; Write subtune number+1 to PlayRoutine+1 to initialize 316 | 317 | PlayRoutine: ldx #$01 318 | beq Play_FiltPos 319 | jmp Play_DoInit 320 | 321 | Play_FiltInit: 322 | Play_FiltSpdTblM81Access: 323 | lda filtSpdTbl-$81,y 324 | sta $d417 325 | and #$70 ;Filter type 326 | sta Play_FiltType+1 327 | Play_FiltNextTblM81Access: 328 | lda filtNextTbl-$81,y 329 | sta Play_FiltPos+1 330 | Play_FiltLimitTblM81Access: 331 | lda filtLimitTbl-$81,y 332 | jmp Play_StoreCutoff 333 | Play_FiltNext: 334 | Play_FiltNextTblM1Access: 335 | lda filtNextTbl-1,y 336 | sta Play_FiltPos+1 337 | jmp Play_FiltDone 338 | 339 | Play_NoNewIns: clc 340 | adc chnTrans,x 341 | ora #$80 342 | sta chnNote,x 343 | lda chnIns,x 344 | bpl Play_HardRestart 345 | jmp Play_Rest 346 | 347 | Play_Commands: cmp #KEYOFF 348 | if PLAYER_SFX = 2 349 | beq Play_KeyoffSfxCheck 350 | else 351 | beq Play_Keyoff 352 | endif 353 | bcs Play_Rest 354 | Play_WavePtr: iny 355 | lda (pattPtrLo),y 356 | jsr Play_SetWavePos 357 | beq Play_Rest ;Returns with A=0 358 | if PLAYER_SFX = 2 359 | Play_KeyoffSfxCheck: 360 | lda chnSfxPtrHi,x 361 | bne Play_Rest 362 | beq Play_Keyoff 363 | endif 364 | 365 | Play_FiltPos: ldy #$00 366 | beq Play_FiltDone 367 | bmi Play_FiltInit 368 | Play_FiltCutoff:lda #$00 369 | Play_FiltLimitTblM1Access: 370 | cmp filtLimitTbl-1,y 371 | beq Play_FiltNext 372 | clc 373 | Play_FiltSpdTblM1Access: 374 | adc filtSpdTbl-1,y 375 | Play_StoreCutoff: 376 | sta Play_FiltCutoff+1 377 | sta $d416 378 | Play_FiltDone: 379 | Play_FiltType: lda #$00 380 | Play_MasterVol: ora #$0f ;Can be modified for fadein/out 381 | sta $d418 382 | jsr Play_ChnExec 383 | ldx #$07 384 | jsr Play_ChnExec 385 | ldx #$0e 386 | 387 | if PLAYER_SFX != 1 388 | Play_ChnExec: inc chnCounter,x 389 | bmi Play_PulseExec 390 | bne Play_Reload 391 | ldy chnSongPos,x 392 | else 393 | Play_ChnExec: ldy chnSongPos,x 394 | beq Play_JumpToSfx 395 | inc chnCounter,x 396 | bmi Play_PulseExec 397 | bne Play_Reload 398 | endif 399 | Play_NewNotes: lda (trackPtrLo),y 400 | tay 401 | Play_PattTblLoM1Access: 402 | lda pattTblLo-1,y 403 | sta pattPtrLo 404 | Play_PattTblHiM1Access: 405 | lda pattTblHi-1,y 406 | sta pattPtrHi 407 | ldy chnPattPos,x 408 | lda (pattPtrLo),y 409 | cmp #WAVEPTR 410 | bcs Play_Commands 411 | lsr 412 | bcs Play_NoNewIns 413 | adc chnTrans,x 414 | ora #$80 415 | sta chnNote,x 416 | iny 417 | lda (pattPtrLo),y 418 | sta chnIns,x 419 | bmi Play_Rest ;Instruments $81-$ff are $01-$7f as legato 420 | Play_HardRestart: 421 | if PLAYER_SFX = 2 422 | lda chnSfxPtrHi,x 423 | bne Play_Rest 424 | endif 425 | lda #$0f 426 | sta $d406,x 427 | Play_Keyoff: lda chnWave,x 428 | and #$fe 429 | sta $d404,x 430 | Play_Rest: iny 431 | lda (pattPtrLo),y 432 | beq Play_PattEnd 433 | bpl Play_NoPattEnd 434 | sta chnDuration,x 435 | iny 436 | lda (pattPtrLo),y 437 | bne Play_NoPattEnd 438 | Play_PattEnd: inc chnSongPos,x 439 | dc.b $24 ;Skip next instruction 440 | Play_NoPattEnd: tya 441 | sta chnPattPos,x 442 | if PLAYER_SFX = 2 443 | lda chnSfxPtrHi,x 444 | bne Play_JumpToSfx 445 | endif 446 | rts 447 | 448 | Play_Reload: lda chnDuration,x 449 | sta chnCounter,x 450 | lda chnPattPos,x 451 | beq Play_Sequencer 452 | Play_NoSequencer: 453 | lda chnNote,x 454 | bpl Play_PulseExec 455 | jmp Play_NewNoteInit 456 | 457 | if PLAYER_SFX > 0 458 | Play_JumpToSfx: jmp Play_SfxExec 459 | endif 460 | 461 | Play_PulseExec: if PLAYER_SFX = 2 462 | lda chnSfxPtrHi,x 463 | bne Play_JumpToSfx 464 | endif 465 | ldy chnPulsePos,x 466 | beq Play_WaveExec 467 | bmi Play_PulseInit 468 | lda chnPulse,x 469 | Play_PulseLimitTblM1Access: 470 | cmp pulseLimitTbl-1,y 471 | beq Play_PulseNext 472 | clc 473 | Play_PulseSpdTblM1Access: 474 | adc pulseSpdTbl-1,y 475 | adc #$00 476 | Play_StorePulse: 477 | sta chnPulse,x 478 | sta $d402,x 479 | sta $d403,x 480 | 481 | Play_WaveExec: ldy chnWavePos,x 482 | beq Play_WaveDone 483 | Play_WaveTblM1Access: 484 | lda waveTbl-1,y 485 | beq Play_Vibrato 486 | cmp #$90 487 | bcs Play_SlideOrDelay 488 | Play_SetWave: sta chnWave,x 489 | sta $d404,x 490 | Play_NoWaveChange: 491 | Play_WaveNextTblM1Access1: 492 | lda waveNextTbl-1,y 493 | sta chnWavePos,x 494 | Play_NoteTblM1Access1: 495 | lda noteTbl-1,y 496 | bmi Play_AbsNote 497 | adc chnNote,x 498 | Play_AbsNote: asl 499 | tay 500 | lda freqTbl-2,y 501 | sta chnFreqLo,x 502 | sta $d400,x 503 | lda freqTbl-1,y 504 | Play_StoreFreqHi: 505 | sta chnFreqHi,x 506 | sta $d401,x 507 | Play_WaveDone: rts 508 | Play_WaveDelayNotOver: 509 | inc chnWaveTime,x 510 | rts 511 | 512 | Play_Sequencer: ldy chnSongPos,x 513 | lda (trackPtrLo),y 514 | bmi Play_SongTrans 515 | bne Play_SequencerDone 516 | Play_SongJump: iny 517 | lda (trackPtrLo),y 518 | tay 519 | lda (trackPtrLo),y 520 | bpl Play_NoSongJumpTrans 521 | Play_SongTrans: sta chnTrans,x 522 | iny 523 | Play_NoSongJumpTrans: 524 | tya 525 | sta chnSongPos,x 526 | Play_SequencerDone: 527 | if PLAYER_SFX = 2 528 | lda chnSfxPtrHi,x 529 | bne Play_JumpToSfx 530 | endif 531 | lda chnNote,x 532 | bpl Play_WaveExec 533 | bmi Play_NewNoteInit 534 | 535 | Play_PulseInit: 536 | Play_PulseNextTblM81Access: 537 | lda pulseNextTbl-$81,y 538 | sta chnPulsePos,x 539 | Play_PulseLimitTblM81Access: 540 | lda pulseLimitTbl-$81,y 541 | jmp Play_StorePulse 542 | Play_PulseNext: 543 | Play_PulseNextTblM1Access: 544 | lda pulseNextTbl-1,y 545 | sta chnPulsePos,x 546 | jmp Play_WaveExec 547 | 548 | Play_SlideOrDelay: 549 | beq Play_Slide 550 | Play_WaveDelay: adc chnWaveTime,x 551 | bne Play_WaveDelayNotOver 552 | clc 553 | sta chnWaveTime,x 554 | bcc Play_NoWaveChange 555 | 556 | Play_Vibrato: lda chnWaveTime,x 557 | bpl Play_VibNoDir 558 | Play_NoteTblM1Access2: 559 | cmp noteTbl-1,y 560 | bcs Play_VibNoDir2 561 | eor #$ff 562 | Play_VibNoDir: sec 563 | Play_VibNoDir2: sbc #$02 564 | sta chnWaveTime,x 565 | lsr 566 | lda chnFreqLo,x 567 | bcs Play_VibDown 568 | Play_VibUp: 569 | Play_WaveNextTblM1Access2: 570 | adc waveNextTbl-1,y 571 | sta chnFreqLo,x 572 | sta $d400,x 573 | bcc Play_VibDone 574 | lda chnFreqHi,x 575 | adc #$00 576 | jmp Play_StoreFreqHi 577 | Play_VibDown: 578 | Play_WaveNextTblM1Access3: 579 | sbc waveNextTbl-1,y 580 | sta chnFreqLo,x 581 | sta $d400,x 582 | bcs Play_VibDone 583 | lda chnFreqHi,x 584 | sbc #$00 585 | jmp Play_StoreFreqHi 586 | 587 | Play_LegatoNoteInit: 588 | tya 589 | and #$7f 590 | tay 591 | bpl Play_FinishLegatoInit 592 | 593 | Play_Slide: lda chnFreqLo,x 594 | Play_NoteTblM1Access3: 595 | adc noteTbl-1,y ;Note: speed must be stored as speed-1 due to C=1 here 596 | sta chnFreqLo,x 597 | sta $d400,x 598 | Play_WaveNextTblM1Access4: 599 | lda waveNextTbl-1,y 600 | Play_SfxFreqMod:adc chnFreqHi,x 601 | jmp Play_StoreFreqHi 602 | 603 | Play_NewNoteInit: 604 | and #$7f 605 | sta chnNote,x ;Reset newnote-flag 606 | ldy chnIns,x 607 | bmi Play_LegatoNoteInit 608 | Play_HRNoteInit: 609 | Play_InsADM1Access: 610 | lda insAD-1,y ;Instruments are 1-indexed just so that the converter can 611 | sta $d405,x ;differentiate between "no instrument change" and the first 612 | Play_InsSRM1Access: ;instrument. Strictly speaking they wouldn't need to be 613 | lda insSR-1,y 614 | sta $d406,x 615 | Play_InsFirstWaveM1Access: 616 | lda insFirstWave-1,y 617 | sta $d404,x 618 | Play_InsPulsePosM1Access: 619 | lda insPulsePos-1,y 620 | beq Play_SkipPulseInit 621 | sta chnPulsePos,x 622 | Play_SkipPulseInit: 623 | Play_InsFiltPosM1Access: 624 | lda insFiltPos-1,y 625 | beq Play_SkipFiltInit 626 | sta Play_FiltPos+1 627 | Play_SkipFiltInit: 628 | Play_FinishLegatoInit: 629 | Play_InsWavePosM1Access: 630 | lda insWavePos-1,y 631 | Play_SetWavePos:sta chnWavePos,x 632 | lda #$00 633 | sta chnWaveTime,x 634 | Play_VibDone: rts 635 | 636 | if PLAYER_SFX > 0 637 | 638 | ; Sound effect support code 639 | 640 | Play_SfxDelay: sec 641 | adc chnSfxTime,x 642 | bne Play_SfxDelayOngoing 643 | Play_SfxDelayDone: 644 | sta chnSfxTime,x 645 | inc chnSfxPos,x ;Delay ended, run still effects only this frame 646 | bne Play_SfxEffects 647 | Play_SfxDelayOngoing: 648 | inc chnSfxTime,x 649 | Play_SfxEffects:lda chnSfxFreqMod,x 650 | clc 651 | bne Play_SfxFreqMod 652 | rts 653 | Play_SfxEnd: sta chnSfxPtrHi,x 654 | if PLAYER_SFX = 2 655 | sta chnWavePos,x ;Also reset wavepos when returning from sound FX to music 656 | endif 657 | Play_SfxDone: rts 658 | 659 | Play_SfxHR: lda #$0f 660 | sta $d406,x 661 | lda chnWave,x 662 | and #$fe 663 | sta $d404,x 664 | lda #$00 665 | sta chnSfxFreqMod,x 666 | sta chnSfxTime,x 667 | inc chnSfxPos,x 668 | rts 669 | 670 | Play_SfxExec: if PLAYER_SFX = 1 671 | lda chnSfxPtrHi,x 672 | beq Play_SfxDone 673 | endif 674 | sta pattPtrHi 675 | if PLAYER_SFX = 2 676 | lda chnNote,x ;Prevent newnoteinit triggering during sound FX 677 | bpl Play_SfxNoNewNote 678 | and #$7f 679 | sta chnNote,x 680 | Play_SfxNoNewNote: 681 | endif 682 | lda chnSfxPtrLo,x 683 | sta pattPtrLo 684 | ldy chnSfxPos,x 685 | beq Play_SfxHR 686 | dey 687 | lda (pattPtrLo),y 688 | bmi Play_SfxDelay 689 | beq Play_SfxEnd 690 | 691 | Play_SfxCommand:sta sfxTemp 692 | iny 693 | lsr sfxTemp 694 | bcc Play_NoSfxFirstFrame 695 | lda (pattPtrLo),y 696 | sta $d405,x 697 | iny 698 | lda (pattPtrLo),y 699 | sta $d406,x 700 | iny 701 | lda #$09 702 | sta $d404,x 703 | 704 | Play_NoSfxFirstFrame: 705 | lsr sfxTemp 706 | bcc Play_NoSfxPulse 707 | lda (pattPtrLo),y 708 | sta $d402,x 709 | sta $d403,x 710 | iny 711 | 712 | Play_NoSfxPulse: 713 | lsr sfxTemp 714 | bcc Play_NoSfxWave 715 | lda (pattPtrLo),y 716 | sta chnWave,x 717 | sta $d404,x 718 | iny 719 | 720 | Play_NoSfxWave: lsr sfxTemp 721 | bcc Play_NoSfxFreq 722 | lda (pattPtrLo),y 723 | sta chnFreqHi,x 724 | sta $d400,x 725 | sta $d401,x 726 | iny 727 | 728 | Play_NoSfxFreq: lsr sfxTemp 729 | bcc Play_NoSfxFreqMod 730 | lda (pattPtrLo),y 731 | sta chnSfxFreqMod,x 732 | iny 733 | 734 | Play_NoSfxFreqMod: 735 | iny 736 | tya 737 | sta chnSfxPos,x 738 | jmp Play_SfxEffects 739 | 740 | endif 741 | 742 | Play_DoInit: dex 743 | txa 744 | sta pattPtrLo 745 | asl 746 | asl 747 | adc pattPtrLo 748 | tay 749 | Play_SongTblAccess1: 750 | lda songTbl,y 751 | sta trackPtrLo 752 | iny 753 | Play_SongTblAccess2: 754 | lda songTbl,y 755 | sta trackPtrHi 756 | iny 757 | ldx #21 758 | lda #$00 759 | Play_ClearVars: sta chnWave-1,x 760 | sta $d400-1,x 761 | dex 762 | bne Play_ClearVars 763 | stx Play_FiltPos+1 764 | stx Play_FiltType+1 765 | stx $d417 766 | stx PlayRoutine+1 767 | jsr Play_ChnInit 768 | ldx #$07 769 | jsr Play_ChnInit 770 | ldx #$0e 771 | Play_ChnInit: 772 | Play_SongTblAccess3: 773 | lda songTbl,y 774 | sta chnSongPos,x 775 | dec chnDuration,x 776 | iny 777 | rts 778 | 779 | freqTbl: dc.w $022d,$024e,$0271,$0296,$02be,$02e8,$0314,$0343,$0374,$03a9,$03e1,$041c 780 | dc.w $045a,$049c,$04e2,$052d,$057c,$05cf,$0628,$0685,$06e8,$0752,$07c1,$0837 781 | dc.w $08b4,$0939,$09c5,$0a5a,$0af7,$0b9e,$0c4f,$0d0a,$0dd1,$0ea3,$0f82,$106e 782 | dc.w $1168,$1271,$138a,$14b3,$15ee,$173c,$189e,$1a15,$1ba2,$1d46,$1f04,$20dc 783 | dc.w $22d0,$24e2,$2714,$2967,$2bdd,$2e79,$313c,$3429,$3744,$3a8d,$3e08,$41b8 784 | dc.w $45a1,$49c5,$4e28,$52cd,$57ba,$5cf1,$6278,$6853,$6e87,$751a,$7c10,$8371 785 | dc.w $8b42,$9389,$9c4f,$a59b,$af74,$b9e2,$c4f0,$d0a6,$dd0e,$ea33,$f820,$ffff 786 | 787 | if PLAYER_MODULES > 0 788 | 789 | fixupDestLoTbl: dc.b Play_SongTblAccess1 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_ZERO)*4 821 | dc.b >Play_SongTblAccess2 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_ZERO)*4 822 | dc.b >Play_SongTblAccess3 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_ZERO)*4 823 | dc.b >Play_PattTblLoM1Access - >PlayRoutine + (FIXUP_SONGSIZE+FIXUP_MINUS1)*4 824 | dc.b >Play_PattTblHiM1Access - >PlayRoutine + (FIXUP_PATTSIZE+FIXUP_MINUS1)*4 825 | dc.b >Play_InsADM1Access - >PlayRoutine + (FIXUP_PATTSIZE+FIXUP_MINUS1)*4 826 | dc.b >Play_InsSRM1Access - >PlayRoutine + (FIXUP_INSSIZE+FIXUP_MINUS1)*4 827 | dc.b >Play_InsFirstWaveM1Access - >PlayRoutine + (FIXUP_INSSIZE+FIXUP_MINUS1)*4 828 | dc.b >Play_InsPulsePosM1Access - >PlayRoutine + (FIXUP_INSSIZE+FIXUP_MINUS1)*4 829 | dc.b >Play_InsFiltPosM1Access - >PlayRoutine + (FIXUP_INSSIZE+FIXUP_MINUS1)*4 830 | dc.b >Play_InsWavePosM1Access - >PlayRoutine + (FIXUP_INSSIZE+FIXUP_MINUS1)*4 831 | dc.b >Play_WaveTblM1Access - >PlayRoutine + (FIXUP_LEGATOINSSIZE+FIXUP_MINUS1)*4 832 | dc.b >Play_NoteTblM1Access1 - >PlayRoutine + (FIXUP_WAVESIZE+FIXUP_MINUS1)*4 833 | dc.b >Play_NoteTblM1Access2 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS1)*4 834 | dc.b >Play_NoteTblM1Access3 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS1)*4 835 | dc.b >Play_WaveNextTblM1Access1 - >PlayRoutine + (FIXUP_WAVESIZE+FIXUP_MINUS1)*4 836 | dc.b >Play_WaveNextTblM1Access2 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS1)*4 837 | dc.b >Play_WaveNextTblM1Access3 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS1)*4 838 | dc.b >Play_WaveNextTblM1Access4 - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS1)*4 839 | dc.b >Play_PulseLimitTblM1Access - >PlayRoutine + (FIXUP_WAVESIZE+FIXUP_MINUS1)*4 840 | dc.b >Play_PulseLimitTblM81Access - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS81)*4 841 | dc.b >Play_PulseSpdTblM1Access - >PlayRoutine + (FIXUP_PULSESIZE+FIXUP_MINUS1)*4 842 | dc.b >Play_PulseNextTblM1Access - >PlayRoutine + (FIXUP_PULSESIZE+FIXUP_MINUS1)*4 843 | dc.b >Play_PulseNextTblM81Access - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS81)*4 844 | dc.b >Play_FiltLimitTblM1Access - >PlayRoutine + (FIXUP_PULSESIZE+FIXUP_MINUS1)*4 845 | dc.b >Play_FiltLimitTblM81Access - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS81)*4 846 | dc.b >Play_FiltSpdTblM1Access - >PlayRoutine + (FIXUP_FILTSIZE+FIXUP_MINUS1)*4 847 | dc.b >Play_FiltSpdTblM81Access - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS81)*4 848 | dc.b >Play_FiltNextTblM1Access - >PlayRoutine + (FIXUP_FILTSIZE+FIXUP_MINUS1)*4 849 | dc.b >Play_FiltNextTblM81Access - >PlayRoutine + (FIXUP_NOSIZE+FIXUP_MINUS81)*4 850 | 851 | fixupSubTbl: dc.b $00,$01,$81 852 | 853 | ; In settable music data mode, include dummy music data labels so that the player 854 | ; will not complain when assembled 855 | 856 | musicHeader: 857 | songTbl: 858 | pattTblLo: 859 | pattTblHi: 860 | insAD: 861 | insSR: 862 | insFirstWave: 863 | insWavePos: 864 | insPulsePos: 865 | insFiltPos: 866 | waveTbl: 867 | noteTbl: 868 | waveNextTbl: 869 | pulseLimitTbl: 870 | pulseSpdTbl: 871 | pulseNextTbl: 872 | filtLimitTbl: 873 | filtSpdTbl: 874 | filtNextTbl: 875 | 876 | endif 877 | 878 | ; Playroutine variables 879 | 880 | chnWave: dc.b 0 881 | chnWavePos: dc.b 0 882 | chnPulsePos: dc.b 0 883 | chnPattPos: dc.b 0 884 | chnDuration: dc.b 0 885 | chnCounter: dc.b 0 886 | chnNote: dc.b 0 887 | 888 | dc.b 0,0,0,0,0,0,0 889 | dc.b 0,0,0,0,0,0,0 890 | 891 | chnTrans: dc.b $80 892 | chnSongPos: dc.b 0 893 | chnIns: dc.b 0 894 | chnWaveTime: dc.b 0 895 | chnPulse: dc.b 0 896 | chnFreqLo: dc.b 0 897 | chnFreqHi: dc.b 0 898 | 899 | dc.b $80,0,0,0,0,0,0 900 | dc.b $80,0,0,0,0,0,0 901 | 902 | if PLAYER_SFX = 2 903 | chnSfxPos: dc.b 0 904 | chnSfxPtrLo: dc.b 0 905 | chnSfxPtrHi: dc.b 0 906 | dc.b 0 907 | dc.b 0 908 | dc.b 0 909 | dc.b 0 910 | 911 | dc.b 0,0,0,0,0,0,0 912 | dc.b 0,0,0 913 | endif 914 | 915 | if PLAYER_SFX = 1 916 | chnSfxPos = chnCounter 917 | chnSfxPtrLo = chnNote 918 | chnSfxPtrHi = chnIns 919 | endif 920 | 921 | if PLAYER_SFX > 0 922 | chnSfxTime = chnWaveTime 923 | chnSfxFreqMod = chnFreqLo 924 | endif -------------------------------------------------------------------------------- /prgexample.s: -------------------------------------------------------------------------------- 1 | ; .prg example of using the playroutine. Press fire to trigger a sound effect. 2 | ; Uses SetMusicData to mimic a "game" scenario with loadable music modules. 3 | 4 | processor 6502 5 | org $0801 6 | 7 | sys: dc.b $0b,$08 8 | dc.b $0a,$00 9 | dc.b $9e 10 | dc.b $32,$30,$36,$31 11 | dc.b $00,$00,$00 12 | 13 | start: sei 14 | lda #$00 15 | sta $d415 16 | lda #$7f 17 | sta $dc0d 18 | lda #$01 19 | sta $d01a 20 | lda #raster 23 | sta $0315 24 | lda #$33 25 | sta $d012 26 | lda #27 27 | sta $d011 28 | lda $dc0d 29 | dec $d019 30 | lda #<$1000 31 | ldx #>$1000 32 | jsr SetMusicData 33 | cli 34 | loop: lda #68 35 | sta $0400 36 | sta $0428 37 | sta $0450 38 | ldx curraster 39 | lda hexchars,x 40 | sta $400+37 41 | lda #"/" 42 | sta $400+38 43 | ldx maxraster 44 | lda hexchars,x 45 | sta $400+39 46 | jmp loop 47 | 48 | hexchars: dc.b $30,$31,$32,$33,$34,$35,$36,$37,$38,$39 49 | dc.b $01,$02,$03,$04,$05,$06 50 | 51 | curraster: dc.b 0 52 | maxraster: dc.b 0 53 | 54 | raster: cld 55 | lda $d019 56 | sta $d019 57 | nop 58 | nop 59 | nop 60 | nop 61 | nop 62 | nop 63 | nop 64 | nop 65 | nop 66 | nop 67 | nop 68 | nop 69 | nop 70 | nop 71 | inc $d020 72 | jsr PlayRoutine 73 | lda $d012 74 | ldx #$0e 75 | stx $d020 76 | sec 77 | sbc #$34 78 | sta curraster 79 | cmp maxraster 80 | bcc raster_nonewmax 81 | sta maxraster 82 | raster_nonewmax:lda $dc00 83 | pha 84 | and #$10 85 | bne raster_nofire 86 | lda prevjoy 87 | and #$10 88 | beq raster_nofire 89 | ldx #14 90 | lda #soundeffect 92 | jsr PlaySfx 93 | raster_nofire: pla 94 | sta prevjoy 95 | jmp $ea31 96 | 97 | prevjoy: dc.b 0 98 | 99 | ; Player configuration 100 | 101 | PLAYER_ZPBASE = $fb 102 | PLAYER_SFX = 2 103 | PLAYER_MODULES = 1 104 | 105 | ; Player 106 | 107 | include "player.s" 108 | 109 | ; Music module (assembled separately) 110 | 111 | org $1000 112 | incbin "musicmodule.bin" 113 | 114 | ; SFX data 115 | 116 | soundeffect: dc.b SFXINIT+SFXPULSE,$00,$fa,$08 ;ADSR $00fa, pulsewidth $80 117 | dc.b SFXWAVE+SFXFREQ,$81,$c0 118 | dc.b SFXWAVE+SFXFREQ,$41,$0c 119 | dc.b SFXWAVE+SFXFREQ,$81,$20 120 | dc.b SFXFREQMOD,$ff ;Decrease freq high with 1 per frame 121 | dc.b SFXWAVE,$80 122 | dc.b SFXDELAY-$10 ;Delay for 16 frames 123 | dc.b SFXEND 124 | -------------------------------------------------------------------------------- /sidexample.s: -------------------------------------------------------------------------------- 1 | ; .sid example of using the playroutine. Assembles the music data directly after 2 | ; the player. SetMusicData not used. 3 | 4 | processor 6502 5 | org $0000 6 | 7 | SUBTUNES = 1 8 | 9 | dc.b "PSID" 10 | dc.b 0,2 11 | dc.b 0,$7c 12 | dc.b $00,$00 13 | dc.b $10,$00 14 | dc.b $10,$03 15 | dc.b 0,SUBTUNES 16 | dc.b 0,1 17 | dc.b 0,0,0,0 18 | 19 | org $0016 20 | dc.b "Minimal player test" 21 | 22 | org $0036 23 | dc.b "Cadaver" 24 | 25 | org $0056 26 | 27 | dc.b "2018 Covert Bitops" 28 | 29 | org $0076 30 | dc.b $00,$10 31 | 32 | org $007c 33 | dc.b $00,$10 34 | rorg $1000 35 | 36 | jmp Init 37 | 38 | ; Player configuration 39 | 40 | PLAYER_ZPBASE = $fc 41 | PLAYER_SFX = 0 42 | PLAYER_MODULES = 0 43 | 44 | ; Player + musicdata 45 | 46 | include "player.s" 47 | 48 | Init: clc 49 | adc #$01 50 | sta PlayRoutine+1 51 | rts 52 | 53 | include "musicdata.s" 54 | --------------------------------------------------------------------------------