|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|
- Sound on the Gameboy and GBC
The Gameboy sound chip is a little weird! It has 4 sound channels, but each has different properties
Channel 2 is a basic Tone channel
Channel 1 is a Tone channel, with Sweep function
Channel 3 is a 'Wave channel' (only 32 bytes!)
Channel 4 is a noise Channel.
|Sound||FF10||NR10 - Channel 1 (Tone & Sweep) Sweep register (R/W)||-TTTDNNN||T=Time,D=direction,N=Numberof shifts|
|Sound||FF11||NR11 - Channel 1 (Tone & Sweep) Sound length/Wave pattern duty (R/W)||DDLLLLLL||L=Length D=Wave pattern Duty|
|Sound||FF12||NR12 - Channel 1 (Tone & Sweep) Volume Envelope (R/W)||VVVVDNNN||C1 Volume / Direction 0=down / envelope Number (fade speed)|
|Sound||FF13||NR13 - Channel 1 (Tone & Sweep) Frequency lo (Write Only)||LLLLLLLL||pitch L|
|Sound||FF14||NR14 - Channel 1 (Tone & Sweep) Frequency hi (R/W)||IC---HHH||C1 Initial / Counter 1=stop / pitch H|
|Sound||FF16||NR21 – Channel 2 (Tone) Sound Length/Wave Pattern Duty (R/W)||DDLLLLLL||L=Length D=Wave pattern Duty|
|Sound||FF17||NR22 - Channel 2 (Tone) Volume Envelope (R/W)||VVVVDNNN||C1 Volume / Direction 0=down / envelope Number (fade speed)|
|Sound||FF18||NR23 - Channel 2 (Tone) Frequency lo data (W)||LLLLLLLL||pitch L|
|Sound||FF19||NR24 - Channel 2 (Tone) Frequency hi data (R/W)||IC---HHH||C1 Initial / Counter 1=stop / pitch H|
|Sound||FF1A||NR30 - Channel 3 (Wave Output) Sound on/off (R/W)||E-------||1=on|
|Sound||FF1B||NR31 - Channel 3 (Wave Output) Sound Length||NNNNNNNN||Higher is shorter - no effect unles C=1 in FF1E|
|Sound||FF1C||NR32 - Channel 3 (Wave Output) Select output level (R/W)||-VV-----||VV=Volume (0=off 1=max 2=50% 3=25%)|
|Sound||FF1D||NR33 - Channel 3 (Wave Output) Frequency's lower data (W)||LLLLLLLL||Low frequency|
|Sound||FF1E||NR34 - Channel 3 (Wave Output) Frequency's higher data (R/W)||RC---HHH||H=high frequency C=counter repeat (loop) R=Restart sample|
|Sound||FF20||NR41 - Channel 4 (Noise) Sound Length (R/W)||---LLLLL||L=Length|
|Sound||FF21||NR42 - Channel 4 (Noise) Volume Envelope (R/W)||VVVVDNNN||Volume / Direction 0=down / envelope Number (fade speed)|
|Sound||FF22||NR43 - Channel 4 (Noise) Polynomial Counter (R/W)||SSSSCDDD||Shift clock frequency (pitch) / Counter Step 0=15bit 1=7bit (sounds eletronic)/ Dividing ratio (roughness)|
|Sound||FF23||NR44 - Channel 4 (Noise) Counter/consecutive; Inital (R/W)||IC------||C1 Initial / Counter 1=stop|
|Sound||FF24||NR50 - Channel control / ON-OFF / Volume (R/W)||-LLL-RRR||Channel volume (7=loud)|
|Sound||FF25||NR51 - Selection of Sound output terminal (R/W)||LLLLRRRR||Channel 1-4 L / Chanel 1-4R (1=on)|
|Sound||FF26||NR52 - Sound on/off||A---4321||read Channel 1-4 status or write All channels on/off (1=on)|
|Wave Pattern RAM||HHHHLLLL||32 4 bit samples|
always, we just need to write our data to the memory addresses between
&FF10-&FF3F to set the sound options...Whatever you're
to do Just don't forget to turn the sound on with FF24 and FF25!
If you're not using sound, turn off the channels with FF26
|Before we try to make any sounds, we need to set the
system volume, and turn on some sound channels!
Each channel is turned on with a 1 bit for the left and right speaker, and the overall volume is 0-7 for each speaker.
|The tone channels have a few configurable parameters...
Firstly (of course) is the tone, defined bt 11 bits in &FF18
We can also define a length, if we want, though it will not have any effect unless the Counter bit in FF19 is set to 1... there si something called the 'Wave Duty' which changes the shape of the tone, try it for different sounds
We can also define a volume, and an Envelope, envelopes are just a 'fade' - 0 is off, 1 is fast, and 7 is slow... we can fade in or out depending in Direction and start volume
We need to set the 'Initial bit' to 1 to actually start the sound!
|Channel 1 is basically thesame as Channel 2, but with
an extra 'Sweep funcion'
The sweep function allows us to change the pitch of the sound over time...
If we do not want this, we can simply set it to 0... however any other value will make the tone change over time, and lower values in T and N will make the tone change faster
|If we're going to
use the wave channel, we need some sound data...
The Wave channel uses 16 bytes, with 4 bits per sample...
we need to fill the area &FF30-FF3F with some wave data to make a sound,
|Making the sound
play is also similar to the tone channels, however we have fewer
Wwe only have 4 options for the volume, and we have no envelope we can use.
|The Gameboy may have a 'Wave' Channel, but
don't think that you're going to be doing speech or anything... not
with 32 samples!
Check out Beatmania on the Gameboy, then look at the Wonderswan version... The Gameboy totally got it's ass handed to it by the WS didn't it!
|If you want to emulate AY, you can use the
Wave channel as another Tone channel... it's a bit of a pain, but it
|The Noise Channel has a lot in common with the Tone
Rather than a frequency, The Noise sound is defined by &FF22 ... SSSS is essentially the pitch... low numbers are white noise, high numbers are a low rumble... the Dividing ratio will change the roughness of the sound, making it sound less 'crisp'...
We can aslo change the Counter step, which will make the noise sound eletronic, which may be depending on the effect you're trying to make
As always, it's best to try different settings to get the sound you're after.
|Now we know how to make the sounds work, we
can easilly write ChibiSound...
We're not covering it here, but it's in the source code, so just go ahead and download it if you want to take a look!
- Sound with the SN76489 on the Master System, GameGear, Megadrive
(Genesis) and BBC Micro!
The SN76489 Sound chip is used on the Master System, GameGear and BBC Micro... while the Genesis does have superior sound, it has backward capability with the older Master System - so we'll use it for now, as all we're trying to do is make simple beeps anyway!
|Format Template||L=Latch C=Channel T=Type XXXX=Data||L||C||C||T||D||D||D||D|
|Tone - Command 1/2||C=Channel L=tone Low data||1||C||C||0||L||L||L||L|
|Tone - Command 2/2||H= High tone data (Higher numbers = lower tone)||0||-||H||H||H||H||H||H|
|Volume||C=Channel (0-2) V=Volume (15=silent 0=max)||1||C||C||1||V||V||V||V|
|Noise Channel||(Channel 3) M=Noise mode (1=white) R=Rate (3=use tone 2)||1||1||1||0||-||M||R||R|
|To make a tone we
will probably want to send 3 bytes to the same channel
The first one will define the Low 4 bits of the Tone (Latch will be 1)
The second will be the High 6 bits (Latch will be 0)
If we want to change the volume, we'll need to send a 3rd byte
Volume is defined by 4 bits (0=max volume).. the Latch will be 1 - there is no second (latch0) byte for a volume command
|Using the Noise
channel is similar, though we send no second byte with the latch off..
We have 3 bits to control the nosie, bit 2 is the Mode - we can either have white noise (1) or eletronic nose (0)
The Rate bits can be 0-2.... setting a rate of 3 will link the rate to the frequency of Tone Channel 2... if you do this you'll probably want to mute tone channel 2 by setting it's volume to 15
the destination platform or CPU, the command data above will work the
same, however what we need to do to send that data to the sound chip
lets take a look at how to get our command data to the sound chip on various systems that use it!
|The Mastersystem and GameGear
The examples above were from the Mastersyem! On the SMS/SGG we just send all our data to port &7F!
|The Sega Genesis /
If we're getting the Genesis Z80 to control the SN76489, we can just send data to port &7F again!
Unlike the NeoGeo, we can access the sound chip from the 68000 - which is probably easier!... we do this by writing to port &C00011
The data we send is identical to the SMS/GG - but the 'White Noise' generated by the Genesis is lower pitch, so you may need to change the frequencies you use for noise.
|The BBC Micro
The Sound Chip shares a port with the keybord... Before we can send any data to the sound chip, we have to set the port to WRITE... we do this by writing 255 to address $FE43 (we only do this once)
Once we've done that, we can write our data to $FE41 in the same way as the examples above!
is just for Beeps and SFX... if we wanted to do proper music on the
Genesis, it would be best to use the Z80... we could even use
Then again, we'd probably want to use the YM2612 sound chip instead!
- Sound with the 'Beeper' on the ZX Spectrum and Apple II
While most of the systems we have looked at have 'proper sound processors... unfortunately not all do!
Before the 128k systems,The ZX spectrum 48k only had what's known as a 'beeper speaker'... this is also used by the Apple II
It's not pretty... but we can use it to make sounds!
|The Beeper is the worst possible sound system! but we
can make something out of it!...
with just one bit controling the sound... we can 'blip' the sound -
which has the effect of sending a pulse to the beeper (shown by the red
arrows to the right)
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 don't have any volume control... and we'll pretty much have to use all our CPU power calculating the delay between the 'blips' re-sounding the blips...
This means, unlike the other options, sound will not play during normal operation... which is why background music isn't really a thing on the ZX Spectrum 48k!
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 destorted 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 speccy 'music players' online that can play 'proper' multi-channel music using the beeper speaker if you really want to push the 48k systems to the max!
|ZX Spectrum 48k & Beeper- The various uses of Port &FE!|
|Port FE on the speccy controls the border, tape (Mic)
and Beeper sound
The "Beeper" sound chip is incredibly crude... it is controlled by bit 4 of the port &FE... by turning it on and off we can make simple sounds...
See the example to the right... by changing the pause (caused by BC) we can change the pitch of the sound... 3000 will be a relatively low pitch... 500 will be higher...
Some clever programs even manage to "Fake" multiple sound channels!
The big disadvantage to all this is that the CPU will be busy during the whole time, so the Beeper chip isn't very helpful, and we'll want to use the AY sound chip on the 128k systems... but on the 48k machines, it's all we've got!
|Beeper sound on the Apple II|
|The principle on
the Apple II is the same, however instead of writing bits to a port, we
simply READ from $C030
The beeper hardware is connected to memory address $C030... and reading from $C030 has the effect of causing the Beeper to flip the wave... it's weird that we read not write..., but that's what we do!
Each time we do, it will flip the sound wave, again we need to wait a while depending on the frequency, then loop the operation to keep the sound playing!
|When we want to make a noise, we need some
On the speccy Chibisound uses the R register... on the Apple, it uses the sourcecode of the program itself as a psudorandom source....
All we need to do is skip some of the 'blips' depending on a bit of the random data... it will make the sound mour rough!
- Bankswitching and hardware detection on the Amstrad CPC
It's time to move on from sound, and start looking at other hardware!...
We're going to start looking now at Bankswitching - to allow us to gain access to the extra RAM and ROM ... we'll also learn how to detect what hardware is available in software - so we can have one game version that works with extra ram (and other things) when we can!
|0||0||-||B||P||P||P||P||Palette selection||B=Border P=Pen (0-3 / 0-15)|
|0||1||-||C||C||C||C||C||Palette color selection||C= Clolor number (0-26)|
|1||0||-||I||H||L||M||M||Rom / Mode||I= Interrupt mode H=High rom bank L=Low rom bank M=screen mode|
|1||1||B||B||B||R||R||R||Ram Bank||R = Ram config B= Bank number (0=128k 1+2=256k etc)|
|64K||128K||192K||And so on!|
|We've covered it before, but remember, by default The
CPC+ features are disabled, leaving us
only a regular CPC... to use the PLUS features, we need to send a
series of 17 bytes to the CRTC at port &BCxx
once the CPC+ features are on, a special bank of 16k 'ASIC' memory will become available, which we can write to between &4000-&7FFF in the same way as our normal memory.
To Turn it on we use:
To turn it off we use:
NOTE: When using the ASIC memory, We should make sure that we're using the 128k banks are disabled, or it can cause problems with some 128k ram upgrades. Mode C1/C3 are OK to use as they page in at &C000
Asic Ram, when enabled appears at &4000-&7FFF, but we also have cartridge ROM
On a CPC+ the Cartrige ROM has 32 banks... numbered 0-31... Banks 0-7 are intended to act as system ROM, appearing between &0000-&3FFF... however ANY bank (0-31) can be paged in at &C000-&FFFF - the same area as screen memory! Even better, if we WRITE to this area - it will be written to the screen RAM through the rom, so we don't need to page out the ROM to write data to the screen!
Setting a rom bank is easy, we write the ROM we want to port &DF... values 0-7 will set the Low rom area number... setting the High rom area is done by adding 128 to the rom we want, and OUTing it to &DF
Once we've selected it we still need to enable the rom, and we do this by writing to Bit 3 of the Gate array!
|To detect a
CPC Plus, we send the PLUS sequence, and turn on the ASIC ram.. .if
nothing happended we don't have a CPC+... we can use this fact to
detect a CPC!
you can write to ASIC ram just like normal memory, don't rely on being
able to read from it!
The development of ChibiAkumas ran into a problem, because reading the CPCPlus interrupt line from the ASIC worked on emulators... but not real hardware!
It's probably best to assume you can't read back any writable asic values!
|So to detect RAM
and the PLUS
ASIC, we need to set a byte in our normal memory, turn on the feature
(ram or asic)... and WRITE to the same location - then turn off the
If the byte we wrote isn't there, then we know the feature does not exist!
In this code we'll use &69 as a byte that we'll write to the test location, and CPL to flip the bits of the location when we test.
|While each system
is different We're going to use a fairly generic detection function...
It will return a value in DE... where D is the hardware type and E is the memory available
On the CPC, D will be 1 if the machine is a PLUS, and E will be 0,1 or 2 depending if the machine is 64k,128k or 256k
It's intended this function is executed at the start of your program, so the game can switch to different functions (or even load a different version of the game) depending on the platform.
|We'll be creating similar functions on all our other systems, but the data returned will be different, because there's no such thing as a Gameboy with ASIC ram or 512K... or a CPC with an 8mhz GBZ80 CPU!|
- Bankswitching and hardware detection on the MSX
The MSX has the most complex bank switching due to its upgradability, unfortunately it doesn't tend to help us!
Most MSX1 have 16k, and most MSX2 have 64k, so bankswitching is really about getting cartridge or system rom, than gaining extra memory
|The Theory of MSX slots is complex and
It's proably best to just ignore them!!! If you're game is on cartridge, just make do with 16k of ram at &C000 - as that's all MSX1 machines have... if your game is MSX2 on disk, Just use THIS
|Slot 1||1||Unexpanded slot|
|Port &A8 Bits||7||6||5||4||3||2||1||0|
|Bank number||3 (&C000-&FFFF)||2 (&8000-&BFFF)||1 (&4000-&7FFF)||0 (&0000-&3FFF)|
|Subslot 0 &FFFF Bits||7||6||5||4||3||2||1||0|
|Subslot number||3 (&C000-&FFFF)||2 (&8000-&BFFF)||1 (&4000-&7FFF)||0 (&0000-&3FFF)|
|We need to check if a slot is expanded before
we write to &FFFF... also remember that this register only
exists in subslot 0... so we need to page in subslot 0 to Bank
3 before we can change it!
|Port||Z80 Address range||Default value|
|Some MSX memory mappers can be read from, but
others can't... so don't do it!
Remember what you wrote back last time, if you need to know! also remember, that a Turbo-R with a MegaFlashRom will have TWO mappers... they both share the same ports, so it just depends what slot is in each bank when it comes to what the Z80 will see!
|Area||Z80 Address Range||Write Address to change bank|
|Disk System?||NO||NO (sometimes)||NO (usually - not always)||YES|
|When our game
starts, we should backup the current configuration of the Rom and RAM,
we do this by reading IN port &A8 to get the slot config... and
memory address &FFFF to get the subslot configuration...
we do need to flip the bits of the data in address &FFFF
|It's wise to backup the default slot layout, as you will need to restore it if you need to use the firmware later to do jobs like disk access... just run the commands above as soon as your program runs, and save the two bytes needed to restore things for later!|
|We're going to use
a generic function for setting our Slot and Subslot into one of the Z80
banks of memory...
Because the slot/subslot registers combine all 4 slots in a single byte, we'll use a 'GetSlotMask' function to convert a slot number to a mask and new value - this will make altering one of the 4 values in that register easier.
It should also be noted that we should only change the subslot on an EXPANDED slot... unexpanded slots do not have any such register.
First we check if the slot is expanded, then we set the subslot if we need to. then we set the slot number - and we're done!
|Finding the base
64k is really easy on disk systems... the boot
sequence finds them for us, the Slot and Subslot will be stored in the
low 4 bits of the memory addresses F341-F344 for bank 0-3 of the Z80
range... of course this will only be usable if we're releasing our game
on a Floppy disk.
We'll need to convert the data in these to the correct format for the Slot register (&A8) and subslot register (&FFFF), but this is pretty easy, and we'll then have an easy way to get all the ram for our game!
above is the way ChibiAkumas finds the memory on the MSX... as
ChibiAkumas requires, and only needs 64k, and only comes on disk, this
was an easy way to solve the problem of finding memory...
Of course, if you want to release a 64k+ cartridge game on the MSX2, you'll need an alternative, and we'll look at that now!
|To find the ram on
a non disk system, we have to step through each slot and subslot and
see if we can find a bank which is RAM
we do this by simply writing to the bank, and seeing if the data changed - if it did, then that's our ram bank, otherwise we move on to another bank.
|We can recognize
the basic MSX types simply by checking the value at memory address
0 will mean an MSX1, 1=MSX2, 2=MSX2+ and 3=Turbo R
if we want to detect a WSX we can out '8' to port 64 / &40
when we read back, we should get the compliment of 8 if it's a WSX type... we need to know this as the WSX has a 6mhz option on it's cpu!
|The Turbo-R has a
7mhz Z80 clone that runs up to 8x the regular speed of an
We can turn it on using the 'CHGCPU' firmware function at memory address &0180... we should do this when our game starts...
also the WSX has a special 6mhz Z80 that will make our game faster - we need to check if the machine is a WSX first, then out 0 to port 65 if it is... this will enable 7mhz mode!
Both these functions were used in ChibiAkumas!
|The Turbo-R is 2-8 times faster than the Z80... but there are some oddities.. firstly it will turn off during disk loading - the z80 will do all the work (it'll also turn on automatically)... also the Turbo-R has a built in limitation to the OUT command (presumably to stop the R800 locking up other hardware with its speediness!)... it will actually perform OUT commands SLOWER than the Z80|
|The WSX has a 6mhz Z80... like the Turbo-R it
has a limitation on it's OUT command speed...
More problematic is the AY sound chip... the frequencies are all different, so your sounds will come out a different pitch!
ChibiAkumas used an alternate pitch table for ArkosTracker on the WSX to compensate!
|We can detect a
BankSwapper by trying to page in an extra bank of memory, and seeing if
a writable bank was actually paged in... if it was, then we must have
extra memory... if not, or we end up with ROM, or empty space that
doesn't store anything then we mustn't have any extra memory.
Unfortunately, so few MSXes have BankSwappers or more than 64k of memory it's probably pretty pointless, unless you're making your minimum requirements the Turbo-R or the MegaFlashRom
- Bankswitching and hardware detection on the ZX Spectrum
The original ZX Spectrum only had 48k of memory... but the later ones had 128k, rather strangely, however, the black +2 and the +3 have extra memory management options!
This makes spectrum memory management more confusing than it need be!
Lets take a look, and untangle it!
|On the ZX Spectrum 128k, the memory banks are split
into 4 16k chunks...
The bottom of these (&0000-&3FFF) is always ROM
This means we have 5 extra banks of memory we can page in... but there's a problem!
The top one (&C000-&FFFF) is the ONLY one we can page the extra memory into, and even worse, this is the ONLY position we can use as the second screen buffer
* +3 disk rom uses &DB00-&E7FF - must be backed up or disk system will break!
|The later spectrum have a more advanced mode, where we
can get rid of the ROM and have memory in all banks!!
unfortunately... this is only available on the +3 and the BLACK +2 ... (not the grey +2... it's effectively a Spectrum 128)... as that means 85% of spectrums cannot use it, we can't really take advantage of it!
IF we COULD... then we would have access to 4 different bank setups
These were added to the spectrum to allow CPM to work - it required RAM at bank &0000 - meaning the original spectrum could not do it.
|Not all banks are
equal on the ZX Spectrum!
Some of the banks are 'Contended' with the screen memory.. this means that they will be slower than the other banks... to makes things worse, the contended banks are different on the +3 (and Black +2) to the spectrum 128 (and the grey+2)
Banks 0 and 2 are NEVER contended on any machine... so you should use these for your main code... and the other 'possibly contended' banks for sprite data and less used code.
If you were going to be super-clever, you could make your code detect the spectrum type, and use banks 1+3 for slow data on the spectrum 128, and 4+6 on the Spectrum+3
|&7FFD||-||-||I||R||S||M||M||M||M= ram bits S=Screen page bit R=Rom Low bit I=I/O Disabling||&5B5C|
|&1FFD||-||-||-||S||D||R||-||P||P = paging mode 0=normal R=Rom high bit D = Disk Motor S=Printer strobe||&5B67|
|The "Screen Page Bit' (bit 3 of &7FFD)
allows us to to use an alternate screen buffer in Ram Bank 7 at
However, there's a problem... this can't be done on 58k machines... if you want to have a second screen buffer on 48k, you'll have to copy the screen from your buffer to &4000 with an LDIR, or some other copy command- there is NO BETTER way on a 48k!
|&1FFD||-||-||-||S||D||O||O||P||P = paging mode 1=+3 model O=Bank Option D = Disk Motor S=Printer strobe||&5B67|
|&C000||Ram 3||Screen 2 (7)||Ram 3||Ram 3|
|&8000||Ram 2||Ram 6||Ram 6||Ram 6|
|&4000||Ram 1||Screen 1 (5)||Screen 1 (5)||Screen 2 (7)|
|&0000||Ram 0||Ram 4||Ram 4||Ram 4|
the Easiest way to detect different spectrums would to be look at the
rom, read in a known location, and see if it's bytes match what we
However some machines use non-standard rom, so this is not advised - but it may be enough if you're not too worried about compatibility!
|We're going to
create a command called Bankswitch_SetCurrent.. this will set
a permanent bankswitch...
We can also use Bankswitch_Set... this will temporarily set a bank... and Bankswitch_Reset will undo it!
These were taken from the ChibiAkumas game!
|We're going to test
to see if we have 128k banks!
We do this by writing a marker, changing to an alternative bank... then altering the address we wrote the marker to...
When we swap back to the original bank, we should see the marker, if we do not, then the bankswitch did not work, and we must only have 48k
|We're going to do
pretty much the same thing with the +3... however there are some
The backups of the &7FFD and &1FFD registers, and the stack are in the &5000 range... we're going to have to use 'Option 0'... as our program code is in Ram Bank 2 we need that not to move, but we will need to back up &5B5C and &5B67 into D and E before we try to turn on the +3 bank... we'll also need to avoid using the stack.
Again, we write our marker
Then we apply the +3 switch... flip the bits at the address of the marker data, and turn it off!
if the marker is still there, we have a +3... if not, we have a 128k machine.
|Don't try to detect a +3 until you've
detected 128k... it won't work!
Also note we're paging in bank 0 AFTER turning on +3 mode... turning +3 on caused a bankswap on the 128k for some reason!
detection code was written for these tutorials... ChibiAkumas required
128k, and did not work differently on +3 (except the disk routines..
and these were a different build of the game)
Use the code above at your own risk! it seems to work, but hasn't had much testing!
- Bankswitching and hardware detection on the Enterprise
The Enterprise is a strange beast! it was an utter commercial failure, but it's OS and memory mapping is superior to the MSX!
Lets take a look at the impressive capabilities of the Elan machine!
|The Enterprise and it's OS supports up to 256x 16k
banks for an insane 4MB of memory (though some may be rom!)
The most basic system supports 64k, but there is also a 128k version (the EP128)...
There are two special segments... the Zero Segment containing the RST's, and our program, (loaded at &0000) and the system segment (loaded at &8000 by default)... around 2/3rds of the system bank will be used by the OS... the rest will be free.
it's important to notice, ONLY memory banks in the base 64k can be used for video memory.
We can use the Zero page bank if we want... but we must 'request' ALL other banks from the OS before using them... it will find one, and tell us what it's allocated - if all the banks are used, it will give us the spare memory of the system page,
The diagram to the right, shows how the OS allocates memory on these two systems.
Essentially the internal memory has bank numbers 255-252 (FF-FC)... as we add more memory, we will get lower and lower numbered banks... for example a 256k system will have banks down to 240 (F0)
The top bank (255) is used as the system segment... the bottom bank (number will depend on upgrades) will be the Zero page...
The system will allocate banks in consecutive order... so we won't use the internal 64k until we have no choice... this means we can be sure to have it available for Vram!
Low numbers (for example banks 0-4) will be rom.
64k Bank (Can be Vram)
Extended bank (Cannot be vram)
|We can ask the OS for memory, and then use it for
want... if we don't need it any more, we can tell the OS to take it
This is how we get a free VRAM bank... we ask for memory banks, until we get one >=FC (a 64k internal bank)... This may take a while, as the OS gives us the low numbered banks first... but once we get one, we can just free up all the ones we didn't really want!
We use RST 6 to do an EXOS call... and command 24 to request a segment, and 25 to give it back!
|If we don't need the OS, we can just use the
memory ourselves without asking...
But if we want it to do Disk reading or other things for us, we need to play nice!
It all depends on how you plan to use the system, and how worried you are about compatibility.
|Remember how limited our bank swapping options were on
the CPC and SPECCY?
Remember how we wished we had a memory mapper on all MSX2?
Well, the Enterprise really delivers on this front!
Just like the other systems, the memory map is split into 16k banks... But any 16k segment can be mapped into any one these banks... you can even have the same segment in two banks!
So far as I've seen, the memory mapping ability of the Enterprise is second to none in the Z80 range... even the Sam Coupe is heavily limited in comparison!
Each chunk of memory uses a different port... and we just OUT the segment number to that port to change the active segment in that memory range...
For example, if we want to set &C000-&FFFF to segment &F3 we just do...
it couldn't be easier!
for the enterprise 'faked' the CPC bankswapping, only paging the extra
ram into &4000...
The Enterprise's Screen and Ram compatibility with the CPC made Porting ChibiAkumas to the EP128 easy!
Even the sprites were the same!... It would have been harder if the game used Mode 0, however, because the last 8 colors are more limited in Enterprise 16 color mode.
|In our tutorials,
taken 1 segment for the &8000 range, one for the VRAM, and the
system will use two for the Zero and System segments...
That means we should be able to request 4 full banks on a 128k machine, or 12 on a 256k machine... on a 64k machine we'll be able to get no full banks (just the partial segment)
We'll keep requesting banks until the OS says NO, and we'll know how much ram the system has.
If we want we can then keep all the banks we requested, or we can free them back to the operating system.
the 'DontFreeMemory' symbol will stop this example giving the memory
back to the OS, but it doesn't store it anywhere... you'd probably want
to add an extra command to do that!... if we don't we'll have to
'guess' what banks were free!
In theory, on a 128k machine, banks F9-FE will be free for our use (and FC will have been allocated to our screen)... but who knows, maybe the user has some weird upgrade rom that's using one... you've just got to ask yourself... 'do I feel lucky?'
- Bankswitching and hardware detection on the Sam Coupe
The Sam Coupe has an incredible spec for a 8-bit system, with a MINIMUM of 256k, and many machines having a whopping 512k... the SAM is a memory monster!
Unfortunately.... it doesn't have as flexible memory mapping as the Enterprise... lets find out why!
|&FA - 250||LMPR||Low Memory Page Register||WHLBBBBB||B=Bank &0000-&7FFF... L=Low rom off (1=off)... H=High rom on (1=on)… W=Write protect &0000-&3FFF|
|&FB - 251||HMPR||High Memory Page Register||MCCBBBBB||B=Bank &8000-&FFFF... C=mode 3 color lookup... M=use external memory expansion|
|&FC - 252||VMPR||Video Memory Page Register||OMMBBBBB||B=Video Bank... M=Mode... O=midi Io|
need to swap banks in 32k chunks it makes it harder to uses memory
efficiently on the SAM... if we page in the screen into one bank, and
our sprites into the other... we have nowhere left for our code!
We will have to work out another way to organize our memory... for example storing a copy of our sprite handling code in the spare memory of the screen bank (the screen uses 24k of the 32k bank)
|We're going to
commands for setting the lower ram bank
is because our code is in the other bank at &8000!
We'll use SetCurrent to set the bank - and we'll create Reset command to restore the bank we set with SetCurrent... we need this for a good reason!
|Our program code is
in the &8000 range... and because we
need to page in the 24k screen... we have to do this at the
&0000-&7FFF range... but we're also going to use this
for our extra ram.
We're going to have to modify our bitmap code, to use the reset command to restore whatever bank we asked for with SetCurrent rather than turn the rom back on.
|To test if we've
got 512k of
memory, we'll use the same procedure as before... bank 31 is the top
bank in the 512k range - we'll try to page it in and write to it...
If the write occurs in our current bank, then we don't have 512k.
- Hardware detection and Bank Switching on the Gameboy/GBC and Sega
It's time to move onto the consoles, lets see how we can detect the platform we're on, and how to use extra rom banking on those systems
|Bank Switching and hardware detection on the Gameboy and GBC|
|Joy||FF00||P1/JOYP - Joypad (R/W)||--BD3210||B=Buttons D=Direction 3210=buttons DULR SSBA|
|CPU||FF4D||KEY1 - CGB Mode Only - Prepare Speed Switch||C------P||C=Current speed P=prepare switch|
|RAM||FF70||SVBK - CGB Mode Only - WRAM Bank (bits 0-2 =0-7)|
|INT||FFFF||IE - Interrupt Enable (R/W)||---JSTLV||J=Joypad S=Serial T=Timer L=Lcd stat V=vblank|
|Banking in the extra GBC ram is super easy!
we just write a number from 1-7 to memory address &FF70... the bank will be paged in to memory address &D000-DFFF
Note if you try to page in "Bank 0" you will get "Bank 1"... there is no bank 0!
|ROM banking is also easy, we just need to write a
to &2000, this will page a rom bank number into area
Note, we also write a 0 to address &6000 - this is to tell the hardware our banker is an ALL ROM banker - and has no RAM
|Gameboy cartridges can also have 32k of extra RAM! This will appear in
the area &A000-&BFFF as 4x 8k pageable banks! Just like the ROM,
it's very easy to use...
first we write 1 to address &6000 to tell the system we have RAM
Then we enable the RAM bank by writing &0A to &0000 - we turn it off by writing &0 to &0000
Finally, we can select the bank number (0-3) by writing to &4000
The Rambank will be available between &A000-&BFFF
sure you turn of cartridge RAM when you're not using it... if the
player is mean, they may turn off the gameboy at a strange time... and
the battery backed up ram could get corrupted >:(
|Turning on 8mhz mode of the Gameboy Color is also
First we have to disable interrupts by writing 0 to &FFFF, then we have to set bits 4,5 of the Joystick line (not sure why... but we do!) at address &FF00
Then we make a 'Prepare speed change' request to port &FF4D
Finally we need to STOP the processor... when it restarts we will be in 8mhz mode!
turn the fast cpu on on a Gameboy seems to have no effect.... but
probably isn't a good idea... so detect the hardware first, and don't
do it on a regular GB!
|Bank Switching on the Sega Mastersystem and GameGear|
|We're not going to try to detect if we're running
the SMS or
Gamegear - as there is no real purpose... we're building for the two
systems separately, so any hardware differentiation should be done with
conditional compilation (IFDEF statements)
We can, however use extra cartridge ROM, and up to 32k of extra RAM contained in the cartridge!
|Writing a value to addresses &FFFD-FFFF of 0 or above will page that ROM bank into the appropriate address range.|
|Address &FFFC controls the extra 'in cartridge' ram bank... it has two banks of 16k ram... these can be paged in to the &8000-&BFFF range|
SUPPOSED to be possible to bank the cartridge RAM into the shadow RAM
are of the addressible range, by writing %00011100... however it doesn't seem to work!
It seems it may not actually be supported by the emulator, as if appears no commercial games actually use it.
| Lesson P30 - Hardware Sprites on the gameboy
The Gameboy has 'Hardware sprites'... these are drawn by the graphics hardware, so are very fast, but are limited in size, and number.
|FF40||L||w||W||T||M||s||S||B||B= Background Display (CGB only) S=Sprite Enable s= sprite size (8x16) M=tileMap address (0=9800-9BFF 1=9C000-9FFF)|
|T=Tile Data (0=8800-97FF 1=8000-8FFF) W= Window Enable w=window tilemap (0=9800-9BFFF 1=9C000-9FFF) L= Lcd enable|
|Sprite Data is stored from &FE00 onwards, it can ONLY be accessed during Vblank
there are 40 sprites, and each definition uses 4 consecutive bytes...
For Example, Sprite 0's bytes are highlighted in black... it has a Y co-ordinate, an X co-ordinate, a Tile Number, and tile Attributes
Y and X are offset, so you can have a sprite partially off the screen, You need to set XY to (8,16) to get the top corner of the screen (0,0)
the 'Sprite number' refers to a pattern within the tile definitions in memory area &8000-&8FFF - Sprites bitmap data is in the same format as the tilemap...
In 8x16 mode is enabled, then the tiles are used in pairs - the bottom bit is unused, and odd numbered tiles are ignored
Bits 7-4 work on both GB and GBC
On the Gameboy, we can only use palette 1 or 0.... on the color gameboy we can use palette 0-7
Note: Color 0 is always transparent on sprites
Tile-Sprite prority allows us to put Sprites BEHIND the background...
|You can't write to Sprite ram outside VBLANK - which is a total pain!
Fortrunately the Gameboy has a DMA (Direct Memory Access)... this is where the hardware automatically copies an area of normal ram into Sprite ram - it's automatic and super-fast... so it can copy a 'cache' of the sprite data to the real sprite ram in vblank!
|Because we can only change sprites during Vblank, we need to set up a DMA interrupt to do the job for us.
We can turn on VBLANK by setting Bit 0 of &FFFF and Enabling interrupts with EI.... when Vblank occurs, a call will be made to memory address &0040 (64)
The gameboy DMA is desinged to copy sprites, we just write the High byte of the source address to &FF46 - the transfer will occur ... we need to wait a bit for it to finish...
HOWEVER.. during the DMA only the memory &FF80-&FFFE is accessible...
So we need to copy some code to &FF80 to run the dma, and Jump to &FF80 at address &0040
Finally we need to turn on the Vblank interrupt , and Enable Interrupts
|Address of Cache and Vblank handler in High Ram (&FF80-FFFE)
Copy the interrupt handler to &FF80
Jump to interrupt handler at &0040
Enabling Vblank and interrupts:
The DMA execution routine we copy to &FF80
|You can try to use a 'Wait command' before writing to the sprite memory instead of using DMA - but since you can't write to Vram out of Vblank it will be difficult|
|Now we have the Cache for the Sprite data, we can actually set up the sprites, we'll use a little routine to do this for us!
We'll pass the X,Y co-ordinates in BC ... the tile number of the pattern data for the sprite in E, and the attributes of the tile (color etc) in H... and the tile number in A
Because each sprite has 4 bytes - we need to multiply A by 4 - and add it to the starting address of the sprite data... then we set the other 4 bytes for the various attributes
|We use this function to position 4 sprites onscreen, 8 pixels apart!|
|We've used 2x2 sprites (4 in total) to make a 'crosshair' 16x16 pixels.
Note: you can use AkuSprite Editor to create GB tiles - which are the same format as the sprite data.