|Lesson P1 - Basic Firmware Text functions|
|Lesson P2 - More Text Functions, Improvements... and the Sam Coupe!|
|Lesson P3 - Bitmap graphics on the Amstrad CPC and Enterprise 128|
|Lesson P4 - Bitmap graphics on the ZX Spectrum and Sam Coupe|
|Lesson P5 - Bitmap graphics on the TI-83 and MSX|
|Lesson P6 - Keyreading on the Amstrad CPC, ZX Spectrum and Sam Coupe|
|Lesson P7 - Keyreading on the MSX, Enterprise and TI-83|
|Lesson P8 - Tilemap graphics on the Sega Master System & Game Gear|
|Lesson P9 - Tilemap graphics on the Gameboy and Gameboy Color|
|Lesson P10 - Tilemap graphics on the MSX1|
|Lesson P11 - Tilemap graphics on the MSX2|
|Lesson P12 - Joypad reading on Master System,GameGear, Gameboy and Gameboy Color|
|Lesson P13 - Palette definitions on the Amstrad CPC and CPC+|
|Lesson P14 - Palette definitions on the Enterprise and Sam Coupe|
|Lesson P15 - Palette definitions on the MSX2 and V9990|
|Lesson P16 - Palette definitions on the Sega Master System and Game Gear|
|Lesson P17 - Palette definitions on the Gameboy and Gameboy Color|
|Lesson P18 - Making Sound with the AY on the Amstrad CPC, MSX,ZX Spectrum.... and NeoGeo + Atari ST!!|
|Lesson P19 - Sound on the Elan Enterprise|
|Lesson P20 - Sound on the Sam Coupe|
|Lesson P21 - Sound on the Gameboy and GBC|
|Lesson P22 - Sound with the SN76489 on the Master System, GameGear, Megadrive (Genesis) and BBC Micro!|
|Lesson P23 - Sound with the 'Beeper' on the ZX Spectrum and Apple II|
|Lesson P24 - Bankswitching and hardware detection on the Amstrad CPC|
|Lesson P25 - Bankswitching and hardware detection on the MSX|
|Lesson P26 - Bankswitching and hardware detection on the ZX Spectrum|
- Hardware Sprites on the Master System / Game Gear and MSX1!
SMS /GG sprites are much easier, so we'll take a look at them, and also look at the MSX1 as a bonus!
|Hardware Sprites on the SMS / GG|
|The SMS/GG memory
supports up to 64 sprites,
There are 3 memory bytes for each hardware sprite, but they are not consecutive!
Look at sprite 0 - shown in black... it's X co-ordinate is at &3F00 ... it's Y co-ordinate is at &3F80... it's Sprite Number is at &3F81
Sprite Numbers can come from memory address &2000, or &0000 (The Tile Patterns) depending on the setting of register &06... we'll be using the Tile patterns for simplicity in our example!
It's important to note that the sprites ALWAYS use colors 16-31
Unfortunately there is no way to flip sprites, so we will need a pair of sprites to allow a player sprite to turn around!
Sprite Attribute Table (In VRAM)
|The Sprite Attribute Table is
held in VRAM so we have to access it using OUT commands, we'll use our
PrepareVram command to set the correct memory address..
When we want to set a sprite, we first set the Y co-ordinate in the first 63 bytes of the table, then we set the X pos and the Tile number
|We make up our 16x16 sprite using 4 separate sprites
SMS sprites can be read from pattern data, or from a seperate bank...
in the same ways, the Tilemap can use the same colors as the sprites,
or a separate palette...
These examples will use the simplest option - sharing both between sprites and tilemap - of course, if you're creating a big game, you'll want to separate them to make your game look the best!
|Hardware Sprites on the MSX|
On the MSX the system is capable of 32 sprites onscreen... but just 4 can be on the same line... The sprites are 2 color.
The sprites patterns can be 8x8 or 16x16 and can be 'doublesize' 2x scaled , but this is declared at the screen level in register 0... so you cannot have some sprites 8x8 regular, and others 16x16 doublesize
Each Sprite is defined by 4 concecutive bytes in the sprite attribute table (usually at &1B00) containing a Y and X co-ordinate, a Pattern number (corresponding to a tile in the sprite data at &3800)
in 16x16 mode patterns will be 4x size, and the tilenumber will only use the top 6 bits %111111-- ... the bottom two will be ignored
when X,Y= 0,0 the sprite will be in the top corner of the drawable area, but will be completely visible.
if you want to clip The Y lines of the sprite, use Y positions of 255 or less - this is effectively -1 or lower
Because the screen is 256 pixels wide, we cannot do this for X Co-ordinates - instead we use the top bit of the 'Color' attribute of each sprite (only the bottom nibble defines color)... When the top bit is 1 - the sprite will be shifted 32 pixels left, so 0,0 will now be -32,0
the order of the 8x8 chunks in 16x16 sprite mode is possibly a little odd...imagine 4 8x8 blocks in a 16x16 sprite... they need to be in the following order:
|We need to actually turn on
sprites, by setting SPD to 0 in Register 8... it's important to notice
that this will make the MSX graphics slightly slower
Register 1 controls the size and scale of the sprites, so can be used for 16x16 or doublesize sprites.
|To set the MSX sprite
parameters, we just need to set our write address to the memory
relating to the sprite we want to change.
Then we just OUT the 4 bytes of data to that address... the address increments automatically with each write.
|We can make up a 16x16 sprite using 4 8x8 sprites
we can also use a single sprite - and doublesize for a bigger sprite!
MSX 2 is capable of much more powerful sprites, and sprites can be
bit-combined to allow a lot of colours! - and that's just hardware
sprites, but hardware sprites slow down the hardware by about 20% - and
are still quite limited
Chibiakumas uses software sprites using 'area copying' VDP commands - meaning no limit to the number of sprites onscreen - just a reduction in speed!
- Hardware Sprites on the CPC+
The CPC Plus is capable of better palettes, but it's prime feature is sixteen 16 color 16x16 sprites... These are separate from screen mode, so are 16 color even in 4 color mode 1...
Lets take a look!
|Like most CPC+ features, Sprites are set and altered by writing to ASIC ram after turning on Plus features,
We've discussed how to do this before in the lesson on CPC+ Palettes
Each sprite is 16x16 making 128 bytes... but the definitions only use the bottom nibble of each address (The top nibble of the sprite data registers cannot store data!), so each sprite is defined by 256 bytes of bitmap data.
as well as bitmap data, there is an address storing the X and Y co-ordinate - and a 4 bit 'Magnification', 2 width bits and 2 height bits
A magnification of 0 is a disabled sprite, a magnification of 1x1 is a 'Mode 2' sized pixel sprite... 2x1 is 'Mode 1'... and so on
So 'Resolution' setting of 9 will be a normal Mode 1 sized sprite
CPC+ sprites are always 16 color, and use an 'extra' 16 colors to the normal palette... these palette definitions are held in the range &6422-&643F
|If you don't know how to turn on PLUS features, see the CPC+ Palette lesson of these tutorials....
Also, just remember the ASIC ram uses the area &4000-&7FFF , so your program code and sprite data have to be somewhere else!
|Before we can do anything with a sprite, we need to set it's bitmap data!
We need to write 256 nibbles to 256 addresses in ASIC ram to set the sprite bitmap... but that's only 128 bytes of data!
To save RAM, we'll 'Compress' two nibbles into the same byte, and unwrap them when we write to the ASIC ram...
We'll also 'misuse' the stack pointer to PUSH data into the asic ram more quickly to save time... unfortunately it's still quite slow, so may mess up interrupts, so we'll do the job in 4 chunks so interrupts can still run frequently
This is the code used by ChibiAkumas to handle CPC+ Sprites!... This is why the bitmap copy is done in 4 parts - as Interrupts had to be handled quickly to keep the 'raster color switching' reliable.
Because we're using PUSH commands, we going backwards, so we need to pass the LAST BYTE of the sprite data in HL
Sprites have a LOT of data, and we can't just change a pointer to
change the 'image' of a sprite... This means it may be slow to animate
them - at least compared to other systems with hardware sprites...
That said, the CPC+ sprites are big and colorful compared to other 8 bit systems!
|Now our sprite has an image, we need to position it on the screen, and give it a scale!
If we want our Sprite to be sized with mode 1 pixels, we need to set it's scale to '9'
To allow a single byte to define an X position, the X co-ordinate is defined in Mode 0 pixels, however you can change this if you want better definition..
Negative numbers can be used to clip a sprite to have it go off the Top/Left of the screen
It is not possible to have a sprite in the border areas of the screen - the sprites are drawn in the normal bitmap area of the screen.
|Now we have our routines to do the work, we need to use them!
To start we need to turn on the plus features...
Then we need to load in the bitmap data - we add 127 to the source address, because our code works BACKWARDS from the last byte of the sprite...
Now we set a position and scale of our sprite - remember the scale cannot be 0 or it will be disabled.
Finally, we set the color, the sample sprite only uses color 3, so that's all we'll set!
|The sprite will be drawn to the screen!|
more than 16 sprites? Well, it's possible if you're REALLY clever - you
can reposition the sprites while the screen is drawing, and you will
effectively see them twice... in ChibiAkumas 2 player mode the
Heart+Scroll sprites were repositioned midscreen - so all 12 sprites
were actually just 6!
Unfortunately, it's so much work to change the bitmap data, it's not possible to use a different sprite bitmap with this method, so you'll have to be clever in your sprite use!
- Bitmap Graphics on the Camputers Lynx
The Camputers Lynx has a very unusual screen layout, it's screen memory conflicts with the normal memory, the effect of this is we cannot use the stack during the writing procedure when we have the video ram paged in.
Lets take a look at how to get graphics onscreen on the Lynx!
|8k ram bank||Seen at Addresses|
|4||Off Board Expansion|
Bank 2 / 3
|Write Green /
|Write Red /
|Write Green /
|We can't use the stack during
drawing, so we'll define some macros to easily turn on the banks to
correctly write to the banks we want to.
This involves paging in the correct banks using port &FFFF, and locking the banks we don't actually want to write to using port &0080
however, if we don't lock either of the vram banks , we can write to All Colors more quickly, and we'll use this for our font routine!
|When it comes to printing characters to the screen we can take advantage of a quirk of the Lynx memory map.
We can Write to Red+Green at the same time! this means we can do two writes to &C000 and &A000 and set all 3 channels - which will save time
Because the screen memory overlaps normal memory where the stack is, we can't use the stack between the ScreenDrawAllColors and ScreenStopDrawing
|Bear in mind that while this writes to RED, GREEN and BLUE - it also writes to ALTGREEN
Altgreen isn't used by the visible screen in this example, but depending on how you're using the screen and memory, that could be a problem for you?!
|We need a way to get the correct memory location for writing data to the screen... we'll pass an X,Y co-ordinate in BC...
Because it's spit into bitplanes, The Camputers screen has 8 pixels per byte, and the screen is 32 bytes wide...
Therefore we need to multiply the Ypos *32
|When we want to show a bitmap to
the screen, we should first use the GetScreenPos to get the correct
screen destination, and set HL to the source destination of the bitmap.
We need to write to the screen 3 times, so we back up the destination pos into the IY register - we can't use the stack while we're drawing to the screen.
We use our macros to page in the graphics ram, and the LDIR command to actually do the data copy, BLUE and RED are in the same bank, we need to add &2000 to the memory address we're writing to so we can write to the RED area
once we've done the RED and BLUE channels, we page in the GREEN and do the same.
finally we repeat for the next line of the bitmap
|When we want to make use of this
function, we need to get the screen position into DE, set the bitmap
data source in HL, set the width in IXH, and the height in lines in IXL
My AkuSprite editor can export a bitmap in the correct layout for this example and the camputers lynx
is just an example of the kind of code you can use - depending on your
exact requirements, you should modify this to do what you need..
For example the Grime Z80 port for the Lynx uses Tilemap code designed to work in 8x8 blocks, which is designed to work in the best way for one byte width blocks.
- Sound and Keyboard on the Camputers Lynx
The Keyboard and sound of the Lynx will be pretty familiar to regulars of these tutorials! The sound is basically an upgraded spectrum (With Volume levels) and the keyboard is pretty similar to the CPC or Spectrum...
Lets learn how to use them!
|Like most Z80 computers, the Lynx keyboard uses a set of ports, which represent 'rows' of the keyboard, and each
These 'rows' are relating to the electrical membrane, not keys as they appear,
The keyboard responds to all ports with a bitmask of %****XXXX 10***00* ... where XXXX is one of the ports shown
In this data, a key will be 1 if it is UP , or 0 if it's pressed down
|In practical terms, this means we set C to 128, and B to 0... then INC B until we get to %00001010
This allows us to read in the raw bit data from the keyboard... we can then use the existing Multplatform Code (Click Here) to convert raw data to letter keys (if we want)
|A beeper is a crude system, where we can define 'blips' of sound to make up a tone
By altering the time between these 'blips' we can affect the pitch of the resulting tone!... the upside is we can make a good range of tones... the downside is, we'll pretty much have to use all our CPU power calculating the delay between the 'blips' re-sounding the blips... unlike the ZX Spectrum, however the Lynx can set volume levels using 6 bits
Sound is controlled on the lynx by port &0084 - or any port that matches the bitmask %********10***10*
This means, unlike the other options, sound will not play during normal operation... so background music will be pretty much impossible
If we want to make a distorted noise, that's pretty easy too... all we need to do is 'randomly' skip some of the 'blips'... the tone will sound rough and distorted as a result!
we're having to create the waveform ourselves here by creating high and
low peaks... it's a real pain to do, but if you're very clever you can
do some impressive stuff!
There is some sourcecode online for some ZX Spectrum 'music players' online that can play 'proper' multi-channel music using the beeper speaker if you really want to push beeper systems to the max!
|The principle of sound is easy ...
1. Blip the speaker ( with noise if required)
2. Wait a while for the next blip
3. Repeat for the desired tone length
But the procedure is a bit of a pain! We need to calculate a good delay to keep the sound the same length whatever the tone, and we need to get some random noise to produce distorted sounds...
'ChibiSound' uses a single byte parameter in the format NVPPPPPP... where P is the pitch (0-63) V is the Volume (Low/High) and N is the Noise (On/Off)
On the Lynx we use the R register as a random source and a AND command with self modifying code to set the volume
- Playing Digital Sound with WAV on the AY!
We've looked before at normal sounds, but lets start to learn how we can convert WAV files so we can play them back on 8 bit machines for Speech and SFX!
Lets take a look!
| Most WAV files these days will be 16 bit - these will be
Signed, and will end up representing a wave with values from 32768 to
-32768 - with silence being Zero.
When we play the wave file on an 8 Bit sytem, we do so by adjusting the 'Volume' of the channel to match the wave, this will recreate the waveform, and the sound!
BUT no 8 bit system has a 16 bit volume level... and memory is limited!
A system like the AY has a 4 bit volume level - so we can 'pack' our wave data into two samples per byte... but we need to convert the 16 bit values to 4 bit unsigned ones.. you can see how the wave is represented by numbers in each bit option in the diagram to the right:
|A wave file, and how it's range is represented in different bits per sample:
|I've made a tool to convert to 1/2/4 bit per sample wave files, but you need to prepare your file for my program!
You can convert your sound file with Audacity, you'll want to make it as small and loud as possible, you'll also want the file to be MONO... (a stereo file can be used, but the right sound channel is ignored!)
then use the Export option
|Select the Wave signed 16-bit PCM option|
ChibiWave converter supports MONO only, if the file is stereo, the LEFT
channel will be used... files MUST be SIGNED 16 bit, but can be 22050
hz or lower... but if you have trouble, make sure your input file is
44100 hz, MONO and 16 bit as shown above!
|In these tutorials we'll look at playing 1, 2 and 4 bit per sample sound files,
I've written a little program called ChibiWave Converter... that can convert a WAV file into a RAW (headerless) file in the correct format!
Just select a wave file, select a Bits per sample... and a Frequency DownSample... if you source file is 44100hz (CD Quality), and you use a 1/4 Downsample, the resulting file will be 11025 hz (voice quality)
The AY sound chip volume is linear, so the sample may be quiet, you can use AV Vol Boost to make it louder!
Most systems can play 4 bit samples, some like the ZX Spectrum can only play 1 bit - actually some can play higher bit depth, but that's not supported by my tool, or these tutorials, so you're on your own!
|There's no reason you can't do 3 bits per sample too, but it's not supported in these tutorials...
It should also be noted that the code here is a 'one size fits all' solution, if you only need 4 bits per sample, you could modify it and write something far better!
|We're going to create a ChibiWave function! this will play our wave files... it takes 4 parameters:
HL is the address of the wave data in memory
DE is the length of the wave data in bytes
A is the bit depth of the samples... values of 0/1/2 represent 1/2 or 4 bit per sample
B is the delay between each sample - increasing it slows down playback - due to the difference in the code and hardware, unfortunately the playback speed is not consistant on all systems.
|When the routine starts, we need
to use Self Modifying code to reprogram some of the functions - we need
to use different modules to split out the sound depending on the bit
depth, and mod in the length of the pause...
We also use IX as the byte count.
|We need to init the AY to some default values, to make sure it's not playing some weird pitch in the channel we're going to use - this could make the sample sound odd!|
|This is the start of the wave
routine... we load in a byte, and shift the first bit in from the byte
to the Accumulator - we then run 'Do4BitWav' - or one of the other
routines, as this call is modified by selfmodifying code.
|However many bits of sample we
have, we need to bit shift them into the maximum volume position, so if
we have 1 bit, because the volume is 4 bits, we shift it left 3 times.
We then send the byte to the 'Volume' of one of the channels (via AYRegWriteQuick - see ChibiSound.asm)
Setting the volume causes a 'blip' to that volume level, doing this repeatedly creates the waveform, and we hear it as the original sample...
|We use B to pause a while (this slows back playback rate) - this is selfmodded in
Now we decrease E, and if there are any more samples in the byte - we repeat the play routine
If there are not, we decrease IX, and if it's nonzero, we read in a new byte...
When playing is done, we turn off the sound!
some AY systems, It's possible to play multiple samples at different
volumes to increasae the bits per sample from 4 to 8 or more...
Unfortunately the Author of these tutorials is to stupid to make it
work... so if you want to look into it check out this site here
|The Spectrum 128k+ also have an AY sound chip, and this code will work on those systems too... but if you want to support the 48k spectrum, you'll want to use a 1 bit per sample file, and we'll learn how to do that in a later lesson!|
- Playing Digital Sound with WAV on the CPC+ via DMA!
The CPC+ has an added function for sound - it can automatically set AY registers from a 'script' within memory without the Z80 doing anything (known as DMA - Direct Memory Access)
This means we can play a digital sound without using ANY cpu power - unfortunately the 'script' is quite large, so it takes a lot of memory...
Lets Check it out!
|Function||Format||Channel 0||Channel 1||Channel 2||Global|
|DMA Data Address
2 bytes - Little Endien (LH)
|LLLLLLLL HHHHHHHH||&6C00 / 01||&6C04 / 05||&6C08 / 09|
|LOAD||&0RVV||Set AY Register R to V|
|PAUSE||&1NNN||Wait N*prescaler ticks - if we have 3+ samples that are the same we can use this to reduce the wav file size|
|REPEAT||&2NNN||Repeat command - this is like FOR I=0 to NNN|
|NOP||$4000||Do nothing this tick|
|LOOP||&4001||return to repeat point - this is like NEXT|
|INTerrupt||&4010||Cause an interrupt - Status register bit 6-4 will be set depending on current channel|
|STOP||&4020||Stop the DMA - end of the sound script|
|If you want
to use DMA to play one sound sample after another - or you want to keep
filling a buffer (of calculated/decompressed samples) - you may want to
have an INT command (&4010) at the end of your list - this will
cause an RST7 call to &0038
Your interrupt handler could then check &6C0F to see if the interrupt was caused by VBLANK or the DMA and act accordingly!
|Lets actually make the AY do something via the DMA!
We're just going to do a simple 'Beep' using channel 2 to start, but all we'll need to do is swap in a proper sample later!
This example should be compiled with Winape's internal assembler.
Because we're going to use CPC+ features, we first need to enable them, by sending the plus init sequence to the Gate Array..
Next we page in the CPC+ registers by OUTing &7FB8 to the Gate array...
We store the address of our DmaList in &6C00 (DMA Channel 0's data)
Now we turn on DMA channel 1 with the Control register at &6C0F
We now turn off the CPC+ registers by OUTing &7FA0 and return to basic...
Because we're using a DMA, the list will be processed WHILE basic is usable!
The sample DmaList
The list in this example just turns Channel 1 on, sets it to top volume, and makes a middle tone before reaching a STOP command.
|If you've been folowing these tutorials, you should understand these commands... if not please see Lesson P13 - we covered the CPC+ functions in that lesson
|My ChibiWave Converter can convert a WAV file for use with DMA on the CPC plus - just select CPC+DMA
As the DMA runs at 15khz, the best quality will be provided by a downsample of 1/3, but you can use 1/4 or more if you prefer...
Note: This converter replaces sequences of the same volume with PAUSE commands - these will reduce the filesize by around 50% - but the size will vary depending on the wave data.
ChibiWave Converter will also include commands to turn on the sound at the start, and turn it off at the end - all you need to add is a STOP command...
|When we want to use the sample we just use INCBIN to include it in our code...
In this example we've added a REPEAT (&2nnn) and a LOOP (&4001) - as we've specified a REPEAT of 2 (&4002) the wave file will play 3 times!
|If we want to slow down the sample, we can change the PRESCALER (playback speed)... 0 is the fastest... 1 will halve the speed - so should be used with Frequency Downsample of 1/6 (around 8khz)|
Prescaler only affects PAUSE commands - but as we added a load into the
data (as part of compression) it will probably work OK... techinically
the playback speed will not be even, but in practacle terms, there's no
- Playing Digital Sound with WAV on the Sam Coupe, Camputers Lynx and ZX Spectrum
The principle of playing waves on the AY are the same as other systems... all we need to do is manipulate the volume of the system... and use an appropriate bit per sample...
Lets learn how to play waves on 3 other systems
|The ZX spectrum only has a 1 bit beeper... controlled by bit 4 of port &FE
As there's no point using a 4 bit sample when we can only use 1 bit, we'll use a simple version of ChibiSound that only plays 1 bit samples...
|We could convert 4 bit samples to get them to play on the Spectrum beeper, but it would be a waste of memory!
That said if your game was going to support AY on 128k systems, and Beeper on 48k ones, it may be worth it!
|The Camputers lynx a 'beeper speaker'.. but unlike the spectrum is uses a 6 bit volume level
We use port &84 to set the volume, we just need to shift the bits 2 to the left to pad the 4 bit sample out to 6 bits
|Unlike other systems, just setting the volume isn't enough...
it wouldn't make a sound - and setting a tone won't help either - as
the frequencies are too low and will make weird squeals...
What we need to do is we need to configure the Envelope to use option B - this will make a peak in our waveform when we send the digital data
|Before we can play our waves We need to do some set up...
We need to enable sound, Set Envelope B and turn envelopes on,and select the Volume register for channel 2 - we have to use channel 2 as it's the one affected by the Envelope option
Because all wave writes will be to the volume register, we're just going to write the volume levels to the data register at port 255, without reselecting the volume register with port 511
|Figuring out how to make the wave play well on the Sam Coupe was a pain in the ass!
The Author of these tutorials cheated, and disasembled the 'Megablast demo' on FRED Magazine Issue 12 (1991) to figure out how to do it right!... Remember! if you can't figure it out yourself, just steal someone elses good ideas!
- Playing Digital Sound with WAV on the Sega MasterSystem/GameGear, Elan Enterprise and GameBoy/GBC
We've looked at most of our systems now, but there's still a few left, fortunately, it's all pretty similar, just like before we just need to switch our volume levels to get the job done...
Lets finish the job!!
|We're just covering the differences here... so please see Lesson P35...
We covered the basics of how ChibiWave works, the concept of playing
digital sound, and how to convert wave files with ChibiWave!
|The Enterprise is pretty easy, we use ports &A8 and &AC to set the Left and Right sound channel volumes
The EP128 has a 6 bit volume, but we're only using 4 bit samples,so we have to shift left by 2 bits
|The SMS/GG version of ChibiWave
is slightly different, rather than using Self modifying code, we use IX
and IY to store our extra values...
Also rather than a direct CALL , we use a JP(IY) to execute the required sample converter.
|On the Sega Mastersystem, we just need to set the top 4 bits to %1101 to set the volume, the remaining 4 bits are the volume level|
|TThis version of ChibiWave will work on other systems too... just change the 'volume command'!
Depending on your system, and what you use the registers for, you may prefer this version that can run from ROM... or the version that needs IX and IY... Remember, some systems like the speccy use IY in their firmware!
|On the Gameboy we have to do things differently, because of the limited command set.
We use bytes of ram to simulate IX and IY - but otherwise the code is basically the same as the SMS/GG version
|Again our main code is virtually the same, the significant change is the 'Call IY' command...
We have to use BC as a tempory pair, load our IY values into that, push them onto the stack, and then RETurn
The result is the same as jumping to IY
|Finally the job of setting the volume level...
On the Gameboy our volume levels are only 3 bit - so we ignore the 4th bit of our samples,
When we set the volume levels, we need to set 3 bits of the top nibble, and 3 bits of the bottom nibble - this sets the Left and Right channels
|The Gameboy DOES have a digital sound channel that can play 4 bit samples - but it uses just 32 bytes of samples (64 samples)...
In theory this could mean we could do better sound, but it seems impossible to 'stream' data into this bamk - as the channel needs to be disabled when the data is updated, and we can't detect when the buffer has been played...