Learn Multi
platform Z80 Assembly Programming... With
Vampires!
Platform Specific Lessons
<- Back to the Main
Contents & Basic Z80 Assembly Lessons
Platform Specific Series - Lets learn how the hardware of the
systems work, so we can get it to do what we want... Covers Amsrad
CPC,MSX,ZX Spectrum, TI-83,Enterprise 128/64 and Sam Coupe!
|
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-3-8910 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 |
|
Lesson
P27
- Bankswitching and hardware detection on the Enterprise |
|
Lesson
P28
- Bankswitching and hardware detection on the Sam Coupe |
|
Lesson
P29
- Hardware detection and Bank Switching on the Gameboy/GBC and
Sega Mastersystem/GameGear |
|
Lesson
P30
- Hardware Sprites on the gameboy |
|
Lesson
P31
- Hardware Sprites on the Master System / Game Gear and MSX1! |
|
Lesson
P32
- Hardware Sprites on the CPC+ |
|
Lesson
P33
- Bitmap Graphics on the Camputers Lynx |
|
Lesson
P34
- Sound and Keyboard on the Camputers Lynx |
|
Lesson
P35
- Playing Digital Sound with WAV on the AY-3-8910! |
|
Lesson
P36
- Playing Digital Sound with WAV on the CPC+ via DMA! |
|
Lesson
P37
- Playing Digital Sound with WAV on the Sam Coupe, Camputers Lynx
and ZX Spectrum |
|
Lesson
P38 - Playing Digital Sound with WAV on the Sega
MasterSystem/GameGear, Elan Enterprise and GameBoy/GBC |
|
Lesson
P39 - Setting the CPC screen with CRTC registers |
|
Lesson
P40 - Syncronized mode switches for 320x200 @ 16 color EGX
graphics on the Amstrad CPC |
|
Lesson
P41 - CRTC Rupture for Interrupt based splitscreen on the CPC |
|
Lesson
P42 - Advanced CRTC Rupture |
|
Lesson
P43- ULANext on the Spectrum NEXT |
|
Lesson
P44- Enhancements to the Classic ULA and Low Res Mode
(Radasjimian) |
|
Lesson
P45 - 256 color mode on the Elan Enterprise |
|
Lesson
P46- Tilemap on the Spectrum NEXT |
|
Lesson
P47- Using 16 color Mode 0 to simulate 2x 4 color Layers |
|
Lesson
P48 - All MSX2 Bitmap Commands - Part 1/2 |
|
Lesson
P49 - All MSX2 Bitmap Commands - Part 2/2 |
|
Lesson
P50 - Alternative Bitmap modes... HighRes, 256 color, YJK (MSX2+)
and Interlaced! |
|
Lesson
P51 - Window - Tilemap Scrolling - Alt Tile Pattern addresses and
Interrupts |
|
Lesson
P52 - MSX1 mode G2 for 768 onscreen tiles |
|
Lesson
P53 - Realtime Sprite Flipping on the Amstrad CPC |
|
Lesson
P54 - Transparency on Amstrad CPC software sprites |
|
Lesson
P55 - LightGun Reading on the Sega Master System |
|
Lesson
P56 - Pixel Plotting on the Amstrad CPC |
|
Lesson
P57 - Stereoscopic 3D on the SegaMasterSystem with the Segascope
3D Glasses |
|
Lesson
P58 - Modes 4, 3, 2 and 1 on the Sam coupe |
|
Lesson
P59 - Hardware scrolling on the MSX 1/2/2+ |
|
Lesson
P60 - Tape loading on the Amstrad CPC (5K subs special) |
|
Lesson
P61 - 2x and 4x sprite scaling with Lookup Tables on the Amstrad
CPC |
|
Lesson
P62 - AMX Mouse reading on the Amstrad CPC |
|
Lesson
P63 - Kempson Mouse reading on the ZX Spectrum + SpecNEXT |
|
Lesson
P64 - Mouse reading on the MSX |
|
Lesson
P65 - Mouse reading on the Sam Coupe |
|
Quadtree
video on the CPC (10k special) - Part 1/2 - Lesson P66 [CPC] |
|
Quadtree
video on the CPC (10k special) - Part 2/2 - Lesson P67 [CPC] |
|
Lesson
P68 - RLE+Bitplanes for lossless compression on the CPC [CPC] |
|
Lesson
P69 - ChibiSound PRO on the CPC, MSX and Spectrum (128 - AY) [CPC]
[MSX] [ZXS] |
|
Lesson P70
- Sound on the SMS/GG (ChibiSound Pro) [SMS] [GG] |
|
Lesson
P71 - Sound on the Elan Enterprise! (ChibiSound Pro) [ENT] |
|
Lesson
P72 - Sound on the ZX Spectrum (Beeper) [ZXS] |
|
Sound
on the Gameboy - Z80 Lesson P73 (ChibiSound Pro) [GMB] |
|
Lesson
P74 - Sound on the SAM Coupe [SAM] |
|
Sound
on the Camputers Lynx(Beeper) - Z80 ASM Lesson P75 [CLX] |
|
Lesson
P31
-
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)
|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
&3F00 |
Y-0 |
Y-1 |
Y-2 |
Y-3 |
Y-4 |
Y-5 |
Y-6 |
Y-7 |
Y-8 |
Y-9 |
Y-10 |
Y-11 |
Y-12 |
Y-13 |
Y-14 |
Y-15 |
&3F10 |
Y-16 |
Y-17 |
Y-18 |
Y-19 |
Y-20 |
Y-21 |
Y-22 |
Y-23 |
Y-24 |
Y-25 |
Y-26 |
Y-27 |
Y-28 |
Y-29 |
Y-30 |
Y-31 |
&3F20 |
Y-32 |
Y-33 |
Y-34 |
Y-35 |
Y-36 |
Y-37 |
Y-38 |
Y-39 |
Y-40 |
Y-41 |
Y-42 |
Y-43 |
Y-44 |
Y-45 |
Y-46 |
Y-47 |
&3F30 |
Y-48 |
Y-49 |
Y-50 |
Y-51 |
Y-52 |
Y-53 |
Y-54 |
Y-55 |
Y-56 |
Y-57 |
Y-58 |
Y-59 |
Y-60 |
Y-61 |
Y-62 |
Y-63 |
&3F40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&3F50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&3F60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&3F70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
&3F80 |
X-0 |
N-0 |
X-1 |
N-1 |
X-2 |
N-2 |
X-3 |
N-3 |
X-4 |
N-4 |
X-5 |
N-5 |
X-6 |
N-6 |
X-7 |
N-7 |
&3F90 |
X-8 |
N-8 |
X-9 |
N-9 |
X-10 |
N-10 |
X-11 |
N-11 |
X-12 |
N-12 |
X-13 |
N-13 |
X-14 |
N-14 |
X-15 |
N-15 |
&3FA0 |
X-16 |
N-16 |
X-17 |
N-17 |
X-18 |
N-18 |
X-19 |
N-19 |
X-20 |
N-20 |
X-21 |
N-21 |
X-22 |
N-22 |
X-23 |
N-23 |
&3FB0 |
X-24 |
N-24 |
X-25 |
N-25 |
X-26 |
N-26 |
X-27 |
N-27 |
X-28 |
N-28 |
X-29 |
N-29 |
X-30 |
N-30 |
X-31 |
N-31 |
&3FC0 |
X-32 |
N-32 |
X-33 |
N-33 |
X-34 |
N-34 |
X-35 |
N-35 |
X-36 |
N-36 |
X-37 |
N-37 |
X-38 |
N-38 |
X-39 |
N-39 |
&3FD0 |
X-40 |
N-40 |
X-41 |
N-41 |
X-42 |
N-42 |
X-43 |
N-43 |
X-44 |
N-44 |
X-45 |
N-45 |
X-46 |
N-46 |
X-47 |
N-47 |
&3FE0 |
X-48 |
N-48 |
X-49 |
N-49 |
X-50 |
N-50 |
X-51 |
N-51 |
X-52 |
N-52 |
X-53 |
N-53 |
X-54 |
N-54 |
X-55 |
N-55 |
&3FF0 |
X-56 |
N-56 |
X-57 |
N-57 |
X-58 |
N-58 |
X-59 |
N-59 |
X-60 |
N-60 |
X-61 |
N-61 |
X-62 |
N-62 |
X-63 |
N-63 |
|
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
|
|
The SMS
sprites can be read from pattern data, or from a separate
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.
Sprite patterns are held at
&3800 - there is enough ram for 64 sprite patterns
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:
There is a special exporter in AkuSprite editor called
'Save RAW MSX1 16x16 Sprite'
|
Sprite Attributes:
Address |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
C |
D |
E |
F |
&1B00 |
Y-0 |
X-0 |
P-0 |
C-0 |
Y-1 |
X-1 |
P-1 |
C-1 |
Y-2 |
X-2 |
P-2 |
C-2 |
Y-3 |
X-3 |
P-3 |
C-3 |
&1B10 |
Y-4 |
X-4 |
P-4 |
C-4 |
Y-5 |
X-5 |
P-5 |
C-5 |
Y-6 |
X-6 |
P-6 |
C-6 |
Y-7 |
X-7 |
P-7 |
C-7 |
&1B20 |
Y-8 |
X-8 |
P-8 |
C-8 |
Y-9 |
X-9 |
P-9 |
C-9 |
Y-10 |
X-10 |
P-10 |
C-10 |
Y-11 |
X-11 |
P-11 |
C-11 |
&1B30 |
Y-12 |
X-12 |
P-12 |
C-12 |
Y-13 |
X-13 |
P-13 |
C-13 |
Y-14 |
X-14 |
P-14 |
C-14 |
Y-15 |
X-15 |
P-15 |
C-15 |
&1B40 |
Y-16 |
X-16 |
P-16 |
C-16 |
Y-17 |
X-17 |
P-17 |
C-17 |
Y-18 |
X-18 |
P-18 |
C-18 |
Y-19 |
X-19 |
P-19 |
C-19 |
&1B50 |
Y-20 |
X-20 |
P-20 |
C-20 |
Y-21 |
X-21 |
P-21 |
C-21 |
Y-22 |
X-22 |
P-22 |
C-22 |
Y-23 |
X-23 |
P-23 |
C-23 |
&1B60 |
Y-24 |
X-24 |
P-24 |
C-24 |
Y-25 |
X-25 |
P-25 |
C-25 |
Y-26 |
X-26 |
P-26 |
C-26 |
Y-27 |
X-27 |
P-27 |
C-27 |
&1B70 |
Y-28 |
X-28 |
P-28 |
C-28 |
Y-29 |
X-29 |
P-29 |
C-29 |
Y-30 |
X-30 |
P-30 |
C-30 |
Y-31 |
X-31 |
P-31 |
C-31 |
Address |
Sprite |
Bits |
Details |
$1B00 |
0 |
XXXXXXXX |
X=Xpos |
$1B01 |
0 |
YYYYYYYY |
Y=Ypos |
$1B02 |
0 |
PPPPPPPP |
P=Patternnum |
$1B03 |
0 |
E---CCCC |
E=Extended
X C=Color |
$1B04 |
1 |
XXXXXXXX |
X=Xpos |
$1B05 |
1 |
YYYYYYYY |
Y=Ypos |
$1B06 |
1 |
PPPPPPPP |
P=Patternnum |
$1B07 |
1 |
E---CCCC |
E=Extended
X C=Color |
.. |
.. |
.. |
.
. |
$1BFD |
31 |
XXXXXXXX |
X=Xpos |
$1BFC |
31 |
YYYYYYYY |
Y=Ypos |
$1BFE |
31 |
PPPPPPPP |
P=Patternnum |
$1BFF |
31 |
E---CCCC |
E=Extended
X C=Color |
|
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.
|
Reg |
Meaning |
Bit7 |
Bit6 |
Bit5 |
Bit4 |
Bit3 |
Bit2 |
Bit1 |
Bit0 |
|
R#1 |
mode register
#1 |
0 |
BL |
IE0 |
M1 |
M2 |
0 |
SI |
MAG |
SI=16x16
sprites / MAG = Big Sprites |
R#5 |
sprite
attribute table (LOW) |
A14 |
A13 |
A12 |
A11 |
A10 |
A9 |
A8 |
A7 |
|
R#6 |
sprite pattern
generator table |
0 |
0 |
A16 |
A15 |
A14 |
A13 |
A12 |
A11 |
|
R#8 |
mode register
#2 |
MS |
LP |
TP |
CB |
VR |
0 |
SPD |
BW |
SPD=SPrite
Disable |
|
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! |
|
|
The MSX 2 is
capable of much more powerful sprites, and sprites can be
bit-combined to allow a lot of colors! - 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!
|
|
Lesson
P32
-
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!
|
|
|
|
|
ASIC Ram registers that define CPC+
Sprites
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
Resolution Bits |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Meaning |
- |
- |
- |
- |
X |
X |
Y |
Y |
X=
Xmagnification YY=
Ymagnification |
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
|
Sprite
Number |
DataAddr (256bytes) |
Xpos (2bytes) |
Ypos (2bytes) |
Resolution (1byte) |
1 |
&4000 |
&6000 |
&6002 |
&6004 |
2 |
&4100 |
&6008 |
&600A |
&600C |
3 |
&4200 |
&6010 |
&6012 |
&6014 |
4 |
&4300 |
&6018 |
&601A |
&601C |
5 |
&4400 |
&6020 |
&6022 |
&6024 |
6 |
&4500 |
&6028 |
&602A |
&602C |
7 |
&4600 |
&6030 |
&6032 |
&6034 |
8 |
&4700 |
&6038 |
&603A |
&603C |
9 |
&4800 |
&6040 |
&6042 |
&6044 |
10 |
&4900 |
&6048 |
&604A |
&604C |
11 |
&4A00 |
&6050 |
&6052 |
&6054 |
12 |
&4B00 |
&6058 |
&605A |
&605C |
13 |
&4C00 |
&6060 |
&6062 |
&6064 |
14 |
&4D00 |
&6068 |
&606A |
&606C |
15 |
&4E00 |
&6070 |
&6072 |
&6074 |
16 |
&4F00 |
&6078 |
&607A |
&607C |
|
|
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! |
Defining a Sprite
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 |
|
CPC+ 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!
|
|
Positioning a Sprite
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. |
|
Using our routines!
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! |
|
Want
more
than 16 sprites? Well, it's possible if you're REALLY clever
- you can re-position the sprites while the screen is
drawing, and you will effectively see them twice... in
ChibiAkumas 2 player mode the Heart+Scroll sprites were
re-positioned 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! |
|
|
Lesson
P33
-
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! |
|
CamputersBitmapTest.asm |
|
|
The
Mysteries
of the LynxAddress bus!
The Lynx does not use all the bits of the address bus for accessing
memory... I guess they figured that as the machine had only 16k, that
using all the 'bits' of the memory address were excessive... two bits
are unused... and that means certain areas are 'Mirrored'... so the
same bank appears in multiple places!
Lets take a look at which of the 16 bits are ignored! ... in the
chart below, bits marked X are ignored, and bits marked 1 work
normally.
F |
E |
D |
C |
B |
A |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
X |
1 |
X |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
What's the effect of this? well, because on the 48k lynx 32k is
VRAM... this just leaves 2x 8k banks of ram... and each of these 2
banks appear in 4 different positions!
8k ram bank |
Seen at Addresses |
A |
&0000-&1FFF
&2000-&3FFF
&8000-&9FFF
&A000-&BFFF |
B |
&4000-&5FFF
&6000-&7FFF
&C000-&DFFF
&E000-&FFFF |
The extra 4 Video Ram banks are 8k each... One for Red, Green and
Blue... and an 'alternate green' which can be paged in
There is also 24k ROM which can be paged in... these appear 'Above'
the ram mentioned previously... and these do not apply to the rom
banking
Lets take a look at the bank switching options
Bank |
&0000
-
&1FFF |
&2000
-
&3FFF |
&4000
-
&5FFF |
&6000
-
&7FFF |
&8000
-
&9FFF |
&A000
-
&BFFF |
&C000
-
&DFFF |
&E000
-
&FFFF |
Ram
amount
(128k+) |
Purpose |
0 |
A
/
Rom1 |
A
/
Rom2 |
B
/
4kRom |
B |
A |
A |
B |
B /
ExtRom |
20K |
Accessing
Rom |
1 |
Ram
|
Ram
|
Ram
|
Ram B
|
Ram A |
Ram |
Ram
|
Ram
|
64K |
Accessing
Ram
|
2 |
A |
A |
B |
B |
A |
A /
Blue |
B /
Red |
B |
16K |
Accessing
Red/Blue Vram |
3 |
A |
A |
B |
B |
A |
A /
AltGreen |
B /
Green |
B |
16K |
Accessing
Green Vram |
4 |
Off Board Expansion |
|
BankSwitching
The bits of the byte written to port &FFFF will control which
banks are accessed ... the combination of the bits selected will
define the resultant memory accessed by the Z80
Port
&FFFF
Bits |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Meaning |
Read
Bank 4 |
Read
Bank 2 / 3 |
Read
Bank 1 |
Read
Bank 0 |
Write
Bank 4 |
Write
Bank 3 |
Write
Bank 2 |
Write
Bank 1 |
Purpose |
|
Read
Vram |
|
Read
ROM |
|
Write Green /
AltGreen Vram |
Write
Red/Blue Vram |
Write
16k RAM |
VRAM Access
The Video Ram is accessible via the Z80 addressable range, but we need
to page in the correct bank AND set the right video option...
This is done with Port &0080
Port
&0080
Bits |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Meaning |
0 |
VSYNC |
CPU
Access |
Show Green
/Altgreen
|
Lock
Bank3 |
Lock
Bank2 |
0 |
0 |
Purpose |
|
Wait for
next redraw
|
Allow Vram
Access |
Pageflipping
1=Alt |
Write Red /
Blue Vram |
Write Green /
AltGreen Vram |
|
|
To write to the banks, we need to enable CPU access... and Lock the
bank we DONT want to write to.
Note... you cannot Read and Write to a bank at the same time... so you
can't copy one area of the screen to another to soft-scroll!
Also it is virtually impossible to READ VRAM from code not in ROM...
this is because the Lynx's limited address bus means it can only
address 16k, and when READ is set to the VRAM the normal ram
disappears!... this means the running code would have to be in VRAM
(thus visible) which in most cases is impractical!
See the table below for the correct settings depending what you want
to do
Mode
|
Color |
Out
(&FFFF) |
Out
(&0080) |
Address
Range |
Read |
Red |
&60 |
&28 |
&C000-&DFFF |
Blue |
&60 |
&28 |
&A000-&BFFF |
Green |
&60 |
&24 |
&C000-&DFFF |
AltGreen |
&60 |
&24 |
&A000-&BFFF |
|
|
|
|
|
Write |
Red |
&03 |
&28 |
&C000-&DFFF |
Blue |
&03 |
&28 |
&A000-&BFFF |
Green |
&05 |
&24 |
&C000-&DFFF |
AltGreen |
&05 |
&24 |
&A000-&BFFF |
|
|
|
|
|
OFF |
|
&0 |
&0 |
|
To speed things up if we just want to do black and white, We can take
advantage of the Lynx write banking to write to both Blue+AltGreen, or
Red+Green at the same time .... note it is not possible to write to
Red+Green+Blue at the same time
Mode
|
Color |
Out
(&FFFF) |
Out
(&0080) |
Address
Range |
Write |
Red+Green |
&07 |
&20 |
&C000-&DFFF |
Blue+AltGreen |
&07 |
&20 |
&A000-&BFFF |
Screen corruption
due to shared Vram
Note: Because of the limited address bus, writing to the areas B or A
in &8000-&9F80 or &6000-&7F80 will affect the
matching screen ram... meaning random pixels may light up!
Even worse, it seems these writes do not affect the internal ram even
if bit 0 of &FFFF is set... meaning the write is 'consumed' by the
screen
Because the stack is at &8FFF, it seems to be outside the visible
screen on the PALE emulator... but inside the visible screen on JYNX
!?!
Therefore, ideally, we should avoid using the stack too!
Paging
in
the VRAM
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!
|
|
Printing
Characters
to the screen
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?!
|
|
Get
Screen position
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 bit-planes, The Camputers screen has 8
pixels per byte, and the screen is 32 bytes wide...
Therefore we need to multiply the Ypos *32 |
|
Drawing
a bitmap to the screen
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 |
|
|
This 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.
|
|
Lesson
P34
-
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! |
|
|
|
|
Reading the Keyboard
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
|
|
7
|
6 |
5
|
4
|
3
|
2
|
1
|
0
|
0000 |
Shift |
Escape |
Down |
Up |
ShiftLK |
|
|
1 |
0001 |
|
|
C |
D |
X |
E |
4 |
3 |
0010 |
|
Control |
A |
S |
Z |
W |
Q |
2 |
0011 |
|
|
F |
G |
V |
T |
R |
5 |
0100 |
|
|
B |
N |
Space |
H |
Y |
6 |
0101 |
|
|
J |
|
M |
U |
8 |
7 |
0110 |
|
|
K |
, |
|
O |
I |
9 |
0111 |
|
|
; |
|
. |
L |
P |
0 |
1000 |
|
|
: |
|
/ |
I |
@ |
- |
1001 |
|
|
Right |
|
Return |
Left |
] |
Delete |
|
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 Multiplatform Code (Click
Here) to convert raw data to letter keys (if we
want) |
|
The Beeper Speaker
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*
Bits |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Detail |
0* |
- |
Volume |
* Bit 7 MUST be zero
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! |
|
|
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 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 |
|
|
Lesson
P35
-
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!
|
|
|
|
Wave files and what we need for 8 bit machines
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:
|
Creating a 16 bit wave file
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 |
|
|
The
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!
|
Converting a wave file for playing on an 8 bit
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!
|
|
Wave Playback on the AY
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 consistent 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
self-modifying 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 self-modded 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! |
|
|
On some
AY systems, It's possible to play multiple samples at
different volumes to increase 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! |
|
|
Lesson
P36
-
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!
|
|
|
|
|
The DMA CPC+ Registers
The CPC+ has three DMA 'Channels'... each reads in one 2-byte (one
command) word every 'tick' at a frequency of 15khz... there are 3
channels to allow all 3 AY channels to be changed at the same time on
a 'tick'... however it is perfectly possible to use DMA channel 0 to
change AY channel 1(B) or 2(C)
Every channel has a DMA address and a Prescaler
The DMA address is a pair of bytes which point to the sound data -
which must be in the MAIN 64k of memory - it cannot be in ROM or 128k
ram. it must also be on an EVEN BYTE - so put an ALIGN 2 before the
data
There is a 'Prescaler' this affects the PAUSE command (and only that
command) - to allow for the playback speed to be reduced....
Finally, there is a Control/Status register... Writing 1 to this will
turn a channels DMA on (starting the data transfer)... This can be
read during your interrupt handler to see what has caused an interrupt
- Bit 7=1 means Vblank just occurred...Bits 6-4=1 mean that one of the
DMA channels caused an interrupt - this can be used to 'alert' when
the playback of a sample has ended so another sample can be
queued.
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 |
|
Prescaler
Playback Speed |
NNNNNNNN |
&6C02 |
&6C06 |
&6C0A |
|
Control/Status
Register
Write:Start DMA
Read:Interrupts occured) |
W: -----210
R: V012---- |
|
|
|
&6C0F |
DMA Commands
The DMA has a very limited set of
commands - each is one word in size!
Command |
Bytes |
Purpose |
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! |
|
A complete example!
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 following these tutorials, you should understand these
commands... if not please see Lesson
P13 - we covered the CPC+ functions in that lesson
|
Converting a WAV file for DMA via ChibiWave converter
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 down-sample 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) |
|
The
Prescaler
only affects PAUSE commands - but as we added a load into
the data (as part of compression) it will probably work
OK... technically the playback speed will not be even, but
in practacle terms, there's no noticeable difference
|
|
|
Lesson
P37
-
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 |
|
|
|
|
Playing Sound Samples on the ZX
Spectrum Beeper |
|
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!
|
|
Playing Sound Samples on the Camputers
Lynx |
|
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
|
|
|
Playing Sound Samples on the Sam Coupe |
|
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
|
0
0 1 0 |
|
B
|
|
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!
|
If
you want to see some super impressive Sam Coupe sound, check
out SAM
MOD player by Stefan Drissen!
It's far better than the sound in this tutorial, it can handle
4 channels, and It's even open
source!
|
|
|
Lesson
P38
-
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!
|
|
Playing Sound Samples on the Enterprise |
|
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 |
|
|
Playing Sound Samples on the SMS/GG |
|
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 |
|
This
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!
|
|
|
Playing Sound Samples on the GB/GBC |
|
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 temporary 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 bank - as the
channel needs to be disabled when the data is updated, and
we can't detect when the buffer has been played... |
CRTC
When we want to reconfigure CRTC registers we do so by writing
to the CRTC,
The CRTC registers are selected with &BCxx (where xx is the
register number),
We then set the new value with &BDxx |
High Byte Value |
F |
E |
D |
C |
B |
A |
9 |
8
|
Name |
&BC |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
0 |
Register Select |
&BD |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
1 |
Register Write |
|
CRTC - Registers
CRTC registers will configure the Logical screen we draw
on, and the physical screen shown by the Analog monitor...
You'll want to leave many of the settings alone, as they'll just
result in a corrupt screen you can't view!... the ones that are
interesting and easy to change are in yellow,
more difficult but interesting ones are in Orange
The default value for a normal CPC screen, a 'Spectrum sized'
256x192, and Overscan screen with no border are shown!
Reg
Num |
Name |
Range |
Bits |
Default
Value |
Speccy
256x192 |
Overscan
384x272(26k) |
Details |
&00 |
Horizontal Total |
0-255 |
DDDDDDDD |
63 |
63 |
63 |
Physical
width of screen � Leave alone! |
&01 |
Horizontal Displayed |
0-255 |
DDDDDDDD |
40 |
32 |
48 |
Logical
width in Chars (8 pixels in mode 1) |
&02 |
Horizontal Sync Position |
0-255 |
DDDDDDDD |
46 |
42 |
51 |
Logical
Xpos |
&03 |
Horizontal and Vertical Sync Widths |
0-15,0-15 |
VVVVHHHH |
142 |
134 |
142 |
Physical
width of screen � Leave alone! |
&04 |
Vertical Total |
0-127 |
-DDDDDDD |
38 |
38 |
38 |
Physical
height of screen � Leave alone! |
&05 |
Vertical Total Adjust |
0-31 |
---DDDDD |
0 |
0 |
0 |
Scanline
Offset |
&06 |
Vertical Displayed |
0-127 |
-DDDDDDD |
25 |
24 |
34 |
Logical
Height in Chars (8 Pixels) |
&07 |
Vertical Sync position |
0-127 |
-DDDDDDD |
30 |
31 |
35 |
Logical
Ypos of screen |
&08 |
Interlace and Skew |
0-3 |
------DD |
0 |
0 |
0 |
0/2=off
1/3=on � Leave alone! |
&09 |
Maximum Raster Address |
0-31 |
---DDDDD |
7 |
7 |
7 |
Max
Raster Address � Leave alone! |
&0A |
Cursor Start Raster |
0-127 |
-DDDDDDD |
0 |
0 |
0 |
|
&0B |
Cursor End Raster |
0-31 |
---DDDDD |
0 |
0 |
0 |
|
&0C |
Display Start Address (H) |
0-63 |
xxPPSSOO |
00 /
16 /
32 / 48 |
00 /
16 /
32 / 48 |
12+1 /
28 /
44+1 / 60 |
PP=Screen
Page (11=C000)
S=Size(11=32k else 16k) O=Offset |
&0D |
Display Start Address (L) |
0-255 |
OOOOOOOO |
0 |
0 |
0 |
O=Offset |
&0E |
Cursor Address (H) |
0-63 |
--DDDDDD |
0 |
0 |
0 |
|
&0F |
Cursor Address (L) |
0-255 |
DDDDDDDD |
0 |
0 |
0 |
|
&10 |
Light Pen Address (H) |
0-63 |
--DDDDDD |
0 |
0 |
0 |
Read Only
|
&11 |
Light Pen Address (L) |
0-255 |
DDDDDDDD |
0 |
0 |
0 |
Read Only
|
|
We can
effect a Scroll by changing &0D (13)... each change to
this register will move the screen by one mode1 character
We can effect fine Horizontal scrolling by changing &05...
and vertical scrolling with &03 - but BEWARE! these may
work on the original Amstrad monitor - but people using TFT's
may see the screen jump and flicker when you do this - so your
game will be unplayable on their machines!... but maybe
that'll learn them for not paying respect to the CTM644!
|
Double buffering
Double buffering means using two screen buffers - one which is
being shown.... and another being drawn.... the advantage is this
means we can build up our screen over time without visible flickering,
the disadvantage is we need 32k of ram to hold 2 screens.!
To set the visible screen buffer we need to change PP in register
&0C... writing 48 will set the visible screen to &C000 -
writing 16 will set it to &4000 and 32 will set it to &8000...
Many games that use double buffering will set the screen to
&4000... ChibiAkumas used &8000 so that the swappable bank at
&4000-&7FFF - but this meant the AmsDOS settings at around
&A600 were lost...
If you wanted to use memory &0000 for the screen, we'd either need
to never turn on interrupts, or use IM2
- because IM1 uses &0038 as the interrupt call.
Double
buffering is great, but you may not want to use it!
If you can redraw the screen quickly in small sections you can
get away with a single buffer - this works best for 464 games
(leaving 48k free for game code) and also for games with super
high frame rate... Games like Dizzy and Skweek used single
buffers, and this allowed more ram for sprite data while
keeping 64k support
For a game like ChibiAkumas - where everything changed every
frame (due to background parallax and high sprite/bullet
count) two buffers were essential!
|
|
Using and Testing the registers!
We're going to define a simple
function called SetCRTC... it will set CRTC reg C to value A
We're going to use this in a little test program! |
|
We're not going to look at the
program code - it's long and not particularly related to the
CRTC - what it does is let us see the changes to each register
onscreen!
To use it, load "CPC_CRTC_TEST.asm" in Winape and compile it.
then type Call &8000 in
basic to start the program |
|
You should see the screen to the
right...
Note: there are some yellow and cyan lines at the bottom of the
screen - these are NOT corruption - they are filled bytes of
memory to show the top of the &FE00-&FFFF range
On the left are two
letters - these are keys you can press to change the
register - for example J and K will change the address of the
start of the screen!
Try the keys - you'll see the settings in VAL (decimal) and
&VL (hex) and the effect onscreen! |
|
Because our text drawing code
hasn't changed, Altering AddrH will make the screen look weird -
we'd need to reprogram our drawing code to accommodate our new
settings to make it all work seamlessly..
|
|
It's very easy to use some
setting that are impossible to view, and now we can't see the
settings any more!
The tool has a Safe Mode to get around this...
Press 0 (zero)... this
will turn on safe mode!
In Safe Mode, the settings you choose will be applied for an
instant, and then it will flip back to the defaults - so you can
see the effect of the settings you chose, and still see all the
options on a normal screen |
|
|
This program
just allows you to test what each register does - and find
suitable settings for the screen size and position you need...
Once you've done that, you'll need to reprogram your sprite,
font and other drawing routines to work with the new screen
position and orientation.
|
Feel the power of EGX!... the
theory!
Lets take a look at a sample EGX version of the Chibiakumas
Screen (based on the MSX version)
|
|
|
EGX - full Screen
The Full EGX screen as we see it in the CPC, each alternate line
is Mode 1/0, and the screen has been optimized so where possible
the graphics have the smoothness of mode 1, but the improved
color of Mode 0 |
Mode 0 part
The Mode 0 lines (even lines) have full 16 colors, and
give the game better color than could be managed in Mode 1 |
Mode 1 part
The Mode 1 lines (odd lines) are 4 colors - we've
selected the most used 4 colors to allow us to 'blend' the mode
1 and mode 0 lines, we've also positioned the text, so that the
top and bottom line of the text are mode one - allowing for
smooth O's and curves in letters |
Hints for EGX
1. Use the 4 most common colors for your Mode 1 graphics (or
colors which will balence out the mode 0 colors)
2. Your Mode 0 pixels must be the equivalent of 2 pixels wide - so you
may want to make parts of the mode 1 wider, so the Mode 0 parts don't
look 'wavy'
3. It's a good idea to Move objects to take advantage of the higher
resolution Mode 1 lines... the "www.chibiakumas.com" and "Episode 1"
have been redesigned, and positioned to allow the tops and bottoms to
be Mode 1 (and made wider to match Mode 0's capabilities)
4. I drew this EGX screen in Krita... the screen was exported from
Akusprite Editor... Once as Mode 1, Once as Mode 0... A 'Transparency
mask' was used to combine the two... The result was pasted back into
AkuSprite Editor, and tweaks were made.
The Gate Array:
We're going to be using the Gate
array at port &7Fxx... Really EGX is pretty simple we just
switch modes fast!... the tricky thing is the timing!
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Name |
Bit meanings |
1 |
0 |
- |
I |
H |
L |
M |
M |
Rom / Mode |
I=
Interrupt mode
H=High rom bank L=Low
rom
bank M=screen
mode |
Timing!
Thinking of the CPC screen in Mode 1...
Each line of the CPC screen is 40 usable characters... but adding the
'border area' it's 64...
the NOP command takes the equivalent of 1 character... so we can wait
a full line of pixels with 64x
NOPs... we can define these
easily with DS 64
Now, we could change mode... wait around 64 NOPs (less the
time our mode change commands took) and do another mode change,
BUT the CPC can only physically change modes once per line - If
we time things carefully, and change to Mode 1 at the end of a line,
then instantly change back to Mode 0 as the next line starts, then
we've done all our changes, and then we can wait
128 NOPs (less
the time our mode change commands took)
If we're just doing a simple test, then this isn't too important, but
if we were developing a game, we'd have to put all our game logic in
that 128 NOPs to keep the screen visible, and allow our game to work
If we
screw up our timings, you'll see something go wrong... with
this example, if the 'Mode Flip' doesn't happen at the end
of the line, you'll see no mode changes at all (as both
happen on the same line, cancellinjg each other out before
the CRTC sees either!)
If something goes wrong, and our code takes more time than
the screen takes to draw, then the effect will toggle on and
off alternating frames...
Try to start simply with an easy example, and take things
from there.
|
|
|
As they only
happen 6 times a screen (every 52 lines) Interrupts can't help
us achieve this trick!... we'll have no choice but to design
our code around changing the screen mode every two lines...
The result is writing our code will be a pain, and we'll end
up use a lot of our Z80 power on the effect...
|
Using EGX
AkuSprite Editor has a special 'EGX' View mode,
it will simulate the display of a 4/16 color alternating
screen layout (with the top line as Mode 1) |
|
We're going going to start by
disabling the firmware interrupt handler, we write a "EI RET"
to &0038
The code in today's example are very sensitive to timing...
we're going to sync to the screen redraw by waiting for a
screen to start redrawing
We read in a byte from port &F5xx... if bit 0 is 1 then
we're at the top of the screen
Now we're going to HALT... to wait for the Vblank to end...
this allows us to be sure any code that follows will run
in a consistant way, syncronized with the screen drawing of
the CPC
|
|
We're going to start the main loop...We need to start our
'Mode Manipulation' at the start of the visible screen...
We check again for the start of a new Vblank, so we know we're
at the top of a new screen.
When we are, then we disable interupts, and wait 64 lines
until we get to the first line of the active screen.
We're going to set IYL to 100 - this is the number of line
pairs in the screen (100*2 = 200)
Finally we have another 32 NOPs (via the DS 32)... this will
wait until the end of the raster line
|
|
We're using port &7Fxx to
set the screen mode, we also need to turn off the High and Low
rom...
We set the screen mode to 0, then swith it back to mode 1...
Because we're at the end of a line, the Mode 1 change happens
on a different line... and as the screen mode can only
change once a line, the change to Mode 1 happens a line
later...
We've effectively created 2 lines with alternating modes, with
5 concecutive commands!
We now have a delay...DS 110 is effctively 110 NOPs... this to
wait until the next pair of lines... It's at this time we'd
need to do any game logic or music playing - and we'd have to
make sure our code was 'timed' to take exactly 110 NOPS,
otherwise the EGX effect wouldn't be stable! |
|
We need to 'Call &8000' to
enable the effect, but you'll need to make sure you have a
proper screen to view!... a sample screen 'EGXTest.scr' is
provided in the sources.7z (in the ResALL folder) |
|
For the effect to be balenced the code for
each line needs to be the equivalent of 64 NOPS...
On the CPC each command will take a multiple of 4
'ticks'... so a command like 'Dec IX' which takes 10
'ticks' in the Z80 documenation actually takes 12..
For the full details check out the docomentation on CPC-Live |
|
Continue to Page 5
Back to Page 3
| |
Buy my Assembly programming book on Amazon in Print or Kindle!
Available worldwide! Search 'ChibiAkumas' on your local Amazon website!
Click here for more info!
Buy my Assembly programming book on Amazon in Print or Kindle!
Available worldwide! Search 'ChibiAkumas' on your local Amazon website!
Click here for more info!
Buy my Assembly programming book on Amazon in Print or Kindle!
Available worldwide! Search 'ChibiAkumas' on your local Amazon website!
Click here for more info!
|