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.)
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.
|
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
| |
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!
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!
|