Y-Quest!
Introduction to the Y-Quest Series...
Y-Quest is a small remake of the dos game 'X-Quest'... This was originally written as part of my Livestream series, and is now being ported to other platforms

The Y-Quest code was bases on the simple series, It's free and open source, so you can use it however you wish

In this series, we'll look at the basic codebase, and the platform specific modifications for each platform

Lesson YQuest1 - Introduction and Data Structures
YQuest is a little game I wrote for multiple Z80 systems in my Livestreams... it has now been ported to the 6502 machines

Lets start by having a quick look at the game, and the structure of the game RAM, and the settings which define levels and enemies.

See YQuest folder folder


Y-Quest!
Y-Quest is an Action-Puzzle game... to complete each stage you need to collect all the 'crystals' onscreen, by navigating your 'Y' Icon around the screen.

The original X-quest was controlled by mouse, however this version is joystick controlled.

Unlike the original, the edges of the play area do not kill you.

Change acceleration with the joystick / keys UDLR...  You can shoot with the firebutton ... if you have a second fire you can immediately stop with it.

There are 16 levels, after which the levels will repeat.



Ram Definitions
All Sprite Objects uses the same 8 byte definition..

The Sprite Number defines the graphic image of the sprite from the sprite data bank... this is a single byte, and the address is calculated by multiplying the sprite number by the size of an 8x8 sprite
Xpos and Ypos are the screen position of the sprite.
Xacc and Yacc are the Acceleration of the sprite (how fast it moves in each direction
Program is the logic routine that handles movement - this is used by enemies that change direction and fire.
Collision program is the logic routine that handles collision - this defines enemies, crystals, bullets etc and how they affect the player... a value of 255 is an unused object... 254 is a dead object which will later respawn
The Ram is defined as offsets from the 'UserRam' Definition - this is to allow the code to work consistently on ROM based systems.

All values will be zeroed on startup, and any values with other required values will be initialized accordingly

CursorX and CursorY are the position of the next character draw - this is used by string printing routines.

SpriteFrame is used by the bitmap drawing routine... this selects which bank of sprites to use... there are up to 4 banks (depending on system memory)
The BulletArray allows for 8 player bullets - this uses the standard 'Sprite Object Layout'

The EnemyBulletArray allows for 8 enemy bullets - this uses the standard 'Sprite Object Layout'

The ObjectArray allows for up to 40 objects including enemies, crystals and mines - this uses the standard 'Sprite Object Layout'
Invincibility is a counter for how long the player is invincible, either at the start of a level, or after being killed
Random Seed is a 16 bit random seed - used by the psuedorandom number generator
KeyTimeout is used as a delay for key processing - this is to reduce the speed of acceleration of the player
Lives is the number of lives of the player
Level is the Level number - starts from 0
Crystals is the number of crystals left to collect - note: due to the object limit only 5 are shown at a time - they respawn when collected.
PlayingSFX is used to control the current sound
PlayingSFX2 is the last sound played - when it does not match PlayingSFX we need to update the sound
Score is the current score, 8 binary coded decimal packed digits (4 bytes)
HiScore is the best score so far.
The PlayerObject handles the player sprite, this uses the standard 'Sprite Object Layout', however there are labels to the component parts of the player object

There are also a backup of the X,Y position called LastPosX and LastPosY

Note: All these addresses are relative to the base address 'UserRam'... this is so the RAM data can be located where ever free ram exists on the target platform.

So the game can work on ROM machines, this is the only writable data, The is no self modifying code or altered data within the other areas of code.


Constants and Data definitions
The Player Object Backup is a copy of the player settings, this is used to reset the player at the start of a round
We have some constant definitions next.

Object Byte Size is the number of bytes in an object... The Player,Enemies and Bullets are all 8 bytes per object
Enemies is the number of objects... this includes Mines and Crystals.
Bullet Count is the number of bullets for Players and Enemies... 8 means the Player can shoot 8 bullets, and enemies can shoot a separate 8 bullets
Onscreen Crystals is the number of crystals onscreen... this can be less than the number per level, as crystals will respawn when collected as required
The Level Map is a bank of pointers to the Level data.
Each Level Definition has the same format.

First is a pair of header bytes... the first byte is the crystal count... the second is unused.

Next comes pairs of object definitions... the first byte is an object type... the second is a count.

The definition is ended by an object of 0 and a count of 255
Although the contents have the same purpose, The Object Type definitions are not in the same format as the Sprite Objects

Each definition is a different enemy type, 0 is the 'empty object' and is used for the end of the level definition
Next we have some Random Number Lookup tables
We have a set of 255 terminated Text strings.

These are used for ingame messages - for the Sega GameGear there are shortened versions
The BCD Definitions are used to add points to the current score...

The BCD code requires source and destination value to be 4 bytes - little endian
Finally we have the Title screen definition... one tile per byte

These use the same sprite numbers (and sprite graphics) as the enemy objects.

There are different versions depending on screen size.

YQ_Multiplatform.asm - the multiplatform code
Yquest 6502 was ported from the Z80 version, Like with my main tutorials, I use zero page entries to simulate z80 registers.

Each subroutine of the multiplatform code has been recreated, with the same layout function and purpose... for that reason I will not be covering the multiplatform code of the 6502 version - as it would repeat the content of lessons 2-5 of the Z80 series.


If you want to see videos of these, please see the z80 series here.

To assist conversion the author uses a program he wrote called 'AsmConv' (it's experimental, and is not publicly released)... this does a crude job of converting the code, but much of the work is done by hand.
To help convert the code many macros and functions are used... these are found in BasicMacros.asm and BasicFunctions.ASM





Lesson YQuest2 - BBC Specific code
The BBC version was the first port of the game on the 6502.

The BBC is a bitmap based system, with software sprites. Lets take a look at the code.

See YQuest folder folder


BBC Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.
Our program loads at address $3000 - but we need some of this area for our screen, so we shift it lower to $0200 (overwriting system vars!)

We also turn off the sound, and set up our screen... this is all basically the same as previous 'simple series'
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
Because the BBC's screen moves down 8 lines in 8 consecutive bytes , we're locking the screen co-ordinates to an 8x4 pixel grid... this means the movement will be a bit jerky, but it's much easier to code!
ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

however we need to calculate the sprite address of the desired tile, so we use GetSpriteAddr to do so
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player

If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' rotuine, this draws the player, and handles movement and drawing of enemies and bullets

Clear Screen and Print Char
To clear the screen (CLS), we need to wipe the bitmap area at address $4180 - we use the CLDIR0 command to do this, which wipes z_bc bytes from address z_hl
PrintChar will show a character to the screen, using a bitmap font the same as the sprites...

We need to calculate the position in the font data of the character

Each Character is 16 bytes, and there are no characters below 32 (space)

We also need to multiply up the X,Y pos into an 8x8 grid position.

Once we've shown a character, we increment the X position for the next character
Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the address in ram of the sprite data we want to show to the screen

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 256 bytes
DoGetSpriteObj will show object IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC)

Note, due to the BBC screen layout, we're 'rounding' the Y co-ordinates to an 8 line tall grid

Get Screen Pos
The Get ScreenPos function will take an XY pos in z_B and z_C... it will return a screen memory address in z_de

Our screen address starts at $4180...

Because of the screen layout (8 Y lines are consecutive in ram), we're only going to look at the top 5 bits of the Y position
Each line is 320 pixels, and there are 4 pixels per byte... this means our screen is 80 bytes wide, and because the 8 lines of each horizontal strip are combined, the top 5 bits must be multiplied by 640 ($280) (80*8=640)

Since the 8 Y lines are consecutive in memory, between each horizontal byte, we multiply the Xpos by 8

Our formula is:
$4180 + ({Top 5 bits of Y} * $280) + Xpos * 8

We achieve the multiplication by bitshifting ops.

Joystick Reading

We're going to read in from the joystick - the BBC joystick is analog - which is a bit of a pain.

We need to select an axis by writing to $FEC0, then read in from $FEC1

We need to see if the result is outside of the 'dead zone' and set the bits in z_h depending on the result.
We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and Palette

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
We have settings to define the palette, and screen size and position... these are transferred to the graphics hardware during startup




Lesson YQuest3 - Atari Lynx Specific code
the Lynx version uses 16 color bitmap graphics, giving great look and smooth movement.

Unfortunately, the screen size is rather small, so we'll resize our graphics to 8x6 tiles rather than 8x8

See YQuest folder folder


Atari Lynx Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'
When our program loads we need to initialize the graphics hardware and set up the screen.

Our screen will be at address $C000
Next we set up the palette... this is defined by 32 bytes at label 'Palette'
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

however we need to calculate the sprite address of the desired tile, so we use GetSpriteAddr to do so
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player

If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' rotuine, this draws the player, and handles movement and drawing of enemies and bullets

Because the Lynx is so fast, we have a second delay to slow things down with a delay loop.

Clear Screen and Print Char
To clear the screen (CLS), we need to wipe the bitmap area at address $4180 - we use the CLDIR0 command to do this, which wipes z_bc bytes from address z_hl
PrintChar will show a character to the screen, using a bitmap font the same as the sprites...

We need to calculate the position in the font data of the character

Each Character is 16 bytes, and there are no characters below 32 (space)

We also need to multiply up the X,Y pos into an 8x8 grid position.

Once we've shown a character, we increment the X position for the next character

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the address in ram of the sprite data we want to show to the screen

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 512 bytes

DoGetSpriteObj will show object z_IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC)

The Bitmap routine skips lines 1 and 5 - this scales the graphics down from 8x8 to 8x6 - allowing smaller graphics on the lynx...

Of course, another option would be to use smaller font and bitmap files (native 8x6) - however in this case storage space was not a problem, so this solution was quicker.

Get Screen Pos
We're going to use a function called "GetScreenPos"... this will calculate the memory address of the pixel we want to write from an X,Y co-ordinate:

Each line is 80 bytes wide ($50) and our screen starts at $C000 so our formula is:

Address = $C000 + (Ypos * $50) + Xpos

We effect a multiply by bitshits... $50 in binary is %01010000 ...

We will shift the Y bits into each '1' position then add to a running total... then add the base and the Xpos

Joystick Reading

When we want to read in from the joystick we just use $FCB0

We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and Palette

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
Finally we define our palette... in native format for the lynx hardware.

Even with the 8x6 graphics, the gameplay screen is a little small...

We could always scale down to 4x4, but this would make the graphics very unclear!


Lesson YQuest4 - C64 Specific code
The C64 version uses single byte wide (4 pixel) sprites - it uses 4 color mode.

Lets take a look!

See YQuest folder folder


Commodore 64 Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'... we also have a 'SpritePointer' - used as temp storage for the color lookup

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.

When our program loads we need to initialize the graphics hardware and set up the screen.

As our program is so big...We need to offset the screen... by default the screen ram is at $2000 - but we'll move it to $6000 by setting a 'base' of $4000.

This also moves the colors from $400 to $4400... the $D800 color attributes are unmoved
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

however we need to calculate the sprite address of the desired tile, so we use GetSpriteAddr to do so
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player

If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' routine, this draws the player, and handles movement and drawing of enemies and bullets

Clear Screen and Print Char
To clear the screen (CLS), we need to wipe the bitmap area at address $6000 - we use the CLDIR0 command to do this, which wipes z_bc bytes from address z_hl
PrintChar will show a character to the screen, using a bitmap font the same as the sprites...

We need to calculate the position in the font data of the character

Each Character is 16 bytes, and there are no characters below 32 (space)

We need to store the pointer to the palette entry for the font (16*2)

We also need to multiply the Y position by 8

Once we've shown a character, we increment the X position for the next character

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the address in ram of the sprite data we want to show to the screen

We also calculate the sprite color pointer

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 128 bytes

DoGetSpriteObj will show object z_IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC)
Once we've done the sprite bitmap data we need to transfer the colors to the color attribute ram areas.

we use the calculate sprite color pointer (SprPtn) to get the correct color bytes from the palette

Get Screen Pos
The GetScreenPos calculation is pretty tricky!

Normally our screen address starts at $2000...  however we've offset the screen by $4000, so it's at $6000 now

Because of the screen layout (8 Y lines are consecutive in ram), we're only going to look at the top 5 bits of the Y position
Each line is 320 pixels, and there are 8 pixels per byte (160 pix and 4 px  per byte in 4 color mode)... this means our screen is 40 bytes wide, and because the 8 lines of each horizontal strip are combined, the top 5 bits must be multiplied by 640 ($140) (40*8=320)

Since the 8 Y lines are consecutive in memory, between each horizontal byte, we multiply the Xpos by 8

Our formula is:
$6000 + (Y * 40) + Xpos * 8

We achieve the multiplication by bitshifting ops.
Each 8x8 square has it's own color attributes... these are stored at two memory addresses $D800+ and $4400+

There is one byte per 8x8 block at each of these addresses... we calculate these using formulas below:

Address1= $4400 + (Ypos * 40) + Xpos
Address2= $D800 + (Ypos * 40) + Xpos

We calculate these in pretty much the same way as before.

Joystick Reading

When we want to read in from the joystick we just use port $DC01

We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and Palette

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
We define a 2 byte palette for each sprite (0-15), and a 16th entry for the font.




Lesson YQuest5 - Apple II Specific code
The Apple II version is basically black and white, it's actually pretty straight forward,

Lets get the port done!

See YQuest folder folder


Apple II Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.
Our program loads at address $0C00 .

We need to set up our screen by reading from a few ports... the act of reading enables the option
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
A single byte on the Apple 2 has 7 pixels not 8!
For convenience, we'll rescale our sprites, and use 7x8 graphics, rather than the 8x8 we use on other systems

ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

however we need to calculate the sprite address of the desired tile, so we use GetSpriteAddr to do so
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player - but we don't have a fire 2 set up on the Apple II version

If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' rotuine, this draws the player, and handles movement and drawing of enemies and bullets

Clear Screen and Print Char
To clear the screen (CLS), we need to wipe the bitmap area at address $4000 - we use the CLDIR0 command to do this, which wipes z_bc bytes from address z_hl
PrintChar will show a character to the screen, using a bitmap font the same as the sprites...

We need to calculate the position in the font data of the character

Each Character is 8 bytes, and there are no characters below 32 (space)

We also need to multiply up the X,Y pos into an 8x8 grid position.

Once we've shown a character, we increment the X position for the next character

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the address in ram of the sprite data we want to show to the screen

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 256 bytes
DoGetSpriteObj will show object IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC)

Note, due to the BBC screen layout, we're 'rounding' the Y co-ordinates to an 8 line tall grid

Get Screen Pos
Calculating our screen address is rather annoying on the Apple II - the screen is split into 3rds... and each line in 8 is separate... the result is a rather annoying formula:

Yline... AABBBCCC - AA*$0028  BBB*$0080  CCC*$0400
+Xpos
+$4000 (screen base address)

We test the top 2 bits and branch out into sub-code that will do additions to calculate the final address...

Joystick Reading

Apple Joysticks are annoying!
they are analog... we have to strobe the port  then read from the X and Y ports, and count up until the top bit changes...
this is a 'timer'...using just 1 bit (the top one) it effectively returns an 'analog' value from about 0-100

We're reading in both the X and Y axis at the same time, skipping out of part of the loop when the X or Y part is complete.
We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites


Lesson YQuest6 - Atari 800 / Atari 5200 Specific code
The Atari 800/5200 version uses 4 color mode... due to the size of the program, we'll have to position the Display list right at the end of the ROM so it will work ok on the Atari 800.

The sprites will be half width 4x8 pixels

See YQuest folder folder


Atari 800 / 5200 Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.
Our program start depends on the system we're building for,

Our first task is to clear the GITA and zero page,

Next we set up the screen Display List and colors
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
The Game is intended to work in 4 color mode, however if you wish, you could run it in 2 color mode instead, the Graphics routines which do the 2 color option have been left intact.

ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

however we need to calculate the sprite address of the desired tile, so we use GetSpriteAddr to do so
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player - or it would, but we've not mapped a button to this function!

If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' rotuine, this draws the player, and handles movement and drawing of enemies and bullets

The system is still too fast... so we slow things down a little more.

Clear Screen and Print Char
To clear the screen (CLS), we need to wipe the bitmap area at address $2060 - we use the CLDIR0 command to do this, which wipes z_bc bytes from address z_hl
PrintChar will show a character to the screen, using a bitmap font the same as the sprites...

We need to calculate the position in the font data of the character

Each Character is 16 bytes, and there are no characters below 32 (space)

We also need to multiply up the X,Y pos into an 8x8 grid position.

Once we've shown a character, we increment the X position for the next character

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the address in ram of the sprite data we want to show to the screen

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 128 bytes
DoGetSpriteObj will show object IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC)

Note, due to the Atari 5200 screen layout, we're 'rounding' the Y co-ordinates to an 8 line tall grid, and the X position to  to a 4 pixel wide (one character) grid

Get Screen Pos
GetScreenPos calculates the screen address from X,Y... our screen base is $2060 - and each line is 40 bytes wide... so our calculation is:

Address= $2060 + (Ypos * 40) + Xpos

Because the 6502 has no multiplication, we do bitshifts to double the value... it's easiest to split (Ypos * 40) into (Ypos * 32) + (Ypos * 8)

We also add the Xpos and $2060 - this calculates the final address in memory...

Joystick Reading

On the Atari 800 , we can read in the UDLR controls of the first joystick from the PIA

The top nibble is joystick 2 - so we ignore this for todays example
On the 5200 we'll have to read from the analog hardware

We read in each axis from the POKEY ($E800/1)

We use a deadzone of 128 - and if the axis is outside of that deadzone we set a bit of the resulting joystick value - this converts analog to digital.
We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and Display List

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
The Display list defines the screen layout... we need one byte per line for the bitmap modes, and becares/Yquest6_15_.pnguse of limitations of the hardware we have to manually define an offset when the VRAM reaches address $3000

The end of the list contains a loop back to the start ... this defines the screen...

if we set Smode to $0E we will have a 4 color screen... if we se Smode to $0F we will have a 2 color high res screen.
Finally we have the rom footer, with the start of the program address.







Lesson YQuest7 - PC Engine / Turbografix Specific code
Lets port Yquest to the PC-Engine

For simplicity, We're going to use the tilemap to do the graphics... we're going to have some trouble though, as the default bank mapping only gives us 8k ram!

See YQuest folder folder

PC Engine Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.

We also need to define the memory address of the Direct page, and set up some symbols for the common code.


Our Header needs to set up the pages of Rom...

each page is just 8k, but our program is too big for that, so we need to page multiple banks in... when the system starts only the first bank is paged in at $E000... so our first task is to page in the other banks (with our code running from $E000) then run the actual address (around $4000)

We page the last bank in after the jump, as that was the bank our program was running from before the jump!
We need to turn on the screen, Reset the tilemap, init the palette, and load the tile patterns
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
The "org $4000" represents the running address the code will finally run from, not the address it will run at startup...

Because of this before the "Restart" we can cannot use JSR or JMP commands as they use absolute addresses, but we can use branches OK if we wish

ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

GetSpriteAddr calculates the pattern number for the sprite.
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player
If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' rotuine, this draws the player, and handles movement and drawing of enemies and bullets

The system is still too fast... so we slow things down a little more.

Clear Screen and Print Char
To clear the screen (CLS), we need to set all the tiles to 0 (Space in the font)
Printchar will show a character to the screen... the first 96 patterns of the tilemap are the font... the first character is space (Char 32)

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the pattern we want to show to the screen.

The first 96 are our font.

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 16 tiles
DoGetSpriteObj will show object IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC)

Note, due to the Atari 5200 screen layout, we're 'rounding' the Y co-ordinates to an 8 line tall grid, and the X position to  to a 4 pixel wide (one character) grid

Get VDPScreenPos, PrepareVram and DefineTiles
We're going to define a function called GetVDPScreenPos to select an X,Y Tilemap position in Vram memory

As the tilemap is at VRAM address $0000 and each line is 32 tiles wide, our formula is:

Address=(Ypos *32) + X

We multiply Y by 32 by bishifts, and select the calculated address...
We're going to define a command called PrepareVram - which will select a memory address to write to, we'll need this for our define tiles function... we write the Low address byte to $0102, and the High address to $0103 when register 0 is selected with ST0

We'll use zero page entries z_de to store the address we want to use.
When we want to define tiles -we'll store a source address in VRAM z_de... a source address in ROM in z_hl, and a byte count in z_bc

We use the PrepareVram function to select an address, then write new bytes to $0102/3 after selecting Reg 0 with ST0

Joystick Reading

We need to reset the 'multitap' hardware by sending 1,3 to port $1000

We then send a 1 - read in 4 bits... and a 0 and read in 4 bits

This returns a byte in the format:
 Run / Start / B / A / Left / Down / Right / Up
We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and First bank header

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
At the end of the first 8k bank we need the rom header, with the start address,

This will end up at $FFFE when the system starts, and will boot-strap our banking routine on startup
We now have our palette in native format
Finally we need to pad the rom out to an 8k boundary



Lesson YQuest8 - VIC20 Specific code
The Vic-20 version uses custom characters to simulate Tiles / Sprites... Lets look at the VIC-20 version of the game

See YQuest folder folder


VIC 20 Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'... we also have a 'SpritePointer' - used as temp storage for the color lookup

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.

We're going to be defining a ROM cartridge, so our RAM for our game is at $1000
We need to start our ROM, we need a pointer to the start of our code...

The start of our code will Init the screen.
Next we transfer our bitmap data to $1C00 (the custom characters)
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

however we need to calculate the sprite address of the desired tile, so we use GetSpriteAddr to do so

We're using the same title screen as the Atari Lynx... but our screen is a little bigger, so we offset by 1 horizontal character
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y and B for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 would immediately stop the player, but we don't have a 2nd fire configured on the VIC

If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' routine, this draws the player, and handles movement and drawing of enemies and bullets

Clear Screen and Print Char
To clear the screen (CLS), we need to wipe the screen area at address $1E00 - we use the CLDIR command to do this, which wipes z_bc bytes from address z_hl

To clear the screen, we use Character 32 - which has been offset by 128 by our custom characters
Due to hardware limitations, we can't use our custom font, and have to use the internal one.

PrintChar will show a character to the screen, the VIC does not use ASCII, and some of the characters are in different places, to compensate for this, we have to subtract 64 from A-Z... we also strip the top 3 bits, as there is no lowercase characters in the font.

We then add 128, as our custom characters are 0+ in the characters set.

Once we've drawn the character, we need to set the color of the matching screen character

Characters and 'sprites' are basically the same thing on the VIC, as we're using custom characters for our configured graphics.

When we show characters to the screen We're drawing the font as White (Color 1)

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty character (Space in the system font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the address in ram of the sprite data we want to show to the screen

We also calculate the sprite color pointer

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 128 bytes

DoGetSpriteObj will show object z_IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw character A onscreen (at XY position BC)

After the character tile has been set we also need to set the correct color at address $9600+
we use the calculate sprite color pointer (SprPtn) to get the correct color bytes from the palette

Get Vdp Screen Pos
When we want to calculate the memory address our formula is:
Address= $1E00 + (Ypos * 22) + Xpos

As multiplying by 22 isn't so easy (and our screen is pretty short) we'll use a loop and addition to effect the multiply

Joystick Reading

On the Vic 20, we ned to read in from ports $9120 and $911F to get UDLR and Fire...

We'll need to ensure that port B is set to READ by writing to $9122


Sprite Data, Palette and Screen Init sesttings

We have 4 different files of sprites

16x4 for the 4 banks of sprites
We define a 1 byte palette for each sprite (0-15)
We have 16 bytes of screen init data, these are transferred to the screen hardware during startup


Lesson YQuest9 - NES Specific code
Lets look at the NES port - We'll be using the tilemap to do the ingame graphics.
Unfortunately because we can't write to VRAM except during Vblank, we'll create a buffer to store the changes, then transfer the buffer during Vblank

See YQuest folder folder

NES Header

So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.

We also need to define the memory address of the Direct page, and set up some symbols for the common code.


We need a rom header... We selected Mapper 4 - it has 8k VRAM , 8K Sram and 128k rom
We need to declare some more symbols...

We have an address for the VDPBuffer (Tilemap) the SpriteBuffer (not actually needed for this program)

VDP_CT is the offset within the buffer of the next free byte of the tilemap buffer.... the buffer will be sent to VRAM during next vblank
We need to define an interrupt handler which will transfer the data from our buffer to VRAM... this will run during VBLANK

We use a DMA to transfer the sprite data, and the data in the tilemap buffer is transferred manually

The VDPBuffer contains 3 bytes per entry... HL VRAM Memory address, and a new byte to write to that address
We need to turn on the screen, Define the palette and pattern data (tiles)

We also need to turn on the extra 'in cartridge' vram with register $A001
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
Our NMI routine will write some data to VRAM, but speed is a problem, as we don't have much time... this routine will write up to 32 tiles per vblank - too many more and it will overrun the Vblank...

If you're being a smarty pants, you could write a RLE routine that compresses the data, so you could set larger numbers of tiles in one go!

ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

GetSpriteAddr calculates the pattern number for the sprite.
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y and z_b for a loop counter - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player
If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' routine, this draws the player, and handles movement and drawing of enemies and bullets

The system is still too fast... so we slow things down a little more.

Clear Screen and Print Char
To clear the screen (CLS), we need to set all the tiles to 0 (Space in the font)
Printchar will show a character to the screen... the first 96 patterns of the tilemap are the font... the first character is space (Char 32)

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the pattern we want to show to the screen.

The first 96 are our font.

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 16 tiles
DoGetSpriteObj will show object IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC).... It doesn't do this directly however, GetVdpScreenPos calculates a position in the VDPbuffer (The buffer for the tilemap) and sets the byte corresponding to the tile...

It will be shown onscreen next vblank


Get VDPScreenPos, PrepareVram, DefineTiles and More
We're going to define a function called GetVDPScreenPos to select an X,Y Tilemap position in Vram memory

The tilemap is 32 tiles (bytes wide).. and the tilemap starts from $2000, so the formula is...

Address=$2000 + (Ypos *32) + X

We multiply Y by 32 by bitshifts, and select the calculated address...

We don't actually write into the Tilemap... we write to the buffer 'VDPBUFFER'

First We write the HL address in vram we want to write to, then we write the byte to change ... writing the byte to change occurs in the sprite routine.
GetVdpBufferCT calculates the next free entry in the buffer... the buffer has a limit of 32 bytes to change (during vblank)... so if it's reached 32x3 then we wait for the next vblank (when it clears down)

We write a Zero to VDP_CT during processing - in case the VBLANK runs while we're writing more data.
When we want to define tiles we do this directly to VRAM (not via the buffer)... we use PrepareVram to select a destination VRAM address

We write z_bc bytes of data from z_hl to the VRAM via port $2007
When we want to define tiles, we can turn off the screen, this means we don't need to wait for VBLANK before writing.

We use $2001 to set the PPUMASK which turns off the tilemap layer

We use $2000 to turn off the NMI interrupt handler
PrepareVram will select a destination VRAM address for writing to, we write the address (from z_de) to $2006 , High byte then Low byte
Waitframe will wait for VBLANK... we check this by writing a 0 to zero page entry 'Vblanked' to change... this will occur when our NMI runs.

Joystick Reading

We need to read in a sequence of 8 bits from port $4016 to get each direction key... we also need to strobe the port by writing 1 then 0 to the same port...

The 8 reads from the port will return the directions:
Read  1 - A
Read  2 - B
Read  3 - Select
Read  4 - Start
Read  5 - Up
Read  6 - Down
Read  7 - Left
Read  8 - Right
We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and First bank header

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
We now have our palette in native format
At the end of our cartridge we have the 'footer'... it has a link to the Start of the program and the NMI interrupt handler


Lesson YQuest10 - SNES Specific code
It's time for the SNES version... like the NES we need a buffer of the tilemap data... unlike the NES version we can use a DMA to copy a whole tilemap during vblank.

See YQuest folder folder

SNES Header

So we can support multiple platforms with different screen sizes we have platform specific screen size definitions

Each system needs an area of ram for game variables, know as 'UserRam'

We use CollisionMasks - these will skip some bits of the internal object positions, this is to ensure we don't detect a collision that is not visible due to the low resolution of screen co-ordinates.

We also need to define the memory address of the Direct page, and set up some symbols for the common code.

We also need a 'ScreenBuffer' in RAM - this will contain the tilemap, and will be transferred fom RAM to VRAM
We need to turn on the Tile layer and set up the palette
We need to set the scroll position of the tilemap,

We clear the RAM buffer (Tilemap copy, transferred to VRAM during Vblank)

We transfer our tile patterns to vram with 'DefineTiles',

Finally we turn on the screen.

We then initialize the 'ChibiSound' Sound driver - which copies the program code to the SPC700 sound chip
We zero the game's ram data. using CLDIR0 - which fills an area with zeros
 
Sound on the SNES is painfully complex, the SPC700 is separate processor with separate ram, and Is NOT a 6502 based chip... for more details see here

ShowTitle
When The title screen starts, we first reset the game settings for a new game to start,

Then we clear the screen.
We want to draw the tilescreen... it's held as a 1 byte per tile data block in our code at 'TitlePic'

We use our ShowSprite Routine to draw it to the screen...

GetSpriteAddr calculates the pattern number for the sprite.
We need to show a few text items to the screen

A 'Press Fire' Message
The 'LearnASM.Net' URL (Very Important!)
The Highscore.

LevelStart
When the game or a new level starts, we first wait for fire to be pressed.
Next we clear the screen,
Reset the player position
Initialize the level enemies with LevelInit

Level Loop
We have to pause a while during the game loop, so we read in from the joystick during this time, and if the user presses a key we'll store it for later.

we use Y and z_b for a loop counter to slow down the game - and X for any pressed button.

we use function call Player_ReadControlsDual - which will read in the joystick and fire button
First we draw the UI (Score etc)

Next we clear the player sprite.
To slow down the acceleration, we have a 'timeout'... if a key was pressed, for a short time we'll ignore any other keys.
We're going to process the input,

We process each direction, and alter the acceleration of the player if a direction is pressed...

We have a 'Bullet fire' routine (in the common code) for Fire 1

Fire 2 will immediately stop the player
If any button is pressed, the key timeout is set
Finally we run the multiplatform 'Draw and Move' routine, this draws the player, and handles movement and drawing of enemies and bullets

The system is still too fast... so we slow things down a little more.

Clear Screen and Print Char
To clear the screen (CLS), we need to set all the tiles to 0 (Space in the font)
Printchar will show a character to the screen... the first 96 patterns of the tilemap are the font... the first character is space (Char 32)

Sprite routines
We have a couple of ShowSprite routines...

BlankSprite will draw the empty sprite (Space in the font), using the object z_IX - to clear the old position of the object
GetSpriteAddr will calculate the pattern we want to show to the screen.

The first 96 are our font.

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 16 tiles
DoGetSpriteObj will show object IX to the screen, getting Sprite, and XY pos from z_IX

ShowSprite will draw Sprite A onscreen (at XY position BC).... It doesn't do this directly however, GetVdpScreenPos calculates a position in the tilemap buffer in z_hl...

We then transfer the two bytes that define the tiles in to ram.

It will be shown onscreen next vblank


Get VDPScreenPos, PrepareVram, DefineTiles and More
We're going to define a function called GetVDPScreenPos to select an X,Y Tilemap position in Vram memory

We need to calculate an address in the ScreenBuffer for the tile.

Each tile is 2 bytes, and the tilemap is 32 tiles wide... so the formula is:

Address= SnesScreenBuffer + (Ypos * 32 * 2) + (Xpos * 2)
When we want to define tiles we do this directly to VRAM (not via the buffer)... we use PrepareVram to select a destination VRAM address

We wait for Vblank before writing any data.

We write z_bc bytes of data from z_hl to the VRAM via ports $2119 and $2118...

We have to write in this order, as we've set Autoinc to occur on $2118
To Check if we're currently in Vblank, we test the top bit of $4212 - while this is Zero we're not currently in vbank
PrepareVram will select a destination VRAM address for writing to, we write the address (from z_de)... low byte to  to $2116 , High byte to $2117
We've defined a Custom NMI handler... this will run during VBlank.... we're going to use a DMA to quickly transfer the SnesScreenBuffer from ram to VRAM

We have to select a destination Vram address with $2116/7.
We define the autoinc mode to update on the write to $2119 - we d this with port $2115
Next and select the destination port with $4301 - we select $2118 - as we'll write in word pairs.
We specify the source (as a 24 bit number) using $4302/3/4... and the number of bytes to transfer with $4305/6/7
We disable the H-DMA (it's a special function we don't want) with $420C

Finally we start the DMA transfer with bit 0 of $420B.

Once we've finished, we set the AutoInc with port $2115 back to update on $2118

Joystick Reading

We need to read in a sequence of 8 bits from port $4016 to get each direction key... we also need to strobe the port by writing 1 then 0 to the same port...

The 8 reads from the port will return the directions:
Read  1 - A
Read  2 - B
Read  3 - Select
Read  4 - Start
Read  5 - Up
Read  6 - Down
Read  7 - Left
Read  8 - Right
We also have a 'Wait for fire' function... this randomizes the seed, and waits for fire to be released, then pressed.

It's used for menus and between levels.


Sprite Data and First bank header

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
We now have our palette in native format
At the end of our cartridge we have the 'footer'... it has a link to the Start of the program (Reset Vector) and the NMI interrupt handler

We need an 8 and 16 bit set of Vectors

Lesson YQuest11 - Hardware Sprites on the PC Engine / Turbografix
Our Yquest program currently only uses the Tilemap... Lets extend it to use Hardware sprites!.
This will give the game nice smooth movement... lets learn how to use PCE hardware sprites!

See YQuest folder folder


Multiplatform Sprite code
We had an unused byte in the object definitions before... We're going to use it now!

This will be the 'Hardware sprite number'...
A Zero will be the same as before - a tile will be used 1+ will be a numbered hardware sprite - relating to one of the hardware sprites the system is capable of.

This flexibility to use tiles or hardware sprites helps with systems with small numbers of hardware sprites
We'll need to set banks of objects to consecutive sprite numbers (EG bullets)...
We use SetHardwareSprites to do this.
We need to alter the DrawAndMove function and check the 'hardware sprite number'

Values 1-128 are hardware sprites... other values (0 / 255 etc) are soft sprites

SATB - Sprite attribute table buffer

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

The PC Engine 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

Byte  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 XS=Xsize
F=Foreground (infront of tilemap) P=Palette


PC Engine Sprite code

the PC-Engine hardware sprites use palette entries 256+...

We're going to set them to the same colors as our tiles.
The Hardware sprites are in a different format to the regular patterns - They are 16x16... in 4 bitplanes

We can export the sprite data in the right format using AkuSprite Editor...

We also need to 'space out' the sprites from 8x8 to 16x16
We load the sprites in VRAM address $2000
DoGetHSpriteObj will draw a hardware sprite to the screen from object z_IX... like before, the XY pos will be read from object z_IX, the sprite frame will also be calculated

We need to calculate the pattern number for the current sprite, and the XY pos...
We need to set the hardware sprite in the SATB (Sprite attribute table buffer) - but we can't write it directly, so we write to VRAM $7F00-7FFF - then execute a transfer to copy these to the video hardware (Showing the sprite)
The Clear Screen Routine needs to turn off all the hardware sprites, we do this by moving the sprites off the visible screen
Until we set the 'Hardware Sprite Numbers' of the objects they will still use the old tile code...

We do this in the 'StartLevel' routine... setting the player object to Hsprite 1... the player bullets to 2-9... and so on...
As with most things on the PC-Engine, the hardware sprites are pretty poweful - and easy!

Each sprite is 16x16, and we have 64 - far more than Yquest needs!

Lesson YQuest12 - adding Hardware Sprites on the NES
Lets add hardware sprites to the NES... unfortunately, like the Tilemap, we can't alter hardware sprites outside of VBlank - so once again we'll use a buffer, and transfer it during VBlank

See YQuest folder folder


Multiplatform Sprite code
We had an unused byte in the object definitions before... We're going to use it now!

This will be the 'Hardware sprite number'...
A Zero will be the same as before - a tile will be used
1+ will be a numbered hardware sprite - relating to one of the hardware sprites the system is capable of.

This flexibility to use tiles or hardware sprites helps with systems with small numbers of hardware sprites
We'll need to set banks of objects to consecutive sprite numbers (EG bullets)...
We use SetHardwareSprites to do this.
We need to alter the DrawAndMove function and check the 'hardware sprite number'

Values 1-128 are hardware sprites... other values (0 / 255 etc) are soft sprites


NES Sprites

Sprites on the NES are defined by 256 bytes of OAM memory- 4 bytes per sprite
The byte is selected by setting the OAM-address with memory location $2003 - effectively with 4x the sprite number...  then by writing the 4 bytes to $2004 (the OAM address autoincs)

Byte Purpose Bits Meaning
1 Ypos YYYYYYYY

2 Tilenum TTTTTTTT

3 Attribs VHB---PP Vflip  Hflip  Background priority  Palette
4 Xpos XXXXXXXX

NES Sprite code 

We need to copy our sprite buffer to the 'OAM' duirng our NMI Vblank handler... we do this by writing the top byte of the address to port $4014... this will transfer 256 bytes of data to the video hardware
In this case the sprites use the same patterns, but separate palettes - the first 4 palettes are for the tilemap... the second 4 palettes are for the sprites.
We need to turn on the hardware sprites with port $2001, and zero the sprite buffer
DoGetHSpriteObj will draw a hardware sprite to the screen from object z_IX... like before, the XY pos will be read from object z_IX, the sprite frame will also be calculated

We need to calculate the pattern number for the current sprite, and the XY pos...

We've defined which of the 4 palettes to use for each of our defined sprites, we store this in z_l

For sprites, The top left corner of the screen is 16 pixels down, so we add 16 to z_iy


We need to set the hardware sprite in the SATB (Sprite attribute table buffer) - but we can't write it directly, so we write to VRAM $7F00-7FFF - then execute a transfer to copy these to the video hardware (Showing the sprite)
The Clear Screen Routine needs to turn off all the hardware sprites, we do this by zeroing the sprite buffer
Until we set the 'Hardware Sprite Numbers' of the objects they will still use the old tile code...

We do this in the 'StartLevel' routine... setting the player object to Hsprite 1... the player bullets to 2-9... and so on...

Getting NES sprites to work is a bit of a pain, but once we have the buffer code set up, it's all pretty easy!

The Nes has 64 sprites, and each can use one of the 4 color palettes - giving our game some nice color too!... especially considering palettes are set for the tilemap only for every 4 tiles (2x2 blocks)

Lesson YQuest13 - SNES Hardware sprites.
Like the tilemap, we can't write to the sprites out of Vblank, so once again we'll use a buffer for the sprites, and use the DMA to transfer the sprite data to vram

See YQuest folder folder


Multiplatform Sprite code
We had an unused byte in the object definitions before... We're going to use it now!

This will be the 'Hardware sprite number'...
A Zero will be the same as before - a tile will be used
1+ will be a numbered hardware sprite - relating to one of the hardware sprites the system is capable of.

This flexibility to use tiles or hardware sprites helps with systems with small numbers of hardware sprites
We'll need to set banks of objects to consecutive sprite numbers (EG bullets)...
We use SetHardwareSprites to do this.
We need to alter the DrawAndMove function and check the 'hardware sprite number'

Values 1-128 are hardware sprites... other values (0 / 255 etc) are soft sprites
We're going to use the same kind of DMA as we did to transfer the tiles, however unlike tile data, we don't write pairs of bytes to two different addresses, we write all the data to a single address.


SNES Sprite code

We need to copy our sprite buffer to the 'OAM' duirng our NMI Vblank handler... we use the DMA to do this

The sprite buffer uses a total of $220 bytes - we need to write these bytes to $2104
To select an 'address' in the OAM to write to we use ports $2102/3 - we write a zero to these to select the start of the OAM.

We want the bytes of data to all write to $2104... first we select 'single address' with $4300.... we select port $2104 with port $4301

We select the source address (our SnesSpriteBuffer) with port $4302/3/4
We select the number of bytes $220 with ports $4305/6/7

we start the transfer with bit 0 of port $420B
The sprites use palette entries from 128 onwards... we define them in the same way as our regular tile palette
We define the Sprite patterns at address $4000 - and transfer the sprite patterns to the VRAM... they don't use the same pattern definitions as the tilemap
Finally, we clear the buffer, and turn on the Tilemap+Sprites with port $212C
DoGetHSpriteObj will draw a hardware sprite to the screen from object z_IX... like before, the XY pos will be read from object z_IX, the sprite frame will also be calculated

We select the pattern with z_h - the first sprite is blank (So we can hide the sprites) so we add 1 to skip the blank sprite

We need to calculate the pattern number for the current sprite, and the XY pos...

We define the palette, and how the sprites overlay the tiles with z_l

To line up the sprites with our Tilemap, we add 16 to z_iy
We need to set the two bytes in the buffer...

In the first part, there are 4 bytes per sprite - Xpos, Ypos, Tile number and attribs
in the second part, there are 2 bits per sprite in a byte - so 4 sprites per byte... these are the 8th bit of the Xpos, and the scaling option

We have to bit shift these into the correct position and OR them into the current value at that memory position!... what a pain!
The Clear Screen Routine needs to turn off all the hardware sprites, we do this by zeroing the sprite buffer
Until we set the 'Hardware Sprite Numbers' of the objects they will still use the old tile code...

We do this in the 'StartLevel' routine... setting the player object to Hsprite 1... the player bullets to 2-9... and so on...

Lesson YQuest14 - C64 Hardware Sprites
We were using bitmap graphics before for our game - making the objects move in 4x8 blocks
The C64 has 8 hardware sprites - we'll use one to draw the player, which will allow for smooth movement.

See YQuest folder folder

The C64 can only do 8 hardware sprites - and we would need 40 to do all the enemy objects!
Many games get around this limitation by altering and moving the sprites as the screen redraws, so one hardware sprite draws multiple objects in multiple different horizontal lines of the screen, but this is complex, and outside of the scope of this tutorial.

Multiplatform Sprite code
We had an unused byte in the object definitions before... We're going to use it now!

This will be the 'Hardware sprite number'...
A Zero will be the same as before - a tile will be used
1+ will be a numbered hardware sprite - relating to one of the hardware sprites the system is capable of.

This flexibility to use tiles or hardware sprites helps with systems with small numbers of hardware sprites
We'll need to set banks of objects to consecutive sprite numbers (EG bullets)...
We use SetHardwareSprites to do this.
We need to alter the DrawAndMove function and check the 'hardware sprite number'

Values 1-128 are hardware sprites... other values (0 / 255 etc) are soft sprites

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

Sprites are 21 vertical lines and 63 bytes each...
In 1bpp (2 color) mode this makes sprites 24x63...
In 2bpp (4 color) mode they are 12x63...

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.

Address Purpose Bits Meaning
$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
$D004 Sprite #2 X-coordinate XXXXXXXX (only bits #0-#7).
$D005 Sprite #2 Y-coordinate YYYYYYYY
$D006 Sprite #3 X-coordinate XXXXXXXX (only bits #0-#7).
$D007 Sprite #3 Y-coordinate YYYYYYYY
$D008 Sprite #4 X-coordinate XXXXXXXX (only bits #0-#7).
$D009 Sprite #4 Y-coordinate YYYYYYYY
$D00A Sprite #5 X-coordinate XXXXXXXX (only bits #0-#7).
$D00B Sprite #5 Y-coordinate YYYYYYYY
$D00C Sprite #6 X-coordinate XXXXXXXX (only bits #0-#7).
$D00D Sprite #6 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
$D029 Sprite #2 color ----CCCC
$D02A Sprite #3 color ----CCCC
$D02B Sprite #4 color ----CCCC
$D02C Sprite #5 color ----CCCC
$D02D Sprite #6 color ----CCCC
$D02E Sprite #7 color ----CCCC

C64 Sprite code

We need to copy our bitmap sprites to the sprite ram at $5000+

DoGetHSpriteObj will draw a hardware sprite to the screen from object z_IX... like before, the XY pos will be read from object z_IX, the sprite frame will also be calculated

We select the pattern with z_h - we only have two sprites in our sprite bitmaps...

4x sprites for the player during the game
4x sprites for the player exploding when dead

We need to calculate the pattern number for the current sprite, and the XY pos...

We define the sprite color, and other sprite settings in z_l

To line up the sprites with our Tilemap, we add 16 to z_iy
settings for the 8 sprites use 1 bit each for $D015 (bit 0-7 for the 8 sprites)

We need to set to 1 or leave to 0 a bit depending on some parameters

we use C64SpriteConvertToMask to calculate the mask to clear a bit, and set it if required (depending on flag/parameter passed)

We need to set the relevant bits and bytes of the sprite within the hardware registers.

The SetHardwareSprite function does this for us.




 

View Options
Default Dark
Simple (Hide this menu)
Print Mode (white background)

Top Menu
***Main Menu***
Youtube channel
Patreon
Introduction to Assembly (Basics for absolute beginners)
Amazon Affiliate Link
Forum
AkuSprite Editor
ChibiTracker
Dec/Bin/Hex/Oct/Ascii Table

Alt Tech
Archive.org
Bitchute
Odysee
Rumble
DailyMotion
Please note: I wlll upload more content to these alt platforms based on the views they bring in

Z80 Content
***Z80 Tutorial List***
Learn Z80 Assembly (2021)
Learn Z80 Assembly (old)
Hello World
Simple Samples
Advanced Series
Multiplatform Series
Platform Specific Series
ChibiAkumas Series
Grime Z80
Z80 Downloads
Z80 Cheatsheet
Sources.7z
DevTools kit
Z80 Platforms
Amstrad CPC
Elan Enterprise
Gameboy & Gameboy Color
Master System & GameGear
MSX & MSX2
Sam Coupe
TI-83
ZX Spectrum
Spectrum NEXT
Camputers Lynx

6502 Content
***6502 Tutorial List***
Learn 6502 Assembly
Advanced Series
Platform Specific Series
Hello World Series
Simple Samples
Grime 6502
6502 Downloads
6502 Cheatsheet
Sources.7z
DevTools kit
6502 Platforms
Apple IIe
Atari 800 and 5200
Atari Lynx
BBC Micro
Commodore 64
Commodore PET
Commander x16
Super Nintendo (SNES)
Nintendo NES / Famicom
PC Engine (Turbografx-16)
Vic 20

68000 Content
***68000 Tutorial List***
Learn 68000 Assembly
Hello World Series
Platform Specific Series
Simple Samples
Grime 68000
68000 Downloads
68000 Cheatsheet
Sources.7z
DevTools kit
68000 Platforms
Amiga 500
Atari ST
Neo Geo
Sega Genesis / Mega Drive
Sinclair QL
X68000 (Sharp x68k)

8086 Content
Learn 8086 Assembly
Platform Specific Series
Hello World Series
Simple Samples
8086 Downloads
8086 Cheatsheet
Sources.7z
DevTools kit
8086 Platforms
Wonderswan
MsDos

ARM Content
Learn ARM Assembly
Learn ARM Thumb Assembly
Platform Specific Series
Hello World
Simple Samples
ARM Downloads
ARM Cheatsheet
Sources.7z
DevTools kit
ARM Platforms
Gameboy Advance
Nintendo DS
Risc Os

Risc-V Content
Learn Risc-V Assembly
Risc-V Downloads
Risc-V Cheatsheet
Sources.7z
DevTools kit

MIPS Content
Learn Risc-V Assembly
Platform Specific Series
Hello World
Simple Samples
MIPS Downloads
MIPS Cheatsheet
Sources.7z
DevTools kit
MIPS Platforms
Playstation
N64

PDP-11 Content
Learn PDP-11 Assembly
Platform Specific Series
Simple Samples
PDP-11 Downloads
PDP-11 Cheatsheet
Sources.7z
DevTools kit
PDP-11 Platforms
PDP-11
UKNC

TMS9900 Content
Learn TMS9900 Assembly
Platform Specific Series
Hello World
TMS9900 Downloads
TMS9900 Cheatsheet
Sources.7z
DevTools kit
TMS9900 Platforms
Ti 99

6809 Content
Learn 6809 Assembly
Learn 6309 Assembly
Platform Specific Series
Hello World Series
Simple Samples
6809 Downloads
6809/6309 Cheatsheet
Sources.7z
DevTools kit
6809 Platforms
Dragon 32/Tandy Coco
Fujitsu FM7
TRS-80 Coco 3
Vectrex

65816 Content
Learn 65816 Assembly
Hello World
Simple Samples
65816 Downloads
65816 Cheatsheet
Sources.7z
DevTools kit
65816 Platforms
SNES

eZ80 Content
Learn eZ80 Assembly
Platform Specific Series
eZ80 Downloads
eZ80 Cheatsheet
Sources.7z
DevTools kit
eZ80 Platforms
Ti84 PCE

IBM370 Content
Learn IBM370 Assembly
Simple Samples
IBM370 Downloads
IBM370 Cheatsheet
Sources.7z
DevTools kit

Super-H Content
Learn SH2 Assembly
Hello World Series
Simple Samples
SH2 Downloads
SH2 Cheatsheet
Sources.7z
DevTools kit
SH2 Platforms
32x
Saturn

PowerPC Content
Learn PowerPC Assembly
Hello World Series
Simple Samples
PowerPC Downloads
PowerPC Cheatsheet
Sources.7z
DevTools kit
PowerPC Platforms
Gamecube

Work in Progress
ChibiAndroids

Misc bits
Ruby programming









Buy my Assembly programming book
on Amazon in Print or Kindle!


Buy my Assembly programming book



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!


Buy my Assembly programming book



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!


Buy my Assembly programming book



Available worldwide!
Search 'ChibiAkumas' on
your local Amazon website!
Click here for more info!