Suck Hunt is a little demo game which uses the 'MinTile'
software tilemap (or hardware on SMS) for graphics, ChibiTracks
for music and presents a little fighting game with an AI CPU
player.
It runs on CPC / MSX2 / Spectrum NEXT and SAM coupe
Lesson
CF1 - Supertiles
The game area of ChibiFighter uses a 32x20 tilemap.
Each game level has a different tilemap scene. To save memory
these are compressed using 'Supertiles' - Blocks of 4x4 tiles.
See
ChibiFighter
folder
Patterns and Supertiles!
Here is our game screen. The background is 32x20 tiles.
We'll split up the background map into 4x4 tile supertiles. The
supertilemap will therefore have 8x5 entries (40 supertiles)
The actual map will use one nibble per supertile definition -
meaning 16 supertiles max.
This means each 32x20 level background uses a supertile map of
just 20 bytes
Here are the tile patterns we've defined
We combine these into supertile definitions.
The Supertile definitions are also compressed - each 16 tile
supertile is stored in 8 bytes. this is achieved by storing each
tile as 2 bit definition, with a 4 bit tile lookup.... This means
we can only use 4 unique tiles in each supertile.
Here is the supertile map that defines the green play area.
The first byte is the height of the supertile map (it must be the
full width of the screen - 8 supertiles)
The next two bytes are a pointer to the supertile definitions.
The remaining bytes are the supertile numbers, one nibble per
supertile
The actual supertile definitions each use 8 bytes.
The first 4 bytes are the Index/LookUp Table of the 4 tile pattern
numbers that are used in this supertile.
The next 4 bytes are the 16 tiles that make up this 4x4
supertile... Each byte is one line of the supertile, pairs of bits
define which Index entry in the LUT define the pattern number for
that tile.
LoadSuperTileMapNibble will
'decompress' the supertilemap into the tilemap.
IXL is the First tile pattern num
(pattern offset) - this is used to skip the font for the
background graphics HL is the Source Supertilemap
HL' is the Destination Tilemap in
RAM - this is used to actually draw the screen.
With MinTile the RAM tilemap is 36 tiles wide... this is to allow
a 4 tile 'scrollable' area
We load in each byte, and split it's two nibbles, processing them
with CopyOneSupertile4ByteLut
A is the Supertile Definition Num
IY is the address of the Source Supertile definitions
HL' is the Destination Tilemap RAM
IXL is the offset tile num (in pattern data)
Each supertile definition is 8 bytes, so we first multiply A by 8,
and add it to the source definitions in IY - storing the result in
DE
The first 4 bytes are the Index/LUT of tile numbers
The second 4 bytes are the 2 bit lookup entries - each byte is one
line of the 4x4 supertile.
We next shift into the supertile numbers, by setting bit 2 of DE
We load a byte into C - each tile is defined by 2 bits so this is
a whole line.
we read 2 bits at a time, and read the actual tilenumber from the
index, and put this in VRAM in HL
after each line of the supertile, we move down in the tilemap vram
by adding 32 (32+4 = 36... the width of the tilemap in VRAM)
Here we use the supertile code to draw the player life bars at
the top of the screen
Lesson
CF2 - Titlescreen and GameStart
Lets take a look at the 'compressed' titlescreen code, and the
game init routine
See
ChibiFighter
folder
Titlescreen
Lets draw our title screen!
At the start of the game routine, we INIT the AI vars (in ram), we
do this by copying from the template (Maybe ROM)
We set the 'songtimer' - this is for the CPC interrupt handler, it's
not actually needed on other systems.
We start the first song for the title screen.
We need to draw the 'ChibiFighter' logo onto the tilemap
(TileCache)
The tilemap for the logo is up to 32x13... but we've used some crude
compression.
Many of the tiles are 0 (black) - so lets 'compress them'
The tilemap only uses <128 tiles, so the top bit of the
tilenumber is free.
If a line has a large string of zero tiles we'll store it a a 1 byte
'skip X tiles' where the top bit is 1... %1nnnnnnn where n is the
tiles to skip.
if the line has ended we will end with a %11111111 (255) byte and
skip to the next line.
This is very simple 'compression' but greatly reduces the tilemap
size.
Here is the tilemap data
After the graphic we show the 'Hyper futile' text, and wait for
fire.
New Game
'NewBattle' is executed at the start of each new opponent battle.
The first game we reset the OpponentNum to 0
We reset the sprites from the template.
The ResetRound function clears all the level and player vars back
to their defaults.
The next sequence is rather long and annoying!
Really it just loads up the sprite data for the player and cpu
characters, positions their sprites and shows the VS message.
GetPlayerSettings loads in the defaults for a
character into sprite IX
GetSubSettings loads in the defaults for the 'sub
sprite' (Fireball atack)
AnimateSprite updates the sprite animation, and
sets the size and pattern settings from the default sprite number
RepaintScreen draws the background tilemap (and
text)
DrawPlayers draws the sprite characters
On The SMS it also loads in the 128 tiles for the player / Character
into pattern data (overwriting the title screen patterns)
The Getplayer settings functions load in the various parameters of
the player / enemy
The enemy sprite also defines the background and level music.
Here are the contents of part of the playerdefs table.
New Round
Battle commences until someone wins two rounds.
This means there can be up to 3 rounds (if there are no draws).
Before each round we show a "Round 1" "Fight" type message.
First we need to initialize the background and reposition the player
sprite.
Then we show the text.
We pause before text messages.
After this message, we're ready to start gameplay!
Lesson
CF3 - MainLoop and Round End
Lets take a look at the main game loop, and the code that handles
the end of a round
See
ChibiFighter
folder
Main Loop
The first task is to update
the 'ticks' - this is used to control timed animation events.
Next we read in keypresses from the joystick.
We want to update our two players. We use two routines.
UpdateFighter handles the input. AnimateSprite
updates the graphics bases on scripted sequences - we'll
see details of these later.
We use the same routines for the human and CPU, but the ReadAI
routine is used to control the CPU - it returns a virtual
'joystick keypress' by the computer, so we can easily switch to
Human VS Human, or even CPU vs CPU!
Next we check if the round time limit has been reached.
If it hasn't we update the time.
The counter shows 60 ticks, but there is a second hidden timer
RoundTime2, which counts the time until the point to reduce the
visible ticks.
Next we repaint the tilemap with RepaintScreen,
and draw the sprites with DrawPlayers
Next we handle the collision detection.
As well as the main sprites, we have 2 sub sprites for the fireball
attack.
We check if there has been a collision between a player's
punches/kicks and fireballs.
Collision detection
First we check the opponents life.
If the opponents life has reached zero, the player has won.
Next we check the life of the object - a fireball which is
offscreen will have a life of zero.
We also check if the attack is <0 a negative value is a block...
an attack of zero is no attack (just standing around!)
Next we check the distance of the attacker from the opponent - if
the attacker is too far away, the attack missed.
We also check if the opponent is blocking (attack <0 - bit 7
nonzero) - if so the attack missed.
In these cases the attack missed, so we clear the attack to 0
The attack has HIT!... so we give the attacker a point. and redraw
the scores.
We move the amount of 'attack' of the attacker to the 'hurt' of
the opponent - this will drain the injured parties life over the
next few ticks.
Round End
If the time runs out we work out which player has the most life
left and run PlayerWins with IX pointing to the winner.
If both players have the same lif, it's a draw!
The winner has been decided - we add 1 to their wins, and show
their name to the screen.
We now check how many wins each player has
If the CPU has 2 wins the game is over.
If the HUMAN has 2 wins, it's time for the next challenger.
Otherwise it's just another round.
If the human has lost we show a simple gameover screen.
Lesson
CF4 - Drawing routines
While the graphics are handled by 'Mintile' (My tilemap code), we
have to do some setup to get it working with our code.
See
ChibiFighter
folder
DrawPlayers
Mintile uses the same code for the tilemap as the sprites.
We need to use selfmodifying code to change the way the tilemap
works to switch it to sprite mode!
On some systems we are running from ROM, so instead of
selfmodifying, we use 'TileClear'
The ChibiFighter Graphics are 4 color bitmaps, even on 16 color
systems, we use a 'Tint', effectively selecting one of 4x four color
palettes
We run the DrawSprite routines to show the CPU and Human players
Next we draw the fireballs to the screen, but only if the objects
are 'alive'
Font code
Character drawing also goes through 'MinTile'
The 'Mintile routine uses a 32x24 tilecache - which keeps track of
updates which are needed to the the onscreen tilemap.
A 'Zero' in the cache means 'skip the tile'... any other value is a
tile change.
Once the tile is written to the screen, it's reset to zero in the
cache.
Screen Drawing
Repaint screen is a general update of the screen.
This sets up the MinTile code to draw the tilemap (zeroing the cache
after drawing)
the 'CLS' routine will transfer the cache to the screen.
Blackout screen wipes the screen to the empty tile (1)
This clears both the cache, and the background tilemap, it's used
for Gameover and the title screen.
DrawScreen is a general refresh of the background objects.
First we redraw the Life bar area at the top of the screen.
Then we transfer the entire background tilemap into the cache - this
forces MinTile to do a complete redraw.
We've only drawn the empty bar so far, we need to draw the filled
life bars and player names.
We do this for the human and CPU player
We draw the other text objects, Time and players score.
The Draw time prints 1 BCD byte to the screen (0-60)
PrintPlayerName will show the character name, and also shows the
'wins'
The round ends with 2 wins, so we only show 0 or 1 icon.
The life bars move towards the center of the screen
They are made up of 3 parts.
A filled part, based on the top 4 bits of the life
A half filled part, based on bit 3 of the life, which may or may not
be drawn.
A blank part, which is the remainder of the bar.
Lesson
CF5 - Scripted Animators!
The ChibiFighter Animation engine handles sequences of moves and
actions by objects in the game.
It's a reworked and optimized version of the one from ChibiAkumas
Ep2... Lets check it out!
See
ChibiFighter
folder
The Animators list
Our animator routine supports up to 15 animation types 1-15.
Each has a pointer to the script in a lookup table.
Animator 0 is 'no animator' - a static sprite!
The first byte of an animator is the 'speed' of the animation
(0=fastest 1=every other frame 3=every 1/4th frame etc)
There can be up to 16 lines in the script
each line takes 4 bytes. The first byte is the animation command,
the 3 other bytes are parameters relating to that command
We define symbols for the possible animation commands,
There is a lookup table for the code which performs the animation
action.
Animation handling
Our ObjectAnimator routine is executed with IX pointing to the
sprite to be animated, and A containing the animator byte.
The animator byte is in the format:
TTTTAAAA ... T=Tick / A=Animator
meaning a maximum of 16 animators, and 16 lines per animator script
(ticks)
GetAnimatorMempos backs the animator byte up into B, and loads the
pointer to the current animation script into HL
The top nibble of the animator byte is the current tick - the line
of the script we need to run,
We effectively multiply this nibble by 4, and add this offset to the
current animator script pos.
We get the command number - the first of the 4 bytes of the line of
the script, and execute it via the 'VectorJump' subroutine.
This uses ex (sp),hl to effectively jump to HL,
while preserving the old value of HL which was pushed onto the
stack.
VectorLookup will read a 16 bit word from the table of addresses
HL
A is used as the entry number, and the result is returned in HL
Animation Commands
At the end of each script we need to update the current tick (line
number) of the script.
The Sprite option changes the current graphic.
The first parameter is the sprite number.
The second byte is a flag to force a repaint of the tilemap -
Mintile has trouble with half tile moves of complex shaped sprites
and this can help reduce glitches
The Third parameter is the direction of the sprite - 0 leaves it
unchanged, 1 faces Right (0 in the flags) 2 faces Left (1 in the
flags)
Move will make an immediate relative 8 bit move to the sprite's
X,Y,Z pos
X is for moving left and right, Y is for jumping, Z is for moving
'In and Out' of the screen (like TMNT or Renegade)
If the player is facing left DoXFlip_IX will reverse the X move.
Attack will set the attack, and optionally a sprite.
This is used for punching or blocking. The sprite for the 'punch'
will be set and a >0 attack will be set... or <0 for a block!
Loop is used for the walking animation, which will continue until
we stop moving.
Spawn is used to spawn the sub object (fireball)
The pointer to the sub object is loaded in from the SubSprite
pointer, and the new object is initialized from the settings array
passed in the parameter.
The animator code supports complex condition
based loops, and executions to subroutines, but they were not
needed for this game.
The unused action types can also be re-written to do anything you
want!
Lesson
CF6 - Fighter Movement Processing
The Animation code performs scripted actions, but actions relating
to the player or cpu's decided action are handled by 'UpdateFighter'
- let's check that out!
See
ChibiFighter
folder
UpdateFighter - Actions!
At the start of the routine we
check if the player is 'hurt'
If the player is injured, they are on the floor, and they cannot
move of their own volition.
We check the Animator is set to 6 (the injury animator)
The D register contains the bits of the joystick in the format
%4321RLDU
We can jump with Fire 2, but We may not have a fire 2!
Fire 2 is a 'synonym' for Down+Fire1 which will also jump!
We don't want the player to be able to punch or block forever, so
we have a 'timeout' between Fires!
It will only decrease once Fire is released, and stops the player
punching again too quickly
We buffer 8 recent keypresses for special moves.
Next we check if fire is pressed,
If it is we check the timeout. The player isn't allowed to hold down
fire, the timeout stops this, and clears the keypress if the player
hit fire recently
We have a special move... Fireball!
The sequence to perform a fireball is Down, Down-Right, Right+Fire
(or the reverse if facing left)
CmpSeq will compare recent keypressses to DE
We handle the direction the player is facing via DoXJoyFlip_IX which
will handle the flipping of Left/Right if pressed
Next we check if the player has tried to jump - this only works if
the player is on the ground (Ypos=152)
If the player can jump we set Spr_Ymove - this will decrease the
Ypos over time until Spr_Ymove=0
We check the down direction...
If down+Fire are pressed we block
Fire on it's own will punch
Fire+ Left or Right will kick
If an action was decided we set it now!
UpdateFighter - Movements!
If no directions we pressed, we reduce the timeout on the buffer
(used for special moves)
We clear the buffer when the timeout hits zero.
If the player is moving left/right AND is on the ground AND it's
not already set, we need to set the animator to 1
If the player has stopped moving and was walking we set the animator
to 0
Next we check up and down keys.
These move in and out of the screen
We check Left and right, and set bit 0 of the flags accordingly
Flags=1 will flip the sprite - making it face left.
Next we check the players position
The player can be 'pushed' offscreen by an attack, but will slide
back in over the next few frames.
We the run the AnimateSprite routine which will handle other
movements.
Lesson
CF7 - Animate Sprite
The AnimateSprite routine handles automatic movements outside of
player control.
See
ChibiFighter
folder
AnimateSprite
Before we do anything we check the objects life,
An object with a life of zero does not need updating!
W next load in the pointer to the animation script command list.
If the sprites animator isn't zero, we run the Animator script
routines, and update the current animator number.
We need to update the position! We need to factor in three things!
Firstly the 16 bit XposW,YposW and ZposW are the center point, so we
need to subtract half the width hand height for the final drawing
positions Spr_Xpos and Spr_Ypos
Second we need to update the draw Ypos position based on the 'depth'
Zpos
Third we need to back up the old width and height - as the new
sprite graphic may change them!
Before we draw it The sprite number may have changed, so we load
in the info for the current image from the SpriteInfo table.
We now process the sprite moves, If the sprite is attempting to
jump we move it up the screen.
If not, and the sprite is above the ground (Ypos<152) we move it
closer to the ground.
We now process any moves being applied to the X position
Finally we run the 'remove and zero' routines.
The 'remove sprite routine takes the sprite off the screen -
redrawing the tilemap underneath.
the 'Zero' routine attempts to reduce flicker, by stopping the
redraw of parts which will be filled by the new sprite -
unfortunately it's not 100% accurate when sprites move a 1/2 tile!
Maths!
The Random Number Generator used by ChibiFighter is a fairly crude
which uses a 16 bit seed.
Collision detection for Punches and Kicks uses the position of two
objects
We're going to check if object IX is in range of IY, Where IX
is the attacker, and IY is the victim!
The range is fixed &0C10 - 12x16 units.
The range checking routine is the one we've used before.
Lesson
CF8 - CPU AI
The AI routine makes a decision on the computers actions. These are
passed to the main routine as a simulated 'Keypress'
Lets take a peek!
See
ChibiFighter
folder
AI Routines
The possible move sequences for the CPU are stored in a table 'Strategy List'.
Each move sequence is 8 bytes. The first byte is a delay between
each direction. the rest of the bytes are Keypresses (when bit 7=1)
or a 'sequence change' for chaining sequences (when bit 7=0)
Strategy list format:
Speed,Keypress,Keypress,Keypress
Keypress %1XXXXXXX = Keypress match (%--21RLDU)
Strategy %0XXXXXXX = Jump to new strategy
ReadAI will return a keypress in
the Accumulator.
If the Timeout for the last keypress has not reached zero,we just
return the same value.
We load in the current strategy.
Zero means the CPU is still thinking what to do
Each Strategy uses 8 bytes, we calculate the offset to the current
strategy and load in the Delay time.
We then shift to the current 'tick' - ant therefore the new
keypress.
In theory we can chain strategies by setting the top bit of the
strategy byte to 0, but actually this was never used in the final
game.
Deciding a new strategy
We combine D and E to decide our final strategy,
We use random data to make the cpu unpredictable, we store this in B
We compare the player (IY) and CPU (IX) position to decide what to
do.
We compare the two characters, and see if they are at the same Z
position on the ground.
If they are not, we move the CPU closer to the player
If the Z position is the same We consider if the CPU should jump,
there is effectively a 1/4 chance the CPU will decide to jump.
We now decide if the CPU needs to move Left or Right to get closer
to the player.
If the Player is far away, we may decide to throw a fireball,
otherwise we'll punch or kick!
Which we do is random!... The direction of the punch or kick is
based on the D register
There is a very small chance of a fireball attack, just 1 in 16.
At the end we set the new AI Strategy
When the CPU is thinking, the
CPU will block for a while and think.
We slow down the CPU to make it dumber to give the human more chance
to retaliate.
Lesson
CF9 - Amstrad CPC code
The Tile drawing and sound routines are platform specific.
Lets take a look at the CPC version.
See
ChibiFighter
folder
Sound Routines
Music and SFX are performed by ChibiTracks.
In addition to the song Chibitracks needs various includes to work.
We install a jump to the IM2 interrupt handler at address
&0038
Our music is designed to play back at 50hz, but there are 6
interrupts per screen redraw on the CPC,
We use a 'SongTimer', and only update the sound every 1 in 6
interrupts.
Before our music can play, we must set the song base and call
StartSong
We can effect SFX by forcibly playing instruments on one of the
channels.
This uses the function 'InstPlaySFX' which executes
'CommonSFXModule'
CommonSFXModule, if defined is also used by ChibiTracker for the
same purpose.
Tile Drawing Routines
The MinTile shared routine provides calculation and tilemap
planning, however we need to do the actual job of 'Drawing' in the
platform code.
The code can horizontally flip tiles, on the Amstrad CPC we use a
256 byte lookup table, to 'pre-calculate the flipping of the 4
pixels in a Mode 1 byte
The LUT needs to be byte aligned, so &8100 or &FE00 would
be fine, but &FE01 would not!
We have a 'GetScreenPos' function , which calculates the VRAM
destination for the sprite objects.
DoStrip will draw one horizontal strip of tiles.
The job is defined by the following registers
HL = VRAM Dest
BC' = Tilemap
DE' = Pattern data
IYL = TileMap Width
The tilemap will either be the TileCache (for the background) or a
mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn
we use stack misuse to speed up loading the pattern data
DoStripRev has essentially the same function, however it
horizontally flips the pattern data via the lookup table.
The rest of the
Tilemap code is a multiplatform module called 'MinTile'
We'll look at that soon in a separate series!
Lesson
CF10 - Sam Coupe code
The Tile drawing and sound routines are platform specific.
Lets take a look at the SAM version.
See
ChibiFighter
folder
Sound Routines
Music and SFX are performed by ChibiTracks.
In addition to the song Chibitracks needs various includes to work.
We're going to use IM1 for interrupts, which means we need RAM at
memory address &0038.
We load our program in at memory address &8000, but we page the
bank into the &0000-7FFF rage, and use the &8000+ range for
Vram
We install a jump to the IM2 interrupt handler at address
&0038
The ChibiTracks update runs in the interrupt handler, it
uses AF,BC,DE,HL and IX only
Before our music can play, we must set the song base and call
StartSong
We can effect SFX by forcibly playing instruments on one of the
channels.
This uses the function 'InstPlaySFX' which executes
'CommonSFXModule'
CommonSFXModule, if defined is also used by ChibiTracker for the
same purpose.
Tile Drawing Routines
The MinTile shared routine provides calculation and tilemap planning,
however we need to do the actual job of 'Drawing' in the platform code.
We have a 'GetScreenPos' function , which calculates the VRAM
destination for the sprite objects.
DoStrip will draw one horizontal strip of tiles.
The job is defined by the following registers
HL = VRAM Dest
BC' = Tilemap
DE' = Pattern data
IYL = TileMap Width
The tilemap will either be the TileCache (for the background) or a
mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn
We use 4 color pattern data, and convert it up to 16 color.
Each byte of the pattern data contains color info for 4 pixels (2bpp
= 4 color). A 'Tint' is used to select one of 4x four color
palettes.
We load the bits into C, and shift them into 2 screen bytes (4
pixels).
If the source byte is zero, we just write directly, to save a little
bit shifting time!
We have to do this twice for each 8 pixel (2 byte) wide tile.
DoStripRev has essentially the same function, however
the bitshifts are reversed to swap the pairs of pixels
The rest of the
Tilemap code is a multiplatform module called 'MinTile'
We'll look at that soon in a separate series!
Lesson
CF11 - SMS code
The Tile drawing and sound routines are platform specific.
Lets take a look at the SMS version.
See
ChibiFighter
folder
Sound Routines
Music and SFX are performed by ChibiTracks.
In addition to the song Chibitracks needs various includes to work.
We're going to use IM1 for interrupts, we need to put a Jump at
memory address &0038 in the header
We need to set bit 5 of VDP Register 1 to enable the Vblank
interrupt
The ChibiTracks update runs in the interrupt handler, it
uses AF,BC,DE,HL and IX only
On the SMS we need to read in the VDP control port &BF to clear
the interrupt - we don't actually need to do anything with the read
data!
Before our music can play, we must set the song base and call
StartSong
We can effect SFX by forcibly playing instruments on one of the
channels.
This uses the function 'InstPlaySFX' which executes
'CommonSFXModule'
CommonSFXModule, if defined is also used by ChibiTracker for the
same purpose.
Tile Drawing Routines
The MinTile shared routine provides calculation and tilemap planning,
however we need to do the actual job of 'Drawing' in the platform code.
Our Tilemap data is actually 4 color - 2 bitplanes.
We 'Tint' the 4 color data, by using IX as a constant value for the
tip 2 bitplanes - effectively giving us 4 palettes of 4 colors.
We define the 4x 4 color palettes, two for our background, and two
for our players
The tile loading routines define IX with the top two bitplane
values to select the 'palette'
We have a 'GetScreenPos' function , which calculates the VRAM
destination for the sprite objects.
DoStrip will draw one horizontal strip of tiles.
The job is defined by the following registers
HL = VRAM Dest
BC' = Tilemap
DE' = Pattern data
IYL = TileMap Width
The tilemap will either be the TileCache (for the background) or a
mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn
We transfer the tile numbers into the tilemap - each uses the 2 byte
tile number (with attributes)
DoStripRev has essentially the same function, however
the X-flip bit is set in the attributes
The rest of the
Tilemap code is a multiplatform module called 'MinTile'
We'll look at that soon in a separate series!
Lesson
CF12 - Spectrum Next code
The Tile drawing and sound routines are platform specific.
Lets take a look at the ZXN version.
See
ChibiFighter
folder
Sound Routines
Music and SFX are performed by ChibiTracks.
In addition to the song Chibitracks needs various includes to work.
We're going to use IM1 for interrupts,
We need to put a Jump at memory address &0038, but on the
spectrum this is ROM.
We can page in RAM at the &0000-1FFF range with NextReg &50
The ChibiTracks update runs in the interrupt handler, it
uses AF,BC,DE,HL and IX only
Before our music can play, we must set the song base and call
StartSong
We can effect SFX by forcibly playing instruments on one of the
channels.
This uses the function 'InstPlaySFX' which executes
'CommonSFXModule'
CommonSFXModule, if defined is also used by ChibiTracker for the
same purpose.
Tile Drawing Routines
The MinTile shared routine provides calculation and tilemap planning,
however we need to do the actual job of 'Drawing' in the platform code.
We use a 1 byte per color palette
We select the palette with reg &40, and send the values with
&41
Although the screen is 256 color, We use a total of 16 colors
We define the 4x 4 color palettes, two for our background, and two
for our players
We use a 'TileTint' to select one of the 4 palettes
We have a 'GetScreenPos' function , which calculates the VRAM
destination for the sprite objects.
We page in the screen VRAM into the &2000-3FFF range.
DoStrip will draw
one horizontal strip of tiles.
The job is defined by the following registers
HL = VRAM Dest
BC' = Tilemap
DE' = Pattern data
IYL = TileMap Width
The tilemap will either be the TileCache (for the background) or a
mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn
The source bitmap data is 4 color, so we multiply the tile number by
16 to get the pattern data address
We read in a byte from the DE source.
We take 2 bits from this, shift them in position and OR in the tint
from B
We do this 4 times (4 pixels) before reading in a second byte.
After 2 bytes we've done a full line.
We repeat for 8 lines, then move across one tile and repeat for
the rest of the strup
DoStripRev has
essentially the same function, however we draw the pixels from right
to left, reversing the pattern bitmap
The rest of the
Tilemap code is a multiplatform module called 'MinTile'
We'll look at that soon in a separate series!
Lesson
CF13 - MSX2 code
The Tile drawing and sound routines are platform specific.
Lets take a look at the MSX2 version.
See
ChibiFighter
folder
Sound Routines
Music and SFX are performed by ChibiTracks.
In addition to the song Chibitracks needs various includes to work.
We're going to use IM1 for interrupts,
By default on the MSX2, the low area is ROM, so &0038 is
redirected to the firmware interrupt handler, however we can
redirect this by patching a jump to our own interrupt hander at
address &FD9A
We also need to set bit 5 of Mode Register 1 to enable the VBlank
interrupt
The ChibiTracks update runs in the interrupt handler, it
uses AF,BC,DE,HL and IX only
When we return we pass control back to the firmware interrupt
handler, so we do not enable interrupts before
returning
Before our music can play, we must set the song base and call
StartSong
We can effect SFX by forcibly playing instruments on one of the
channels.
This uses the function 'InstPlaySFX' which executes
'CommonSFXModule'
CommonSFXModule, if defined is also used by ChibiTracker for the
same purpose.
Tile Drawing Routines
The MinTile shared routine provides calculation and tilemap planning,
however we need to do the actual job of 'Drawing' in the platform code.
We need to transfer our tile patterns to the hidden area of VRAM
(Lines 256+)
For sprites we need to store a normal copy, and an Xflipped copy.
We're running in 16 color mode, but our source patterns are only 4
color
We define the 4x 4 color palettes, two for our background, and two
for our players
We send our tile patterns to Vram, applying a 'Tint' to select one
of the 4 colors
There are two versions 'SendTiles' for background graphics, and
'SendTilesDuo' which sends a regular tile and Xflips the tile.
We transfer the 16 bytes of the source pattern. we
use a HMMC command to send the data.
Each pattern byte contains 4 pixels. We have formatted the source
data to allow it to easily be converted.
We mask the first two pixels and OR in our tint, sending them to the
VDP.
We then load the data again, bit shift it 2 pixels, and mask the
second two pixels.
SendTilesDUO will define the pattern, once normally,
and once x-flipped 128 Y-lines down
Each line of our tile pattern is 2 bytes, we move to the right of
the tile, and rotate the bytes of the source data to Xflip them
DoStrip will draw
one horizontal strip of tiles.
The job is defined by the following registers
HL = X,Y pos in VRAM
BC' = Tilemap
DE' = Pattern data
IYL = TileMap Width
The tilemap will either be the TileCache (for the background) or a
mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn
We calculate the source pos in VRAM for the tile we want to transfer
and use HMMM to write the tile to the screen.
We repeat for 8 lines, then move across one tile and repeat for
the rest of the strup
DoStripRev has
essentially the same function, however we use the flipped version of
the tiles
The rest of the
Tilemap code is a multiplatform module called 'MinTile'