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 68000 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


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
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 - x68000 Specific code
The x68000  version was the first port of the game on the 68000.

its a bitmap based system, with software sprites, and a simpler screen layout than the AtariST or Amiga.

See YQuest folder


X68000 Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions
We need to set up all the screen registers for a 256x256 @ 16 color bitmap screen.

We set up our palette of 16 colors... we do this by writing to $E82000+
We zero the game's ram data. using function CLDIR0 - which fills an area with zeros
 
We're only using bitmap 'soft sprites' on the x68000 - of course the X68000 has hardware sprites, but they're more than what we need for such a simple game, so we won't use them 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...

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 D1 for a loop counter - and D2 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 $C00000 - we use the CLDIR0 command to do this

We set the number of bytes to clear with D1... although our screen is 256 pixels wide, the memory layout means we have to clear 1024 bytes of width
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 32 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 A4 - 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... each sprite is 32 bytes

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 512 bytes (16 sprites)
DoGetSpriteObj will show object A4 to the screen, getting Sprite, and XY pos from A4

ShowSprite will draw Sprite D0 onscreen (at XY position D1,D4)

Get Screen Pos
The Get ScreenPos function will take an XY pos in D1 and D4... it will return a screen memory address in A2

Our screen address starts at $C00000..

Each pixel is stored in a separate word... so we multiply the Xpos by 2

Each line is 1024 bytes wide (irrespective of resolution)... so we multiply the Ypos by 1024

Joystick Reading

We read in the Joystick  buttons from $E9A001... we need to select which set of controls using $E9A005... if we want more than 2 fire buttons, we need to do this.

We return the results in D0

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 the palette words... on per color - these are in native x68000 format.
We need to define $800 bytes of ram for game variables

Lesson YQuest3 - Sinclair QL Specific code
The Sinclair QL also uses a bitmap screen - with 8 color graphics.... let take a look at the QL version

See YQuest folder


Sinclair QL Header
So we can support multiple platforms with different screen sizes we have platform specific screen size definitions
We need to turn on 8 color mode.w

We zero the game's ram data. using function CLDIR0 - which fills an area with zeros
 
We don't know where our program code will run in the final memory - due to VASM's optimization JMP's and JSR's will be switched to relative commands, but we need to fix the common code so the Level address reading works.

Modifications to the multiplatform code
Because we don't know where in ram the program will run, we to adjust the read routine for the level data... by adding the effective address of the program start, we will get the correct address for the level data.

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 D1 for a loop counter - and D2 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 $20000 - we use the CLDIR0 command to do this

We set the number of bytes to clear with D1... the screen is 256x256... and each byte has 2 pixels
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 32 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 A4 - 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... each sprite is 32 bytes

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 512 bytes (16 sprites)
DoGetSpriteObj will show object A4 to the screen, getting Sprite, and XY pos from A4

ShowSprite will draw Sprite D0 onscreen (at XY position D1,D4)

Get Screen Pos
The Get ScreenPos function will take an XY pos in D1 and D4... it will return a screen memory address in A2

Our screen address starts at $20000..

Each pixel is stored in a separate word... so we multiply the Xpos by 2

Each line is 128 bytes - so we multiply the Ypos by 128

Joystick Reading

Reading in from the keyboard has to be done with Trap 1 - command 9
We have to send a sequence of command bytes - with byte 6 as the row number - the trap will return a byte in D1 - with a bit high when the button is down.

We're going to use Trap #1 to read in from the keyboard, we need to pass the command we just defined to the trap in A3
The function will return the bits of the line in D1
We're going to use the bits in D1 to build up the result in D0

We're going to move each of the controls in one by one,
We'll use D2 as a temporary copy of the the register, and shift the required key bit into D0
We'll use Escape as a start button and Space,Enter and \ as Fire 1-3, shifting them into D0
Finally we'll shift in all the bits of UDLR into D0

The Cursor Keys are also used by an external Joystick if one is connected
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 YQuest4 - Amiga Specific code
Lets Port to the Amiga... we won't use hardware sprites on this system, we'll do the graphics in software

See YQuest folder


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

Because the Amiga screen is split into bitplanes, it's difficult to offset a sprite horizontally by anything other than 8 pixel blocks...

Therefore we're limiting the horizontal collision detection to match.
The Amiga needs quite a lot of config...

We need to connect to the graphics driver, set up the screen and interrupts
We've got some ram for the screen... we do this with the 'Copperlist' - a set of commands for the coprocessor

We need to set it up with the video hardware
We need to init the palette, and turn on the coprocessor
We zero the game's ram data. using function CLDIR0 - which fills an area with zeros
 
We're only using bitmap 'soft sprites' on the Amiga - of course the Amiga has hardware sprites, but they're more than what we need for such a simple game, so we won't use them here.... Later we'll upgrade this program with smoother sprite movement, but for now it's limited to 8x8 blocks

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 D1 for a loop counter - and D2 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 "ScreenRam"

The screen is 320 * 200 and each byte has 2 pixels
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 32 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 A4 - 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... each sprite is 32 bytes

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 512 bytes (16 sprites)
DoGetSpriteObj will show object A4 to the screen, getting Sprite, and XY pos from A4

ShowSprite will draw Sprite D0 onscreen (at XY position D1,D4)

We transfer the bytes of the sprite to the 4 bitplanes of the graphics screen.
Get Screen Pos
The Get ScreenPos function will take an XY pos in D1 and D4... it will return a screen memory address in A2

Each line is 40 bytes - the base is 'Screen_Ram' (in chip ram.

Joystick Reading

We read in the Joystick  buttons from $E9A001... we need to select which set of controls using $E9A005... if we want more than 2 fire buttons, we need to do this.

We return the results in D0

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 Chip ram

We need to wait for Vblank before we turn on the chip ram, we do this by testing port $DFF004
We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
We need to define the area of memory for the screen and Copperlist - these need to be in 'Chip Ram'
We need to define some sound samples for the sound driver (chibisound) to use. these also must be in chip ram.
We need to define $800 bytes of ram for game variables

The graphics routine here is based on the old simple series
The horizontal movement is pretty jerky here... we'll need to make a better sprite routine to fix that, but we'll look at that next time!



Lesson YQuest5 - Genesis Specific code

Let's look at making Yquest work on the Genesis... we'll use the tilemap to draw graphics to the screen.

See YQuest folder


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

We're using the tilemap - which is limiting movement to 8x8 blocks

We also need to define the 'user ram' area which is used for the game vars.
We need a cartridge header for our game.
We need to Disable the TMSS chip (copy protection)

We also set up our screen, by transferring the VDP registers to the screen hardware
We need to define our color palette.

We also need to define the pattern data for the tiles.

Finally we turn on the screen.
We zero the game's ram data. using function CLDIR0 - which fills an area with zeros

We're only going to use the tilemap for graphics in todays version - this will make the movement 'blocky....

Later we'll upgrade the game with hardware sprites, which will make it better.

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 D1 for a loop counter - and D2 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 Tilemap area from $C000 onwards... the tilemap is 32 * 28... and each tile is 2 bytes


We set the number of bytes to clear with D1... although our screen is 256 pixels wide, the memory layout means we have to clear 1024 bytes of width
PrintChar will show a character to the screen, using a tile of the tilemap.

We need to calculate the position in the Tilemapof the character

Each Character is 32 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 blank tile, using the object A4 - to clear the old position of the object
GetSpriteAddr will calculate the address in the pattern data... the first 96 tiles are the font

We also handle the sprite frame... there are 4 frames of animation for each of the 16 sprites
DoGetSpriteObj will show object A4 to the screen, getting Sprite, and XY pos from A4

ShowSprite will draw Sprite D0 onscreen (at XY position D1,D4)

We need to calculate the vram address of the tile we want to change, we then write the bytes of that tile with the new tile number.

Joystick Reading

There are two ports which are read and written for the joypad...
Joypad 1 is at address $A10005
Joypad 2 is at address $A10003

First, however, we need to set one of the bits of these ports to WRITE... we do this with 2 ports...
Joypad 1 is at address $A1000B
Joypad 2 is at address $A10009
The Joypad needs a sequence of writes to select the 'sections' of the joypad... this is achieved by writes with bit 6 as a 1

The first batch rerturns Up, Down, Left, Right... button C and Button B

The second batch returns Button A and Start

The final batch are Button X, Button Y and Button Z... as well as Mode.

Note: some of the buttons are duplicated... eg Up is returned in the first and second batch.
Finally, we shift around the bits, so we have all the buttons in a neat order in a single register.
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 the palette words... on per color - these are in native Genesis format.
Finally, we have our screen Init settings

Lesson YQuest6 - NeoGeo Specific code
Let's look at the NeoGeo... we'll use the Fix Layer to draw graphics to the screen.

We need to define our tile bitmaps in the XML file - as they are defined by ROM.

See YQuest folder


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

We're using the tilemap - which is limiting movement to 8x8 blocks

We also need to define the 'user ram' area which is used for the game vars.
We need a cartridge header for our game.

This is very long, and is based on "Neo-Geo Assembly Programming for the Absolute Beginner" by freem
We need to define our color palette, we define the common background color, then transfer the other 16 colors.

We use firmware calls to clear the Fix layer and Sprites.
We zero the game's ram data. using function CLDIR0 - which fills an area with zeros

The example today uses the FIX layer for all the graphics, it doesn't give good movement, but it gets things working quickly and easily.

We'll add hardware sprites in a later episode
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 also 'kick the watchdog'... if we don't do this regularly, the machine will reset (a crash protection for the arcade NeoGeo)

We use D1 for a loop counter - and D2 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 Fix layer from Vram address $7000+

We need to set the AutoInc mode by writing to $3C004 (This isn't usually a problem, as we usually don't write consecutive tiles.

The Fix layer is 40 tiles wide and 32 tiles tall - each tile is 2 bytes
PrintChar will show a character to the screen, using a tile of the Fix Layer.

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 blank tile, using the object  - to clear the old position of the object
GetSpriteAddr will calculate the address in the pattern data...

The font is loaded at address $10000 (tile $800)
The sprite graphics are loaded at address $16000 (768 tiles after the font)

We also handle the sprite frame... there are 4 frames of animation for each of the 16 sprites
DoGetSpriteObj will show object A4 to the screen, getting Sprite, and XY pos from A4

ShowSprite will draw Sprite D0 onscreen (at XY position D1,D4)

We need to calculate the vram address of the tile we want to change, we then write the bytes of that tile with the new tile number.

Remember: the Fix tilemap is 40 tiles wide, and 32 tiles tall, but consecutive tiles go DOWN the screen not across, so the formula is:

Address = $7000 + (Ypos * 32) + Xpos

Joystick Reading
There are 3 ports we want to use to read in the buttons
$380000 allows us to read in Select and Start for Joy 1 and 2

$30000 allows us to read in the directions and buttons of Joystick 1
$34000 allows us to read in the directions and buttons of Joystick 1

Palette data
We have the palette words... one per color - these are in native NeoGeo format.

Lesson YQuest7 - Atari ST Specific code

Lets Port to the Atari ST... The ST can't do hardware sprites, and unfortunately it's screen layout is a bit of a pain.

See YQuest folder


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

Because the Atari ST's screen is split into bitplanes, it's difficult to offset a sprite horizontally by anything other than 8 pixel blocks...

Therefore we're limiting the horizontal collision detection to match.
To Start the Atari ST verstion, we need to enable supervisor mode,
Next we need to turn on the screen, and init the screen ram.
We need to init the palette, and turn on the joystick handling routine
We zero the game's ram data. using function CLDIR0 - which fills an area with zeros
 
The software sprites in this example are limited to 8 pixel horizontal blocks like the simple series, later we'll upgrade the code with smoother movment.

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 D1 for a loop counter - and D2 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 "ScreenRam"

The screen buffer is 32256 bytes
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 32 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 A4 - 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... each sprite is 32 bytes

We also handle the sprite frame... there are 4 frames of animation for each sprite - each bank is 512 bytes (16 sprites)
DoGetSpriteObj will show object A4 to the screen, getting Sprite, and XY pos from A4

ShowSprite will draw Sprite D0 onscreen (at XY position D1,D4)

We transfer the bytes of the sprite to the 4 bitplanes of the graphics screen (in 4 consecutive words of vram)


****
Get Screen Pos
The Get ScreenPos function will take an XY pos in D1 and D4... it will return a screen memory address in A2

Each line is 160 bytes , 4 bitplanes are next to each other as words, eg $0000 $1111 $2222 $3333

Joystick Reading

To operate the joysticks we need to add a handler to the Atari OS.

We need to get the 'IKBD' vector table, then put our 'Joystick Handler' into it

This makes 'Joystick Handler' run every time a button is pressed - we then store the data from the joysticks in two bytes of 'Joystick Data' for later.

The format of the read in byte is %F---RLDU
We convert this into the format %---FRLDU
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, Palette and screen ram

We have 5 different files of sprites

96 for the font
16x4 for the 4 banks of sprites
We need to define a palette (in Atari ST format)
We need to define enough space for the screen ram, the screen is 32000 bytes in size, but we need it to be aligned on a byte boundary, so we allocate 32256 bytes

We need to define $800 bytes of ram for game variables

The graphics routine here is based on the old ones
The horizontal movement is pretty jerky here... we'll need to make a better sprite routine to fix that, but we'll look at that next time!


Lesson YQuest8 - x68000 Hardware Sprites
The old x68000 version with bitmap sprites had pretty smooth movement, but as there's so little documentation online for X68k hardware sprites, we'll use them anyway!
Lets learn how.

See YQuest 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

x68000 Hardware sprite code
We need to extend the display init routine... we need some extra settings for hardware sprites to set them up and enable them...

Note, these settings are different depending on the screen mode... take a look here
Hardware sprites don't use the same palette... we use address $E82200+ for the palette (In the same format as the normal palette.

The hardware sprites are 16x16, and are split into 4x 8x8 blocks in sprite ram... Sprite pattern ram is address $EB8000+




We need to space out our sprites to convert them from 8x8 to 16x16
Use Tools menu ->Sprites->SpriteSpace in my Akusprite editor

Of course AkuSprite Editor can export x68000 format sprites
When we want to set a sprite, we write 4 consecutive words from address $EB0000+

Address F E D C B A 9 8
7 6 5 4 3 2 1 0
$EB0000 - - - - - - X X
X X X X X X X X   X=Xpos
$EB0002 - - - - - - Y Y
Y Y Y Y Y Y Y Y   Y=Ypos
$EB0004 V H - - C C C C
S S S S S S S S   V=Vflip, H=Hflip, C=color, S=sprite
$EB0006 - - - - - - - -
- - - - - - P P   P=Priority (00=off 01=back 11=front)  
Because each hardware sprite can only appear on the screen once,
We need to allocate hardware sprites to each object... we use the 'SetHardwareSprites' function to do this
We have a new DoGetHspriteObj function - this will do the same as the old DoGetSpriteObj, however it uses the new hardware sprite code, rather than software sprites.
Because hardware sprites are a separate 'layer' - we need to alter our Clear Screen Routine.

We need to go through each hardware sprite, and remove it from the screen.
To remove a sprite we can just change it's XY co-ordinate to 0,0 - this is completely offscreen, so the sprite won't be drawn.
The fonts are still drawn with software sprites, but because objects are now drawn with Hardware sprites, they can now properly overlap.

the X68000 has incredibly powerful graphics, with multiple bitmap layers, and 128 hardware sprites.

It shouldn't really be a surprise though, as it's graphically as powerful as the Capcom CPS arcade system.




Lesson YQuest9 - Per pixel movement on the Amiga and Atari ST
The Amiga + Atari ST store data in bitplanes... one byte contains 8 pixels...
Our old code wasn't smart enough to allow our 8x8 sprite to be 'half on' a byte - so our movement was jerky... lets improve the code and make the movement smooth!

See YQuest folder


Smooth movement - theory.
Our sprite is 8 pixels wide... To move smoothly across the screen, as it moves across the 'byte boundary' we'll need to split its bits up and shift them accordingly..

in this screenshot, the code has been altered, so the 2nd byte is shifted a few pixels down...as the sprite moves, the shifted 2nd part will be clearly separate.
We need to 'chop up' our 8 pixel sprite into 2 parts...

We'll need an AND mask for the background pixels to keep, then we'll shift our sprite pixels into those locations - we do this for the 2 horizontal bytes the 8 pixel wide sprite may occupy (if it's entirely in the first one, the second will actually be blank)

We need to 'chop up' our 8 pixel sprite into 2 parts...
    Sprite bits         ------12 345678--
    Mask to keep bg:    11111100 00000011
    Screen Byte  :      AAAAAAAA BBBBBBBB

Our virtual co-ordinates work at 128 units wide, meaning there's 4 possible positions for our sprite
    Screen Byte  :      AAAAAAAA BBBBBBBB
    Pos 0:              12345678 --------
    Pos 1:              --123456 78------
    Pos 2:              ----1234 5678----
    Pos 3:              ------12 345678--
Amiga code
Our code is similar to before.

Once again, we calculate the screen byte position.

We now use the 4 unused bits of the virtual position to calculate a 'shift' (D7/D6) and 'mask' (D1/D4)

We use the mask to clear the bits of the background our sprite will overwrite, and the shifts to move the sprite into those positions.

As before, we need to do this for all 4 bitplanes, and each of the 8 lines of our sprite.

Atari ST code

The Atari ST code is basically the same as the Amiga, only on the Atari bitplanes are in neighboring words in RAM, making the 'next byte' code a little different.

Once again, we calculate the screen byte position.

We now use the 4 unused bits of the virtual position to calculate a 'shift' (D7/D6) and 'mask' (D1/D4)

We use the mask to clear the bits of the background our sprite will overwrite, and the shifts to move the sprite into those positions.

As before, we need to do this for all 4 bitplanes, and each of the 8 lines of our sprite.
Our code here only works with 8 pixel sprites... a 24 pixel wide sprite would be more complex, the 1st and 3rd byte would need masking, and all 3 would need bitshifts.
It's outside of the scope of what we're doing here, but it may be easier to design your game with a fixed sprite size, and make larger sprites out of multiple smaller ones, that way you can test and optimize one good sprite fast routine, rather than having a complex one that's not as well optimized

Lesson YQuest10 - Hardware sprites on the Genesis
We got Yquest working before with tilemap graphics, but that's not really enough!
Lets extend Yquest and use hardware sprites.

See YQuest 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

Sprites
In H40 mode (the mode used in these tutorials), we can have up to 80 sprites onscreen...  (H32 mode can do only 64)
Each sprite can be 8x8 to 32x32
The position (and other settings) are defined by 8 by (4 Words) per sprite.

The 'Link' Connects the sprites together, each sprite should point to the next (starting from Sprite 0) and the last sprite should point back to sprite 0, this is not like neogeo chaining,it doesn't make the sprites move together it doesn't make the sprites move together - it defines the 'draw order' of the active sprites.
Address Sprite Num Details
$D800 1 Ypos
$D802 1 Size,Link
$D804 1 Palette,Flip,Pattern
$D806 1 Xpos
$D808 2 Ypos
$D80A 2 Size,Link
$D80C 2 Palette,Flip,Pattern
$D80E 2 Xpos
$D810 3 Ypos


$D9FF 64 Xpos � Last Sprite of H32


$DA7F 80 Xpos � Last Sprite of H80

As we can see, Each sprite has 4 words, they use the same pattern data as the background...

Address   F   E   D   C   B   A   9   8   
 7   6   5   4    3   2   1   0   Details
$D800 - - - - - - Y Y
Y Y Y Y Y Y Y Y  Y-Pos
$D802 - - - - W W H H
- L L L L L L L  Width (8,16,24,32), Height (8,16,24,32), Link (to next sprite)
$D804 P C C V H N N N
N N N N N N N N  Priority, Color palette , Vflip, Hflip, tile Number
$D806 - - - - - - - X
X X X X X X X X  X-pos
MegaDrive (genesis) Hardware sprite code
The 'Link' part of hardware sprites builds a 'display order'... if it's not set for a hardware sprite, the sprite won't show... for this reason, we init all the hardware sprites we need with a link to the next using our 'Blank Sprite' routine.
When we want to set a sprite, we write 4 consecutive words from Vram address $D800+


Because each hardware sprite can only appear on the screen once,
We need to allocate hardware sprites to each object... we use the 'SetHardwareSprites' function to do this
We have a new DoGetHspriteObj function - this will do the same as the old DoGetSpriteObj, however it uses the new hardware sprite code, rather than software sprites.

We use the same patterns as the tile data.

The top left visible screen position is (128,128)

We need to calculate the 'link' for each sprite... each sprite 'links' to the next, except the last, which links to sprite 0
Because hardware sprites are a separate 'layer' - we need to alter our Clear Screen Routine.

We need to go through each hardware sprite, and remove it from the screen.
To remove a sprite we can just change it's XY co-ordinate to 0,0 - this is completely offscreen, so the sprite won't be drawn.
The Genesis makes using hardware sprites easy!... we can use the same 8x8 patterns, and the sprites are simple to use.

The only tricky bit is that LINK parameter - if we don't set that up, we'll have no sprites visible!

Lesson YQuest11 - NeoGeo Hardware Sprites
Let's look at the NeoGeo... we'll use the Fix Layer to draw graphics to the screen.

We need to define our tile bitmaps in the XML file - as they are defined by ROM.

See YQuest 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

Hardware Sprites
On the NeoGeo Sprites are 16 pixels wide, and are made up of tiles 16 pixels tall... they are 16 colors (4 bitplanes) so each tile is 128 bytes... The NeoGeo is capable of up to 380 sprites total.

A sprite can be made up of up to 32 tiles to make the sprite taller... there is also a 'Chain' bit - this will connect a sprite to the previous sprite (Sharing its position) to make a sprite wider!

Hardware sprite Tiles are not in the same format as the FIX layer - they are 16x16 pixels in size and use bitplanes, but the layout is odd, and they are split onto two rom files (C1/C2 or C3/C4 etc)

The sprite is split into 2x 8 pixel wide 'columns' the rightmost one is stored first - each pair of bitplanes 0 & 1 are stored together in ROM C1 - and 2 & 3 are stores in ROM C2 - all 16 lines of the right half are stored first - then the 16 lines of the left side are stored - making a total of 128 bytes (64 in each file)

Sprite Attributes
Each sprite is defined by 1-32 tile definitions, A palette/flip byte, a Scale byte, a Ypos/Height byte and an Xpos... lets see what all these bits do in detail!
Note, it's possible to scale a sprite DOWN, but it's not possible to scale them up...

Address of
Bit

Function Sprite 1
F E D C B A 9 8
7 6 5 4 3 2 1 0 Purpose Sample
TileAddr 1,2..32 $0040,$0042�$007E
N N N N N N N N
N N N N N N N N  N=Tile Number L $2000
TilePal 1,2..32 $0041,$0043...$007F
P P P P P P P P
N N N N A A V H  P=Pallete, N=tile Number H, A=Animate (4/8) V=Vflip H=Hflip $0100
Shrink $8001
- - - - H H H H
V V V V V V V V  H=H shrink (F=off), V=V shrink (FF=off) $0FFF
Ypos $8201
Y Y Y Y Y Y Y Y
Y C T T T T T T  Y=Y pos (from bottom, so 248-Y) C=Chain another sprite on right, T=Tile count vertically $E002
Xpos $8401
X X X X X X X X
X - - - - - - -  X=X pos (from left) $0800

Valid Tile Counts (T) are 1-12 , 32 and 33 (13-31 are invalid)
A special Tile Height of 33 makes the sprite the Full height of screen - use for building a 'tilemap'


Sprite Attribute Addresses
We need to set various addresses in the Sprite Attribute data... to do this we use two 68000 addresses...

First we write the Address of the Sprite Attribute we want to change to $3C0000, then we write the new word value for the address to $3C0002

 Sprite Num
 TileAddr (1st)   TilePal (1st)   TileAddr (32nd)   TilePal (32nd)   Shrink     Ypos      Xpos   
0 $0000 $0001 $003E $003F $8000 $8200 $8400
1 $0040 $0041 $007E $007F $8001 $8201 $8401
2 $0080 $0081 $00BE $00BF $8002 $8202 $8402
3 $00C0 $00C1 $00FE $00FF $8003 $8203 $8403
 379  $5EC0 $5EC1 $5EC0 $5EC1 $817B $837B $857B

NeoGeo Hardware sprite code
Hardware sprites are defined in ROM - we need to set up our XML file so MAME knows where the files are, and where to map them in VRAM
Because each hardware sprite can only appear on the screen once,
We need to allocate hardware sprites to each object... we use the 'SetHardwareSprites' function to do this
We have a new DoGetHspriteObj function - this will do the same as the old DoGetSpriteObj, however it uses the new hardware sprite code, rather than software sprites.

We get the X,Y pos of the sprite from the object... The Ypos on the NeoGeo is reversed (from the bottom)  so we subtract our Ypos from 288+207

We use SetSprite to actually set the hardware sprite
SetSprite will set a hardware sprite of a single Tile (16x16)

We pass the Hardware sprite number in D0... the XY pos in D1,D2... the pattern in D3 and the palette in D4
Because hardware sprites are a separate 'layer' - we need to alter our Clear Screen Routine.

We need to go through each hardware sprite, and remove it from the screen.
To remove a sprite we can just change it's XY co-ordinate to 0,0 - this is completely offscreen, so the sprite won't be drawn.
The sprites on the neo-geo are 16x16, but our graphics are 8x8...

We can covert our 8x8 tiles into 16x16 ones using the 'SpriteSpace' function of AkuSprite Editor
Remember, a neogeo sprite can be more than 1 tile... and can be scaled.

Here' we just using a single unscaled tile16x16 tile... but the neogeo is capable of much more.