Topic: [MZ-700] IN-GAME 1-bit PWM
Hi,
I'm the owner of an MZ-700 and coding some games on it.
First, let me introduce to its sound hardware:
- Loudpseaker 8Ohm/1W
- Notes are usually played through a square signal from 16Hz to 1.108404688MHz using i8253 counter 0 in mode 3.
PWM can be done by alternating mode 0 and mode 4 without setting counter 0.
In fact, we can also use mode 0 with setting count 0 to create a one-shot square signal with a variable duty: intersective PWM.
To be able to output sound in-game, I use an interrupt. Stock MZ-700 mostly uses /INT to toggle AM.PM flag and to readjust time through counters 1 and 2. Since my games are standalone, I usually shut down this interrupt to allow "fast" screen drawing using stack trick (POP/PUSH) because you can access VRAM only when horizontal blanking is on.
So I need another trick to be able use interrupts without disturbing the drawing code. It happens the clock of counter 1 is the same signal which freezes CPU when attempting to access VRAM when BLNK is 0. Interrupt is raised at falling edge of BLNK. So I use HALT instruction to wait for an ISR to toggle the sound output or not then transfer N VRAM blocks through stack then restore a valid stack and do that for 25 lines (in total, we have 2 x 28 x 25 characters/attributes to draw in 100 horizontal blanks - 312 for a frame). I won't dig in the details so let say what is possible:
- for a full 50 FPS, I need an ISR every 4 horizontal blanks to avoid screen corruption. PWM rate is 3902.8314Hz.
- for a full 25 FPS, I need an ISR every 3 horizontal blanks to avoid screen corruption. PWM rate is 5203.7752Hz.
I used IM2 a la Spectrum to set my ISR and here is the ISR:
sfx_isr:
PUSH HL
PUSH AF
if DEBUG
LD HL,isr_count
INC (HL)
endif
LD HL,$E006 ; rearm counter to get the next ISR
LD (HL),PIT_CNT2
LD A,(sfx_buffer) ; get $20 or $28 to toggle the output sound (PWM 0 or 100%)
sfx_isr_buffer_index equ $-2
INC L
LD (HL),A
LD HL,sfx_isr_buffer_index ; advance in buffer (256-byte ring page)
sfx_isr_on_off:
NOP ; opcodes $34 (INC [HL]) for ON or $00 (NOP) for OFF
POP AF
POP HL
EI
RET
$20 is mode 0 (final sound output is 0) and $28 is mode 4 (final sound output is 1).
I then have a code to run in the game loop to fill the ring page on the fly from the data generated by pcm2pwm v1.
For 50 FPS, I need 78 bytes in the ring page to play per frame.
For 25 FPS, I need 104 bytes in the ring page to play per frame.