Learn Multi platform Z80 Assembly Programming... With Vampires!
Hello World Series
<- Back to the Main Contents & Basic Z80 Assembly Lessons

Introduction to the Hello World Series...
We've covered a lot of complex stuff in these tutorials, but I think it's time to take a new approach, and offer the absolute basics for people who just want to get started!

In this series, we're going to go over every system covered in these tutorials, and create a 'One File Hello World'... that is a Single ASM file, which can show a 'Hello World' message one one system with NO INCLUDES

Contrary to my usual method, we WILL use the firmware in this series - as the goal is to get 'Hello World' on screen as simply as possible!

For each system we'll define a PrintChar to print a single character, we'll extend this to a whole line with a common PrintString, and we'll also create a NewLine command to start a new line - this should allow you to start building your own simple programs!

As an added bonus, there will be an extra 'chunk' which will allow the Monitor and Memdump to work... this will need an include, but the logic is simple... if you can get data from the CPU and Memory to screen, then you can start testing and building up your own code.


Once we've got the working ASM file - we'll then go into the technicalities of how to get the program running on an emulator... in other words, how to build a Disk/Tape/Cart image, load it into an emulator.... and where needed, load it in basic!
if you're using my Devkit you won't need this, but if you're not, or want to tweak it for your own needs, then this will tell you all you need.

Essentially this series will give you the simplest, shortest path to running your own mini program on the machine of your choice!

Enough chit-chat... lets start saying HELLO to the 8 bits!

Lesson H1 - Hello World on the CPC
As always, the CPC is my first 'Go to' system for development, and we'll be using Winape again, for it's superb built in Assembler!

We'll use the firmware to do the work of drawing text to the screen, and we'll create our own PrintString function!

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


Showing Characters and Newlines
To Print a Character, we're going to use firmware call &BBFA... calling this will print the Accumulator to the screen.
By using PrintChar with character 13 then character 10, we can start a new line
We're going to use this Printchar routine in our Printstring function

This function will be common to every platform
To use this function, we just point HL to the message, and Call PrintString

Running the Program
Select Assemble from the Assemble Menu
In the emulator type Call &1200 and press enter
The message will be shown onscreen
The Complete code


PrintChar     equ &BB5A   

    org &1200

    ld hl,Message            ;Address of string
    Call PrintString        ;Show String to screen

    ret                ;Finished Hello World

PrintString:
    ld a,(hl)    ;Print a '255' terminated string
    cp 255
    ret z
    inc hl
    call PrintChar
    jr PrintString

Message: db 'Hello World 323!',255

NewLine:
    ld a,13        ;Carriage return
    call PrintChar
    ld a,10        ;Line Feed
    jp PrintChar



With a Monitor
We also have Montior Tools we can add...

These have been covered previously in the Multiplatform tutorials...
With the Monitor, we can dump the contents of the Main Registers...

We can also dump areas of memory.
If we can see the registers and memory, we can use this to debug code we're working on, and work out what's happening...

Of course, on Winape we actually have a better option in the built in debugger (Press F7!)

Building a Disk or Tape
DISK:
The easiest way to run our program is compiling with WinApe, but if you want to use a different Assembler or emulator, we need to create an image!

Creating a disk image can be performed with CPCDiskXP.exe

To add an executable to a disk image, we need to add an AmsDos Header...











As we'll need to run our program often, it's best to do this from the command line (there's a batch file to do this in my DevTools)
Here's an example usage:


We've specified the Run Address
the Source file (Binary program we compiled)
and a Destination Disk image
When it comes to loading a program in basic from Disk or Tape there are three stages
1. Allocate memory with the MEMORY command
2. Load the Binary program to the address in our ORG statement
3. Call the address of the ORG statement
Tape:
If you want to build a tape I use 2cdt
This is a command line tool that builds a tape image, here's an example usage:


By default 2CDT adds a file to an image, the first line starts a New Image with the -n switch
We specify the Name of the file as it appears on the tape with -R
Next we specify the source file name and path
Finally we specify the destination tape image name
If nessasary we can specify the type of file, in this example we specified one file as a 'Pure Data Block' (rather than a turbo loading data block)

Note: The go.bas has to be already in CPC format (not a text file)... create a basic file on the CPC, and export it from the disk image with CPCDiskXP

The program can be loaded on the CPC in the same way as the Disk image
Cartridge:
The Easiest way to create a cartridge is with NoCart , this tool converts a disk image into a CPC+ Cartridge.
It's a command line tool, and the command is as follows:


We specify a Source Disk image - this is the disk with the program we want to make into a cartridge
A Destination cartridge to create
Finally, a basic run command which will start the program
+

Advanced Cartridge:
Nocart works by using an Amsdos firmware emulator to run the disk image... if we want to use the full capabilities of the CPC+, then we probably want a more advanced solution... BuildCPR Will convert a binary rom to a cartrige - but you'll have to know how to do everything yourself with no default firmware, so it's not something for beginners...
BuildCPR is released as source, but there is a compiled version in \Utils\buildcpr in my DevTools kit

I used a modified version of the OS source file PATCHDOS.asm from BuildCPR, which loads the first 32k of ROM from the cartridge into RAM, and then executes it - effectively the same as my Disk code above. (This is available in my DevTools - \Utils\buildcpr folder)

This needs to be compiled with PASMO to a ROM file
Next we need to merge the ROM


We need to merge the roms with our program binary into a temp file
Then we convert the temp file into a destination CPR Cartridge ...


Creating a 'Real' Cartridge allows you to use ROM banking to gain access to large amounts of data allowing you to have a machine with 64k ram and 512k rom - if you convert disk images, you've effectively got a 464+ with a disk system.

'Real' cartridges are better, but they're more work, especially if you're planning to release for non plus CPC's as well.

Lesson H2 - Hello World on the ZX Spectrum
The ZX Spectrum is one of the most popular Z80 machines around, and it's well emulated too!

Unfortunately, making usable Disk or Tape images can be a bit tricky, but we can get around it with some work!

See SimpleHelloWorld folder

Beginning a program
We're going to compile our program with the VASM assembler, and test with the FUSE emulator...
I tend to use Notepad++ for my editor - you can get a pre-configured package in my DevTools download
When we start our program, the first command should be an ORG &8000 statement... this defines that our program will be loaded in memory address &8000 (32768)

Showing Characters and Newlines
We need a 'PrintChar' function that will print the Accumulator as a character to the screen,

To do this we need to Open a channel to the screen, we do this by loading A with 2, then CALLing &1601

Now our channel is open,to Print the Character we just use RST 16 (sometimes called RST 2)... this is the same as CALL &0010

Note we have to do a lot of PUSH and POPs to ensure that none of our registers are changed during the PrintChar function
We're going to use this Printchar routine in our Printstring function

This function will be common to every platform
To use this function, we just point HL to the message, and Call PrintString

Compiling and running the program
If your using my Devtools and Notepad++, press F6 and select VASM ZXS


If you're not using my Devtools then read on - it's going to be tricky!
Manually creating a Disk or Tape
(You don't need this if you're using my Devtools!)
We're compiling with Oldstyle VASM...  The build script I use is shown to the right.
There are a few parameters you may want to change...
The Source file is specified - this version is called by a batch file, so it's shown as %1 here,
The Destination file - we write to Program.Bin here.... we're outputting to binary here (specified by -Fbin)
Some Symbols are defined - you won't need these for the Hello World example, but my other files use them.

Note: This is just a binary, we need to get it into a tape or Disk to run it.
*** Creating a Tape Image ***
First we're going to convert our file to a TAP... we're going to use BinTap to do this (note the version in my download package is a newer build)

We Specify a source Binary, a destination TAP, a filename (for the file in the TAP) and the origin (though we can override the origin in our loader)
We want to add a loader to our tape, the loader is shown too the right, it has two lines.

The first line loads the next file to memory address 32768 (&8000 - the same address as our ORG statement)
the next line is a 'Randomize' statement, but it's actually a trick to 'Call &8000' - the USR statement defines a user function... this will run our program

We can just create the basic program in notepad, and convert it using MkTap (I didn't write it, but I can't find a link to the original)
We're specifying:
A Source file to read from (in text format)
A destination TAP file
A Spectrum filename for the file (inside the TAP)
We're also specifying a line number for the program to start (10)


We've now got a program TAPe and a loader TAPe... we need to combine them using the windows COPY command - specifying /B for binary copy

We specify the source files, separater by +... order is important
We specify the destination file for the complete TAP

We can specify the tape file on the Fuse emulator command line to load it automatically
*** Creating a DSK (Spectrum +3) Image ***
The Spectrum +3 uses the same Disk file format as the Amstrad CPC, but we need a different kind of header for the files, I've created a tool called PlusThreeHeader that can add the correct file header

First we use PlusThreeHeader to convert our Sourcefile into a Destination file valid for the Speccy (We need to give it a .C extension)

Next we use CPCDiskXP to add the Destination file to a Disk image.
When we want to start Fuse, we can specify a Source Disk image that we want to load, we also need to specify that we're using a +3 machine... we can even load a Memory Snapshot at the same time, to get the machine ready to load our program!
To actually start the program, once again we need a basic loader..

Once it's typed in, we could save a memory Snapshot, and load it on the command line to allow us to start our program quickly
If we want our disk to start automatically with the Loader, we just need to save a file called "DISK" and specify the line number to start the program (10)
*** Creating a TRD image ***
TRD (Betadisk) is supported by many SD card disk emulators like the DIVMMC ENJOY and is the only real option for disks on non +3 systems... unfortunately it's a major pain!

You can download an empty TRD image here... You can get a detailed (translated) TRD manual here... an English manual is also here

To add files to a disk image we use TrdTool

TRDTool cannot overwrite files... first we try to delete the file we want to add from the disk image (in case it already exists on the disk)
Then we add the file we want to add to the disk image

The file MUST end in .C
When loading Fuse, we probably want to emulate 128k Machine, and we need to enable Betadisk

We need to specify our TRD Image we made
You may also want to have a memory snapshot loaded up, with your program about to start!
When sending a command to TRDOS, we first have to start with "Randomize USR 15619 : REM :"... what follows will be the actual command TRDOS executes

You'll probably want to save a memory snapshot to save you typing this lot again!

You can start the TRDOS command line with Randomize USR 15616...

* Note: Some very old versions of TRDOS did not use 15619/15616 - they used alternate numbers
If you save a file called 'boot' it will autostart where possible (48k and DIVMMC only?)

You may want to develop for the ZX spectrum or MSX, but you should startwith the CPC and WINAPE!
Winape has a built in Assembler and Debugger that are second to none! In the early days you will make a lot of mistakes, and having the compiler ,emulator and debugger in one place will save you a lot of time and problems!You can even develop MSX and SPECTRUM code with it - the MSX and Spectrum versions of ChibiAkumas are compiled in winape too!.... If youdont't use widows, Winape works OK with WINE!

Monitor tools
in the Multiplatform series, we created some tools for debugging programs,
we can use these with this hello world example by including a few extra files.
This gives us two tools, a Monitor, which will dump the CPU registers to screen.

and a MemDump, which will output a number of bytes of memory to screen
We can use these tools to see the state of the CPU and Memory, and this allows us to test and debug our code.

It's worth bearing in mind that TRD is bigger than DSK... a single TRD can take 720k of data, but a DSK is only 180k...

During development of ChibiAkumas, the game was developed on TRD, before compression was applied to make the game run on DSK.

Lesson H3 - Hello World on the MSX / MSX2
The MSX gives us two options, we can create a Disk, or a Cartridge...

The process we need to use to create the binary will vary depending on the system, we'll look at the Cartridge first.

See SimpleHelloWorld folder

Making a MSX Cartridge
We're going to compile our program with the VASM assembler, and OpenMSX for emulation

First we need to create a header... the header is 10 bytes... the header starts at &4000 in MSX memory (but byte 0 in the Binary output of the assembler)

The first two bytes are always AB... the next two bytes point to the first line of code in ram (so probably &400A - 10 bytes in)
The next 8 bytes are all &00  - as they are unused for our purposes




Showing Characters and Newlines
The first thing we need to do is set up our screen, we need to CALL &006F to select screen mode...

Then we need to write 32 to &F3B0 to tell the firmware the width of the screen we're using.
If we want to show a character to the screen, we can just use firmware function &00A2 - this will print the accumulator to the screen.
If we want to start a new line, we can just print Character 13, then Character 10 to the screen.
We're going to use this Printchar routine in our Printstring function

This function will be common to every platform
To use this function, we just point HL to the message, and Call PrintString
The code will show our message to the screen

Finishing a MSX Cartridge
After our program has run, we can't return to basic, as we didn't use it... instead we can just halt the processor
OpenMSX won't really like it if our cartridge size isn't a multiple of 8k...

We can pad our cartridge to 32k with VASM with an ORG &C000 statement.
We need to use VASM to compile the source into a binary file... here is a sample command line...

We need to specify a source file
We need a destination for the cartridge
In this example we're also defining some Symbols - you'll only need these for the Montor test.
All we need to do next is load the cartridge...

we specify the Cartridge file on the command line... You can use MSX Catapult if you prefer though!

Making a DISK image file
We need to add a small header to our binary file to make it valid for the operating system - it contains a calculated checksum, so it's easier to use a tool to make it for us, I've created a command line tool called MSXheader which will do this for us

We specify a Source file to read
A Destination file to save the result,
and a Load address... this is the place in memory the MSX OS will load the file by default
We don't even need to create a real disk image, OpenMSX can use a folder as a disk... so putting a file in the specified folder will allow the MSX to use it, but if we want to create a disk image (which we will for redistribution) we can use the free tool BFI... We need to collect all the files for the disk into a folder, then convert the folder to a disk image

We can specify a source folder with the files,
A Disk Image to create,
I found some machines had trouble booting from the image, so I added a new bootsector to replace the default boot sector with an MSX bootsector, which seemed to solve the problem
We can start OpenMSX from the command line with the finished disk image

We specify a source disk image,
We also specify a machine type, I tend to use the MSX2+ Sony XDJ as a good testing machine for disk software
We need to load our program into memory (the load address is in the file header)
We then need to define USR0 to point to the address we want to call, and execute the program with a USR0(0) call

To make the disk boot automatically save this to the disk as AUTOEXEC.BAS



Monitor tools
in the Multiplatform series, we created some tools for debugging programs,
we can use these with this hello world example by including a few extra files.
This gives us two tools, a Monitor, which will dump the CPU registers to screen.

and a MemDump, which will output a number of bytes of memory to screen
We can use these tools to see the state of the CPU and Memory, and this allows us to test and debug our code.

Lesson H4 - Hello World on the Sam Coupe
The Sam Coupe is pretty similar to the Speccy, but there are some differences in the hello world code...

One good thing is the SAM has a disk drive my standard, so we also need to create a usable disk image for the SAM

See SimpleHelloWorld folder

Beginning our program
To Start our program, we just need to define an origin of &8000
At the end of our program, we can just use RET to return to the OS, but that will clear the screen,

For testing we may want to use DI HALT to stop the processor instead.

Printing to the screen
If we want to print a character in A to the screen, just like on the speccy, we just use RST 16

This will print our character to the screen
When we need to create a newline, we just print CHR 13 to the screen.
We use PrintChar to print a 255 terminated string
By loading HL with the string address, we can show our string to the screen.


Compiling our program
We're going to use VASM to assemble the program to a binary file.

We're going to specify the Source Asm file,
The Destination Binary,
We're also going to define some symbols, these are only needed for the monitor tools
Finally we'll output a listing file - this is optional but will help if we have any unexpected problems

The Monitor Tools
In the Multiplatform series, we created some tools for debugging programs,
we can use these with this hello world example by including a few extra files.
This gives us two tools, a Monitor, which will dump the CPU registers to screen.

and a MemDump, which will output a number of bytes of memory to screen
We can use these tools to see the state of the CPU and Memory, and this allows us to test and debug our code.

Creating a disk
To create a disk for the Sam Coupe I use  "DiskMan" (the SimCoupe .DSK Manipulator by Andrew Collier)
You can get the original Here, But I use a modified version - I added the ability to load in a list of changes from a text file - my version is in the Devtools download (Source included)
We can use my version to automate the creation of a disk image.
If you're using my program, we can call it with a script on the command line
The text in the script are sent as keypresses to the program (including Enter keys)...
In this example we do 3 kinds of command
N = Create a new disk image
S = Save a file to the image (Specifying Source file, and Destination name)
W = Write the complete disk to our computer as a disk image

If we save our program with the name AutoGo , the SAM will load it automatically for us!
We can start SimCoupe with our disk image by specifying the disk image on the command line

If you don't want to use the modified DiskMan, you can use the same keypresses on the regular version,

Alternatively there are disk editors that use a User Interface - the trouble is, while these programs work, they aren't efficient for developing, where you may be running your program dozens of times when fixing a problem
Loading a program
We can see the contents of our disk with the DIR command.
If we've created an AutoGo file, then it will run automatically... we can load to a different address in the same way as the spectrum,
We need to specify the load address as well.
If we want to call a program in memory, we use the USR command to call an address - we need to send the output to somewhere, so we use the RANDOMIZE command for this.... We can also use CALL &8000

Lesson H5 - Hello World on the Elan Enterprise
The Enterprise has a complex OS, but we can get it to do simple text with relatively few commands,

Lets learn how to make the Enterprise say 'Hello!'

See SimpleHelloWorld folder

Beginning our program
First we need to define the header for our program...
Our program code will start at &100, but we need a 16 byte header, so we start our header at &0F0
We need to put the length of the file in the header 2 bytes in.
Our file start will need to define a new Stack Pointer - we'll define the SP to point just before our program at &100 -  this will give around 128 bytes of stack data
We need to initialize a text screen, first we need to open a stream to the device 'VIDEO:'.... we do this as stream 10.

All calls to the OS use RST 48 (called RST 6 on some operating systems)... the byte following the RST is the command number

Next we need to do some extra commands with a command call 'Special Function'... this init's the screen and turns it on



Showing Characters and Newlines
When we want to print a character to the screen, we need to use the channel we opened to the screen before (Channel 10)

We load B with the character we want to show (from A) - and call the EXOS call (RST 48) and use command 7 to send the character to the stream.
We can effect a newline by printing Character 13 then Character 10
We can repeatedly print characters from a HL pointer, until we get to a chr 255.

We can call this function after pointing HL to a text string, to print that string to the screen.


The result will be shown to the screen.

Ending our program
After our program has finished, we'll stop the processor so we can see the result.
After the last byte of our program, we need to define the FileEnd so we can calculate the length of the file.

Running the program from FileIO
If we have FileIO set up (Learn more here) we can just use a folder on our hard drive to run the program from...

In this case we need to specify File: as the source drive, and our program name

Running the program from disk image
If we we want to create a DSK image, for a real machine (or if we haven't got FileIO set up) we can just convert a folder with the program into a disk image with the free tool BFI...
We need to specify two things...
The Source folder,
and the Destination disk image
We can start our program with a RUN command...

Alternatively, if we save our program with the name START, we can load it automatically by pressing F1



If we want to save time, we can configure EP128emu on the command line,

I specify a config file with the correct emulator settings,
and also a memory Snapshot to save time typing the run command

Setting up FileIO is a bit of a pain, but the load time on the emulator is faster than from Disk Image, so it's worth it...

The same goes for using snapshot save states, they can help reduce the load time - and saving a second or two every run really ads up when you're developing a game!

Compiling our program
We're going to use VASM to assemble the program to a binary file.

We're going to specify the Source Asm file,
The Destination Binary,
We're also going to define some symbols, these are only needed for the monitor tools
Finally we'll output a listing file - this is optional but will help if we have any unexpected problems

The Monitor Tools
In the Multiplatform series, we created some tools for debugging programs,
we can use these with this hello world example by including a few extra files.
This gives us two tools, a Monitor, which will dump the CPU registers to screen.

and a MemDump, which will output a number of bytes of memory to screen
We can use these tools to see the state of the CPU and Memory, and this allows us to test and debug our code.


Lesson H6- Hello World on the Camputers Lynx
The Camputers Lynx is one of the more odd 8 bits - getting Hello World on the screen is pretty easy, but due to a lack of tools, making a TAP file is not!

Lets learn how to make Hello World, and turn it into a valid tap file to run on our emulator

See SimpleHelloWorld folder


Beginning a program
A good starting point for our program is the address &6500, basic and the stack uses areas before this.

Next we're going to do something a little odd, when the program loads, the Lynx will be making quite a noise! to stop this we need to write zero to %10000100

Printing a character to the screen
Printing a character to the screen is easy, all we do is use RST 8 (called RST 1 on some assemblers)... this will print the character in the accumulator
If we want to start a new line, we can just print a character
We're going to extend that printchar command into a printstring,
All we do is repeatedly read in from HL, sending each character to Printchar, until we get to a character 255.
The "Hello World" message will be shown!



The Monitor Tools
In the Multiplatform series, we created some tools for debugging programs,
we can use these with this hello world example by including a few extra files.
This gives us two tools, a Monitor, which will dump the CPU registers to screen.

and a MemDump, which will output a number of bytes of memory to screen
We can use these tools to see the state of the CPU and Memory, and this allows us to test and debug our code.


Lynx TAP file format
As the LYNX has no cartridges, our best way of getting data from our assembler to the Lynx is to make a TAP file...
Creating a valid TAP file requires some byte calculations, but is not too hard... here is the format of the TAP file
FilePos Bytes Suggestion Purpose
0 8 “NONAME” filename
8 1 M file type (M=Machine code)
9 2 &100 Program length
11 2 &6500 Load Point (LH – little endian)
13 len ? Program Code
13+len 2 &1313 Check digit (sum of program code) – twice!
15+len 2 &6500 Execute point
17+len 1 &65 High byte of Execute point

Compiling our program, and Creating a TAP file
First, We need to compile our program into a binary, we use VASM in these tutorials, but you can use another assembler if you prefer.
We run VASM on the command line

We need to specify a source file,
We need to specify a destination binary
For my Monitor tools, we need to define some symbols
finally, we can output a listing, which can be used for debugging
OK, we've made our binary, but there's a problem! - we need to add a header and footer to our file, and unfortunately they're quite complex!
The header needs the file size adding to it, that's not too bad, and we could make it with VASM, but the footer needs a checksum which we must calculate,

To create the header, I use a sample header file, and attach it to the binary made in the last stage, then I patch in the size, and add the checksums.
We're going to use a tool I wrote called BinaryTools - this tool can do various useful things for building ROM images!

First we make a second copy of the program, this is so we can use one as a source to calculate checksums, and the other as the destination for those checksums.

Now we calculate the checksums, and append them to the end of the program binary

Next we insert the new length of the binary into the template header.

Now we append the Execute position (the ORG we used in our code) to the end of the file (with the high byte (&65) repeated), we also insert it into the header.

Our last stage is to use the Copy command in binary mode (/b) to merge the header and the program binary into a usable tape file!

Phew! we've made a usable TAP!
We can automatically start our TAP with Jynx, by specifying the TAP on the command line!
The tape will load itself without us typing a single command!

The Firmware of the Lynx is pretty slow, even when drawing text, it may be better to use our own font and draw things ourselves (like in the main tutorials), but of course that will use up memory!

You'll have to decide what your priorities are, and write your code accordingly!

Lesson H7- Hello World on the TI-83
Its time to move along to another system, this time we're going to learn how to get 'Hello World' on the TI-83 calculator

See SimpleHelloWorld folder


Beginning a program
To start our program, we need our code to start at &9D95, but we need a 2 byte header with the bytes &BB,&6D
To allow for the header, we specify our code origin &9D93
When we finish our program, we just use a RET command to return to the OS

Printing a character to the screen
We have to use firmware calls for everything which use RST &28 (also called RST 5 on some assemblers)... there are two bytes after the RST call, these define the OS function we want to use...

In this case we want to use 'PutC' (Put Character) using the code &4504 - this prints the Accumulator to the screen as a character.


We're also going to define a newline command, we use the same RST &28, but this time we use &452E NewLine function.
Because we now have the ability to print a character, we can extend this into a function to print whole strings.

Our code prints a string that is ended with a 255, we just pass the string address in HL


This can be used to show a test string to the screen

The Monitor Tools
As a bonus, we can include the Monitor functions we wrote in the multiplatform series,

These allow us to show the Z80 registers, or parts of the system memory for debugging purposes
This adds 2 new functions... Monitor shows all the registers...

Monitor_MemDumpDirect shows part of the memory... it shows C bytes from address HL
Due to the small screen, the Monitor isn't very useful, but it's always there if you need it!

The Limited screen space means these monitor tools aren't a lot of use - for more practical purposes, it's probably better to print the one register you're interested in on screen when you're debugging

See the main tutorial series for details on how to do Locate commands to re-position the print cursor!

Creating a valid executable file
In these tutorials we use VASM to compile, we need to specify various parameters.



First, We need to specify a Source file,
Next we specify a Destination - this is a binary, but we'll need to convert it to an 8xp later
We also specify some symbols - these are only needed for the assembler.
Finally we specify a Listing file - this is for debugging and contains output which may help us if we have trouble.

We're also specifying to ignore case sensitivity, and check for labels that look like commands
We can't use the program yet, we need to convert it with bin28xp, this will add the header required for the calculator to run the program.

The program takes a source file, and outputs a matching 8xp, we should be able to use this with our emulator
if we're using WabbitEmu, we can start our emulator with the new program via the command line.


In this example we're specifying the complete 8xp to execute

We're also specifying a memory save state on the command line as well, this prepares the calculator to launch the program.
To start a program, we need to specify the program name to the command ASM...

In this example, I've saved a memory snapshot so I don't have to type the command name
Without the snapshot we need to load the program ourself!

First we need to use the 'Catalog' function button

Then we need to select the ASM( function
Unfortunately this just starts the function to run the code, we still need to specify the program,

Select the PRGM button, and choose the program from the list,

This will prepare your program to run, just select the enter button to run it!





Loading a program on the TI is a pain, so you definitely want to use a save state to save time loading the program.

If you want to run your program on a real machine, you'll need a USB cable - unfortunately they're horridly overpriced - so it's probably best to just stick to the emulator

Lesson H8- Hello World on the Sega Master System and GameGear
The Sega Mastersystem and GameGear are virtually the same system, so we can create one Hello World that will run on both systems.

Unfortunately, the SMS/GG have no firmware to help us, so we're going to have to create our own font, and work out how to get it to the screen... Lets go!

See SimpleHelloWorld folder

The example today will build for the Sega Mastersystem by default... but with a few tweaks we can compile for the GameGear...

To do this just define the symbol 'BuildSGG' (with 'BuildSGG equ 1') - this will compile the Gamegear version (with alternate palette, tile offset, screen size and rom footer)

Beginning a program

Our rom starts from address &0000 - and the first 128 bytes or so are RST's and interrupt handlers - we'll fill these all with RET's (&C9) effectively turning off interrupts.

RST 0 at &0000 will be the first byte executed by the system, so we need to put a jump at this point to jump to our actual program code..

As soon as our program starts, we'll turn off interrupts, and set the stack pointer up to a valid RAM address
As well as the header - we should create a footer to our rom... though depending on our emulator setup (if we don't specify a firmware rom), this may not actually be needed.

Though rom files can be up to 512k, this footer always at &7FF0... it contains a few bytes, most of which we can just leave the same as here.

There is a 2 byte checksum - this is only actually checked by the Export SMS - so we can ignore this if we run on a Japanese SMS or a game gear... We'll learn how to create a valid checksum in a moment.

Depending on our planned region and rom size, we may need to set the last byte to the correct value for our cartridge... the top nibble is region, the bottom is size
the options are shown below:
Region Nibble
Region Meaning
  Rom Size Nibble
Rom Size Meaning
&3-
SMS Japan
&-C
32k
&4-
SMS Export
&-D
128k
&5-
GG Japan
&-0
256k
&6-
GG Export
&-1
512k
&7-
GG International


Note: There are actually smaller cartridges than the 32k in these tutorials, but for simplicity we'll only look at 32k here (padded with 0 bytes)
Also Note: Although SMS/GG is specified in the header, it's actually the filename used by fusion to decide what system to use for the rom... a SMS file extension is the Master system and GG is the gamegear


Only the International SMS checks the Checksum, and while the later GameGears check the header for the ''TMR SEGA" string, all other systems check nothing at all!

If you run the FUSION emulator without a bios Rom, then you can test your game on SMS or GG just fine with no header at all!

Initializing the screen

We need to turn on the screen and set it up, to do this we're going to write default values to the VDP control port

We're going to define symbols for the VDP Control port (&BF) and VDP data port (&BE)

To transfer the data to the VDP we'll use OTIR.

OTIR repeatedly OUTs bytes from HL to port C... and the byte count will be in B

The code to the right will set up a valid screen.




Ok, so the screen will be working, but we won't be able to see anything - as all the colors will be black!

We need to set up a valid palette!... we're going to use OTIR to send the palette data to the VDP data port, but first we need to select the destination address in VRAM (&C000 is the start of the palette)... we do this with a function called 'PrepareVram'

Next we out the bytes of the palette to the Vdp Data port (&BE)

Note: we're sending 32 bytes of data on the GameGear - but only 16 on the Master System... that's because the GameGear has a better palette range than the SMS!


The palette is the biggest difference between the SMS and GG... both have 16 colors for the tiles (background), but the SMS uses one byte per color (2 bit per channel), but the GameGear has 2 bytes per color (4 bits per channel)

We use a symbol (BuildSGG) if it's defined, we'll use the GameGear palette, otherwise we'll use the MasterSystem one.
We're going define tiles 0-96 as our font (0 will be space)

We're importing a font file called 'Font96.FNT'... this uses 8x8 characters (SMS/GG tiles are also 8x8)
The font file is black and white (1 bit per pixel)... but the SMS/GG uses 4 bitplanes (4 bits per pixel)...

We write the same byte 4 times, setting each bitplane to the same value, efffectively setting the text to color 15

If we wanted to set the font color to 1, we could load a with zero after the first 'out (vdpData),a', effectively setting bitplanes 2,3,4 to zero.



We have a font, but we need to actually handle the 'cursor' position.

Each time we show a character, we need to work out the position to show it, but we then need to update the position so we know where the next one should be shown to the screen.

For this purpose we'll use two bytes of Ram at (&C000/1) for an X and Y position...

But because the gamegear screen is smaller than the master system, the top left visible tile is not (0,0), but is actually (6,3)... we need to define the correct start position depending on the system

Printing a character to the screen

Ok, we need to print a character to the screen, but it's a little tricky.

The 'Tilemap' starts at VRAM address &3800... each tile is 2 bytes, and the tilemap is 32 tiles wide... therefore our formula is:

Tile VramAddress=&3800 + (Ypos * 64)+ (Xpos *2)

We achieve this calculation via a series of bitshifts... once we've found the Vram address, and selected it with the 'PrepareVram' function, we just need to OUT 2 bytes to the VdpData port,
Our font starts with Space (Character 32), so we subtract 32 from the Ascii character to get the tile number... the second byte we write is always zero in this case (it's used for palette options flipping tiles and a few other things)
Once we've shown the character, we need to update the cursor, we increase 'NextCharX' and see if we're at the end of the screen

The screen is 32 tiles wide on the SMS, and just 20 on the GG... If we're at the end of the screen, we call the NewLine function to move down a line
The newline command increases the Y position, and resets the X position to the left of the screen.

Using our PrintChar to print a string

We're going to write a simple string printing routine, it prints a 255 terminated string.

All we do is read in bytes from HL until we get to a 255, at which point we return... we print all other characters with our PrintChar routine
To print the string, just load HL with the address of the string, and call the function
This will show 'Hello World' to the screen

Patching the CRC into the Rom footer

We wrote a footer at &7FF0 before, but we didn't put a valid checksum, which will mean the game won't boot on some SMS

To fix this, we need to calculate a 16 bit 'Checksum' and write it to bytes &7FFA/B - we achieve this by adding each byte from &0000-&7FEF (even if the rom is larger than 32k)...

The Checksum is 16 bit, this means if the calculation goes over 65535 (&FFFF) it rolls back to 0

I've written a tool called 'BinaryTools' which can create a valid checksum, it's included in the Devtools pack... the command to create the CRC is shown below:



Monitor Tools
As an added bonus we can include the 'Monitor Tools' we developed in the multiplatform series.

These allow us to show the registers or parts of the Ram for testing and debugging purposes
This gives us 2 commands...

Monitor shows all the registers,

MemDumpDirect will show C bytes from address HL to the screen
We can see the result onscreen.
This example code is pretty long, but we've got the screen set up correctly, and now we can see the registers and Ram, so we can do more from here!

the "PrepareVram" function can be used to do sprites and things as well, so you should have a great starting point to make something better.


Lesson H9- Hello World on the Gameboy and Gameboy Color
This time we're going to learn how to make Hello World on the Gameboy and GBC... essentially they're the same system, but remember, the Gameboy isn't a 'true' z80 - it's a gbz80!

See SimpleHelloWorld folder

As far as todays code goes, the regular Game Boy and GBC are virtually the same, so the code we're making today will compile on GB or GBC - we just need to define a symbol BuildGBC for the Gameboy Color!

The Batch files provided in the DevTools will do this automatically!

Beginning a program

We need to start our rom cartridge, the first 64 bytes are used by the Z80 RST's - the gameboy does not need these, not even RST7 - which does not handle the interrupt handler on the gameboy.

In fact, the Gameboy Interrupts are handled by addreses &0040, &0048, &0050, &0058, &0060 - we need to make sure these interrupts won't do anything, so we just put a RETI at each
Execution of our cartridge will start at address &0100, but we need to put the cartridge header at &0104 - so we only have room for a jump here
Next we need the rom header - this is very important for a real gameboy - as if the header Checksum, or Nintendo Logo are not correct the game won't boot.

We'll learn how to calculate that checksum later.
Our actual code will start at the 'begin' label
We're going to have to do quite a bit of set up to get the system working, we need to define our font, and palette so we can see anything on screen...

The gameboy is a great little system, but if you're just starting out with Z80, then a system like the Amstrad CPC is easier to get started with thanks to it's firmware.

Initializing the screen

We're going to start by setting up the position of our screen... we're going to reset the 'tilemap' - moving it to position (0,0)

We're also going to define two defined memory addresses NextCharacterX and Y - these will remember the position of the next character to draw - these will be in Gameboy Ram at &C000/1


Next we need to turn off the screen so we can set things up...

We have to wait until the screen is in Vblank... to do this we check address &FF44 - which will return the screen line - when the screen line is 156, we're in Vblank....

To turn off the screen we reset bit 7 of &FF40
Now our screen is off ,we need to define our font...

We're going to put this in tiles 0-95 - our font has 96 characters (from 32-128... first character is space, containing all standard visible Ascii characters)

Our bitmap font is a black and white 8x8 font (8 bytes per character)... we're going to load from the label 'BitmapFont' into address &8000 - the start of the patterns in video ram

Now, we read in each byte - but we write it twice... the Gameboy uses 2 bits per pixel - meaning 2 bitplanes, so we 'double up' the font data, writing it twice... This effectively means the font is Color 3 in the palette...

We repeat for all the bytes of the font.


The next stage is to set the palette... this depends on if we're using a Gameboy Color, or classic.

On the Black and white GB:
on a black and white gameboy, we define the background with address &FF47... sprites are defined by &FF48/9... each uses 2 bits per color, 00=White and 11=black

On the Color GB:
On color systems we have 8 palettes (0-7)... we're defining a function called 'SetGBCPalettes'... we're using palette 0 for the background, and palette 7 for the font... each palette has 4 colors - 2 bytes per color, so we multiply the palette number by 8.
On the Color GB:

The native format of the GBC palettes is 16 bit in the format -BBBBBGG GGGRRRRR

We read in bytes from our palette definiton (the end of the definition is defined by a bytepair of 255,255)

Next we write the palette number we want to change to &FF68 (from C), after that we write the Low byte (from E) to &FF69
We increment C, and write the second palette number we want to change to &FF68 (from C), and write the High byte (from D) to &FF69

we Inc C again, and repeat to do the same for the following palette entry.
Ok, our font and palette are ready, so now we can turn the screen back on!

We're ready to write our program!

Printing a character to the screen

When we want to print a character to the screen, we need to calculate the position in the Tilemap we want to draw the character,

Although the screen is 20 tiles wide, The tilemap is 32 tiles wide... it starts at memory address &9800

Our formula is:  Tile Address= &9800 + (ypos*32) + xpos

We calculate this by reading in our Y-Pos, multiplying it by 32 (via bitshifts) and adding the X-Pos... then we add the &9800 base...

The tile address for the character is now in HL
Before we write to Vram, we need to wait... we'll use a function called 'LCDWait' to do this... we'll take a look at this in a moment!

As our font starts at character 32, we subtact 32 from the accumulator, the write A to the address in HL to set the tile visible at the location we calculated.

On the Black and white GB, we're done!
On the Color GB:
We need to select the palette... we're going to use GBC palette 7...

The palette entry for our tile is at the same address (in HL)... BUT we need to switch in the extra GBC ram... to do this we write a 1 to address &FF47...

This turns the GBC ram on... we write our palette number 7 to HL...

Finally we turn off the GBC ram, by writing a 0
When we want to wait for Vram to be ready, we need to check the 'Mode' bits of &FF41... in bits 0-1

If the 'Mode' is 0 or 1 - we can Write, if it's 2 or 3 we need to wait, so we can check this by waiting for bit 1 to be zero.
When we want to start a newline, we just Zero NextCharX, and increase NextCharY... we need to do this when Xpos reaches 20 (the width of the screen)

Using our PrintChar to print a string

We can build up our PrintChar routine into a PrintString routine, Our string is 255 terminated.
We load HL with the string address, and Call PrintString
The Text will be shown to screen

Compiling our rom is easy, we just need to compile to a binary file... because we've included the header in our source as-is... we can just load it straight away in Visual boy Advance!

If we're using the BGB emulator, we need to fix the CRC first, lets learn how!


Patching the CRC into the Rom

To make our ROM work with the BGB emulator , we need to ensure the checksum is OK, we can fix it with RGBFIX

-v will tell the software to fix the rom checksum
-p 0 will pad the rom with zeros.


Monitor Tools

We can include the Monitor tools we wrote in the Multiplatform series.

These will help us develop our programs
If we want to see the state of the registers, we can just call the Monitor function...

We can also dump parts of the memory, just load HL with an address, and C with a bytecount, and call Montior_MemDumpDirect
The Results can be seen here.



 

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)

My Game projects
Chibi Aliens
Chibi Akumas

Work in Progress
Learn 6809 Assembly
Learn 65816 Assembly
Learn 6809 Assembly
Learn PDP11 Assembly
Learn TMS9900 Assembly
Learn 8086 Assembly (x86)
Learn Risc-V Assembly
Wonderswan
MsDos
Learn ARM Assembly
Dragon 32/Tandy Coco
Ti 99
Gameboy Advance
Risc Os

Misc bits
Ruby programming




Chibi Akumas V1.666 has taken over 350 hours of development, if you want to support my work, and learn all the secrets of the game's development, please back me on patreon!





Thanks to Homebrew Legends for help promoting my game!
Buy Chibi Akuma(s) from PolyPlay
Buy ChibiAkuma(s) games now!