5caf0d49041b — Laurens Holst 3 years ago
modules: Replace period / frequency lookup tables with calculations.

Based on a single base-2 exponentiation table.
This allows binary fractions for key values, for pitch bends and such.
M src/COM.asm +7 -1
@@ 38,6 38,7 @@ COM_Main:
 	INCLUDE "Mapper.asm"
 	INCLUDE "Heap.asm"
 	INCLUDE "HeapTest.asm"
+	INCLUDE "Math.asm"
 	INCLUDE "VDP.asm"
 	INCLUDE "Application.asm"
 	INCLUDE "CLI.asm"

          
@@ 48,7 49,6 @@ COM_Main:
 	INCLUDE "MSXMusic.asm"
 	INCLUDE "PSG.asm"
 	INCLUDE "Synthesizer.asm"
-	INCLUDE "Resources.asm"
 	INCLUDE "modules/Channel.asm"
 	INCLUDE "modules/Note.asm"
 	INCLUDE "modules/Constants.asm"

          
@@ 75,3 75,9 @@ Heap_main:
 	Heap HEAP, HEAP_SIZE
 
 	ENDS
+
+	SECTION TPA_PAGE1
+
+	INCLUDE "Resources.asm"
+
+	ENDS

          
A => src/Math.asm +48 -0
@@ 0,0 1,48 @@ 
+;
+; Math functions
+;
+
+; hl = exponent (signed fixed point 1.4.11) (least significant bit ignored)
+; dehl <- product (fixed point 16.16)
+; Modifies: af, b, de, hl
+Math_Pow2: PROC
+	ld b,h
+	res 0,l
+	ld a,h
+	and 07H
+	ld h,a
+	ld de,Math_pow2Table
+	add hl,de
+	ld e,(hl)
+	inc l
+	ld d,(hl)
+	ex de,hl
+	sra b
+	sra b
+	sra b
+	jp m,NegativeExponent
+PositiveExponent:
+	ld de,1
+	ret z
+PositiveShiftLoop:
+	add hl,hl
+	ex de,hl
+	adc hl,hl
+	ex de,hl
+	djnz PositiveShiftLoop
+	ret
+NegativeExponent:
+	ld de,0
+	scf
+	rr h
+	rr l
+	ld a,-1
+	sub b
+	ret z
+	ld b,a
+NegativeShiftLoop:
+	srl h
+	rr l
+	djnz NegativeShiftLoop
+	ret
+	ENDP

          
M src/modules/OPLLChannel.asm +1 -1
@@ 9,7 9,7 @@ OPLLChannel: MACRO ?msxMusic, ?channel
 	sustain: Or sustainVelocity.value, channel.controllers + 69
 	volumeControllers: Attenuate channel.controllers + 7, channel.controllers + 11
 	volume: Attenuate channel.note.velocity, volumeControllers.value
-	opllTone: OPLLTone ?msxMusic, ?channel, instrument.value, volume.value, gate.value, channel.note.trigger, channel.note.key, sustain.value
+	opllTone: OPLLTone ?msxMusic, ?channel, instrument.value, volume.value, gate.value, channel.note.trigger, channel.note.key, Constants_instance + 0, sustain.value
 	Update:
 		call instrument.Update
 		call gate.Update

          
M src/modules/OPLLTone.asm +37 -10
@@ 1,7 1,7 @@ 
 ;
 ;
 ;
-OPLLTone: MACRO ?msxMusic, ?channel, ?instrument, ?volume, ?gate, ?trigger, ?key, ?sustain
+OPLLTone: MACRO ?msxMusic, ?channel, ?instrument, ?volume, ?gate, ?trigger, ?key, ?keyFraction, ?sustain
 	WriteRegister: equ ?msxMusic + MSXMusic.WriteRegister
 	Update:
 		ld a,(?trigger)

          
@@ 33,20 33,19 @@ OPLLTone: MACRO ?msxMusic, ?channel, ?in
 		ld a,30H + ?channel
 		call WriteRegister
 		ld a,(?key)
+		ld b,a
+		ld a,(?keyFraction)
 		add a,a
-		ld e,a
-		ld d,0
-		ld hl,OPLLTone_frequencyTable
-		add hl,de
-		ld d,(hl)
+		ld c,a
+		call OPLLTone_GetFNumBlock
+		ld d,l
 		ld a,10H + ?channel
 		call WriteRegister
-		inc hl
 		ld a,(?gate)
 		rrca
 		rrca
 		and 00010000B
-		or (hl)
+		or h
 		ld d,a
 		ld a,(?sustain)
 		bit 6,a

          
@@ 59,6 58,34 @@ OPLLTone: MACRO ?msxMusic, ?channel, ?in
 		jp WriteRegister
 	ENDM
 
-; ix = this
-OPLLTone_Construct:
+; b = note number
+; c = fraction
+; hl <- fnum + block
+OPLLTone_GetFNumBlock: PROC
+	ld hl,0AAABH  ; de = hl * (1 / 1.5)
+	muluw hl,bc
+	ld hl,-5263  ; (-69 / 12 + log2(440) + 18 + log2(72) - log2(3579545) - 8) * 2048
+	add hl,de
+	ld a,h
+	and ~07H
+	ld c,0
+	jp m,Continue
+	cp 8 << 3
+	jr nc,Clamp
+	ld c,a
+	ld a,h
+	sub c
+	ld h,a
+Continue:
+	call Math_Pow2
+	ld l,h
+	ld a,c
+	rrca
+	rrca
+	or e
+	ld h,a
 	ret
+Clamp:
+	ld hl,0FFFH
+	ret
+	ENDP

          
M src/modules/PSGEnvelope.asm +18 -8
@@ 1,7 1,7 @@ 
 ;
 ;
 ;
-PSGEnvelope: MACRO ?psg, ?channel, ?shape, ?key, ?gate
+PSGEnvelope: MACRO ?psg, ?channel, ?shape, ?key, ?keyFraction, ?gate
 	WriteRegister: equ ?psg + PSG.WriteRegister
 	ReadRegister: equ ?psg + PSG.ReadRegister
 	Update:

          
@@ 16,16 16,15 @@ PSGEnvelope: MACRO ?psg, ?channel, ?shap
 		ld a,0DH
 		call nz,WriteRegister
 		ld a,(?key)
+		ld b,a
+		ld a,(?keyFraction)
 		add a,a
-		ld e,a
-		ld d,0
-		ld hl,PSGEnvelope_frequencyTable
-		add hl,de
-		ld d,(hl)
+		ld c,a
+		call PSGEnvelope_GetPeriod
+		ld d,l
 		ld a,0BH
 		call WriteRegister
-		inc hl
-		ld d,(hl)
+		ld d,h
 		ld a,0CH
 		call WriteRegister
 		ld a,08H + ?channel

          
@@ 41,3 40,14 @@ PSGEnvelope: MACRO ?psg, ?channel, ?shap
 		ld a,08H + ?channel
 		jp WriteRegister
 	ENDM
+
+; b = note number
+; c = fraction
+; hl <- period
+PSGEnvelope_GetPeriod:
+	ld hl,0AAABH  ; de = hl * (1 / 1.5)
+	muluw hl,bc
+	ld hl,-10773  ; (69 / 12 - log2(440) - log2(256) + log2(3579545) - 16) * 2048
+	and a
+	sbc hl,de
+	jp Math_Pow2

          
M src/modules/PSGEnvelopeChannel.asm +1 -1
@@ 5,7 5,7 @@ PSGEnvelopeChannel: MACRO ?psg, ?channel
 	channel: Channel
 	shape: Clamp channel.program, Constants_instance + 0, Constants_instance + 7
 	gate: Or channel.note.gate, channel.controllers + 64
-	psgEnvelope: PSGEnvelope ?psg, ?channel, shape.value, channel.note.key, gate.value
+	psgEnvelope: PSGEnvelope ?psg, ?channel, shape.value, channel.note.key, Constants_instance + 0, gate.value
 	Update:
 		call shape.Update
 		call gate.Update

          
M src/modules/PSGNoise.asm +24 -6
@@ 1,16 1,17 @@ 
 ;
 ;
 ;
-PSGNoise: MACRO ?psg, ?channel, ?key, ?gate
+PSGNoise: MACRO ?psg, ?channel, ?key, ?keyFraction, ?gate
 	WriteRegister: equ ?psg + PSG.WriteRegister
 	ReadRegister: equ ?psg + PSG.ReadRegister
 	Update:
 		ld a,(?key)
-		ld e,a
-		ld d,0
-		ld hl,PSGNoise_frequencyTable
-		add hl,de
-		ld d,(hl)
+		ld b,a
+		ld a,(?keyFraction)
+		add a,a
+		ld c,a
+		call PSGNoise_GetPeriod
+		ld d,l
 		ld a,06H
 		call WriteRegister
 		ld a,07H

          
@@ 25,3 26,20 @@ PSGNoise: MACRO ?psg, ?channel, ?key, ?g
 		ld a,07H
 		jp WriteRegister
 	ENDM
+
+; b = note number
+; c = fraction
+; hl <- period
+PSGNoise_GetPeriod:
+	ld hl,0AAABH  ; de = hl * (1 / 1.5)
+	muluw hl,bc
+	ld hl,-14869  ; (69 / 12 - 6 - log2(440) - log2(16) + log2(3579545) - 16) * 2048
+	and a
+	sbc hl,de
+	call Math_Pow2
+	ld a,l
+	and ~1FH
+	or h
+	ret z
+	ld hl,1FH  ; clamp
+	ret

          
M src/modules/PSGNoiseChannel.asm +1 -1
@@ 4,7 4,7 @@ 
 PSGNoiseChannel: MACRO ?psg, ?channel
 	channel: Channel
 	gate: Or channel.note.gate, channel.controllers + 64
-	psgNoise: PSGNoise ?psg, ?channel, channel.note.key, gate.value
+	psgNoise: PSGNoise ?psg, ?channel, channel.note.key, Constants_instance + 0, gate.value
 	Update:
 		call gate.Update
 		jp psgNoise.Update

          
M src/modules/PSGTone.asm +23 -8
@@ 1,7 1,7 @@ 
 ;
 ;
 ;
-PSGTone: MACRO ?psg, ?channel, ?key, ?gate, ?volume
+PSGTone: MACRO ?psg, ?channel, ?key, ?keyFraction, ?gate, ?volume
 	WriteRegister: equ ?psg + PSG.WriteRegister
 	ReadRegister: equ ?psg + PSG.ReadRegister
 	Update:

          
@@ 19,16 19,15 @@ PSGTone: MACRO ?psg, ?channel, ?key, ?ga
 		ld a,08H + ?channel
 		call WriteRegister
 		ld a,(?key)
+		ld b,a
+		ld a,(?keyFraction)
 		add a,a
-		ld e,a
-		ld d,0
-		ld hl,PSGTone_frequencyTable
-		add hl,de
-		ld d,(hl)
+		ld c,a
+		call PSGTone_GetPeriod
+		ld d,l
 		ld a,00H + ?channel * 2
 		call WriteRegister
-		inc hl
-		ld d,(hl)
+		ld d,h
 		ld a,01H + ?channel * 2
 		call WriteRegister
 		ld a,07H

          
@@ 43,3 42,19 @@ PSGTone: MACRO ?psg, ?channel, ?key, ?ga
 		ld a,07H
 		jp WriteRegister
 	ENDM
+
+; b = note number
+; c = fraction
+; hl <- period
+PSGTone_GetPeriod:
+	ld hl,0AAABH  ; de = hl * (1 / 1.5)
+	muluw hl,bc
+	ld hl,-2580  ; (69 / 12 - log2(440) - log2(16) + log2(3579545) - 16) * 2048
+	and a
+	sbc hl,de
+	call Math_Pow2
+	ld a,h
+	and ~0FH
+	ret z
+	ld hl,0FFFH  ; clamp
+	ret

          
M src/modules/PSGToneChannel.asm +1 -1
@@ 6,7 6,7 @@ PSGToneChannel: MACRO ?psg, ?channel
 	gate: Or channel.note.gate, channel.controllers + 64
 	volumeControllers: Attenuate channel.controllers + 7, channel.controllers + 11
 	volume: Attenuate channel.note.velocity, volumeControllers.value
-	psgTone: PSGTone ?psg, ?channel, channel.note.key, gate.value, volume.value
+	psgTone: PSGTone ?psg, ?channel, channel.note.key, Constants_instance + 0, gate.value, volume.value
 	Update:
 		call gate.Update
 		call volumeControllers.Update

          
M tools/buildres.js +18 -133
@@ 1,149 1,34 @@ 
 function main()
 {
-	console.log("OPLLTone_frequencyTable:");
-	console.log(new OPLLToneTable().toAsm());
-	console.log("");
-	console.log("PSGTone_frequencyTable:");
-	console.log(new PSGToneTable().toAsm());
-	console.log("");
-	console.log("PSGNoise_frequencyTable:");
-	console.log(new PSGNoiseTable().toAsm());
-	console.log("");
-	console.log("PSGEnvelope_frequencyTable:");
-	console.log(new PSGEnvelopeTable().toAsm());
+	console.log("\tALIGN 2");
+	console.log("Math_pow2Table:");
+	console.log(new Pow2Table().toAsm());
 }
 
-class OPLLToneTable
+function pow2(x)
+{
+	return Math.pow(2, x);
+}
+
+function log2(x)
+{
+	return Math.log(x) / Math.log(2);
+}
+
+class Pow2Table
 {
 	constructor()
 	{
-		this.notes = [];
-		for (let i = 0; i < 128; i++)
+		this.fractions = [];
+		for (let i = 0; i < 1024; i++)
 		{
-			this.notes.push(new OPLLTone(Math.pow(2, (i - 69) / 12) * 440));
-		}
-	}
-	
-	toAsm()
-	{
-		return "\tdw " + this.notes.join(", ");
-	}
-}
-
-class PSGToneTable
-{
-	constructor()
-	{
-		this.notes = [];
-		for (let i = 0; i < 128; i++)
-		{
-			this.notes.push(new PSGTone(Math.pow(2, (i - 69) / 12) * 440));
-		}
-	}
-	
-	toAsm()
-	{
-		return "\tdw " + this.notes.join(", ");
-	}
-}
-
-class PSGNoiseTable
-{
-	constructor()
-	{
-		this.notes = [];
-		for (let i = 0; i < 128; i++)
-		{
-			this.notes.push(new PSGNoise(Math.pow(2, (i - 69) / 12 + 6) * 440));
+			this.fractions.push(Math.floor((pow2(i / 1024) - 1) * 65536));
 		}
 	}
 	
 	toAsm()
 	{
-		return "\tdb " + this.notes.join(", ");
-	}
-}
-
-class PSGEnvelopeTable
-{
-	constructor()
-	{
-		this.notes = [];
-		for (let i = 0; i < 128; i++)
-		{
-			this.notes.push(new PSGEnvelope(Math.pow(2, (i - 69) / 12) * 440));
-		}
-	}
-	
-	toAsm()
-	{
-		return "\tdw " + this.notes.join(", ");
-	}
-}
-
-class OPLLTone
-{
-	constructor(frequency)
-	{
-		this.frequency = frequency;
-		this.fnum = frequency * Math.pow(2, 18) / (3579545 / 72);
-		this.oct = 0;
-		while (this.fnum >= 511.5 && this.oct < 7)
-		{
-			this.fnum /= 2;
-			this.oct++;
-		}
-		this.fnum = Math.min(this.fnum, 511);
-	}
-
-	toString()
-	{
-		return this.oct << 9 | Math.round(this.fnum);
-	}
-}
-
-class PSGTone
-{
-	constructor(frequency)
-	{
-		this.frequency = frequency;
-		this.period = 3579545 / (16 * frequency);
-		this.period = Math.min(this.period, 4095);
-	}
-
-	toString()
-	{
-		return Math.round(this.period);
-	}
-}
-
-class PSGNoise
-{
-	constructor(frequency)
-	{
-		this.frequency = frequency;
-		this.period = 3579545 / (16 * frequency);
-		this.period = Math.min(this.period, 31);
-	}
-
-	toString()
-	{
-		return Math.round(this.period);
-	}
-}
-
-class PSGEnvelope
-{
-	constructor(frequency)
-	{
-		this.frequency = frequency;
-		this.period = 3579545 / (256 * frequency);
-		this.period = Math.min(this.period, 65535);
-	}
-
-	toString()
-	{
-		return Math.round(this.period);
+		return "\tdw " + this.fractions.join(", ");
 	}
 }