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 );
|
||||
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
|
||||
float pw = s.ff4f / 32768.0f;
|
||||
|
||||
float fb, res = s.patchRam.vcfReso / 24.0f; // guess
|
||||
float cut = s.patchRam.vcfFreq / 400.0f; // guess
|
||||
float fb, res = s.patchRam.vcfReso / 28.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 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);
|
||||
lastpw = pw;
|
||||
|
||||
out = b4 * (0.275);
|
||||
out = b4 * (0.2);
|
||||
|
||||
buffer[i] += (gain * b4 * vr58c106);
|
||||
}
|
||||
|
@ -34,10 +34,12 @@ class Voice {
|
||||
void run(Synth &s, float *buffer, uint32_t samples);
|
||||
void envelope(Synth &s);
|
||||
void calcPitch(Synth &s);
|
||||
void calcFilter(Synth &s);
|
||||
|
||||
uint16_t ff71 = 0; // stores pitch + fraction
|
||||
|
||||
float omega;
|
||||
uint16_t vcfenv;
|
||||
|
||||
private:
|
||||
enum { ATTACK,
|
||||
@ -54,6 +56,7 @@ class Voice {
|
||||
bool ff08 = 0; // set to indicate decay/sustain phase
|
||||
bool ff10 = 0; // note on bit
|
||||
bool ff11 = 0; // drives the "gate" signal for VCA
|
||||
uint16_t ff27 = 0; // envelope for voice
|
||||
bool ff33 = 0; // releasing flag
|
||||
|
||||
uint16_t env;
|
||||
@ -91,6 +94,9 @@ class Synth {
|
||||
uint16_t ff56 = 0; // LFO Delay envelope
|
||||
uint16_t ff5a = 0; // LFO Delay holdoff
|
||||
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
|
||||
uint16_t ff6f = 0; // computed pitch amount
|
||||
// uint16_t ff71 = 0; // unsure, to do with pitch
|
||||
@ -126,7 +132,6 @@ class Synth {
|
||||
uint8_t switch2 = 24;
|
||||
} patchRam;
|
||||
|
||||
|
||||
float pitchCV[104];
|
||||
|
||||
void runLFO();
|
||||
|
@ -36,249 +36,6 @@ 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;
|
||||
|
||||
// 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) {
|
||||
// this current implementation doesn't reset the voice
|
||||
(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) {
|
||||
uint16_t bc, ea = env;
|
||||
|
||||
@ -375,6 +554,8 @@ h0563:
|
||||
|
||||
h0590:
|
||||
env = ea;
|
||||
ff27 = ea;
|
||||
|
||||
// printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user