Simple sample code on a variety of Z80 machines
<- Back to the Main Contents & Basic Z80 Assembly Lessons


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