Learn Multi platform 68000 Assembly Programming... By Magic!

Platform Specific Lessons




Lesson P51 - Multiplatform Software tilemap on the Amiga via BLIT (Mintile)
We've written a minimal multiplatform Tile/Sprite routine in the Mintile series, Now, lets take a look at the platform specific code to quickly draw tiles to the screen on the Amiga using the blitter

AMI_MinTileBlit.asm


MinTile is a multiplatform 'engine' which allows us to define our game code in a common way, and let the platform specific code handle the platform specific work!... it was used to write 'ChibiFighter' on the Z80.

For more details on Mintile, see the Mintile series here...

The blitter isn't really efficient working at such small sizes, so the speed isn't very good, but this makes a nice example of using the blitter with bit shifted data.

Tile Drawing Routines

Our example shows an onscreen tilemap and two software 'sprites'.

Mintile supports scrolling and X-Flip (but not Y-flip)

The 'sprites' are actually miniature tile maps, this is to reduce the amount of platform specific code.

These are all created via the DoStrip platform specific routine, which we'll look at here.

This example writes to the screen in software.
We have a 'GetScreenPos' function , which calculates the VRAM destination for the sprite objects.
It calculates a VRAM destination in A6, from an X,Y position in D1,D4

Co-ordinates are in 'Logical Units' (Pairs of pixels) - this is to allow for single byte co-ordinates and maintain compatibility with the 8 bit systems

Each line of the screen is 40 bytes, but there are 4 bitplanes.
This routine calculates the bytepos in A6, but preserves the full xpos in d1
We need a 1 byte 'mask' for the 4 bitplanes of the 8 lines of our tile.

We'll bitshift this using the destination Xpos as a mask for our tile and the screen.
DoStrip will draw one horizontal strip of tiles.
The job is defined by the following registers:
A2=Source Tilemap
A5=Tile Bitmap Patterns
A6=Vram destination
D7=Width of strip

The tilemap will either be the TileCache (for the background) or a mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn.

We calculate the address of the source pattern data by multiplying the tile number by 64.
Actually the tiles are only 32 bit bytes, but they are 'padded' to 1 word wide, so we can do the bitshifts.
We need to set up the blitter, First we need to work out the bitshift, to allow per pixel movement.
We do this with the bottom two bits of D1, however these need to be shifted to the top 4 bits of BLTCON0/BLTCON1

We use the formula "$5A = !AC+A!C" to do the blitting operation A is the one tile mask, B is the source pattern C is the current screen data, D is the final screen destination.

we need to define the "Mods" that shift the sources after each line, to move to the next line.




We need to horizontally flip the bytes in the tiles, and we'll do this via a 256 byte Lookup Table.

For each of the 256 possible bytes, We calculate the opposite position of the 8 pixels of a bitplane
The blitter can't do the Xflip job for us, so we'll generate Xflipped patterns in advance.

TestChibikoRev will store the flipped tile data
We use our LUT to xflip the source pattern data.
DoStripRev has essentially the same function, however we use the X-flipped patterns, and move through the tilemap from right to left.







Lesson P52 - Multiplatform Software tilemap on the Atari ST(Mintile)
We've written a minimal multiplatform Tile/Sprite routine in the Mintile series, Now, lets take a look at the platform specific code to quickly draw tiles to the screen on the Atari ST

AST_MinTile.asm


MinTile is a multiplatform 'engine' which allows us to define our game code in a common way, and let the platform specific code handle the platform specific work!... it was used to write 'ChibiFighter' on the Z80.

For more details on Mintile, see the Mintile series here...

Tile Drawing Routines

Our example shows an onscreen tilemap and two software 'sprites'.

Mintile supports scrolling and X-Flip (but not Y-flip)

The 'sprites' are actually miniature tile maps, this is to reduce the amount of platform specific code.

These are all created via the DoStrip platform specific routine, which we'll look at here.

This example writes to the screen in software.
We have a 'GetScreenPos' function , which calculates the VRAM destination for the sprite objects.
It calculates a VRAM destination in A6, from an X,Y position in D1,D4

Co-ordinates are in 'Logical Units' (Pairs of pixels) - this is to allow for single byte co-ordinates and maintain compatibility with the 8 bit systems

Each line of the screen is 40 bytes, but there are 4 bitplanes, these are grouped in clusters of 4 words, with one word per bitplane in the format: $0000111122223333
Because of this, we need a 'byte' offset to calculate the position within the word, depending on if our tile is odd or even

DoStrip will draw one horizontal strip of tiles.

The job is defined by the following registers:

A2=Source Tilemap
A5=Tile Bitmap Patterns
A6=Vram destination
D7=Width of strip

The tilemap will either be the TileCache (for the background) or a mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn.

We calculate the address of the source pattern data by multiplying the tile number by 32

There are 4 bitplanes for each tile, spaced 2 bytes apart.
After each line we add 160 to move down a line. we repeat 8 times

We need to move across a tile, but due to the memory layout, depending on if we're on an even tile, or an odd one we'll need to shift one byte, or 7

After a tile we repeat for all the tiles on the line (the count is in D7)
We need to horizontally flip the bytes in the tiles, and we'll do this via a 256 byte Lookup Table.

For each of the 256 possible bytes, We calculate the opposite position of the 8 pixels of a bitplane
DoStripRev has essentially the same function, however we flip the pixels via the LUT

We load A1 with the address of the lookup table, we then read in a byte into D0, and use the source byte as an offset in the lookup table. Reading from this resulting address gets the X-flipped equivalent byte, which we write to the screen.

We write 4 times per line and for all 8 lines






Lesson P53 - Multiplatform Software tilemap on the NeoGeo via the FIX Layer (mintile)
We've written a minimal multiplatform Tile/Sprite routine in the Mintile series, Now, lets take a look at the platform specific code to quickly draw tiles to the screen on the NeoGeo using the Fix layer

NEO_MinTile.asm


MinTile is a multiplatform 'engine' which allows us to define our game code in a common way, and let the platform specific code handle the platform specific work!... it was used to write 'ChibiFighter' on the Z80.

For more details on Mintile, see the Mintile series here...

The FIX layer is really only designed for the GUI, but it works like a normal 8x8 pixel tile tilemap, so is an easy way to get graphics to the screen.

Tile Drawing Routines

Our example shows an onscreen tilemap and two software 'sprites'.

Mintile supports scrolling and X-Flip (but not Y-flip)

The 'sprites' are actually miniature tile maps, this is to reduce the amount of platform specific code.

These are all created via the DoStrip platform specific routine, which we'll look at here.

This example writes to the screen using the Fix layer.
We have a 'GetScreenPos' function , which calculates the VRAM destination for the sprite objects.
It calculates a VRAM destination in A6, from an X,Y position in D1,D4

Co-ordinates are in 'Logical Units' (Pairs of pixels) - this is to allow for single byte co-ordinates and maintain compatibility with the 8 bit systems

The Fixlayer goes down the screen, then across, so we multiply the Xpos by 32, and add the Ypos to the base address of the Fix layer ($7000)
DoStrip will draw one horizontal strip of tiles.
The job is defined by the following registers:
A2=Source Tilemap
A5=Tile Bitmap Patterns
A6=Vram destination
D7=Width of strip

The tilemap will either be the TileCache (for the background) or a mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn.

We add A5 to the tile number - by default this is $1800+768 - to point to the first FIX tile for the background,palette 1

To move across the screen, we add 32 to the destination address (as each column has 32 tiles)


we repeat until the line is done.


DoStripRev will draw the X-flipped character, but the FIX layer can't perform the Xflip operation, so we have an alternative set of Xflipped patterns we use for the job.


DoStripRev will use the Xflipped patterns, and walk through the source A2 tilemap in reverse
We need to define the address of the FIX layer data at the appropriate memory address in NeoGeo.xml




Lesson P54 - Multiplatform Software tilemap on the NeoGeo via Hardware Sprites (MinTile)
We've written a minimal multiplatform Tile/Sprite routine in the Mintile series, Now, lets take a look at the platform specific code to quickly draw tiles to the screen on the NeoGeo using Hardware Sprites

NEO_MinTile_Hsprite.asm


MinTile is a multiplatform 'engine' which allows us to define our game code in a common way, and let the platform specific code handle the platform specific work!... it was used to write 'ChibiFighter' on the Z80.

For more details on Mintile, see the Mintile series here...


The NeoGeo had no tilemap, but it has 'chainable' hardware sprites, that can be combined to form a tilemap.
That's what we'll do this time! The sprites are 16x16, but we'll scale them to 50% to simulate an 8x8 tilemap.

Tile Drawing Routines

Our example shows an onscreen tilemap and two software 'sprites'.

Mintile supports scrolling and X-Flip (but not Y-flip)

The 'sprites' are actually miniature tile maps, this is to reduce the amount of platform specific code.
These are all created via the DoStrip platform specific routine, which we'll look at here.

The sprites are scaled to 50% for the background, so we need to double the size of our tile patterns to get them the right size (which is why they appear small in this example)
Before we can use the 'Tilemap' We have to build it out of sprites!
There are a variety of addresses which define each of the sprite.

Address of
Bit

Function Sprite 1
F E D C B A 9 8
7 6 5 4 3 2 1 0 Purpose Sample Value
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 288+208-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
The first sprite is the 'Anchor' to which all the others acre connected, We need to set all the sprite parameters for the object.
The Sprite is 32 tiles tall, with a scale of $77F (50% - 8x8 pixels)

We build all the tiles in the column, all of them point to tile $2200 by default (a blank tile), and palette 1

Our tilemap is 32x24, each sprite draws a full column, so we've used 32 of the available sprites!
DoStrip will draw one horizontal strip of tiles. it's intended for the background tiles.

The job is defined by the following registers:
A2=Source Tilemap
A5=Tile Bitmap Patterns
A6=Vram destination
D7=Width of strip

The tilemap will either be the TileCache (for the background) or a mini tilemap (for sprites)
If using the tile cache we zero the tiles after they are drawn.

We set the matching sprite tile for our fake tile map to make it visible in the desired position.


DoStripSprite will draw one horizontal strip of tiles for the movable characters

The job is defined by the following registers:
A2=Source Tilemap
A5=Tile Bitmap Patterns
D1,D4= X,Y pos
D7=Width of strip

We use HspriteNum to track the next free sprite number.

We position the hardware sprite, and set the tile number (Vram Address $0000+), Palette ($0001+), Scale ($8000+), Xpos ($8200+) and Ypos ($8400+)

Due to the way MinTile works, it's not really possible to chain tiles for the vertical height, so we use more hardware sprites for each column.
DoStripRev functions essentially the same, however it sets the 'H' bit of $0001+ to Xflip the patterns, and moves through the tilemap from right to left.
We need to define the address of the Sprite pattern data at the appropriate memory address in NeoGeo.xml




Lesson P55 - Interrupt Driven Music on the X68000 (ChibiTracks)
Lets upgrade our previous example with sound via ChibiTracks, we'll use the VBlank interrupt handlers to play some music

x68_MinTile_WithSound.asm



The Interrupt Handler - Vblanks
We're going to use VBlank to control the speed of our music.

Vblank and many other interrupts are generated by the MFT. The interrupts can be enabled with $E88007/9, however they are also 'masked' with $E88013/5 (a bit 1 in both enables an interrupt!)

Interrupt 6 - Gpip4 is the Vblank (referred to as V-Disp) is controlled by bit 7 of $E88009 and $E88015 - This causes an execution of vector $46 at address $000118


We need to load the address of our interrupt handler to $000118


We don't need to, but actually we can change this address!... The default vector base for the interrupts is vector $40 at address $000100 - this can be changed with the top 4 bits of $e88017
We need to protect all our register, as ChibiTracks will use them.

We return from our interrupt handler with RTE

ChibiTracks
To use ChibiTracks, we need to include the modules of the music player,

We also need the platform specific "Chibisound Pro" sound driver.


We also need to have a ChibiTracks music file to play.
We need to allocate up to 256 bytes of memory for ChibiSoundPro, and ChibiTracks player.

We define pointers to the ChibiTracks variables within this ram.
Before we play anything we need to init the ChibiSoundPro Driver.

To start our song, we set SongBase to point to the music file we want to play, and call StartSong - we do this any time we want the music to change

If we want to change the speed, we can load a new value into SongSpeed at any time.
All that's left is to execute the PLAY routine to update the playing music.




Lesson P56 - Interrupt Driven Music on the Genesis (ChibiTracks)
Lets upgrade our previous example with sound via ChibiTracks, we'll use the VBlank interrupt handlers to play some music

GEN_MinTile_Sprite_WithSound.asm



The Interrupt Handler - Vblanks
The Vblank (Vsync) interrupt will cause an execution of the IRQ6 vector at $00000078

First we need to put the address of our interrupt handler at this address.



We still need to do a bit more though!...
Next we need to set the interrupt level in the SR register.
This is controlled by bits 8-10.
We need to set a value of 6 or lower, to allow our interrupt to occur.

Next we need to set bit 5 of VDP register 1 to one, this will enable Vblank on the video hardware
We need to protect all our register, as ChibiTracks will use them.

We return from our interrupt handler with RTE

ChibiTracks
To use ChibiTracks, we need to include the modules of the music player,

We also need the platform specific "Chibisound Pro" sound driver.


We also need to have a ChibiTracks music file to play.
We need to allocate up to 256 bytes of memory for ChibiSoundPro, and ChibiTracks player.

We define pointers to the ChibiTracks variables within this ram.
Before we play anything we need to init the ChibiSoundPro Driver.

To start our song, we set SongBase to point to the music file we want to play, and call StartSong - we do this any time we want the music to change

If we want to change the speed, we can load a new value into SongSpeed at any time.
All that's left is to execute the PLAY routine to update the playing music.




Lesson P57 - Interrupt Driven Music on the NeoGeo (ChibiTracks)
Lets upgrade our previous example with sound via ChibiTracks, we'll use the VBlank interrupt handlers to play some music

NEO_MinTile_Hsprite_WithSound.asm



The Interrupt Handler - Vblanks
The Vblank (Vsync) interrupt will cause an execution of the IRQ0 vector at $00000064


First we need to put the address of our interrupt handler at this address.
Next we need to set the interrupt level in the SR register.
This is controlled by bits 8-10.
We need to set a value of 0, to allow our interrupt to occur, this will also enable the other interrupts.
Our interrupt handler needs to check if our game is running, we do this with bit 7 of $10FD80 - A value of 1 means our game is running, otherwise we run the system interrupt handler at $10FD80

Next we need to do some system tasks, we clear the interrupt with bit 2 of $3C000C.
We kick the watchdog at $3C000C - this tells the system we haven't crashed (otherwise it auto reboots)
We run to system routines to do various jobs, $C0044A handles IO,  $C004CE  shows any system messages.

We can now worry about updating our music!
We need to protect all our register, as ChibiTracks will use them.

We return from our interrupt handler with RTE

ChibiTracks
To use ChibiTracks, we need to include the modules of the music player,

We also need the platform specific "Chibisound Pro" sound driver.

We also need to have a ChibiTracks music file to play.
We need to allocate up to 256 bytes of memory for ChibiSoundPro, and ChibiTracks player.

We define pointers to the ChibiTracks variables within this ram.
Before we play anything we need to init the ChibiSoundPro Driver.

To start our song, we set SongBase to point to the music file we want to play, and call StartSong - we do this any time we want the music to change

If we want to change the speed, we can load a new value into SongSpeed at any time.
All that's left is to execute the PLAY routine to update the playing music.





Lesson P58 - Interrupt Driven Music on the Atari ST (ChibiTracks)
Lets upgrade our previous example with sound via ChibiTracks, we'll use the VBlank interrupt handlers to play some music

AST_MinTile_Withsound.asm




We're going to 'Play nice' with the OS for a change!

The OS will do most of the VBlank work for us, we'll just tag onto it's job list, and update our music once a frame.

The Interrupt Handler - Vblanks
The system executes a series of interrupt handlers during Vblank, These are defined in a list, with the lists pointer  at address $000456. The length of the list is at address $000454 (8 by default)

We want to register the address of interrupt handler in this list, so we need to look for a zero entry ($00000000) if we can find one we put our interrupt handler in it's slot.

ChibiTracks uses all our registers, but the OS is protecting them for us.

We return from our interrupt handler with RTS, the other defined Vblanks interrupt handlers will (or did) execute.

ChibiTracks
To use ChibiTracks, we need to include the modules of the music player,

We also need the platform specific "Chibisound Pro" sound driver.

We also need to have a ChibiTracks music file to play.
We need to allocate up to 256 bytes of memory for ChibiSoundPro, and ChibiTracks player.

We define pointers to the ChibiTracks variables within this ram.
Before we play anything we need to init the ChibiSoundPro Driver.

To start our song, we set SongBase to point to the music file we want to play, and call StartSong - we do this any time we want the music to change

If we want to change the speed, we can load a new value into SongSpeed at any time.
All that's left is to execute the PLAY routine to update the playing music.




Lesson P59 - Interrupt Driven Music on the Amiga (ChibiTracks)
Lets upgrade our previous example with sound via ChibiTracks, we'll use the VBlank interrupt handlers to play some music

AMI_MinTileBlit_WithSound.asm




We're going to 'Play nice' with the OS for a change!

The OS will do most of the VBlank work for us, we'll just tag onto it's job list, and update our music once a frame.

The Interrupt Handler - Vblanks
We need to get the operating system to define the interrupt, we load a pointer to the OS into A6 from address $4

We need to define a structure with the information about the interrupt, we load it into A1

We select the interrupt type with D0, we want interrupt 5 (VERTB - vblank)

we now execute function 'Exec - AddIntServer' at address -168
Here is the structure that defines the interrupt in A1. The first few bytes should be left unchanged.

LN_NAME points to a zero terminated string giving the interrupt a name - it can be anything really.

IS_DATA can point to a data structure for the interrupts parameters, it's address is loaded into A1. This will be handy if you're using multiple instances of the same interrupt handler, but we don't really need it.

IS_CODE points to the actual subroutine which will be executed.
ChibiTracks uses all our registers, the calling routine will deal with D0 and D1 for us though, so we don't Push and Pop those.
A0 must retain the value $DFF000 for Vblank interrupts - We're backing up all Address registers so that is fine!

We've added a little test of IS_DATA, by adding 1 to A1 (MyData)

To tell the OS to pass control to other interrupts we set the Zero flag and return with RTS

ChibiTracks
To use ChibiTracks, we need to include the modules of the music player,

We also need the platform specific "Chibisound Pro" sound driver.

We also need to have a ChibiTracks music file to play.
We need to allocate up to 256 bytes of memory for ChibiSoundPro, and ChibiTracks player.

We define pointers to the ChibiTracks variables within this ram.
Before we play anything we need to init the ChibiSoundPro Driver.

To start our song, we set SongBase to point to the music file we want to play, and call StartSong - we do this any time we want the music to change

If we want to change the speed, we can load a new value into SongSpeed at any time.
All that's left is to execute the PLAY routine to update the playing music.




Lesson P60 - Interrupt Driven Music on the QL (ChibiTracks)
Lets upgrade our previous example with sound via ChibiTracks, we'll use the VBlank interrupt handlers to play some music

SQL_MintileWithSound.asm



The Interrupt Handler - Vblanks
We're going to take over the vblank interrupt handler of the operating system!

The OS passes control to the interrupt list at address +$3C in the SysVars - Usually at address $2803C

We can check the actual address of SysVars by executing Trap #1 function #0 (MT_INF)... this returns the base of SysVars in A0

We load the address of our InterruptList into +$3C (not the interrupt itself)
The Structure of the interrupt list is a linked list which takes 2 Longs.

The 1st long points to the next list, or $00000000 if this is the end of the list.
the 2nd long points to the address of our interrupt handler.

We use the interrupt to play our music, and return with RTS.

The OS is preserving all the registers for us, so we don't PUSH or POP any.

Finally we need to set the interrupt level to 0 in the SR register to start interrupts.


ChibiTracks
To use ChibiTracks, we need to include the modules of the music player,

We also need the platform specific "Chibisound Pro" sound driver.

We also need to have a ChibiTracks music file to play.
We need to allocate up to 256 bytes of memory for ChibiSoundPro, and ChibiTracks player.

We define pointers to the ChibiTracks variables within this ram.
Before we play anything we need to init the ChibiSoundPro Driver.

To start our song, we set SongBase to point to the music file we want to play, and call StartSong - we do this any time we want the music to change

If we want to change the speed, we can load a new value into SongSpeed at any time.
All that's left is to execute the PLAY routine to update the playing music.