I am currently experimenting with an approach to create a volume effect. Instead of emitting a high or low signal with 100% duty every ISR, I have introduced (partially for the moment) the notion of 4-bit PCM instead of 1-bit PCM. Well, I realized that the mode 0 allowed me to create a low and then a high signal with a "programmable" duty. Indeed, as soon as the counter reaches the threshold, it switches to the high signal and stays there until the counter is reprogrammed.
So I thought I could have fun creating a 4-bit PCM with duties like this : 0/15, 1/15, 2/15, ..., 14/15, 15/15. Indeed with 1/15, the volume seems much lower.
Now the technique (and why only 4-bit PCM):
Instead of a 256-byte page containing the mode programming bytes of counter 0 (used for the production of its square to 1.108404688 MHz), I determine two contiguous tables. The first one continues to play the same role but indirectly because it contains indexes on the second page which contains code every 16 bytes to produce a square signal with a duty related to the index of the period of an ISR (one every 3.902825 KHz)
Here is the definition of the two pages:
align 256
sfx_buffer:
DS 256,$00
sfx_play_pwm_0:
LD HL,$E007 ; duty 0% !
LD (HL),$28
JP sfx_isr_ret
align 16
sfx_play_pwm_1_15:
LD HL,$E007 ; 16-bit counter, duty 6,6...%
LD (HL),$30
LD L,$04
LD (HL),SFX_DUTY_1_15>>0
LD (HL),SFX_DUTY_1_15>>8
JP sfx_isr_ret
align 16
...
sfx_play_pwm_14_15:
LD HL,$E007 ; 16-bit counter, duty 93,3...%
LD (HL),$30
LD L,$04
LD (HL),SFX_DUTY_14_15>>0
LD (HL),SFX_DUTY_14_15>>8
JP sfx_isr_ret
align 16
sfx_play_pwm_15:
LD HL,$E007 ; duty 100% !
LD (HL),$20
JP sfx_isr_ret
I took 4-bit index because I only have 2 bytes left for the 1/15 to 14/15. By switching to 5-bit, I wouldn't be able to fit all the duties in one page of code.
Now I have to adapt the ISR :
sfx_isr: PUSH HL
PUSH AF
LD HL,$E006 ; rearm counter to get the next ISR
LD (HL),PIT_CNT2
LD HL,sfx_buffer ; current index stored in the first page
sfx_isr_buffer_index equ $-2
LD L,(HL) ; read a byte $i0 where i is the index
INC H ; go to the next page
JP (HL) ; jump to the code emitting a square signal with the right duty
sfx_isr_ret:
LD HL,sfx_isr_buffer_index ; next index for the next ISR
sfx_isr_on_off:
NOP ; either $34 (INC [HL]) when playing is ON or $00 (NOP) if OFF
POP AF
POP HL
EI
RET
The ISR initially had 114 execution cycles. Here we have 115 cycles to which we must add either 30 cycles or 57 cycles depending on the desired duty: 145 or 172 cycles. The game always seems to run at 50 fps.
The reason I wanted to do this is twofold:
- to see if by this set of duty, we could create a volume effect and thus have the possibility to mix several sources.
- to test the possibility to make "intersective" PWM : https://en.wikipedia.org/wiki/Pulse-width_modulation.