Simple sample code on a variety of Z80 machines
<- Back to the Main Contents & Basic Z80 Assembly Lessons

Introduction to the Simple Series...
Continuing from the 'hello world' series, we're going to create some super basic examples to get you started! Each will be a single ASM file (where possible) and we'll look at the basics you'll need to make a game with as little code as possible, Lets make a start!

Lesson S1 - Easy Sprites on the CPC
In this example we're going to learn how to get a bitmap sprite onto the screen... first we'll do a 8x8 'tile'... then we'll extend the example to a much larger sprite, so you can modify the code for any sprite size you wish!

See SimpleBitmap folder

Beginning a CPC program
Load up WinApe , Select  Show Assembler from the Assembler menu
We need to define a start for our program in memory... BASIC and the firmware need the first chunk, so memory address &1200 is a good starting point

to start your program at &1200 add the command ORG &1200

Drawing an 8x8 sprite

First we want to get the memory address of the screen we want to draw to...

We need to specify the co-ordinates of the pixel we want...
We specify the X position in DE (from the left of the screen)
We specify the Y position in HL (from the bottom of the screen)

Then we call &BC1D (SCR DOT POSITION)... this will return the screen byte position in HL
We need some bitmap data... In Mode 1 each pixel is 2 bits - so 2 bytes are each line...

a bit set in just the left hand nibble of each byte defines color 1,
a bit in the right hand sets color 2... a bit in both sides sets color 3
As we have the destination in HL, We're going to store the source address of the bitmap in DE

We'll load the line count in B - our bitmap is 8 lines
We're going to draw a line...

first we need to back up HL into the stack with PUSH... we need it in a moment..

Next we load a byte from address DE... and write it to HL... and increase HL and DE...

We do this twice for the two bytes of the line
now we need HL from before... we want to move down a screen line line
We call &BC26 to do this (SCR NEXT LINE)... this updates HL for our next line...

We decrease B and repeat the line routine until our tile is drawn, then we return!
To run this test, type CALL &1200 in basic

The resulting sprite is shown to the screen!
We could do the same for 16 color sprites in mode 0 - which uses 4 bits per pixel, but the byte layout is a lot more confusing!

  We can load A with 0 and CALL &BD1C (MC SET MODE) to set screen mode 0

Extending the example for larger sprites.

With just a few changes we can make our previous code work with larger sprites (of any size)... we're going to show a 48x48 sprite

We're going to use C as a 'Line Bytecount' and repeat the byte copy routine several times per line until C reaches Zero.

We'll import the byte data from a file using INCBIN
The sprite will be shown on screen!
How did we create the sprite? Well, we can use my AkuSprite Editor!

This can export a bitmap to the correct byte format for the screen of all the systems covered in my tutorials!

The 'Save Raw Bitmap' option in the CPC / FILE menu will create the data file used in today's tutorial.

The program also supports 16 color Mode 0 sprites!
Our bitmap is on the screen... but the colors of our chibiko character are wrong

Maybe we can fix that with firmware function &BC32 (SCR SET INK)... but you'll have to figure that out yourself!


Lesson S2 - Easy Sprites on the ZX Spectrum
We Looked last time at the CPC, a 2 bpp system, The speccy is just 1bpp - so each pixel is one bit, but we have the added problem of the color map...

The Bitmap data is at &4000 - the colors are at &5800

Lets learn how to draw sprites on the speccy screen!

See SimpleBitmap folder

Calculating Screen position for bitmaps and colors

We're going to need to be able to calculate the screen ram position, for two purposes...

First we'll do the more complex screen co-ordinates... The screen layout is weird... the final HL address is made up of the bits in the following format:
0 1 0 Y7 Y6 Y2 Y1 Y0     Y5 Y4 Y3 X4 X3 X2 X1 X0

The X bits are all at the bottom - so moving across the screen is easy, but moving down is a pain - so long as we're in an 8 pixel tall strip (effectively a character block) we can just INC D, but beyond that we have to do some work!... the top 3 bits are always %010 - because the screen base is &4000

We need a version call ed GetScreenPos... which coverts (X,Y) position (B,C) to a DE memory address - we'll use this to calculate the screen memory position of a byte we want to write...

We specify the X position in bytes... and the Y position in lines

Once we've written one line, we need to move down the screen, If we're aligned to an 8x8 block, we can just INC D... but otherwise we'll need something more complex, we've got a 'GetNextLine' function to do this for us.



Color data is simpler... the data is 'linear'... but there is only 1 byte for each 8x8 square...

If we work out which 8x8 block we're in (by ignoring the low 3 bits of the Y co-ordinate) we just need to multiply that Y-block number by 32... we do this by bit-shifting

If we want to move down a line, we just add 32 to DE


Drawing an 8x8 sprite

Ok, we're going to draw a simple sprite... because our sprite is a single 'square', we just need to color one block.

Our program starts at address &8000 - this is where our loader basic file will run

First we set B and C with the X,Y Co-ordinate of the sprite destination... then we use GetColMemPos to set DE to the destination...

We write %00000011 to set the block to magenta!
We need some bitmap data!... we're going to define a smiley face for a sprite..

The sprite is 8x8 - and each byte stores 8 pixels... so the whole 8x8 sprite is just 8 bytes
Lets draw the pixel data!
We use the GetScreenPos to calculate the location in RAM... and then load HL with the sprite source...

We need to copy just one byte for each line, using GetNextLine after each one... we use B as a line counter, so we repeat the procedure 8 times.
The result can be seen here!
The example above is very simple, so the color drawing part is just a single 1 byte write... it will be more complex if we're doing a bigger area!

Drawing a Larger sprite

This time we're going to use a larger sprite - 48x48 pixels...

When it comes to the color are, this equates to a 6x6 area...

We use C as the Y counter... and B as the X counter...

We back up the destination with a PUSH/POP DE so we can calculate a line down with GetNextColLine.
We now need to draw our bitmap data, we use GetSpritePos to set DE to the screen memory location

We set HL to the source bitmap...

We're going to use IXH as the line count - our bitmap is 48 lines tall...

We're setting BC to 6 - the width of the bitmap in bytes... why BC rather than just B? we're going to use LDIR - which will copy BC bytes from the source HL to the destination DE

Once we've done a line, we restore the start position by POPping DE... and use GetNextLine... we then decrease our IXH counter and repeat
The result can be seen here.
If you want to covert bitmap data, you can use my AkuSprite Editor... it supports all the systems in these tutorials...

The 'SaveRawBitmap' option on the ZX menu will save a black and white bitmaps.
The example above filled the entire sprite a single color - if we wanted to set the 36 color blocks to different colors, we'd want a second 'colormap' bitmap for the sprite which would color it in.


Lesson S3 - Easy Sprites on the Enterprise
Lets learn how to draw some simple 4 color sprites on the Enterprise!... the Enterprise screen is similar in byte format to the CPC, but the line order is easier... unfortunately, setting up the screen is not!

Let's learn how to do it!

See SimpleBitmap folder

Starting our Program

Lets start our Enterprise program!

First we need a heade to start our program, and set up a valid stack pointer... next we want to initialize our graphics screen!
Uhh, this is complex - so we're not going to go in detail!

Effectively, we need to get a valid 16k ram bank from the OS... we keep requesting ram until we find one, and set it up as the screen when we do.

This code was taken from a sample from the Enterprise Forever forum... fortunately we don't really need to change it - so we can just run it and trust it to work!
The function aboves uses a RequestVidBank function to get the ram banks...

We don't need to worry about how this works,  it'll just do the job for us!
Ok, this is a bit we may want to change...

The Enterprise screen uses a 'LPT' table - this defines the size of the screen... our default screen is 320x200 - and we probably want to leave it that way, but we may want to change the color depth and the colors...

There are 8 definable colors - in 4 color mode half are unused - in 16 color mode, only 8 can be defined - the other 8 are the same with a 'Color Bias'... but that's outside of what we're going to cover today!
The Enterprise screen doesn't HAVE to be 320x200 - we can make it bigger by changing the borders and margins, we can even change the memory layout to make the screen work exactly like a CPC screen...

We're not going to look at that here though, as the Enterprise screen layout is more logical than the CPC one, and a bigger screen would use more than 16k of our memory

Calculating Screen position by X,Y co-ordinate

When we want to draw some data to the screen, we need to calculate the memory address of any pixel we want to change.

The formula for the enterprise is really simple...

Memory address = &C000 + ( Ypos * 80) + Xpos

&50 (80 in hex) is %01010000... we can effect a multiply by shifting the Y position to each of the '1' bits, and adding the two together

We just then add the base &C000 and the Xpos... and HL will contain the final address...

Don't worry if you don't understand this... you can just use this code as is!
Moving down a line on the Enterprise is far easier than systems like the Speccy and the CPC...

Because each line is directly below the previous, we just add &80 to L

If L goes over 255, we need to INC H as well

Drawing an 8x8 sprite

We're going to need to define a sample sprite... here's a smiley face!

Each 8 pixel line is 2 bytes... so 16 bytes in total...

The format is a little odd... each byte is split into 2 nibble 'bitplanes'.... %11110000 is color 1... %00001111 is color 2... %11111111 is color 3... there are no prizes for guessing color 0's bit definition!
Ok, we need to draw our sprite!

We set BC to the XY position we want to draw our sprite... this sets HL to the video memory address...

We load DE with the sprite data source address... we also load B with the number of Y lines

We back up HL for later... then we read two bytes from DE and write them to HL... increasing DE and HL

Once we've done a line, we restore DE, and use GetNextLine to move down the screen...

We then decrease B and repeat.
The resulting sprite can be seen!
The example above filled the entire sprite a single color - if we wanted to set the 36 color blocks to different colors, we'd want a second 'colormap' bitmap for the sprite which would color it in.

Drawing a bigger sprite

What if we want a bigger sprite? Lets use a 48x48 sprite

In this example we'll set B to 48 - as our sprite is 48 lines tall...

This time we'll need a loop for drawing the bytes horizontal line - we'll use C for this... 48 pixels is 6 bytes... so we set C to 6
We've drawn the Chibiko character to screen!

Of course this same code can do any size of sprite, we just need to change the B and C values.
You can export a valid Enterprise sprite using my AkuSprite Editor - it's free and open source, and is included in the sources.7z file.
16 Color mode on the Enterprise is exactly the same.... it's half the resolution, and the bit layout of each byte is different (it's the same as the CPC), but the procedure to copy to the screen is exactly the same,

Akusprite Editor can also output the correct format for 16 color Enterprise modes!

Lesson S4 - Easy Sprites on the Sam Coupe
The Sam Coupe's screen is pretty easy to use, as it's just 24k of basic system memory... unfortunately it uses up almost half the address space, and we need to page out the rom to use it...

Lets find out how to do it!

See SimpleBitmap folder

Getting started

Ok, Let's get started!...

First we need our program to start somewhere... We're going to start at &8000... we need to start somewhere other than &0000-&7FFF, as we'll page that ram out and in for our video.

Ok, let's prep a stack pointer... again it's got to be above &BFFF for safety, so we'll load it to &BFFF
Right! lets get things set up.

Let's turn off interrupts with a DI... interrupts will typically call &0038, and we want to swap out the &0000-&7FFF memory area with screen memory.

We need to define a pair of banks of ram for our screen... we'll use banks 14 and 15... the last two banks of a 256k machine (the minimum spec for the SAM)... we do this with port 252

We need to configure the low area... by default it's ROM - we need to turn of that and set it as RAM with bit 5... we also need to set the low ram bank to set that banks 14 & 15 in the range of &0000-&7FFF

We're now ready to do some graphics coding!

Calculating Screen position by X,Y co-ordinate

We're going to need to be able to turn a X,Y position into a memory address, so we know where to write to to set data... We'll Write 'GetScreenPos' it'll take a XY position and return an address in DE

Because our screen is 256 pixels wide, and each byte is 2 pixels, To calculate a memory location we can use the formula:
Addr= Ypos * 128 + Xpos

Because we have no multiply command, we effect this by loading the high byte (D) with the Ypos, shift one bit into the low byte (E bit 7 - 'worth' 128)... we then add the Xpos - simple really!

Because our screen base is actually going to be at address &0000 we have nothing else to do!
We'll need to move down a line after each draw, but that's easy too... again, we just add 128 bytes...

If we overflow the low E register we have to INC the D register

Drawing an 8x8 sprite

Ok, we need some sprite data... the SAM screen is easy... each nibble is a pixel, so we just need to specify the bytes to make our 'smiley face'
Ok, we need to do some copying!

we're going to load BC with the X,Y location to draw the sprite... we use GetScreenPos to calculate an address (in DE) for the destination...

We load HL with the source address of the sprite... and we load IXL with a 'line counter'... the sprite has 8 lines...

Now we use LDIR to copy from HL to DE... each line is 4 bytes, so we set BC to 4

When we need to move down a line, we use PUSH/POP to back up the old DE, and use GetNextLine to calculate the new line address

we repeat until IXL is zero, drawing each line.
Here is the smiley result!
When we're done, we may want to turn the firmware rom back on, and reset the memory to the default, we do this with another write to port 250...

Though in this case we'll actually just HALT the cpu

Drawing a larger sprite

It's pretty easy to modify this code for a bigger sprite, just change IXL and BC...

In this case we'll use a 48x48 sprite...
And here's the result!
Working with the SAM is both easy and hard... getting data to the screen is easier than systems like the MSX, just write to memory, but we have to page this huge 24k screen into ram, so we may need to 'plan' our game around that ... For example, if you need interrupts, then rather than using IM1 (which uses &0038 address), then why not use IM2?

Lesson S5 - Easy Sprites on the MSX2
Unlike the systems we've looked at so far, the MSX2 video memory is separate from the normal ram... in the 'VDP'

We'll have to send commands to the VDP to draw pixels to the screen, and copy sprites from one area to another,
Lets figure it out!

See SimpleBitmap folder

Setting up our screen

In this example we'll make a cartridge ROM... We need to start our rom file with a header...

(If you want to know the details, see the HelloWorld example)
We're going to need access to some VDP ports...
To make our code clearer, we'll define 4 ports as symbols...

We'll use the OUT command to access these!
First we need to get our graphics screen ready...

We're going to define a set of bytes for the control registers... they're at VDPScreenInitData,

Each is a pair, the first is a value... the second (with +128) is the register which will take that value...

We're going to use the OTIR command to copy all this data to the 'VdpOut_Control'


The graphics mode will be enabled.
The screen will turn to a graphics mode... But, Hey! there's some weird corruption!

Don't worry, that's just some old junk on the screen from the previous text mode, the mode change has worked fine!

Getting a Bitmap from Z80 RAM to the VDP screen (VRAM)

We've got a sprite in our ROM cartridge (Z80 Ram)... but we need to get it to the screen VRAM...

In the 16 color mode, the visible screen is 256x192, but the total screen is 256x1024... all lines below 192 are hidden, but if we copy sprites there to the invisible area, we can get the VDP to fast copy them later to the visible area.

Either way, the first stage is to copy from the ROM to VRAM...
Ok, this is going to get tricky, but we'll go through it!

We need to write a series of settings and commands to the VDP via OUTS, but to make it easy, we'll use a function called 'VDP_HMMC_Generated_ViaStack'

We're going to need to load the first byte of our sprite into the Accumulator
IX will be the destination Xpos in pixels
IY will be the destination Ypos in pixels
HL is the width of the bitmap in pixels
DE is the height of the bitmap in pixels
We're going to use lots of OUTI's (Bytes &ED,A3) to send data to the VDP port 'VdpOut_Control'... but we need the bytes to be in ram somewhere, with HL pointing to the first byte for OUTI to work...

We'll achieve this by pushing all the values onto the stack (in ram), and use this with the OUTI... we need to use a command called 'HMMC' to do the CPU->VRAM transfer - this is command &F0

We need to set up the 'Indirect write' register - and tell it we want to start writing from 'Register 36' (DX - Dest Xpos)... you don't need to know the register numbers, but if you want to, they're all on the MSX page

Sendign the data is done with 11 OUTI's .... we represent these as bytecode to make it neater

Once this is all done, we'll have set up the screen area for the bitmap, and written the first pixel, now we just need to send the rest!
We've only sent one byte so far (the one in A)... so we need to send the other pixels!, but now we can just write them to the 'VDP_SendByteData' port, and the VDP will fill the square we defined in the last stage!
Our smiley has been printed on the screen.
We've got our bitmap to the screen!
This is a great way of transferring sprites from CPU Ram to VRAM, or for generating graphics from calculations... but it's too slow for sprites in a game...
We need to use the HMMM command for sprites, copying VRAM to VRAM!

Fast copying from VRAM to VRAM

Ok, we learned how to get data from RAM to VRAM, but that was pretty slow, once our sprite is somewhere in VRAM, we want to be able to copy it faster...

Like last time, we're going to store the settings in our registers... this time we're going to use the shadow registers too, as we don't have enough otherwise!

We use a different command this time HMMM! (Fast copy)
When we're doing fast commands, we need to check if the VDP isn't busy doing something else, we do this with 'VDP_FirmwareSafeWait'....

Why is it 'Firmware Safe'? well, the firmware interrupt handler needs register 0 to be selected, but we need to select register 2 to see if the VDP is busy, so we need to disable interrupts while we're working to be 'safe' to work with the firmware.
The next stage is pretty much the same as last time, but this time we're using command &D0

We need to check if the VDP is still busy with that VDP_FirmwareSafeWait command,

we also need to set up more registers, this time we're starting from Register 32 (SX: StartX)... and writing 15 bytes.
We've copied the first sprite quickly, and made a second smiley!
Remember... the VRAM screen is 256x1024, if we store our sprites in a 'grid' offscreen, we can copy those sprites into the visible screen as we need them!

This is how ChibiAkumas worked on the MSX2 - lines 512+ were the level and character sprites (and font)... the first 512 were the two screen buffers (Screen buffer 2 must start at line 256)

Working with larger sprites

The code can really works with any sprite size, all we need to do is change the Width and Height in the VDP commands...

In this case we're using a 48x48 sprite
You can see the results here.
This command is super fast and totally kicks ass!
Because the VDP has it's own CPU, a large sprite will still be drawing while our CPU gets on with other work... this is why we need to check if the VDP is busy or not before sending another command.

Lesson S6 - Easy Tiles on the MSX1
The MSX1 is graphically more simple... similar to a Spectrum (but with better color) each pixel is either the foreground or background color...

While the system is capable of hardware sprites, they may be a bit complex for your first game, so instead we'll look at drawing pictures with the tilemap!... Lets learn how!

See SimpleBitmap folder

Setting up our screen

We're going to create a cartridge... first we need to define a header to do this.

See the Hello World example for more details.
We need access to the VDP (graphics) hardware... we will OUT these when we want to change hardware registers or write data.
OK, we're ready to start our program!

First we need to set up our screen, we use an OTIR command, this will copy a bank of settings to the VDP...

Each is a pair, the first is the new value for the register - the second is the register number (plus 128)... this will set up the tilemap screen this tutorial needs.

We're ready to start our example!


We can't set 'pixels' directly on the screen like the spectrum, instead we define our image as a 'tile'... then define an 8x8 block of the screen to show that tile...

The disadvantage is that this makes setting a pixel harder (so a 3d game vector like 'star wars' would be tricky).. but we can change the entire screen by altering 768 bytes (32x24 tilemap) so we can 'scroll' the screen quickly!

Showing an 8x8 tile to screen

We're going to need to define a function to select a VRAM memory address to write to, it's pretty simple...

To select the VRAM address VdpOut_Data will write to, we first write the low address (L) to the  VdpOut_Control port...  then we write the high address (H)...

But we need to set bit 6 of the H address... this tells the VDP we want to WRITE to vram (if we didn't we'd read)...

Finally we set C to VdpOut_Data... this will help us later!
Ok, we're going to have to define our graphic (a smiley) as a TILE...

The tile has two parts, the first is a 1bpp bitmap... we need to copy this to &0000+ (the bmp data address)... we're going to use tile 128 (to make space for a font)... and as each tile is 8 bytes tall, we load to memory &0000+128*8

We use OTIR to copy B bytes of data from HL to C (remember we set C to VdpOut_Data) with the 'SetWriteAddress' Command


As well as 8 bytes of bitmap data, each tile also has 8 bytes of color data (one byte per line)... this data starts at address &2000

This data is in a simple format, the top nibble is the foreground color, the bottom is the background color.

The colors are shown below:
   0       1       2       3       4       5       6       7   
8 9 A B C D E F


We've defined our tile, but it's not visible on the screen! To get it shown on the screen, we need to set the correct byte of the Tilemap (starting at &1800 in vram) to our tile number

We're going to define another function called 'GetScreenPos' - it takes an X,Y address in BC

The tilemap is 32 bytes wide, so our calculation for the VRAM address of a tile is &1800+(Ypos*32)+Xpos...

We calculate the address here, and use SetWriteAddress to select the VRAM Address
To make the tile visible, we need to use GetVDPScreenPos to set the destination address in the tilemap, then write the tilenumber (128) to that address.
The tile will be shown to the screen!

Notice there are two shades of yellow in the face, and the background in parts is black, and green in others... thanks to the tile per line color map.
On the Spectrum, we only have one color attribute per 8x8 square, but on the MSX1 it's 8x1! We also have full control of the foreground and background color (no shared BRIGHT attribute)

Some MSX1 games are 'speccy ports', but this doesn't do the potential of the MSX1 tilemap justice.

Showing a bigger sprite!

Ok, this time we're going to show a 48x48 bitmap... we're going to split this into 8x8 tiles...

In tiles, our 48x48 pixel bitmap will be 6x6 tiles...

To do this you can use my AkuSprite Editor (included in the sources.7z)... The Save Raw MSX1 Bitmap option will save in the required layout
We need to import the binary saved by AkuSprite Editor, we also need to define a matching colormap.
6*6*8 is 288... that's more than B can store, so we can't easily use OUTIR to transfer the data, so we'll define our own called OutiDE...

This function will use OUTI to send the data, and use DE as the byte counter (we can't use BC as C is the destination port)
We're going to use that OutiDE command to transfer our bitmap data, and palette for our tile in the same way as with the smaller tile.
Our 'Chibiko' image is made up of 36 tiles, we'll need some code to set all the tiles to the correct tile numbers to show the bitmap correctly.

We'll create a function called 'FillAreaWithTiles'... it'll take an X,Y stary position (in BC), a Width and Height (in HL), and a first tile number (in E)

The routine will show all the tiles to the screen
We'll use this function to show our character to the screen.
The result can be seen here.

We've used the Tilemap here to simulate sprites, rather than real hardware sprites... this saves us from the limitations of a fixed number of sprites onscreen, but means we can only have graphics aligned to 8x8 positions...

If you're just starting out, this should be enough for you, as you'll be able to do plenty with  the Tilemap, and later once you're more familiar with the hardware you can move onto hardware sprites!


Lesson S7 - Easy Sprites on the Camputers Lynx
The Camputers Lynx is a little tricky is uses different memory banks for the Red+Blue and Green color channels... we'll need to do some commands to effect the switch, and we CANNOT use the stack when we do, as the stack Ram will not be writable!

Lets give it a go!

See SimpleBitmap folder

Showing an 8x8 tile to screen

We're going to need a function to calculate the memory address of our destination... the screen is 32 bytes wide, and starts at &A000 in ram, so our formula is:

Address= &A000+(Ypos*32)+Xpos

We achieve the *32 by loading the Ypos (C) into H, and shifting to the right 3 times into L
We're going to show an 8x8 smiley face to the screen!... as each line needs a Blue,Red and Green component... we need 3 bytes per line.
Starting our program, we use GetScreenPos to get the Destination screen location... we then move this into DE...

Next we load the source into HL

We need two registers for width and height of the sprite... we use IXH and IXL to store these
Because we can't use the stack, we use shadow registers (HL',BC' and DE')... EXX swaps the regular registers with these shadow ones... we'll use them during bank switching, so we need BC=&FFFF

We need to back up DE for our processing, so we back it up into IY (IYH/IYL)

We're ready to start writing graphics data!
Lets do the Blue byte!... (at memory address &A000+)

We page in the Blue/Red ram by sending &03 to port &FFFF (BC') and &28 to &xx80...

We copy the byte from HL to DE with a LDI
Lets do the Red byte!... (at memory address &C000+)

There's no paging, as the Red bytes are in the same bank as the blue ones...

We DEC DE to reset the write position, then we set bit 6, and reset bit 5... changing &Axxx to &Cxxx

Then we do another LDI
We need to do the Green byte next (also at memory address &C000+)

it's in a different bank, so this time we send &05 to port &FFFF (BC') and &24 to &xx80...

We do another LDI
We need to add &20 (32) to our position in DE to move down a line, then we repeat for the other 7 lines
We turn off drawing by sending &0 to port &FFFF (BC') and &0 to &xx80... we can now use the stack again!
We've finally got our smiley!

Showing a bigger sprite!

With a few changes, our code can do larger sprites... This time we'll use a 48x48 sprite... (6 bytes wide, 48 lines)

We need to set B=0, as we're going to use LDIR!
instead of LDI we'll use LDIR, and we restore each time from IY to DE
Our sprite will be shown to the screen!
Akusprite Editor can export bitmaps in the correct format for this tutorial code.


Lesson S8 - Easy Sprites on the TI-83
The TI-83 isn't really a games handheld... but it can do a few simple graphics...

Lets learn how to make a simple sprite onscreen!

See SimpleBitmap folder

The TI-83 documentation use X and Y backwards!
X goes down... and Y goes across the screen... the 'X and Y' in this documentation is the more typical way... X goes across - Y goes down!

Showing an 8x8 tile to screen

We need to set up some stuff before we start drawing... first we need two ports (which we access via OUT) to control the LCD - one takes commands (&10) and the other takes data (&11)

We then have the header for our file.

we run a system routine (via RST 40)... the command is 'RunIndicOff) (&4570) this turns off the 'busy' icon which would otherwise appear in the top right

We need to wait for the LCD to process the command, so we call &000B (LCD Busy Quick) - this command will wait for the LCD to finish

we then need to send a command to the LCD - command 1 - this defines the screen as using 8 pixel wide characters

We then set the Autoinc... this will make consecutive writes go across the screen (which the documentation calls the Y axis!)
We're going to create a 'GetScreenPos' function... this will taxe an XY pos in DE... we have to convert these into 'Command bytes', so we add &80 for the Ypos, and &20 for the Xpos...

We'll define a function called SendByte, this will send these to the Command port , and wait for the LCD to process them
We're defining our sprite here... it's just one byte per line.
We're going to draw our 8x8 sprite!

First we load the destination pos in DE... we load the source sprite in HL, and the height in B

for each line, we load in one byte from HL, and send it to the Data port of the LCD (&11)... Unfortunately we have to wait after EVERY write!

As our sprite is just one byte wide, we just inc E after each write, and recalc our screen postiion, we then repeat for the next line!
The Sprite will be shown to screen

Showing a larger sprite to screen

If we add an X loop, we can make the code work with wider sprites, this will allow us to draw our 48x48 sprite
Our sprite will be shown onscreen.
We have to do a wait with LCD_BUSY_QUICK (&000B) after every byte written to the screen

It means we'll not have the fastest graphics, but the screen is so small it's unlikely to matter


Lesson S9 - Easy Tile bitmaps on the Gameboy and Gameboy Color
If we're just getting started, and we want to get some simple graphics on the screen, then the Tilemap will be the easiest way to do this.

Unfortunately, we'll need quite a lot of code, but it's relatively simple if we break it down... lets do that now!

See SimpleBitmap folder


The Cartridge header

We need a valid cartridge header - the one shown will work for this example... note some emulators will need a valid checksum at $&014D - VisualBoyAdvance does not.
There are lots of options in the Header, but if you want to customize it, you'll need to look into it yourself, as it's outside the scope of what we're covering here...

The example shown here should be enough for most people starting coding the gameboy.

Setting up Tiles and palettes

First we're going to turn off interrupts and set up our stack.

Next we'll reset the scroll of the tilemap to zero

Ok, we want to define our tiles, but we need to turn the screen off first...

First we wait for Vblank, by waiting for the linenumber (at &FF44) to be line 145...

Then we turn the screen off by clearing bit 7 of &FF40 (LDC Control)
We need to copy our tiles... we've got a 'DefineTiles' Function to do this ... it will copy BC bytes from HL to DE...

DE needs to be an address in VRAM... Tiles start at &8000 - each tile is 8 lines - 2 bytes per line - so 16 bytes...

We start from tile 128 (so we can have a font from 0-127)... so we set DE to 128*16+&8000
Our define tiles function first checks if the LCD is busy - with the 'LCDWait'  function - we'll take a look at that in a moment...

Next it copies the data... LDIR would be perfect for this - but the GBZ80 doesn't have it!

we use a set of commands to simulate a LDIR - including LDI - a special GBZ80 command that Loads from HL, then adds 1 to HL! (LoaD and Increment)
LCD Wait will make sure we can safely write to VRAM, it checks bit 1 of &FF41 - when it's zero we can write to VRAM without problems.
OK, we want to set up our palette...

On the Gameboy Color we're going to use a function called SegGBCPalettes... each palette is 8 bytes, we specify the palette address with C
On the Regular Gameboy, we use 2 bits for each of the four colors to set the darkness of the color...
We set the background and first sprite palette to one set of colors, then invert them for the second sprite set.
On the Gameboy Color We set palettes with two ports...

Port &FF68 selects a color 'address'... &FF69 sets that byte of the color...

The colors use a 5 bit per channel color format
We're ready to turn our screen back on - by setting bit 7 of &FF40

Setting a 8x8 tile

When we want to set a visible tile, we need to write to the correct address... we'll use GetVDPScreenPos to calculate the address from an XY co-ordinate in (B,C)

The base of the tilemap is at &9800, and the tilemap is 32 tiles wide (each tile is 1 byte),
so our formula is: &9800 + (32 * Ypos)+ Xpos
We use our GetVDPScreenPos function to calculate a memory address.

Now we need to check VRAM is available, so we use LCDWait again.

Now we just write the tile number (128) to HL
on the Gameboy Color there's one extra step we probably want to do.
The Tilemap has an extra byte - at the same address in a different bank - this holds the palette number.

We page it in by writing 1 to &FF4F... and page it out by writing 0 to the same address
Tiles on the gameboy use 2 bitplanes per line... each bitplaine is a single bit for 8 pixels, and the two bitplanes together
Our tile will be show to the screen
The GBC has more options than just palette in its extra ram bank, it can also do H/V flip, and supports an extra bit for tile number, allowing tiles upto 512!

Using multiple tiles to define a larger bitmap

For this example we don't have enough registers... we'll define two bytes of RAM (which starts at &C000+) for that purpose
We're going to break our image up into 8x8 chunks, and draw it onto the screen with a function called 'FillAreaWithTiles'
Our function will use zIXH (defined above) as the end of horzontal byte count, and zIHL is the vertical line end count

We use our GetVDPScreenPos to calculate the start of the line, and start writing our bytes until the end of the line.

On the Gameboy Color we also set the palette of the tile (in the extra VRAM bank)... we're using Palette 1 in this example
The result can be seen here.


Lesson S10 - Easy Tile bitmaps on the Sega Mastersystem or GameGear
Just like the Gameboy, the easiest way to get graphics to the screen is the Tilemap, and we'll use it now to get some simple images to the screen...

In case you don't know the SMS/GG are the same - the GG screen is just smaller

See SimpleBitmap folder

The Cartridge header/Footer

To Start our cartridge, we need a jump to our program code, we also need the RST and NMI handers, but we'll set all those to RETurns

We're also going to define symbols for the Control port, and Dataport of the VDP
Our cartridge also needs a footer, this should appear at &7FF0
Remember! Depending on our emulator and settings we may need a valid Checksum, but most emulators will not mind if it's wrong.

Setting up Screen and palettes

When our program starts, we first need to set up a stack pointer, and set up the screen with a set of predefined screen setting... this will turn our screen on so we can use tiles.

We also need to define our palette...

The palette is one of the few times the SMS and GG are different - as the GG has a better palette!


Setting a 8x8 tile

We're going to show an 8x8 tile 'smiley' to the screen...

The tile is 8 lines tall, and each line has 4 bytes which uses 4 'bitplanes' (on bit of all 8 pixels per byte)
We're going to transfer our bitmap into Tile ram using a function called 'Define Tiles'
We specify a memory address in DE - each tile is 4 bytes per line (4 bitplanes) and 8 lines... in this case our first tile is tile 128, so we set DE to 128*4*8
The Define Tiles function will use the PrepareVRAM function to select a memory address, then send the bytes to the VdpData port.
If we want to set a tile, we can use the GetVDPScreenPos command to convert an XY co-ordinate in BC to a memory address in VRAM

Once we've selected the VRAM address we need to write two bytes to select the tile number, and any other options.
We need to convert an X,Y co-ordinate into a memory address in VRAM... the tilemap starts at &3800 - and is 32 tiles wide... each tile is 2 bytes, so our formula is:
Vram Address = &3800 + (64 * Ypos) + (2 * Xpos)

We effect the multiplication with bit shifting.

But, there's a catch! the GameGear does not show the full tilemap, the tilemap starts at X,Y pos (0,0), but the first visible tile is (6,3), so we add 6 to the Xpos, and 3 to the Ypos

Finally we use PrepareVram, which will select the address as the next destination to write to.
The smiley will be shown to the screen.
We've just put a tile on the screen, for a simple beginners game this should be enough to make something fun!

Later on you'll want to progress to sprites, but since the sprite and tiles use the same 8x8 bitmap data format, you'll be able to use the same graphic!

Using multiple tiles to define a larger bitmap

If we want to show a larger graphic, we need to split it into 8x8 tiles, and use those tiles to show the image...
We can export the image from my AkuSprite Editor... it will split an image into 8x8 tiles in the same layout as the code in this tutorial

The "Save Raw Bitmap" option will save the image as valid tiles or sprites for the SMS/GG
We're going to use a function called 'FillAreaWithTiles'... this will use concecutive numbered tiles to draw a grid based bitmap on our screen's tilemap.

BC is the start XY Pos, HL is the Width and Height... DE is the first tile number of the image.
We need to set a grid area with consecutive tile numbers to draw our bitmap... the code to the right will do this...

We use GetVDPScreenPos to calculate the start of a line... then write the tilenumbers from DE... INCreasing DE each time

H will mark the end of the line... when we get there, we increase C and recalculate the screenpos.

We repeat until we've done all the lines
Our character will be shown to the screen!


Lesson S11 - Joystick Reading on the Amstrad CPC
We looked at drawing an 8x8 Smiley before... lets extend that now, and make the joystick move that icon around the screen...

As we now have graphics on screen, and the ability to control them, this could be the basis of a simple game!

CPC_Joystick.asm

The Code

We're going to need some variables for our player position... one for the Current position, and one for the previous position (so we can remove the old sprite)
Our code will only redraw the character when the joystick is pressed... so the first run we need to FORCE a draw - otherwise we wouldn't be able to see our character!

The Joystick routine we're going to use is firmware function &BB24, this returns the buttons as bits in A - if all the bits are 0 then no buttons are pressed
OK, our joystick has been pressed...

We need to back up the current position of the player...

We're going to run our 'blank player' function... this is just our previous sprite drawing routine, with an all zero sprite
We need to process the joystick.

We need to test each bit of the value of A returned &BB24... HL is the Y axis... DE is the X axis
Ok, we've moved our player, but we need to check if the sprite will go foff the screen

If the player Xpos is greater than 320-4 then it's off the screen (our character is 4 bytes wide)... also if it's below 0 that's a problem, but the value will have wrapped round to 65535, so we don't need to check this!

If the player Ypos is under 8 we're over the screen (0 is the bottom of the screen - and our character is 8 lines tall)... if it's over 200 we're also over the screen...

If the position fails any of these boundary checks, we need to reset the position
We need to draw the sprite in the new position... finally we wait a bit and repeat.
We'll be able to move the sprite around!
We've got a movable sprite onscreen!
We could use this code as a template... and add a second computer controlled player... maybe we could add a 'ball' or a 'bullet' and create some kind of battle between the two players!

Really it's up to you - you've got graphics and input - so you can make whatever simple game you want!


Lesson S12 - KeyControl Reading on the ZX Spectrum
We're going to add cursor controls to our sprite routine on each of our systems! As the Spectrum often does not have a joystick we're going to read the keyboard instead.

We'll use QAOP to read Up,Down,Left and Right!

ZXS_Joystick.asm

The Code

First we're going to clear our spectrum screen, we do this with a LDIR... we're going to set all the pixels to black.

We're going to use a LDIR command to fill the &4000-&5800 range - setting the first byte to 255 so the LDIR will copy it to the rest of the range.

We're then going to jump to our Draw routine!
Our keyreading routine uses the firmware, so don't put a DI in!

During the interrupt handler, the firmware writes any keypress into memory address &5C08 (LASTK)
We're going to read this in - if it's unchanged, then we'll wait until it does change!

We skip this the first time the program runs... we load A with zero, so the key reading routines won't register a keypress
First we need to back up the previous position of the player... we'll need it later..

Now we remove the previous player sprite - we use the 'BlankPlayer' function - this uses our previous sprite routine to draw a 'blank' sprite over the previous player image.
We're going to compare the key in the Accumulator to the direction keys.... and alter C (ypos) and B (xpos) accordingly.

In each case we'll compare to our key, and 'skip' over the change to the co-ordinate if the key isn't pressed.
First we'll save the new postition, and then we'll check if the player has gone offscreen... if they have we'll need to reset the players position...

If the player XY pos goes below zero, it'll wrap around to 255... so we only need to check the top boundary....

We'll check if X (in B) goes over 32, and if Y (in C) goes over 192.
If the player has gone out of range, we'll reset their position to the last good one from the backup.
We're done, so we draw the player sprite at the new position...

Next we clear the keypress buffer, so we don't process the same keypress twice.

Finally, we pause a while, and repeat the procedure.
The result? A small character onscreen we can move around!
In this tutorial we've used the Keyboard - we could of course support a 'cursor' joystick in a similar way, or even a Kempson with some more advanced code...

This was covered in the Platform specific series - but these tutorials are aimed at beginners, so it's not something we'll look at here.

Lesson S13 - Joystick reading on the Enterprise
We're going to extend our previous sprite routine, by allowing the sprite to be moved around the screen.

As the Enterprise has a built in joystick (which is part of the keyboard) we'll be using that to control out 'game'... Lets make it happen!

ENT_Joystick.asm

The Code

As before, we need our program header, SP init, and screen setup,

This time we have a new addition, we need to connect a stream to the keyboard to read the built in joystick... we're loading it as stream 11


We're going to need some variables!... we define two bytes to store the Current XY location of our character.

We'll also have a second 'backup' copy of the location.
The first time we start our loop, we skip over the key reading routine (TestChar)... typically we don't redraw until a key is pressed, but the fist iteration needs to show the player no matter what!
We can read a character using EXOS call 5... this will wait for a character - so if no keypress is waiting the program will stop...

While this would be fine for this simple sample, This isn't great for a real game, as we'd probably want some CPU character to move when the player doesn't...

to combat this, we use EXOS call 9 to test the stream, and tell us if a key is waiting to be read or not... if not we return 0
Looking back at the start of our drawing code, we're going to back up the current player position...

Next we'll Blank the old sprite position with the 'BlankPlayer' function - this wipes the old sprite off the screen, using the bitmap routine we wrote before.
We're now going to compare the keypress we read in, and alter the XY position in BC accordingly...

The internal joystick uses special keypresses... we check for these, and skip over the code to change the player position if the key is not pressed

The UDLR keypresses are as follows:

&B0

&B8

&BC

&B4

We've read in the cursor changes, so we save the new cursor position.

Now we need to compare the new position and see if the player has gone off the screen...

the top left corner of the screen is 0,0... and our positions use a single byte... this means if the player goes off the top or left hand side of the screen the position will 'wrap' back to 255 (as a 1 byte value cannot be negative)...

Therefore we only need to compare against the top boundary... 80 bytes wide, and 200 lines tall - minus our 2 byte, 8 line tall sprite
If the player has gone offscreen, then we need to reset the position from the backup we took.

We finally draw the new position of the player sprite, pause a moment and repeat.
We will be able to control our smiley with the 'keyboard' joystick!
We've only used the keyboard Joystick... If you want to use the 'real' joysticks you'll need to use the &B5 and &B6 ports... take a look Here for more info!


Lesson S14 - Key Reading on the Sam Coupe

We made our bitmap example before - lets now extend it to add key reading to move the bitmap around the screen.

SAM_Joystick.asm

The Code

Our code starts with our SP definition... next we need to select a bank for our screen memory - we're using banks 14/15 for the screen... these are the top two banks of a 256k system.

We're now going to skip over the keyboard routine to start the first draw even though a key hasn't been pressed.
We're going to need an X and Y byte to remember the position of our player,

We'll also have a backup, which we'll need for some purposes.
We're going to use firmware function &0169 which will return a key in A... we'll wait until we get one...

The start of our drawing loop will backup X and Y for later, it will also run a 'BlankPlayer' routine...
This is a modified version of our previous sprite routine which will write an 'empty' sprite to clear the old position of the player
We're now going to compare A (containing our keypress) with our direction keys... if a key ISNT pressed we'll skip over the code that would move the player in that direction...

We repeat for the keys QAOP for the directions UDLR
We're going to update the stored XY variables with the new position... then we'll check if the players went over the 'boundaries'...

The top left corner is 0,0... our XY values are a single register, and if we go below zero, we wrap around to 255, therefore, we don't need to check the bottom constraint.
If the player is out of the boundaries, we'll reset the position from the backup...

Finally, we'll draw the player in the new position.... pause a moment and repeat the procedure
Our player smiley will be movable with the QAOP Keys.
The Sam Coupe can use joysticks which will use number keys... Joystick 1 uses 6-0... Joystick 2 use 1-5.

You'll just need to reconfigure the direction keys if you want to use one of these joysticks


Lesson S15 - Joystick Reading on the MSX1 + MSX2
We're going to use the previous two bitmap routines that drew a smiley face to the screen, we'll now use a firmware function (&00D5) to get the joystick directions and move our sprite around the screen!

MSX1_Joystick.asm
MSX2_Joystick.asm
The Code
We need to define 4 byes of RAM for our player position... a pair for the current XY position... and a pair for the previous position
When our screen is set up (the same way in the bitmap tutorial), we need to reset our player position... then we skip over the key reading routine to the drawing routine.
We're going to use firmware function &00D5 to read the joystick... this function uses A - if A=0 it will read cursors, 1 or 2 reads joystick 1 or 2 respectively
We only read the joystick in this example, but there is also a function to read fire buttons

We use this to read in the controls, and wait for a key to be pressed

&00D5 - GSTCK
Input: A=0/1/2 .... Key/Joy1/Joy2
Output: a number 1-8 based on direction:
 8 
 1 
 2 
 7
 3
 6  5  4
&00D8 - GTTRIG
Input: A=0-4... Button to test (Space / J1-A / J2-A / J1-B / J2-B)
Output: &FF=button pressed, &00= Button not pressed
The start of our loop first backs up the current position of the player, then it uses a function known as 'BlankPlayer'

BlankPlayer will use the sprite routines to remove the previous sprite.
It's time to move our player based on the keypresses... BC contains the XY position of our player.

We check each direction, and if the direction is not pressed, we skip over the code to change the register,

This will move the character Up, Down, Left or Right.
We need to make sure our character doesn't go offscreen...

Our top-left position is 0,0 - if we go below zero, the byte will wrap around back to 255 - therefore we only need to check if the X position goes over 32, or the Y position goes over 24
If the player has gone offscreen, we'll use 'PlayerReset' to restore the old position.

Whatever happens we redraw the new position of the character, wait a moment and repeat.
The sprite will be shown on screen...

There are two versions of this program, one for MSX1 and one for MSX2

The example here only supports UDLR... you really probably want diagonal support as well... but you'll have to code that yourself.

Lesson S16 - Keyboard Reading on the Camputers Lynx
On the Camputers Lynx, we'll use the keyboard to read cursor keys and control our player... the Lnyx does have joysticks, but our emulator doesn't support them!

Let take a look!

CLX_Joystick.asm

The Code

We need to define some bytes of ram to store our variables... we store the current XY position in one pair, and the previous XY position in the other
Starting our program, we need to skip over the key-reading routine on the first run, this is because the reading routine will loop untill a key is pressed, and we need to show the first sprite to the screen.
We're going to use a 'ReadCursors' function to read in the keys of the Lynx - the Lynx actually had joystick ports, but it seems our emulator doesn't support them!?

We wait for a key to be pressed before we proceed
We're going to read in the Cursors... we want to read in from two ports, and then shift the bits of the cursors into the layout %----RLDU

Port      7             6           5           4           3           2           1           0     
 &0080 
Shift Escape Down Up ShiftLK

1
&0180

C D X E 4 3
&0280
Control A S Z W Q 2
&0380

F G V T R 5
&0480

B N Space H Y 6
&0580

J
M U 8 7
&0680

K ,
O I 9
&0780

;
. L P 0
&0880

:
/ I @ -
&0980

Right

Return Left
] Delete
First we're going to back up the XY position of the player - we'll need it later to restore the player position if they go offscreen.

Next we remove the current sprite... we do this with a function called 'BlankPlayer' - it will use an 'empty' sprite with the bitmap routine we saw in the bitmap example.
We've got the XY pos in BC... we need to test each button (which will be bit zero in D)

If a button is pressed we need to Add/Subtract from B or C... We do this by testing each button and skipping over the 'change' commands.
We've updated the XY position, so we store it in the variables for our player.

Next we need to check if the player is still onscreen... the screen is 32 bytes wide - and 200 lines tall (or so)...

As the top left corner of the screen is 0,0, and we use one byte to store our X and Y position, if we go off the top, or left hand side of the screen, we'll wrap round back to 255,

Therefore we only need to check the top boundaries of 32 and 192


 

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

Top Menu
Youtube channel
ASM Programming Forums
GitHub
Dec/Bin/Hex/Oct/Ascii Table

Z80 Content
Learn Z80 Assembly
Hello World
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
Learn 6502 Assembly
Advanced Series
Platform Specific Series
Grime 6502
6502 Downloads
6502 Cheatsheet
Sources.7z
DevTools kit
6502 Platforms
Apple IIe
Atari 800 and 5200
Atari Lynx
BBC Micro
Commodore 64
Commander x16
Super Nintendo (SNES)
Nintendo NES / Famicom
PC Engine (Turbografx-16)
Vic 20

68000 Content
Learn 68000 Assembly
Platform Specific Series
Grime 68000
68000 Downloads
68000 Cheatsheet
Sources.7z
DevTools kit
68000 Platforms
Amiga 500
Atari ST
Neo Geo
Sega Genesis / Mega Drive
Sinclair QL (Quantum Leap)
X68000 (Sharp x68k)

8086 Content
Learn 8086 Assembly
8086 Downloads
8086 Cheatsheet
Sources.7z
DevTools kit
8086 Platforms
Wonderswan
MsDos

ARM Content
Learn ARM Assembly
ARM Downloads
ARM Cheatsheet
Sources.7z
DevTools kit
ARM Platforms
Gameboy Advance
Risc Os

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

PDP-11 Content
Learn PDP-11 Assembly
PDP-11 Downloads
PDP-11 Cheatsheet
Sources.7z
DevTools kit

My Game projects
Chibi Aliens
Chibi Akumas

Work in Progress
Learn 6809 Assembly
Learn 65816 Assembly
Learn 6809 Assembly
Learn TMS9900 Assembly
Dragon 32/Tandy Coco
Ti 99

Misc bits
Ruby programming













































If you want to support my work, please consider backing me on patreon!










Buy Chibi Akuma(s) from PolyPlay
Buy ChibiAkuma(s) games now!



































































































































































































































If you want to support my work, please consider backing me on patreon!










Buy Chibi Akuma(s) from PolyPlay
Buy ChibiAkuma(s) games now!



































































































































































































































If you want to support my work, please consider backing me on patreon!










Buy Chibi Akuma(s) from PolyPlay
Buy ChibiAkuma(s) games now!