Learn Multi platform Z80 Assembly
Programming...With Vampires!
ChibiSound PRO / ChibiTracks / ChibiTracker PRO!
ChibiSound PRO, ChibiTracks,
ChibiTracker PRO - The Unholy Trinity!
With
the huge number of platforms and CPU's I cover, I had a bit of a
problem! In the past I had done simple sounds with my
multiplatform 'Chibisound' driver, and music with the AY based
'Arkostracker'... but while I had managed to write AY wrappers to
allow it to play on some extra Z80 systems, it was too complex for
me to realistically port to more platforms.
The
solution I came up with was to develop my own music software from
scratch!
ChibiSound PRO is the platform specific
sound driver, it handles making consistent pitched tones and noise
effects on the hardware.
ChibiTracks is the player, it works
currently on Z80, GBZ80, 6502 and 68000, however it is simple
enough to be ported to other systems, and such ports are likely!
ChibiTracker PRO is the windows based
tracker, it's designed for composing songs to play on the retro systems
ChibiTracks is designed for simple music and provides a multiplatform
solution to play a single compiled music binary file on multiple
platforms.
ChibiTracks is NOT designed to provide advanced features comparable to
solutions like ArkosTracker
To understand how ChibiTracks works, lets look at all the components:
ChibiSound PRO
ChibiSound is the sound driver that handles the particularities of a
system, there is typically one driver per system, though the CPC and MSX
drivers are essentially identical except for the AY register setting
routines.
The original 'ChibiSound' gave us one channel, one
Volume bit, six pitch bits, and the ability to turn noise on. Pitches
were not matched across systems, so sound 32 won't sound the same on all
systems.
The updated 'ChibiSound Pro' gives us all the channels
provided by the hardware, 8 volume bits, 16 pitch bits, and the ability
to turn noise on. Pitches were not matched across systems, however the
'ChibiOctave' lookup table provides values which ARE matched across all
systems.
ChibiSound PRO is essentially a reduced subset of AY functionality, and
was designed on the Z80 - it's 'PRO' suffix is a parody of the
'SoundBlaster PRO' - which could only do 8 bit sound so wasn't up to
professional standards! (neither is ChibiSound PRO)
ChibiSound PRO provides a standard interface to the underlying
hardware, it allows the following features to be set for each channel on
the underlying hardware:
Function
Register
Notes:
Channel Number (bit 0-6)
Noise On/Off (bit 7)
H
Multiple channels can be supported, but on single channel
systems only Channel 0 will be sure to play.
If possible Channel 0 will be a center channel, Channels 1+ may be
left/right
Noise bit turns the noise effect on (1) or off (0) - this can be
set on any channel, if the underlying hardware only supports one
noise channel, this will be resolved by the driver.
Volume
L
Set volume of the channel (0-255). Higher numbers are louder. O
is off
Pitch
DE
Set the pitch of the channel (0-65535). Higher numbers are
higher pitch.
Using DE does not standardize the resulting pitch - however a
'Lookup table' of notes 'ChibiOctave' provides a standardized way
of getting the correct DE value to get a pitch correct note on the
platform.
Chibisound PRO does not offer features like Envelope, LFE etc, as
providing consistent functionality across different platforms would not
be realistic.
Lesson
CT1
- ChibiTracks ASM Overview
Lets take a look at a ChibiTracks ASM source file - as saved by
ChibiTracker PRO!
These are used to load and save work in progress songs - and can
also be compiled by VASM into playable songs with ChibiTracks!
ChibiAkumasTheme.asm
ChibiTracks
ChibiTracks is the player for music files.
A Song contains multiple Channels (Currently tested
for 3)
A Channel takes it's commands from a Sequence of
Patterns
Patterns contains a set of lines. Each of the lines contain a sequence of one or more commands, and a delay to
wait until the next line.
Channels also play instruments (usually started by
the pattern). These are also a sequence of commands and delays, and are
used to set ChibiSound settings for the playing sound. A channel will
stop making a sound when an instrument ends (unless it loops)
It is important to note that functionally Patterns
and Instrument command lines are identical - they are
literally processed by the same code in ChibiTracks, however it would
not make sense to
ChibiTracker PRO can output two kinds of file - a binary file (.CBT)
which can be used with any player, or an ASM file - the default format
used to save and reload files (.ASM) - This is a VASM/MAXAM compatible
Z80 ASM file which contains all the binary data in DB/DW lines, and all
extra data for the player in rem statements (Starting with a semicolon).
There should be no difference between an compiled ASM and a binary CBT
file.
There are two optional functions which can be enabled (but make the
player larger)
ChibiTracks_AllowRelocation - This
allows a song to be played from a different address than it was built,
For example if a CBT binary was a save address of &4000, it can be
loaded and played from address &8000 provided this function is
enabled.
This function is mandatory on 68000, as only 16 bit addresses are
specified in the binary files.
ChibiTracks_AllowSpeedChange - This
allows the speed of songs to be changed via the 'SongSpeed' function -
allowing songs to be sped up or slowed down on playback at the cost of a
slightly slower and bigger player - Alternatively you can save with the
'PreMultiply Song Speed' option - in which case it's not needed.
ChibiTracks - A song file!
Lets take a look at the ASM file to see how ChibiTracks works.
At the start of the ASM file is the 'ChibiTracker' block
These lines all appear as comments in the ASM file, but are
functional lines to ChibiTracker PRO.
These define settings and data that are used by the editor, but
are not needed by the ChibiTracks player - because they are in REM
statements, they will not be contained in any built file.
Next is the binary header - this is used by ChibiTracks
player.... All 16 bit addresses are in Little Endian (even on
68000)
This contains the channel count (Should always
be 3 at this stage as the player does not support more)
Repeatpoint is the entry number in the sequence
which the song should restart when it reaches the end.
Songspeed is the multiplier for the line delay,
A delay of 2 with a song speed of 5 would wait 10x interrupts
before moving to the next line. NOTE: it is possible to assemble
the player without songspeed support (ChibiTracks_AllowSpeedChange
not defined). You can still get the same result by saving with
'Premultiplied songspeed' option enabled in ChibiTracker
PatternList is a pointer to the pattern list
table. This list appears at the end of the ASM file
InstrumentList is a pointer to the Instrument
list table. This list appears at the end of the ASM file
There then follows one 16 bit address for each channel, which
points to the sequence of patterns for that channel.
The Sequences define the pattern numbers that make up the
song. These are looked up in the PatternList table.
Patten 255 represents the end of the song, at which play will
resume at the repeatpoint.
Here are two sample patterns.
Each line in the ASM file is a line of the pattern. Lets take a
look at one!
db 2,&0D,&3A,&0B,&01,&00
;2
first is the length of the delay after
this line before the next line is processed. 2 means 2*Songspeed
ticks until the next line.
There then follows a sequence of commands
to be processed by ChibiTracks.
&0D means set pitch to note &3A
&0B means play instrument &01
&00 is the 'End of commands'
command, the player will wait until the delay finishes before
processing the next line
db 1,&10,&00
;33
Line 33 of the first pattern just has the 'End
of pattern' command &10 - This tells the player to
move to the next pattern in sequence - The delay has no effect in
this case
Instruments are in the exact same format, and use the exact same
decoding routine of the player.
However!... a instrument should end with a delay of 0, at which
point the instrument will stop sounding.
it is also possible to loop an instrument with a loop (Byte
&0C, followed by a negative byte), the byte value will be
added (subtracted) from the current position in the command
sequence, effectively looping part of the sequence.
To stop such a loop, a silent instrument, or other instrument
should be played on the channel to stop the looping one.
At the end of the song data is the PatternList and
InstrumentList,
These are a table of little endian 16 bit addresses of the pattern
and instrument command sequences.
ChibiTracks - Byte commands
There are two types of command in ChibiTracks.
One Byte commands have the command number in the high nibble, and the 4
bit parameter in the bottom nibble, For Example
&F1: &F_ = Shift
volume &_1= Volume up 1
Two Byte commands have a second byte parameter, their top nibble is
always Zero, For Example &0E,&FF: &0E =
Set Volume &FF= New Volume
Command Type
Byte Data
Notes
One Byte
&Fx
Vol shift -8 to +7
&Ex
Pitch shift down 0 to -15
&Dx
Pitch shift up 0 to +15
&Cx
Noise state x (1=on 0=off)
&10
End of Pattern
Two Byte
&0F,x
Pitch to x
&0E,x
Vol to x
&0D,x
Note to x (Even=true pitch Odd = Flat/Sharp)
&0C,x
Loop to Offset -0 to -255 bytes (eg &FF = back 1 byte)
&0B,x
Play Instrument x
&01,x &02,x
&03,x &04,x
Reserved for you to add custom functionality to the player!
(Named in ChibiTracker IXH IXL IYH IYL)
ChibiTracker PRO is a DotNet 3.5 application, it has also been complied
correctly in newer versions of DotNetYou can find the sourcecode for
Chibitracker in the Z80 sources.... see the folder "sources/C#
Tools/ChibiTracker"
There's a pre built EXE file in the sources/tools folder.
ChibiTracker supports a variety of MIDI controllers where available.
ChibiTracks
and ChibiTracker are far from the best music player and editor!
Their 'strength' is they are simple and designed to be easy to
port to multiple systems, and play the same song file on all
those systems.
If you're just looking at the AY sound chip, take a look at ArkosTracker,
which is better than what I can write!
Installing ChibiTracker PRO
If you do not have SlimDX installed, ChibiTracker will not run
correctly
On start up ChibiTracker automatically loads a set of sample
instruments from the "ChibiTracker_defaults.cfg" file
We can load the sample song from File-
Load Chibisound ASM
There is a sample song in folder "ChibiTracker_defaults.cfg"
"ChibiAkumasTheme.asm" is the ChibiAkumas theme song (Converted
from the ArkosTracker1 original)
Chibitracker saves and loads from ASM files - these music files
can also be assembled with VASM Z80 and played in your programs!
You can play the song with the play
button.
UI Overview
Here is the ChibiTracker UI
The main panel is the pattern editor.
above that is the Tab Strip - to
select other tools
At the top is the Playback buttons
for playing the song
At the top-left is the instrument list
at the middle-left is the sequence
list - this is the order of the patterns that make up
the song - each column in the list is a different channel.
at the bottom-left is the octave selection
- this changes
ChibiTracker tries to mimic ArkosTracker1 for keypresses and UI
layout.
This is the instrument list.
It's used to select an instrument to enter into the pattern when
editing.
It's also used to select the edited instrument in the instrument
editor.
This is the sequence list, it
defines the patterns played in the song.
Each column is a different channel, each row is a pattern to play
at that point.
You can change the selected pattern at a point by double clicking,
and selecting a new pattern from the list.
This is the Play controls and
Status.
The play controls allow for start, stop, looping and edit of the
pattern.
The Status line shows the byte data produced by the player, and
MIDI data received from the controller when connected.
This is the Pattern editor.
each column is a different channel.
When in 'Edit Mode' (purple background) the pattern can be edited.
Keyboard Keys:
Space - Toggle edit mode
letter / num keys - note keys.
Num0 - Num9 - Drum (when editing) / Select instrument 0-9
Backspace - clear line
Insert - Add line
Delete - Remove line
F1 - Restart Play
F2 - Resume Play
F3 - Pattern Loop
F4 - Stop
F8 - Octave down
F9 - Octave up
F9 - Set Volume
F10 - Set Pitch
This is the instrument editor...
it's rather basic at the moment!
A line of the instrument can be selected in the top list.
The Command / events for that line can be altered in the bottom
panel.
The Delay is the time this instrument commands occurs before the
next line
Each command can change the Pitch,Note,Volume and Noise - leave a
box blank to make no changes.
By changing the Pitch repeatedly (as shown here) we can make a
'wavy' sound.
Noise can be used for drum effects.
See the sample instruments for some examples.
The data in the top list is in the format:
Delay (byte count) Commands........
This is the settings page. It
allows various changes.
Song Settings define options of the edited song.
Editor settings change how the editor works, if
you're getting 'clicking' or poor performance, you can try
changing these.
Midi Controller Allows you to connect a Midi
Controller such as a piano type keyboard.
This is the pattern list.
You can double click on a pattern to rename it - This is optional,
and is just to allow you to organize your song more easilly
This is the Info Panel.
It shows some help info by default, and may show debug information
in some special cases.
This is the Midi Editor.
This only appears if you connect a midi device.
This allows you to map buttons and dials to ChibiTracker events.
Of course A keyboard can also be used to set notes in the pattern!
UI Overview
Lesson
CT2
- ChibiTracks Header Load and Init
Lets start to look at the Z80 ChibiTracks music player code!
Multiplatform_ChibiTracks.asm
State Memory, symbols, and the silent instrument
ChibiTracks uses a 16 byte
block to keep track of each of the channels (16*3=48 bytes total)
These contain the values to be passed to the ChibiSound PRO
driver,
There are address pointers for the playing Instrument Line and
Pattern Line, as well as 'Timeouts' after which the next line must
be processed.
There is also an address for the sequence position - this has no
timeout, as the next sequence is loaded when the playing pattern
ends.
ChibiTracks will zero and initialize the memory it is provided for
its channel data. Instrument/Pattern/Sequence data are loaded from
the playing song.
There are EQU definitions which are used as offsets to the
corresponding data for a channel
Some Extra EQU definitions point to the start of the channels,
and the end of the data block
When a channel ends it must be switched to a 'silent instrument'
- this is an instrument script which only sets the channel volume
to zero
ChibiTracks
is designed to work from ROM, so the variables for channels and
other core purposes can be loaded in a small block of RAM.
In addition to the 48 bytes here there are aso : SongOffset
(word),SongBase (word), Instrumentlist (word), Patternlist
(word), SongChannels (byte),SongSpeed (byte) and RepPoint
(byte).
So ChibiTracks needs less than 64 bytes RAM total!
ChibiSound PRO may need a few more though - depending on the
platform!
Common functions
A song has a built in 'Load address' where it's expected to be
in memory, however it's possible to play it from any address if
the 'ChibiTracks_AllowRelocation' option is enabled.
There is a shared sub to add this offset to DE, though there are
many other such routines contained in the code where other
registers are used
The tracker updates patterns after a certain 'time' has passed.
This time may need to speed up and slow down depending on how fast
the end system is.
enabling 'ChibiTracks_AllowSpeedChange' will calculate this speed
change at play time.
Alternately, enable 'PreMultiply SongSpeed' during export - and
this calculation will be done in advance and
'ChibiTracks_AllowSpeedChange' is not needed - saving memory and
CPU power, but losing the ability to change song speed later.
Song INIT
We run StartSong to init the music.
RAM variable SongBase should point to the music
data.
Before we start playing anything, we need to clear the contents of
the ChibiTracks RAM
We can zero pretty much everything - but we need to program each
channel with its number.
The first time we play as song, we start fron sequence entry 0
(we may loop back to an alternate position)
We first load in the song channel count and the repeat point
If we use speed changes, we load that too.
If we are allowing relocation we calculate the difference between
the address we're playing from, and the address the song was
exported to play from - the result is out offset.
We load in the pattern list address
Finally we load in the instrument list address
The next three addresses are the song sequences.
we use these to Initialize each of the channel patterns
We use any offset in A (for looping back) as an offset, and load
in the first pattern in the channels sequence.
We use GetNextSequence to read in a pattern number and get the
channel to play it.
Pattern=255 is the end of the song - in which case we force a
restart.
Otherwise we calculate the address of the pattern, and load it in
to the channel pattern address.
To restart the song we clear down the stack, load in the repeat
pos (sequence offset) and reload the song.
Lesson
CT3
- The main play routine
Lets look at the main player code of ChibiTracks
Multiplatform_ChibiTracks.asm
The Update routine
We run ChibiTracks_Play during our
interrupt routine.
This uses AF,HL,BC,DE and IX only - IY and shadow registers are
not used
We update all the channels separately. First we decrease the delay
time for the current pattern line... if it reached zero we need to
process a new line of the pattern.
Next we update the channel itself (for any current playing
instrument. If the delay time for the current instrument IS zero -
there is no instrument playing any more... if it REACHED zero, the
we need to process a new line of the instrument.
We repeat for all the other channels, and run the ChibiSoundPro_Update
routine. this is only used on systems like the ZX
Spectrum Beeper, which needs positive action by the cpu to
generate the sound
To update the pattern we read in the
address of the next line, we load in the Delay (the time the next
line should play) and multiply it by the playing speed.
We process the commands for this pattern line, and store the new
pattern address.
To update the channel we read in the
address of the next line, we load in the Delay (the time the next
line should play) - if it's zero the instrument has stopped and we
need to silence the channel
We process the commands for this instrument line, and store the
new instrument address.
We need to update the sound the channel is playing.
We use TweenTone to calculate the DE pair for
the note frequency from the ChibiOctave lookup table
We load H and L from the channels settungs and use
ChibiSoundPro_Set to update the sound the channel is playing
The Script processor
There are two types of
commands.
One byte commands use the top nibble as the command number, and
the bottom nibble as the parameter
Two byte commands use the first byte as the command number (The
top nibble is always zero) , and the following byte as the
parameter
A Zero byte &00 is a special case - and represents the end of
the script.
We can use a one nibble pitch bend for small adjustments (slight
wavy notes).
There is a 1 byte version as well for larger shifts.
A -8 to +7 volume shift can also be used.
The noise command is a bit of an odd case!
The only valid options are &C0 or &C1, as only the bottom
bit of the parameter is used.
It either turns noise on (&C1) or off (&C0)
The two byte command routine loads the second byte into the C
register
We have commands to set the playing note (in the octave) volume
and pitch bend
These are just loaded into the channel data for processing by the
channel code.
The loop command uses the parameter as a negative byte offset
within the script - to work correctly it should point to a command
byte within the script
It's used for repeating instruments - for example a wavy sound
with a looping pitch bend.
If an instrument is to be triggered, the instrument's address is
loaded from the Instrument List.
The Delay is set to 1, so it will reach 0 next tick and force the
first line of the instrument to play.
Lesson
CT4
- Tone Calculation
The Note number and pitch shift need to be used to calculate a 16
bit DE value for ChibiSound Pro
Lets learn how!
Multiplatform_ChibiTracks.asm
Tone Lookup
ChibiSound PRO is the platform specific driver for the sound on
each system.
There is a matching ChibiOctave table which allows the lookup of
the correct frequency for notes.
It only contains the pure tones, not sharps or flats, however
these can be calculated by adding two notes and dividing by two to
calculate the in-between value.
GetChibiTone will lookup the note E from the
lookup table.
The format is %NNNNNNNS - where N is a Note, and S 'sharpens' the
note.
TweenTone takes a note in E, and a shift in A
The top nibble in A is a direct shift to the note number in E,
The bottom nibble in A is a 0-15 'fractional shift' to the note
number in E, allowing a smooth transition between note E and E+1
0-15 fractional Tweener
Tween16HLDE calculates
a fractional position between HL and DE. it returns a result in HL
A should be a value of 0-15, any value over 16 is treated as 16
If A=0 then the result is HL=100% of the value in HL
If A=16 then the result is HL=100% of the value in DE
If A=8 then the result is HL is halfway between HL and DE
and so on
Fraction16 performs the calculation HL=HL * (A/16)
We move our value into DE, and repeatedly divide it by
two, testing the matching bit in A to see if we need to add it to
HL to create the correct resulting fraction.
This gives a quick calculated 1/16 - 15/16 fraction of HL