Videopac G7000 BIOS
Last changed: 2004-08-31

Custom sound player

Here in this chapter I explain routines for a VSYNC interrupt driven custom sound player. This means that you can create your own sound effects that are still playable with playsound. It is possible to keep or to replace the built-in tunes. The piece of code shown here is used in several variants by a lot of the commercial games. The one I explain here supports loading the sound shift registers with any value which is a feature that is only used by a few of the commercial games. Most game leave out the code handling it. The demo tune included into the full example does not use it, I explain it just to be complete.

The sound hardware

The sound hardware is just a 24 bit shift register with one end connected to the sound output. This 24 bit register is split into the 3 separate byte wide registers vdc_sound0/1/2. The sound is controlled by the vdc_soundctrl register. The highest bit switches the sound output on or off. The next bit enables a loop mode, the sound output will be rotated back into the other end of the shift register when this bit is set. Otherwise the sound will stop after all 24 bits are sent. Bit 5 is used to select between two different shift frequencies. Another source of sound is a noise generator enabled with bit 4. The volume of the sound output is controlled by the last 4 bits inside the vdc_soundctrl register.

The number of sounds the hardware can create is very limited and creating a tone with a certain frequency is beyond the scope of the BIOS code. It is possible to generate a certain frequency by modulating a noise signal: Generate an exact timing with the timer and a software timing loop and toggle the sound enable bit, this is used by Videopac 31 (Musician). But here I explain tunes that the BIOS routines can generate.

The BIOS tune player

The tunes played by the BIOS use commands interpreted by the BIOS tune player as long as bit 6 of iram_irqctrl is set. The pointer to the current tune command is set by playsound. This pointer points into bank 3, so the tune for the "SELECT GAME" sound starts at 034Ah. The custom tune player just gets the data from another bank. It is obviously possible to overwrite the built-in tunes as desired, but this example keeps them intact. Every VSYNC interrupt the tune player is called, but most of the commands have a duration. Only after this duration is over a new command is loaded by a jump to 040Ah, which normally jumps to soundirq.

The tune player opcodes

There are 4 commands supported by the BIOS. Three of the commands have a parameter. The commands are differentiated by the highest bit that is set. The custom tune player here supports another sound opcode to remove a limit by the BIOS routines that is only relevant when creating custom tunes.

Play tone

If bit 7 of the command byte is set a new tone starts. The duration of this command is the command byte with bit 7 cleared. The parameter byte is a pointer to the new contents for the shift registers and vdc_soundctrl. They are always read from bank 3. There are 10 different tones from 0300h-0327h, but the BIOS tunes only use 8 of them. If you want more than those 10 tones you have to create your own opcode as shown later.

Load control

If bit 7 of the command byte is 0 and bit 6 is set the control register is loaded. The duration is the command byte with bit 6 cleared. The parameter byte is the new value for vdc_soundctrl. This command is used to change the sound volume and to play sound with noise.

Silence

If the bits 7 and 6 are 0 and bit 5 is set the sound is turned off. The duration is the command byte with bit 5 cleared. This command does not have a parameter byte.

Jump

If the bits 7, 6 and 5 are 0 and bit 4 is set the current tune position is set to the parameter byte and the tune player is restarted. It is possible to create endless sounds with this opcode. To play another sound just call playsound again.

Stop sound

If the BIOS does not recognise a command it turns the sound off and stops playing. The BIOS tunes use 0 for this.

Opcode 00Fh

If this custom tune player reads a command byte 00Fh it turns off any sound and copies the next 3 bytes into the sound shift register. After that the tune player is restarted, the next command executes immediately. This command should load vdc_soundctrl, because the sound is still turned off from loading the shift register.

The program

As usual I only show the relevant parts of the program, the full program can be downloaded as usual.

The IRQ vectors

To create custom sound tunes the first part of the BIOS sound IRQ has to be replaced, the place to do this is at 040Ah:


        jmp     selectgame      ; RESET
        jmp     irq             ; interrupt
        jmp     timer           ; timer
        jmp     vsyncirq        ; VSYNC-interrupt
        jmp     start           ; after selectgame
        jmp     mysoundirq      ; sound-interrupt

Feeding the BIOS player with new data

The 8048 is still in IRQ mode, so SEL MB1 is not possible and the BIOS has switched to RB0. First we have to see if the tune currently played is one of the built-in tunes. Here I leave all built-in tunes intact, the new ones start at 076h. The current tune position is delivered in R4, if it is smaller than 076h the old BIOS routine is called.


mysoundirq
        ; check if BIOS sound or custom sound
        mov     a,r4
        add     a,#08ah         ; >= 076h
        jc      .custom
        jmp     soundirq        ; BIOS tune

Now I just read a byte as command byte and test for the new 00Fh opcode. If it is not the new opcode I read another byte as parameter byte. The rest is handled by the BIOS in parsesnd.


.custom ; custom sound handler, read sound opcodes from current page
        mov     a,r4
        movp    a,@a
        mov     r1,a            ; command byte
        inc     r4
        xrl     a,#0fh
        jz      .op0f           ; test for new opcode
        mov     a,r4
        movp    a,@a
        mov     r2,a            ; parameter byte
        jmp     parsesnd        ; let BIOS sound IRQ handle opcode

There is no BIOS routine for the new opcode 00Fh, so I have to do it all by hand. First the sound gets turned off, because I change the sound shift register. Then the next 3 bytes are copied into the shift register and the sound IRQ handler is started again to execute the next sound opcode.


        ; opcode 0F: sound off, copy next 3 bytes into A7/8/9
.op0f   mov     r0,#vdc_soundctrl
        clr     a
        movx    @r0,a           ; old sound off
        mov     r0,#vdc_sound0
        mov     r1,#3           ; number of bytes to copy
.loop   mov     a,r4
        movp    a,@a
        movx    @r0,a
        inc     r0
        inc     r4
        djnz    r1,.loop
        jmp     mysoundirq      ; restart sound handler