![]() |
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. |
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 |
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. |
![]() |
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 |
![]() |
Joystick Reading
Sprite Data and Palette
![]() |
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
|
![]() |
![]() |
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. |
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 |
![]() |
Joystick Reading
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
|
![]() |
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 |
![]() |
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 |
![]() |
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
Sprite Data and Chip ram
![]() |
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
|
![]() |
![]() |
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. |
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 |
![]() |
Joystick Reading
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 |
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 |
![]() |
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
|
![]() |
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. |
![]() |
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 |
![]() |
Joystick Reading
Sprite Data, Palette and screen ram
![]() |
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! |
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+
|
![]() |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. |
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--
Our virtual co-ordinates work at 128 units wide, meaning there's 4 possible positions for 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
|
![]() |
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 | 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 |
![]() |
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! |
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) |
![]() |
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'
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 |
![]() |
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. |