Learn Multi platform Z80 Assembly Programming... With Vampires!
Depending on the scroll
direction, we may need to change the direction the gradient
moves, we use self-modifying code for this, and alter the bits
within the gradient definition. There are 3 options, Scroll Right, Left or no movement (inteded for vertical levels) |
![]() |
These
routines work in PAIRS - therefore a gradient change can
only occur on EVEN lines The routine is limited to make it as fast as possible, so many comprimises were made! |
![]() |
The bitshift procedure is pretty simple, we need to pull out
the 2 bits that make up the rightmost pixel, shift the bits to
the left, and OR in the two bits - to put them in the left
hand pixel we store the result back into the memory which makes up the gradient definition |
![]() |
We start with a blank screen... our gradient procedure is: 1. Fill a strip 4 pixels wide, and 192 pixels tall with the gradient (on the left hand side) 2. Copy it over the whole screen 3. Add the tilestrips, sprites etc. |
![]() |
The background pixel shift for
the V9K is almost identical to the MSX2... We take the rightmost pixel in the two bytes and shift it... Then we shift the other 3 left, and put the shifted first pixel at the far right. Once the bytes have been shifted, we save them back to the Gradient definition. |
![]() |
RLE stands for Run Length
Encoding... this is a compression method where consecutive
pixels that are the same are compressed.. .by storing them as
a color and a count RLE is LOSSLESS - the image will be a perfect recreation of the original, but it will be slower to draw! Lets imagine we have a bitmap.... and the pixels are colored 122222222221111111123 ... that's 21 characters.... Now let's imagine we stored that as 1x1.2x10.1x8.2x1.3x1... that's 19 characters Now that's a ridiculous example, but the principle stands - and rather than characters, we'll store our data in Nibbles (half bytes) |
Rle Bitmap shown on the CPC!![]() |
With the right data, RLE files are much smaller than raw bitmaps, and because the compressor 'understands' image data, they can beat compressors designed to work with byte data (an RLE compressor can save space on 5 consecutive pixels the same color, a byte compressor cannot) One thing to bear in mind, the resulting RLE will be more efficient if lots of consecutive pixels are identical, and you can use this in your art design if you need to save space! |
A file optimized for RLE - note the grey and black 'lines' and few single pixels These compress better than a checkerboard ![]() |
Bits | Meaning |
RRRRCCCC+ | C repetitions of RLE data R if C=16 then read in the next byte and add to count... if that's 255 read in the next byte too!!) |
CCCC0000+ BBBBBBBB.... BBBBBBBB | C bytes of linear (uncompressed raw) byte data (there are C x bytes of B) (C can be more than byte when 16/255+) |
00000000 CCCCCCCC+ BBBBBBBB | C repetitions of Byte B (all bytes are the same color... C can be more than one byte when 255+) |
Why
so many options? COMPRESSION! Not all data is RLE compressible - and trying to compress it will make it bigger... so we have a 'linear' option to resolve this... Also writing lots of bytes of the same color is slow - so we have a 'repeated byte' option too! |
![]() |
My free, open source AkuSprite
Editor can create RLE files for the CPC, Spectrum, MSX and Sam
Coupe... the file format is basically the same, but because of
the different bit depths of the machines, the number (and
order) of the pixels compressed differers... on the Spectrum
the color information is also RLE compressed as an extra data
set. It should be possible to write decompress ors for the other systems, however it is not my plan to do so at this time, as I have no plans to use RLE on other systems at this time - and porting the decompresser is time consuming. |
![]() |
![]() |
The
compressor was designed for the CPC, but was ported to the
other systems, on the speccy each nibble is 4 pixels - on
the MSX/SAM it's just one! Basically the decompresser is the same though, just with code reprogrammed for screen pixel drawing, and the 'GetNextLine' command is changed |
The RLE export for the SAM is
basically the same, we need to add some extra commands to
page in the video ram, but otherwise it's the same like the CPC, the palette is not included . |
![]() |
The
ChibiAkumas RLE format probably isn't the best - but it's
FREE... Chibiakumas was developed for personal achievement by the developer, and the author wanted to try to make his own RLE compressor - being the best or fastest wasn't the mission! |
![]() |
First we check what kind of
data we have ....as discussed before, the second nibble is the
RLE count... if the count is zero - then this byte is linear
data. Otherwise we store the count for later |
![]() |
![]() |
On the
MSX each RLE nibble is just one pixel... on the Speccy
it's 4 pixels - also, on the speccy the format is simpler,
as unlike the CPC, the pixels on the speccy are
consecutive - so the code that shifts the bits is
different. |
![]() |
The
nibble pair code is just a speedup! You could remove it and the regular RLE code could do the job, the drawing would just be significantly slower. |
A normal sprite will have
some kind of Bitmap data, and some Code... the Code reads in
the Bitmap data, and 'Draws' the sprite to the screen A Compiled sprite is different... there is no Bitmap Data to read in... the sole purpose of the code is to draw that one sprite...and it's optimized to do that job as fast as possible! Effectively a compiled sprite is an ASM program, and AkuSprite Editor can produce the code to show the sprite for us! |
![]() |
In ChibiAkumas, Compiled sprites are used for the
background of the last level of EP1, and the 'Sakuya' battle
of EP2 - which had a pre-rendered 3D background with up to 8
frames on repeating animation... Completely redrawing a full screen each frame during gameplay takes a lot of CPU power, so this is a time Compiled sprites are needed... The Compiled sprites in ChibiAkumas do have some bitmap data... this is because a pure compiled sprite of a complex 16k screen could easily end up as greater than 64k! In this episode, we'll learn how to make and use compiled sprites for the CPC and ZX spectrum... Because direct memory access is slow on the MSX, compiled sprites are not possible on the MSX! |
![]() |
RLE
is good when you need the space, and can spare the CPU
power... Compiled Sprites are for when you need the CPU power, and can spare the space... In both cases, it's unlikely you're going to be able to clip the sprite (have the sprite partially onscreen) so they're really best for 'special cases' |
![]() |
We're going to convert the same bitmap we used in the RLE
example... We just load the picture we want to convert into AkuSprite editor... and select the AddOne menu option... the source code is copied into the clipboard This will add an extra frame to the compiled sprite data (multiple frames can be combined into one code file)... AddOneDiff adds the difference between the new frame and the last one - effectively a transparent layer. Clear will remove all the compiled sprite data. |
![]() |
We can paste the compiled
sprite code into Winape to execute it... We do need to make a few changes first... |
![]() |
Delete the incomplete ORG statement | ![]() |
Remove the last EI command from the EndCode - the Firmware interrupt handler won't like it. | ![]() |
To keep the firmware happy,
we need to change the first Jump
to a Call,
and backup shadow BC This will produce a program we can run from basic. |
Before:![]() After: ![]() |
We can show the sprite by
typing "Call &8000"
from basic. The sprite will be shown - much faster than the RLE - and basic will continue working. |
![]() |
![]() |
Akusprite
Editor also does speccy colors, so you can do a
full-screen full color background... This was used in ChibiAkumas EP1 V1.666 during the last boss battle for the background. |
The start point of the Compiled sprite is pretty simple...
we use Stack Misuse to get data to the screen as fast as
possible... this is where we use PUSH commands to write
bitmap data to the screen. We first need to back up the real stack pointer, then point SP to the right hand side of the top line of the destination... Next we load IX with a pointer to the DrawOrder line list, and execute 'JumpToNextLine' which will handle the draw |
![]() |
The DrawOrder is a set of pointers to sections of code... the idea is that many images will have repeating parts, and we can use the same code to draw multiple lines... unfortunately this image has no such lines, so it doesn't save space - but usually it will! | ![]() |
When we start drawing a line, we load in the address of that line code, and jump to the code. | ![]() |
The DrawOrder list allows us to reuse lines to save space, however this still needs 400 bytes for a full 200 line screen... if many of the lines are the same (eg if much of the screen is blank) we can use a Looper... this will repeat one or two lines a certain number of times, allowing for a smaller DrawOrder table to fill the screen. | ![]() |
Because PUSH is the fastest way to fill the screen we use
it for the fill.. The first few bytes are all blank, so we load DE with &0000, and use 'Multipush' to do the fill... this is a set of PUSH DE commands... even though we're misusing the stack, we can use a CALL here, as we've got two bytes left to draw, and the code is designed to work around this... we'll look at this in a moment... The next bytes we need to write are &0080 - we use HL.. then we want to write &7100 - we use BC... Now we want to write $0000 again... the compiler knows DE still contains this, so we can just PUSH DE again! |
![]() |
We now need to write $0040... HL still contains $0080...
so if we just change L to $40, we'll have the right value to
push... We need some more $0000's... and DE still contains that value... we also need $8000... BC contains $7100, so if we change B to $80 we'll have the value new need... Finally we need to push 2 DE's, and then we've completed the line |
![]() |
![]() |
The C# Sprite Compiler remembers the
current state of each register, and trys to use the best
way to produce the new value we need... this can be
setting part of the register with commands like LD
L,&xx or copying parts of other registers like LD L,B It's just a case of finding the simplest way to get the resulting pair to PUSH to save speed, and space! |
We CALLED MultipushDE5... we're going to push bitmap data to
the screen, so we need to get that return address out of
there... we pop it into HL, and then do the 5 pushes... Once we've done, we effectively return, by Jumping to the address in HL that we popped earlier |
![]() |
The NextLinePushDE2 is just a pair of DE pushes... but execution falls into the code to calculate the start of the next screen line, and then falls into the jump code which runs the drawing routine for the next line | ![]() |
The NextLine
Command will need to be modified if you're intending to use
a second screen buffer, You will also need to reprogram the code if you want to reposition the sprite |
![]() |
![]() |
We often
try to combine the last command with the next line command
with functions like " jp NextLinePushHl " Because these commands will be used often, we can save a byte or two with the last command in this way. |