G.O.Bach's BDKformat revised 1989.10.02

by F. G. Kostella

G.O.Bach is copyright by RUN/IDG

The following is an description of the formats used by G.O.Bach and may be useful to programmers wishing to write their own players or attach a G.O.Bach song to their own programs.

My terminology:

VOICE One of any SID chip's 3 oscillators. Named Voice 1, Voice 2, and Voice 3.

PATCH The grouping of values placed into one of the 3 voices set of registers that produce an audible sound, ie., waveform, pulse width, ADSR. Typically given a name like: "flute", "percussion", "sax", etc. A sound/timbre. I've adopted this name to avoid confusion with VOICE.

SONG FORMAT:

The song will be stored as a single record of a VLIR file, more on that later. The song should start with a header of .word values that are offsets from the start of this table:

offset to MONO SID STEREO SID*

.word SPECIAL_STRINGS.

word NOTE_TABLE Voice 1 Left Voice 1

.word NOTE_TABLE Voice 2 Left Voice 2

.word NOTE_TABLE Voice 3 Left Voice 3

.word NOTE_TABLE n/a Right Voice 1 *

.word NOTE_TABLE n/a Right Voice 2 *

.word NOTE_TABLE n/a Right Voice 3 *

.word PSEUDO_OSC Pseudo 1 Pseudo 1

.word PSEUDO_OSC Pseudo 2 Pseudo 2

.word PSEUDO_OSC Pseudo 3 Pseudo 3 *

(*=not implemented this version)

; now a description of the data/code pointed to by the above.

SPECIAL_STRINGS

An $0000 indicates that this funtion should be ignored. I intend to use this primarily to print to the screen the song title, author, etc. Will have it pass the caller a pointer to 4 strings of 80 characters (or less) that will contain only the ASCII alpha numerics . The caller may handle word wrap and other screen formatting if needed.

The editor allocates 80*4 bytes in memory for these strings and saves them along with the rest of record 1. The 2nd, 3rd and 4th strings are located by adding 80 to the table offset value.

NOTE_TABLE

Table of commands for one voice. Generally specifies the pitch and duration of a note. Here is a breakdown of the data organization:

BIT VALUES (X=don't care)

7 6 5 4 3 2 1 0

0 X X X X X X X $00-$7F Note pitch, index into pitch table(s).

Followed by a byte length duration value, ie., the

number of interrupts * 2 to play this note.

1 X X X X X X X Control code, list follows:

1 1 1 1 1 1 1 1 $FF Kill voice, song ends for this voice.

1 1 1 1 1 1 1 0 $FE Voice rest. Turn off the voice's gate for

the following byte value duration, the same value as a played

note would use. Actually, the player code should not

alter any sid registers; when a note times out, the player

will clear the gate bit.

1 1 1 1 1 1 0 1 $FD Change "patch". More on patches later, but

they are loaded separately from the SONG. The player

will know it's address. This byte is followed by a single

byte used to index into the table of patches. (0-23)

1 1 1 1 1 1 0 0 $FC Repeat Sign. The beginning of a section to

be repeated. Followed by a one byte value indicating the

number of times to repeat. If 0 then no repeat is called.

1 1 1 1 1 0 1 1 $FB "Two Dots", end of the repeat section, ie.,

go to the start of the repeat section (and decrement the

repeat counter).

1 1 1 1 1 0 1 0 $FA Kill Song. Not implemented in Editor. A Player

should keep track of the active voices and shut itself down

when all three voices have been stopped by the $FF Kill Voice

command.

ä We may need to add D.S., D.C., etc. to maintain compatability with other

formats-!!?? If so, they will occupy the next few bytesü

All other values:

1 A B C X X X X $80-FA Future/reserved.

PSEUDO_INIT

As usual, an $0000 indicates that this function should be ignored.

This points to a PSEUDO_OSCILLATOR_STRUCTURE:

.byte SCALING ; Process (decrement index into table and apply

; table value to register) the pseudo osc. every

; time this value counts down to zero

..byte TABLE_SIZE ; Length of the following table (1-64).

.byte TABLE ; Table of 1 to 64 bytes ranging in value

; from 0-63.

Roughly, the pseudo oscillator is attached to the end of the Player interrupt code and works like this: A counter is loaded with the SCALING value upon initialization, every pass through the routine decrements this counter, when it reaches ZERO, it is reloaded with the SCALING value and the PSEUDO code is called. This code first decrements a TABLE_INDEX (set to TABLE_SIZE upon initialization), if it reaches zero it is reloaded with TABLE_SIZE. The PSEUDO code then pulls the value from the table and applies it to a SID register value. In my editor, there are a number of options, but most of them work by buffering the current value of a SID register and adding this table value to it before writing it to the register again. Please note: this table is read from last entry to first. (cuts down on interrupt processing.)

PATCHES

Here is Wayne's method of saving patches, to which I've added one byte for the Pseudo controls

.word pulse width

.byte control byte for voice ;BIT 0 is always set! BIT 3 always clear.

.byte attack/decay

.byte sustain/release

.byte filter cutoff frequency / HB

.byte filter cutoff frequency / LB lower 3 bits

.byte Resonance / Control filter

; NOTE: The three filter ON bits will always be SET by the editor if the filter is used by the voice using this patch. It is up to the Player to determine which voice's bit to set. If a voice is not using the filter, it should not copy the filter cutoff frequency bytes or the resonance bytes or the filter type/mode bits to the actual SID registers.

.byte ; Filter Mode / Volume ;V3cut (bit 7) is NOT used by the editor, leave clear. When copying a new volume into this register, if the filter mode (or filter for current voice) is not changing, then the filter mode must not be altered! Be sure to buffer the last filter mode..

.byte PSEUDO CONTROL, detail:

BIT PSEUDO # modulates (when bit set)

7 1 frequency high

6 1 frequency low

5 1 pulse width

4 1 filter freq.

3 2 frequency high

2 2 frequency low

1 2 pulse width

0 2 filter freq.