return to ChibiAkumas.com Choose a Color Scheme: Dark
Print Mode


Learn Multi platform Z80 Assembly Programming... With Vampires!
Chibi Akumas Technical Documentation

Overview
This series is designed to provide an educational insight into the programming of the ChibiAkumas game... it is intended for those who want to know what programming an 8-bit game is like, or are looking to modify the ChibiAkumas game....

In this series, we'll look at the 'Akuyou' game engine, how it works, what design choices were made, and why... and how to alter the game and it's graphics

Chibiakumas is open source, and you are totally welcome to modify it and release you're own version... A great way (if not the best way) to learn assembly is to take some-one elses code, and modify it to do new things and learn step by step, using existing code to help you!
Lesson Aku1 - Screen Co-ordinates and Text Drawing
Before we can really start looking at the code, we need to understand the basics of how the game was designed.

The first thing we'll look at is how graphics and screen co-ordinates work within the ChibiAkumas 'Akuyou' Game engine.




Screen Co-ordinates
Chibi akumas was designed for the Amstrad CPC  'Mode 1', which has a 320x200 screen, with 4 bits per pixel... this means there are 80 bytes across, and 200 lines of bytes down.

Because the 'Bullet hell' style I wanted for the game would require a huge amount of on screen action, I knew I had to always aim to be as fast as possible!... the Z80 is an 8 bit CPU, so is strong only at numbers from 0-255

Also, I needed some way of handling objects that were partially on screen (Sprite clipping), so I needed the game to use XY co-ordinates that did not go above 255, and mapped well onto the CPC screen.

I decided that the 320x200 CPC screen would be treated as 160x200 giving 2 x co-ordinates per byte (while most sprites only move in single bytes, this allowed bullets to use the left or right half of one byte for smoother movement)

As I had already decided my sprites would typically be 24x24 pixels in size, I then added a 24 pixel border on all sides which would be the 'off screen zone' for sprite clipping.

This gave a virtual screen of 208x248, with a central visible window of 160x200... (24,24)-(184,224) is visible

Unfortunately, this did not account for the MSX & spectrum's smaller screen, I needed to alter the visible area - but keep the virtual one the same, so I removed 8 pixels from the sky... as more objects are ground based than sky - so fewer objects to reposition
I also removed 16 pixels from either side of the visible screen. so on those systems V1.666 has a visible screen of (40,32)-(168,224)
On an 8 bit system... memory is always limited... by keeping XY co-ordinates to 0-255 we only need 2 bytes per object... so 256 bullets will need 512 bytes... if we allowed the x co-ordinate to go up to 320... it would have been 768 bytes!!!
Memory isn't the only problem... 8 bit CPU's don't like working in 16 bit - so keeping everything under 256 saved speed AND memory!

In processing power terms the Turbo-R version of ChibiAkumas COULD have had a lot smoother movement, however the co-ordinate system meant it was impossible for objects to move  in less than 2 pixels jumps in the X axis

Unfortunately 8 bit programming means coping with such limitations... and their unexpected consequences!
Text Co-Ordinates
Text co-ordinates work differently, they are based on the CPC's firmware co-ordinate of  0-39 columns, and 0-24 rows
As much of the games text is centered, I removed 4 columns from each side, and the bottom row, so on the MSX and spectrum visible text co-ordinates are now from 4-35 across  0-23 down

for future games that use a new game engine, I will probably resize the CPC screen to 256x192 so that all the systems use the same screen size (It also allows the CPC to use the faster INC L instead of INC HL in sprite commands)... it was something I considered during the development of the of the original game, but it seemed backwards as the game was a horizontal shooter, and I did not want the game to be accused of being a 'Speccy Port'x

Getting Text on the screen
Lets get some text on the screen using the Akuyou game engine!

First we need to select our font... 2 is the normal font... 1 is the mini font (half width)

Next we need to define how many characters to show...(used for the 'boss text where the letters type themselves)... this is stored in register I... but the speccy can't do I registers - so we use a macro 'LDIA' which will work on all systems

Next we set our text position using HL - 0400 is the top left on the MSX & Speccy, and slightly inset on the CPC

Finally we point BC to our &80 terminated text string... and the text will show on screen!
On the Speccy we need to use Interrupt Mode 2, so the I register is in use.... the LDIA macro uses a memory address to simulate the I register on the spectrum, and the real register on other systems...

It was probably a mistake to use the I register at all.. but it seemed like a good idea at the time!

Recompiling the core

The Akuyou Core handles Game play, graphics and input...
The Bootstrap handles level loading, continue and game over, and the redefine keys functions...
Whenever we change Bootstrap.asm or the core files, we need to recompile.

To do this, Load Build.ASM...  enable ONE (and only one) of the compiler symbols... BuildCPC  BuildENT.... BuildMSX or BuildZX and compile

the core will be rebuilt for the system selected.



Lesson Aku2 - Movements
As well as a position, stars and objects all need a movement direction... once again, for efficiency this had to be a single byte.
Generally speaking Movements are the same for stars and objects, however the 'advanced' movements (above 128) are only available to objects

It should be noted that 'stars' (bullets) are handled by the same code for enemies and players.




Basic Movements (0-127)
Basic moves are defined when the top bit 7 is 0... the remaining  bits are in the format:
-DYYYXXX
 7  6  5  4  3  2  1  0
- D Y Y Y X X X

Where:
X is horizontal move from -4 to +3
Y is the vertical move from -4 to +3
D is the 'speed doubler' which will increase the move speed

Advanced Movements (128-255)
When the first bit of the move byte is 1 then the move is an 'advanced' move... note these only work with objects, they cannot be used by Stars... (the stars use a simpler version of the code)

'Background moves' are for background objects (like the castle in Chibiakumas' they move far slower than any other object in the game, and move in the same direction as the scroll.

'Seekers' target a player 'mveSeeker' will target one of the living players (alternates each time)... this is used by coins, and certain enemies

'Wave' is a wave movement used by episode 1 - Note this is obsolete and is not supported by the latest game engine - the 'Animators' function added with EP2 is far more advanced and completely replaces this function

'Custom moves' are also mostly useless now..  they effectively passed all the object parameters to a piece of level code to handle the movement - but again most (or all) of this can now be done far better (and with less debugging) by animators!

We'll cover animators in a later lesson
Name Bits Bit Meanings
mveBackground %1100SSSS S= Speed
mveSeeker_P1 %100001SS S= Speed
mveSeeker_P2 %100100SS S= Speed
mveSeeker %100010SS S= Speed
mveWave %1010DSPP D = Depth bit, S= Speed, PP Position
mveCustom1 %1111XXXX X=Data passed to subroutine (variable?)
mveCustom2 %1110XXXX X=Data passed to subroutine (variable?)
mveCustom3 %1101XXXX X=Data passed to subroutine (variable?)
mveCustom4 %1011XXXX X=Data passed to subroutine (variable?)
Custom Moves and Waves are made irrelevent by the 'Animators'... these were added in EP2, and allow scripted 'timed' changes to movement direction, animation and enemy firing...
They're very flexible, and need much less debugging than 'Custom Moves' that had to be written in ASM code!
We'll cover 'Animators' in a later lesson!

Lets give it a go!

We use this in the 'Event Stream'... a series of bytes in the level code that define objects that appear in the level...

We'll look at the 'event stream' properly later, but lets have a quick look!... take a look at 'Level_Single.asm"

An event needs a time - in this example, we've put 2 commands at time '10'
First we turn off animators, by setting the animator to 0...

We've created an Enemy that can take 1 hit
It uses 2 frames of animation with sprite 15
It's X position is the middle of the screen ... remember! the screen is 160 visible units wide, with a hidden border of 24 - so 80+24 is the middle!
it's Y position is slightly off middle... remember! the screen is 200 visible units tall, with a hidden border of 24
The Move is &23... look at the 'Basic movements' chart above -try some other byte values from &00-&7F... and see what happens!
This little example was just something to get you started...

There is far more to discuss on the 'Event Stream' and 'Animators' - but it's too much for now, we'll come back to them later!


Lesson Aku3 - Sprite Basics
We had a look at creating an object last week, this week lets change the sprite into something new!

We'll have a look at the file formats, and the basics of the sprite editor!



Binary File Formats
Binary Sprite file format (CPC/ENT/ZX/SAM)

Akusprite files that have been saved for a platform such as the CPC will have a header... this header has 6 bytes per sprite... the length of the header can vary, it just needs to be big enough to hold all the sprites

Byte 1 is the height of the sprite data in lines
Byte 2 is the Width of the sprite in bytes
Byte 3 is the Y offset... a 16 pixel sprite may have 4 blank lines at the top... in this case the sprite would have a height of 12 and an offset of 4... this is to save memory
Byte 4 is the Settings... the top bit (7) defines if the sprite is transparent (slow) or PSET (fast)... bit 5 defines if the sprite changes or accepts the background color on the speccy... other bits also select the transparent color (bytemask) on cpc
Bits 5 and 6 define the offset to the bitmap data of the sprite in the file... in this case the sprite starts at &0100... this is defined in the spriteeditor by 'SpriteDataOffset' in the Spritelist tab

These 6 bytes will be repeated for the next sprite, and so on

The bitmap data of the sprites appears after the header
Because of the way the VDP works MSX files have the sprite data in a RLE bitmap... the format of the header is different, and uses 10 bytes per sprite

Byte 1 is the height of the sprite data in lines
Byte 2 is the Width of the sprite in bytes
Byte 3 is the Y offset... a 16 pixel sprite may have 4 blank lines at the top... in this case the sprite would have a height of 12 and an offset of 4... this is to save memory
Byte 4 is the Settings... the top bit (7) defines if the sprite is transparent (slow) or PSET (fast)... bit 5 defines if the sprite changes or accepts the background color on the speccy... other bits also select the transparent color (bytemask) on cpc
Bytes 5 and 6 are the X position of the sprite data in the RLE bitmap in pixels
Bytes 7 and 8 are the Y position of the sprite data in the RLE bitmap in pixels
Byte 9 is the Width of the sprite in pixels (0 means 256 pixels wide)
Byte 10 is the Height of the sprite in pixels (0 means 256 pixels wide)

As the bitmap data is in an RLE file, there is no bitmap data following the header - it is in a separate file

The Sprite RLE is shown to the right ->
Whenever you save the sprites for MSX, the tilemap is saved in the clipboard, so you can check it!


Getting started with AkuSprite Editor

AkuSprite Editor is still under heavy development, so it may look different to what you see here, but hopefully the functionality will be the same or better!

AkuSprite is a sprite editor which supports up to 8 banks of up to 64 sprites!

Sprites have up to 16 colors... and 'viewer filters' can be used to temporarrily reduce the color depth  (without altering the original 16 color data)...  This can be used for preview - and also for export

for systems like the spectrum, 'Color attributes' can be painted onto the sprite - these are stored separately from the '16 color pixel data'

a 17th 'transparent' color is supported  - this color is converted to whatever is appropriate depending on export system

By default sprites are up to 256x256, but there is a 512x512 mode - which is intended for creating CPC screens (which are 320x200)

Akusprite can save to many formats, such as CPC .SCR... compressed RLE, bitmap sprites, raw bitmap (headerless native screen bytes)... it can also export ASM code for color palettes...

Auksprite formats are used by ChibiAkumas and in my Z80 tutorials - it is, and will continue to be my graphics tool in all future development - and as it's open source, you can add anything you want to it!

While sprites can be exported to various formats, the native save format is .TXT - and is essentially CSV format - so you can even edit the sprites in the file with notepad or excel!
Lets take a look at the basics of the screen!

We have the Primary 16 color palette - this is used to draw our sprite.

We have the seconday palette - this is used for applying block color attributes for systems like the spectrum and c64

The tool strip has the drawing tools and other main functions

The main drawing area is for painting - there are also sub-tabs for view options,  sprite list, and notes (free-text comments)

The settings panel has sprite and tool settings - also Zoom and palette reconfiguration options

The preview panel shows the current sprite at pixel size, has sprite and bank selection, and summary info on the current sprite

Pixel Paint allows 16 color drawing of dots onto the sprite
ZX Paint will allow color attributes to be applied to the sprite
Color swap will swap a color for another... this can work on 8x8 blocks, or the whole sprite

It is used for converting 16 color sprites to 4 color (or vice-versa) and for converting sprites to 2 color
These tools all have options in the tool settings panel

A sprite file can only have ONE set of color attributes, it is not possible to have AppleII color attributes and ZX attributes in the same file... you'll need to save two versions of the file.
AkuSprite is really just a basic pixel editor for small sprites - but there are options for importing and exporting sprites to other programs!

Chibiakumas was drawn on Krita - a free photoshop like app which is totally awesome!

Need more power, no problem! AkuSprite editor is designed to allow you to transfer a sprite to a better application, and import back once the sprite is finished!

Copy the sprite to the clipboard, edit it in the program of your choice, and paste it back!

If you want to do all the work in an external app, you can export all the sprites as a set of PNG files using the "BMP MAP" function... edit them, and reimport them!

Note, this only works for the 16 color data... you will have to apply color attributes in the sprite editor.

Each bank is stored in a separate file... and a MAP file defines the areas used by the sprites...

If you re-import the sprites, the editor looks at the MAP - finds the color of each sprite, and works out the area of each sprite - then it imports the sprites using those areas...

The 16 colors are re-imported from Bank0 - so you can re-create all the basic sprite info JUST from the set of PNG files!

the _MAP file defines the position of the sprites... 64 special colors are used to define the areas of each sprite... a map of these colors is held at the bottom of the _MAP file..


the 16 colors used by the sprites are also in the _BANK0 file - make sure you use the correct colors, or the sprites may not re-import correctly!

Lesson Aku3 Part II - The Return!
Watch the video of the use of the sprite editor to actually create a sprite!





Lesson Aku4 - The Star Array!
We looked recently at moves, and how to create an object... lets look now at how the 'Star Array' draws and handles the bullets of the player and enemies!






The Data Block:
The 'Data Block' is a bank of &700 bytes that defines all the attributes of bullets and objects in the game (also saved object settings bank 1)
The organization of the block is a little weird, it's designed to allow movement through the array by changing the Low byte, and looking at a different variable of the same object by changing the High byte

Addr &00 &40 &80 &C0 &FF
VBlock+&0000 Star array Y Star array Y Star array Y Star array Y
VBlock+&0100 Star array X Star array X Star array X Star array X
VBlock+&0200 Star array Move Star array Move Star array Move Star array Move
VBlock+&0300 Object Array Y Object Array Animator Player Stars Y Player Stars Y
VBlock+&0400 Object Array X Object Array Size Player Stars X Player Stars X
VBlock+&0500 Object Array Move Object Array Program Player Stars Move Player Stars Move
VBlock+&0600 Object Array Sprite Object Array Life Obj Saved Settings -15 entries x 8 bytes

VBlock+&0700




The Object Array saved settings is used by the Event stream for 'template' enemies... there is actually a second bank of saved settings, but it's RAM is in the level block - because there wasn't another 128 bytes of spare ram in the core!

The Star Array:
The 'star array' is the normal bullet routine that handles the players bullets, the enemy bullets - and also the particles on the Turbo-R version.

Each bullet has 1 byte 3 variables, a Y position, an X position and a Move (See Lesson Aku2 for the move info)

The data is byte aligned for a 256 byte array, so if the star array started at &0100, then bullet 3's Y co-ordinate would be &0102, it's X co-ordinate would be  &0202 and the move would be at  &0302 - this allows the code to move through the array memory more efficiently

A Y-position of 0 means that star is not in use, so this is used to find empty slots for new stars, and decide which stars need drawing. The star array remembers how big the in use array is, and wont try to draw more than needed, to save a little time

Enemy bullets may hit the player, so when drawing the enemy bullets, the players location 'hit box' is pre-calculated and inserted into the conditions by 'self modifying code' to reduce the amount of time during the star loop - for the player loop this hit code is also disabled by self modifying code...

Player bullets hitting enemies is handled during the 'object drawing loop' which draws enemies and background objects.

Stars are added to the array, either during the object drawing code for enemy bullets, or the player handler for player fire.
The Star Loop..
The Star loop will scan through star data, and find any living stars (where Y>0)

Once one is found its data is read in, and a simpler version of DoMoves is executed (to save time)
Drawing the Stars...

When it comes to drawing the stars, each system is somewhat different, so we'll just look at the CPC version!

We read in the screen memory, and work out the location of the star... we read in 2 concecutive addresses, one into HL, and one into DE - because our star is 2x2 - so we need 2 line addresses, and this is faster than calling GetNextLine

Now we work out if our star is on the LEFT or RIGH of the pixel, by checking Bit 0 of A (when we started the function)

Now we read in the byte that is currently on the screen, and blank out the half used by the star - replacing that half with our star color..
We then write the same data to the line below... WITHOUT reading in what was there before, this may corrupt two pixels - but we saved some time!

The MSX version (non V9K) also directly addresses the memory via the VDP's Ram access commands... because the V9K is so fast, the V9K version uses sprites for it's bullets!
The Spectrum Star drawing code is similar to the Amstrad, however Speccy stars are 6x6 - and because there are 8 pixels per byte on the speccy not 2 like the CPC, there are 4 branches for drawing the star in each possible X positionon the spectrum!
The Enterprise uses the CPC code - because both systems have the same screen-byte layout!

Collision Detection...
We need to do collision detection to see if the stars have hurt the player,

We work out the player position (and hence the 'hitbox' of the player) in advance and pump it in using Self modifying code, this is quicker than looking at the data in the player array each time.

If the bullet is within the players hit box, we call the Player_Hit _Injure routine to hurt the player

Lesson Aku5 - The Object Array!
It's time to get serious! Lets see how the Object array works!

The object array draws enemy and background sprites, it also handles collision with the player, and player bullets
Lets take a look at how it works!




Lets look again at the Data block!

The object array only uses the first 128 bytes of a bank of memory - this was to reduce the memory footprints, and also allows the code to move through the data more quickly, by setting bit 6 of the HL address pointer to jump to the second half of the data

Y =  Y position (0=dead object)
X = X position
Move =  byte Move code (see Above)
Sprite = Sprite Number BBSSSSSS S= sprite number (0-63) B=number of banks (1/2/4)
Animator = Animation script (we'll cover these later)
Size = Size of sprite (in pixels)
Program = Fire program (We'll cover these later)
Life = BPLLLLLL B=bullets hurt object (otherwise timed) P= Hurts Player L= Life (0-63)
Addr &00 &40 &80 &C0 &FF
VB+&0000 Star Y Star Y Star Y Star Y
VB+&0100 Star X Star X Star X Star X
VB+&0200 Star Move Star Move Star Move Star Move
VB+&0300 Object Y Object Animator Player Stars Y Player Stars Y
VB+&0400 Object X Object Size Player Stars X Player Stars X
VB+&0500 Object Move Object Program Player Stars Move Player Stars Move
VB+&0600 Object Sprite Object Life Obj Settings -15 x 8 bytes

VB+&0700





Boss enemies are often multiple objects! Zombichu was made up of 3 objects - each 24*96 strips
The 'Angler Grinder' was made up of dozens of 24x24 size objects - it all depends on the shape of the final sprite!
Essentially lots of small sprites are faster than one big one - because 'blank' areas slow down the sprite routines
Order of operations
Each object is handled with the following procedure:
    1.  Work out the players hitbox
    2. Find the next living object
    3. Read in it's data from memory
    4. Execute object animator
    4. See if object is in players hitbox
    5. Check object's life, and age if needed
    6. See if a player bullet has hit the object and kill object if needed
    7. Move the object
    8. Save the objects new settings back to memory
    8. Make the object shoot (if progam requires it)
    9. Draw object sprite to screen
    10. Repeat for next object

Work out the players hitbox
To save time in the loop, we first work out the 'hit box' of the player, we can then just compare the objects position to these hitbox co-ordinates to see if the object has hurt the player - we do this with self modifying code.

This may need to be done again if an object is an different size... originally all objects were 24x24, but with EP2 and V1.666 they can be various sizes, this will mean recalculation of the collision detection

Find the next living object
We scan through the object data looking for an object with a Y co-ordinate above zero... this marks a living object.

We keep A as Zero, so we can just do CP C to check if C=0

Because we're only looking at Y we just need to INC L to move to the next object in the array to save time (rather than INC HL)

Read in it's data from memory

When we find an object we need to process we read it's data into the registrers, Because the data is held in two 'columns' we set Bit 6 half-way through the read, and read back the other way.

We'll also need to work out what sprite bank to show... (not shown in source screenshot)
Working out the bank is complex, and hardware specific... it's not shown here!

Execute the object animator
Animators handle complex movement and fire patterns, we'll look at them later in these tutorials!

See if object is in players hitbox

We compare the object position to the player hitbox, we also check if the player is alive!
If the player is hit, we will run the handler for that event... we need to check if the object has been removed after the 'hit' because powerups will disappear after they have been hit, and want no more processing
Not all objects hurt the player - Coins and powerup have the top bit of their life set... and do collision dectection - but won't decrease the players life when the 'hit' occurs... they use 'Program 3' which tells the system they are a powerup - which powerup is decided by their sprite number.

Check object's life, and age if needed
All objects have a life... an immortal object will not hurt the player (byte 0) ... objects which do hurt the player (bit 6) either age through bullet strikes, or onscreen time (bit 7)... Life is 0-63... if an object reaches zero, then it's dead.

See if a player bullet has hit the object and kill object if needed

We need to check if any player bullets are in the hitbox of the object - to do this we need to process all the player bullets, so we need to back up many of our registers, so we can continue object processing later.

If the object was hit, we need to do something!

We use self modifying code after the scan to handle the hit (we don't allow more than one hit per frame)


Move the object

We move the object with DoMoves... we looked at that here

Save the objects new settings back to memory

We need to store the new object settings back to the memory

Effectively this is the reverse of stage 3

Make the object shoot (if progam requires it)
the "Program' of an object typically defines how it shoots...
so this is the point where we will make the object fire a burst...
Some special programs do other things!
Special programs can make the sprite use an alternate bank, or switch banks for every 1 x unit (For 2 pixel shifts of background objects on 4 pixel per byte CPC)
They also define 'powerups' and special objects.. they can even be defined as a 'seekpoint' which sucks the player in - this is used to make the player fly to the edge of the screen at the end of a level!


Draw object sprite to screen
Now we've worked out the new position we can draw the sprite to screen

We need to set the following parameters to do this:
SprShow_BankAddr - Address of the sprite bank
SprShow_SprNum - Sprite number
SprShow_X - X position in bytes
SprShow_Y - Y position in lines

We also need to bank in the sprite data before calling ShowSprite
We've not shown the 'Bank Selection code' here - it uses the current game tick and the top two bits of the sprite number... if you want to see it, please download the sourcecode - or watch the video!
Repeat for next object
The whole procedure is repeated for the next object!
When the last object is done, control returns to the level loop

We've only looked at the basics here, we'll cover things like the ShowSprite routine and Animators in later lessons!
Please be patient! The Object array is very complex, as it does all the onscreen enemies!




Visit www.ChibiAkumas.com to get my games and their source code! | Support me on patreon