Learn Multi platform 6502 Assembly Programming... For Monsters!

Super Simple Series

In this series we'll take a look at a variety of basic tasks that you may need to look at when you're starting out with programming these classic systems...

In each case the source-code will be a single file with no Includes to make things easy if you're using a different assembler (though some may include a binary.)


Lesson S16 - Per-Pixel Simple Sprite Clipping on the Atari ST
Our last example was a bit jerky moving horizontally. Because of the bitplane layout we were moving in 1 byte chunks... this time we'll add bit shifting for some smooth moves!


AST_BitmapClipping.asm


Logical Units and clipping

Our new cropping routine will bitshift the sprite by up to 8 pixels.

This means that some of the first and/or last byte of the sprite may not be shown.

To 'solve this' we've added a 1 byte (8 pixel) wide strip of space on either side of our character's bitmap data.

For clarity this has been made white so we can see what's happening, but really it should be black!
Here is the raw sprite data in AkuSprite editor
Rather than padding our sprite in this way we could instead we could make our code handle the first and last byte differently, but this would make the code longer and more complicated.


Our updated Sprite Routine
We need one small change to our cropping routine... we now need a 'partial byte' Xpos so we can calculate the bit shift.


Our cropping routine will 'lose' 8 pixels of our sprite, so we also need to increase the width of the logical screen by 4 units to compensate.
We reduce the width of our sprite by 8 pixels, to remove half the 'padding' (the other half is needed by the pixel shifting routine)
We need to fix up our co-ordinates.

Our routine works horizontally in 8 pixel blocks, and we need to reduce our width (in D3) by one for DBRA
We vconvert the Xpos to byte blocks (8 pixels)

We need to convert our height (D6) and Ypos (D4) from logical units to lines

We then use GetScreenPos to get the VRAM destionation (in register A2)

Our sprite routine still works in bytes, so we need to take the 'unused' part of the X-pos to calculate the bits we need to shift the source bitmap.... we store this in D5
GetVDPScreenPos will calculate the VRAM destination A2 from X,Y pos (D1,D4)

We have each 320 pixel line is 160 bytes, but we have to cope with the fact that pairs of bytes are grouped together, but the bitplanes of those pairs are in 8 consecutive bytes in vram.


Lesson S17 - Tilemap clipping on the Genesis
We drew our chibiko character on the screen before with the tilemap and moved it around the screen, but this time we'll improve the routine, and allow the bitmap to go partially offscreen!

Lets give it a go.

GEN_BitmapClipping.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


Here we're going to use the tilemap to draw our character to the screen, so our sprites will move by 8x8 chunks.

Next time we'll do the same with hardware sprites.

Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"

registers D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register D7 is the first tile number we'll use to draw our character

If there is nothing onscreen, 'DoCrop' will return the Carry set.


We need to draw the grid of tiles to the screen.

First we calculate the XY address in the tilemap in VRAM

We then transfer all the numbered tiles into VRAM,

If our sprite is horizontally cropped, we need to skip some tiles with spritehclip

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 D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register A6 is the address of the source bitmap data.

First we zero D2,D5 - they are used for temp values, and spritehclip which is used for the horizontally skipped bytes after each line
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw ypos (D4)... 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 (D6), 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 D5 and set the new 'draw position' to Ypos (D4) =0

We use 'and.l #%11111100,d0' to round to a whole 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've calculated the top (d5) and bottom (d2) crop... we now use these to calculate the new height of the sprite (D6).
We then skip over any bytes in the source (D7) based on the number of lines of tiles 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 (D1) ... 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 (D3), 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 D5 and set the new 'draw position' to Xpos (D1) =0
Next we do the same for the right,

We add the width (D3) 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 (d2)
We've calculated the left and right crop... we now use these to calculate the new width of the sprite (D3).

We then skip over any bytes in the source (D7) based on the number of bytes we need to remove from the left (D5).
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



Lesson S18 - Hardware Sprite Clipping on the Genesis
Our last example was distinctly disappointing!... using the Tilemap is easy, but gave jerky movement!

Lets smooth things over and make things move more neatly!

GEN_BitmapClippingHsprite.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'll use hardware sprites... wooo!
We'll build up our big sprite out of little 8x8 ones... our 48x48 sprite will need 6x6 tiles - 36 in total!
We could use bigger sprites, but for convenience we'll stick to 8x8 ones.

Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"

registers D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register D7 is the first tile number we'll use to draw our character

If there is nothing onscreen, 'DoCrop' will return the Carry set.

First we need to convert our logical units.

We need the X and Y pos in pixels.

We need the width and height in tiles (8x8 pixel blocks)

We need to allocate each of the 36 sprites a separate hardware sprite number, we use consecutive sprites defined by D5.
We need to set all the hardware sprites in a grid to make the Chibiko sprite.

for each we define the X and Y position and sprite pos.

Each sprite needs to link to the next hardware sprite number, so we INC D5 with each sprite.
We need the last hardware sprite to point back to the first.

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 D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register A6 is the address of the source bitmap data.

First we zero D2,D5 - they are used for temp values, and spritehclip which is used for the horizontally skipped bytes after each line
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw ypos (D4)... 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 (D6), 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 D5 and set the new 'draw position' to Ypos (D4) =0

We need to EOR the bottom two bits to correct the Y position as we remove tiles from the top.
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 (d5) and bottom (d2) crop... we now use these to calculate the new height of the sprite (D6).
We then skip over any bytes in the source (D7) based on the number of lines of tiles we need to remove from the top.

We're still working in 8x8 pixel tiles when working with hardware sprites.
now we do the same for the X axis
First we remove the xpos of the first visible pixel from the draw Xpos (D1) ... 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 (D3), 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 D5 and set the new 'draw position' to Xpos (D1) =0

We need to EOR the bottom two bits to correct the X position as we remove tiles from the left.
Next we do the same for the right,

We add the width (D3) 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 (d2)
We've calculated the left and right crop... we now use these to calculate the new width of the sprite (D3). Again we have to work in 8x8 tiles.

We then skip over any bytes in the source (D7) based on the number of bytes we need to remove from the left (D5).
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

etely offscreen, there's no point trying to draw, so we set the carry and return

Lesson S19 - Simple Sprite Clipping on the Sinclair QL
Lets extend our Sinclair QL example, and allow the sprite to go partially offscreen


SQL_BitmapClipping.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


The QL screen is 256x256 in 8 color mode, so in this case our logical screen will be 128 x 128

Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"

registers D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register A6 is the address of the source bitmap data.

If there is nothing onscreen, 'DoCrop' will return the Carry set.
We use GetScreenPos to get the destination address in A2, from the XyPos in D1,D4

We transfer 2 bytes (1 word) per 4 pixels along each line of our sprite,

After a line, we may need to skip a few bytes (spritehclip) before the next line due to cropping,

We repeat until the sprite is done.
GetVDPScreenPos will calculate the VRAM destination A2 from X,Y pos (D1,D4)

Each 4 pixel block takes 2 bytes, each line is 128 bytes, and the screen base is $20000

Therefore our VRAM address formula is:

VRAM = $20000+ (Xpos * 2) + Ypos * 128)

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 D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register A6 is the address of the source bitmap data.

First we zero D2,D5 - they are used for temp values, and spritehclip which is used for the horizontally skipped bytes after each line
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw ypos (D4)... 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 (D6), 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 D5 and set the new 'draw position' to Ypos (D4) =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 (d5) and bottom (d2) crop... we now use these to calculate the new height of the sprite (D6).
We then skip over any bytes in the source (A6) based on the number of lines 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 (D1) ... if the result is greater than zero, then nothing is off the screen at the left.

Anything else is the number of lines we need to remove from the left, we store this in D5 and set the new 'draw position' to Xpos (D1) =0
Next we do the same for the right,

We add the width (D3) 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 (d2)
We've calculated the left and right crop... we now use these to calculate the new width of the sprite (D3).

We need to work in 4 pixel blocks, so we mask with 'and #%11111110,d0'.

We then skip over any bytes in the source (A6) based on the number of bytes we need to remove from the left (D5).
We need to convert our logical units into 'proper co-ordinates'

We convert the Ypos & Height into lines by multiplying by 2.

We change the Xpos and width into bytes (Clusters of 4 pixels) by dividing by 2.

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



Lesson S20 - Fix layer clipping on the NeoGeo
Lets take our previous NeoGeo example where we drew a graphic on the 'Fix layer' and add clipping, so the image can go partially offscreen.

NEO_BitmapClipping.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


Here we're going to use the Fix layer to draw our character to the screen, so our sprites will move by 8x8 chunks.

Next time we'll do the same with hardware sprites.

Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"

registers D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register D7 is the first tile number we'll use to draw our character

If there is nothing onscreen, 'DoCrop' will return the Carry set.


Once the crop is done we convert our co-ordinates from logical units to tiles.
Our FillAreaWithTiles routine will draw the character to the screen.

Before each horizontal line of tiles, we need to calculate the Vram Destination into D5

our formula is Vram= $7000 + (Xpos * 32) + (Ypos) + 2


We transfer the tiles to the screen one at a time.

The NeoGeo Fix layer is organized down, then across... but our code is designed to work across, then down.


to correct this we add 32 after each block - effectively moving across the screen one block, we then repeat until the character is done.

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 D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register A6 is the address of the source bitmap data.

First we zero D2,D5 - they are used for temp values, and spritehclip which is used for the horizontally skipped bytes after each line
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw ypos (D4)... 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 (D6), 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 D5 and set the new 'draw position' to Ypos (D4) =0

We use 'and.l #%11111100,d0' to round to a whole 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've calculated the top (d5) and bottom (d2) crop... we now use these to calculate the new height of the sprite (D6).
We then skip over any bytes in the source (D7) based on the number of lines of tiles 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 (D1) ... 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 (D3), 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 D5 and set the new 'draw position' to Xpos (D1) =0
Next we do the same for the right,

We add the width (D3) 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 (d2)
We've calculated the left and right crop... we now use these to calculate the new width of the sprite (D3).

We then skip over any bytes in the source (D7) based on the number of bytes we need to remove from the left (D5).
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


Lesson S21 - Hardware Sprite clipping on the NeoGeo
Lets do more sprite clipping... this time with hardware sprites!

NEO_BitmapClippingHsprite.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 for smooth moves, however the hardware sprites are 16x16, so our code will need quite a few changes!

Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"

registers D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register D7 is the first tile number we'll use to draw our character

If there is nothing onscreen, 'DoCrop' will return the Carry set.


Once the crop is done we convert our co-ordinates from logical units to pixel co-ordinates, and width and height in 16 pixel sprites.
We need to draw our chibiko character out of hardware sprites.

We set the X,Y position of each hardware sprite - using D5 as the hardware sprite number.

Subroutine SetSprite will do the job of defining the hardware sprite in Vram.

after each line, if there's any horizontal cropping, we use 'spritehclip' to remove the unwanted sprites
Ok, lets set up the sprite...

First we're going to set up the 'Shrink' scale... this is at address $8000 + the sprite number
We select the address in VRAM using memory mapped port $3C0000

we set all the scale bits to 1 ($0FFF)... this is 'Normal size' - anything else would be smaller...
We set the new value using memory mapped port $3C0002     
Next we want to set the Y position... the address in VRAM we need to write to is $8200+ the sprite number - so we add $200 to our write position...

We need to shift our Y position to bit 7 of the word, as the low bits handle the height (and horizontal chaining) of the sprite we need to add 1 to the low byte - so our sprite has a height of 1     
Next is the turn of the X position.... this has it's VRAM address at &8400+ the sprite number, so again we add $200 our write position     
The rest of the attributes are at VRAM address $0000 + sprnum*64

We multiply the sprite number in D0 by 64 by bitshifting to the left 6 times     
Next we need to set the palette at $0001+sprnum*64... so we add one to the last address

We need to set the top byte to our palette number.
In this simple example, we don't use the low byte... the low byte handles V/H flipping, Automatic sprite animation, and the top 4 bits of the Tile Number (it's 20 bit in total)
As we crop our sprite, the number of hardware sprites the character will need may go down.

If we do nothing, now unneeded sprites may 'trail' as the sprite is cropped.

To resolve this we set the position of any unneeded sprites to pos 255,255 - making them invisible.

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 D1,D4 is the X,Y co-ordinate in logical units
registers D3,D6 is the Width,Height in logical units
register A6 is the address of the source bitmap data.

First we zero D2,D5 - they are used for temp values, and spritehclip which is used for the horizontally skipped bytes after each line
Ok... lets crop the top of the sprite...
First we remove the ypos of the first visible pixel from the draw ypos (D4)... 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 (D6), 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 D5 and set the new 'draw position' to Ypos (D4) =0

we EOR with 'eor.b #%00000111,d0' to correct the draw position as 16 pixel (8 logical unit) tiles are removed.
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 (d5) and bottom (d2) crop... we now use these to calculate the new height of the sprite (D6).
We then skip over any bytes in the source (D7) based on the number of lines of sprites 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 (D1) ... 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 (D3), 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 D5 and set the new 'draw position' to Xpos (D1) =0
Next we do the same for the right,

We add the width (D3) 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 (d2)
We've calculated the left and right crop... we now use these to calculate the new width of the sprite (D3).

We then skip over any tiles in the source (D7) based on the number of tiles we need to remove from the left (D5).
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