Section | Addr | Name | Bits | Bit Meaning |
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) |
Sound | FF30
� FF3F |
Wave Pattern RAM | HHHHLLLL | 32 4 bit samples |
As
always,
we just need to write our data to the memory addresses
between &FF10-&FF3F to set the sound
options...Whatever you're trying 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
and &FF19 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 options, 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 works ok! If you want to play digital wave files, check out this tutorial! |
![]() |
The Noise Channel has a lot in common with the Tone Channels 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 easily 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! |
![]() |
Bits | |||||||||
Command | Bit Details | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
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 |
![]() |
Whatever
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 will vary... 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 / Megadrive 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! |
![]() ![]() ![]() |
Chibisound
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 ArkosPlayer! Then again, we'd probably want to use the YM2612 sound chip instead! |
![]() |
![]() |
Not
impressed by a few beeps... Well Fine! Check out this
tutorial, It'll teach you how to play wave files and digital
sample |
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! |
![]() |
![]() |
Really, 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 random data! On the speccy Chibisound uses the R register... on the Apple, it uses the sourcecode of the program itself as a pseudorandom 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 more rough! |
Ever heard
the speech samples on those old games? you can do that too! Playing wave files is pretty easy... see here |
![]() |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Name | Bit meanings |
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! | ||||||||||||||
Area | C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9* | CA | CB* | CC | CD | CE | CF | D0-FF |
&0000 | RAM_0 | RAM_0 | RAM_4 | RAM_0 | RAM_0 | RAM_0 | RAM_0 | RAM_0 | RAM_0 | RAM_0* | RAM_8 | RAM_0* | RAM_0 | RAM_0 | RAM_0 | RAM_0 | ... |
&4000 | RAM_1 | RAM_1 | RAM_5 | RAM_3 | RAM_4 | RAM_5 | RAM_6 | RAM_7 | RAM_1 | RAM_1* | RAM_9 | RAM_3* | RAM_8 | RAM_9 | RAM_10 | RAM_11 | ... |
&8000 | RAM_2 | RAM_2 | RAM_6 | RAM_2 | RAM_2 | RAM_2 | RAM_2 | RAM_2 | RAM_2 | RAM_2* | RAM_10 | RAM_2* | RAM_2 | RAM_2 | RAM_2 | RAM_2 | ... |
&C000 | RAM_3 | RAM_7 | RAM_7 | RAM_7 | RAM_3 | RAM_3 | RAM_3 | RAM_3 | RAM_3 | RAM_11 | RAM_11 | RAM_11 | RAM_3 | RAM_3 | RAM_3 | RAM_3 | ... |
We've covered it before, but remember, by default The CPC+
features are disabled, leaving us with 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: ld bc,&7fb8 out (c),c To turn it off we use: ld bc,&7fa0 out (c),c 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 |
![]() |
To detect
a CPC Plus, we send the PLUS sequence, and turn on the ASIC
ram.. .if nothing happened we don't have a CPC+... we can
use this fact to detect a CPC! |
![]() |
![]() |
While 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 feature... 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! |
![]() |
Lesson
P25
-
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 annoying! It's probably 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 |
Subslots | ||||
Slot 0 | 0-0 | 0-1 | 0-2 | 0-3 |
Slot 1 | 1 | Unexpanded slot | ||
Slot 2 | 2-0 | 2-1 | 2-2 | 2-3 |
Slot 3 | 3-0 | 3-1 | 3-2 | 3-3 |
ROM | |
RAM | |
Cartridge | |
Empty |
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! |
![]() |
Slot | Address |
1 | &FCC1 |
2 | &FCC2 |
3 | &FCC3 |
4 | &FCC4 |
Port | Z80 Address range | Default value |
&FC | &0000-&3FFF | 3 |
&FD | &4000-&7FFF | 2 |
&FE | &8000-&BFFF | 1 |
&FF | &C000-&FFFF | 0 |
![]() |
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 |
1 | &4000-&5FFF | &5000 |
2 | &6000-&7FFF | &7000 |
3 | &8000-&9FFF | &9000 |
4 | &A000-&BFFF | &B000 |
System | MSX1 | MSX2 | MSX2+ | Turbo-R |
Memory | 16k | 64k | 64k | 256K |
Disk System? | NO | NO (sometimes) | NO (usually - not always) | YES |
Mapper? | NO | NO | NO (sometimes) | 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...
although 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! |
![]() |
The Code
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 &002D
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 MSX!
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 |
![]() |
![]() |
Lesson
P26
-
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 |
Dark=Contended |
Bits | ||||||||||
Port | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bit meanings | Backup |
&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 &C000... 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! |
![]() |
Bits | ||||||||||
Port | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Bit meanings | Backup |
&1FFD | - | - | - | S | D | O | O | P | P = paging mode 1=+3 model O=Bank Option D = Disk Motor S=Printer strobe | &5B67 |
Bank | Option
0 |
|
Option
1 |
|
Option
2 |
|
Option
3 |
&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 |
![]() |
Note, 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 expect... 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 problems! 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 bank swap on the 128k for some reason! |
![]() |
![]() |
Note, this 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! |
![]() |
Lesson
P27
-
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 whatever we
want... if we don't need it any more, we can tell the OS to take
it back! 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... ld a,&F3 out (&B3),a it couldn't be easier! |
|
![]() |
ChibiAkumas for the
enterprise 'faked' the CPC bank swapping, 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, we've already
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. |
![]() |
Setting
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?' |
![]() |
![]() |
Lesson
P28
-
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! |
![]() |
![]() |
![]() |
Port | Name | Description | Bits | Bit Meaning |
&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 |
Range |
Page Addr | Example |
&0000 | (&250) | 0 |
&4000 | (&250)+1 | 1 |
&8000 | (&251) | 10 |
&C000 | (&251)+1 | 11 |
![]() |
Because we 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 define some
commands for setting the lower ram bank
(&0000-&7FFF)... this 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. |
![]() |
![]() ![]() ![]() ![]() |
Lesson
P29
-
Hardware detection and Bank Switching on the Gameboy/GBC and
Sega Mastersystem/GameGear 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 |
Section | Addr | Name | Bits | Bit Meaning |
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 value to
&2000, this will page a rom bank number into area
&4000-&7FFF 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 |
![]() |
Make 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 pretty easy... 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! |
![]() |
![]() |
Trying to
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 on 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 | ![]() |
It's
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. |
![]() |
![]() |
![]() |
Bit | |||||||||
Address | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Meaning |
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 Attributes:
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. |
![]() ![]() |