Simple sample code on a variety of
Z80 machines
 |
Lesson
S30 - Sprite clipping on the Spectrum Next (Part 1/2)
We drew our 'Chibiko' mascot to the screen before, but this time
we're going to improve the algorithm.
First we'll add 'sprite clipping' so parts of the sprite can go
offscreen. |
 |
ZXN_Bitmap_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
We're using 256
color mode today, but our graphic is only 4 color... also the
colors are inverted, but this is because we're using XORed and
we've colored the background purple so we can see the drawable
area.
|
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we set up the default getnextline routine (used for uncropped
sprites)
next we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0 |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L). |
 |
We then skip over any bytes in the source (IY) based on the number
of lines of bytes we need to remove from the top.
|
|
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We've calculated the left and right crop... We use these
to calculate the bytes to skip after each line of our sprite
(SpriteHClip)
If this is zero, there's no horizontal crop
We need to crop, so we calculate the new width in H.
We also need to turn on the slower cropping routine via self
modifying code |
 |
We need to alter the start byte to compensate for any left hand
clipping |
 |
We need to convert out Xpos and Width to a number of bytes, and
our Ypos and height to a number of lines.
We clear the carry to tell the calling routine we cropped the sprite
successfully (it needs drawing) and return. |
 |
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Logical Units and clipping
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then calculate the VRAM destination
with GetScreenPos, and run the ShowSprite routine |
 |
The screen is 256x192, and each pixel is 1 byte - making 48k in
total.
We page the VRAM into the &2000-&3FFF range... this is
usually ROM, and if means we can still use IM1 if we've paged it to
RAM.
We therefore split the screen into six 8k blocks, and select the one
we need using NextReg &51 |
 |
The showsprite routine transfers bytes from the source (now in DE)
to VRAM in HL
after each line GetNextLine moves the HL destination down one pixel
line.
This routine is switched via self modifying code if the
sprite is cropped.
|
|
GetNextLine will increase the HL address, but the 48k screen is
split into eight 8k banks, so if we go over the boundary of one
bank, we need to page in the next with NextReg &51
If the sprite is cropped, we need to use the GetNextLineWithClip to
remove the un-needed source bitmap bytes.
|
|
 |
We've only looked at the basic cropping and
sprite drawing routines here.
Next time we'll take a peek at the use of stack misuse to speed up
reading and writing, and the double buffering code.
|
Next time we'll look at the 'Double buffering' and 'Stack misuse'
that allow for no flicker and faster sprite drawing.
|
 |
 |
Lesson
S31 - Sprite clipping on the Spectrum Next (Part 2/2)
Lets take a look in more detail at our sprite routine, this time
we'll look at the double buffer, and stack misuse. |
 |
ZXN_Bitmap_Clipping.asm
|
 |
Stack misuse is a trick where we use the PUSH
or POP commands to quickly read or write data, by pointing the SP
register to the start of data we want to read, or end of data we
want to write, we can use these commands to quickly transfer data.
BUT because the SP register is not pointing to a normal stack, we
probably can't use CALLS or interrupts.
|
 |
Double Buffering
We're going to use two buffers. One will shown to the viewer while
the other is being drawn, we'll then swap the two in hardware and
repeat.
Each buffer is 48k, and uses 3x 16k banks or 6x 8k banks.
We use banks 8 and 11 for the two screen buffers, We select the
visible screen buffer with NextReg &12
|
 |
We use 16 banks when selecting the buffer, but for paging it into
Z80 ram we use 8k banks and nextreg &51
We page the VRAM into the &2000-&3FFF range... this is
usually ROM, and if means we can still use IM1 if we've paged it to
RAM.
We therefore split the screen into six 8k blocks, and select the one
we need using NextReg &51
|
|
 |
By paging in
vram into the range &2000-&3FFF we can still use Interrupt
mode 1!
We just need to page in some ram into the &0000-&1FFF
range.
|
Clearing the screen
We'll use stack misuse to clear the screen - this means we need to
calculate the address of the byte after the end of the screen, as
each 2 byte write will be done by a PUSH command.
We're going to page in the screen in 8k chunks, so we need to do
this 6 times. |

|
We're going to fill the 8k screen part with the bytes in DE.
To reduce the number of loops, we do 32 'PUSH DE' commands, writing
64 bytes... using lots of repeated commands to reduce loops is
called 'Unwrapping loops'... As loop conditions take time, the fewer
we use, the faster the fill will be.
First we back up the true stack pointer in IY. We move the end of
the destination range from HL into the stack pointer... each PUSH DE
decreases the stack pointer by two, so we are working in reverse.
As we're misusing the stack, we stop interrupts with DI, and restore
the true stack pointer in IY
|
|
Drawing the sprite
Because we need to recalculate the screen destination after each
line, it won't help much to use the stack for writing, instead we'll
use it to read the bitmap data with POP commands.
We back up the stack pointer with self modifying code, and move the
image source data address from IY to SP.
We then POP two bytes of data from the source image, and write them
to the screen.
Once the sprite is finished, we restore the stack pointer address. |
 |
Our "get next line" routine also needs to be changed.
If we need to clip the sprite, we need to update the stack pointer
to skip over the source bytes that are not needed.
We then have the routine to handle the change of the HL VRAM
destination. It also handles the bank switching.
|
|
 |
Lesson
S32 - Sprite clipping on the MSX1
We drew our 'Chibiko' mascot to the screen before, but this time
we're going to improve the algorithm.
First we'll add 'sprite clipping' so parts of the sprite can go
offscreen, we'll also use double buffering, so there's no screen
flicker. |
 |
MSX1_Bitmap_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
We're going to
use the tilemap to draw the graphic today, so we're limited to 8x8
blocks. That's 4x4 logical units!
|
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we set up the default SpriteHClip routine (We assume the
sprite won't be horizontally clipped)
next we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0
we mask with and %11111100 to convert to a number of tiles. |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
we mask with and %11111100 to convert to a number of tiles.
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L). |
 |
We then skip over any bytes in the source (IY) based on the number
of lines of tiles we need to remove from the top.
|
|
We then remove the needed start bytes |
 |
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We've calculated the left and right crop... We use these
to calculate the bytes to skip after each line of our sprite
(SpriteHClip)
If this is zero, there's no horizontal crop
We need to work in a number of tiles, so we divide by 4
We need to crop, so we calculate the new width in H. |
 |
We need to alter the start byte to compensate for any left hand
clipping |
 |
We need to convert out Xpos and Width, Ypos and height to a number
of tiles.
We clear the carry to tell the calling routine we cropped the sprite
successfully (it needs drawing) and return. |
 |
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Screen buffers
We're using two Tilemap
buffers, One at &1800 and the other at &1C00
The bottom 3 bits of graphics register 2 define the address of the
tilemap (Pattern Table)
We flip the bottom bit so we draw to the alternative bank than the
one showing
|
 |
Our 'GetVDP Screenpos' function will calculate the VRAM
destination from an XY position.
It has been modified to use the VisibleScreenBank to write to the
correct buffer.
|
|
Logical Units and clipping
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then calculate the VRAM destination
with GetScreenPos, and run the ShowSprite routine
The Chibiko bitmap starts from tile 128 on the MSX1 and SMS |
 |
The FillAreaWithTiles will draw a grid of tiles to the screen.
Where the graphic is horizontally cropped, we skip some tiles after
each line with SpriteHClip
|
|
Clear screen
We clear the screen in pretty much the same way.
We calculate the base of the Tilemap vram, then write 32x24 (one
full screens worth) of zero tiles to clear the screen.
|
 |
 |
Lesson
S33 - Sprite clipping on the MSX2
We drew our 'Chibiko' mascot to the screen before, but this time
we're going to improve the algorithm.
We'll use sprite clipping, and double buffering! |
 |
MSX2_Bitmap_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
We're using VDP
commands today, so we'll have to define the area of the sprite to
transfer from the 'invisible area' (Ypos 512+) to the two Visible
screens (Ypos 0 and Ypos 256)
|
 |
Double Buffer
We're using two screen buffers
- these appear in the 256x1024 VRAM area.
One is at Ypos 0, the other is at Ypos 256... the sprites are at
512+
We need to use bit 5 of VDP register 2 to define the base address of
the sprite.
NOTE, our screen is 256x192, so there is a small amount of screen
unused between the two screen buffers. |
 |
Clear Screen
We use the fast fill command to clear the screen.
The screen is 256x192, and our VRAM base will be either from line
Y=0 or Y=256
|
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0 |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L). |
 |
We update the height of the sprite in H.
The MSX2 version is different to other versions! We store the final
pixels to remove from the top in IYL
|
|
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We need to crop, so we calculate the new width in H.
We need to alter the start pos to compensate for any left hand
clipping in IYH
|
 |
We update the Logical units to physical pixels |
|
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Logical Units and clipping
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then need to prepare the values for
the "VDP_HMMM_ViaStack" function.
This uses 6 register pairs - some of which are in the shadow regs.
IX = X source IY = Y source
HL' = X dest DE' = Y Dest
HL = Width DE =
Height |
 |


|
Lesson
S34 - Bitmap clipping on the SMS/GG via the tilemap
We drew our 'Chibiko' mascot to the screen before, but this time
we're going to improve the algorithm.
First we'll add 'sprite clipping' so parts of the sprite can go
offscreen, we'll also use double buffering, so there's no screen
flicker.
We'll use the Tilemap this time, next time we'll use Hardware
Sprites |
 |
SMS_Bitmap_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
We're going to
use the tilemap to draw the graphic today, so we're limited to 8x8
blocks. That's 4x4 logical units!
|
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we set up the default SpriteHClip routine (We assume the
sprite won't be horizontally clipped)
next we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0
we mask with and %11111100 to convert to a number of tiles. |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
we mask with and %11111100 to convert to a number of tiles.
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L). |
 |
We then skip over any bytes in the source (IY) based on the number
of lines of tiles we need to remove from the top.
|
|
We then remove the needed start bytes |
 |
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We've calculated the left and right crop... We use these
to calculate the bytes to skip after each line of our sprite
(SpriteHClip)
If this is zero, there's no horizontal crop
We need to work in a number of tiles, so we divide by 4
We need to crop, so we calculate the new width in H. |
 |
We need to alter the start byte to compensate for any left hand
clipping |
 |
We need to convert out Xpos and Width, Ypos and height to a number
of tiles.
We clear the carry to tell the calling routine we cropped the sprite
successfully (it needs drawing) and return. |
 |
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Screen buffers
We're using two Tilemap
buffers, One at &3000 and the other at &3800
Each Tilemap is 32x24, and 2 bytes per tile, so &600 bytes total
We toggle bit 1 of VDP register 2 to switch between these two
tilemaps.
We first wait for Vblank, so that we allow the previous screen to
show. So interrupts work correctly we need to define an interrupt
handler which will clear the interrupt by reading in from the VDP
control port
|
 |
The CLS screen routine will select the start of the tilemap
(&3000 / &3800)
We then transfer 32*24 tiles of zero to the Tilemap ram. |
 |
Our 'GetVDP Screenpos' function will calculate the VRAM
destination from an XY position.
It has been modified to use the VisibleScreenBank to write to the
correct buffer.
|
|
Logical Units and clipping
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then calculate the VRAM destination
with GetScreenPos, and run the ShowSprite routine
The Chibiko bitmap starts from tile 128 on the MSX1 and SMS |
 |
The FillAreaWithTiles will draw a grid of tiles to the screen.
Where the graphic is horizontally cropped, we skip some tiles after
each line with SpriteHClip
|
|


|
Lesson
S35 - Bitmap clipping on the SMS/GG via Hardware Sprites
We used the tilemap to draw our graphic last time, this time we'll
take a step up and use hardware sprites!
We'll build our 48x48 chibiko mascot out of 6x6 hardware sprites |
 |
SMS_BitmapHsprite_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
This time we're
using Hardware sprites, so we can move smoothly... yay!
BUUUUT... there's a limitation... the X co-ordinate is only one
byte, so we can't smoothly move the sprite off the left of the
screen. The best we can do is 'hide' one 8 pixel strip on the left
by enabling bit 5 of VDP Register 0
|
 |
Gamegear & Mastersystem Sprite Memory (&3F00)
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 Y co-ordinate is at
&3F00 ... it's X 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 |
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we set up the default SpriteHClip routine (We assume the
sprite won't be horizontally clipped)
next we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0
We need to deal with the 'offset' of any removed tiles to keep the
movement smooth, so we AND and XOR with %00000011 |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L).
We need to round this up to a total number of sprites, so we add 3
and AND with %11111100 |
 |
We then skip over any bytes in the source (IY) based on the number
of lines of tiles we need to remove from the top.
|
|
We then remove the needed start bytes |
 |
now we do the same for the X axis.
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
We need to deal with the 'offset' of any removed tiles to keep the
movement smooth, so we AND and XOR with %00000011
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We've calculated the left and right crop... We use these
to calculate the bytes to skip after each line of our sprite
(SpriteHClip)
If this is zero, there's no horizontal crop
We need to work in a number of tiles, so we divide by 4
We need to crop, so we calculate the new width in H,
rounding to a number of tiles. |
 |
We need to alter the start byte to compensate for any left hand
clipping |
 |
We need to convert out Xpos and Ypos to pixels (*2), and height
and width to tiles (/4)
We clear the carry to tell the calling routine we cropped the sprite
successfully (it needs drawing) and return. |
 |
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Using the Hardware Sprites
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then calculate the VRAM destination
with GetScreenPos, and run the ShowSprite routine
The Chibiko bitmap starts from tile 128 on the MSX1 and SMS |
 |
The FillAreaWithTiles will draw a grid of sprites to the screen.
Where the graphic is horizontally cropped, we skip some tiles after
each line with SpriteHClip
|
|
As the sprite is clipped, we will need fewer than 36 tiles to draw
our mascot, but these sprites will stay on the screen!
We need to hide these sprites so they do not 'trail' on the screen |
 |


|
Lesson
S36 - Bitmap clipping on the GB/GBC via the tilemap
We drew our 'Chibiko' mascot to the screen before, but this time
we're going to improve the algorithm.
First we'll add 'sprite clipping' so parts of the sprite can go
offscreen, we'll also use double buffering, so there's no screen
flicker.
We'll use the Tilemap this time, next time we'll use Hardware
Sprites |
 |
GB_Bitmap_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
We're going to
use the tilemap to draw the graphic today, so we're limited to 8x8
blocks. That's 4x4 logical units!
|
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we set up the default SpriteHClip routine (We assume the
sprite won't be horizontally clipped)
next we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0
we add 3 to round up, and mask with AND %11111100 to convert to a
number of tiles.
Note: the GBZ80 has no NEG command, so we simulate one with CPL...
INC A |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
we mask with and %11111100 to convert to a number of tiles.
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L). |
 |
We then skip over any bytes in the source (IY) based on the number
of lines of tiles we need to remove from the top.
|
|
We then remove the needed start bytes from the IY pair |
 |
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We've calculated the left and right crop... We use these
to calculate the bytes to skip after each line of our sprite
(SpriteHClip)
If this is zero, there's no horizontal crop
We need to work in a number of tiles, so we divide by 4
We need to crop, so we calculate the new width in H. |
 |
We need to alter the start byte to compensate for any left hand
clipping |
 |
We need to convert out Xpos and Width, Ypos and height to a number
of tiles.
We clear the carry to tell the calling routine we cropped the sprite
successfully (it needs drawing) and return. |
 |
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Screen buffers
We're using two Tilemap
buffers, One at &9800 and the other at &9C00
Each Tilemap is 32x32, and 2 bytes per tile, so &400 bytes total
Bit 3 of port port &FF40 selects the currently visible tilemap,
we store the top byte of the tilemap base with VisibleScreenBank
|
 |
The CLS screen routine will select the start of the tilemap
(&9800 / 9C00)
We then transfer 32*18 tiles of zero to the Tilemap ram (the full
width and visible height)
We need to wait for H or V blank with bit 1 of port &FF41 |
 |
Our 'GetVDP Screenpos' function will calculate the VRAM
destination from an XY position.
It has been modified to use the VisibleScreenBank to write to the
correct buffer.
|
|
Using the Tiles!
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then calculate the VRAM destination
with GetScreenPos, and run the ShowSprite routine
The Chibiko bitmap starts from tile pattern 128 on the Gameboy |
 |
The FillAreaWithTiles will draw a grid of tiles to the screen.
Where the graphic is horizontally cropped, we skip some tiles after
each line with SpriteHClip
|
|


|
Lesson
S37 - Bitmap clipping on the GB/GBC via Hardware sprites
We drew our 'Chibiko' mascot with the tilemap... but we can do
better!... This time we'll use hardware sprites to do the job for
smooth moves! |
 |
GB_BitmapHsprite_Clipping.asm
|
 |
Logical Units and clipping
To allow us to crop our sprites, we'll need to move our sprite
around 'logical space' - only a portion of this will be the visible
screen.
We'll use this to crop the sprite. |
 |
We're designing our code for screens of 256x192 - and we'll use
'Logical Co-ordinates' which define a visible screen of 192x96 (2
pixels per logical unit)
The visible logical screen is in the center of the logical space
area to allow the sprite to be 'partially offscreen' on any side. |
 |
We need to crop any offscreen part to
get to the first visible pixel of the sprite.
After each line We also need to crop the unused
pixels to the next visible pixel
|
 |
We're using
hardware sprites.. wooo!
We need to transfer Hsprites during Vblank, so we'll use 256 bytes
of ram as a cache, and transfer them in the interrupt handler
|
 |
Logical Cropping
Our cropping routine will work out the X,Y pos in bytes, and width
and height + any skipped pixels from the source data
registers B,C is the X,Y co-ordinate in logical units
registers H,L is the Width,Height in logical units
register IY is the address of the source bitmap data.
First we set up the default SpriteHClip routine (We assume the
sprite won't be horizontally clipped)
next we zero D,E - they are used for temp values |
 |
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw
ypos (C)... if the result is greater than zero, then nothing is off
the screen at the top.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the height of our sprite (L),
if the amount to crop is not less than the height then the sprite is
completely offscreen.
Anything else is the number of lines we need to remove from the top,
we store this in E and set the new 'draw position' to Ypos (C) =0
we add 3 to round up, and mask and XOR the bottom 2 bits to
calculate the offset (0-7 pixels) to keep the movement smooth as we
remove tiles.
Note: the GBZ80 has no NEG command, so we simulate one with CPL...
INC A |
|
Next we do the same for the bottom,
We add the height to the Ypos, and subtract the height of the
logical screen, if it's over the screen height (greater than zero)
we need to crop again - the result is the amount to crop
|
|
We've calculated the top (E) and bottom (D) crop... we now use
these to calculate the new height of the sprite (L). |
 |
We then skip over any bytes in the source (IY) based on the number
of lines of tiles we need to remove from the top.
|
|
We then remove the needed start bytes from the IY pair |
 |
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw
Xpos (B) ... if the result is greater than zero, then nothing is off
the screen at the left.
if the result is less than zero we need to crop... we convert the
negative to a positive and compare to the width of our sprite (H),
if the amount to crop is not less than the width then the sprite is
completely offscreen.
We calculate the offset using AND and XOR, to allow the object to
move smoothly as we remove strips of the sprite.
Anything else is the number of lines we need to remove from the
left, we store this in E and set the new 'draw position' to Xpos (B)
=0
|
|
Next we do the same for the right,
We add the width (H) to the Xpos, and subtract the width of the
logical screen, if it's over the screen width (greater than zero) we
need to crop again - the result is the amount to crop from the right
(D) |
 |
We've calculated the left and right crop... We use these
to calculate the bytes to skip after each line of our sprite
(SpriteHClip)
If this is zero, there's no horizontal crop
We need to work in a number of tiles, so we divide by 4
We need to crop, so we calculate the new width in H. |
 |
We need to alter the start byte to compensate for any left hand
clipping |
 |
We need to convert out Xpos and Width, Ypos and height to a number
of pixels (by multiplying logical units by 2)
we clear the carry to tell the calling routine we've a sprite to
draw. |
 |
If the sprite is completely offscreen, there's no point trying to
draw, so we set the carry and return |
|
Hardware sprites
We load an Interrupt handler
in at address &0040, this will be executed by the hardware
during Vblank.
We need to trasnfer out VblankInterruptHandler to address &FF80,
so it can run during the interrupt.
|

 |
We use port &FF46, which takes the top byte of the cache
address, and transfers the cache to the hardware sprite data. |
 |
Sending the sprites!
When we want to draw our sprite, we first run the DoCrop routine
to calculate the required cropping.
If there's anything to draw, we then calculate the VRAM destination
with GetScreenPos, and run the ShowSprite routine
The Chibiko bitmap starts from tile 128 on the Gameboy
Our object is 48x48 pixels - 6x6 tile patterns, so we'll need 36
hardware sprites to draw it to the screen. |
 |
The FillAreaWithSprites will draw a grid of sprites to the screen
via the cache,
We need to give each 8x8 tile a different hardware sprite, and each
has 4 bytes of data.
Where the graphic is horizontally cropped, we skip some tiles after
each line with SpriteHClip
|
|
As we crop our object, the number of hardware sprites it will need
will decrease, so we need to 'zero' any unneeded sprites, to take
them off the screen. |
 |

|
Lesson
S38 - Bitmap movement with Overscan on the CPC!
We've drawn a moving sprite before, but in the past we've used
normal 32k 320x200 or less.
But the CPC screen has a 'secret trick' - we can enlarge the screen
using 'overscan' for a 32k 384x270 (approx) screen!
Lets learn how! |
 |
CPC_Bitmap_Overscan.asm
|
 |
 |
We're only
going to look at the overscan bit today, as the sprite movement
isn't anything new!
If you want to know about the direction processing, please see the
other simple series examples.
|
Setting up the screen
We're going to set up the screen, we need to set up all the CRTC
registers - you can see the full list here
We use port &BCxx to select register xx
We use port &BDxx to set the selected register to value xx
&0C/D define the base screen address, and also allow the
32k screen we need for overscan.
|
 |
Here are the values we're using
|
|
Our screen is huge, and spans two 16k banks...
Bank 1 is &8200-&BFFF
Bank 2 is &C000-&FFFF
Memory <&8200 is free, so we'll use it for our stack!
Memory &0000-&3FFF is free so we can use IM1,
Memory &4000-&7FFF is free so we can use ram bank switching
but there's a slight memory 'gap' between them we'll have to deal
with!
To make it clear where the two 'banks' start and end, we'll fill
them different colors. |
 |
Here is the result. |
 |
Calculating the screen addresses
In overscan mode, our screen is 96 bytes. While our calculations
are similar, we need to do them slightly differently to cope with
the 'bank gap'
Looking at Y line (in C) - we need to take each set of bits, and
work with them separately:
YYYYYYYY Y line number (0-270)
-----YYY (0-7) - this
part needs to be multiplied by &0800 and added to the total
YYYYY--- (0-31) - This part
needs to be multiplied by 96 (&60)
There is a gap between the two memory banks, we need to cope with
Screen base for lines<128 = &8200
Screen base for lines>=128 = &C000
(subtract 128 from y)
We also need to add the Xpos (in bytes) |
 |
We also have a Get Next Line function to move down a line.
in 8 line blocks we can just add &0800 to move down a lline, but
when we get to the end of the &FFFF range we need to loop back
to the top by adding &C060.
We also have to deal with the 'gap' between the two banks at
&BFA0-&BFFF
|
|
Our sprite drawing routine is unchanged from before.
|
|
Reading the joystick
The firmware will ruin our overscan, so we're using a hardware
direct joystick reading routine.
The Joystick is connected to Line &49 of the keyboard matrix.
Key input on the CPC goes through the 8255 PPI and AY soundchip, we
need to use the PPI to talk to the AY, and tell it to read input
through reg 14
There are 4 ports we need to use
Port &F6C0 = Select Register
Port &F600 = Inactive
Port &F6xx = Receive Data
Port &F4xx = Send Data to selected Register |
 |
| |
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!
|