diff --git a/plugin/Makefile b/plugin/Makefile index da69fab..e636350 100644 --- a/plugin/Makefile +++ b/plugin/Makefile @@ -11,7 +11,7 @@ NAME = chassis FILES_DSP = \ parameters.cpp \ - digital.cpp \ + voicecpu.cpp \ chassis.cpp \ voice.cpp diff --git a/plugin/digital.cpp b/plugin/voicecpu.cpp similarity index 96% rename from plugin/digital.cpp rename to plugin/voicecpu.cpp index 3288f0e..e19e6a1 100644 --- a/plugin/digital.cpp +++ b/plugin/voicecpu.cpp @@ -18,7 +18,12 @@ // contains the emulation of the digital bits -#include "digital.hpp" +// what's with the crazy private variables and all the gotos with crazy labels? +// this code emulates the uPD7811 code directly (probably inefficiently) +// to allow for documenting what the variables actually do +// they're really bitfields holding a bit for each voice + +#include "voicecpu.hpp" #include #include @@ -31,6 +36,136 @@ bool Voice::isFree() { return ff10 == false; } +void Synth::lfoDelay() { + // compute LFO delay + + uint16_t a, bc, d, ea, tos; + + // 030d: 45 11 3f ONIW $0011,$3F ; are any notes enabled + // 0310: 4e 59 JRE $036B ; no, just run LFO + if (!keyon /* ff11 */) goto h036b; // skip ahead if no notes are pressed + + // 0312 + if (!(ff1e & 0x08)) goto h0323; // a note is running, don't reset + + // 0315 no note is running, reset it all + bc = 0; + ff56 = 0; // delay envelope + ff5a = 0; // holdoff timer + ff1e &= 0xf1; // mask bits in flag byte + +h0323: + if (!(ff1e & 0x02)) goto h0370; // compute delay envelope + if (!(ff1e & 0x04)) goto h0388; // compute holdoff timer + + // 032b + bc |= 0xff00; // initial scaling value? + +h032d: + tos = bc; // push bc + + // 032e + a = lfoDepthTable[patchRam.vcoLfo]; + + // MUL B; MOV A, EAH + a = (a * (bc >> 8)) >> 8; + + // 0333 ADDNCW $0064; MVI A, $FF + a += ff64; + if (a > 0xff) a = 0xff; + + // 0338 sets up HL to store computed pitch LFO output + + // 33b + bc = ff4d; // current LFO output + + // 033f + ea = (bc & 0xff) * a; // MUL C + d = a; // MOV D,A + a = (ea >> 8); // MOV A,EAH + bc &= 0xff00; + bc |= a; // MOV C,A + a = d; // MOV A,D + + // 0345 + ea = (bc >> 8) * a; // MUL B + ea += (bc & 0xff); // EADD EA, C + + // 0349 + ea >>= 3; // divide by eight + + // 034f + ff51 = ea; // save scaled pitch LFO + + bc = tos; // pop BC, contains scaling amount + a = patchRam.vcfLfo << 1; // amount is doubled and stored at ff48 + + // 0354 + ea = (bc >> 8) * a; // MUL B + a = ea >> 8; // MOV A, EAH + bc = ff4d; // current LFO output + + // 035b + ea = (bc & 0xff) * a; // MUL C + d = a; // MOV D,A + a = ea >> 8; // MOV A, EAH + bc &= 0xff00; + bc |= a; // MOV C,A + a = d; // MOV A,D + ea = (bc >> 8) * a; // MUL B + ea += (bc & 0xff); // EADD EA,C + ea >>= 1; // DSLR A + ff53 = ea; // save scaled VCF LFO + goto h03a1; + +h036b: + ff1e |= 0x08; // set LFO flag + goto h0323; + +h0370: // calculate holdoff time + // printf("0370 "); + ea = ff56; // holdoff time + bc = attackTable[patchRam.lfoDelay]; // stored at ff58 + // 0379 + ea += bc; // DADD EA,BC + ff56 = ea; // STEAX (DE) which still holds ff56 from 0x370 + + a = ea >> 8; // MOV A, EAH + if (a & 0xc0) goto h0385; // OFFI A, $C0 + bc &= 0xff; // MOV B, 0 + goto h032d; +h0385: + ff1e |= 0x02; // stop predelay flag + +h0388: + // printf("0388 "); + ea = ff5a; // envelope speed + + // 038d + bc = lfoDelayTable[patchRam.lfoDelay >> 4]; // delay setting divided by 8 and saved at ff6c + + // printf("---------------------------------------- %04x %04x\n", ea, bc); + + // 0391 DADDNC EA, BC + if ((ea + bc) > 0xffff) goto h039a; + ea += bc; + + // 394 + ff5a = ea; // STEAX (HL) hl still contains ff5a + + bc |= (ea & 0xff00); // MOV A, EAH; MOV B, A + goto h032d; + +h039a: + ff1e |= 0x04; + bc |= 0xff; // MVI B, $ff + goto h032d; + +h03a1: + // printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a); + return; +} + void Voice::calcPitch(Synth &s) { uint32_t bc, ea, a; @@ -145,11 +280,6 @@ h04d5: } void Voice::on(uint32_t key, bool reset = 0) { - // what's with the crazy private variables and all the gotos with crazy labels? - // this code emulates the 78C11 code directly (probably inefficiently) - // to allow for documenting what the variables actually do - // they're really bitfields holding a bit for each voice - // this current implementation doesn't reset the voice (void)reset; @@ -248,139 +378,6 @@ h0590: // printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33); } -void Synth::lfoDelay() { - // compute LFO delay - - uint16_t a, bc, d, ea, tos; - - // 030d - if (!keyon /* ff11 */) goto h036b; // skip ahead if no notes are pressed - - // 0312 - if (!(ff1e & 0x08)) goto h0323; // a note is running, don't reset - - // 0315 no note is running, reset it all - bc = 0; - ff56 = 0; // delay envelope - ff5a = 0; // holdoff timer - ff1e &= 0xf1; // mask bits in flag byte - -h0323: - if (!(ff1e & 0x02)) goto h0370; // compute delay envelope - if (!(ff1e & 0x04)) goto h0388; // compute holdoff timer - - // 032b - bc |= 0xff00; // initial scaling value? - - // printf("0323 "); - -h032d: - tos = bc; // push bc - - // 032e - a = lfoDepthTable[patchRam.vcoLfo]; - - // MUL B; MOV A, EAH - a = (a * (bc >> 8)) >> 8; - - // 0333 ADDNCW $0064; MVI A, $FF - a += ff64; - if (a > 0xff) a = 0xff; - - // 0338 sets up HL to store computed pitch LFO output - - // 33b - bc = ff4d; // current LFO output - - // printf("-----------------------%02x %04x\n", a, bc); - - // 033f - ea = (bc & 0xff) * a; // MUL C - d = a; // MOV D,A - a = (ea >> 8); // MOV A,EAH - bc &= 0xff00; - bc |= a; // MOV C,A - a = d; // MOV A,D - - // 0345 - ea = (bc >> 8) * a; // MUL B - ea += (bc & 0xff); // EADD EA, C - - // 0349 - ea >>= 3; // divide by eight - - // 034f - ff51 = ea; // save scaled pitch LFO - - bc = tos; // pop BC, contains scaling amount - a = patchRam.vcfLfo << 1; // amount is doubled and stored at ff48 - - // 0354 - ea = (bc >> 8) * a; // MUL B - a = ea >> 8; // MOV A, EAH - bc = ff4d; // current LFO output - - // 035b - ea = (bc & 0xff) * a; // MUL C - d = a; // MOV D,A - a = ea >> 8; // MOV A, EAH - bc &= 0xff00; - bc |= a; // MOV C,A - a = d; // MOV A,D - ea = (bc >> 8) * a; // MUL B - ea += (bc & 0xff); // EADD EA,C - ea >>= 1; // DSLR A - ff53 = ea; // save scaled VCF LFO - goto h03a1; - -h036b: - ff1e |= 0x08; // set LFO flag - goto h0323; - -h0370: // calculate holdoff time - // printf("0370 "); - ea = ff56; // holdoff time - bc = attackTable[patchRam.lfoDelay]; // stored at ff58 - // 0379 - ea += bc; // DADD EA,BC - ff56 = ea; // STEAX (DE) which still holds ff56 from 0x370 - - a = ea >> 8; // MOV A, EAH - if (a & 0xc0) goto h0385; // OFFI A, $C0 - bc &= 0xff; // MOV B, 0 - goto h032d; -h0385: - ff1e |= 0x02; // stop predelay flag - -h0388: - // printf("0388 "); - ea = ff5a; // envelope speed - - // 038d - bc = lfoDelayTable[patchRam.lfoDelay >> 4]; // delay setting divided by 8 and saved at ff6c - - // printf("---------------------------------------- %04x %04x\n", ea, bc); - - // 0391 DADDNC EA, BC - if ((ea + bc) > 0xffff) goto h039a; - ea += bc; - - // 394 - ff5a = ea; // STEAX (HL) hl still contains ff5a - - bc |= (ea & 0xff00); // MOV A, EAH; MOV B, A - goto h032d; - -h039a: - ff1e |= 0x04; - bc |= 0xff; // MVI B, $ff - goto h032d; - -h03a1: - // printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a); - return; -} - void Synth::runLFO() { // compute a loop's worth of LFO diff --git a/plugin/digital.hpp b/plugin/voicecpu.hpp similarity index 100% rename from plugin/digital.hpp rename to plugin/voicecpu.hpp