filter control calculations seem about right
This commit is contained in:
parent
ca65162e3f
commit
210401d6ed
@ -146,6 +146,8 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv
|
|||||||
|
|
||||||
// printf("voice %d note = %02x ff71 = %04x\n",i, s.voice[i].note, s.voice[i].ff71 );
|
// printf("voice %d note = %02x ff71 = %04x\n",i, s.voice[i].note, s.voice[i].ff71 );
|
||||||
s.voice[i].envelope(s);
|
s.voice[i].envelope(s);
|
||||||
|
s.voice[i].calcFilter(s);
|
||||||
|
//printf("voice %d vcf level = %04x\n", i, s.voice[i].vcfenv );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +41,15 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
|
|||||||
// there's a resistor on the panel board to sprag the range
|
// there's a resistor on the panel board to sprag the range
|
||||||
float pw = s.ff4f / 32768.0f;
|
float pw = s.ff4f / 32768.0f;
|
||||||
|
|
||||||
float fb, res = s.patchRam.vcfReso / 24.0f; // guess
|
float fb, res = s.patchRam.vcfReso / 28.0f; // guess
|
||||||
float cut = s.patchRam.vcfFreq / 400.0f; // guess
|
//float cut = s.patchRam.vcfFreq / 400.0f; // guess
|
||||||
|
|
||||||
|
float cut = 248.0f * (powf(2,(vcfenv-0x1880)/1143.0f));
|
||||||
|
// now radians
|
||||||
|
cut = 0.25 * 6.2832 * cut / 48000.0f;
|
||||||
|
|
||||||
|
// now correct
|
||||||
|
cut = cut/(1+cut);
|
||||||
|
|
||||||
float sqr = (s.patchRam.switch1 & 0x08) ? 0.63 : 0; //? 0.175 : 0;
|
float sqr = (s.patchRam.switch1 & 0x08) ? 0.63 : 0; //? 0.175 : 0;
|
||||||
float saw = (s.patchRam.switch1 & 0x10) ? 0.8 : 0; //? 0.220 : 0;
|
float saw = (s.patchRam.switch1 & 0x10) ? 0.8 : 0; //? 0.220 : 0;
|
||||||
@ -114,7 +121,7 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
|
|||||||
vr58c106 += ((vcaEnv - vr58c106) * 0.0075);
|
vr58c106 += ((vcaEnv - vr58c106) * 0.0075);
|
||||||
lastpw = pw;
|
lastpw = pw;
|
||||||
|
|
||||||
out = b4 * (0.275);
|
out = b4 * (0.2);
|
||||||
|
|
||||||
buffer[i] += (gain * b4 * vr58c106);
|
buffer[i] += (gain * b4 * vr58c106);
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,12 @@ class Voice {
|
|||||||
void run(Synth &s, float *buffer, uint32_t samples);
|
void run(Synth &s, float *buffer, uint32_t samples);
|
||||||
void envelope(Synth &s);
|
void envelope(Synth &s);
|
||||||
void calcPitch(Synth &s);
|
void calcPitch(Synth &s);
|
||||||
|
void calcFilter(Synth &s);
|
||||||
|
|
||||||
uint16_t ff71 = 0; // stores pitch + fraction
|
uint16_t ff71 = 0; // stores pitch + fraction
|
||||||
|
|
||||||
float omega;
|
float omega;
|
||||||
|
uint16_t vcfenv;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum { ATTACK,
|
enum { ATTACK,
|
||||||
@ -54,6 +56,7 @@ class Voice {
|
|||||||
bool ff08 = 0; // set to indicate decay/sustain phase
|
bool ff08 = 0; // set to indicate decay/sustain phase
|
||||||
bool ff10 = 0; // note on bit
|
bool ff10 = 0; // note on bit
|
||||||
bool ff11 = 0; // drives the "gate" signal for VCA
|
bool ff11 = 0; // drives the "gate" signal for VCA
|
||||||
|
uint16_t ff27 = 0; // envelope for voice
|
||||||
bool ff33 = 0; // releasing flag
|
bool ff33 = 0; // releasing flag
|
||||||
|
|
||||||
uint16_t env;
|
uint16_t env;
|
||||||
@ -91,6 +94,9 @@ class Synth {
|
|||||||
uint16_t ff56 = 0; // LFO Delay envelope
|
uint16_t ff56 = 0; // LFO Delay envelope
|
||||||
uint16_t ff5a = 0; // LFO Delay holdoff
|
uint16_t ff5a = 0; // LFO Delay holdoff
|
||||||
uint8_t ff64 = 0; // LFO mod sens amount
|
uint8_t ff64 = 0; // LFO mod sens amount
|
||||||
|
uint16_t ff65 = 0; // computed VCF bend amount
|
||||||
|
uint8_t ff6a = 0;
|
||||||
|
uint8_t ff6b = 0;
|
||||||
uint8_t ff6e = 0; // fractional pitch temp
|
uint8_t ff6e = 0; // fractional pitch temp
|
||||||
uint16_t ff6f = 0; // computed pitch amount
|
uint16_t ff6f = 0; // computed pitch amount
|
||||||
// uint16_t ff71 = 0; // unsure, to do with pitch
|
// uint16_t ff71 = 0; // unsure, to do with pitch
|
||||||
@ -126,7 +132,6 @@ class Synth {
|
|||||||
uint8_t switch2 = 24;
|
uint8_t switch2 = 24;
|
||||||
} patchRam;
|
} patchRam;
|
||||||
|
|
||||||
|
|
||||||
float pitchCV[104];
|
float pitchCV[104];
|
||||||
|
|
||||||
void runLFO();
|
void runLFO();
|
||||||
|
@ -36,249 +36,6 @@ bool Voice::isFree() {
|
|||||||
return ff10 == false;
|
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;
|
|
||||||
|
|
||||||
// 03ad
|
|
||||||
ea = 0x1818;
|
|
||||||
|
|
||||||
// add in tuning value from ff61, not implemented
|
|
||||||
|
|
||||||
// 03ba
|
|
||||||
bc = s.ff51; // computed pitch LFO
|
|
||||||
if (s.ff4a & 0x02) {
|
|
||||||
ea -= bc;
|
|
||||||
} else {
|
|
||||||
ea += bc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 03c6
|
|
||||||
// add in bender from ff68
|
|
||||||
|
|
||||||
// 03d2
|
|
||||||
s.ff6f = ea;
|
|
||||||
|
|
||||||
ea = ff71;
|
|
||||||
a = note;
|
|
||||||
|
|
||||||
// 03e3
|
|
||||||
bc = a << 8;
|
|
||||||
|
|
||||||
// 03e6
|
|
||||||
a = 0; // set from porta coefficient ff7d
|
|
||||||
|
|
||||||
if (a != 0) goto h03f5;
|
|
||||||
|
|
||||||
// 3eb
|
|
||||||
ea = bc;
|
|
||||||
h03ec:
|
|
||||||
ff71 = ea; // STEAX (DE++)
|
|
||||||
|
|
||||||
// we're not looping so we can ignore until
|
|
||||||
// 03ee inrw ff0f voice counter
|
|
||||||
// 03f0 eqiw ff0f, 06
|
|
||||||
// 03f3 jr 03e0
|
|
||||||
|
|
||||||
goto h0407;
|
|
||||||
|
|
||||||
h03f5:
|
|
||||||
if (ea == bc) goto h03ec; // DNE EA, BC; JR 03EC store value
|
|
||||||
if (!(ea > bc)) goto h0401; // DGT EA, BC; JR 0401
|
|
||||||
ea -= a;
|
|
||||||
if (!(ea > bc)) ea = bc;
|
|
||||||
goto h03ec;
|
|
||||||
h0401:
|
|
||||||
ea += a;
|
|
||||||
if (!(ea < bc)) ea = bc;
|
|
||||||
goto h03ec;
|
|
||||||
|
|
||||||
h0407:
|
|
||||||
// this outputs the Sub Osc CV
|
|
||||||
// ignore until
|
|
||||||
// 0413 mviw ff0f, 0 reset voice counter
|
|
||||||
s.ff34 = 1; // unsure what this is used for
|
|
||||||
|
|
||||||
// 0419
|
|
||||||
ea = ff71; // pitch + fraction per voice
|
|
||||||
bc = s.ff6f; // tune + lfo + bend
|
|
||||||
ea += bc;
|
|
||||||
// 0424
|
|
||||||
s.ff6e = ea & 0xff; // MOV A, EAL; STAW 006e
|
|
||||||
a = ea >> 8; // mov a, EAH
|
|
||||||
|
|
||||||
// 0428
|
|
||||||
if (a <= 0x2f) goto h04a5; // GTI A,$2F; JRE $04A5
|
|
||||||
if (a >= 0x97) goto h04ac; // LTI A,$97; JRE $04AC
|
|
||||||
|
|
||||||
a -= 0x30;
|
|
||||||
|
|
||||||
h0432:
|
|
||||||
// printf("setting omega for note %d \n", a);
|
|
||||||
omega = ((s.pitchCV[a + 1] - s.pitchCV[a]) * (s.ff6e / 256.0)) + s.pitchCV[a];
|
|
||||||
// 0432 onwards calculates the address for the CV
|
|
||||||
// table at E60 and stacks it
|
|
||||||
// 043a onwards fetches the value from the divider
|
|
||||||
// table and computes a linear interpolation with the next one up
|
|
||||||
// using the fractional value stored in ff6e
|
|
||||||
|
|
||||||
// 045a onwards decides which divider to program
|
|
||||||
|
|
||||||
// 0471 unstacks the CV table address and calculates a linear
|
|
||||||
// interpolation of this and the next CV value using ff6e
|
|
||||||
// 048b onwards sends it to the correct DAC
|
|
||||||
|
|
||||||
// 0496 onwards works out which vocie to do next and loops
|
|
||||||
|
|
||||||
// 04a3
|
|
||||||
goto h04d5;
|
|
||||||
|
|
||||||
h04a5: // pitch too low
|
|
||||||
s.ff6e = 0;
|
|
||||||
a = 0;
|
|
||||||
goto h0432;
|
|
||||||
|
|
||||||
h04ac: // pitch too high
|
|
||||||
s.ff6e = 0;
|
|
||||||
a = 0x66;
|
|
||||||
goto h0432;
|
|
||||||
|
|
||||||
// 04b3 programs the dividers somehow
|
|
||||||
// ignore until 04d5
|
|
||||||
|
|
||||||
h04d5:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Voice::on(uint32_t key, bool reset = 0) {
|
void Voice::on(uint32_t key, bool reset = 0) {
|
||||||
// this current implementation doesn't reset the voice
|
// this current implementation doesn't reset the voice
|
||||||
(void)reset;
|
(void)reset;
|
||||||
@ -318,6 +75,428 @@ void Voice::off() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: 5b 1e BIT 3,$001E ; ramp-up is complete?
|
||||||
|
// 0314: ce JR $0323 ; no
|
||||||
|
if (!(ff1e & 0x08)) goto h0323;
|
||||||
|
|
||||||
|
// no not was playing so reset
|
||||||
|
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
|
||||||
|
// 032e: 01 49 LDAW $0049 ; DCO LFO depth
|
||||||
|
a = lfoDepthTable[patchRam.vcoLfo];
|
||||||
|
|
||||||
|
// MUL B; MOV A, EAH
|
||||||
|
a = (a * (bc >> 8)) >> 8;
|
||||||
|
|
||||||
|
// add in the modwheel amount, clamp if it exceeds 0xff
|
||||||
|
// 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
|
||||||
|
// 0352: 01 48 LDAW $0048 ; VCF LFO 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
|
||||||
|
ea = ff56; // holdoff time
|
||||||
|
|
||||||
|
// 0375: 70 1f 58 ff LBCD $FF58 ; add on delay amount
|
||||||
|
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:
|
||||||
|
ea = ff5a; // envelope speed
|
||||||
|
|
||||||
|
// 038d: 70 1f 6c ff LBCD $FF6C ; value from "short" LFO lookup table
|
||||||
|
bc = lfoDelayTable[patchRam.lfoDelay >> 4]; // delay setting divided by 8 and saved at ff6c
|
||||||
|
|
||||||
|
// 0391 DADDNC EA, BC
|
||||||
|
if ((ea + bc) > 0xffff) goto h039a;
|
||||||
|
ea += bc;
|
||||||
|
|
||||||
|
// 0394
|
||||||
|
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:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::calcPitch(Synth &s) {
|
||||||
|
uint32_t bc, ea, a;
|
||||||
|
|
||||||
|
// 03ad
|
||||||
|
ea = 0x1818;
|
||||||
|
|
||||||
|
// add in tuning value from ff61, not implemented
|
||||||
|
|
||||||
|
// 03ba
|
||||||
|
// 03ba: 70 1f 51 ff LBCD $FF51 ; computed pitch LFO?
|
||||||
|
bc = s.ff51; // computed pitch LFO
|
||||||
|
if (s.ff4a & 0x02) {
|
||||||
|
ea -= bc;
|
||||||
|
} else {
|
||||||
|
ea += bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 03c6
|
||||||
|
// add in bender from ff68
|
||||||
|
|
||||||
|
// 03d2: 24 6f ff LXI DE,$FF6F
|
||||||
|
// 03d5: 48 92 STEAX (DE) ; save final value
|
||||||
|
s.ff6f = ea;
|
||||||
|
|
||||||
|
// these are set, because in the uPD7811 code it loops around all six voices
|
||||||
|
// 03d7: 71 0f 00 MVIW $000F,$00 ; voice counter
|
||||||
|
// 03da: 24 71 ff LXI DE,$FF71 ; DAC pitch table for voices
|
||||||
|
// 03dd: 34 09 ff LXI HL,$FF09 ; note pitch table for voices
|
||||||
|
// 03e0: 48 82 LDEAX (DE) ; fetch
|
||||||
|
// 03e2: 2d LDAX (HL+) ; fetch note
|
||||||
|
|
||||||
|
ea = ff71;
|
||||||
|
a = note;
|
||||||
|
|
||||||
|
// 03e3 MOV B,A
|
||||||
|
bc = a << 8;
|
||||||
|
|
||||||
|
// 03e6: 01 7d LDAW $007D ; porta coefficient
|
||||||
|
a = 0x00; // set from porta coefficient ff7d
|
||||||
|
|
||||||
|
if (a != 0) goto h03f5;
|
||||||
|
|
||||||
|
// 3eb
|
||||||
|
ea = bc;
|
||||||
|
h03ec:
|
||||||
|
ff71 = ea; // STEAX (DE++)
|
||||||
|
|
||||||
|
// if we were handling all voices in this loop we'd do
|
||||||
|
// 03ee: 20 0f INRW $000F ; voice counter
|
||||||
|
// 03f0: 75 0f 06 EQIW $000F,$06 ; loop
|
||||||
|
// 03f3: ec JR $03E0 ; loop around note
|
||||||
|
// 03f4: d2 JR $0407 ; jump ahead
|
||||||
|
|
||||||
|
goto h0407;
|
||||||
|
|
||||||
|
h03f5:
|
||||||
|
// portamento down
|
||||||
|
if (ea == bc) goto h03ec; // DNE EA, BC; JR 03EC store value
|
||||||
|
if (!(ea > bc)) goto h0401; // DGT EA, BC; JR 0401
|
||||||
|
ea -= a;
|
||||||
|
if (!(ea > bc)) ea = bc;
|
||||||
|
goto h03ec;
|
||||||
|
h0401:
|
||||||
|
// portamento up
|
||||||
|
ea += a;
|
||||||
|
if (!(ea < bc)) ea = bc;
|
||||||
|
goto h03ec;
|
||||||
|
|
||||||
|
h0407:
|
||||||
|
// bit of code that outputs sub osc CV
|
||||||
|
// 0413: 71 0f 00 MVIW $000F,$00 ; reset voice counter
|
||||||
|
// 0416: 71 34 01 MVIW $0034,$01 ; voice selector, first voice
|
||||||
|
|
||||||
|
// 0419
|
||||||
|
ea = ff71; // pitch + fraction per voice
|
||||||
|
bc = s.ff6f; // tune + lfo + bend
|
||||||
|
ea += bc;
|
||||||
|
// 0424
|
||||||
|
s.ff6e = ea & 0xff; // MOV A, EAL; STAW 006e
|
||||||
|
a = ea >> 8; // mov a, EAH
|
||||||
|
|
||||||
|
// 0428
|
||||||
|
if (a <= 0x2f) goto h04a5; // GTI A,$2F; JRE $04A5
|
||||||
|
if (a >= 0x97) goto h04ac; // LTI A,$97; JRE $04AC
|
||||||
|
|
||||||
|
a -= 0x30;
|
||||||
|
|
||||||
|
h0432:
|
||||||
|
// printf("setting omega for note %d \n", a);
|
||||||
|
omega = ((s.pitchCV[a + 1] - s.pitchCV[a]) * (s.ff6e / 256.0)) + s.pitchCV[a];
|
||||||
|
// 0432 onwards calculates the address for the CV
|
||||||
|
// table at E60 and stacks it
|
||||||
|
// 043a onwards fetches the value from the divider
|
||||||
|
// table and computes a linear interpolation with the next one up
|
||||||
|
// using the fractional value stored in ff6e
|
||||||
|
|
||||||
|
// 045a onwards decides which divider to program
|
||||||
|
|
||||||
|
// 0471 unstacks the CV table address and calculates a linear
|
||||||
|
// interpolation of this and the next CV value using ff6e
|
||||||
|
// 048b onwards sends it to the correct DAC
|
||||||
|
|
||||||
|
// 0496 onwards works out which voice to do next and loops
|
||||||
|
|
||||||
|
// 04a3: 4e 30 JRE $04D5 ; calculate filter
|
||||||
|
return;
|
||||||
|
|
||||||
|
h04a5: // pitch too low
|
||||||
|
s.ff6e = 0;
|
||||||
|
a = 0;
|
||||||
|
goto h0432;
|
||||||
|
|
||||||
|
h04ac: // pitch too high
|
||||||
|
s.ff6e = 0;
|
||||||
|
a = 0x66;
|
||||||
|
goto h0432;
|
||||||
|
|
||||||
|
// 04b3 programs the dividers somehow
|
||||||
|
}
|
||||||
|
|
||||||
|
void Voice::calcFilter(Synth &s) {
|
||||||
|
// 04d5
|
||||||
|
|
||||||
|
uint16_t a, bc, ea, hl, tos;
|
||||||
|
|
||||||
|
goto h04d5;
|
||||||
|
|
||||||
|
h04d5:
|
||||||
|
s.ff6a = 0;
|
||||||
|
// 04d8: 70 1f 3d ff LBCD $FF3D ; VCF cutoff
|
||||||
|
// 04dc: a5 DMOV EA,BC
|
||||||
|
ea = s.patchRam.vcfFreq << 7; // stored exended to two bytes
|
||||||
|
bc = s.ff53; // scaled VCF LFO
|
||||||
|
|
||||||
|
// 04e1: 59 4a BIT 1,$004A ; LFO add/sub flag
|
||||||
|
// 04e3: e7 JR $04CB
|
||||||
|
// 04e4: 74 b5 DSUBNB EA,BC ; add, skip if no borrow
|
||||||
|
// 04e6: 71 6a 01 MVIW $006A,$01 ; set a flag?
|
||||||
|
if (!(s.ff4a & 0x02)) {
|
||||||
|
ea += bc;
|
||||||
|
} else {
|
||||||
|
if ((ea - bc) < bc) {
|
||||||
|
s.ff6a = 1;
|
||||||
|
}
|
||||||
|
ea -= bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bc = s.ff65;
|
||||||
|
if (!(s.ff1e & 0x20)) {
|
||||||
|
if ((ea + bc) > 0xffff) {
|
||||||
|
s.ff6a = 0;
|
||||||
|
}
|
||||||
|
ea += bc;
|
||||||
|
} else {
|
||||||
|
if ((ea - bc) < bc) {
|
||||||
|
s.ff6a = 1;
|
||||||
|
}
|
||||||
|
ea -= bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 04f6
|
||||||
|
tos = ea;
|
||||||
|
|
||||||
|
// 04f7: 71 0f 00 MVIW $000F,$00 ; voice counter
|
||||||
|
// 04fa: 71 34 01 MVIW $0034,$01 ; voice enable bit
|
||||||
|
// 04fd: 34 71 ff LXI HL,$FF71 ; pitch + fraction table
|
||||||
|
// 0500: 24 25 ff LXI DE,$FF25 ; release time
|
||||||
|
// 0503: b3 PUSH HL ; TOS = address of DAC note, then VCF Bias
|
||||||
|
|
||||||
|
// 0504
|
||||||
|
ea = ff27; // current envelope level
|
||||||
|
|
||||||
|
// in the real ROM the envelope code would run here
|
||||||
|
|
||||||
|
// there's something about the first voice, I'd need to emulate that a bit more
|
||||||
|
|
||||||
|
// 05a6 sets the DAC depending on the state of the ENV/GATE switch
|
||||||
|
// 05c0: a3 POP HL ; HL might have started as FF71
|
||||||
|
|
||||||
|
// hl = ff71; // this was stored on the stack back at 0503
|
||||||
|
|
||||||
|
a = s.ff6a;
|
||||||
|
s.ff6b = a;
|
||||||
|
|
||||||
|
ea = ff27;
|
||||||
|
bc = ea;
|
||||||
|
|
||||||
|
// 05c8: 01 41 LDAW $0041 ; VCF ENV MOD
|
||||||
|
a = s.patchRam.vcfEnv << 1; // stored doubled
|
||||||
|
|
||||||
|
// 05c8
|
||||||
|
ea = (bc & 0xff) * a; // MUL C
|
||||||
|
a = ea >> 8; // MOV A, EAH
|
||||||
|
bc &= 0xff00;
|
||||||
|
bc |= a; // MOV C,A
|
||||||
|
a = s.patchRam.vcfEnv << 1; // stored doubled
|
||||||
|
ea = (bc >> 8) * a; // MUL B
|
||||||
|
ea += (bc & 0xff); // EADD EA,C
|
||||||
|
bc = ea;
|
||||||
|
ea = tos; // precomputed VCF knob + LFO + bend
|
||||||
|
|
||||||
|
if (!(s.patchRam.switch2 & 0x02)) {
|
||||||
|
if ((ea + bc) > 0xffff) {
|
||||||
|
s.ff6b = 0;
|
||||||
|
}
|
||||||
|
ea += bc;
|
||||||
|
} else {
|
||||||
|
if ((ea - bc) < bc) {
|
||||||
|
s.ff6b = 1;
|
||||||
|
}
|
||||||
|
ea -= bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 05e0
|
||||||
|
tos = ea; // save
|
||||||
|
ea = ff71; // pitch value
|
||||||
|
|
||||||
|
ea >>= 2;
|
||||||
|
bc = ea;
|
||||||
|
ea >>= 1;
|
||||||
|
ea += bc; // multiplied by 0.375
|
||||||
|
|
||||||
|
bc = 0x1680; // this is 0x3c00 * 0.375, middle C * 0.375
|
||||||
|
|
||||||
|
if (ea <= bc) goto h0620;
|
||||||
|
|
||||||
|
// 05f3
|
||||||
|
ea -= bc;
|
||||||
|
bc = ea;
|
||||||
|
|
||||||
|
// 05f6
|
||||||
|
a = s.patchRam.vcfKey << 1; // stored doubled at ff42
|
||||||
|
ea = (bc & 0xff) * a; // MUL C
|
||||||
|
a = ea >> 8; // MOV A, EAH
|
||||||
|
bc &= 0xff00;
|
||||||
|
bc |= a; // MOV C,A
|
||||||
|
a = s.patchRam.vcfKey << 1; // needs lookup table
|
||||||
|
ea = (bc >> 8) * a; // MUL B
|
||||||
|
ea += (bc & 0xff); // EADD EA,C
|
||||||
|
bc = ea;
|
||||||
|
|
||||||
|
// 0603
|
||||||
|
ea = tos; // get saved VCF back
|
||||||
|
|
||||||
|
// 0604: 74 a5 DADDNC EA,BC
|
||||||
|
// 0606: 71 6b 00 MVIW $006B,$00
|
||||||
|
|
||||||
|
if ((ea + bc) > 0xffff) {
|
||||||
|
s.ff6b = 0;
|
||||||
|
}
|
||||||
|
ea += bc;
|
||||||
|
|
||||||
|
h0609:
|
||||||
|
if (!(ea & 0xc000)) goto h063c;
|
||||||
|
ea = 0;
|
||||||
|
// 0611
|
||||||
|
if (!(s.ff6b & 0x01))
|
||||||
|
ea = 0x3fff;
|
||||||
|
goto h063c;
|
||||||
|
|
||||||
|
h0620:
|
||||||
|
bc = ea;
|
||||||
|
ea = 0x1680;
|
||||||
|
ea -= bc;
|
||||||
|
bc = ea;
|
||||||
|
|
||||||
|
// 0627
|
||||||
|
a = s.patchRam.vcfKey << 1; // stored doubled at ff42
|
||||||
|
ea = (bc & 0xff) * a; // MUL C
|
||||||
|
a = ea >> 8; // MOV A, EAH
|
||||||
|
bc &= 0xff00;
|
||||||
|
bc |= a; // MOV C,A
|
||||||
|
a = s.patchRam.vcfKey << 1; // needs lookup table
|
||||||
|
ea = (bc >> 8) * a; // MUL B
|
||||||
|
ea += (bc & 0xff); // EADD EA,C
|
||||||
|
bc = ea;
|
||||||
|
|
||||||
|
// 0634
|
||||||
|
ea = tos;
|
||||||
|
if ((ea - bc) < bc) {
|
||||||
|
s.ff6b = 1;
|
||||||
|
}
|
||||||
|
ea -= bc;
|
||||||
|
goto h0609;
|
||||||
|
|
||||||
|
|
||||||
|
h063c:
|
||||||
|
vcfenv = ea;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void Voice::envelope(Synth &s) {
|
void Voice::envelope(Synth &s) {
|
||||||
uint16_t bc, ea = env;
|
uint16_t bc, ea = env;
|
||||||
|
|
||||||
@ -375,6 +554,8 @@ h0563:
|
|||||||
|
|
||||||
h0590:
|
h0590:
|
||||||
env = ea;
|
env = ea;
|
||||||
|
ff27 = ea;
|
||||||
|
|
||||||
// printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33);
|
// printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user