Learn Multi platform
6502 Assembly Programming... For
Monsters!
Platform
Specific Lessons
Platform Specific Series - Now we
know the basics, lets look at the details of the platforms we're covering!
MaxTile Definitions
| Using the 4 color screen, unlike the other systems, the tiles
are only 4x8 (not 8x8)... this gives a 'virtual screen' resolution
of 128x192 pixels (the screen is actually 160 pixels wide) |
 |
Maxtile Caches draws to the screen in 3 thirds to reduce
flicker.
We use some spare memory for the 3 draw caches (each 256 bytes)
we also use 256 bytes for the 'Xflip lookup table'
The VRAM base of the screen is $2060 - we center the virtual
screen by adding 4
|
|
Here is the Tile pattern data
The tiles are 8x8 in 2 color mode, or 4x8 in 4 color mode - either
way each tile is 8 bytes.
'Fill tiles' are a special case, they fill a tile with 2 bytes
(saving time and memory)
|
|
We'll calculate an Xflip
LUT
We swap the pixels in each byte, storing the flipped equivalent in
the LUT
This saves memory compared to storing alternative patterns.
There are two versions
Yflip does not require a lookup table, we instead move UP VRAM
instead of DOWN as we draw the pattern!
|
|
When we want to draw a sprite object to the
screen, we need to calculate the VRAM
destination.
MaxTile uses X,Y co-ordinates in 'Logical Units' (Pairs of pixels)
- this is passed in z_BC, and the Vram destination is returned in
z_HL
The screen base is at address $2060 (plus 4 to center the virtual
screen).
Each screen Y line is 40 bytes wide, so we multiply the Y line by
40 via bitshifts, effectively adding Y*32 and Y*8 to get Y*40
We also add the X position in bytes!
|
 |
Tile Drawing!
The calling
routine that exexutes DrawTile will set X and Y to zero, so we
can rely on this being the case at the start of the routine...
however we need to ensure that X and Y still contain zero by the
end of our routine
|
 |
The DrawTile Routine is called by the shared code, This shared
code will backup and restore the stack pointer and load the first
byte of the 16 bit tile number into A
The low bit is shifted out (the update bit)... we need to reset it
to 0 anyway!
The following zero page entries are loaded:
z_BCs = Tilemap
z_DEs = Tile Bitmap Pattern data
z_HL = VRAM Destination
|
 |
To optimize things, the tile drawing works as a 'binary tree',
deciding the kind of tile drawing routine to use
The Platform specific draw routine DrawTile starts by ckecking Bit
1 (now Bit 0)
This is the 'Program' flag - If this is 0, then this is the
simplest unflipped tile, otherwise we switch to the advanced
routine.
If we're drawing a basic tile, we shift a 0 back into A, and write
it back, that clears the Update flag, as we will draw the tile
now.
|
|
We're going to draw a basic unflipped
tile
We need to load the second byte of the tilenumber.
Max tile was designed to multiply the tilenumber by 16, by using
the top 12 bits of the two byte tile value, but we need to
multiply by 8, so we bitshift the value, dividing it by two
We add this to z_DE (the pattern data)
This gives us the source address - which we load into z_HL |
|
We need to write 1 byte for each line,
after each line we add 40 to the VRAM destination to move down to
the next line - we do this by tweaking the Y register, except the
last line, where we would need Y to equal 280 ($118) which is
impossible, so we INC z_H, and set Y to 24 ($18)
We upldate the source in z_HLs by INCing zero page entry z_Ls
We repeat for each 8 lines of our tile.
|
|
If the Program bit was 1, then either we need to
flip, or run some 'custom code'
Next we check the XY bits, if both are 0 this is not a flip
(Transparent, Filled, Double etc)
If either bit (or both) is 1, then this is some kind of flip, so
we calculate the pattern source address, and move it into the
z_HLs |
 |
If the Y flip bit is 0 we must be X flipping!
We load each source pattern byte from z_HSs, then Xflip it via the
Lookup table (in z_BC)
We then write these to the screen in the same way as the unflipped
data.
We move down the screen using the Y register in the same way as
before. |
|
If the Y flip bit was set we now check if the X
flip bit is also set.
If it's not we're just Yflipping!
To flip vertically we write the source pattern data normally, but
we read it backwards from the source pattern, by adding 7 to z_HLs
to move to the last line. |
 |
if the X bit was set as well as Y we need to XY
flip.
We do this with a combination of both 'tricks'
We use the LUT to X flip, and draw to the screen Bottom to Top to
Y flip!
|
|
MaxTile Custom Draw Types
Bits 1,2,3... XYP=%001 defines a custom program
When a custom program is being used Bits 4,5 define the program
type (rather than part of the low tile number)
%00=Filled Tile
%01=Double height tile
%10=unused
%11=Transparent tile/empty tile
The remaining two bits 6,7 act as the High part of the tile number
%------NN nnnnnnnn
|
 |
A double height
tile uses only 4 lines of a pattern, so we multiply the pattern
number by 4 (half our 8 byte tile)
We then draw each line of the pattern twice to the screen.
|
|
The fill tile is
the simplest!
We use 2 bytes from the pattern data, and fill each line with one
of the two
We use z_E for one line, then z_D for one line - this allows us to
make a nice 'checkerboard' patterned fill, or alternate lines in
different colors.
As so little data is read, This routine is the fastest, so should
be used for as much as possible of our tilemap!
|
|
The final type is the Transparent
tile.
if the Tilenumber=255 then this tile is completely transparent,
and no data will be drawn
|
|
Our transparency is a crude '0 byte' transparency.
Basically, any byte equal to 0 is not drawn to the screen, others
are drawn normally.
We use the same pattern data as usual, but as we read in each
byte, we only draw it to the screen if it isn't zero..
|
|
MaxTile Definitions
Using the 4 color screen, unlike the other systems, the tiles
are only 4x8 (not 8x8)...
this gives a 'virtual screen' resolution of 128x192 pixels (the
screen is actually 160 pixels wide)
MaxTile also supports 2 color mode for 8x8 tiles and a screen of
256x192 |
 |
Maxtile Caches draws to the screen in 3 thirds to reduce
flicker.
We use some spare memory for the 3 draw caches (each 256 bytes)
we also use 256 bytes for the 'Xflip lookup table'
The VRAM base of the screen is $6000 - we center the virtual
screen by adding $20 ($6020 total)
|
|
Here is the Tile pattern data
The tiles are 8x8 in 2 color mode, or 4x8 in 4 color mode - either
way each tile is 8 bytes.
'Fill tiles' are a special case, they fill a tile with 2 bytes
(saving time and memory)
|
|
We'll calculate an Xflip
LUT
We swap the pixels in each byte, storing the flipped equivalent in
the LUT
This saves memory compared to storing alternative patterns.
There are two versions
Yflip does not require a lookup table, we instead move UP VRAM
instead of DOWN as we draw the pattern!
|
|
When we want to draw a sprite object to the
screen, we need to calculate the VRAM
destination.
MaxTile uses X,Y co-ordinates in 'Logical Units' (Pairs of pixels)
- this is passed in z_BC, and the Vram destination is returned in
z_HL
The screen base is at address $2000 (plus $20 to center the
virtual screen).
On the C64 blocks of 8 consecutive bytes move down the screen, the
9th byte jumps back up, and is 1 block across from the first byte.
Considering our destination with X in bytes and Y in lines, by
splitting the bits of X and Y, we could consider the calculation
to be:
Vram Address= (%XXXXXXXX * 8) + (%YYYYY--- * 40) + %-----YYY +
$2000
|
 |
Tile Drawing!
The calling
routine that exexutes DrawTile will set X and Y to zero, so we
can rely on this being the case at the start of the routine...
however we need to ensure that X and Y still contain zero by the
end of our routine
|
 |
The DrawTile Routine is called by the shared code, This shared
code will backup and restore the stack pointer and load the first
byte of the 16 bit tile number into A
The low bit is shifted out (the update bit)... we need to reset it
to 0 anyway!
The following zero page entries are loaded:
z_BCs = Tilemap
z_DEs = Tile Bitmap Pattern data
z_HL = VRAM Destination
|
 |
To optimize things, the tile drawing works as a 'binary tree',
deciding the kind of tile drawing routine to use
The Platform specific draw routine DrawTile starts by ckecking Bit
1 (now Bit 0)
This is the 'Program' flag - If this is 0, then this is the
simplest unflipped tile, otherwise we switch to the advanced
routine.
If we're drawing a basic tile, we shift a 0 back into A, and write
it back, that clears the Update flag, as we will draw the tile
now.
|
|
We're going to draw a basic unflipped
tile
We need to load the second byte of the tilenumber.
Max tile was designed to multiply the tilenumber by 16, by using
the top 12 bits of the two byte tile value, but we need to
multiply by 8, so we bitshift the value, dividing it by two
We add this to z_DE (the pattern data)
This gives us the source address - which we load into z_HL |
|
We need to write 1 byte for each line,
To make things easy, this code requires our tile to be vertically
aligned to an 8 line boundary.
as the 8 lines are consecutive bytes in Vram, we just write 8
consecutive bytes from our pattern to VRAM
|
|
If the Program bit was 1, then either we need to
flip, or run some 'custom code'
Next we check the XY bits, if both are 0 this is not a flip
(Transparent, Filled, Double etc)
If either bit (or both) is 1, then this is some kind of flip, so
we calculate the pattern source address, and move it into the
z_HLs |
 |
If the Y flip bit is 0 we must be X flipping!
We load each source pattern byte from z_HSs, then Xflip it via the
Lookup table (in z_BC)
We then write these to the screen in the same way as the unflipped
data.
We move down the screen using the Y register in the same way as
before. |
|
If the Y flip bit was set we now check if the X
flip bit is also set.
If it's not we're just Yflipping!
To flip vertically we write the source pattern data normally, but
we read it backwards from the source pattern, by adding 7 to z_HLs
to move to the last line. |
 |
if the X bit was set as well as Y we need to XY
flip.
We do this with a combination of both 'tricks'
We use the LUT to X flip, and draw to the screen Bottom to Top to
Y flip!
|
|
MaxTile Custom Draw Types
Bits 1,2,3... XYP=%001 defines a custom program
When a custom program is being used Bits 4,5 define the program
type (rather than part of the low tile number)
%00=Filled Tile
%01=Double height tile
%10=unused
%11=Transparent tile/empty tile
The remaining two bits 6,7 act as the High part of the tile number
%------NN nnnnnnnn
|
 |
A double height
tile uses only 4 lines of a pattern, so we multiply the pattern
number by 4 (half our 8 byte tile)
We then draw each line of the pattern twice to the screen.
|
|
The fill tile is
the simplest!
We use 2 bytes from the pattern data, and fill each line with one
of the two
We use z_E for one line, then z_D for one line - this allows us to
make a nice 'checkerboard' patterned fill, or alternate lines in
different colors.
As so little data is read, This routine is the fastest, so should
be used for as much as possible of our tilemap!
|
|
The final type is the Transparent
tile.
if the Tilenumber=255 then this tile is completely transparent,
and no data will be drawn
|
|
Our transparency is a crude '0 byte' transparency.
Basically, any byte equal to 0 is not drawn to the screen, others
are drawn normally.
We use the same pattern data as usual, but as we read in each
byte, we only draw it to the screen if it isn't zero..
|
|
MaxTile Definitions
On the PC engine, we use the hardware tilemap to draw the
Maxtile Tilemap.
The Double height and flipped tiles are 'preconverted' and stored
in VRAM.
This version uses the tilemap for the 'simulated sprites' - there
is an alternative version which uses hardware sprites |
|
Maxtile Caches draws to the screen in 3 thirds to
reduce flicker.
We use some spare memory for the 3 draw caches (each 256 bytes)
The VRAM base of the Tilemap is $6000 - we center the virtual
screen by adding $20 ($6020 total)
FourColorPatterns if defined will switch to 4 color mode.
|
|
Tilemap Definitions
As with everything else on the PC engine Vram... each tile
definition takes one memory address, which contains 2 bytes...
The top 4 bits pppp
define a 16 color palette number from 0-15...
The remaining 12 bits nnnn
nnnnnnnn define the tile number -
as stated, the first 64-256 probably can't be used because they
overlap the tilemap... no's 2048-4095 CANNOT be used, as the
memory these would use would be 64k-128k... and this memory is not
installed in the PC Engine. |
ppppnnnn nnnnnnnn
|
Tile definitions
Tile definitions use 4 bitplanes for 16 colors, and tile
definitions are 8x8 - so 32 bytes ... Data is transferred in Words, and
rather strangely we send bitplane 1+2 of lines, one at a time... then we
do the same for bitplanes 3 and 4
|
Byte
1 |
Byte
2 |
| First
16
bytes |
00111100
01111111
01100011
01100011
01111111
01100011
01100011
00000000 |
00222200
02222222
02200022
02200022
02222222
02200022
02200022
00000000 |
| Second
16
bytes |
00333300
03333333
03300033
03300033
03333333
03300033
03300033
00000000 |
00444400
04444444
04400044
04400044
04444444
04400044
04400044
00000000 |
Sending tiles to VRAM
Here is the Tile pattern
data
We use 4 bytes per tile for the solid fill tiles - these are
always 16 color.
For the Tile patterns, we either use 2 bitplanes (4 color - 16
bytes per tile) or all 4 (16 color - 32 bytes per tile)
These are the unflipped tiles only, we flip and double-height them
in software as required. |
|
We send our data with DefineTilesCombo and DefineFills
DefineTilesCombo will do the Xflip,Yflip,XYflip and doubleheight
conversion for us!
If using 4 color data, we use z_ixh and z_ixl to define the static
color setting for bitplanes 2/3 (0/1 are loaded from the pattern
data)
|
|
Our solid fills
are the simplest, we use the same 2 lines for each of our bytes
We select the VRAM destination with 'ST0 #0'... writing the
destination address to $102/3
we then select 'Data write' with 'ST0 #2' and sends our lines of
data to $102/3...
we send all 8 lines of bitplane 0/1... then all 8 bitplanes of 2/3
|
 |
| We need to process the same data, but convert it to
all the different versions we require. |
|
We need to shift our 'tile destination' adding the offset in YX
to the one in the zeropage DE pair
DE is a memory address, but YX is a tile number, so we multiply by
16 before adding (16 words = 32 bytes)
We then select the VRAM Destination and prepare to write our
tiles.
|
|
We'll need to X flip data in many case - we can do
this by swapping all the bits in the bytes we read.
We have a function to do this conversion...
This means we can use the same code for our Normal+Xflip
we have an alternate one for Yflip+XYFlip - which reads the source
pattern data bottom to top.
|
|
our DefineTilesX routine does our Normal
(unflipped) and X flipped tiles.
We transfer all the lines of our patterns to $102/3
If we're using 4 colors, than after 16 bytes (bitplanes 0/1) we
transfer 8 lines of empty data for bitplanes 2/3 from z_IXH/z_IXL
|
|
For Y flip (also XY flip) we work from the last
line of our source data (bytes 14/15) backwards to our first
(Bytes 0/1)
Because of the Vram layout we do two bitplanes at a time...
for 4 color patterns we write ixh/l to bitplanes 2/3 after we do
0/1 |
 |
The double height tile code is a bit of a pain.
Each tile needs 8 bytes (4 lines) of bitplane 0/1 and 8 bytes (4
lines) of bitplane 1/2
We have two routines to build a double height tile. One for the
first half of the tile, and another to do the second
DoDoubleLines will transfer 4 lines from our pattern
DoDoubleEmptyLine will be used by 4 color patterns.
|
 |
Tile Drawing!
The calling
routine that exexutes DrawTile will set X and Y to zero, so we
can rely on this being the case at the start of the routine...
however we need to ensure that X and Y still contain zero by the
end of our routine
|
 |
When we want to draw a sprite object to the screen, we need to calculate the VRAM destination.
MaxTile uses X,Y co-ordinates in 'Logical Units' (Pairs of pixels)
- this is passed in z_BC, and the Vram destination is returned in
z_HL
The tilemap base is $6000 - to centre our virtual screen we move
two strips down
|
|
The DrawTile Routine is called by the shared code, This shared
code will backup and restore the stack pointer and load the first
byte of the 16 bit tile number into A
The low bit is shifted out (the update bit)... we need to reset it
to 0 anyway!
The following zero page entries are loaded:
z_BCs = Tilemap
z_DEs = Tile Bitmap Pattern data
z_HL = VRAM Destination
|
 |
To optimize things, the tile drawing works as a 'binary tree',
deciding the kind of tile drawing routine to use
The Platform specific draw routine DrawTile starts by ckecking Bit
1 (now Bit 0)
This is the 'Program' flag - If this is 0, then this is the
simplest unflipped tile, otherwise we switch to the advanced
routine.
If we're drawing a basic tile, we shift a 0 back into A, and write
it back, that clears the Update flag, as we will draw the tile
now.
|
|
We're going to draw a basic unflipped
tile
We need to load the second byte of the tilenumber.
Maxtile was designed for 16 bytes per tile, but we just want to
write the tile number, so we shift to the right, effectively
dividing by 16
We add our tile base in DEs, and write into the tilemap |
|
If the Program bit was 1, then either we need to
flip, or run some 'custom code'
Next we check the XY bits, if both are 0 this is not a flip
(Transparent, Filled, Double etc)
If either bit (or both) is 1, then this is some kind of flip, so
we calculate the tile number, and move it into the z_HLs |
 |
We check our X and Y flip bits, and switch to the
pre-flipped tiles accordingly.
We then draw our tile onscreen in the same way as before. |
|
MaxTile Custom Draw Types
Bits 1,2,3... XYP=%001 defines a custom program
When a custom program is being used Bits 4,5 define the program
type (rather than part of the low tile number)
%00=Filled Tile
%01=Double height tile
%10=unused
%11=Transparent tile/empty tile
The remaining two bits 6,7 act as the High part of the tile number
%------NN nnnnnnnn
The first type is the Transparent
tile.
if the Tilenumber=255 then this tile is completely transparent,
and no data will be drawn
Ideally others should be partially transparent, but we can't do
that on the PC engine tilemap, so we just draw the tile as usual.
|
 |
A double height
or fill tile simply use the
alternate tile patterns.
FillTiles do not add the z_DE tile pattern base - as their
patterns are separate from the rest. |
|
MaxTile Definitions
Here' we're using Hardware sprites to move our characters around
the screen.
Note that PC-engine hardware sprites are 16x16 minimum, but we're
treating them as 8x8 to allow Maxtile to work the same as on other
systems.
This means 3/4 of the sprite pattern is empty - which isn't very
efficient! - as we use up all the hardware sprites, part of our
characters will disappear.
We transfer the data to the sprite pattern only as it appears
onscreen - more like a bitmap system than the previous tile
system.
Our code supports 4 or 16 color patterns. Although our sprites use
a different format, we use the same source pattern data and
convert it in realtime. |
|
Our tilemap is drawn using the previous code,
We need some new variables, a counter for the hardware sprite
number, and an Xshift and Yshift to move the sprite as we flip it
(to compensate for the fact we're only using the top left part of
the sprite)
|
|
Sprite Definitions
The basic sprite size is 16x16, though larger sprites can be created by
tilling them, for up to 32x64.... only neighboring sprites can be tilled.
Sprites are NOT in the same format as the tilemap, they are 16x16 with 4
bitplanes, but each plane is sent separately
For Example lets look at a sprite, where all pixels are color 0
or color 15!
First
16 writes
(Bitplane 1) |
1110000000000111
1000000100000001
1000000100000001
0000000100000000
0000000100000000
0000000100000000
0000000100000000
0000000111111100
0011111110000000
0000000010000000
0000000010000000
0000000010000000
0000000010000000
1000000010000001
1000000010000001
1110000000000111 |
Second
16 writes
(Bitplane 2) |
2220000000000222
2000000200000002
2000000200000002
0000000200000000
0000000200000000
0000000200000000
0000000200000000
0000000222222200
0022222220000000
0000000020000000
0000000020000000
0000000020000000
0000000020000000
2000000020000002
2000000020000002
2220000000000222 |
|
Third
16 writes
(Bitplane 3) |
3330000000000333
3000000300000003
3000000300000003
0000000300000000
0000000300000000
0000000300000000
0000000300000000
0000000333333300
0033333330000000
0000000030000000
0000000030000000
0000000030000000
0000000030000000
3000000030000003
3000000030000003
3330000000000333 |
Fourth
16 writes
(Bitplane 4) |
4440000000000444
4000000400000004
4000000400000004
0000000400000000
0000000400000000
0000000400000000
0000000400000000
0000000444444400
0044444440000000
0000000040000000
0000000040000000
0000000040000000
0000000040000000
4000000040000004
4000000040000004
4440000000000444 |
|
Sprites are stored in regular VRAM... the sprite definitions are stored in
special ram which we CANNOT ACCESS...however we can allocate a bank of 256
addresses (each containing one word) called SATB, and then get the
hardware to copy that ram to the special ram... it's suggested you use
$7F00 for that purpose.
To start the copy we just write the address to control reg $13
SATB - Sprite attribute
table buffer
The Sprite table allows for up to 64 sprites... each one has 4 words of
data - making 256 words in total... the format is as follows
| Address |
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Notes |
| 1 |
- |
- |
- |
- |
- |
- |
Y |
Y |
|
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y=Ypos (64 is
first visible line) |
| 2 |
- |
- |
- |
- |
- |
- |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
X |
X=Xpos (32 is
first visible line) |
| 3 |
- |
- |
- |
- |
- |
A |
A |
A |
|
A |
A |
A |
A |
A |
A |
A |
A |
A=Address (Top
10 bits $trueaddress>>5 ) |
| 4 |
YF |
- |
YS |
YS |
XF |
- |
- |
XS |
|
F |
- |
- |
- |
P |
P |
P |
P |
YF=Yflip XF=Xflip
YS=Ysize (16/32/64) XS=Xsize
(16/32)
F=Foreground
(infront of tilemap) P=Palette |
Tile Drawing!
The calling
routine that exexutes DrawTile will set X and Y to zero, so we
can rely on this being the case at the start of the routine...
however we need to ensure that X and Y still contain zero by the
end of our routine
|
 |
Here is our tile pattern data,
We'll convert it as the sprites appear onscreen to the correct
format. |
 |
When we want to draw a sprite object to the screen, we usually
need to calculate the VRAM destination.
In this case though, we need an XY co-ordinate, but we need to
shift them into H L to maintain compatibility
|
 |
The DrawTile Routine is called by the shared code, This shared
code will backup and restore the stack pointer and load the first
byte of the 16 bit tile number into A
The low bit is shifted out (the update bit)... we need to reset it
to 0 anyway!
The following zero page entries are loaded:
z_BCs = Tilemap
z_DEs = Tile Bitmap Pattern data
z_HL = VRAM Destination
|
 |
To optimize things, the tile drawing works as a 'binary tree',
deciding the kind of tile drawing routine to use
The Platform specific draw routine DrawTile starts by ckecking Bit
1 (now Bit 0)
This is the 'Program' flag - If this is 0, then this is the
simplest unflipped tile, otherwise we switch to the advanced
routine.
If we're drawing a basic tile, we shift a 0 back into A, and write
it back, that clears the Update flag, as we will draw the tile
now.
Before we start our draw, we clear XShift/YShift... because we're
only using the top left corner of our sprite, we must shift the
drawpos when Xflipping or Y-flipping
|
|
We're going to draw a basic unflipped
tile
We need to load the second byte of the tilenumber.
Maxtile was designed for 16 bytes per tile, so we shift to
multiply by 1 for our tile number.
We add our tile base in DEs, and write into the tilemap |
|
We check there's actually any hardware sprites left! (64 total)
if we've got some free, we use 'SpritePrep' to select the VRAM
destination.
'TransferSpriteTileBlockx2' will send 2 of the bitplanes - we run
this twice.
This is to ensure we can use the same pattern data as the Tiles,
which sent bitplanes in pairs.
If we're only using 4 colors we send empty data for the unused 2
bitplanes. |
 |
SpritePrep will set up sprite A (in the
accumulator)
First we set up the X/Y pos from z_hl
We set up the pattern Address (based on the sprite number)
Next we set up the attributes
Finally we select the pattern VRAM - as we'll send the pattern
data next. |
 |
TransferSpriteTileBlockx2 will
send the pattern data to VRAM
Due to the sprite layout, we need to send one byte from our
source, and one empty byte.
Then we send 16 empty bytes.
First we send the even bytes from our source, then we send the odd
ones - this is to 'convert' the source data for the sprite format. |
 |
If the Program bit was 1, then either we need to
flip, or run some 'custom code'
Next we check the XY bits, if both are 0 this is not a flip
(Transparent, Filled, Double etc)
If either bit (or both) is 1, then this is some kind of flip, so
we calculate the tile number, and move it into the z_HLs
We load the bitmap address and we use the same draw code as
before.
BUT we need to set the X/Y flip attribute in A before calling
DrawTileBasicH.
Our sprites are 16x16, but we're pretending they are 8x8, so we
need to load an X-Y position shift to keep the 8x8 block in the
correct position as we flip |
 |
MaxTile Custom Draw Types
Bits 1,2,3... XYP=%001 defines a custom program
When a custom program is being used Bits 4,5 define the program
type (rather than part of the low tile number)
%00=Filled Tile
%01=Double height tile
%10=unused
%11=Transparent tile/empty tile
The remaining two bits 6,7 act as the High part of the tile number
%------NN nnnnnnnn
The first type is the Transparent
tile.
if the Tilenumber=255 then this tile is completely transparent,
and no data will be drawn
In all other cases, since all our sprites are transparent, we just
use the normal draw routine.
|
 |
A Fill Tile sends the same 4 source bytes for all 8 lines of
each of the bitplanes
This is 16 color - even if the regular patterns are 4 color |
 |
A double height
tile uses half the pattern data
First we calculate the source tile, by multiplying the 'whole tile
number' by 32 (or 16 for 4 color tiles)
we then look at the least significant bit - this is the 'half tile
shift' and move down 4 lines (8 bytes = 4*2 bitplanes) if we need
the 'odd half' of the tile.
We then transfer the data in a similar way as before, but we send
each source line twice. |
|
Once we've drawn the hardware sprites to the screen we need to
remove the un-needed ones,
learUnusedHsprites will remove any unused sprites from the screen,
setting all their attributes to zero.
It also transfers the VRAM STAB sprite table to the actual sprite
hardware, showing the sprites in the process.
|
 |
MaxTile Definitions
On the SNES, we use the hardware tilemap to draw the Maxtile
Tilemap.
The SNES tilemap can perform X and Y flip for us.
The Double height tiles are 'preconverted' and stored in VRAM.
This version uses the tilemap for the 'simulated sprites' - there
is an alternative version which uses hardware sprites |
|
Maxtile Caches draws to the screen in 3 thirds to
reduce flicker.
We use some spare memory for the 3 draw caches (each 256 bytes)
The VRAM base of the Tilemap is 2 lines down from SnesScreenBuffer
(to center the virtual screen)
FourColorPatterns if defined will switch to 4 color mode.
Note we use $0001 as the first pattern number .... $0000 has a
special meaning to maxtile and will cause a malfunction if used as
the first pattern
|
|
Tile Definitions
Tile definitions use 4 bitplanes for
16 colors, and tile definitions are 8x8 - so 32 bytes ... Data is
transferred in Words, and rather strangely we send bitplane 1+2 of lines,
one at a time... then we do the same for bitplanes 3 and 4
|
Byte 1 |
Byte 2 |
| First
16
bytes |
00111100
01111111
01100011
01100011
01111111
01100011
01100011
00000000 |
00222200
02222222
02200022
02200022
02222222
02200022
02200022
00000000 |
| Second
16
bytes |
00333300
03333333
03300033
03300033
03333333
03300033
03300033
00000000 |
00444400
04444444
04400044
04400044
04444444
04400044
04400044
00000000 |
Tilemap Data
The Tilemap will typically start from address $0000, each entry contains two
bytes
| F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
|
|
| V |
H |
L |
P |
P |
P |
T |
T |
|
T |
T |
T |
T |
T |
T |
T |
T |
|
V=vflip
H=hflip L=layer (in
front of sprites) P=palette
T=tile number |
Sending tiles to VRAM
Here is the Tile pattern
data
We use 4 bytes per tile for the solid fill tiles - these are
always 16 color.
For the Tile patterns, we either use 2 bitplanes (4 color - 16
bytes per tile) or all 4 (16 color - 32 bytes per tile)
These are the unflipped tiles only.
The SNES can flip tiles for us, and we convert them and store
alternate patterns in VRAM for doubleheight |
|
We don't write directly into VRAM, we write to an
$800 byte buffer called 'SnesScreenBuffer'
This will be transferred to VRAM during the NMI via a DMA.
The DMA, once set up, automatically shifts all the data from
normal memory to VRAM - all in the time the vblank occurs |
 |
We send our data with DefineTiles, DefineDoubleTiles and
DefineFills
These transfer (and convert) the source pattern data into 16 color
patterns in VRAM
if using 4 color source patterns, IXH and IXL in the zeropage
define the two extra bitplanes not defined in the pattern data.
|
|
Our solid fills
are the simplest, we use the same 2 lines for each of our bytes
We select the VRAM destination with 'ST0 #0'... writing the
destination address to $102/3
we then select 'Data write' with 'ST0 #2' and sends our lines of
data to $102/3...
we send all 8 lines of bitplane 0/1... then all 8 bitplanes of 2/3
|
 |
DefineTiles
handles our regular tiles, we send all the bytes 'AS IS' to VRAM.
EXCEPT if we're using 4 colors!
In that case, after we've sent all 16 bytes of our tile, we need
to send 16 'dummy bytes' to fill bitplanes 2/3 from IXH,IXL |
 |
The double height tile code is a bit of a pain.
Each tile needs 8 bytes (4 lines) of bitplane 0/1 and 8 bytes (4
lines) of bitplane 1/2
We run 'DefineTiles4Lines' to send 4 lines twice.
we add 8 bytes, to move to the other bitplanes of the same half -
unless we're using 4 color patterns, in which case we send 'dummy
data' from IXH/IXL
After each send we add 16/32 to move to the next tile
|
 |
Tile Drawing!
The calling
routine that exexutes DrawTile will set X and Y to zero, so we
can rely on this being the case at the start of the routine...
however we need to ensure that X and Y still contain zero by the
end of our routine
|
 |
When we want to draw a sprite object to the screen, we need to calculate the VRAM destination.
MaxTile uses X,Y co-ordinates in 'Logical Units' (Pairs of pixels)
- this is passed in z_BC, and the Vram destination is returned in
z_HL
The tilemap is 32 tiles wide, and each tile is 2 bytes.
|
|
The DrawTile Routine is called by the shared code, This shared
code will backup and restore the stack pointer and load the first
byte of the 16 bit tile number into A
The low bit is shifted out (the update bit)... we need to reset it
to 0 anyway!
The following zero page entries are loaded:
z_BCs = Tilemap
z_DEs = Tile Bitmap Pattern data
z_HL = VRAM Destination
|
 |
To optimize things, the tile drawing works as a 'binary tree',
deciding the kind of tile drawing routine to use
The Platform specific draw routine DrawTile starts by ckecking Bit
1 (now Bit 0)
This is the 'Program' flag - If this is 0, then this is the
simplest unflipped tile, otherwise we switch to the advanced
routine.
If we're drawing a basic tile, we shift a 0 back into A, and write
it back, that clears the Update flag, as we will draw the tile
now.
|
|
We're going to draw a basic unflipped
tile
We need to load the second byte of the tilenumber.
Maxtile was designed for 16 bytes per tile, but we just want to
write the tile number, so we shift to the right, effectively
dividing by 16
We add our tile base in DEs, and write into the tilemap cache
(SnesScreenBuffer) at address z_hl |
|
If the Program bit was 1, then either we need to
flip, or run some 'custom code'
Next we check the XY bits, if both are 0 this is not a flip
(Transparent, Filled, Double etc)
If either bit (or both) is 1, then this is some kind of flip, so
we calculate the tile number, and move it into the z_HLs |
 |
We check our X and Y flip bits, and switch the
value we put in z_B accordingly - Bit 6 is Xflip, Bit 7 is Yflip
We then draw our tile onscreen in the same way as before. |
|
MaxTile Custom Draw Types
Bits 1,2,3... XYP=%001 defines a custom program
When a custom program is being used Bits 4,5 define the program
type (rather than part of the low tile number)
%00=Filled Tile
%01=Double height tile
%10=unused
%11=Transparent tile/empty tile
The remaining two bits 6,7 act as the High part of the tile number
%------NN nnnnnnnn
The first type is the Transparent
tile.
if the Tilenumber=255 then this tile is completely transparent,
and no data will be drawn
Ideally others should be partially transparent, but we can't do
that on the SNES tilemap, so we just draw the tile as usual.
|
 |
A double height
or fill tile simply use the
alternate tile patterns.
FillTiles do not add the z_DE tile pattern base - as their
patterns are separate from the rest. |
|
MaxTile Definitions
Here' we're using Hardware sprites to move our characters around
the screen.
We're using 8x8 hardware sprites, using the same pattern data as
the background tiles.
Our code supports 4 or 16 color patterns. Although our sprites use
a different format, we use the same source pattern data and
convert it in realtime. |
|
We define various settings for our sprite drawing,
most are the same as the prvious tilemap example
We define some new ram for the sprite data buffer SnesSpriteBuffer
- it's transferred to VRAM during NMI via a DMA
|
 |
| Our pattern data is the same as before, but the sprites use
patterns from address $4000+ , so we copy the patterns to this
address for the sprites. |
 |
| We need to transfer the sprite cache into VRAM during the NMI
interrupt |
 |
We need to enable the 'sprite layer', and also turn on the
interrupts
|
 |
Sprite Definitions - Overview
Sprites use as special bank of 512 bytes of 'OAM' memory for their
definitions... they also use standard VRAM for the pattern data.
In theory the Pattern data can be relocated... but in practice it's best to
just assume it's at $4000 (address in 16 bit words)
Sprites can be various sizes - a 'default size' is set for all sprites...
and certain selected sprites can be double size...
this is, however a bit tricky... lets say you have the default size as
8x8... and one double size 16,16 sprite
If we point this sprite 'double size' 16x16 sprite to pattern 'Tile
0', the 4 8x8 chunks will be made up of tile numbers:
Lets look at this example of a 16x16
sprite in AkuSprite Editor... Akusprite editor is designed for 8x8
sprites, but we can export a 16x16 one in the following way
If we want to export this quickly, so we can use it as a single
doublesize sprite, one option is to tick the 'FixedSize' tickbox,
and set the size to 128,16
This will export the sprite correctly - of course there will be a
lot of unused space in the exported file... so we would want to
combine all our 16x16 together into a single image
Generally it would be easier to build up a 16x16 sprite from 4 8x8
sprites
|
 |
Sprite Definitions - Ports Used
| Address |
Name |
Purpose |
Bits |
Details |
| $2101
|
OBSEL |
OAM
size
(Sprite) |
SSSNNBBB |
S=size
N=Bame addr B=Base addr |
| $2102
|
OAMADDL/L |
OAM
address
|
LLLLLLLL |
a=oam
address
L |
| $2103
|
OAMADDL/H |
OAM
address
|
R000000H |
R=
priority Rotation / H=oam address MSB |
| $2104
|
OAMDATA |
OAM
data
|
???????? |
|
| $212C
|
TM |
Main
screen
designation |
---S4321 |
S=sprites
4-1=enable
Bgx |
| $2138
|
OAMDATAREAD |
Read
data
from OAM |
|
|
Sprite Definitions - OAM Data
The SNES has 128 hardware sprites.
Selecting a HL address is done by setting registers $2102 (L) and
$2103 (H)
Each address below $0100 holds Two Bytes (The first table)...each address
$0100 or above holds just one!... All data is written via the $2104
Note, Sprites use Palettes from 128... so the color palette used is the
value in CCC +128
Sprite data should only be written to Vram during Vsync.
| Address |
Byte
1 |
Byte
2 |
Meaning |
SprNum |
| $0000 |
XXXXXXXX |
YYYYYYYY |
X=Xpos (bits
0-7) Y=Ypos |
0 |
| $0001 |
YXPPCCCT |
TTTTTTTT |
Y=yflip
X=xflip P=priority compared to BG (C=palette +128) |
0 |
| $0002 |
XXXXXXXX |
YYYYYYYY |
X=Xpos (bits
0-7) Y=Ypos |
1 |
| $0003 |
YXPPCCCT |
TTTTTTTT |
Y=yflip
X=xflip P=priority compared to BG (C=palette +128) |
1 |
| � |
� |
� |
� |
� |
| � |
� |
� |
� |
� |
| $00FE |
XXXXXXXX |
YYYYYYYY |
X=Xpos (bits
0-7) Y=Ypos |
127 |
| $00FF |
YXPPCCCT |
TTTTTTTT |
Y=yflip
X=xflip P=priority compared to BG (C=palette +128) T= Tile Pattern
number
|
127 |
| $0100 |
SXSXSXSX |
(no 2nd byte) |
S=doubleSize
sprite X=Xpos (bit 8) |
0-3 |
| $0101 |
SXSXSXSX |
(no 2nd byte) |
S=doubleSize
sprite X=Xpos (bit 8) |
4-6 |
| � |
� |
|
� |
� |
| $011F |
SXSXSXSX |
(no 2nd byte) |
S=doubleSize
sprite X=Xpos (bit 8) |
124-127 |
Tile Drawing!
The calling
routine that exexutes DrawTile will set X and Y to zero, so we
can rely on this being the case at the start of the routine...
however we need to ensure that X and Y still contain zero by the
end of our routine
|
 |
When we want to draw a sprite object to the screen, we usually
need to calculate the VRAM destination.
In this case though, we need an XY co-ordinate, but we need to
shift them into H L to maintain compatibility
|
 |
The DrawTile Routine is called by the shared code, This shared
code will backup and restore the stack pointer and load the first
byte of the 16 bit tile number into A
The low bit is shifted out (the update bit)... we need to reset it
to 0 anyway!
The following zero page entries are loaded:
z_BCs = Tilemap
z_DEs = Tile Bitmap Pattern data
z_HL = VRAM Destination
|
 |
To optimize things, the tile drawing works as a 'binary tree',
deciding the kind of tile drawing routine to use
The Platform specific draw routine DrawTile starts by ckecking Bit
1 (now Bit 0)
This is the 'Program' flag - If this is 0, then this is the
simplest unflipped tile, otherwise we switch to the advanced
routine.
If we're drawing a basic tile, we shift a 0 back into A, and write
it back, that clears the Update flag, as we will draw the tile
now.
Before we start our draw, we clear XShift/YShift... because we're
only using the top left corner of our sprite, we must shift the
drawpos when Xflipping or Y-flipping
|
|
We're going to draw a basic unflipped
tile
We need to load the second byte of the tilenumber.
Maxtile was designed for 16 bytes per tile, so we shift to
multiply by 1 for our tile number.
We add our tile base in DEs, and write into the tilemap
we use 'SetHardwareSprite' to set the sprite attributes |
|
Set hardware sprite moves all the parameters of the
sprite into the sprite VRAM cache.
The only tricky thing is the 2nd part, where 2 bits (X pos bit 9 +
Sprite Doublesize bit) need to be shifted to the correct position
This is because the byte is shared between 4 different hardware
sprites |
 |
If the Program bit was 1, then either we need to
flip, or run some 'custom code'
Next we check the XY bits, if both are 0 this is not a flip
(Transparent, Filled, Double etc)
If either bit (or both) is 1, then this is some kind of flip, so
we calculate the tile number, and move it into the z_HLs
We load the bitmap address and we use the same draw code as
before.
BUT we need to set the X/Y flip attribute in A before calling
DrawTileBasicH.
Our sprites are 16x16, but we're pretending they are 8x8, so we
need to load an X-Y position shift to keep the 8x8 block in the
correct position as we flip |
 |
MaxTile Custom Draw Types
Bits 1,2,3... XYP=%001 defines a custom program
When a custom program is being used Bits 4,5 define the program
type (rather than part of the low tile number)
%00=Filled Tile
%01=Double height tile
%10=unused
%11=Transparent tile/empty tile
The remaining two bits 6,7 act as the High part of the tile number
%------NN nnnnnnnn
The first type is the Transparent
tile.
if the Tilenumber=255 then this tile is completely transparent,
and no data will be drawn
In all other cases, since all our sprites are transparent, we just
use the normal draw routine.
|
 |
A Fill Tile sends
the same 4 source bytes for all 8 lines of each of the bitplanes
This is 16 color - even if the regular patterns are 4 color |
|
A double height
tile uses half the pattern data
We use the alternate stretched tiles. |
|
Once we've drawn the hardware sprites to the screen we need to
remove the un-needed ones,
ClearUnusedHsprites will remove
any unused sprites from the screen, setting all their attributes
to zero.
|
 |
Mouse hardware ports
The X-Y movement is passed by the paddle ports, however these are a
'relative location' and will repeat as we keep moving the mouse, so must
be compared to the previous value to produce a relative offset.
The left and right mouse buttons use Fire and Up of the Joystick.
| Address |
Description
|
Normal
Bits
|
Mouse
Bits
|
Meaning |
| $D419 |
Paddle
X value (Mouse X move) |
xxxxxxxx |
xPPPPPPn |
x=dont
care (ignored) / P=Mouse Pos MOD 64 / n=noise bit |
| $D41A |
Paddle
Y value (Mouse Y move) |
yyyyyyyy |
xPPPPPPn |
x=dont
care (ignored) / P=Mouse Pos MOD 64 / n=noise bit |
| $DC01 |
CIA1:
Port B, keyboard matrix rows and joystick #1 |
---FRLDU |
---L---R |
Left /
Right Mousebuttons |
The mouse handler code (interrupt handler)
Here is our mouse example.
The top line shows the raw values from the ports.
The second line is the X-Y position of the sprite.
Our mouse controls the sprite, and the left and right mouse buttons
change its color |
 |
We need some zero page bytes to store the last settings of the
paddle values, so we can work out the difference.
We also define some pointers to the sprite's XY position to make
moving it more easy/clear in our code |
 |
Our interrupt handler uses the routine movchk to
calculate the relative shift of the X and Y pot
We load the current value in A , and the old value in Y and run the
routine.
It returns the old value in A, and a 16 bit signed shift in XA - we
only need an 8 bit shift for the Y axis, but we use all 16 bits for
the X pos
We set the position of the first Hardware sprite...
The Xpos is set by bits 0-7 of $D000, and the most significant bit
is bit 0 of $D010
The Ypos is set by bits 0-8 of $D001 |
 |
MovChk subtracts the
OldValue from the NewValue to create a signed offset for the axis.
The Pots give a value from $40-$C0, and the least significant bit is
'unreliable'
In our code, only the middle 6 bits are used... these are converted
to a signed 16 bit value in XA (X is the High byte, A is the Low
byte)
|
 |
Preparing our example
We need to load the address of our interrupt handler into address
$314-$315
This will be executed by the C64 ROM,
We also need to turn on interrupts, and clear the interrupt flag. |
 |
We're using one of the hardware sprites to show our
cursor.
Here we initialize the sprites parameters, showing it to the screen.
We load a bitmap 'Cursor' into ram at address $2000 which we've set
up to be the address containing Sprite 0's image
|
|
The main loop loads the raw values from the hardware ports and
shows them to the screen.
It also loads in the sprite position and shows that to the screen.
The sprite position is set by the interrupt handler, but here we
read the 'joystick port' and check the mouse buttons.
We tweak the color accordingly (in X) - and update the sprite color.
|
|
Overview
Maxtile can only
draw whole tiles, this means on systems with allowing partial
movements the edges of the screen will show parts of unchanged
tiles.
On systems allowing Half tile movement the area which is left
'dirty' will be 4 pixels
On systems allowing Quarter tile movement the area which is left
'dirty' will be 6 pixels
As it's calculations are done in pairs of Pixels, Maxtile cannot do
more than Quarter tile movement |
 |
The simplest solution to this problem is just to cover up the
mess!
We'll create a 4-6 pixel border around the screen, and cover up the
partial tile draws.
We will design our code with the system in mind, and fill the areas
required as fast as possible!
|
|
BBC Version
First we're going to fill the top and bottom of the screen
'ClearBorderTopBottom' will fill 4 lines - we use it for the top and
the bottom of the screen.
we also run 'ClearBorder4', this clears a 4x4 block at the left of
the screen - which will be missed by our main routine which does the
vertical strips.
|
 |
Because of the way the BBC screen works, where 8
consecutive bytes in memory go down the screen with the 9th
'jumping' back up to the top,
To fill our 4 pixel tall strip, there are 4 'filled' bytes followed
by 4 skipped bytes
With these skipped lines, The top 4 lines of the screen use 512
bytes total |
|
We're now going to clear the sides.
Because of the screen layout, and To save time, we can do the Left
and right in one go.
We first do a 4x8 strip on the right of the screen, and then INC Y,
and do a 4x8 strip on the left.
We repeat on the height of the screen.
BUT this leaves a 4x4 block at the start on the left... which is why
we filled it before
It also leaves a 4x8 block on the right at the end... we'll do that
next.
|
 |
'ClearBorder4' fills a 4x4 block, we use it at the
start and end to fill the 'missed bit'
|
 |
What Is NativeSprite?
Nativesprite allows us to use platform specific sprite
capabilities, to form 'objects' (grids of sprites which can be
used in the same way on all systems.
This allows us to write a multiplatform program, but gain the
benefits of the hardware capabilities.
Nativesprite splits the job of drawing sprites into three parts. |
 |
Part 1 is
multiplatform, it is a list of NativeSprite objects with X,Y
co-ordinates (in logical units - Pairs of Pixels)
Our game can change the co-ordinates and sprite object pointers to
change in-game graphics in a multiplatform way.
|
|
Part 2 is platform
specific, and defines the sprite pattern grid,
This defines how the object is made up of hardware sprite
patterns.
It also offers platform specific functions, like sprite scaling or
palettes where available.
On systems where no Hardware sprites are available, XOR software
sprites are used (made up of 8x8 tiles)... XOR is used because it
means we don't need to worry about redrawing the background.
Part 3 is also platform specific.
This is the bitmap pattern data used to draw the sprite, in the
format of the hardware sprites or screen memory.
|
|
PC
Engine Sprite Definitions
The PC Engine has 64 hardware sprites. The basic sprite size is 16x16,
though larger sprites can be created by tilling them, for up to
32x64.... only neighboring sprites can be tilled. Sprites use the second
set of palettes, from 256-511
Sprites are NOT in the same format as the tilemap, they are 16x16 with 4
bitplanes, but each plane is sent separately
For Example lets look at a sprite,
where all pixels are color 0 or color 15!
First
16 writes
(Bitplane 1) |
1110000000000111
1000000100000001
1000000100000001
0000000100000000
0000000100000000
0000000100000000
0000000100000000
0000000111111100
0011111110000000
0000000010000000
0000000010000000
0000000010000000
0000000010000000
1000000010000001
1000000010000001
1110000000000111 |
Second
16 writes
(Bitplane 2) |
2220000000000222
2000000200000002
2000000200000002
0000000200000000
0000000200000000
0000000200000000
0000000200000000
0000000222222200
0022222220000000
0000000020000000
0000000020000000
0000000020000000
0000000020000000
2000000020000002
2000000020000002
2220000000000222 |
|
Third
16 writes
(Bitplane 3) |
3330000000000333
3000000300000003
3000000300000003
0000000300000000
0000000300000000
0000000300000000
0000000300000000
0000000333333300
0033333330000000
0000000030000000
0000000030000000
0000000030000000
0000000030000000
3000000030000003
3000000030000003
3330000000000333 |
Fourth
16 writes
(Bitplane 4) |
4440000000000444
4000000400000004
4000000400000004
0000000400000000
0000000400000000
0000000400000000
0000000400000000
0000000444444400
0044444440000000
0000000040000000
0000000040000000
0000000040000000
0000000040000000
4000000040000004
4000000040000004
4440000000000444 |
|
Sprites are stored in regular VRAM... the sprite definitions are stored
in special ram which we CANNOT ACCESS...however we can allocate a bank
of 256 addresses (each containing one word) called SATB, and then get
the hardware to copy that ram to the special ram... it's suggested you
use $7F00 for that purpose.
To start the copy we just write the address to control reg $13
SATB - Sprite
attribute table buffer
The Sprite table allows for up to 64 sprites... each one has 4 words of
data - making 256 words in total... the format is as follows
| Address |
F |
E |
D |
C |
B |
A |
9 |
8 |
|
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Notes |
| 1 |
- |
- |
- |
- |
- |
- |
Y |
Y |
|
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y |
Y=Ypos (64 is
first visible line) |
| 2 |
- |
- |
- |
- |
- |
- |
X |
X |
|
X |
X |
X |
X |
X |
X |
X |
X |
X=Xpos (32 is
first visible line) |
| 3 |
- |
- |
- |
- |
- |
A |
A |
A |
|
A |
A |
A |
A |
A |
A |
A |
A |
A=Address (Top
10 bits $trueaddress>>5 ) |
| 4 |
YF |
- |
YS |
YS |
XF |
- |
- |
XS |
|
F |
- |
- |
- |
P |
P |
P |
P |
YF=Yflip XF=Xflip
YS=Ysize (16/32/64) XS=Xsize
(16/32)
F=Foreground
(infront of tilemap) P=Palette
(256+) |
NativeSprite
NativeSpr_Init
will prepare the system for drawing hardware sprites.
On the PC engine we first need to turn on hardware sprites with
bit 6 of Video Register 5.
We now need to transfer the sprite patterns (16x16 pixels ...
128 bytes each) to VRAM. $2000 is a good address to transfer
sprites into VRAM.
Sprites do not use the same palettes as the background tiles -
they use the 'second set' of palettes 256-511,
We select palette address $0100 to begin configuring the sprite
palettes. |
 |
NativeSpr_DrawArray
draws the array of sprite objects defined in Part1
The Co-ordinates of the sprite (in pairs of pixels) and the
pointer to the sprite object (part 2)
We use NativeSpr_DrawExtra to draw the sprite to the screen. |
|
Here is the correct Object definition
for the PC Engine
The Width and Height in patterns is defined in the first two
bytes.
The next word is the 'Attributes' which define the hardware
sprite size and palette. This is shared for the entire sprite
object (PC Engine sprite word 4)
Each tile has a pattern Word, however it must be the correct
format for the sprite parameters, which is the source address
bitshifted right 5 bits (For Example $2000>>5) |
 |
| NativeSpr_DrawOne will load
in the parameters, The XY position for the draw, and the pointer
to the sprite object definition |
 |
NativeSpr_DrawExtra
will draw one more sprite using the next free Hardware Sprite
Number
NativeSpr_Draw will draw
object HL at position B,C using Hardware sprite number A
onwards.
On the PC Engine the attribute defines the sprite patterns as
16x16, 32x32 or 64x64.
NativeSprite uses a shared attribute to define this size.
The 'Virtual screen' uses Logical Units (Pairs of Pixels). The
logical screen is 128x96 and starts from co-ordinate 64,80.
Offscreen sprites are not drawn and do not use a hardware sprite
number
The Sprite attribute buffer is in VRAM at address $7F00 - there
are 4 bytes per sprite.
We convert and transfer the Ypos and Xpos, we then transfer the
sprite pattern address (shifted >>5)
We then transfer the shared attribute.
This buffer at $7F00 does not update the sprites, we need to
initiate a transfer with Video Register $13 (VRAM-SATB
Block Transfer Source) |

|
Hardware sprites stay on screen until they are
removed. If we reduced the size or number the objects drawn to
the screen, we will need to remove any hardware sprites that are
no longer needed.
NativeSpr_ClearUnused will hide
the sprites which are no longer needed, and is automatically
executed after the NativeSprite array is drawn.
NativeSpr_HideAll will remove
all the hardware sprites from the screen, and should be used for
things like title screens. It should also be used before
redrawing the background with XOR sprites.
NativeSpr_Hide will remove a
single hardware sprite by number.
On the PC Engine we zero all the attributes, moving the sprite
offscreen.
|

|
What Is NativeSprite?
Nativesprite allows us to use platform specific sprite
capabilities, to form 'objects' (grids of sprites which can be
used in the same way on all systems.
This allows us to write a multiplatform program, but gain the
benefits of the hardware capabilities.
Nativesprite splits the job of drawing sprites into three
parts. |
 |
Part 1 is
multiplatform, it is a list of NativeSprite objects with X,Y
co-ordinates (in logical units - Pairs of Pixels)
Our game can change the co-ordinates and sprite object
pointers to change in-game graphics in a multiplatform way.
|
|
Part 2 is
platform specific, and defines the sprite pattern grid,
This defines how the object is made up of hardware sprite
patterns.
It also offers platform specific functions, like sprite
scaling or palettes where available.
On the C64, we define a shared 'Attribute byte' which allows
selection of Double Height, or DoubleWidth sprite patterns for
this object
We then define a Pattern byte, and ColorByte for each pattern.
in this example the sprite patterns are $40, due to the fact
the sprite patterns are stored at address $5000
Part 3 is also platform
specific.
This is the bitmap pattern data used to draw the sprite, in
the format of the hardware sprites or screen memory.
|
|
C64 Sprites
The Sprite pointers for the bitmap data are a single byte...
multiplying the sprite pointer by 64 will give the address of the
sprite *within the 16k bank of Vram* (so must be in the range
$0000-$3FFF)...
$1000-$2000 and $9000-$A000 are seen by the VIC as character ROM,
so sprites cannot be in this area!
We can move our screen base to something more convenient... so for
example with a screen base of $4000 (Screen ram at $6000)- our
sprites can be at $5000
$D018
Memory setup register.
|
$DD00
Vic 2 bank
(Bottom 2 bits)
|
Screen Base
|
Sprite Patterns
|
Sprite Pointers
|
%00011000
(Screen base $2000 / Colors at $400) |
%------10
(Bank $4000-7FFF) |
$6000
($4000+$2000) |
$5000 (one option)
$4000+? |
$47F8
($4000+$7F8) |
Sprites are 21 vertical lines and 63 bytes each. With one unused
byte this makes a 64 byte pattern size.
In 1bpp (2 color) mode this makes sprites 24x21.
In 2bpp (4 color) mode they are 12x21.
In both modes, Color 0 is Transparent
In 2bpp mode color 1,2 are read from $D025/6 and color 3 is the
sprite color.
The Top Left first visible co-ordinate is (24,50)
| Address |
Purpose |
Bits |
Meaning |
ScreenBase+
$07F8-$07FF |
Sprite
pointers
(default - will change if screen moved) |
SSSSSSSS |
s*64=memory
address |
| $D000 |
Sprite
#0 X-coordinate |
XXXXXXXX |
(only
bits #0-#7). |
| $D001 |
Sprite
#0 Y-coordinate |
YYYYYYYY |
|
| $D002 |
Sprite
#1 X-coordinate |
XXXXXXXX |
(only
bits #0-#7). |
| $D003 |
Sprite
#1 Y-coordinate |
YYYYYYYY |
|
| ... |
... |
... |
... |
| $D00E |
Sprite
#7 X-coordinate |
XXXXXXXX |
(only
bits #0-#7). |
| $D00F |
Sprite
#7 Y-coordinate |
YYYYYYYY |
|
| $D010 |
Sprite
#0-#7 X-coordinates |
76543210 |
(bit #8) |
| $D015 |
Sprite
enable register |
76543210 |
1=on
|
| $D017 |
Sprite
double height register |
76543210 |
|
| $D01B |
Sprite
priority register |
76543210 |
|
| $D01C |
Sprite
multicolor mode register |
76543210 |
0=2
color 1=4color
|
| $D01D |
Sprite
double width register |
76543210 |
|
| $D01E |
Sprite-sprite
collision register |
76543210 |
|
| $D01F |
Sprite-background
collision
reg |
76543210 |
|
| $D025 |
Sprite
extra color #1 |
----CCCC |
|
| $D026 |
Sprite
extra color #2 |
----CCCC |
|
| $D027 |
Sprite
#0 color |
----CCCC |
|
| $D028 |
Sprite
#1 color |
----CCCC |
|
...
|
... |
... |
|
| $D02E |
Sprite
#7 color |
----CCCC |
|
NativeSprite
NativeSpr_Init
will prepare the system for drawing hardware sprites.
On the C64, we need to load the sprite patterns to the
correct memory range, With the screen memory layout used by
this example, we load to address $5000
If we use the Multicolor '4 color sprites' two shared colors
are defined, which are used by all sprites. These are
defined by $D025 and $D026. |
 |
NativeSpr_DrawArray
draws the array of sprite objects defined in Part1
The Co-ordinates of the sprite (in pairs of pixels) and the
pointer to the sprite object (part 2)
We use NativeSpr_DrawExtra to draw the sprite to the screen. |
|
Here is the correct Object
definition for the C64
The Width and Height in patterns is defined in the first two
bytes.
On the C64, we define a shared 'Attribute byte' which allows
selection of Double Height, or DoubleWidth sprite patterns
for this object
We then define a Pattern byte, and ColorByte for each
pattern. in this example the sprite patterns are $40, due to
the fact the sprite patterns are stored at address $5000
|
 |
| NativeSpr_DrawOne will
load in the parameters, The XY position for the draw, and
the pointer to the sprite object definition |
 |
NativeSpr_DrawExtra
will draw one more sprite using the next free Hardware
Sprite Number
NativeSpr_Draw will draw
object HL at position B,C using Hardware sprite number A
onwards.
First we take the Ypos and Xpos and convert these to sprite
screen coordinates.
Next we load the Object Width and Height, and shared
Attributes.
The Attributes are used to calculate the 'size' of the
patterns in pixels, which are loaded in from NativeSpr_Sizes |
|
We transfer all the atributes for each sprite pattern in
the object to a different hardware sprite.
|
 |
| C64SpriteConvertToMask is used to convert
the '1 bit per hardware sprite' attributes |
 |
Hardware sprites stay on screen until they
are removed. If we reduced the size or number the objects
drawn to the screen, we will need to remove any hardware
sprites that are no longer needed.
NativeSpr_ClearUnused will
hide the sprites which are no longer needed, and is
automatically executed after the NativeSprite array is
drawn.
NativeSpr_HideAll will
remove all the hardware sprites from the screen, and should
be used for things like title screens. It should also be
used before redrawing the background with XOR sprites.
NativeSpr_Hide will remove
a single hardware sprite by number.
On the C64, we set the X,Y pos both to zero
|
 |
|
| |
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!
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!
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!
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!
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!
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!
|