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
S19 - Keypad Reading on the
Commodore PET!
Let's use the keyboard to move a simple character block
'sprite' around the screen of the Commodore Pet!
PET_keys.asm
Defining Parameters, and drawing sprites
First we're going to need some zero page bytes to define an X,Y
position.
Our 'sprites' will have 2 frames of animation (stored in FrameNo),
and we'll use different 'pairs' for Up+Down, Left and Right (Stored
in FrameBase)
We're going to need to define some frames of animation for our
test. Each frame is 4x4 characters (8x8 pixels).
There are two frames per direction, with 3 directions (Up and Down
use the same sprite)
These were created with my AkuSprite editor!
Given a 'Sprite number' we need to multiply this by 16 to get the
offset for the sprite. (each sprite is 4x4, 16 bytes)
We use GetVDPScreenPos to calculate a VRAM address from the X,Y
co-ordinate, and GetNextLine to move down one VRAM line.
We copy all the bytes from the sprite source z_hl to the VRAM
destination z_de
BlankPlayer is an alternate version which removes the sprite from
the screen.
GetVDPScreenPos calculates the VRAM address of the screen position
specified by X,Y registers, in Zero page entries z_d+Z_e
The screen base is $8000 in ram, and each line is 40 bytes, so our
formula is:
VRAM= $8000 + Ypos *40 + Xpos
As it's hard to multiply by 40, we multiply Ypos by 8 and 32, and
add the two together!
To move down a line, we add 40 to the current VRAM position
This works fine on the
standard 40 character screen.
If you're using 80 character wide screen modes, you'll need alter
the formula!
Reading the Keyboard
To read a line of the keyboard, we first write the line number to
memory mapped port $E810,
We then read a byte from $E812 - each bit will be a different key -
A 0 means the key is pressed.
A 1 means the key is not pressed.
We test each cursor key, and Space and Enter (we can use these as
fires)
Our test program
We're going to define the starting position of our character - z_h
will store our joystick buttons - we initialize this to zero - as we
need to run the main loop but don't want to cause any keypresses.
Next comes the joystick reading routine - we skip this the first
run
We read in from the joystick into z_H - until a key is pressed we'll
wait in an infinite loop... when it is pressed we start our drawing
routine...
First we clear the old player sprite from the screen.
We test each of the bits in z_h (which was returned from the
joystick routine)
In each case, if the bit is 0 then the button isn't down - and we
skip over the routine to move the character's X or Y position
Before moving we check if we're already at the 'limit' of the screen
- we don't want to go over the edges, so we skip moving if we are
already at the edge.
We save the new XY position.
We then 'flip' the frame (0 and 1)... this gives a 'walking'
animation.
We then draw the new player position... wait a while and repeat.
Here is the result!
Lesson
S20 - Sprite Clipping on the BBC
We've looked before at drawing a bitmap to the screen, but there's a
challenge!
For many games we'll need to be able to draw sprites that are
'partially off' the screen, We'll clip off the offscreen part.
Lets look at the upgraded code.
BBC_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
The joystick and
sprite routines used here are based on those in the simple series,
but a few changes will be needed.
The sprite routine will have 'clipping' and is likely to be slower
than the unclipped version, so if your sprite can't go offscreen
this may not be the best option.
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
at the start of our drawing routine we need to calculate the VRAM
destination of the sprite,
We multiply the 'Width' by 8, to match the X offset of the sprite -
this is because there's 8 lines 'between' each horizontal byte in
VRAM
Unlike our previous bitmap example, Our Sprite drawing routine
does each horizontal line one at a time, not 8x8 blocks, this means
we add 8 to the VRAM address after each byte.
We repeat for the width of the sprite.
After each line, we need to move down the VRAM address one line,
what we need to do to achieve this depends on the line.
for the first 7 lines we just add $0001, but after 8 lines we need
to add $0200
After each line, we may need to 'skip over' any clipped bytes of
our source bitmap to get to the next line.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, 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 z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to physical units
(Horizontal bytes and vertical 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
Lesson
S21 - Sprite Clipping on the C64
We've looked before at drawing a bitmap to the screen, but we need
to do better!
For many games we'll need to be able to draw sprites that are
'partially off' the screen, We'll clip off the offscreen part.
Lets look at the upgraded code.
C64_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
The joystick and
sprite routines used here are based on those in the simple series,
but a few changes will be needed.
The sprite routine will have 'clipping' and is likely to be slower
than the unclipped version, so if your sprite can't go offscreen
this may not be the best option.
on the C64,
we're using the larger screen size of 160x200...
We're also using 4 color mode and narrower tiles of 4x8
On the C64,The logical units are still based on the principle a
single tile is 4x4 logical units.
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
at the start of our drawing routine we need to calculate the VRAM
destination of the sprite,
We multiply the 'Width' by 8, to match the X offset of the sprite -
this is because there's 8 lines 'between' each horizontal byte in
VRAM
Unlike our previous bitmap example, Our Sprite drawing routine
does each horizontal line one at a time, not 4x8 blocks, this means
we add 8 to the VRAM address after each byte.
We repeat for the width of the sprite.
After each line, we need to move down the VRAM address one line,
what we need to do to achieve this depends on the line.
for the first 7 lines we just add $0001, but after 8 lines we need
to add $0140
After each line, we may need to 'skip over' any clipped bytes of
our source bitmap to get to the next line.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, 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 z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to physical units
(Horizontal bytes and vertical 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
Lesson
S22 - Sprite Clipping on the VIC-20
We looked last before at using custom characters to draw a bitmap
'sprite' on the screen.
This time we'll extend the example, and add the ability to 'clip'
the sprite so it can partially go offscreen.
VIC_Clipping_Rom.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 joystick and
sprite routines used here are based on those in the simple series,
but a few changes will be needed.
The sprite routine will have 'clipping' and is likely to be slower
than the unclipped version, so if your sprite can't go offscreen
this may not be the best option.
The VIC 20
can't do our ideal 256x192... it's
22*23 tile screen gives us an effective 'Logical' resolution of
88*96 units
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
on the Vic we have two versions of this code...
DrawPlayer shows the Chibiko graphic,
BlankPlayer removes it.
If there is nothing onscreen, 'DoCrop' will return the Carry set.
If there's something to draw, we'll start by calculating the Vram
destination position.
We transfer the tiles that make up our character to VRAM at z_de
($1E00+)... we also set the matching colors in color ram at $9600+
We repeat for the full width of the line, however after each line,
if the sprite is horizontally cropped, we need to remove
'spritehclip' tiles.
We then repeat for the other remaining lines.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, if
the amount to crop is not less than the height then the sprite is
completely offscreen.
We have to mask using "and #%11111100" to compensate for the fact
this system can only work in 8x8 pixel tiles (4 logical units)
Anything else is the number of lines we need to remove from the top,
we store this in z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to physical units
(Horizontal bytes and vertical 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
Lesson
S23 -Sprite Clipping on the Atari 800 / 5200
Lets port our sprite clipping routine to the Atari systems, We'll
'upgrade' our previous sprite routine, so it can crop the sprites so
they are partially offscreen.
A52_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
The joystick and
sprite routines used here are based on those in the simple series,
but a few changes will be needed.
The sprite routine will have 'clipping' and is likely to be slower
than the unclipped version, so if your sprite can't go offscreen
this may not be the best option.
We're going to
use 4 colors on the Atari 800/5200... which means
we'll use a screen resolution of 160x192 pixels, and a logical
resolution of 160x96... meaning our tiles are 4x8 pixels on this
system, not 8x8 like most.
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
DrawPlayer shows the Chibiko graphic using XOR
If there is nothing onscreen, 'DoCrop' will return the Carry set.
If there's something to draw, we'll start by calculating the Vram
destination position.
We transfer bitmap data from the source address in zero page pair
z_iy.
We XOR this with the screen data at z_de.
We repeat this for all the bytes of the line, we then need to move
down a line, and maybe crop the source data
After each line we need to move the VRAM destination down one line
- each line is 40 bytes
We need to update the source bitmap byte... we do this by adding Y
to the source address z_iy
If we had to crop the left or right hand side of the sprite, we
will need to skip some bytes before the next line of the source
bitmap.
We do this now.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, 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 z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to physical units
(Horizontal bytes and vertical 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
Lesson
S24 - Sprite Clipping on the Apple II
The Apple II screen is kind of Black and white - and Kind of
color!... depending on the combination... lets learn what it all
means, and get a smiley onscreen!
AP2_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
The joystick and
sprite routines used here are based on those in the simple series,
but a few changes will be needed.
The sprite routine will have 'clipping' and is likely to be slower
than the unclipped version, so if your sprite can't go offscreen
this may not be the best option.
The Apple II
screen is weird! each byte stores 7 pixels and one color bit! for
this reason although our tile is 4x4 logical units, but this will
represent a physical 7x8 pixel bitmap tile.
We'll only crop horizontally in byte increments anyway.
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
DrawPlayer shows the Chibiko graphic using XOR
If there is nothing onscreen, 'DoCrop' will return the Carry set.
If there's something to draw, we'll start by calculating the Vram
destination position.
We transfer bitmap data from the source address in zero page pair
z_iy.
We XOR this with the screen data at z_de.
We repeat this for all the bytes of the line, we then need to move
down a line, and maybe crop the source data
After each line we need to move the VRAM destination down one
line... unfortunately on the Apple II this is a little tricky!
Depending on the bits of our Y line, how we need to adjust our Vram
pos will vary.
The address of the line is calculated from the following bits
%AABBBCCC - AA*$0028 BBB*$0080
CCC*$0400
In this part of the code, the Low byte z_E will be stored in Y, and
the High byte z_D will be stored in X.
To move down a line, first we inc the CCC part, by adding $04 to the
high byte of the VRAM address.
If that overflows, we need to update the BBB part, which uses bit 7
of the low byte, and bits 0,1 of the High byte of the VRAM address
If that overflows we need to update the AA part, by adding $28 to
the low byte of the Low byte of the VRAM address
We need to update the source bitmap byte... we do this by adding Y
to the source address z_iy
If we had to crop the left or right hand side of the sprite, we
will need to skip some bytes before the next line of the source
bitmap.
We do this now.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, 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 z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to physical units
(Horizontal bytes and vertical 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
Lesson
S25 - Sprite Clipping on the Atari
Lynx
We drew a sprite to the screen before, now lets upgrade it with
some sprite clipping.
LNX_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
The joystick and
sprite routines used here are based on those in the simple series,
but a few changes will be needed.
The sprite routine will have 'clipping' and is likely to be slower
than the unclipped version, so if your sprite can't go offscreen
this may not be the best option.
The LYNX screen
is pretty small!... we'll only have screen space for a logical
screen of 80x51 units
Of course, this smaller logical visible screen will still be
centered in a larger 256x256 logical screen
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
If there is nothing onscreen, 'DoCrop' will return the Carry set.
If there's something to draw, we'll start by calculating the Vram
destination position.
We transfer bitmap data from the source address in zero page pair
z_iy.
We XOR this with the screen data at z_de.
We repeat this for all the bytes of the line
After each line we need to do 3 things.
1. increase z_IY by the number of bytes we drew to the screen
2. add any 'skipped' horizontal bytes to z_IY which are missed due
to cropping
3. add $50 bytes to the VRAM destination to move down 1 line.
We then repeat for the next line of our sprite
Page flipping
To avoid any pesky flicker,
we're using 2 memory addresses for screen drawing... $B800 and $D800
We draw to one, and show the other... then flip them over - this
means the viewer never sees the drawing screen.
Before the next draw we clear the screen...
the screen is $2000 bytes in size (Well.. actually $1FE0) so we
write zeros to all these based on our current 'ScreenBuffer'
address.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, 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 z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to physical units
(Horizontal bytes and vertical lines).
There's 2 pixels per logical unit, and 2 per horizontal byte, so no
corrections are needed on the X axis for the Lynx, but the Y axis
needs doubling.
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
S26 - Tile Bitmap Clipping
on the NES
We drew our 'Chibiko' mascot on the NES using the tilemap
before, this time we'll improve the code, allowing the image to
be clipped.
Lets have a go!
NES_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
Our logical units
are in 2x2 pixel blocks, but our tiles are 8x8 pixels, therefore
we can only draw movements of our graphics in 4x4 logical unit
'jumps'
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the first tile pattern
If there is nothing onscreen, 'DoCrop' will return the Carry set.
We don't actually write to the tilemap, we write to a cache of
changes that will be written during the next vblank
GetVDPScreenPos calculates and writes the address of the tile to
change, writing it in the tilemap.
We then write the new tile number... we repeat this for each line of
the tilemap
After a line of the tilemap, we update the source tile number to
compensate for any clipping with "NoSpriteClip"
When we want to change a tile, we calculate the address of the
tile we want to change in VRAM, we write the HL pair of the
destination address into the VDPBuffer, a third byte needs to be
written to tell the NMI what tile to set.
We can only set a total of 32 tiles per NMI in this way, so if there
are already 32*3 bytes in the buffer we wait for the VBlank
The NMI interrupt handler transfers all the tiles to change during
VBLANK.
The count of tiles is held in VDP_CT
The actual data is stored in the VDP_Buffer
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, if
the amount to crop is not less than the height then the sprite is
completely offscreen.
We need to mask the count to a whole number of tiles, we do this
with AND #%11111100
Anything else is the number of lines we need to remove from the top,
we store this in z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to a tile count -
effectively dividing Xpos,Ypos, Width and Height by 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
The
Tilemap is easy to use, but hardly allows us smooth
movement...
Next time we'll upgrade the code to use Hardware sprites... and
get things really moving!
Lesson
S27 - Hardware Sprite Clipping
on the NES
Using the Tilemap isn't so useful for moving objects, as we
can't shift the co-ordinates at the pixel level,
This time, lets do the same as last time, but we'll make our
48x48 pixel sprite out of 6x6 hardware sprites for smooth moves!
NES_Bitmap_Clipping_Hsprite.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
Our logical units
are in 2x2 pixel blocks, but our tiles are 8x8 pixels, therefore
we can only draw movements of our graphics in 4x4 logical unit
'jumps'
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the first tile pattern
If there is nothing onscreen, 'DoCrop' will return the Carry set.
We need to build our object out of 8x8 pixel hardware sprites,
we'll need to keep a pointer to the next available hardware sprite,
we use 'HspriteNum' for this.
We need to set the 4 bytes that define each sprite, we write these
to the buffer 'SpriteBuffer' - this is copied to sprite VRAM during
the V-Blank NMI.
Each sprite needs to be spaced 8 pixels apart, so we add 8 to the
Xpos after each sprite, and 8 to the Ypos after each line
The NMI interrupt handler transfers the buffer to sprite ram with
port $4014.
We just write the top byte of the buffer address to this port
our Chibiko usually uses 36 sprites, but if it goes partially
offscreen it will use less,
We need to zero the unused sprites to clear these.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, if the amount to crop is not less than the height then
the sprite is completely offscreen.
If the top line is partially off the screen we need to convert the
ypos so the sprite starts partially offscreen, we do this with EOR
#%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 need to convert it to a number of sprites (4 logical units) with
AND #%11111100
We've calculated the top and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) based on the
number of lines we need to remove from the top.
We add 4 to the amount to remove, as we're overstating the size of
the screen by 4 logical units to allow for the 'partially offscreen'
sprites drawn at the top.
now we do the same for the X axis
First we remove the xpos of the first visible pixel... 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, 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 z_e and adjust the starting draw with an EOR
#%00000011 to correct the starting position to adjust for the
removed sprites
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units to pixels and spritesto a tile count
- effectively dividing Width and Height by 4 and multiplying
Xpos and Ypos 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
Remeber! The NES
only has 64 hardware sprites, so this will only work for a maximum
single object of 8x8 tiles (64x64 pixels) - so drawing 48x48 pixel
sprites like this isn't so realistic for a complete game
That's why we used tiles last time, as we can draw as many tiles
as we want!
Lesson
S28 - Tile Bitmap Clipping
on the SNES
We drew our 'Chibiko' mascot on the SNES using the tilemap before,
this time we'll improve the code, allowing the image to be clipped.
Lets have a go!
SNS_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
Our logical units
are in 2x2 pixel blocks, but our tiles are 8x8 pixels, therefore
we can only draw movements of our graphics in 4x4 logical unit
'jumps'
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the first tile pattern
If there is nothing onscreen, 'DoCrop' will return the Carry set.
We don't actually write to the tilemap, we write to a cache of
changes that will be written during the next vblank
GetVDPScreenPos calculates the address in the cache to change in HL
We then write the new tile number... we repeat this for each line of
the tilemap
After a line of the tilemap, we update the source tile number to
compensate for any clipping with "NoSpriteClip"
When we want to change a tile, we calculate the address of the
tile we want to change in VRAM, we write the HL pair of the
destination address into the VDPBuffer, a third byte needs to be
written to tell the NMI what tile to set.
We can only set a total of 32 tiles per NMI in this way, so if there
are already 32*3 bytes in the buffer we wait for the VBlank
The NMI interrupt handler transfers all the tiles to change during
VBLANK.
We use the DMA to do this... but it has a lot of settings we need to
configure to transfer the data!
Here's the correct settings for the tilemap.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, if
the amount to crop is not less than the height then the sprite is
completely offscreen.
We need to mask the count to a whole number of tiles, we do this
with AND #%11111100
Anything else is the number of lines we need to remove from the top,
we store this in z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to a tile count -
effectively dividing Xpos,Ypos, Width and Height by 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
The
Tilemap is easy to use, but hardly allows us smooth
movement...
Next time we'll upgrade the code to use Hardware sprites... and
get things really moving!
Lesson
S29 - Hardware Sprite Clipping on
the SNES
Using the Tilemap isn't so useful for moving objects, as we can't
shift the co-ordinates at the pixel level,
This time, lets do the same as last time, but we'll make our 48x48
pixel sprite out of 6x6 hardware sprites for smooth moves!
SNS_Bitmap_Clipping_Hsprite.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 can now move our
sprite in pixels, but our sprites are still 8x8 pixel ' blocks',
we'll need to combine lots of hardware sprites to make our
character (6*6=36 hsprites)
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the first tile pattern
If there is nothing onscreen, 'DoCrop' will return the Carry set.
We shift our X and Y position by -8
This is to allow sprites to be partially offscreen at the top and
left of the screen.
If our Xpos goes negative, we need to set the top bit (Xpos can be
0-511) - this is stored in z_L
We need to build our object out of 8x8 pixel hardware sprites,
we'll need to keep a pointer to the next available hardware sprite,
we use 'HspriteNum' for this.
We use "SetHardwareSprite" to set the hardware sprites
Each sprite needs to be spaced 8 pixels apart, so we add 8 to the
Xpos after each sprite, and 8 to the Ypos after each line
After each line of the sprite, if the sprite has been cropped on the
left or right we skip the patterns with spritehclip
Each of the sprites use 2 words in the first 256 word range...
there's also 2 bits in the second 256 word range (4 sprites are
combined in one byte)
We use the DMA to transfer the sprite cache during NMI...
but it has a lot of settings we need to configure to transfer the
data!
our Chibiko usually uses 36 sprites, but if it goes partially
offscreen it will use less,
We need to zero the unused sprites to clear these.
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, if the amount to crop is not less than the height then
the sprite is completely offscreen.
If the top line is partially off the screen we need to convert the
ypos so the sprite starts partially offscreen, we do this with EOR
#%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 need to convert it to a number of sprites (4 logical units) with
AND #%11111100
We've calculated the top and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) based on the
number of lines we need to remove from the top.
We add 4 to the amount to remove, as we're overstating the size of
the screen by 4 logical units to allow for the 'partially offscreen'
sprites drawn at the top.
now we do the same for the X axis
First we remove the xpos of the first visible pixel... 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, 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 z_e and adjust the starting draw with an EOR
#%00000011 to correct the starting position to adjust for the
removed sprites
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units to pixels and spritesto a tile count
- effectively dividing Width and Height by 4 and multiplying
Xpos and Ypos 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
S30 - Tile Bitmap Clipping on
the PC-Engine/Turbografix
We drew our 'Chibiko' mascot on the PC-Engine using the tilemap
before, this time we'll improve the code, allowing the image to be
clipped.
Lets have a go!
PCE_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
Our logical units
are in 2x2 pixel blocks, but our tiles are 8x8 pixels, therefore
we can only draw movements of our graphics in 4x4 logical unit
'jumps'
Our Sprite Routine
Before we draw our sprite we need to crop it with "DoCrop"
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the first tile pattern - We use tile pattern
384 (128+256)
If there is nothing onscreen, 'DoCrop' will return the Carry set.
When we want to write to the tilemap, we first use
'GetVDPScreenPos' to select the destination memory address from XY
co-ordinate.
We need to select Video reg #2 with ST0, we then write the tile
number to $0102/3
After one line, we may need to skip some patterns before starting
the next line (if our sprite is off the sides of the screen) - we do
this with SpriteHClip.
When we want to change a tile, we calculate the address of the
tile we want to change in VRAM. We need to use Video Reg #0 to
select the memory address, we then write the address to $0102/3
Our Tilemap starts at memory address $0000, the tilemap is 32*32,
each address holds 1 word (One tile), so our formula is
VRAM= Ypos*32 + Xpos
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
Zeropage pair z_bc is the X,Y co-ordinate in logical units
Zeropage pair z_hl is the Width,Height in logical units
Zeropage pair z_iy is the source bitmap address of the sprite data
First we zero z_DE - it's 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... 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, if
the amount to crop is not less than the height then the sprite is
completely offscreen.
We need to mask the count to a whole number of tiles, we do this
with AND #%11111100
Anything else is the number of lines we need to remove from the top,
we store this in z_e and set the new 'draw position' to Ypos (z_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 and bottom crop... we now use these to
calculate the new height of the sprite.
We then skip over any bytes in the source (z_iy) 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... 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, 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 z_e and set the new 'draw position' to Xpos
(z_c) =0
Next we do the same for the right,
We add the width 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
We've calculated the left and right crop... we now use these to
calculate the new width of the sprite.
We then skip over any bytes in the source (z_iy) based on the number
of bytes we need to remove from the left.
We've finished cropping our sprite!... but we need to convert our
co-ordinates from logical units (pairs of pixels) to a tile count -
effectively dividing Xpos,Ypos, Width and Height by 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
The
Tilemap is easy to use, but hardly allows us smooth
movement...
Next time we'll upgrade the code to use Hardware sprites... and
get things really moving!