A quick update on this: Just got the parser to output it's first blob of correct asm data. Unfortunately today is the last day I can work on it for the forseeable future, but I'm glad I got this far. There's still tons of functionality missing, and the code would probably give any self-respecting programmer a heart attack at this point. So long story short, there probably won't be any public release till beginning of 2017.
In the meantime, here's a little preview, to give you an idea how the thing works (it has a name now, too, btw - MDAL aka Music Data Abstraction Language).
The input music file looks like this:
CONFIG=PhaseSqueek
:SEQUENCE
intro
pattern0
[loop]
pattern0
pattern2
:intro
A=a1, B=c3, C=e3, GMIX=or, MIXAB=or, MIXCD=or, SPD=$8
.
A=a2
.
A=a3
.
A=a2, C=0
.
:pattern0
.
:pattern2
.
which gives us this:
dw intro
dw pattern0
loop
dw pattern0
dw pattern2
dw 0
intro
dw #b000, fx0, #b000, a1, c3, #4040, #c6, #c6, #0, #b000, e3, #0, #4040, #0, #800
dw #b085, #800
dw #b001, #b0c4, a2, #0, #800
dw #b085, #800
dw #b001, #b0c4, a3, #0, #800
dw #b085, #800
dw #b001, #b0c4, a2, #0, #b0c0, #0, #0, #800
dw #b085, #800
db #40
pattern0
dw #b085, #800
db #40
pattern2
dw #b085, #800
db #40
based on this config:
MDAL_VERSION(0)
USE_SEQUENCE;
USE_PATTERNS;
WORD_DIRECTIVE("dw");
BYTE_DIRECTIVE("db");
HEX_PREFIX("#");
CFG_SEQUENCE {
USE_END("dw 0");
USE_LOOP(LABEL, "loop");
USE_TRACKS(1);
}
CFG_PATTERNS {
USE_END("db #40");
}
CFG_COMMANDS {
WORD("FX","fx0");
WORD("A", 0, USE_LAST_SET|USE_RESTS(0));
WORD("B", 0, USE_LAST_SET|USE_RESTS(0));
BYTE("DA", $40, USE_LAST_SET);
BYTE("DB", $40, USE_LAST_SET);
BYTE("SIDA", $c6, USE_LAST_SET|FORCE_SUBSTITUTION("off"=$c6, "on"=$ce));
BYTE("SIDB", $c6, USE_LAST_SET|FORCE_SUBSTITUTION("off "=$c6, "on"=$ce));
BYTE("ESA", 0, USE_LAST_SET);
BYTE("ESB", 0, USE_LAST_SET);
WORD("PAB", 0, USE_LAST_SET);
WORD("C", 0, USE_LAST_SET|USE_RESTS(0));
WORD("D", 0, USE_LAST_SET|USE_RESTS(0));
BYTE("DC", $40, USE_LAST_SET);
BYTE("DD", $40, USE_LAST_SET);
BOOL("NC", false, USE_LAST_SET|FORCE_SUBSTITUTION("off"=false, "on"=true));
WORD("PCD", 0, USE_LAST_SET);
BYTE("DRUM", 0, FORCE_SUBSTITUTION("kick"=$40, "hat"=$80));
BYTE("GMIX", $b0, FORCE_REPEAT|FORCE_SUBSTITUTION("or"=$b0, "xor"=$a8, "and"=$a0));
BYTE("MIXAB", $b0, USE_LAST_SET|FORCE_SUBSTITUTION("or"=$b0, "xor"=$a8, "and"=$a0));
BYTE("MIXCD", $b0, USE_LAST_SET|FORCE_SUBSTITUTION("or"=$b0, "xor"=$a8, "and"=$a0));
BYTE("GSPD", $10, GLOBAL_CONST);
BYTE("SPD", $10, FORCE_REPEAT);
}
CFG_FIELDS {
WORD(0, REQUIRED(), SET_IF(!(FX&A&B&DA&DB&SIDA&SIDB&ESA&ESB&PAB&C&D&DC&DD&NC&PCD&MIXAB&MIXCD), 4), SET_IF(!FX, 1), SET_IF(!(A&B&DA&DB&SIDA&SIDB&ESA&ESB&PAB&MIXAB), $80), SET_HI(GMIX));
WORD("fx0", SET(FX));
WORD(0, REQUIRED(A|B|DA|DB|SIDA|SIDB|ESA|ESB|PAB|MIXAB), SET_IF(!(A&B), 1), SET_IF(!(SIDA&SIDB&ESA&ESB), 4), SET_IF(!DA&DB, $40), SET_IF(!PAB, $80), SET_HI(MIXAB));
WORD(1, REQUIRED(B), SET(A));
WORD(2, REQUIRED(A), SET(B));
WORD($4040, SET_HI(DA), SET_LO(DB));
WORD($00c6, SET_HI(ESA), SET_LO(SIDA));
WORD($00c6, SET_HI(ESB), SET_LO(SIDB));
WORD(3, SET(PAB));
WORD(4, REQUIRED(C|D|DC|DD|NC|PCD|MIXCD), SET_IF(!(C&D), 1), SET_IF(!(DC&DD), $40), SET_IF(!PCD, $80), SET_HI(MIXCD));
WORD(5, REQUIRED(D), SET(C));
WORD(6, REQUIRED(C), SET(D));
WORD($4040, SET_HI(DC), SET_LO(DD));
WORD(7, SET(PCD));
WORD($1000, REQUIRED(), SET_HI(SPD), SET_LO(DRUM));
REQUIRE_SEQ_BEGIN();
}